Remove 2 suffix for multi_buffer, outline, copilot

Max Brunsfeld and Mikayla created

Co-authored-by: Mikayla <mikayla@zed.dev>

Change summary

Cargo.lock                                |  112 
Cargo.toml                                |    3 
crates/assistant2/Cargo.toml              |    2 
crates/breadcrumbs/Cargo.toml             |    2 
crates/copilot/Cargo.toml                 |   23 
crates/copilot/src/copilot.rs             |  346 
crates/copilot/src/sign_in.rs             |  392 
crates/copilot2/Cargo.toml                |   51 
crates/copilot2/src/copilot2.rs           | 1253 ---
crates/copilot2/src/request.rs            |  225 
crates/copilot2/src/sign_in.rs            |  211 
crates/copilot_button/Cargo.toml          |    2 
crates/editor/Cargo.toml                  |    8 
crates/editor2/src/editor_tests.rs        | 8268 ------------------------
crates/multi_buffer/Cargo.toml            |   32 
crates/multi_buffer/src/multi_buffer.rs   |  188 
crates/multi_buffer2/Cargo.toml           |   78 
crates/multi_buffer2/src/anchor.rs        |  138 
crates/multi_buffer2/src/multi_buffer2.rs | 5390 ----------------
crates/outline/Cargo.toml                 |   16 
crates/outline/src/outline.rs             |  208 
crates/outline2/Cargo.toml                |   29 
crates/outline2/src/outline.rs            |  309 
crates/project2/Cargo.toml                |    2 
crates/zed/Cargo.toml                     |    4 
25 files changed, 602 insertions(+), 16,690 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -385,7 +385,7 @@ dependencies = [
  "language2",
  "log",
  "menu2",
- "multi_buffer2",
+ "multi_buffer",
  "ordered-float 2.10.0",
  "parking_lot 0.11.2",
  "project2",
@@ -1093,7 +1093,7 @@ dependencies = [
  "gpui2",
  "itertools 0.10.5",
  "language2",
- "outline2",
+ "outline",
  "project2",
  "search",
  "settings2",
@@ -1947,33 +1947,6 @@ dependencies = [
 [[package]]
 name = "copilot"
 version = "0.1.0"
-dependencies = [
- "anyhow",
- "async-compression",
- "async-tar",
- "clock",
- "collections",
- "context_menu",
- "fs",
- "futures 0.3.28",
- "gpui",
- "language",
- "log",
- "lsp",
- "node_runtime",
- "parking_lot 0.11.2",
- "rpc",
- "serde",
- "serde_derive",
- "settings",
- "smol",
- "theme",
- "util",
-]
-
-[[package]]
-name = "copilot2"
-version = "0.1.0"
 dependencies = [
  "anyhow",
  "async-compression",
@@ -2003,7 +1976,7 @@ name = "copilot_button"
 version = "0.1.0"
 dependencies = [
  "anyhow",
- "copilot2",
+ "copilot",
  "editor",
  "fs2",
  "futures 0.3.28",
@@ -2692,7 +2665,7 @@ dependencies = [
  "clock",
  "collections",
  "convert_case 0.6.0",
- "copilot2",
+ "copilot",
  "ctor",
  "db2",
  "env_logger",
@@ -2706,7 +2679,7 @@ dependencies = [
  "lazy_static",
  "log",
  "lsp2",
- "multi_buffer2",
+ "multi_buffer",
  "ordered-float 2.10.0",
  "parking_lot 0.11.2",
  "postage",
@@ -5071,55 +5044,6 @@ dependencies = [
 [[package]]
 name = "multi_buffer"
 version = "0.1.0"
-dependencies = [
- "aho-corasick",
- "anyhow",
- "client",
- "clock",
- "collections",
- "context_menu",
- "convert_case 0.6.0",
- "copilot",
- "ctor",
- "env_logger",
- "futures 0.3.28",
- "git",
- "gpui",
- "indoc",
- "itertools 0.10.5",
- "language",
- "lazy_static",
- "log",
- "lsp",
- "ordered-float 2.10.0",
- "parking_lot 0.11.2",
- "postage",
- "project",
- "pulldown-cmark",
- "rand 0.8.5",
- "rich_text",
- "schemars",
- "serde",
- "serde_derive",
- "settings",
- "smallvec",
- "smol",
- "snippet",
- "sum_tree",
- "text",
- "theme",
- "tree-sitter",
- "tree-sitter-html",
- "tree-sitter-rust",
- "tree-sitter-typescript",
- "unindent",
- "util",
- "workspace",
-]
-
-[[package]]
-name = "multi_buffer2"
-version = "0.1.0"
 dependencies = [
  "aho-corasick",
  "anyhow",
@@ -5127,7 +5051,7 @@ dependencies = [
  "clock",
  "collections",
  "convert_case 0.6.0",
- "copilot2",
+ "copilot",
  "ctor",
  "env_logger",
  "futures 0.3.28",
@@ -5742,24 +5666,6 @@ dependencies = [
 [[package]]
 name = "outline"
 version = "0.1.0"
-dependencies = [
- "editor",
- "fuzzy",
- "gpui",
- "language",
- "ordered-float 2.10.0",
- "picker",
- "postage",
- "settings",
- "smol",
- "text",
- "theme",
- "workspace",
-]
-
-[[package]]
-name = "outline2"
-version = "0.1.0"
 dependencies = [
  "editor",
  "fuzzy2",
@@ -6353,7 +6259,7 @@ dependencies = [
  "client2",
  "clock",
  "collections",
- "copilot2",
+ "copilot",
  "ctor",
  "db2",
  "env_logger",
@@ -11013,7 +10919,7 @@ dependencies = [
  "collab_ui",
  "collections",
  "command_palette",
- "copilot2",
+ "copilot",
  "copilot_button",
  "ctor",
  "db2",
@@ -11045,7 +10951,7 @@ dependencies = [
  "node_runtime",
  "notifications2",
  "num_cpus",
- "outline2",
+ "outline",
  "parking_lot 0.11.2",
  "postage",
  "project2",

Cargo.toml 🔗

@@ -24,7 +24,6 @@ members = [
     "crates/component_test",
     "crates/context_menu",
     "crates/copilot",
-    "crates/copilot2",
     "crates/copilot_button",
     "crates/db",
     "crates/db2",
@@ -64,12 +63,10 @@ members = [
     "crates/menu",
     "crates/menu2",
     "crates/multi_buffer",
-    "crates/multi_buffer2",
     "crates/node_runtime",
     "crates/notifications",
     "crates/notifications2",
     "crates/outline",
-    "crates/outline2",
     "crates/picker",
     "crates/plugin",
     "crates/plugin_macros",

crates/assistant2/Cargo.toml 🔗

@@ -17,7 +17,7 @@ fs = { package = "fs2", path = "../fs2" }
 gpui = { package = "gpui2", path = "../gpui2" }
 language = { package = "language2", path = "../language2" }
 menu = { package = "menu2", path = "../menu2" }
-multi_buffer = { package = "multi_buffer2", path = "../multi_buffer2" }
+multi_buffer = { path = "../multi_buffer" }
 project = { package = "project2", path = "../project2" }
 search = { path = "../search" }
 semantic_index = { package = "semantic_index2", path = "../semantic_index2" }

crates/breadcrumbs/Cargo.toml 🔗

@@ -19,7 +19,7 @@ search = { path = "../search" }
 settings = { package = "settings2", path = "../settings2" }
 theme = { package = "theme2", path = "../theme2" }
 workspace = { package = "workspace2", path = "../workspace2" }
-outline = { package = "outline2", path = "../outline2" }
+outline = { path = "../outline" }
 itertools = "0.10"
 
 [dev-dependencies]

crates/copilot/Cargo.toml 🔗

@@ -20,14 +20,15 @@ test-support = [
 
 [dependencies]
 collections = { path = "../collections" }
-context_menu = { path = "../context_menu" }
-gpui = { path = "../gpui" }
-language = { path = "../language" }
-settings = { path = "../settings" }
-theme = { path = "../theme" }
-lsp = { path = "../lsp" }
+# context_menu = { path = "../context_menu" }
+gpui = { package = "gpui2", path = "../gpui2" }
+language = { package = "language2", path = "../language2" }
+settings = { package = "settings2", path = "../settings2" }
+theme = { package = "theme2", path = "../theme2" }
+lsp = { package = "lsp2", path = "../lsp2" }
 node_runtime = { path = "../node_runtime"}
 util = { path = "../util" }
+ui = { package = "ui2", path = "../ui2" }
 async-compression.workspace = true
 async-tar = "0.4.2"
 anyhow.workspace = true
@@ -42,9 +43,9 @@ parking_lot.workspace = true
 clock = { path = "../clock" }
 collections = { path = "../collections", features = ["test-support"] }
 fs = { path = "../fs", features = ["test-support"] }
-gpui = { path = "../gpui", features = ["test-support"] }
-language = { path = "../language", features = ["test-support"] }
-lsp = { path = "../lsp", features = ["test-support"] }
-rpc = { path = "../rpc", features = ["test-support"] }
-settings = { path = "../settings", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+language = { package = "language2", path = "../language2", features = ["test-support"] }
+lsp = { package = "lsp2", path = "../lsp2", features = ["test-support"] }
+rpc = { package = "rpc2", path = "../rpc2", features = ["test-support"] }
+settings = { package = "settings2", path = "../settings2", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }

crates/copilot/src/copilot.rs 🔗

@@ -1,13 +1,14 @@
 pub mod request;
 mod sign_in;
 
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, Context as _, Result};
 use async_compression::futures::bufread::GzipDecoder;
 use async_tar::Archive;
 use collections::{HashMap, HashSet};
 use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt};
 use gpui::{
-    actions, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task, WeakModelHandle,
+    actions, AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Model,
+    ModelContext, Task, WeakModel,
 };
 use language::{
     language_settings::{all_language_settings, language_settings},
@@ -21,24 +22,27 @@ use request::StatusNotification;
 use settings::SettingsStore;
 use smol::{fs, io::BufReader, stream::StreamExt};
 use std::{
+    any::TypeId,
     ffi::OsString,
     mem,
     ops::Range,
     path::{Path, PathBuf},
-    pin::Pin,
     sync::Arc,
 };
 use util::{
     fs::remove_matching, github::latest_github_release, http::HttpClient, paths, ResultExt,
 };
 
-const COPILOT_AUTH_NAMESPACE: &'static str = "copilot_auth";
-actions!(copilot_auth, [SignIn, SignOut]);
-
-const COPILOT_NAMESPACE: &'static str = "copilot";
 actions!(
     copilot,
-    [Suggest, NextSuggestion, PreviousSuggestion, Reinstall]
+    [
+        Suggest,
+        NextSuggestion,
+        PreviousSuggestion,
+        Reinstall,
+        SignIn,
+        SignOut
+    ]
 );
 
 pub fn init(
@@ -47,50 +51,69 @@ pub fn init(
     node_runtime: Arc<dyn NodeRuntime>,
     cx: &mut AppContext,
 ) {
-    let copilot = cx.add_model({
+    let copilot = cx.new_model({
         let node_runtime = node_runtime.clone();
         move |cx| Copilot::start(new_server_id, http, node_runtime, cx)
     });
     cx.set_global(copilot.clone());
-
     cx.observe(&copilot, |handle, cx| {
+        let copilot_action_types = [
+            TypeId::of::<Suggest>(),
+            TypeId::of::<NextSuggestion>(),
+            TypeId::of::<PreviousSuggestion>(),
+            TypeId::of::<Reinstall>(),
+        ];
+        let copilot_auth_action_types = [TypeId::of::<SignOut>()];
+        let copilot_no_auth_action_types = [TypeId::of::<SignIn>()];
         let status = handle.read(cx).status();
-        cx.update_default_global::<collections::CommandPaletteFilter, _, _>(move |filter, _cx| {
-            match status {
-                Status::Disabled => {
-                    filter.hidden_namespaces.insert(COPILOT_NAMESPACE);
-                    filter.hidden_namespaces.insert(COPILOT_AUTH_NAMESPACE);
-                }
-                Status::Authorized => {
-                    filter.hidden_namespaces.remove(COPILOT_NAMESPACE);
-                    filter.hidden_namespaces.remove(COPILOT_AUTH_NAMESPACE);
+        let filter = cx.default_global::<collections::CommandPaletteFilter>();
+
+        match status {
+            Status::Disabled => {
+                filter.hidden_action_types.extend(copilot_action_types);
+                filter.hidden_action_types.extend(copilot_auth_action_types);
+                filter
+                    .hidden_action_types
+                    .extend(copilot_no_auth_action_types);
+            }
+            Status::Authorized => {
+                filter
+                    .hidden_action_types
+                    .extend(copilot_no_auth_action_types);
+                for type_id in copilot_action_types
+                    .iter()
+                    .chain(&copilot_auth_action_types)
+                {
+                    filter.hidden_action_types.remove(type_id);
                 }
-                _ => {
-                    filter.hidden_namespaces.insert(COPILOT_NAMESPACE);
-                    filter.hidden_namespaces.remove(COPILOT_AUTH_NAMESPACE);
+            }
+            _ => {
+                filter.hidden_action_types.extend(copilot_action_types);
+                filter.hidden_action_types.extend(copilot_auth_action_types);
+                for type_id in &copilot_no_auth_action_types {
+                    filter.hidden_action_types.remove(type_id);
                 }
             }
-        });
+        }
     })
     .detach();
 
     sign_in::init(cx);
-    cx.add_global_action(|_: &SignIn, cx| {
+    cx.on_action(|_: &SignIn, cx| {
         if let Some(copilot) = Copilot::global(cx) {
             copilot
                 .update(cx, |copilot, cx| copilot.sign_in(cx))
                 .detach_and_log_err(cx);
         }
     });
-    cx.add_global_action(|_: &SignOut, cx| {
+    cx.on_action(|_: &SignOut, cx| {
         if let Some(copilot) = Copilot::global(cx) {
             copilot
                 .update(cx, |copilot, cx| copilot.sign_out(cx))
                 .detach_and_log_err(cx);
         }
     });
-
-    cx.add_global_action(|_: &Reinstall, cx| {
+    cx.on_action(|_: &Reinstall, cx| {
         if let Some(copilot) = Copilot::global(cx) {
             copilot
                 .update(cx, |copilot, cx| copilot.reinstall(cx))
@@ -133,7 +156,7 @@ struct RunningCopilotServer {
     name: LanguageServerName,
     lsp: Arc<LanguageServer>,
     sign_in_status: SignInStatus,
-    registered_buffers: HashMap<usize, RegisteredBuffer>,
+    registered_buffers: HashMap<EntityId, RegisteredBuffer>,
 }
 
 #[derive(Clone, Debug)]
@@ -180,7 +203,7 @@ struct RegisteredBuffer {
 impl RegisteredBuffer {
     fn report_changes(
         &mut self,
-        buffer: &ModelHandle<Buffer>,
+        buffer: &Model<Buffer>,
         cx: &mut ModelContext<Copilot>,
     ) -> oneshot::Receiver<(i32, BufferSnapshot)> {
         let (done_tx, done_rx) = oneshot::channel();
@@ -189,23 +212,23 @@ impl RegisteredBuffer {
             let _ = done_tx.send((self.snapshot_version, self.snapshot.clone()));
         } else {
             let buffer = buffer.downgrade();
-            let id = buffer.id();
+            let id = buffer.entity_id();
             let prev_pending_change =
                 mem::replace(&mut self.pending_buffer_change, Task::ready(None));
-            self.pending_buffer_change = cx.spawn_weak(|copilot, mut cx| async move {
+            self.pending_buffer_change = cx.spawn(move |copilot, mut cx| async move {
                 prev_pending_change.await;
 
-                let old_version = copilot.upgrade(&cx)?.update(&mut cx, |copilot, _| {
-                    let server = copilot.server.as_authenticated().log_err()?;
-                    let buffer = server.registered_buffers.get_mut(&id)?;
-                    Some(buffer.snapshot.version.clone())
-                })?;
-                let new_snapshot = buffer
-                    .upgrade(&cx)?
-                    .read_with(&cx, |buffer, _| buffer.snapshot());
+                let old_version = copilot
+                    .update(&mut cx, |copilot, _| {
+                        let server = copilot.server.as_authenticated().log_err()?;
+                        let buffer = server.registered_buffers.get_mut(&id)?;
+                        Some(buffer.snapshot.version.clone())
+                    })
+                    .ok()??;
+                let new_snapshot = buffer.update(&mut cx, |buffer, _| buffer.snapshot()).ok()?;
 
                 let content_changes = cx
-                    .background()
+                    .background_executor()
                     .spawn({
                         let new_snapshot = new_snapshot.clone();
                         async move {
@@ -231,28 +254,30 @@ impl RegisteredBuffer {
                     })
                     .await;
 
-                copilot.upgrade(&cx)?.update(&mut cx, |copilot, _| {
-                    let server = copilot.server.as_authenticated().log_err()?;
-                    let buffer = server.registered_buffers.get_mut(&id)?;
-                    if !content_changes.is_empty() {
-                        buffer.snapshot_version += 1;
-                        buffer.snapshot = new_snapshot;
-                        server
-                            .lsp
-                            .notify::<lsp::notification::DidChangeTextDocument>(
-                                lsp::DidChangeTextDocumentParams {
-                                    text_document: lsp::VersionedTextDocumentIdentifier::new(
-                                        buffer.uri.clone(),
-                                        buffer.snapshot_version,
-                                    ),
-                                    content_changes,
-                                },
-                            )
-                            .log_err();
-                    }
-                    let _ = done_tx.send((buffer.snapshot_version, buffer.snapshot.clone()));
-                    Some(())
-                })?;
+                copilot
+                    .update(&mut cx, |copilot, _| {
+                        let server = copilot.server.as_authenticated().log_err()?;
+                        let buffer = server.registered_buffers.get_mut(&id)?;
+                        if !content_changes.is_empty() {
+                            buffer.snapshot_version += 1;
+                            buffer.snapshot = new_snapshot;
+                            server
+                                .lsp
+                                .notify::<lsp::notification::DidChangeTextDocument>(
+                                    lsp::DidChangeTextDocumentParams {
+                                        text_document: lsp::VersionedTextDocumentIdentifier::new(
+                                            buffer.uri.clone(),
+                                            buffer.snapshot_version,
+                                        ),
+                                        content_changes,
+                                    },
+                                )
+                                .log_err();
+                        }
+                        let _ = done_tx.send((buffer.snapshot_version, buffer.snapshot.clone()));
+                        Some(())
+                    })
+                    .ok()?;
 
                 Some(())
             });
@@ -273,36 +298,21 @@ pub struct Copilot {
     http: Arc<dyn HttpClient>,
     node_runtime: Arc<dyn NodeRuntime>,
     server: CopilotServer,
-    buffers: HashSet<WeakModelHandle<Buffer>>,
+    buffers: HashSet<WeakModel<Buffer>>,
     server_id: LanguageServerId,
+    _subscription: gpui::Subscription,
 }
 
 pub enum Event {
     CopilotLanguageServerStarted,
 }
 
-impl Entity for Copilot {
-    type Event = Event;
-
-    fn app_will_quit(
-        &mut self,
-        _: &mut AppContext,
-    ) -> Option<Pin<Box<dyn 'static + Future<Output = ()>>>> {
-        match mem::replace(&mut self.server, CopilotServer::Disabled) {
-            CopilotServer::Running(server) => Some(Box::pin(async move {
-                if let Some(shutdown) = server.lsp.shutdown() {
-                    shutdown.await;
-                }
-            })),
-            _ => None,
-        }
-    }
-}
+impl EventEmitter<Event> for Copilot {}
 
 impl Copilot {
-    pub fn global(cx: &AppContext) -> Option<ModelHandle<Self>> {
-        if cx.has_global::<ModelHandle<Self>>() {
-            Some(cx.global::<ModelHandle<Self>>().clone())
+    pub fn global(cx: &AppContext) -> Option<Model<Self>> {
+        if cx.has_global::<Model<Self>>() {
+            Some(cx.global::<Model<Self>>().clone())
         } else {
             None
         }
@@ -320,24 +330,39 @@ impl Copilot {
             node_runtime,
             server: CopilotServer::Disabled,
             buffers: Default::default(),
+            _subscription: cx.on_app_quit(Self::shutdown_language_server),
         };
         this.enable_or_disable_copilot(cx);
-        cx.observe_global::<SettingsStore, _>(move |this, cx| this.enable_or_disable_copilot(cx))
+        cx.observe_global::<SettingsStore>(move |this, cx| this.enable_or_disable_copilot(cx))
             .detach();
         this
     }
 
-    fn enable_or_disable_copilot(&mut self, cx: &mut ModelContext<Copilot>) {
+    fn shutdown_language_server(
+        &mut self,
+        _cx: &mut ModelContext<Self>,
+    ) -> impl Future<Output = ()> {
+        let shutdown = match mem::replace(&mut self.server, CopilotServer::Disabled) {
+            CopilotServer::Running(server) => Some(Box::pin(async move { server.lsp.shutdown() })),
+            _ => None,
+        };
+
+        async move {
+            if let Some(shutdown) = shutdown {
+                shutdown.await;
+            }
+        }
+    }
+
+    fn enable_or_disable_copilot(&mut self, cx: &mut ModelContext<Self>) {
         let server_id = self.server_id;
         let http = self.http.clone();
         let node_runtime = self.node_runtime.clone();
         if all_language_settings(None, cx).copilot_enabled(None, None) {
             if matches!(self.server, CopilotServer::Disabled) {
                 let start_task = cx
-                    .spawn({
-                        move |this, cx| {
-                            Self::start_language_server(server_id, http, node_runtime, this, cx)
-                        }
+                    .spawn(move |this, cx| {
+                        Self::start_language_server(server_id, http, node_runtime, this, cx)
                     })
                     .shared();
                 self.server = CopilotServer::Starting { task: start_task };
@@ -350,14 +375,14 @@ impl Copilot {
     }
 
     #[cfg(any(test, feature = "test-support"))]
-    pub fn fake(cx: &mut gpui::TestAppContext) -> (ModelHandle<Self>, lsp::FakeLanguageServer) {
+    pub fn fake(cx: &mut gpui::TestAppContext) -> (Model<Self>, lsp::FakeLanguageServer) {
         use node_runtime::FakeNodeRuntime;
 
         let (server, fake_server) =
             LanguageServer::fake("copilot".into(), Default::default(), cx.to_async());
         let http = util::http::FakeHttpClient::create(|_| async { unreachable!() });
         let node_runtime = FakeNodeRuntime::new();
-        let this = cx.add_model(|_| Self {
+        let this = cx.new_model(|cx| Self {
             server_id: LanguageServerId(0),
             http: http.clone(),
             node_runtime,
@@ -367,6 +392,7 @@ impl Copilot {
                 sign_in_status: SignInStatus::Authorized,
                 registered_buffers: Default::default(),
             }),
+            _subscription: cx.on_app_quit(Self::shutdown_language_server),
             buffers: Default::default(),
         });
         (this, fake_server)
@@ -376,7 +402,7 @@ impl Copilot {
         new_server_id: LanguageServerId,
         http: Arc<dyn HttpClient>,
         node_runtime: Arc<dyn NodeRuntime>,
-        this: ModelHandle<Self>,
+        this: WeakModel<Self>,
         mut cx: AsyncAppContext,
     ) -> impl Future<Output = ()> {
         async move {
@@ -448,6 +474,7 @@ impl Copilot {
                     }
                 }
             })
+            .ok();
         }
     }
 
@@ -489,7 +516,7 @@ impl Copilot {
                                                     cx.notify();
                                                 }
                                             }
-                                        });
+                                        })?;
                                         let response = lsp
                                             .request::<request::SignInConfirm>(
                                                 request::SignInConfirmParams {
@@ -515,7 +542,7 @@ impl Copilot {
                                     );
                                     Err(Arc::new(error))
                                 }
-                            })
+                            })?
                         })
                         .shared();
                     server.sign_in_status = SignInStatus::SigningIn {
@@ -527,7 +554,7 @@ impl Copilot {
                 }
             };
 
-            cx.foreground()
+            cx.background_executor()
                 .spawn(task.map_err(|err| anyhow!("{:?}", err)))
         } else {
             // If we're downloading, wait until download is finished
@@ -540,7 +567,7 @@ impl Copilot {
         self.update_sign_in_status(request::SignInStatus::NotSignedIn, cx);
         if let CopilotServer::Running(RunningCopilotServer { lsp: server, .. }) = &self.server {
             let server = server.clone();
-            cx.background().spawn(async move {
+            cx.background_executor().spawn(async move {
                 server
                     .request::<request::SignOut>(request::SignOutParams {})
                     .await?;
@@ -570,7 +597,7 @@ impl Copilot {
 
         cx.notify();
 
-        cx.foreground().spawn(start_task)
+        cx.background_executor().spawn(start_task)
     }
 
     pub fn language_server(&self) -> Option<(&LanguageServerName, &Arc<LanguageServer>)> {
@@ -581,7 +608,7 @@ impl Copilot {
         }
     }
 
-    pub fn register_buffer(&mut self, buffer: &ModelHandle<Buffer>, cx: &mut ModelContext<Self>) {
+    pub fn register_buffer(&mut self, buffer: &Model<Buffer>, cx: &mut ModelContext<Self>) {
         let weak_buffer = buffer.downgrade();
         self.buffers.insert(weak_buffer.clone());
 
@@ -596,51 +623,54 @@ impl Copilot {
                 return;
             }
 
-            registered_buffers.entry(buffer.id()).or_insert_with(|| {
-                let uri: lsp::Url = uri_for_buffer(buffer, cx);
-                let language_id = id_for_language(buffer.read(cx).language());
-                let snapshot = buffer.read(cx).snapshot();
-                server
-                    .notify::<lsp::notification::DidOpenTextDocument>(
-                        lsp::DidOpenTextDocumentParams {
-                            text_document: lsp::TextDocumentItem {
-                                uri: uri.clone(),
-                                language_id: language_id.clone(),
-                                version: 0,
-                                text: snapshot.text(),
+            registered_buffers
+                .entry(buffer.entity_id())
+                .or_insert_with(|| {
+                    let uri: lsp::Url = uri_for_buffer(buffer, cx);
+                    let language_id = id_for_language(buffer.read(cx).language());
+                    let snapshot = buffer.read(cx).snapshot();
+                    server
+                        .notify::<lsp::notification::DidOpenTextDocument>(
+                            lsp::DidOpenTextDocumentParams {
+                                text_document: lsp::TextDocumentItem {
+                                    uri: uri.clone(),
+                                    language_id: language_id.clone(),
+                                    version: 0,
+                                    text: snapshot.text(),
+                                },
                             },
-                        },
-                    )
-                    .log_err();
+                        )
+                        .log_err();
 
-                RegisteredBuffer {
-                    uri,
-                    language_id,
-                    snapshot,
-                    snapshot_version: 0,
-                    pending_buffer_change: Task::ready(Some(())),
-                    _subscriptions: [
-                        cx.subscribe(buffer, |this, buffer, event, cx| {
-                            this.handle_buffer_event(buffer, event, cx).log_err();
-                        }),
-                        cx.observe_release(buffer, move |this, _buffer, _cx| {
-                            this.buffers.remove(&weak_buffer);
-                            this.unregister_buffer(&weak_buffer);
-                        }),
-                    ],
-                }
-            });
+                    RegisteredBuffer {
+                        uri,
+                        language_id,
+                        snapshot,
+                        snapshot_version: 0,
+                        pending_buffer_change: Task::ready(Some(())),
+                        _subscriptions: [
+                            cx.subscribe(buffer, |this, buffer, event, cx| {
+                                this.handle_buffer_event(buffer, event, cx).log_err();
+                            }),
+                            cx.observe_release(buffer, move |this, _buffer, _cx| {
+                                this.buffers.remove(&weak_buffer);
+                                this.unregister_buffer(&weak_buffer);
+                            }),
+                        ],
+                    }
+                });
         }
     }
 
     fn handle_buffer_event(
         &mut self,
-        buffer: ModelHandle<Buffer>,
+        buffer: Model<Buffer>,
         event: &language::Event,
         cx: &mut ModelContext<Self>,
     ) -> Result<()> {
         if let Ok(server) = self.server.as_running() {
-            if let Some(registered_buffer) = server.registered_buffers.get_mut(&buffer.id()) {
+            if let Some(registered_buffer) = server.registered_buffers.get_mut(&buffer.entity_id())
+            {
                 match event {
                     language::Event::Edited => {
                         let _ = registered_buffer.report_changes(&buffer, cx);
@@ -694,9 +724,9 @@ impl Copilot {
         Ok(())
     }
 
-    fn unregister_buffer(&mut self, buffer: &WeakModelHandle<Buffer>) {
+    fn unregister_buffer(&mut self, buffer: &WeakModel<Buffer>) {
         if let Ok(server) = self.server.as_running() {
-            if let Some(buffer) = server.registered_buffers.remove(&buffer.id()) {
+            if let Some(buffer) = server.registered_buffers.remove(&buffer.entity_id()) {
                 server
                     .lsp
                     .notify::<lsp::notification::DidCloseTextDocument>(
@@ -711,7 +741,7 @@ impl Copilot {
 
     pub fn completions<T>(
         &mut self,
-        buffer: &ModelHandle<Buffer>,
+        buffer: &Model<Buffer>,
         position: T,
         cx: &mut ModelContext<Self>,
     ) -> Task<Result<Vec<Completion>>>
@@ -723,7 +753,7 @@ impl Copilot {
 
     pub fn completions_cycling<T>(
         &mut self,
-        buffer: &ModelHandle<Buffer>,
+        buffer: &Model<Buffer>,
         position: T,
         cx: &mut ModelContext<Self>,
     ) -> Task<Result<Vec<Completion>>>
@@ -748,7 +778,7 @@ impl Copilot {
                 .request::<request::NotifyAccepted>(request::NotifyAcceptedParams {
                     uuid: completion.uuid.clone(),
                 });
-        cx.background().spawn(async move {
+        cx.background_executor().spawn(async move {
             request.await?;
             Ok(())
         })
@@ -772,7 +802,7 @@ impl Copilot {
                         .map(|completion| completion.uuid.clone())
                         .collect(),
                 });
-        cx.background().spawn(async move {
+        cx.background_executor().spawn(async move {
             request.await?;
             Ok(())
         })
@@ -780,7 +810,7 @@ impl Copilot {
 
     fn request_completions<R, T>(
         &mut self,
-        buffer: &ModelHandle<Buffer>,
+        buffer: &Model<Buffer>,
         position: T,
         cx: &mut ModelContext<Self>,
     ) -> Task<Result<Vec<Completion>>>
@@ -799,7 +829,10 @@ impl Copilot {
             Err(error) => return Task::ready(Err(error)),
         };
         let lsp = server.lsp.clone();
-        let registered_buffer = server.registered_buffers.get_mut(&buffer.id()).unwrap();
+        let registered_buffer = server
+            .registered_buffers
+            .get_mut(&buffer.entity_id())
+            .unwrap();
         let snapshot = registered_buffer.report_changes(buffer, cx);
         let buffer = buffer.read(cx);
         let uri = registered_buffer.uri.clone();
@@ -812,7 +845,7 @@ impl Copilot {
             .map(|file| file.path().to_path_buf())
             .unwrap_or_default();
 
-        cx.foreground().spawn(async move {
+        cx.background_executor().spawn(async move {
             let (version, snapshot) = snapshot.await?;
             let result = lsp
                 .request::<R>(request::GetCompletionsParams {
@@ -869,7 +902,7 @@ impl Copilot {
         lsp_status: request::SignInStatus,
         cx: &mut ModelContext<Self>,
     ) {
-        self.buffers.retain(|buffer| buffer.is_upgradable(cx));
+        self.buffers.retain(|buffer| buffer.is_upgradable());
 
         if let Ok(server) = self.server.as_running() {
             match lsp_status {
@@ -878,20 +911,20 @@ impl Copilot {
                 | request::SignInStatus::AlreadySignedIn { .. } => {
                     server.sign_in_status = SignInStatus::Authorized;
                     for buffer in self.buffers.iter().cloned().collect::<Vec<_>>() {
-                        if let Some(buffer) = buffer.upgrade(cx) {
+                        if let Some(buffer) = buffer.upgrade() {
                             self.register_buffer(&buffer, cx);
                         }
                     }
                 }
                 request::SignInStatus::NotAuthorized { .. } => {
                     server.sign_in_status = SignInStatus::Unauthorized;
-                    for buffer in self.buffers.iter().copied().collect::<Vec<_>>() {
+                    for buffer in self.buffers.iter().cloned().collect::<Vec<_>>() {
                         self.unregister_buffer(&buffer);
                     }
                 }
                 request::SignInStatus::NotSignedIn => {
                     server.sign_in_status = SignInStatus::SignedOut;
-                    for buffer in self.buffers.iter().copied().collect::<Vec<_>>() {
+                    for buffer in self.buffers.iter().cloned().collect::<Vec<_>>() {
                         self.unregister_buffer(&buffer);
                     }
                 }
@@ -911,11 +944,11 @@ fn id_for_language(language: Option<&Arc<Language>>) -> String {
     }
 }
 
-fn uri_for_buffer(buffer: &ModelHandle<Buffer>, cx: &AppContext) -> lsp::Url {
+fn uri_for_buffer(buffer: &Model<Buffer>, cx: &AppContext) -> lsp::Url {
     if let Some(file) = buffer.read(cx).file().and_then(|file| file.as_local()) {
         lsp::Url::from_file_path(file.abs_path(cx)).unwrap()
     } else {
-        format!("buffer://{}", buffer.id()).parse().unwrap()
+        format!("buffer://{}", buffer.entity_id()).parse().unwrap()
     }
 }
 
@@ -994,15 +1027,16 @@ async fn get_copilot_lsp(http: Arc<dyn HttpClient>) -> anyhow::Result<PathBuf> {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use gpui::{executor::Deterministic, TestAppContext};
+    use gpui::TestAppContext;
 
     #[gpui::test(iterations = 10)]
-    async fn test_buffer_management(deterministic: Arc<Deterministic>, cx: &mut TestAppContext) {
-        deterministic.forbid_parking();
+    async fn test_buffer_management(cx: &mut TestAppContext) {
         let (copilot, mut lsp) = Copilot::fake(cx);
 
-        let buffer_1 = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "Hello"));
-        let buffer_1_uri: lsp::Url = format!("buffer://{}", buffer_1.id()).parse().unwrap();
+        let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "Hello"));
+        let buffer_1_uri: lsp::Url = format!("buffer://{}", buffer_1.entity_id().as_u64())
+            .parse()
+            .unwrap();
         copilot.update(cx, |copilot, cx| copilot.register_buffer(&buffer_1, cx));
         assert_eq!(
             lsp.receive_notification::<lsp::notification::DidOpenTextDocument>()
@@ -1017,8 +1051,10 @@ mod tests {
             }
         );
 
-        let buffer_2 = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "Goodbye"));
-        let buffer_2_uri: lsp::Url = format!("buffer://{}", buffer_2.id()).parse().unwrap();
+        let buffer_2 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "Goodbye"));
+        let buffer_2_uri: lsp::Url = format!("buffer://{}", buffer_2.entity_id().as_u64())
+            .parse()
+            .unwrap();
         copilot.update(cx, |copilot, cx| copilot.register_buffer(&buffer_2, cx));
         assert_eq!(
             lsp.receive_notification::<lsp::notification::DidOpenTextDocument>()
@@ -1114,6 +1150,7 @@ mod tests {
             .update(cx, |copilot, cx| copilot.sign_in(cx))
             .await
             .unwrap();
+
         assert_eq!(
             lsp.receive_notification::<lsp::notification::DidOpenTextDocument>()
                 .await,
@@ -1138,7 +1175,6 @@ mod tests {
                 ),
             }
         );
-
         // Dropping a buffer causes it to be closed on the LSP side as well.
         cx.update(|_| drop(buffer_2));
         assert_eq!(

crates/copilot/src/sign_in.rs 🔗

@@ -1,18 +1,11 @@
 use crate::{request::PromptUserDeviceFlow, Copilot, Status};
 use gpui::{
-    elements::*,
-    geometry::rect::RectF,
-    platform::{WindowBounds, WindowKind, WindowOptions},
-    AnyElement, AnyViewHandle, AppContext, ClipboardItem, Element, Entity, View, ViewContext,
-    WindowHandle,
+    div, size, AppContext, Bounds, ClipboardItem, Element, GlobalPixels, InteractiveElement,
+    IntoElement, ParentElement, Point, Render, Styled, ViewContext, VisualContext, WindowBounds,
+    WindowHandle, WindowKind, WindowOptions,
 };
-use theme::ui::modal;
-
-#[derive(PartialEq, Eq, Debug, Clone)]
-struct CopyUserCode;
-
-#[derive(PartialEq, Eq, Debug, Clone)]
-struct OpenGithub;
+use theme::ActiveTheme;
+use ui::{prelude::*, Button, Icon, IconElement, Label};
 
 const COPILOT_SIGN_UP_URL: &'static str = "https://github.com/features/copilot";
 
@@ -26,14 +19,11 @@ pub fn init(cx: &mut AppContext) {
                 crate::Status::SigningIn { prompt } => {
                     if let Some(window) = verification_window.as_mut() {
                         let updated = window
-                            .root(cx)
-                            .map(|root| {
-                                root.update(cx, |verification, cx| {
-                                    verification.set_status(status.clone(), cx);
-                                    cx.activate_window();
-                                })
+                            .update(cx, |verification, cx| {
+                                verification.set_status(status.clone(), cx);
+                                cx.activate_window();
                             })
-                            .is_some();
+                            .is_ok();
                         if !updated {
                             verification_window = Some(create_copilot_auth_window(cx, &status));
                         }
@@ -43,18 +33,20 @@ pub fn init(cx: &mut AppContext) {
                 }
                 Status::Authorized | Status::Unauthorized => {
                     if let Some(window) = verification_window.as_ref() {
-                        if let Some(verification) = window.root(cx) {
-                            verification.update(cx, |verification, cx| {
+                        window
+                            .update(cx, |verification, cx| {
                                 verification.set_status(status, cx);
-                                cx.platform().activate(true);
+                                cx.activate(true);
                                 cx.activate_window();
-                            });
-                        }
+                            })
+                            .ok();
                     }
                 }
                 _ => {
                     if let Some(code_verification) = verification_window.take() {
-                        code_verification.update(cx, |cx| cx.remove_window());
+                        code_verification
+                            .update(cx, |_, cx| cx.remove_window())
+                            .ok();
                     }
                 }
             }
@@ -67,20 +59,21 @@ fn create_copilot_auth_window(
     cx: &mut AppContext,
     status: &Status,
 ) -> WindowHandle<CopilotCodeVerification> {
-    let window_size = theme::current(cx).copilot.modal.dimensions();
+    let window_size = size(GlobalPixels::from(280.), GlobalPixels::from(280.));
     let window_options = WindowOptions {
-        bounds: WindowBounds::Fixed(RectF::new(Default::default(), window_size)),
+        bounds: WindowBounds::Fixed(Bounds::new(Point::default(), window_size)),
         titlebar: None,
         center: true,
         focus: true,
         show: true,
-        kind: WindowKind::Normal,
+        kind: WindowKind::PopUp,
         is_movable: true,
-        screen: None,
+        display_id: None,
     };
-    cx.add_window(window_options, |_cx| {
-        CopilotCodeVerification::new(status.clone())
-    })
+    let window = cx.open_window(window_options, |cx| {
+        cx.new_view(|_| CopilotCodeVerification::new(status.clone()))
+    });
+    window
 }
 
 pub struct CopilotCodeVerification {
@@ -103,273 +96,116 @@ impl CopilotCodeVerification {
 
     fn render_device_code(
         data: &PromptUserDeviceFlow,
-        style: &theme::Copilot,
         cx: &mut ViewContext<Self>,
-    ) -> impl Element<Self> {
+    ) -> impl IntoElement {
         let copied = cx
             .read_from_clipboard()
             .map(|item| item.text() == &data.user_code)
             .unwrap_or(false);
-
-        let device_code_style = &style.auth.prompting.device_code;
-
-        MouseEventHandler::new::<Self, _>(0, cx, |state, _cx| {
-            Flex::row()
-                .with_child(
-                    Label::new(data.user_code.clone(), device_code_style.text.clone())
-                        .aligned()
-                        .contained()
-                        .with_style(device_code_style.left_container)
-                        .constrained()
-                        .with_width(device_code_style.left),
-                )
-                .with_child(
-                    Label::new(
-                        if copied { "Copied!" } else { "Copy" },
-                        device_code_style.cta.style_for(state).text.clone(),
-                    )
-                    .aligned()
-                    .contained()
-                    .with_style(*device_code_style.right_container.style_for(state))
-                    .constrained()
-                    .with_width(device_code_style.right),
-                )
-                .contained()
-                .with_style(device_code_style.cta.style_for(state).container)
-        })
-        .on_click(gpui::platform::MouseButton::Left, {
-            let user_code = data.user_code.clone();
-            move |_, _, cx| {
-                cx.platform()
-                    .write_to_clipboard(ClipboardItem::new(user_code.clone()));
-                cx.notify();
-            }
-        })
-        .with_cursor_style(gpui::platform::CursorStyle::PointingHand)
+        h_stack()
+            .cursor_pointer()
+            .justify_between()
+            .on_mouse_down(gpui::MouseButton::Left, {
+                let user_code = data.user_code.clone();
+                move |_, cx| {
+                    cx.write_to_clipboard(ClipboardItem::new(user_code.clone()));
+                    cx.notify();
+                }
+            })
+            .child(Label::new(data.user_code.clone()))
+            .child(div())
+            .child(Label::new(if copied { "Copied!" } else { "Copy" }))
     }
 
     fn render_prompting_modal(
         connect_clicked: bool,
         data: &PromptUserDeviceFlow,
-        style: &theme::Copilot,
         cx: &mut ViewContext<Self>,
-    ) -> AnyElement<Self> {
-        enum ConnectButton {}
-
-        Flex::column()
-            .with_child(
-                Flex::column()
-                    .with_children([
-                        Label::new(
-                            "Enable Copilot by connecting",
-                            style.auth.prompting.subheading.text.clone(),
-                        )
-                        .aligned(),
-                        Label::new(
-                            "your existing license.",
-                            style.auth.prompting.subheading.text.clone(),
-                        )
-                        .aligned(),
-                    ])
-                    .align_children_center()
-                    .contained()
-                    .with_style(style.auth.prompting.subheading.container),
-            )
-            .with_child(Self::render_device_code(data, &style, cx))
-            .with_child(
-                Flex::column()
-                    .with_children([
-                        Label::new(
-                            "Paste this code into GitHub after",
-                            style.auth.prompting.hint.text.clone(),
-                        )
-                        .aligned(),
-                        Label::new(
-                            "clicking the button below.",
-                            style.auth.prompting.hint.text.clone(),
-                        )
-                        .aligned(),
-                    ])
-                    .align_children_center()
-                    .contained()
-                    .with_style(style.auth.prompting.hint.container.clone()),
-            )
-            .with_child(theme::ui::cta_button::<ConnectButton, _, _, _>(
-                if connect_clicked {
-                    "Waiting for connection..."
-                } else {
-                    "Connect to GitHub"
-                },
-                style.auth.content_width,
-                &style.auth.cta_button,
-                cx,
-                {
-                    let verification_uri = data.verification_uri.clone();
-                    move |_, verification, cx| {
-                        cx.platform().open_url(&verification_uri);
-                        verification.connect_clicked = true;
-                    }
-                },
+    ) -> impl Element {
+        let connect_button_label = if connect_clicked {
+            "Waiting for connection..."
+        } else {
+            "Connect to Github"
+        };
+        v_stack()
+            .flex_1()
+            .items_center()
+            .justify_between()
+            .w_full()
+            .child(Label::new(
+                "Enable Copilot by connecting your existing license",
             ))
-            .align_children_center()
-            .into_any()
-    }
-
-    fn render_enabled_modal(
-        style: &theme::Copilot,
-        cx: &mut ViewContext<Self>,
-    ) -> AnyElement<Self> {
-        enum DoneButton {}
-
-        let enabled_style = &style.auth.authorized;
-        Flex::column()
-            .with_child(
-                Label::new("Copilot Enabled!", enabled_style.subheading.text.clone())
-                    .contained()
-                    .with_style(enabled_style.subheading.container)
-                    .aligned(),
+            .child(Self::render_device_code(data, cx))
+            .child(
+                Label::new("Paste this code into GitHub after clicking the button below.")
+                    .size(ui::LabelSize::Small),
             )
-            .with_child(
-                Flex::column()
-                    .with_children([
-                        Label::new(
-                            "You can update your settings or",
-                            enabled_style.hint.text.clone(),
-                        )
-                        .aligned(),
-                        Label::new(
-                            "sign out from the Copilot menu in",
-                            enabled_style.hint.text.clone(),
-                        )
-                        .aligned(),
-                        Label::new("the status bar.", enabled_style.hint.text.clone()).aligned(),
-                    ])
-                    .align_children_center()
-                    .contained()
-                    .with_style(enabled_style.hint.container),
+            .child(
+                Button::new("connect-button", connect_button_label).on_click({
+                    let verification_uri = data.verification_uri.clone();
+                    cx.listener(move |this, _, cx| {
+                        cx.open_url(&verification_uri);
+                        this.connect_clicked = true;
+                    })
+                }),
             )
-            .with_child(theme::ui::cta_button::<DoneButton, _, _, _>(
-                "Done",
-                style.auth.content_width,
-                &style.auth.cta_button,
-                cx,
-                |_, _, cx| cx.remove_window(),
+    }
+    fn render_enabled_modal() -> impl Element {
+        v_stack()
+            .child(Label::new("Copilot Enabled!"))
+            .child(Label::new(
+                "You can update your settings or sign out from the Copilot menu in the status bar.",
             ))
-            .align_children_center()
-            .into_any()
+            .child(
+                Button::new("copilot-enabled-done-button", "Done")
+                    .on_click(|_, cx| cx.remove_window()),
+            )
     }
 
-    fn render_unauthorized_modal(
-        style: &theme::Copilot,
-        cx: &mut ViewContext<Self>,
-    ) -> AnyElement<Self> {
-        let unauthorized_style = &style.auth.not_authorized;
-
-        Flex::column()
-            .with_child(
-                Flex::column()
-                    .with_children([
-                        Label::new(
-                            "Enable Copilot by connecting",
-                            unauthorized_style.subheading.text.clone(),
-                        )
-                        .aligned(),
-                        Label::new(
-                            "your existing license.",
-                            unauthorized_style.subheading.text.clone(),
-                        )
-                        .aligned(),
-                    ])
-                    .align_children_center()
-                    .contained()
-                    .with_style(unauthorized_style.subheading.container),
-            )
-            .with_child(
-                Flex::column()
-                    .with_children([
-                        Label::new(
-                            "You must have an active copilot",
-                            unauthorized_style.warning.text.clone(),
-                        )
-                        .aligned(),
-                        Label::new(
-                            "license to use it in Zed.",
-                            unauthorized_style.warning.text.clone(),
-                        )
-                        .aligned(),
-                    ])
-                    .align_children_center()
-                    .contained()
-                    .with_style(unauthorized_style.warning.container),
+    fn render_unauthorized_modal() -> impl Element {
+        v_stack()
+            .child(Label::new(
+                "Enable Copilot by connecting your existing license.",
+            ))
+            .child(
+                Label::new("You must have an active Copilot license to use it in Zed.")
+                    .color(Color::Warning),
             )
-            .with_child(theme::ui::cta_button::<Self, _, _, _>(
-                "Subscribe on GitHub",
-                style.auth.content_width,
-                &style.auth.cta_button,
-                cx,
-                |_, _, cx| {
+            .child(
+                Button::new("copilot-subscribe-button", "Subscibe on Github").on_click(|_, cx| {
                     cx.remove_window();
-                    cx.platform().open_url(COPILOT_SIGN_UP_URL)
-                },
-            ))
-            .align_children_center()
-            .into_any()
+                    cx.open_url(COPILOT_SIGN_UP_URL)
+                }),
+            )
     }
 }
 
-impl Entity for CopilotCodeVerification {
-    type Event = ();
-}
-
-impl View for CopilotCodeVerification {
-    fn ui_name() -> &'static str {
-        "CopilotCodeVerification"
-    }
-
-    fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
-        cx.notify()
-    }
-
-    fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
-        cx.notify()
-    }
-
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
-        enum ConnectModal {}
-
-        let style = theme::current(cx).clone();
-
-        modal::<ConnectModal, _, _, _, _>(
-            "Connect Copilot to Zed",
-            &style.copilot.modal,
-            cx,
-            |cx| {
-                Flex::column()
-                    .with_children([
-                        theme::ui::icon(&style.copilot.auth.header).into_any(),
-                        match &self.status {
-                            Status::SigningIn {
-                                prompt: Some(prompt),
-                            } => Self::render_prompting_modal(
-                                self.connect_clicked,
-                                &prompt,
-                                &style.copilot,
-                                cx,
-                            ),
-                            Status::Unauthorized => {
-                                self.connect_clicked = false;
-                                Self::render_unauthorized_modal(&style.copilot, cx)
-                            }
-                            Status::Authorized => {
-                                self.connect_clicked = false;
-                                Self::render_enabled_modal(&style.copilot, cx)
-                            }
-                            _ => Empty::new().into_any(),
-                        },
-                    ])
-                    .align_children_center()
-            },
-        )
-        .into_any()
+impl Render for CopilotCodeVerification {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+        let prompt = match &self.status {
+            Status::SigningIn {
+                prompt: Some(prompt),
+            } => Self::render_prompting_modal(self.connect_clicked, &prompt, cx).into_any_element(),
+            Status::Unauthorized => {
+                self.connect_clicked = false;
+                Self::render_unauthorized_modal().into_any_element()
+            }
+            Status::Authorized => {
+                self.connect_clicked = false;
+                Self::render_enabled_modal().into_any_element()
+            }
+            _ => div().into_any_element(),
+        };
+        div()
+            .id("copilot code verification")
+            .flex()
+            .flex_col()
+            .size_full()
+            .items_center()
+            .p_10()
+            .bg(cx.theme().colors().element_background)
+            .child(ui::Label::new("Connect Copilot to Zed"))
+            .child(IconElement::new(Icon::ZedXCopilot))
+            .child(prompt)
     }
 }

crates/copilot2/Cargo.toml 🔗

@@ -1,51 +0,0 @@
-[package]
-name = "copilot2"
-version = "0.1.0"
-edition = "2021"
-publish = false
-
-[lib]
-path = "src/copilot2.rs"
-doctest = false
-
-[features]
-test-support = [
-    "collections/test-support",
-    "gpui/test-support",
-    "language/test-support",
-    "lsp/test-support",
-    "settings/test-support",
-    "util/test-support",
-]
-
-[dependencies]
-collections = { path = "../collections" }
-# context_menu = { path = "../context_menu" }
-gpui = { package = "gpui2", path = "../gpui2" }
-language = { package = "language2", path = "../language2" }
-settings = { package = "settings2", path = "../settings2" }
-theme = { package = "theme2", path = "../theme2" }
-lsp = { package = "lsp2", path = "../lsp2" }
-node_runtime = { path = "../node_runtime"}
-util = { path = "../util" }
-ui = { package = "ui2", path = "../ui2" }
-async-compression.workspace = true
-async-tar = "0.4.2"
-anyhow.workspace = true
-log.workspace = true
-serde.workspace = true
-serde_derive.workspace = true
-smol.workspace = true
-futures.workspace = true
-parking_lot.workspace = true
-
-[dev-dependencies]
-clock = { path = "../clock" }
-collections = { path = "../collections", features = ["test-support"] }
-fs = { path = "../fs", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
-language = { package = "language2", path = "../language2", features = ["test-support"] }
-lsp = { package = "lsp2", path = "../lsp2", features = ["test-support"] }
-rpc = { package = "rpc2", path = "../rpc2", features = ["test-support"] }
-settings = { package = "settings2", path = "../settings2", features = ["test-support"] }
-util = { path = "../util", features = ["test-support"] }

crates/copilot2/src/copilot2.rs 🔗

@@ -1,1253 +0,0 @@
-pub mod request;
-mod sign_in;
-
-use anyhow::{anyhow, Context as _, Result};
-use async_compression::futures::bufread::GzipDecoder;
-use async_tar::Archive;
-use collections::{HashMap, HashSet};
-use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt};
-use gpui::{
-    actions, AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Model,
-    ModelContext, Task, WeakModel,
-};
-use language::{
-    language_settings::{all_language_settings, language_settings},
-    point_from_lsp, point_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, Language,
-    LanguageServerName, PointUtf16, ToPointUtf16,
-};
-use lsp::{LanguageServer, LanguageServerBinary, LanguageServerId};
-use node_runtime::NodeRuntime;
-use parking_lot::Mutex;
-use request::StatusNotification;
-use settings::SettingsStore;
-use smol::{fs, io::BufReader, stream::StreamExt};
-use std::{
-    any::TypeId,
-    ffi::OsString,
-    mem,
-    ops::Range,
-    path::{Path, PathBuf},
-    sync::Arc,
-};
-use util::{
-    fs::remove_matching, github::latest_github_release, http::HttpClient, paths, ResultExt,
-};
-
-actions!(
-    copilot,
-    [
-        Suggest,
-        NextSuggestion,
-        PreviousSuggestion,
-        Reinstall,
-        SignIn,
-        SignOut
-    ]
-);
-
-pub fn init(
-    new_server_id: LanguageServerId,
-    http: Arc<dyn HttpClient>,
-    node_runtime: Arc<dyn NodeRuntime>,
-    cx: &mut AppContext,
-) {
-    let copilot = cx.new_model({
-        let node_runtime = node_runtime.clone();
-        move |cx| Copilot::start(new_server_id, http, node_runtime, cx)
-    });
-    cx.set_global(copilot.clone());
-    cx.observe(&copilot, |handle, cx| {
-        let copilot_action_types = [
-            TypeId::of::<Suggest>(),
-            TypeId::of::<NextSuggestion>(),
-            TypeId::of::<PreviousSuggestion>(),
-            TypeId::of::<Reinstall>(),
-        ];
-        let copilot_auth_action_types = [TypeId::of::<SignOut>()];
-        let copilot_no_auth_action_types = [TypeId::of::<SignIn>()];
-        let status = handle.read(cx).status();
-        let filter = cx.default_global::<collections::CommandPaletteFilter>();
-
-        match status {
-            Status::Disabled => {
-                filter.hidden_action_types.extend(copilot_action_types);
-                filter.hidden_action_types.extend(copilot_auth_action_types);
-                filter
-                    .hidden_action_types
-                    .extend(copilot_no_auth_action_types);
-            }
-            Status::Authorized => {
-                filter
-                    .hidden_action_types
-                    .extend(copilot_no_auth_action_types);
-                for type_id in copilot_action_types
-                    .iter()
-                    .chain(&copilot_auth_action_types)
-                {
-                    filter.hidden_action_types.remove(type_id);
-                }
-            }
-            _ => {
-                filter.hidden_action_types.extend(copilot_action_types);
-                filter.hidden_action_types.extend(copilot_auth_action_types);
-                for type_id in &copilot_no_auth_action_types {
-                    filter.hidden_action_types.remove(type_id);
-                }
-            }
-        }
-    })
-    .detach();
-
-    sign_in::init(cx);
-    cx.on_action(|_: &SignIn, cx| {
-        if let Some(copilot) = Copilot::global(cx) {
-            copilot
-                .update(cx, |copilot, cx| copilot.sign_in(cx))
-                .detach_and_log_err(cx);
-        }
-    });
-    cx.on_action(|_: &SignOut, cx| {
-        if let Some(copilot) = Copilot::global(cx) {
-            copilot
-                .update(cx, |copilot, cx| copilot.sign_out(cx))
-                .detach_and_log_err(cx);
-        }
-    });
-    cx.on_action(|_: &Reinstall, cx| {
-        if let Some(copilot) = Copilot::global(cx) {
-            copilot
-                .update(cx, |copilot, cx| copilot.reinstall(cx))
-                .detach();
-        }
-    });
-}
-
-enum CopilotServer {
-    Disabled,
-    Starting { task: Shared<Task<()>> },
-    Error(Arc<str>),
-    Running(RunningCopilotServer),
-}
-
-impl CopilotServer {
-    fn as_authenticated(&mut self) -> Result<&mut RunningCopilotServer> {
-        let server = self.as_running()?;
-        if matches!(server.sign_in_status, SignInStatus::Authorized { .. }) {
-            Ok(server)
-        } else {
-            Err(anyhow!("must sign in before using copilot"))
-        }
-    }
-
-    fn as_running(&mut self) -> Result<&mut RunningCopilotServer> {
-        match self {
-            CopilotServer::Starting { .. } => Err(anyhow!("copilot is still starting")),
-            CopilotServer::Disabled => Err(anyhow!("copilot is disabled")),
-            CopilotServer::Error(error) => Err(anyhow!(
-                "copilot was not started because of an error: {}",
-                error
-            )),
-            CopilotServer::Running(server) => Ok(server),
-        }
-    }
-}
-
-struct RunningCopilotServer {
-    name: LanguageServerName,
-    lsp: Arc<LanguageServer>,
-    sign_in_status: SignInStatus,
-    registered_buffers: HashMap<EntityId, RegisteredBuffer>,
-}
-
-#[derive(Clone, Debug)]
-enum SignInStatus {
-    Authorized,
-    Unauthorized,
-    SigningIn {
-        prompt: Option<request::PromptUserDeviceFlow>,
-        task: Shared<Task<Result<(), Arc<anyhow::Error>>>>,
-    },
-    SignedOut,
-}
-
-#[derive(Debug, Clone)]
-pub enum Status {
-    Starting {
-        task: Shared<Task<()>>,
-    },
-    Error(Arc<str>),
-    Disabled,
-    SignedOut,
-    SigningIn {
-        prompt: Option<request::PromptUserDeviceFlow>,
-    },
-    Unauthorized,
-    Authorized,
-}
-
-impl Status {
-    pub fn is_authorized(&self) -> bool {
-        matches!(self, Status::Authorized)
-    }
-}
-
-struct RegisteredBuffer {
-    uri: lsp::Url,
-    language_id: String,
-    snapshot: BufferSnapshot,
-    snapshot_version: i32,
-    _subscriptions: [gpui::Subscription; 2],
-    pending_buffer_change: Task<Option<()>>,
-}
-
-impl RegisteredBuffer {
-    fn report_changes(
-        &mut self,
-        buffer: &Model<Buffer>,
-        cx: &mut ModelContext<Copilot>,
-    ) -> oneshot::Receiver<(i32, BufferSnapshot)> {
-        let (done_tx, done_rx) = oneshot::channel();
-
-        if buffer.read(cx).version() == self.snapshot.version {
-            let _ = done_tx.send((self.snapshot_version, self.snapshot.clone()));
-        } else {
-            let buffer = buffer.downgrade();
-            let id = buffer.entity_id();
-            let prev_pending_change =
-                mem::replace(&mut self.pending_buffer_change, Task::ready(None));
-            self.pending_buffer_change = cx.spawn(move |copilot, mut cx| async move {
-                prev_pending_change.await;
-
-                let old_version = copilot
-                    .update(&mut cx, |copilot, _| {
-                        let server = copilot.server.as_authenticated().log_err()?;
-                        let buffer = server.registered_buffers.get_mut(&id)?;
-                        Some(buffer.snapshot.version.clone())
-                    })
-                    .ok()??;
-                let new_snapshot = buffer.update(&mut cx, |buffer, _| buffer.snapshot()).ok()?;
-
-                let content_changes = cx
-                    .background_executor()
-                    .spawn({
-                        let new_snapshot = new_snapshot.clone();
-                        async move {
-                            new_snapshot
-                                .edits_since::<(PointUtf16, usize)>(&old_version)
-                                .map(|edit| {
-                                    let edit_start = edit.new.start.0;
-                                    let edit_end = edit_start + (edit.old.end.0 - edit.old.start.0);
-                                    let new_text = new_snapshot
-                                        .text_for_range(edit.new.start.1..edit.new.end.1)
-                                        .collect();
-                                    lsp::TextDocumentContentChangeEvent {
-                                        range: Some(lsp::Range::new(
-                                            point_to_lsp(edit_start),
-                                            point_to_lsp(edit_end),
-                                        )),
-                                        range_length: None,
-                                        text: new_text,
-                                    }
-                                })
-                                .collect::<Vec<_>>()
-                        }
-                    })
-                    .await;
-
-                copilot
-                    .update(&mut cx, |copilot, _| {
-                        let server = copilot.server.as_authenticated().log_err()?;
-                        let buffer = server.registered_buffers.get_mut(&id)?;
-                        if !content_changes.is_empty() {
-                            buffer.snapshot_version += 1;
-                            buffer.snapshot = new_snapshot;
-                            server
-                                .lsp
-                                .notify::<lsp::notification::DidChangeTextDocument>(
-                                    lsp::DidChangeTextDocumentParams {
-                                        text_document: lsp::VersionedTextDocumentIdentifier::new(
-                                            buffer.uri.clone(),
-                                            buffer.snapshot_version,
-                                        ),
-                                        content_changes,
-                                    },
-                                )
-                                .log_err();
-                        }
-                        let _ = done_tx.send((buffer.snapshot_version, buffer.snapshot.clone()));
-                        Some(())
-                    })
-                    .ok()?;
-
-                Some(())
-            });
-        }
-
-        done_rx
-    }
-}
-
-#[derive(Debug)]
-pub struct Completion {
-    pub uuid: String,
-    pub range: Range<Anchor>,
-    pub text: String,
-}
-
-pub struct Copilot {
-    http: Arc<dyn HttpClient>,
-    node_runtime: Arc<dyn NodeRuntime>,
-    server: CopilotServer,
-    buffers: HashSet<WeakModel<Buffer>>,
-    server_id: LanguageServerId,
-    _subscription: gpui::Subscription,
-}
-
-pub enum Event {
-    CopilotLanguageServerStarted,
-}
-
-impl EventEmitter<Event> for Copilot {}
-
-impl Copilot {
-    pub fn global(cx: &AppContext) -> Option<Model<Self>> {
-        if cx.has_global::<Model<Self>>() {
-            Some(cx.global::<Model<Self>>().clone())
-        } else {
-            None
-        }
-    }
-
-    fn start(
-        new_server_id: LanguageServerId,
-        http: Arc<dyn HttpClient>,
-        node_runtime: Arc<dyn NodeRuntime>,
-        cx: &mut ModelContext<Self>,
-    ) -> Self {
-        let mut this = Self {
-            server_id: new_server_id,
-            http,
-            node_runtime,
-            server: CopilotServer::Disabled,
-            buffers: Default::default(),
-            _subscription: cx.on_app_quit(Self::shutdown_language_server),
-        };
-        this.enable_or_disable_copilot(cx);
-        cx.observe_global::<SettingsStore>(move |this, cx| this.enable_or_disable_copilot(cx))
-            .detach();
-        this
-    }
-
-    fn shutdown_language_server(
-        &mut self,
-        _cx: &mut ModelContext<Self>,
-    ) -> impl Future<Output = ()> {
-        let shutdown = match mem::replace(&mut self.server, CopilotServer::Disabled) {
-            CopilotServer::Running(server) => Some(Box::pin(async move { server.lsp.shutdown() })),
-            _ => None,
-        };
-
-        async move {
-            if let Some(shutdown) = shutdown {
-                shutdown.await;
-            }
-        }
-    }
-
-    fn enable_or_disable_copilot(&mut self, cx: &mut ModelContext<Self>) {
-        let server_id = self.server_id;
-        let http = self.http.clone();
-        let node_runtime = self.node_runtime.clone();
-        if all_language_settings(None, cx).copilot_enabled(None, None) {
-            if matches!(self.server, CopilotServer::Disabled) {
-                let start_task = cx
-                    .spawn(move |this, cx| {
-                        Self::start_language_server(server_id, http, node_runtime, this, cx)
-                    })
-                    .shared();
-                self.server = CopilotServer::Starting { task: start_task };
-                cx.notify();
-            }
-        } else {
-            self.server = CopilotServer::Disabled;
-            cx.notify();
-        }
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn fake(cx: &mut gpui::TestAppContext) -> (Model<Self>, lsp::FakeLanguageServer) {
-        use node_runtime::FakeNodeRuntime;
-
-        let (server, fake_server) =
-            LanguageServer::fake("copilot".into(), Default::default(), cx.to_async());
-        let http = util::http::FakeHttpClient::create(|_| async { unreachable!() });
-        let node_runtime = FakeNodeRuntime::new();
-        let this = cx.new_model(|cx| Self {
-            server_id: LanguageServerId(0),
-            http: http.clone(),
-            node_runtime,
-            server: CopilotServer::Running(RunningCopilotServer {
-                name: LanguageServerName(Arc::from("copilot")),
-                lsp: Arc::new(server),
-                sign_in_status: SignInStatus::Authorized,
-                registered_buffers: Default::default(),
-            }),
-            _subscription: cx.on_app_quit(Self::shutdown_language_server),
-            buffers: Default::default(),
-        });
-        (this, fake_server)
-    }
-
-    fn start_language_server(
-        new_server_id: LanguageServerId,
-        http: Arc<dyn HttpClient>,
-        node_runtime: Arc<dyn NodeRuntime>,
-        this: WeakModel<Self>,
-        mut cx: AsyncAppContext,
-    ) -> impl Future<Output = ()> {
-        async move {
-            let start_language_server = async {
-                let server_path = get_copilot_lsp(http).await?;
-                let node_path = node_runtime.binary_path().await?;
-                let arguments: Vec<OsString> = vec![server_path.into(), "--stdio".into()];
-                let binary = LanguageServerBinary {
-                    path: node_path,
-                    arguments,
-                };
-
-                let server = LanguageServer::new(
-                    Arc::new(Mutex::new(None)),
-                    new_server_id,
-                    binary,
-                    Path::new("/"),
-                    None,
-                    cx.clone(),
-                )?;
-
-                server
-                    .on_notification::<StatusNotification, _>(
-                        |_, _| { /* Silence the notification */ },
-                    )
-                    .detach();
-
-                let server = server.initialize(Default::default()).await?;
-
-                let status = server
-                    .request::<request::CheckStatus>(request::CheckStatusParams {
-                        local_checks_only: false,
-                    })
-                    .await?;
-
-                server
-                    .request::<request::SetEditorInfo>(request::SetEditorInfoParams {
-                        editor_info: request::EditorInfo {
-                            name: "zed".into(),
-                            version: env!("CARGO_PKG_VERSION").into(),
-                        },
-                        editor_plugin_info: request::EditorPluginInfo {
-                            name: "zed-copilot".into(),
-                            version: "0.0.1".into(),
-                        },
-                    })
-                    .await?;
-
-                anyhow::Ok((server, status))
-            };
-
-            let server = start_language_server.await;
-            this.update(&mut cx, |this, cx| {
-                cx.notify();
-                match server {
-                    Ok((server, status)) => {
-                        this.server = CopilotServer::Running(RunningCopilotServer {
-                            name: LanguageServerName(Arc::from("copilot")),
-                            lsp: server,
-                            sign_in_status: SignInStatus::SignedOut,
-                            registered_buffers: Default::default(),
-                        });
-                        cx.emit(Event::CopilotLanguageServerStarted);
-                        this.update_sign_in_status(status, cx);
-                    }
-                    Err(error) => {
-                        this.server = CopilotServer::Error(error.to_string().into());
-                        cx.notify()
-                    }
-                }
-            })
-            .ok();
-        }
-    }
-
-    pub fn sign_in(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
-        if let CopilotServer::Running(server) = &mut self.server {
-            let task = match &server.sign_in_status {
-                SignInStatus::Authorized { .. } => Task::ready(Ok(())).shared(),
-                SignInStatus::SigningIn { task, .. } => {
-                    cx.notify();
-                    task.clone()
-                }
-                SignInStatus::SignedOut | SignInStatus::Unauthorized { .. } => {
-                    let lsp = server.lsp.clone();
-                    let task = cx
-                        .spawn(|this, mut cx| async move {
-                            let sign_in = async {
-                                let sign_in = lsp
-                                    .request::<request::SignInInitiate>(
-                                        request::SignInInitiateParams {},
-                                    )
-                                    .await?;
-                                match sign_in {
-                                    request::SignInInitiateResult::AlreadySignedIn { user } => {
-                                        Ok(request::SignInStatus::Ok { user })
-                                    }
-                                    request::SignInInitiateResult::PromptUserDeviceFlow(flow) => {
-                                        this.update(&mut cx, |this, cx| {
-                                            if let CopilotServer::Running(RunningCopilotServer {
-                                                sign_in_status: status,
-                                                ..
-                                            }) = &mut this.server
-                                            {
-                                                if let SignInStatus::SigningIn {
-                                                    prompt: prompt_flow,
-                                                    ..
-                                                } = status
-                                                {
-                                                    *prompt_flow = Some(flow.clone());
-                                                    cx.notify();
-                                                }
-                                            }
-                                        })?;
-                                        let response = lsp
-                                            .request::<request::SignInConfirm>(
-                                                request::SignInConfirmParams {
-                                                    user_code: flow.user_code,
-                                                },
-                                            )
-                                            .await?;
-                                        Ok(response)
-                                    }
-                                }
-                            };
-
-                            let sign_in = sign_in.await;
-                            this.update(&mut cx, |this, cx| match sign_in {
-                                Ok(status) => {
-                                    this.update_sign_in_status(status, cx);
-                                    Ok(())
-                                }
-                                Err(error) => {
-                                    this.update_sign_in_status(
-                                        request::SignInStatus::NotSignedIn,
-                                        cx,
-                                    );
-                                    Err(Arc::new(error))
-                                }
-                            })?
-                        })
-                        .shared();
-                    server.sign_in_status = SignInStatus::SigningIn {
-                        prompt: None,
-                        task: task.clone(),
-                    };
-                    cx.notify();
-                    task
-                }
-            };
-
-            cx.background_executor()
-                .spawn(task.map_err(|err| anyhow!("{:?}", err)))
-        } else {
-            // If we're downloading, wait until download is finished
-            // If we're in a stuck state, display to the user
-            Task::ready(Err(anyhow!("copilot hasn't started yet")))
-        }
-    }
-
-    fn sign_out(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
-        self.update_sign_in_status(request::SignInStatus::NotSignedIn, cx);
-        if let CopilotServer::Running(RunningCopilotServer { lsp: server, .. }) = &self.server {
-            let server = server.clone();
-            cx.background_executor().spawn(async move {
-                server
-                    .request::<request::SignOut>(request::SignOutParams {})
-                    .await?;
-                anyhow::Ok(())
-            })
-        } else {
-            Task::ready(Err(anyhow!("copilot hasn't started yet")))
-        }
-    }
-
-    pub fn reinstall(&mut self, cx: &mut ModelContext<Self>) -> Task<()> {
-        let start_task = cx
-            .spawn({
-                let http = self.http.clone();
-                let node_runtime = self.node_runtime.clone();
-                let server_id = self.server_id;
-                move |this, cx| async move {
-                    clear_copilot_dir().await;
-                    Self::start_language_server(server_id, http, node_runtime, this, cx).await
-                }
-            })
-            .shared();
-
-        self.server = CopilotServer::Starting {
-            task: start_task.clone(),
-        };
-
-        cx.notify();
-
-        cx.background_executor().spawn(start_task)
-    }
-
-    pub fn language_server(&self) -> Option<(&LanguageServerName, &Arc<LanguageServer>)> {
-        if let CopilotServer::Running(server) = &self.server {
-            Some((&server.name, &server.lsp))
-        } else {
-            None
-        }
-    }
-
-    pub fn register_buffer(&mut self, buffer: &Model<Buffer>, cx: &mut ModelContext<Self>) {
-        let weak_buffer = buffer.downgrade();
-        self.buffers.insert(weak_buffer.clone());
-
-        if let CopilotServer::Running(RunningCopilotServer {
-            lsp: server,
-            sign_in_status: status,
-            registered_buffers,
-            ..
-        }) = &mut self.server
-        {
-            if !matches!(status, SignInStatus::Authorized { .. }) {
-                return;
-            }
-
-            registered_buffers
-                .entry(buffer.entity_id())
-                .or_insert_with(|| {
-                    let uri: lsp::Url = uri_for_buffer(buffer, cx);
-                    let language_id = id_for_language(buffer.read(cx).language());
-                    let snapshot = buffer.read(cx).snapshot();
-                    server
-                        .notify::<lsp::notification::DidOpenTextDocument>(
-                            lsp::DidOpenTextDocumentParams {
-                                text_document: lsp::TextDocumentItem {
-                                    uri: uri.clone(),
-                                    language_id: language_id.clone(),
-                                    version: 0,
-                                    text: snapshot.text(),
-                                },
-                            },
-                        )
-                        .log_err();
-
-                    RegisteredBuffer {
-                        uri,
-                        language_id,
-                        snapshot,
-                        snapshot_version: 0,
-                        pending_buffer_change: Task::ready(Some(())),
-                        _subscriptions: [
-                            cx.subscribe(buffer, |this, buffer, event, cx| {
-                                this.handle_buffer_event(buffer, event, cx).log_err();
-                            }),
-                            cx.observe_release(buffer, move |this, _buffer, _cx| {
-                                this.buffers.remove(&weak_buffer);
-                                this.unregister_buffer(&weak_buffer);
-                            }),
-                        ],
-                    }
-                });
-        }
-    }
-
-    fn handle_buffer_event(
-        &mut self,
-        buffer: Model<Buffer>,
-        event: &language::Event,
-        cx: &mut ModelContext<Self>,
-    ) -> Result<()> {
-        if let Ok(server) = self.server.as_running() {
-            if let Some(registered_buffer) = server.registered_buffers.get_mut(&buffer.entity_id())
-            {
-                match event {
-                    language::Event::Edited => {
-                        let _ = registered_buffer.report_changes(&buffer, cx);
-                    }
-                    language::Event::Saved => {
-                        server
-                            .lsp
-                            .notify::<lsp::notification::DidSaveTextDocument>(
-                                lsp::DidSaveTextDocumentParams {
-                                    text_document: lsp::TextDocumentIdentifier::new(
-                                        registered_buffer.uri.clone(),
-                                    ),
-                                    text: None,
-                                },
-                            )?;
-                    }
-                    language::Event::FileHandleChanged | language::Event::LanguageChanged => {
-                        let new_language_id = id_for_language(buffer.read(cx).language());
-                        let new_uri = uri_for_buffer(&buffer, cx);
-                        if new_uri != registered_buffer.uri
-                            || new_language_id != registered_buffer.language_id
-                        {
-                            let old_uri = mem::replace(&mut registered_buffer.uri, new_uri);
-                            registered_buffer.language_id = new_language_id;
-                            server
-                                .lsp
-                                .notify::<lsp::notification::DidCloseTextDocument>(
-                                    lsp::DidCloseTextDocumentParams {
-                                        text_document: lsp::TextDocumentIdentifier::new(old_uri),
-                                    },
-                                )?;
-                            server
-                                .lsp
-                                .notify::<lsp::notification::DidOpenTextDocument>(
-                                    lsp::DidOpenTextDocumentParams {
-                                        text_document: lsp::TextDocumentItem::new(
-                                            registered_buffer.uri.clone(),
-                                            registered_buffer.language_id.clone(),
-                                            registered_buffer.snapshot_version,
-                                            registered_buffer.snapshot.text(),
-                                        ),
-                                    },
-                                )?;
-                        }
-                    }
-                    _ => {}
-                }
-            }
-        }
-
-        Ok(())
-    }
-
-    fn unregister_buffer(&mut self, buffer: &WeakModel<Buffer>) {
-        if let Ok(server) = self.server.as_running() {
-            if let Some(buffer) = server.registered_buffers.remove(&buffer.entity_id()) {
-                server
-                    .lsp
-                    .notify::<lsp::notification::DidCloseTextDocument>(
-                        lsp::DidCloseTextDocumentParams {
-                            text_document: lsp::TextDocumentIdentifier::new(buffer.uri),
-                        },
-                    )
-                    .log_err();
-            }
-        }
-    }
-
-    pub fn completions<T>(
-        &mut self,
-        buffer: &Model<Buffer>,
-        position: T,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Vec<Completion>>>
-    where
-        T: ToPointUtf16,
-    {
-        self.request_completions::<request::GetCompletions, _>(buffer, position, cx)
-    }
-
-    pub fn completions_cycling<T>(
-        &mut self,
-        buffer: &Model<Buffer>,
-        position: T,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Vec<Completion>>>
-    where
-        T: ToPointUtf16,
-    {
-        self.request_completions::<request::GetCompletionsCycling, _>(buffer, position, cx)
-    }
-
-    pub fn accept_completion(
-        &mut self,
-        completion: &Completion,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<()>> {
-        let server = match self.server.as_authenticated() {
-            Ok(server) => server,
-            Err(error) => return Task::ready(Err(error)),
-        };
-        let request =
-            server
-                .lsp
-                .request::<request::NotifyAccepted>(request::NotifyAcceptedParams {
-                    uuid: completion.uuid.clone(),
-                });
-        cx.background_executor().spawn(async move {
-            request.await?;
-            Ok(())
-        })
-    }
-
-    pub fn discard_completions(
-        &mut self,
-        completions: &[Completion],
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<()>> {
-        let server = match self.server.as_authenticated() {
-            Ok(server) => server,
-            Err(error) => return Task::ready(Err(error)),
-        };
-        let request =
-            server
-                .lsp
-                .request::<request::NotifyRejected>(request::NotifyRejectedParams {
-                    uuids: completions
-                        .iter()
-                        .map(|completion| completion.uuid.clone())
-                        .collect(),
-                });
-        cx.background_executor().spawn(async move {
-            request.await?;
-            Ok(())
-        })
-    }
-
-    fn request_completions<R, T>(
-        &mut self,
-        buffer: &Model<Buffer>,
-        position: T,
-        cx: &mut ModelContext<Self>,
-    ) -> Task<Result<Vec<Completion>>>
-    where
-        R: 'static
-            + lsp::request::Request<
-                Params = request::GetCompletionsParams,
-                Result = request::GetCompletionsResult,
-            >,
-        T: ToPointUtf16,
-    {
-        self.register_buffer(buffer, cx);
-
-        let server = match self.server.as_authenticated() {
-            Ok(server) => server,
-            Err(error) => return Task::ready(Err(error)),
-        };
-        let lsp = server.lsp.clone();
-        let registered_buffer = server
-            .registered_buffers
-            .get_mut(&buffer.entity_id())
-            .unwrap();
-        let snapshot = registered_buffer.report_changes(buffer, cx);
-        let buffer = buffer.read(cx);
-        let uri = registered_buffer.uri.clone();
-        let position = position.to_point_utf16(buffer);
-        let settings = language_settings(buffer.language_at(position).as_ref(), buffer.file(), cx);
-        let tab_size = settings.tab_size;
-        let hard_tabs = settings.hard_tabs;
-        let relative_path = buffer
-            .file()
-            .map(|file| file.path().to_path_buf())
-            .unwrap_or_default();
-
-        cx.background_executor().spawn(async move {
-            let (version, snapshot) = snapshot.await?;
-            let result = lsp
-                .request::<R>(request::GetCompletionsParams {
-                    doc: request::GetCompletionsDocument {
-                        uri,
-                        tab_size: tab_size.into(),
-                        indent_size: 1,
-                        insert_spaces: !hard_tabs,
-                        relative_path: relative_path.to_string_lossy().into(),
-                        position: point_to_lsp(position),
-                        version: version.try_into().unwrap(),
-                    },
-                })
-                .await?;
-            let completions = result
-                .completions
-                .into_iter()
-                .map(|completion| {
-                    let start = snapshot
-                        .clip_point_utf16(point_from_lsp(completion.range.start), Bias::Left);
-                    let end =
-                        snapshot.clip_point_utf16(point_from_lsp(completion.range.end), Bias::Left);
-                    Completion {
-                        uuid: completion.uuid,
-                        range: snapshot.anchor_before(start)..snapshot.anchor_after(end),
-                        text: completion.text,
-                    }
-                })
-                .collect();
-            anyhow::Ok(completions)
-        })
-    }
-
-    pub fn status(&self) -> Status {
-        match &self.server {
-            CopilotServer::Starting { task } => Status::Starting { task: task.clone() },
-            CopilotServer::Disabled => Status::Disabled,
-            CopilotServer::Error(error) => Status::Error(error.clone()),
-            CopilotServer::Running(RunningCopilotServer { sign_in_status, .. }) => {
-                match sign_in_status {
-                    SignInStatus::Authorized { .. } => Status::Authorized,
-                    SignInStatus::Unauthorized { .. } => Status::Unauthorized,
-                    SignInStatus::SigningIn { prompt, .. } => Status::SigningIn {
-                        prompt: prompt.clone(),
-                    },
-                    SignInStatus::SignedOut => Status::SignedOut,
-                }
-            }
-        }
-    }
-
-    fn update_sign_in_status(
-        &mut self,
-        lsp_status: request::SignInStatus,
-        cx: &mut ModelContext<Self>,
-    ) {
-        self.buffers.retain(|buffer| buffer.is_upgradable());
-
-        if let Ok(server) = self.server.as_running() {
-            match lsp_status {
-                request::SignInStatus::Ok { .. }
-                | request::SignInStatus::MaybeOk { .. }
-                | request::SignInStatus::AlreadySignedIn { .. } => {
-                    server.sign_in_status = SignInStatus::Authorized;
-                    for buffer in self.buffers.iter().cloned().collect::<Vec<_>>() {
-                        if let Some(buffer) = buffer.upgrade() {
-                            self.register_buffer(&buffer, cx);
-                        }
-                    }
-                }
-                request::SignInStatus::NotAuthorized { .. } => {
-                    server.sign_in_status = SignInStatus::Unauthorized;
-                    for buffer in self.buffers.iter().cloned().collect::<Vec<_>>() {
-                        self.unregister_buffer(&buffer);
-                    }
-                }
-                request::SignInStatus::NotSignedIn => {
-                    server.sign_in_status = SignInStatus::SignedOut;
-                    for buffer in self.buffers.iter().cloned().collect::<Vec<_>>() {
-                        self.unregister_buffer(&buffer);
-                    }
-                }
-            }
-
-            cx.notify();
-        }
-    }
-}
-
-fn id_for_language(language: Option<&Arc<Language>>) -> String {
-    let language_name = language.map(|language| language.name());
-    match language_name.as_deref() {
-        Some("Plain Text") => "plaintext".to_string(),
-        Some(language_name) => language_name.to_lowercase(),
-        None => "plaintext".to_string(),
-    }
-}
-
-fn uri_for_buffer(buffer: &Model<Buffer>, cx: &AppContext) -> lsp::Url {
-    if let Some(file) = buffer.read(cx).file().and_then(|file| file.as_local()) {
-        lsp::Url::from_file_path(file.abs_path(cx)).unwrap()
-    } else {
-        format!("buffer://{}", buffer.entity_id()).parse().unwrap()
-    }
-}
-
-async fn clear_copilot_dir() {
-    remove_matching(&paths::COPILOT_DIR, |_| true).await
-}
-
-async fn get_copilot_lsp(http: Arc<dyn HttpClient>) -> anyhow::Result<PathBuf> {
-    const SERVER_PATH: &'static str = "dist/agent.js";
-
-    ///Check for the latest copilot language server and download it if we haven't already
-    async fn fetch_latest(http: Arc<dyn HttpClient>) -> anyhow::Result<PathBuf> {
-        let release = latest_github_release("zed-industries/copilot", false, http.clone()).await?;
-
-        let version_dir = &*paths::COPILOT_DIR.join(format!("copilot-{}", release.name));
-
-        fs::create_dir_all(version_dir).await?;
-        let server_path = version_dir.join(SERVER_PATH);
-
-        if fs::metadata(&server_path).await.is_err() {
-            // Copilot LSP looks for this dist dir specifcially, so lets add it in.
-            let dist_dir = version_dir.join("dist");
-            fs::create_dir_all(dist_dir.as_path()).await?;
-
-            let url = &release
-                .assets
-                .get(0)
-                .context("Github release for copilot contained no assets")?
-                .browser_download_url;
-
-            let mut response = http
-                .get(&url, Default::default(), true)
-                .await
-                .map_err(|err| anyhow!("error downloading copilot release: {}", err))?;
-            let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut()));
-            let archive = Archive::new(decompressed_bytes);
-            archive.unpack(dist_dir).await?;
-
-            remove_matching(&paths::COPILOT_DIR, |entry| entry != version_dir).await;
-        }
-
-        Ok(server_path)
-    }
-
-    match fetch_latest(http).await {
-        ok @ Result::Ok(..) => ok,
-        e @ Err(..) => {
-            e.log_err();
-            // Fetch a cached binary, if it exists
-            (|| async move {
-                let mut last_version_dir = None;
-                let mut entries = fs::read_dir(paths::COPILOT_DIR.as_path()).await?;
-                while let Some(entry) = entries.next().await {
-                    let entry = entry?;
-                    if entry.file_type().await?.is_dir() {
-                        last_version_dir = Some(entry.path());
-                    }
-                }
-                let last_version_dir =
-                    last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?;
-                let server_path = last_version_dir.join(SERVER_PATH);
-                if server_path.exists() {
-                    Ok(server_path)
-                } else {
-                    Err(anyhow!(
-                        "missing executable in directory {:?}",
-                        last_version_dir
-                    ))
-                }
-            })()
-            .await
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use gpui::TestAppContext;
-
-    #[gpui::test(iterations = 10)]
-    async fn test_buffer_management(cx: &mut TestAppContext) {
-        let (copilot, mut lsp) = Copilot::fake(cx);
-
-        let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "Hello"));
-        let buffer_1_uri: lsp::Url = format!("buffer://{}", buffer_1.entity_id().as_u64())
-            .parse()
-            .unwrap();
-        copilot.update(cx, |copilot, cx| copilot.register_buffer(&buffer_1, cx));
-        assert_eq!(
-            lsp.receive_notification::<lsp::notification::DidOpenTextDocument>()
-                .await,
-            lsp::DidOpenTextDocumentParams {
-                text_document: lsp::TextDocumentItem::new(
-                    buffer_1_uri.clone(),
-                    "plaintext".into(),
-                    0,
-                    "Hello".into()
-                ),
-            }
-        );
-
-        let buffer_2 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "Goodbye"));
-        let buffer_2_uri: lsp::Url = format!("buffer://{}", buffer_2.entity_id().as_u64())
-            .parse()
-            .unwrap();
-        copilot.update(cx, |copilot, cx| copilot.register_buffer(&buffer_2, cx));
-        assert_eq!(
-            lsp.receive_notification::<lsp::notification::DidOpenTextDocument>()
-                .await,
-            lsp::DidOpenTextDocumentParams {
-                text_document: lsp::TextDocumentItem::new(
-                    buffer_2_uri.clone(),
-                    "plaintext".into(),
-                    0,
-                    "Goodbye".into()
-                ),
-            }
-        );
-
-        buffer_1.update(cx, |buffer, cx| buffer.edit([(5..5, " world")], None, cx));
-        assert_eq!(
-            lsp.receive_notification::<lsp::notification::DidChangeTextDocument>()
-                .await,
-            lsp::DidChangeTextDocumentParams {
-                text_document: lsp::VersionedTextDocumentIdentifier::new(buffer_1_uri.clone(), 1),
-                content_changes: vec![lsp::TextDocumentContentChangeEvent {
-                    range: Some(lsp::Range::new(
-                        lsp::Position::new(0, 5),
-                        lsp::Position::new(0, 5)
-                    )),
-                    range_length: None,
-                    text: " world".into(),
-                }],
-            }
-        );
-
-        // Ensure updates to the file are reflected in the LSP.
-        buffer_1.update(cx, |buffer, cx| {
-            buffer.file_updated(
-                Arc::new(File {
-                    abs_path: "/root/child/buffer-1".into(),
-                    path: Path::new("child/buffer-1").into(),
-                }),
-                cx,
-            )
-        });
-        assert_eq!(
-            lsp.receive_notification::<lsp::notification::DidCloseTextDocument>()
-                .await,
-            lsp::DidCloseTextDocumentParams {
-                text_document: lsp::TextDocumentIdentifier::new(buffer_1_uri),
-            }
-        );
-        let buffer_1_uri = lsp::Url::from_file_path("/root/child/buffer-1").unwrap();
-        assert_eq!(
-            lsp.receive_notification::<lsp::notification::DidOpenTextDocument>()
-                .await,
-            lsp::DidOpenTextDocumentParams {
-                text_document: lsp::TextDocumentItem::new(
-                    buffer_1_uri.clone(),
-                    "plaintext".into(),
-                    1,
-                    "Hello world".into()
-                ),
-            }
-        );
-
-        // Ensure all previously-registered buffers are closed when signing out.
-        lsp.handle_request::<request::SignOut, _, _>(|_, _| async {
-            Ok(request::SignOutResult {})
-        });
-        copilot
-            .update(cx, |copilot, cx| copilot.sign_out(cx))
-            .await
-            .unwrap();
-        assert_eq!(
-            lsp.receive_notification::<lsp::notification::DidCloseTextDocument>()
-                .await,
-            lsp::DidCloseTextDocumentParams {
-                text_document: lsp::TextDocumentIdentifier::new(buffer_1_uri.clone()),
-            }
-        );
-        assert_eq!(
-            lsp.receive_notification::<lsp::notification::DidCloseTextDocument>()
-                .await,
-            lsp::DidCloseTextDocumentParams {
-                text_document: lsp::TextDocumentIdentifier::new(buffer_2_uri.clone()),
-            }
-        );
-
-        // Ensure all previously-registered buffers are re-opened when signing in.
-        lsp.handle_request::<request::SignInInitiate, _, _>(|_, _| async {
-            Ok(request::SignInInitiateResult::AlreadySignedIn {
-                user: "user-1".into(),
-            })
-        });
-        copilot
-            .update(cx, |copilot, cx| copilot.sign_in(cx))
-            .await
-            .unwrap();
-
-        assert_eq!(
-            lsp.receive_notification::<lsp::notification::DidOpenTextDocument>()
-                .await,
-            lsp::DidOpenTextDocumentParams {
-                text_document: lsp::TextDocumentItem::new(
-                    buffer_1_uri.clone(),
-                    "plaintext".into(),
-                    0,
-                    "Hello world".into()
-                ),
-            }
-        );
-        assert_eq!(
-            lsp.receive_notification::<lsp::notification::DidOpenTextDocument>()
-                .await,
-            lsp::DidOpenTextDocumentParams {
-                text_document: lsp::TextDocumentItem::new(
-                    buffer_2_uri.clone(),
-                    "plaintext".into(),
-                    0,
-                    "Goodbye".into()
-                ),
-            }
-        );
-        // Dropping a buffer causes it to be closed on the LSP side as well.
-        cx.update(|_| drop(buffer_2));
-        assert_eq!(
-            lsp.receive_notification::<lsp::notification::DidCloseTextDocument>()
-                .await,
-            lsp::DidCloseTextDocumentParams {
-                text_document: lsp::TextDocumentIdentifier::new(buffer_2_uri),
-            }
-        );
-    }
-
-    struct File {
-        abs_path: PathBuf,
-        path: Arc<Path>,
-    }
-
-    impl language::File for File {
-        fn as_local(&self) -> Option<&dyn language::LocalFile> {
-            Some(self)
-        }
-
-        fn mtime(&self) -> std::time::SystemTime {
-            unimplemented!()
-        }
-
-        fn path(&self) -> &Arc<Path> {
-            &self.path
-        }
-
-        fn full_path(&self, _: &AppContext) -> PathBuf {
-            unimplemented!()
-        }
-
-        fn file_name<'a>(&'a self, _: &'a AppContext) -> &'a std::ffi::OsStr {
-            unimplemented!()
-        }
-
-        fn is_deleted(&self) -> bool {
-            unimplemented!()
-        }
-
-        fn as_any(&self) -> &dyn std::any::Any {
-            unimplemented!()
-        }
-
-        fn to_proto(&self) -> rpc::proto::File {
-            unimplemented!()
-        }
-
-        fn worktree_id(&self) -> usize {
-            0
-        }
-    }
-
-    impl language::LocalFile for File {
-        fn abs_path(&self, _: &AppContext) -> PathBuf {
-            self.abs_path.clone()
-        }
-
-        fn load(&self, _: &AppContext) -> Task<Result<String>> {
-            unimplemented!()
-        }
-
-        fn buffer_reloaded(
-            &self,
-            _: u64,
-            _: &clock::Global,
-            _: language::RopeFingerprint,
-            _: language::LineEnding,
-            _: std::time::SystemTime,
-            _: &mut AppContext,
-        ) {
-            unimplemented!()
-        }
-    }
-}

crates/copilot2/src/request.rs 🔗

@@ -1,225 +0,0 @@
-use serde::{Deserialize, Serialize};
-
-pub enum CheckStatus {}
-
-#[derive(Debug, Serialize, Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct CheckStatusParams {
-    pub local_checks_only: bool,
-}
-
-impl lsp::request::Request for CheckStatus {
-    type Params = CheckStatusParams;
-    type Result = SignInStatus;
-    const METHOD: &'static str = "checkStatus";
-}
-
-pub enum SignInInitiate {}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct SignInInitiateParams {}
-
-#[derive(Debug, Serialize, Deserialize)]
-#[serde(tag = "status")]
-pub enum SignInInitiateResult {
-    AlreadySignedIn { user: String },
-    PromptUserDeviceFlow(PromptUserDeviceFlow),
-}
-
-#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct PromptUserDeviceFlow {
-    pub user_code: String,
-    pub verification_uri: String,
-}
-
-impl lsp::request::Request for SignInInitiate {
-    type Params = SignInInitiateParams;
-    type Result = SignInInitiateResult;
-    const METHOD: &'static str = "signInInitiate";
-}
-
-pub enum SignInConfirm {}
-
-#[derive(Debug, Serialize, Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct SignInConfirmParams {
-    pub user_code: String,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-#[serde(tag = "status")]
-pub enum SignInStatus {
-    #[serde(rename = "OK")]
-    Ok {
-        user: String,
-    },
-    MaybeOk {
-        user: String,
-    },
-    AlreadySignedIn {
-        user: String,
-    },
-    NotAuthorized {
-        user: String,
-    },
-    NotSignedIn,
-}
-
-impl lsp::request::Request for SignInConfirm {
-    type Params = SignInConfirmParams;
-    type Result = SignInStatus;
-    const METHOD: &'static str = "signInConfirm";
-}
-
-pub enum SignOut {}
-
-#[derive(Debug, Serialize, Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct SignOutParams {}
-
-#[derive(Debug, Serialize, Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct SignOutResult {}
-
-impl lsp::request::Request for SignOut {
-    type Params = SignOutParams;
-    type Result = SignOutResult;
-    const METHOD: &'static str = "signOut";
-}
-
-pub enum GetCompletions {}
-
-#[derive(Debug, Serialize, Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct GetCompletionsParams {
-    pub doc: GetCompletionsDocument,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct GetCompletionsDocument {
-    pub tab_size: u32,
-    pub indent_size: u32,
-    pub insert_spaces: bool,
-    pub uri: lsp::Url,
-    pub relative_path: String,
-    pub position: lsp::Position,
-    pub version: usize,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct GetCompletionsResult {
-    pub completions: Vec<Completion>,
-}
-
-#[derive(Clone, Debug, Default, Serialize, Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct Completion {
-    pub text: String,
-    pub position: lsp::Position,
-    pub uuid: String,
-    pub range: lsp::Range,
-    pub display_text: String,
-}
-
-impl lsp::request::Request for GetCompletions {
-    type Params = GetCompletionsParams;
-    type Result = GetCompletionsResult;
-    const METHOD: &'static str = "getCompletions";
-}
-
-pub enum GetCompletionsCycling {}
-
-impl lsp::request::Request for GetCompletionsCycling {
-    type Params = GetCompletionsParams;
-    type Result = GetCompletionsResult;
-    const METHOD: &'static str = "getCompletionsCycling";
-}
-
-pub enum LogMessage {}
-
-#[derive(Debug, Serialize, Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct LogMessageParams {
-    pub level: u8,
-    pub message: String,
-    pub metadata_str: String,
-    pub extra: Vec<String>,
-}
-
-impl lsp::notification::Notification for LogMessage {
-    type Params = LogMessageParams;
-    const METHOD: &'static str = "LogMessage";
-}
-
-pub enum StatusNotification {}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct StatusNotificationParams {
-    pub message: String,
-    pub status: String, // One of Normal/InProgress
-}
-
-impl lsp::notification::Notification for StatusNotification {
-    type Params = StatusNotificationParams;
-    const METHOD: &'static str = "statusNotification";
-}
-
-pub enum SetEditorInfo {}
-
-#[derive(Debug, Serialize, Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct SetEditorInfoParams {
-    pub editor_info: EditorInfo,
-    pub editor_plugin_info: EditorPluginInfo,
-}
-
-impl lsp::request::Request for SetEditorInfo {
-    type Params = SetEditorInfoParams;
-    type Result = String;
-    const METHOD: &'static str = "setEditorInfo";
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct EditorInfo {
-    pub name: String,
-    pub version: String,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct EditorPluginInfo {
-    pub name: String,
-    pub version: String,
-}
-
-pub enum NotifyAccepted {}
-
-#[derive(Debug, Serialize, Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct NotifyAcceptedParams {
-    pub uuid: String,
-}
-
-impl lsp::request::Request for NotifyAccepted {
-    type Params = NotifyAcceptedParams;
-    type Result = String;
-    const METHOD: &'static str = "notifyAccepted";
-}
-
-pub enum NotifyRejected {}
-
-#[derive(Debug, Serialize, Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct NotifyRejectedParams {
-    pub uuids: Vec<String>,
-}
-
-impl lsp::request::Request for NotifyRejected {
-    type Params = NotifyRejectedParams;
-    type Result = String;
-    const METHOD: &'static str = "notifyRejected";
-}

crates/copilot2/src/sign_in.rs 🔗

@@ -1,211 +0,0 @@
-use crate::{request::PromptUserDeviceFlow, Copilot, Status};
-use gpui::{
-    div, size, AppContext, Bounds, ClipboardItem, Element, GlobalPixels, InteractiveElement,
-    IntoElement, ParentElement, Point, Render, Styled, ViewContext, VisualContext, WindowBounds,
-    WindowHandle, WindowKind, WindowOptions,
-};
-use theme::ActiveTheme;
-use ui::{prelude::*, Button, Icon, IconElement, Label};
-
-const COPILOT_SIGN_UP_URL: &'static str = "https://github.com/features/copilot";
-
-pub fn init(cx: &mut AppContext) {
-    if let Some(copilot) = Copilot::global(cx) {
-        let mut verification_window: Option<WindowHandle<CopilotCodeVerification>> = None;
-        cx.observe(&copilot, move |copilot, cx| {
-            let status = copilot.read(cx).status();
-
-            match &status {
-                crate::Status::SigningIn { prompt } => {
-                    if let Some(window) = verification_window.as_mut() {
-                        let updated = window
-                            .update(cx, |verification, cx| {
-                                verification.set_status(status.clone(), cx);
-                                cx.activate_window();
-                            })
-                            .is_ok();
-                        if !updated {
-                            verification_window = Some(create_copilot_auth_window(cx, &status));
-                        }
-                    } else if let Some(_prompt) = prompt {
-                        verification_window = Some(create_copilot_auth_window(cx, &status));
-                    }
-                }
-                Status::Authorized | Status::Unauthorized => {
-                    if let Some(window) = verification_window.as_ref() {
-                        window
-                            .update(cx, |verification, cx| {
-                                verification.set_status(status, cx);
-                                cx.activate(true);
-                                cx.activate_window();
-                            })
-                            .ok();
-                    }
-                }
-                _ => {
-                    if let Some(code_verification) = verification_window.take() {
-                        code_verification
-                            .update(cx, |_, cx| cx.remove_window())
-                            .ok();
-                    }
-                }
-            }
-        })
-        .detach();
-    }
-}
-
-fn create_copilot_auth_window(
-    cx: &mut AppContext,
-    status: &Status,
-) -> WindowHandle<CopilotCodeVerification> {
-    let window_size = size(GlobalPixels::from(280.), GlobalPixels::from(280.));
-    let window_options = WindowOptions {
-        bounds: WindowBounds::Fixed(Bounds::new(Point::default(), window_size)),
-        titlebar: None,
-        center: true,
-        focus: true,
-        show: true,
-        kind: WindowKind::PopUp,
-        is_movable: true,
-        display_id: None,
-    };
-    let window = cx.open_window(window_options, |cx| {
-        cx.new_view(|_| CopilotCodeVerification::new(status.clone()))
-    });
-    window
-}
-
-pub struct CopilotCodeVerification {
-    status: Status,
-    connect_clicked: bool,
-}
-
-impl CopilotCodeVerification {
-    pub fn new(status: Status) -> Self {
-        Self {
-            status,
-            connect_clicked: false,
-        }
-    }
-
-    pub fn set_status(&mut self, status: Status, cx: &mut ViewContext<Self>) {
-        self.status = status;
-        cx.notify();
-    }
-
-    fn render_device_code(
-        data: &PromptUserDeviceFlow,
-        cx: &mut ViewContext<Self>,
-    ) -> impl IntoElement {
-        let copied = cx
-            .read_from_clipboard()
-            .map(|item| item.text() == &data.user_code)
-            .unwrap_or(false);
-        h_stack()
-            .cursor_pointer()
-            .justify_between()
-            .on_mouse_down(gpui::MouseButton::Left, {
-                let user_code = data.user_code.clone();
-                move |_, cx| {
-                    cx.write_to_clipboard(ClipboardItem::new(user_code.clone()));
-                    cx.notify();
-                }
-            })
-            .child(Label::new(data.user_code.clone()))
-            .child(div())
-            .child(Label::new(if copied { "Copied!" } else { "Copy" }))
-    }
-
-    fn render_prompting_modal(
-        connect_clicked: bool,
-        data: &PromptUserDeviceFlow,
-        cx: &mut ViewContext<Self>,
-    ) -> impl Element {
-        let connect_button_label = if connect_clicked {
-            "Waiting for connection..."
-        } else {
-            "Connect to Github"
-        };
-        v_stack()
-            .flex_1()
-            .items_center()
-            .justify_between()
-            .w_full()
-            .child(Label::new(
-                "Enable Copilot by connecting your existing license",
-            ))
-            .child(Self::render_device_code(data, cx))
-            .child(
-                Label::new("Paste this code into GitHub after clicking the button below.")
-                    .size(ui::LabelSize::Small),
-            )
-            .child(
-                Button::new("connect-button", connect_button_label).on_click({
-                    let verification_uri = data.verification_uri.clone();
-                    cx.listener(move |this, _, cx| {
-                        cx.open_url(&verification_uri);
-                        this.connect_clicked = true;
-                    })
-                }),
-            )
-    }
-    fn render_enabled_modal() -> impl Element {
-        v_stack()
-            .child(Label::new("Copilot Enabled!"))
-            .child(Label::new(
-                "You can update your settings or sign out from the Copilot menu in the status bar.",
-            ))
-            .child(
-                Button::new("copilot-enabled-done-button", "Done")
-                    .on_click(|_, cx| cx.remove_window()),
-            )
-    }
-
-    fn render_unauthorized_modal() -> impl Element {
-        v_stack()
-            .child(Label::new(
-                "Enable Copilot by connecting your existing license.",
-            ))
-            .child(
-                Label::new("You must have an active Copilot license to use it in Zed.")
-                    .color(Color::Warning),
-            )
-            .child(
-                Button::new("copilot-subscribe-button", "Subscibe on Github").on_click(|_, cx| {
-                    cx.remove_window();
-                    cx.open_url(COPILOT_SIGN_UP_URL)
-                }),
-            )
-    }
-}
-
-impl Render for CopilotCodeVerification {
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
-        let prompt = match &self.status {
-            Status::SigningIn {
-                prompt: Some(prompt),
-            } => Self::render_prompting_modal(self.connect_clicked, &prompt, cx).into_any_element(),
-            Status::Unauthorized => {
-                self.connect_clicked = false;
-                Self::render_unauthorized_modal().into_any_element()
-            }
-            Status::Authorized => {
-                self.connect_clicked = false;
-                Self::render_enabled_modal().into_any_element()
-            }
-            _ => div().into_any_element(),
-        };
-        div()
-            .id("copilot code verification")
-            .flex()
-            .flex_col()
-            .size_full()
-            .items_center()
-            .p_10()
-            .bg(cx.theme().colors().element_background)
-            .child(ui::Label::new("Connect Copilot to Zed"))
-            .child(IconElement::new(Icon::ZedXCopilot))
-            .child(prompt)
-    }
-}

crates/copilot_button/Cargo.toml 🔗

@@ -9,7 +9,7 @@ path = "src/copilot_button.rs"
 doctest = false
 
 [dependencies]
-copilot = { package = "copilot2", path = "../copilot2" }
+copilot = { path = "../copilot" }
 editor = { path = "../editor" }
 fs = { package = "fs2", path = "../fs2" }
 zed-actions = { package="zed_actions2", path = "../zed_actions2"}

crates/editor/Cargo.toml 🔗

@@ -25,7 +25,7 @@ test-support = [
 [dependencies]
 client = { package = "client2", path = "../client2" }
 clock = { path = "../clock" }
-copilot = { package="copilot2", path = "../copilot2" }
+copilot = { path = "../copilot" }
 db = { package="db2", path = "../db2" }
 collections = { path = "../collections" }
 # context_menu = { path = "../context_menu" }
@@ -34,7 +34,7 @@ git = { package = "git3", path = "../git3" }
 gpui = { package = "gpui2", path = "../gpui2" }
 language = { package = "language2", path = "../language2" }
 lsp = { package = "lsp2", path = "../lsp2" }
-multi_buffer = { package = "multi_buffer2", path = "../multi_buffer2" }
+multi_buffer = { path = "../multi_buffer" }
 project = { package = "project2", path = "../project2" }
 rpc = { package = "rpc2", path = "../rpc2" }
 rich_text = { package = "rich_text2", path = "../rich_text2" }
@@ -72,7 +72,7 @@ tree-sitter-html = { workspace = true, optional = true }
 tree-sitter-typescript = { workspace = true, optional = true }
 
 [dev-dependencies]
-copilot = { package="copilot2", path = "../copilot2", features = ["test-support"] }
+copilot = { path = "../copilot", features = ["test-support"] }
 text = { package="text2", path = "../text2", features = ["test-support"] }
 language = { package="language2", path = "../language2", features = ["test-support"] }
 lsp = { package = "lsp2", path = "../lsp2", features = ["test-support"] }
@@ -81,7 +81,7 @@ util = { path = "../util", features = ["test-support"] }
 project = { package = "project2", path = "../project2", features = ["test-support"] }
 settings = { package = "settings2", path = "../settings2", features = ["test-support"] }
 workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }
-multi_buffer = { package = "multi_buffer2", path = "../multi_buffer2", features = ["test-support"] }
+multi_buffer = { path = "../multi_buffer", features = ["test-support"] }
 
 ctor.workspace = true
 env_logger.workspace = true

crates/editor2/src/editor_tests.rs 🔗

@@ -1,8268 +0,0 @@
-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 futures::StreamExt;
-use gpui::{
-    div,
-    serde_json::{self, json},
-    TestAppContext, VisualTestContext, WindowBounds, WindowOptions,
-};
-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::{FollowEvent, FollowableItem, Item, ItemHandle},
-    NavigationEntry, ViewId,
-};
-
-#[gpui::test]
-fn test_edit_events(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let buffer = cx.new_model(|cx| {
-        let mut buffer = language::Buffer::new(0, cx.entity_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| {
-            let view = cx.view().clone();
-            cx.subscribe(&view, move |_, _, event: &EditorEvent, _| {
-                if matches!(event, EditorEvent::Edited | EditorEvent::BufferEdited) {
-                    events.borrow_mut().push(("editor1", event.clone()));
-                }
-            })
-            .detach();
-            Editor::for_buffer(buffer.clone(), None, cx)
-        }
-    });
-
-    let editor2 = cx.add_window({
-        let events = events.clone();
-        |cx| {
-            cx.subscribe(&cx.view().clone(), move |_, _, event: &EditorEvent, _| {
-                if matches!(event, EditorEvent::Edited | EditorEvent::BufferEdited) {
-                    events.borrow_mut().push(("editor2", event.clone()));
-                }
-            })
-            .detach();
-            Editor::for_buffer(buffer.clone(), None, 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", EditorEvent::Edited),
-            ("editor1", EditorEvent::BufferEdited),
-            ("editor2", EditorEvent::BufferEdited),
-        ]
-    );
-
-    // 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", EditorEvent::Edited),
-            ("editor1", EditorEvent::BufferEdited),
-            ("editor2", EditorEvent::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", EditorEvent::Edited),
-            ("editor1", EditorEvent::BufferEdited),
-            ("editor2", EditorEvent::BufferEdited),
-        ]
-    );
-
-    // 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", EditorEvent::Edited),
-            ("editor1", EditorEvent::BufferEdited),
-            ("editor2", EditorEvent::BufferEdited),
-        ]
-    );
-
-    // 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", EditorEvent::Edited),
-            ("editor1", EditorEvent::BufferEdited),
-            ("editor2", EditorEvent::BufferEdited),
-        ]
-    );
-
-    // 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", EditorEvent::Edited),
-            ("editor1", EditorEvent::BufferEdited),
-            ("editor2", EditorEvent::BufferEdited),
-        ]
-    );
-
-    // 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.new_model(|cx| language::Buffer::new(0, cx.entity_id().as_u64(), "123456"));
-    let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval());
-    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let editor = cx.add_window(|cx| build_editor(buffer.clone(), 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.new_model(|cx| {
-        let mut buffer = language::Buffer::new(0, cx.entity_id().as_u64(), "abcde");
-        // Ensure automatic grouping doesn't occur.
-        buffer.set_group_interval(Duration::ZERO);
-        buffer
-    });
-
-    let buffer = cx.new_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)
-    });
-
-    _ = 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))
-            .unwrap(),
-        [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
-    );
-
-    _ = editor.update(cx, |view, cx| {
-        view.update_selection(
-            DisplayPoint::new(3, 3),
-            0,
-            gpui::Point::<f32>::default(),
-            cx,
-        );
-    });
-
-    assert_eq!(
-        editor
-            .update(cx, |view, cx| view.selections.display_ranges(cx))
-            .unwrap(),
-        [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
-    );
-
-    _ = editor.update(cx, |view, cx| {
-        view.update_selection(
-            DisplayPoint::new(1, 1),
-            0,
-            gpui::Point::<f32>::default(),
-            cx,
-        );
-    });
-
-    assert_eq!(
-        editor
-            .update(cx, |view, cx| view.selections.display_ranges(cx))
-            .unwrap(),
-        [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,
-            gpui::Point::<f32>::default(),
-            cx,
-        );
-    });
-
-    assert_eq!(
-        editor
-            .update(cx, |view, cx| view.selections.display_ranges(cx))
-            .unwrap(),
-        [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,
-            gpui::Point::<f32>::default(),
-            cx,
-        );
-    });
-
-    assert_eq!(
-        editor
-            .update(cx, |view, cx| view.selections.display_ranges(cx))
-            .unwrap(),
-        [
-            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))
-            .unwrap(),
-        [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)
-    });
-
-    _ = 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,
-            gpui::Point::<f32>::default(),
-            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,
-            gpui::Point::<f32>::default(),
-            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)
-    });
-
-    _ = 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.open_window(Default::default(), |cx| cx.new_view(|cx| editor.clone(cx)))
-        })
-        .unwrap();
-
-    let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
-    let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)).unwrap();
-
-    assert_eq!(
-        cloned_editor
-            .update(cx, |e, cx| e.display_text(cx))
-            .unwrap(),
-        editor.update(cx, |e, cx| e.display_text(cx)).unwrap()
-    );
-    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
-            .update(cx, |editor, cx| editor.selections.ranges::<Point>(cx))
-            .unwrap(),
-        editor
-            .update(cx, |editor, cx| editor.selections.ranges(cx))
-            .unwrap()
-    );
-    assert_set_eq!(
-        cloned_editor
-            .update(cx, |e, cx| e.selections.display_ranges(cx))
-            .unwrap(),
-        editor
-            .update(cx, |e, cx| e.selections.display_ranges(cx))
-            .unwrap()
-    );
-}
-
-//todo!(editor navigate)
-#[gpui::test]
-async fn test_navigation_history(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    use workspace::item::Item;
-
-    let fs = FakeFs::new(cx.executor());
-    let project = Project::test(fs, [], cx).await;
-    let workspace = cx.add_window(|cx| Workspace::test_new(project, cx));
-    let pane = workspace
-        .update(cx, |workspace, _| workspace.active_pane().clone())
-        .unwrap();
-
-    _ = workspace.update(cx, |_v, cx| {
-        cx.new_view(|cx| {
-            let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
-            let mut editor = build_editor(buffer.clone(), cx);
-            let handle = cx.view();
-            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.entity_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.entity_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(gpui::Point::<f32>::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(gpui::Point::<f32>::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),
-                gpui::Point::new(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)
-    });
-
-    _ = view.update(cx, |view, cx| {
-        view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
-        view.update_selection(
-            DisplayPoint::new(1, 1),
-            0,
-            gpui::Point::<f32>::default(),
-            cx,
-        );
-        view.end_selection(cx);
-
-        view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
-        view.update_selection(
-            DisplayPoint::new(0, 3),
-            0,
-            gpui::Point::<f32>::default(),
-            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)
-    });
-
-    _ = 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));
-
-    _ = 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)
-    });
-
-    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())]
-        );
-    });
-}
-
-//todo!(finish editor tests)
-#[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)
-    });
-    _ = 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)
-    });
-    _ = 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)
-    });
-    _ = 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);
-    });
-}
-
-//todo!(finish editor tests)
-#[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)
-    });
-
-    _ = view.update(cx, |view, cx| {
-        view.set_wrap_width(Some(140.0.into()), 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)]
-        );
-    });
-}
-
-//todo!(simulate_resize)
-#[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()
-            .unwrap()
-            .text
-            .line_height_in_pixels(cx.rem_size())
-    });
-    cx.simulate_window_resize(cx.window, size(px(100.), 4. * line_height));
-
-    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()
-            .unwrap()
-            .text
-            .line_height_in_pixels(cx.rem_size())
-    });
-    let window = cx.window;
-    cx.simulate_window_resize(window, size(px(1000.), 4. * line_height + px(0.5)));
-
-    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(),
-            gpui::Point::new(0., 0.)
-        );
-        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
-        assert_eq!(
-            editor.snapshot(cx).scroll_position(),
-            gpui::Point::new(0., 3.)
-        );
-        editor.scroll_screen(&ScrollAmount::Page(1.), cx);
-        assert_eq!(
-            editor.snapshot(cx).scroll_position(),
-            gpui::Point::new(0., 6.)
-        );
-        editor.scroll_screen(&ScrollAmount::Page(-1.), cx);
-        assert_eq!(
-            editor.snapshot(cx).scroll_position(),
-            gpui::Point::new(0., 3.)
-        );
-
-        editor.scroll_screen(&ScrollAmount::Page(-0.5), cx);
-        assert_eq!(
-            editor.snapshot(cx).scroll_position(),
-            gpui::Point::new(0., 1.)
-        );
-        editor.scroll_screen(&ScrollAmount::Page(0.5), cx);
-        assert_eq!(
-            editor.snapshot(cx).scroll_position(),
-            gpui::Point::new(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()
-            .unwrap()
-            .text
-            .line_height_in_pixels(cx.rem_size())
-    });
-    let window = cx.window;
-    cx.simulate_window_resize(window, size(px(1000.), 6. * line_height));
-
-    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(),
-            gpui::Point::new(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(),
-            gpui::Point::new(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(),
-            gpui::Point::new(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(),
-            gpui::Point::new(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()
-            .unwrap()
-            .text
-            .line_height_in_pixels(cx.rem_size())
-    });
-    let window = cx.window;
-    cx.simulate_window_resize(window, size(px(100.), 4. * line_height));
-    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)
-    });
-
-    _ = 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)
-    });
-
-    _ = 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
-    });
-
-    _ = 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
-    });
-
-    _ = 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.new_model(|cx| {
-        Buffer::new(0, cx.entity_id().as_u64(), "a = 1\nb = 2\n").with_language(toml_language, cx)
-    });
-    let rust_buffer = cx.new_model(|cx| {
-        Buffer::new(0, cx.entity_id().as_u64(), "const c: usize = 3;\n")
-            .with_language(rust_language, cx)
-    });
-    let multibuffer = cx.new_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)
-    });
-    _ = 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)
-    });
-    _ = 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)]
-        );
-    });
-}
-
-//todo!(select_anchor_ranges)
-#[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)
-    });
-    _ = 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)
-    });
-    _ = 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)
-    });
-    _ = 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)
-    });
-    _ = 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(|_| div().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);
-    });
-}
-
-//todo!(test_transpose)
-#[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.set_style(EditorStyle::default(), 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.set_style(EditorStyle::default(), 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.set_style(EditorStyle::default(), 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.set_style(EditorStyle::default(), 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));
-    assert_eq!(
-        cx.read_from_clipboard().map(|item| item.text().to_owned()),
-        Some("fox jumps over\n".to_owned())
-    );
-
-    // 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)
-    });
-    _ = 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)
-    });
-    _ = 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)
-    });
-    _ = 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]
-async fn test_add_selection_above_below(cx: &mut TestAppContext) {
-    init_test(cx, |_| {});
-
-    let mut cx = EditorTestContext::new(cx).await;
-
-    // let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
-    cx.set_state(indoc!(
-        r#"abc
-           defˇghi
-
-           jk
-           nlmo
-           "#
-    ));
-
-    cx.update_editor(|editor, cx| {
-        editor.add_selection_above(&Default::default(), cx);
-    });
-
-    cx.assert_editor_state(indoc!(
-        r#"abcˇ
-           defˇghi
-
-           jk
-           nlmo
-           "#
-    ));
-
-    cx.update_editor(|editor, cx| {
-        editor.add_selection_above(&Default::default(), cx);
-    });
-
-    cx.assert_editor_state(indoc!(
-        r#"abcˇ
-            defˇghi
-
-            jk
-            nlmo
-            "#
-    ));
-
-    cx.update_editor(|view, cx| {
-        view.add_selection_below(&Default::default(), cx);
-    });
-
-    cx.assert_editor_state(indoc!(
-        r#"abc
-           defˇghi
-
-           jk
-           nlmo
-           "#
-    ));
-
-    cx.update_editor(|view, cx| {
-        view.undo_selection(&Default::default(), cx);
-    });
-
-    cx.assert_editor_state(indoc!(
-        r#"abcˇ
-           defˇghi
-
-           jk
-           nlmo
-           "#
-    ));
-
-    cx.update_editor(|view, cx| {
-        view.redo_selection(&Default::default(), cx);
-    });
-
-    cx.assert_editor_state(indoc!(
-        r#"abc
-           defˇghi
-
-           jk
-           nlmo
-           "#
-    ));
-
-    cx.update_editor(|view, cx| {
-        view.add_selection_below(&Default::default(), cx);
-    });
-
-    cx.assert_editor_state(indoc!(
-        r#"abc
-           defˇghi
-
-           jk
-           nlmˇo
-           "#
-    ));
-
-    cx.update_editor(|view, cx| {
-        view.add_selection_below(&Default::default(), cx);
-    });
-
-    cx.assert_editor_state(indoc!(
-        r#"abc
-           defˇghi
-
-           jk
-           nlmˇo
-           "#
-    ));
-
-    // change selections
-    cx.set_state(indoc!(
-        r#"abc
-           def«ˇg»hi
-
-           jk
-           nlmo
-           "#
-    ));
-
-    cx.update_editor(|view, cx| {
-        view.add_selection_below(&Default::default(), cx);
-    });
-
-    cx.assert_editor_state(indoc!(
-        r#"abc
-           def«ˇg»hi
-
-           jk
-           nlm«ˇo»
-           "#
-    ));
-
-    cx.update_editor(|view, cx| {
-        view.add_selection_below(&Default::default(), cx);
-    });
-
-    cx.assert_editor_state(indoc!(
-        r#"abc
-           def«ˇg»hi
-
-           jk
-           nlm«ˇo»
-           "#
-    ));
-
-    cx.update_editor(|view, cx| {
-        view.add_selection_above(&Default::default(), cx);
-    });
-
-    cx.assert_editor_state(indoc!(
-        r#"abc
-           def«ˇg»hi
-
-           jk
-           nlmo
-           "#
-    ));
-
-    cx.update_editor(|view, cx| {
-        view.add_selection_above(&Default::default(), cx);
-    });
-
-    cx.assert_editor_state(indoc!(
-        r#"abc
-           def«ˇg»hi
-
-           jk
-           nlmo
-           "#
-    ));
-
-    // Change selections again
-    cx.set_state(indoc!(
-        r#"a«bc
-           defgˇ»hi
-
-           jk
-           nlmo
-           "#
-    ));
-
-    cx.update_editor(|view, cx| {
-        view.add_selection_below(&Default::default(), cx);
-    });
-
-    cx.assert_editor_state(indoc!(
-        r#"a«bcˇ»
-           d«efgˇ»hi
-
-           j«kˇ»
-           nlmo
-           "#
-    ));
-
-    cx.update_editor(|view, cx| {
-        view.add_selection_below(&Default::default(), cx);
-    });
-    cx.assert_editor_state(indoc!(
-        r#"a«bcˇ»
-           d«efgˇ»hi
-
-           j«kˇ»
-           n«lmoˇ»
-           "#
-    ));
-    cx.update_editor(|view, cx| {
-        view.add_selection_above(&Default::default(), cx);
-    });
-
-    cx.assert_editor_state(indoc!(
-        r#"a«bcˇ»
-           d«efgˇ»hi
-
-           j«kˇ»
-           nlmo
-           "#
-    ));
-
-    // Change selections again
-    cx.set_state(indoc!(
-        r#"abc
-           d«ˇefghi
-
-           jk
-           nlm»o
-           "#
-    ));
-
-    cx.update_editor(|view, cx| {
-        view.add_selection_above(&Default::default(), cx);
-    });
-
-    cx.assert_editor_state(indoc!(
-        r#"a«ˇbc»
-           d«ˇef»ghi
-
-           j«ˇk»
-           n«ˇlm»o
-           "#
-    ));
-
-    cx.update_editor(|view, cx| {
-        view.add_selection_below(&Default::default(), cx);
-    });
-
-    cx.assert_editor_state(indoc!(
-        r#"abc
-           d«ˇef»ghi
-
-           j«ˇk»
-           n«ˇlm»o
-           "#
-    ));
-}
-
-#[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
-        .new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx));
-    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
-
-    view.condition::<crate::EditorEvent>(&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
-        .new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx));
-    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
-    editor
-        .condition::<crate::EditorEvent>(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
-        .new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx));
-    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
-    view.condition::<crate::EditorEvent>(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
-        .new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx));
-    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
-    editor
-        .condition::<crate::EditorEvent>(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)
-            ]
-        );
-    });
-}
-
-// todo!(select_anchor_ranges)
-#[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) = cx.add_window_view(|cx| build_editor(buffer, 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.executor());
-    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.executor().start_waiting();
-    let fake_server = fake_servers.next().await.unwrap();
-
-    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, 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))
-        .unwrap();
-    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.executor().start_waiting();
-    let _x = save.await;
-
-    assert_eq!(
-        editor.update(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))
-        .unwrap();
-    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
-    cx.executor().start_waiting();
-    save.await;
-    assert_eq!(
-        editor.update(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))
-        .unwrap();
-    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.executor().start_waiting();
-    save.await;
-}
-
-#[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.executor());
-    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.executor().start_waiting();
-    let fake_server = fake_servers.next().await.unwrap();
-
-    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, 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))
-        .unwrap();
-    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.executor().start_waiting();
-    save.await;
-    assert_eq!(
-        editor.update(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))
-        .unwrap();
-    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
-    cx.executor().start_waiting();
-    save.await;
-    assert_eq!(
-        editor.update(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))
-        .unwrap();
-    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.executor().start_waiting();
-    save.await;
-}
-
-#[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.executor());
-    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.executor().start_waiting();
-    let fake_server = fake_servers.next().await.unwrap();
-
-    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, 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)
-        })
-        .unwrap();
-    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.executor().start_waiting();
-    format.await;
-    assert_eq!(
-        editor.update(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)
-        })
-        .unwrap();
-    cx.executor().advance_clock(super::FORMAT_TIMEOUT);
-    cx.executor().start_waiting();
-    format.await;
-    assert_eq!(
-        editor.update(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_executor().clone();
-            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.executor().run_until_parked();
-
-    // Submit a second format request.
-    let format_2 = cx
-        .update_editor(|editor, cx| editor.format(&Format, cx))
-        .unwrap();
-    cx.executor().run_until_parked();
-
-    // Wait for both format requests to complete
-    cx.executor().advance_clock(Duration::from_millis(200));
-    cx.executor().start_waiting();
-    format_1.await.unwrap();
-    cx.executor().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.executor().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.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a')));
-    let multibuffer = cx.new_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) = cx.add_window_view(|cx| build_editor(multibuffer, 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.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), initial_text));
-    let multibuffer = cx.new_model(|cx| {
-        let mut multibuffer = MultiBuffer::new(0);
-        multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
-        multibuffer
-    });
-
-    let (view, cx) = cx.add_window_view(|cx| build_editor(multibuffer, 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.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a')));
-    let mut excerpt1_id = None;
-    let multibuffer = cx.new_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
-    });
-
-    // 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.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a')));
-    let mut excerpt1_id = None;
-    let multibuffer = cx.new_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
-    });
-
-    _ = 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
-        .new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx));
-    let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
-    view.condition::<crate::EditorEvent>(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)
-    });
-
-    _ = 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,
-            cx.theme().colors(),
-        );
-        // Enforce a consistent ordering based on color without relying on the ordering of the
-        // highlight's `TypeId` which is non-executor.
-        highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
-        assert_eq!(
-            highlighted_ranges,
-            &[
-                (
-                    DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
-                    Hsla::red(),
-                ),
-                (
-                    DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
-                    Hsla::red(),
-                ),
-                (
-                    DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
-                    Hsla::green(),
-                ),
-                (
-                    DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
-                    Hsla::green(),
-                ),
-            ]
-        );
-        assert_eq!(
-            editor.background_highlights_in_range(
-                anchor_range(Point::new(5, 6)..Point::new(6, 4)),
-                &snapshot,
-                cx.theme().colors(),
-            ),
-            &[(
-                DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
-                Hsla::red(),
-            )]
-        );
-    });
-}
-
-// todo!(following)
-#[gpui::test]
-async fn test_following(cx: &mut gpui::TestAppContext) {
-    init_test(cx, |_| {});
-
-    let fs = FakeFs::new(cx.executor());
-    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.new_model(|cx| MultiBuffer::singleton(buffer, cx))
-    });
-    let leader = cx.add_window(|cx| build_editor(buffer.clone(), cx));
-    let follower = cx.update(|cx| {
-        cx.open_window(
-            WindowOptions {
-                bounds: WindowBounds::Fixed(Bounds::from_corners(
-                    gpui::Point::new((0. as f64).into(), (0. as f64).into()),
-                    gpui::Point::new((10. as f64).into(), (80. as f64).into()),
-                )),
-                ..Default::default()
-            },
-            |cx| cx.new_view(|cx| build_editor(buffer.clone(), 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.root_view(cx).unwrap(),
-                move |_, leader, event, cx| {
-                    leader
-                        .read(cx)
-                        .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
-                },
-            )
-            .detach();
-
-            cx.subscribe(
-                &follower.root_view(cx).unwrap(),
-                move |_, _, event: &EditorEvent, _cx| {
-                    if matches!(Editor::to_follow_event(event), Some(FollowEvent::Unfollow)) {
-                        *is_still_following.borrow_mut() = false;
-                    }
-
-                    if let EditorEvent::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)
-        })
-        .unwrap()
-        .await
-        .unwrap();
-    _ = follower.update(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(gpui::Point::new(1.5, 3.5), cx);
-    });
-    follower
-        .update(cx, |follower, cx| {
-            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
-        })
-        .unwrap()
-        .await
-        .unwrap();
-    assert_eq!(
-        follower
-            .update(cx, |follower, cx| follower.scroll_position(cx))
-            .unwrap(),
-        gpui::Point::new(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(gpui::Point::new(1.5, 3.5), cx);
-    });
-    follower
-        .update(cx, |follower, cx| {
-            follower.apply_update_proto(&project, pending_update.borrow_mut().take().unwrap(), cx)
-        })
-        .unwrap()
-        .await
-        .unwrap();
-    _ = follower.update(cx, |follower, cx| {
-        assert_eq!(follower.scroll_position(cx), gpui::Point::new(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)
-        })
-        .unwrap()
-        .await
-        .unwrap();
-    _ = follower.update(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)
-        })
-        .unwrap()
-        .await
-        .unwrap();
-    _ = follower.update(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: gpui::Point::new(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.executor());
-    let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
-    let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
-    let pane = workspace
-        .update(cx, |workspace, _| workspace.active_pane().clone())
-        .unwrap();
-
-    let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
-
-    let leader = pane.update(cx, |_, cx| {
-        let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
-        cx.new_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_window(*workspace.deref(), |_, cx| {
-            Editor::from_state_proto(
-                pane.clone(),
-                workspace.root_view(cx).unwrap(),
-                ViewId {
-                    creator: Default::default(),
-                    id: 0,
-                },
-                &mut state_message,
-                cx,
-            )
-        })
-        .unwrap()
-        .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.update(cx, |editor, cx| editor.text(cx)),
-        leader.update(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_window(*workspace.deref(), |_, cx| {
-            Editor::from_state_proto(
-                pane.clone(),
-                workspace.root_view(cx).unwrap().clone(),
-                ViewId {
-                    creator: Default::default(),
-                    id: 0,
-                },
-                &mut state_message,
-                cx,
-            )
-        })
-        .unwrap()
-        .unwrap()
-        .await
-        .unwrap();
-    assert_eq!(
-        follower_2.update(cx, |editor, cx| editor.text(cx)),
-        leader.update(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.update(cx, |editor, cx| editor.text(cx)),
-        leader.update(cx, |editor, cx| editor.text(cx))
-    );
-}
-
-#[gpui::test]
-async fn go_to_prev_overlapping_diagnostic(
-    executor: BackgroundExecutor,
-    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()
-        });
-    });
-
-    executor.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(executor: BackgroundExecutor, 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));
-    executor.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')
-            }"},
-    );
-}
-
-// todo!(completions)
-#[gpui::test(iterations = 10)]
-async fn test_copilot(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
-    // flaky
-    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![],
-    );
-    executor.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![],
-    );
-    executor.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![],
-    );
-    executor.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");
-    executor.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![],
-    );
-    executor.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));
-    executor.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(
-    executor: BackgroundExecutor,
-    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));
-    executor.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(executor: BackgroundExecutor, 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.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "a = 1\nb = 2\n"));
-    let buffer_2 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "c = 3\nd = 4\n"));
-    let multibuffer = cx.new_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));
-
-    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);
-    });
-    executor.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.
-    executor.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(executor: BackgroundExecutor, 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.executor());
-    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.new_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));
-
-    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);
-    });
-
-    executor.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);
-    });
-
-    executor.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.executor());
-    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));
-
-    let cx = &mut VisualTestContext::from_window(*workspace, cx);
-
-    let worktree_id = workspace
-        .update(cx, |workspace, cx| {
-            workspace.project().update(cx, |project, cx| {
-                project.worktrees().next().unwrap().read(cx).id()
-            })
-        })
-        .unwrap();
-
-    let buffer = project
-        .update(cx, |project, cx| {
-            project.open_local_buffer("/a/main.rs", cx)
-        })
-        .await
-        .unwrap();
-    cx.executor().run_until_parked();
-    cx.executor().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)
-        })
-        .unwrap()
-        .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| {
-        editor.focus(cx);
-        editor.change_selections(None, cx, |s| {
-            s.select_ranges([Point::new(0, 21)..Point::new(0, 20)])
-        });
-        editor.handle_input("{", cx);
-    });
-
-    cx.executor().run_until_parked();
-
-    _ = buffer.update(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.executor());
-    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.executor().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.executor().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.executor().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.executor().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.executor().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.executor().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.executor().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.executor().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.executor());
-    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.new_model(|cx| MultiBuffer::singleton(buffer, cx));
-    let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx));
-    _ = editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx));
-
-    editor
-        .update(cx, |editor, cx| {
-            editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
-        })
-        .unwrap()
-        .await;
-    assert_eq!(
-        editor.update(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.update(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(|store: &mut SettingsStore, 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(|store: &mut SettingsStore, cx| {
-            store.update_user_settings::<ProjectSettings>(cx, f);
-        });
-    });
-}
-
-pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
-    _ = cx.update(|cx| {
-        let store = SettingsStore::test(cx);
-        cx.set_global(store);
-        theme::init(theme::LoadThemes::JustBase, 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/multi_buffer/Cargo.toml 🔗

@@ -20,20 +20,19 @@ test-support = [
 ]
 
 [dependencies]
-client = { path = "../client" }
+client = { package = "client2", path = "../client2" }
 clock = { path = "../clock" }
 collections = { path = "../collections" }
-context_menu = { path = "../context_menu" }
-git = { path = "../git" }
-gpui = { path = "../gpui" }
-language = { path = "../language" }
-lsp = { path = "../lsp" }
-rich_text = { path = "../rich_text" }
-settings = { path = "../settings" }
+git = { package = "git3", path = "../git3" }
+gpui = { package = "gpui2", path = "../gpui2" }
+language = { package = "language2", path = "../language2" }
+lsp = { package = "lsp2", path = "../lsp2" }
+rich_text = { package = "rich_text2", path = "../rich_text2" }
+settings = { package = "settings2", path = "../settings2" }
 snippet = { path = "../snippet" }
 sum_tree = { path = "../sum_tree" }
-text = { path = "../text" }
-theme = { path = "../theme" }
+text = { package = "text2", path = "../text2" }
+theme = { package = "theme2", path = "../theme2" }
 util = { path = "../util" }
 
 aho-corasick = "1.1"
@@ -61,14 +60,13 @@ tree-sitter-typescript = { workspace = true, optional = true }
 
 [dev-dependencies]
 copilot = { path = "../copilot", features = ["test-support"] }
-text = { path = "../text", features = ["test-support"] }
-language = { path = "../language", features = ["test-support"] }
-lsp = { path = "../lsp", features = ["test-support"] }
-gpui = { path = "../gpui", features = ["test-support"] }
+text = { package = "text2", path = "../text2", features = ["test-support"] }
+language = { package = "language2", path = "../language2", features = ["test-support"] }
+lsp = { package = "lsp2", path = "../lsp2", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
 util = { path = "../util", features = ["test-support"] }
-project = { path = "../project", features = ["test-support"] }
-settings = { path = "../settings", features = ["test-support"] }
-workspace = { path = "../workspace", features = ["test-support"] }
+project = { package = "project2", path = "../project2", features = ["test-support"] }
+settings = { package = "settings2", path = "../settings2", features = ["test-support"] }
 
 ctor.workspace = true
 env_logger.workspace = true

crates/multi_buffer/src/multi_buffer.rs 🔗

@@ -6,7 +6,7 @@ use clock::ReplicaId;
 use collections::{BTreeMap, Bound, HashMap, HashSet};
 use futures::{channel::mpsc, SinkExt};
 use git::diff::DiffHunk;
-use gpui::{AppContext, Entity, ModelContext, ModelHandle};
+use gpui::{AppContext, EventEmitter, Model, ModelContext};
 pub use language::Completion;
 use language::{
     char_kind,
@@ -38,6 +38,9 @@ use text::{
 use theme::SyntaxTheme;
 use util::post_inc;
 
+#[cfg(any(test, feature = "test-support"))]
+use gpui::Context;
+
 const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize];
 
 #[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
@@ -57,7 +60,7 @@ pub struct MultiBuffer {
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub enum Event {
     ExcerptsAdded {
-        buffer: ModelHandle<Buffer>,
+        buffer: Model<Buffer>,
         predecessor: ExcerptId,
         excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
     },
@@ -119,7 +122,7 @@ pub trait ToPointUtf16: 'static + fmt::Debug {
 }
 
 struct BufferState {
-    buffer: ModelHandle<Buffer>,
+    buffer: Model<Buffer>,
     last_version: clock::Global,
     last_parse_count: usize,
     last_selections_update_count: usize,
@@ -279,7 +282,7 @@ impl MultiBuffer {
         self
     }
 
-    pub fn singleton(buffer: ModelHandle<Buffer>, cx: &mut ModelContext<Self>) -> Self {
+    pub fn singleton(buffer: Model<Buffer>, cx: &mut ModelContext<Self>) -> Self {
         let mut this = Self::new(buffer.read(cx).replica_id());
         this.singleton = true;
         this.push_excerpts(
@@ -308,7 +311,7 @@ impl MultiBuffer {
         self.snapshot.borrow()
     }
 
-    pub fn as_singleton(&self) -> Option<ModelHandle<Buffer>> {
+    pub fn as_singleton(&self) -> Option<Model<Buffer>> {
         if self.singleton {
             return Some(
                 self.buffers
@@ -681,7 +684,7 @@ impl MultiBuffer {
 
     pub fn push_transaction<'a, T>(&mut self, buffer_transactions: T, cx: &mut ModelContext<Self>)
     where
-        T: IntoIterator<Item = (&'a ModelHandle<Buffer>, &'a language::Transaction)>,
+        T: IntoIterator<Item = (&'a Model<Buffer>, &'a language::Transaction)>,
     {
         self.history
             .push_transaction(buffer_transactions, Instant::now(), cx);
@@ -863,19 +866,19 @@ impl MultiBuffer {
 
     pub fn stream_excerpts_with_context_lines(
         &mut self,
-        buffer: ModelHandle<Buffer>,
+        buffer: Model<Buffer>,
         ranges: Vec<Range<text::Anchor>>,
         context_line_count: u32,
         cx: &mut ModelContext<Self>,
     ) -> mpsc::Receiver<Range<Anchor>> {
-        let (mut tx, rx) = mpsc::channel(256);
-        cx.spawn(|this, mut cx| async move {
-            let (buffer_id, buffer_snapshot) =
-                buffer.read_with(&cx, |buffer, _| (buffer.remote_id(), buffer.snapshot()));
+        let (buffer_id, buffer_snapshot) =
+            buffer.update(cx, |buffer, _| (buffer.remote_id(), buffer.snapshot()));
 
+        let (mut tx, rx) = mpsc::channel(256);
+        cx.spawn(move |this, mut cx| async move {
             let mut excerpt_ranges = Vec::new();
             let mut range_counts = Vec::new();
-            cx.background()
+            cx.background_executor()
                 .scoped(|scope| {
                     scope.spawn(async {
                         let (ranges, counts) =
@@ -889,9 +892,12 @@ impl MultiBuffer {
             let mut ranges = ranges.into_iter();
             let mut range_counts = range_counts.into_iter();
             for excerpt_ranges in excerpt_ranges.chunks(100) {
-                let excerpt_ids = this.update(&mut cx, |this, cx| {
+                let excerpt_ids = match this.update(&mut cx, |this, cx| {
                     this.push_excerpts(buffer.clone(), excerpt_ranges.iter().cloned(), cx)
-                });
+                }) {
+                    Ok(excerpt_ids) => excerpt_ids,
+                    Err(_) => return,
+                };
 
                 for (excerpt_id, range_count) in excerpt_ids.into_iter().zip(range_counts.by_ref())
                 {
@@ -920,7 +926,7 @@ impl MultiBuffer {
 
     pub fn push_excerpts<O>(
         &mut self,
-        buffer: ModelHandle<Buffer>,
+        buffer: Model<Buffer>,
         ranges: impl IntoIterator<Item = ExcerptRange<O>>,
         cx: &mut ModelContext<Self>,
     ) -> Vec<ExcerptId>
@@ -932,7 +938,7 @@ impl MultiBuffer {
 
     pub fn push_excerpts_with_context_lines<O>(
         &mut self,
-        buffer: ModelHandle<Buffer>,
+        buffer: Model<Buffer>,
         ranges: Vec<Range<O>>,
         context_line_count: u32,
         cx: &mut ModelContext<Self>,
@@ -970,7 +976,7 @@ impl MultiBuffer {
     pub fn insert_excerpts_after<O>(
         &mut self,
         prev_excerpt_id: ExcerptId,
-        buffer: ModelHandle<Buffer>,
+        buffer: Model<Buffer>,
         ranges: impl IntoIterator<Item = ExcerptRange<O>>,
         cx: &mut ModelContext<Self>,
     ) -> Vec<ExcerptId>
@@ -995,7 +1001,7 @@ impl MultiBuffer {
     pub fn insert_excerpts_with_ids_after<O>(
         &mut self,
         prev_excerpt_id: ExcerptId,
-        buffer: ModelHandle<Buffer>,
+        buffer: Model<Buffer>,
         ranges: impl IntoIterator<Item = (ExcerptId, ExcerptRange<O>)>,
         cx: &mut ModelContext<Self>,
     ) where
@@ -1132,7 +1138,7 @@ impl MultiBuffer {
 
     pub fn excerpts_for_buffer(
         &self,
-        buffer: &ModelHandle<Buffer>,
+        buffer: &Model<Buffer>,
         cx: &AppContext,
     ) -> Vec<(ExcerptId, ExcerptRange<text::Anchor>)> {
         let mut excerpts = Vec::new();
@@ -1169,7 +1175,7 @@ impl MultiBuffer {
         &self,
         position: impl ToOffset,
         cx: &AppContext,
-    ) -> Option<(ExcerptId, ModelHandle<Buffer>, Range<text::Anchor>)> {
+    ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
         let snapshot = self.read(cx);
         let position = position.to_offset(&snapshot);
 
@@ -1197,7 +1203,7 @@ impl MultiBuffer {
         &self,
         point: T,
         cx: &AppContext,
-    ) -> Option<(ModelHandle<Buffer>, usize, ExcerptId)> {
+    ) -> Option<(Model<Buffer>, usize, ExcerptId)> {
         let snapshot = self.read(cx);
         let offset = point.to_offset(&snapshot);
         let mut cursor = snapshot.excerpts.cursor::<usize>();
@@ -1219,7 +1225,7 @@ impl MultiBuffer {
         &self,
         range: Range<T>,
         cx: &AppContext,
-    ) -> Vec<(ModelHandle<Buffer>, Range<usize>, ExcerptId)> {
+    ) -> Vec<(Model<Buffer>, Range<usize>, ExcerptId)> {
         let snapshot = self.read(cx);
         let start = range.start.to_offset(&snapshot);
         let end = range.end.to_offset(&snapshot);
@@ -1377,7 +1383,7 @@ impl MultiBuffer {
         &self,
         position: T,
         cx: &AppContext,
-    ) -> Option<(ModelHandle<Buffer>, language::Anchor)> {
+    ) -> Option<(Model<Buffer>, language::Anchor)> {
         let snapshot = self.read(cx);
         let anchor = snapshot.anchor_before(position);
         let buffer = self
@@ -1391,7 +1397,7 @@ impl MultiBuffer {
 
     fn on_buffer_event(
         &mut self,
-        _: ModelHandle<Buffer>,
+        _: Model<Buffer>,
         event: &language::Event,
         cx: &mut ModelContext<Self>,
     ) {
@@ -1414,7 +1420,7 @@ impl MultiBuffer {
         });
     }
 
-    pub fn all_buffers(&self) -> HashSet<ModelHandle<Buffer>> {
+    pub fn all_buffers(&self) -> HashSet<Model<Buffer>> {
         self.buffers
             .borrow()
             .values()
@@ -1422,7 +1428,7 @@ impl MultiBuffer {
             .collect()
     }
 
-    pub fn buffer(&self, buffer_id: u64) -> Option<ModelHandle<Buffer>> {
+    pub fn buffer(&self, buffer_id: u64) -> Option<Model<Buffer>> {
         self.buffers
             .borrow()
             .get(&buffer_id)
@@ -1487,7 +1493,7 @@ impl MultiBuffer {
         language_settings(language.as_ref(), file, cx)
     }
 
-    pub fn for_each_buffer(&self, mut f: impl FnMut(&ModelHandle<Buffer>)) {
+    pub fn for_each_buffer(&self, mut f: impl FnMut(&Model<Buffer>)) {
         self.buffers
             .borrow()
             .values()
@@ -1642,18 +1648,18 @@ impl MultiBuffer {
 
 #[cfg(any(test, feature = "test-support"))]
 impl MultiBuffer {
-    pub fn build_simple(text: &str, cx: &mut gpui::AppContext) -> ModelHandle<Self> {
-        let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text));
-        cx.add_model(|cx| Self::singleton(buffer, cx))
+    pub fn build_simple(text: &str, cx: &mut gpui::AppContext) -> Model<Self> {
+        let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text));
+        cx.new_model(|cx| Self::singleton(buffer, cx))
     }
 
     pub fn build_multi<const COUNT: usize>(
         excerpts: [(&str, Vec<Range<Point>>); COUNT],
         cx: &mut gpui::AppContext,
-    ) -> ModelHandle<Self> {
-        let multi = cx.add_model(|_| Self::new(0));
+    ) -> Model<Self> {
+        let multi = cx.new_model(|_| Self::new(0));
         for (text, ranges) in excerpts {
-            let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text));
+            let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text));
             let excerpt_ranges = ranges.into_iter().map(|range| ExcerptRange {
                 context: range,
                 primary: None,
@@ -1666,15 +1672,12 @@ impl MultiBuffer {
         multi
     }
 
-    pub fn build_from_buffer(
-        buffer: ModelHandle<Buffer>,
-        cx: &mut gpui::AppContext,
-    ) -> ModelHandle<Self> {
-        cx.add_model(|cx| Self::singleton(buffer, cx))
+    pub fn build_from_buffer(buffer: Model<Buffer>, cx: &mut gpui::AppContext) -> Model<Self> {
+        cx.new_model(|cx| Self::singleton(buffer, cx))
     }
 
-    pub fn build_random(rng: &mut impl rand::Rng, cx: &mut gpui::AppContext) -> ModelHandle<Self> {
-        cx.add_model(|cx| {
+    pub fn build_random(rng: &mut impl rand::Rng, cx: &mut gpui::AppContext) -> Model<Self> {
+        cx.new_model(|cx| {
             let mut multibuffer = MultiBuffer::new(0);
             let mutation_count = rng.gen_range(1..=5);
             multibuffer.randomly_edit_excerpts(rng, mutation_count, cx);
@@ -1745,7 +1748,7 @@ impl MultiBuffer {
             if excerpt_ids.is_empty() || (rng.gen() && excerpt_ids.len() < max_excerpts) {
                 let buffer_handle = if rng.gen() || self.buffers.borrow().is_empty() {
                     let text = RandomCharIter::new(&mut *rng).take(10).collect::<String>();
-                    buffers.push(cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, text)));
+                    buffers.push(cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text)));
                     let buffer = buffers.last().unwrap().read(cx);
                     log::info!(
                         "Creating new buffer {} with text: {:?}",
@@ -1868,9 +1871,7 @@ impl MultiBuffer {
     }
 }
 
-impl Entity for MultiBuffer {
-    type Event = Event;
-}
+impl EventEmitter<Event> for MultiBuffer {}
 
 impl MultiBufferSnapshot {
     pub fn text(&self) -> String {
@@ -3405,7 +3406,7 @@ impl History {
         now: Instant,
         cx: &mut ModelContext<MultiBuffer>,
     ) where
-        T: IntoIterator<Item = (&'a ModelHandle<Buffer>, &'a language::Transaction)>,
+        T: IntoIterator<Item = (&'a Model<Buffer>, &'a language::Transaction)>,
     {
         assert_eq!(self.transaction_depth, 0);
         let transaction = Transaction {
@@ -4131,18 +4132,19 @@ where
 mod tests {
     use super::*;
     use futures::StreamExt;
-    use gpui::{AppContext, TestAppContext};
+    use gpui::{AppContext, Context, TestAppContext};
     use language::{Buffer, Rope};
+    use parking_lot::RwLock;
     use rand::prelude::*;
     use settings::SettingsStore;
-    use std::{env, rc::Rc};
+    use std::env;
     use util::test::sample_text;
 
     #[gpui::test]
     fn test_singleton(cx: &mut AppContext) {
         let buffer =
-            cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, sample_text(6, 6, 'a')));
-        let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
+            cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(6, 6, 'a')));
+        let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
 
         let snapshot = multibuffer.read(cx).snapshot(cx);
         assert_eq!(snapshot.text(), buffer.read(cx).text());
@@ -4168,11 +4170,11 @@ mod tests {
 
     #[gpui::test]
     fn test_remote(cx: &mut AppContext) {
-        let host_buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "a"));
-        let guest_buffer = cx.add_model(|cx| {
+        let host_buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "a"));
+        let guest_buffer = cx.new_model(|cx| {
             let state = host_buffer.read(cx).to_proto();
             let ops = cx
-                .background()
+                .background_executor()
                 .block(host_buffer.read(cx).serialize_ops(None, cx));
             let mut buffer = Buffer::from_proto(1, state, None).unwrap();
             buffer
@@ -4184,7 +4186,7 @@ mod tests {
                 .unwrap();
             buffer
         });
-        let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(guest_buffer.clone(), cx));
+        let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(guest_buffer.clone(), cx));
         let snapshot = multibuffer.read(cx).snapshot(cx);
         assert_eq!(snapshot.text(), "a");
 
@@ -4200,17 +4202,17 @@ mod tests {
     #[gpui::test]
     fn test_excerpt_boundaries_and_clipping(cx: &mut AppContext) {
         let buffer_1 =
-            cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, sample_text(6, 6, 'a')));
+            cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(6, 6, 'a')));
         let buffer_2 =
-            cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, sample_text(6, 6, 'g')));
-        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
+            cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(6, 6, 'g')));
+        let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
 
-        let events = Rc::new(RefCell::new(Vec::<Event>::new()));
+        let events = Arc::new(RwLock::new(Vec::<Event>::new()));
         multibuffer.update(cx, |_, cx| {
             let events = events.clone();
             cx.subscribe(&multibuffer, move |_, _, event, _| {
                 if let Event::Edited { .. } = event {
-                    events.borrow_mut().push(event.clone())
+                    events.write().push(event.clone())
                 }
             })
             .detach();
@@ -4263,7 +4265,7 @@ mod tests {
 
         // Adding excerpts emits an edited event.
         assert_eq!(
-            events.borrow().as_slice(),
+            events.read().as_slice(),
             &[
                 Event::Edited {
                     sigleton_buffer_edited: false
@@ -4436,13 +4438,13 @@ mod tests {
     #[gpui::test]
     fn test_excerpt_events(cx: &mut AppContext) {
         let buffer_1 =
-            cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, sample_text(10, 3, 'a')));
+            cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(10, 3, 'a')));
         let buffer_2 =
-            cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, sample_text(10, 3, 'm')));
+            cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(10, 3, 'm')));
 
-        let leader_multibuffer = cx.add_model(|_| MultiBuffer::new(0));
-        let follower_multibuffer = cx.add_model(|_| MultiBuffer::new(0));
-        let follower_edit_event_count = Rc::new(RefCell::new(0));
+        let leader_multibuffer = cx.new_model(|_| MultiBuffer::new(0));
+        let follower_multibuffer = cx.new_model(|_| MultiBuffer::new(0));
+        let follower_edit_event_count = Arc::new(RwLock::new(0));
 
         follower_multibuffer.update(cx, |_, cx| {
             let follower_edit_event_count = follower_edit_event_count.clone();
@@ -4456,7 +4458,7 @@ mod tests {
                     } => follower.insert_excerpts_with_ids_after(predecessor, buffer, excerpts, cx),
                     Event::ExcerptsRemoved { ids } => follower.remove_excerpts(ids, cx),
                     Event::Edited { .. } => {
-                        *follower_edit_event_count.borrow_mut() += 1;
+                        *follower_edit_event_count.write() += 1;
                     }
                     _ => {}
                 },
@@ -4499,7 +4501,7 @@ mod tests {
             leader_multibuffer.read(cx).snapshot(cx).text(),
             follower_multibuffer.read(cx).snapshot(cx).text(),
         );
-        assert_eq!(*follower_edit_event_count.borrow(), 2);
+        assert_eq!(*follower_edit_event_count.read(), 2);
 
         leader_multibuffer.update(cx, |leader, cx| {
             let excerpt_ids = leader.excerpt_ids();
@@ -4509,7 +4511,7 @@ mod tests {
             leader_multibuffer.read(cx).snapshot(cx).text(),
             follower_multibuffer.read(cx).snapshot(cx).text(),
         );
-        assert_eq!(*follower_edit_event_count.borrow(), 3);
+        assert_eq!(*follower_edit_event_count.read(), 3);
 
         // Removing an empty set of excerpts is a noop.
         leader_multibuffer.update(cx, |leader, cx| {
@@ -4519,7 +4521,7 @@ mod tests {
             leader_multibuffer.read(cx).snapshot(cx).text(),
             follower_multibuffer.read(cx).snapshot(cx).text(),
         );
-        assert_eq!(*follower_edit_event_count.borrow(), 3);
+        assert_eq!(*follower_edit_event_count.read(), 3);
 
         // Adding an empty set of excerpts is a noop.
         leader_multibuffer.update(cx, |leader, cx| {
@@ -4529,7 +4531,7 @@ mod tests {
             leader_multibuffer.read(cx).snapshot(cx).text(),
             follower_multibuffer.read(cx).snapshot(cx).text(),
         );
-        assert_eq!(*follower_edit_event_count.borrow(), 3);
+        assert_eq!(*follower_edit_event_count.read(), 3);
 
         leader_multibuffer.update(cx, |leader, cx| {
             leader.clear(cx);
@@ -4538,14 +4540,14 @@ mod tests {
             leader_multibuffer.read(cx).snapshot(cx).text(),
             follower_multibuffer.read(cx).snapshot(cx).text(),
         );
-        assert_eq!(*follower_edit_event_count.borrow(), 4);
+        assert_eq!(*follower_edit_event_count.read(), 4);
     }
 
     #[gpui::test]
     fn test_push_excerpts_with_context_lines(cx: &mut AppContext) {
         let buffer =
-            cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, sample_text(20, 3, 'a')));
-        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
+            cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(20, 3, 'a')));
+        let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
         let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| {
             multibuffer.push_excerpts_with_context_lines(
                 buffer.clone(),
@@ -4581,8 +4583,8 @@ mod tests {
     #[gpui::test]
     async fn test_stream_excerpts_with_context_lines(cx: &mut TestAppContext) {
         let buffer =
-            cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, sample_text(20, 3, 'a')));
-        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
+            cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(20, 3, 'a')));
+        let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
         let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| {
             let snapshot = buffer.read(cx);
             let ranges = vec![
@@ -4596,7 +4598,7 @@ mod tests {
 
         let anchor_ranges = anchor_ranges.collect::<Vec<_>>().await;
 
-        let snapshot = multibuffer.read_with(cx, |multibuffer, cx| multibuffer.snapshot(cx));
+        let snapshot = multibuffer.update(cx, |multibuffer, cx| multibuffer.snapshot(cx));
         assert_eq!(
             snapshot.text(),
             "bbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\n\nnnn\nooo\nppp\nqqq\nrrr\n"
@@ -4617,7 +4619,7 @@ mod tests {
 
     #[gpui::test]
     fn test_empty_multibuffer(cx: &mut AppContext) {
-        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
+        let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
 
         let snapshot = multibuffer.read(cx).snapshot(cx);
         assert_eq!(snapshot.text(), "");
@@ -4627,8 +4629,8 @@ mod tests {
 
     #[gpui::test]
     fn test_singleton_multibuffer_anchors(cx: &mut AppContext) {
-        let buffer = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "abcd"));
-        let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
+        let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcd"));
+        let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
         let old_snapshot = multibuffer.read(cx).snapshot(cx);
         buffer.update(cx, |buffer, cx| {
             buffer.edit([(0..0, "X")], None, cx);
@@ -4647,9 +4649,9 @@ mod tests {
 
     #[gpui::test]
     fn test_multibuffer_anchors(cx: &mut AppContext) {
-        let buffer_1 = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "abcd"));
-        let buffer_2 = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "efghi"));
-        let multibuffer = cx.add_model(|cx| {
+        let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcd"));
+        let buffer_2 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "efghi"));
+        let multibuffer = cx.new_model(|cx| {
             let mut multibuffer = MultiBuffer::new(0);
             multibuffer.push_excerpts(
                 buffer_1.clone(),
@@ -4705,9 +4707,10 @@ mod tests {
 
     #[gpui::test]
     fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut AppContext) {
-        let buffer_1 = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "abcd"));
-        let buffer_2 = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "ABCDEFGHIJKLMNOP"));
-        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
+        let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcd"));
+        let buffer_2 =
+            cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "ABCDEFGHIJKLMNOP"));
+        let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
 
         // Create an insertion id in buffer 1 that doesn't exist in buffer 2.
         // Add an excerpt from buffer 1 that spans this new insertion.
@@ -4840,10 +4843,10 @@ mod tests {
             .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
             .unwrap_or(10);
 
-        let mut buffers: Vec<ModelHandle<Buffer>> = Vec::new();
-        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
+        let mut buffers: Vec<Model<Buffer>> = Vec::new();
+        let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
         let mut excerpt_ids = Vec::<ExcerptId>::new();
-        let mut expected_excerpts = Vec::<(ModelHandle<Buffer>, Range<text::Anchor>)>::new();
+        let mut expected_excerpts = Vec::<(Model<Buffer>, Range<text::Anchor>)>::new();
         let mut anchors = Vec::new();
         let mut old_versions = Vec::new();
 
@@ -4918,7 +4921,7 @@ mod tests {
                             .take(10)
                             .collect::<String>();
                         buffers.push(
-                            cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, base_text)),
+                            cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), base_text)),
                         );
                         buffers.last().unwrap()
                     } else {
@@ -5258,11 +5261,12 @@ mod tests {
 
     #[gpui::test]
     fn test_history(cx: &mut AppContext) {
-        cx.set_global(SettingsStore::test(cx));
+        let test_settings = SettingsStore::test(cx);
+        cx.set_global(test_settings);
 
-        let buffer_1 = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "1234"));
-        let buffer_2 = cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "5678"));
-        let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
+        let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "1234"));
+        let buffer_2 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "5678"));
+        let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
         let group_interval = multibuffer.read(cx).history.group_interval;
         multibuffer.update(cx, |multibuffer, cx| {
             multibuffer.push_excerpts(

crates/multi_buffer2/Cargo.toml 🔗

@@ -1,78 +0,0 @@
-[package]
-name = "multi_buffer2"
-version = "0.1.0"
-edition = "2021"
-publish = false
-
-[lib]
-path = "src/multi_buffer2.rs"
-doctest = false
-
-[features]
-test-support = [
-    "copilot/test-support",
-    "text/test-support",
-    "language/test-support",
-    "gpui/test-support",
-    "util/test-support",
-    "tree-sitter-rust",
-    "tree-sitter-typescript"
-]
-
-[dependencies]
-client = { package = "client2", path = "../client2" }
-clock = { path = "../clock" }
-collections = { path = "../collections" }
-git = { package = "git3", path = "../git3" }
-gpui = { package = "gpui2", path = "../gpui2" }
-language = { package = "language2", path = "../language2" }
-lsp = { package = "lsp2", path = "../lsp2" }
-rich_text = { package = "rich_text2", path = "../rich_text2" }
-settings = { package = "settings2", path = "../settings2" }
-snippet = { path = "../snippet" }
-sum_tree = { path = "../sum_tree" }
-text = { package = "text2", path = "../text2" }
-theme = { package = "theme2", path = "../theme2" }
-util = { path = "../util" }
-
-aho-corasick = "1.1"
-anyhow.workspace = true
-convert_case = "0.6.0"
-futures.workspace = true
-indoc = "1.0.4"
-itertools = "0.10"
-lazy_static.workspace = true
-log.workspace = true
-ordered-float.workspace = true
-parking_lot.workspace = true
-postage.workspace = true
-pulldown-cmark = { version = "0.9.2", default-features = false }
-rand.workspace = true
-schemars.workspace = true
-serde.workspace = true
-serde_derive.workspace = true
-smallvec.workspace = true
-smol.workspace = true
-
-tree-sitter-rust = { workspace = true, optional = true }
-tree-sitter-html = { workspace = true, optional = true }
-tree-sitter-typescript = { workspace = true, optional = true }
-
-[dev-dependencies]
-copilot = { package = "copilot2", path = "../copilot2", features = ["test-support"] }
-text = { package = "text2", path = "../text2", features = ["test-support"] }
-language = { package = "language2", path = "../language2", features = ["test-support"] }
-lsp = { package = "lsp2", path = "../lsp2", features = ["test-support"] }
-gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
-util = { path = "../util", features = ["test-support"] }
-project = { package = "project2", path = "../project2", features = ["test-support"] }
-settings = { package = "settings2", path = "../settings2", features = ["test-support"] }
-
-ctor.workspace = true
-env_logger.workspace = true
-rand.workspace = true
-unindent.workspace = true
-tree-sitter.workspace = true
-tree-sitter-rust.workspace = true
-tree-sitter-html.workspace = true
-tree-sitter-typescript.workspace = true

crates/multi_buffer2/src/anchor.rs 🔗

@@ -1,138 +0,0 @@
-use super::{ExcerptId, MultiBufferSnapshot, ToOffset, ToOffsetUtf16, ToPoint};
-use language::{OffsetUtf16, Point, TextDimension};
-use std::{
-    cmp::Ordering,
-    ops::{Range, Sub},
-};
-use sum_tree::Bias;
-
-#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)]
-pub struct Anchor {
-    pub buffer_id: Option<u64>,
-    pub excerpt_id: ExcerptId,
-    pub text_anchor: text::Anchor,
-}
-
-impl Anchor {
-    pub fn min() -> Self {
-        Self {
-            buffer_id: None,
-            excerpt_id: ExcerptId::min(),
-            text_anchor: text::Anchor::MIN,
-        }
-    }
-
-    pub fn max() -> Self {
-        Self {
-            buffer_id: None,
-            excerpt_id: ExcerptId::max(),
-            text_anchor: text::Anchor::MAX,
-        }
-    }
-
-    pub fn cmp(&self, other: &Anchor, snapshot: &MultiBufferSnapshot) -> Ordering {
-        let excerpt_id_cmp = self.excerpt_id.cmp(&other.excerpt_id, snapshot);
-        if excerpt_id_cmp.is_eq() {
-            if self.excerpt_id == ExcerptId::min() || self.excerpt_id == ExcerptId::max() {
-                Ordering::Equal
-            } else if let Some(excerpt) = snapshot.excerpt(self.excerpt_id) {
-                self.text_anchor.cmp(&other.text_anchor, &excerpt.buffer)
-            } else {
-                Ordering::Equal
-            }
-        } else {
-            excerpt_id_cmp
-        }
-    }
-
-    pub fn bias(&self) -> Bias {
-        self.text_anchor.bias
-    }
-
-    pub fn bias_left(&self, snapshot: &MultiBufferSnapshot) -> Anchor {
-        if self.text_anchor.bias != Bias::Left {
-            if let Some(excerpt) = snapshot.excerpt(self.excerpt_id) {
-                return Self {
-                    buffer_id: self.buffer_id,
-                    excerpt_id: self.excerpt_id.clone(),
-                    text_anchor: self.text_anchor.bias_left(&excerpt.buffer),
-                };
-            }
-        }
-        self.clone()
-    }
-
-    pub fn bias_right(&self, snapshot: &MultiBufferSnapshot) -> Anchor {
-        if self.text_anchor.bias != Bias::Right {
-            if let Some(excerpt) = snapshot.excerpt(self.excerpt_id) {
-                return Self {
-                    buffer_id: self.buffer_id,
-                    excerpt_id: self.excerpt_id.clone(),
-                    text_anchor: self.text_anchor.bias_right(&excerpt.buffer),
-                };
-            }
-        }
-        self.clone()
-    }
-
-    pub fn summary<D>(&self, snapshot: &MultiBufferSnapshot) -> D
-    where
-        D: TextDimension + Ord + Sub<D, Output = D>,
-    {
-        snapshot.summary_for_anchor(self)
-    }
-
-    pub fn is_valid(&self, snapshot: &MultiBufferSnapshot) -> bool {
-        if *self == Anchor::min() || *self == Anchor::max() {
-            true
-        } else if let Some(excerpt) = snapshot.excerpt(self.excerpt_id) {
-            excerpt.contains(self)
-                && (self.text_anchor == excerpt.range.context.start
-                    || self.text_anchor == excerpt.range.context.end
-                    || self.text_anchor.is_valid(&excerpt.buffer))
-        } else {
-            false
-        }
-    }
-}
-
-impl ToOffset for Anchor {
-    fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize {
-        self.summary(snapshot)
-    }
-}
-
-impl ToOffsetUtf16 for Anchor {
-    fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16 {
-        self.summary(snapshot)
-    }
-}
-
-impl ToPoint for Anchor {
-    fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
-        self.summary(snapshot)
-    }
-}
-
-pub trait AnchorRangeExt {
-    fn cmp(&self, b: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> Ordering;
-    fn to_offset(&self, content: &MultiBufferSnapshot) -> Range<usize>;
-    fn to_point(&self, content: &MultiBufferSnapshot) -> Range<Point>;
-}
-
-impl AnchorRangeExt for Range<Anchor> {
-    fn cmp(&self, other: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> Ordering {
-        match self.start.cmp(&other.start, buffer) {
-            Ordering::Equal => other.end.cmp(&self.end, buffer),
-            ord => ord,
-        }
-    }
-
-    fn to_offset(&self, content: &MultiBufferSnapshot) -> Range<usize> {
-        self.start.to_offset(content)..self.end.to_offset(content)
-    }
-
-    fn to_point(&self, content: &MultiBufferSnapshot) -> Range<Point> {
-        self.start.to_point(content)..self.end.to_point(content)
-    }
-}

crates/multi_buffer2/src/multi_buffer2.rs 🔗

@@ -1,5390 +0,0 @@
-mod anchor;
-
-pub use anchor::{Anchor, AnchorRangeExt};
-use anyhow::{anyhow, Result};
-use clock::ReplicaId;
-use collections::{BTreeMap, Bound, HashMap, HashSet};
-use futures::{channel::mpsc, SinkExt};
-use git::diff::DiffHunk;
-use gpui::{AppContext, EventEmitter, Model, ModelContext};
-pub use language::Completion;
-use language::{
-    char_kind,
-    language_settings::{language_settings, LanguageSettings},
-    AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, CursorShape,
-    DiagnosticEntry, File, IndentSize, Language, LanguageScope, OffsetRangeExt, OffsetUtf16,
-    Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, ToOffset as _,
-    ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, Unclipped,
-};
-use std::{
-    borrow::Cow,
-    cell::{Ref, RefCell},
-    cmp, fmt,
-    future::Future,
-    io,
-    iter::{self, FromIterator},
-    mem,
-    ops::{Range, RangeBounds, Sub},
-    str,
-    sync::Arc,
-    time::{Duration, Instant},
-};
-use sum_tree::{Bias, Cursor, SumTree};
-use text::{
-    locator::Locator,
-    subscription::{Subscription, Topic},
-    Edit, TextSummary,
-};
-use theme::SyntaxTheme;
-use util::post_inc;
-
-#[cfg(any(test, feature = "test-support"))]
-use gpui::Context;
-
-const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize];
-
-#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
-pub struct ExcerptId(usize);
-
-pub struct MultiBuffer {
-    snapshot: RefCell<MultiBufferSnapshot>,
-    buffers: RefCell<HashMap<u64, BufferState>>,
-    next_excerpt_id: usize,
-    subscriptions: Topic,
-    singleton: bool,
-    replica_id: ReplicaId,
-    history: History,
-    title: Option<String>,
-}
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum Event {
-    ExcerptsAdded {
-        buffer: Model<Buffer>,
-        predecessor: ExcerptId,
-        excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
-    },
-    ExcerptsRemoved {
-        ids: Vec<ExcerptId>,
-    },
-    ExcerptsEdited {
-        ids: Vec<ExcerptId>,
-    },
-    Edited {
-        sigleton_buffer_edited: bool,
-    },
-    TransactionUndone {
-        transaction_id: TransactionId,
-    },
-    Reloaded,
-    DiffBaseChanged,
-    LanguageChanged,
-    Reparsed,
-    Saved,
-    FileHandleChanged,
-    Closed,
-    DirtyChanged,
-    DiagnosticsUpdated,
-}
-
-#[derive(Clone)]
-struct History {
-    next_transaction_id: TransactionId,
-    undo_stack: Vec<Transaction>,
-    redo_stack: Vec<Transaction>,
-    transaction_depth: usize,
-    group_interval: Duration,
-}
-
-#[derive(Clone)]
-struct Transaction {
-    id: TransactionId,
-    buffer_transactions: HashMap<u64, text::TransactionId>,
-    first_edit_at: Instant,
-    last_edit_at: Instant,
-    suppress_grouping: bool,
-}
-
-pub trait ToOffset: 'static + fmt::Debug {
-    fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize;
-}
-
-pub trait ToOffsetUtf16: 'static + fmt::Debug {
-    fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16;
-}
-
-pub trait ToPoint: 'static + fmt::Debug {
-    fn to_point(&self, snapshot: &MultiBufferSnapshot) -> Point;
-}
-
-pub trait ToPointUtf16: 'static + fmt::Debug {
-    fn to_point_utf16(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16;
-}
-
-struct BufferState {
-    buffer: Model<Buffer>,
-    last_version: clock::Global,
-    last_parse_count: usize,
-    last_selections_update_count: usize,
-    last_diagnostics_update_count: usize,
-    last_file_update_count: usize,
-    last_git_diff_update_count: usize,
-    excerpts: Vec<Locator>,
-    _subscriptions: [gpui::Subscription; 2],
-}
-
-#[derive(Clone, Default)]
-pub struct MultiBufferSnapshot {
-    singleton: bool,
-    excerpts: SumTree<Excerpt>,
-    excerpt_ids: SumTree<ExcerptIdMapping>,
-    parse_count: usize,
-    diagnostics_update_count: usize,
-    trailing_excerpt_update_count: usize,
-    git_diff_update_count: usize,
-    edit_count: usize,
-    is_dirty: bool,
-    has_conflict: bool,
-}
-
-pub struct ExcerptBoundary {
-    pub id: ExcerptId,
-    pub row: u32,
-    pub buffer: BufferSnapshot,
-    pub range: ExcerptRange<text::Anchor>,
-    pub starts_new_buffer: bool,
-}
-
-#[derive(Clone)]
-struct Excerpt {
-    id: ExcerptId,
-    locator: Locator,
-    buffer_id: u64,
-    buffer: BufferSnapshot,
-    range: ExcerptRange<text::Anchor>,
-    max_buffer_row: u32,
-    text_summary: TextSummary,
-    has_trailing_newline: bool,
-}
-
-#[derive(Clone, Debug)]
-struct ExcerptIdMapping {
-    id: ExcerptId,
-    locator: Locator,
-}
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct ExcerptRange<T> {
-    pub context: Range<T>,
-    pub primary: Option<Range<T>>,
-}
-
-#[derive(Clone, Debug, Default)]
-struct ExcerptSummary {
-    excerpt_id: ExcerptId,
-    excerpt_locator: Locator,
-    max_buffer_row: u32,
-    text: TextSummary,
-}
-
-#[derive(Clone)]
-pub struct MultiBufferRows<'a> {
-    buffer_row_range: Range<u32>,
-    excerpts: Cursor<'a, Excerpt, Point>,
-}
-
-pub struct MultiBufferChunks<'a> {
-    range: Range<usize>,
-    excerpts: Cursor<'a, Excerpt, usize>,
-    excerpt_chunks: Option<ExcerptChunks<'a>>,
-    language_aware: bool,
-}
-
-pub struct MultiBufferBytes<'a> {
-    range: Range<usize>,
-    excerpts: Cursor<'a, Excerpt, usize>,
-    excerpt_bytes: Option<ExcerptBytes<'a>>,
-    chunk: &'a [u8],
-}
-
-pub struct ReversedMultiBufferBytes<'a> {
-    range: Range<usize>,
-    excerpts: Cursor<'a, Excerpt, usize>,
-    excerpt_bytes: Option<ExcerptBytes<'a>>,
-    chunk: &'a [u8],
-}
-
-struct ExcerptChunks<'a> {
-    content_chunks: BufferChunks<'a>,
-    footer_height: usize,
-}
-
-struct ExcerptBytes<'a> {
-    content_bytes: text::Bytes<'a>,
-    footer_height: usize,
-}
-
-impl MultiBuffer {
-    pub fn new(replica_id: ReplicaId) -> Self {
-        Self {
-            snapshot: Default::default(),
-            buffers: Default::default(),
-            next_excerpt_id: 1,
-            subscriptions: Default::default(),
-            singleton: false,
-            replica_id,
-            history: History {
-                next_transaction_id: Default::default(),
-                undo_stack: Default::default(),
-                redo_stack: Default::default(),
-                transaction_depth: 0,
-                group_interval: Duration::from_millis(300),
-            },
-            title: Default::default(),
-        }
-    }
-
-    pub fn clone(&self, new_cx: &mut ModelContext<Self>) -> Self {
-        let mut buffers = HashMap::default();
-        for (buffer_id, buffer_state) in self.buffers.borrow().iter() {
-            buffers.insert(
-                *buffer_id,
-                BufferState {
-                    buffer: buffer_state.buffer.clone(),
-                    last_version: buffer_state.last_version.clone(),
-                    last_parse_count: buffer_state.last_parse_count,
-                    last_selections_update_count: buffer_state.last_selections_update_count,
-                    last_diagnostics_update_count: buffer_state.last_diagnostics_update_count,
-                    last_file_update_count: buffer_state.last_file_update_count,
-                    last_git_diff_update_count: buffer_state.last_git_diff_update_count,
-                    excerpts: buffer_state.excerpts.clone(),
-                    _subscriptions: [
-                        new_cx.observe(&buffer_state.buffer, |_, _, cx| cx.notify()),
-                        new_cx.subscribe(&buffer_state.buffer, Self::on_buffer_event),
-                    ],
-                },
-            );
-        }
-        Self {
-            snapshot: RefCell::new(self.snapshot.borrow().clone()),
-            buffers: RefCell::new(buffers),
-            next_excerpt_id: 1,
-            subscriptions: Default::default(),
-            singleton: self.singleton,
-            replica_id: self.replica_id,
-            history: self.history.clone(),
-            title: self.title.clone(),
-        }
-    }
-
-    pub fn with_title(mut self, title: String) -> Self {
-        self.title = Some(title);
-        self
-    }
-
-    pub fn singleton(buffer: Model<Buffer>, cx: &mut ModelContext<Self>) -> Self {
-        let mut this = Self::new(buffer.read(cx).replica_id());
-        this.singleton = true;
-        this.push_excerpts(
-            buffer,
-            [ExcerptRange {
-                context: text::Anchor::MIN..text::Anchor::MAX,
-                primary: None,
-            }],
-            cx,
-        );
-        this.snapshot.borrow_mut().singleton = true;
-        this
-    }
-
-    pub fn replica_id(&self) -> ReplicaId {
-        self.replica_id
-    }
-
-    pub fn snapshot(&self, cx: &AppContext) -> MultiBufferSnapshot {
-        self.sync(cx);
-        self.snapshot.borrow().clone()
-    }
-
-    pub fn read(&self, cx: &AppContext) -> Ref<MultiBufferSnapshot> {
-        self.sync(cx);
-        self.snapshot.borrow()
-    }
-
-    pub fn as_singleton(&self) -> Option<Model<Buffer>> {
-        if self.singleton {
-            return Some(
-                self.buffers
-                    .borrow()
-                    .values()
-                    .next()
-                    .unwrap()
-                    .buffer
-                    .clone(),
-            );
-        } else {
-            None
-        }
-    }
-
-    pub fn is_singleton(&self) -> bool {
-        self.singleton
-    }
-
-    pub fn subscribe(&mut self) -> Subscription {
-        self.subscriptions.subscribe()
-    }
-
-    pub fn is_dirty(&self, cx: &AppContext) -> bool {
-        self.read(cx).is_dirty()
-    }
-
-    pub fn has_conflict(&self, cx: &AppContext) -> bool {
-        self.read(cx).has_conflict()
-    }
-
-    // The `is_empty` signature doesn't match what clippy expects
-    #[allow(clippy::len_without_is_empty)]
-    pub fn len(&self, cx: &AppContext) -> usize {
-        self.read(cx).len()
-    }
-
-    pub fn is_empty(&self, cx: &AppContext) -> bool {
-        self.len(cx) != 0
-    }
-
-    pub fn symbols_containing<T: ToOffset>(
-        &self,
-        offset: T,
-        theme: Option<&SyntaxTheme>,
-        cx: &AppContext,
-    ) -> Option<(u64, Vec<OutlineItem<Anchor>>)> {
-        self.read(cx).symbols_containing(offset, theme)
-    }
-
-    pub fn edit<I, S, T>(
-        &mut self,
-        edits: I,
-        mut autoindent_mode: Option<AutoindentMode>,
-        cx: &mut ModelContext<Self>,
-    ) where
-        I: IntoIterator<Item = (Range<S>, T)>,
-        S: ToOffset,
-        T: Into<Arc<str>>,
-    {
-        if self.buffers.borrow().is_empty() {
-            return;
-        }
-
-        let snapshot = self.read(cx);
-        let edits = edits.into_iter().map(|(range, new_text)| {
-            let mut range = range.start.to_offset(&snapshot)..range.end.to_offset(&snapshot);
-            if range.start > range.end {
-                mem::swap(&mut range.start, &mut range.end);
-            }
-            (range, new_text)
-        });
-
-        if let Some(buffer) = self.as_singleton() {
-            return buffer.update(cx, |buffer, cx| {
-                buffer.edit(edits, autoindent_mode, cx);
-            });
-        }
-
-        let original_indent_columns = match &mut autoindent_mode {
-            Some(AutoindentMode::Block {
-                original_indent_columns,
-            }) => mem::take(original_indent_columns),
-            _ => Default::default(),
-        };
-
-        struct BufferEdit {
-            range: Range<usize>,
-            new_text: Arc<str>,
-            is_insertion: bool,
-            original_indent_column: u32,
-        }
-        let mut buffer_edits: HashMap<u64, Vec<BufferEdit>> = Default::default();
-        let mut edited_excerpt_ids = Vec::new();
-        let mut cursor = snapshot.excerpts.cursor::<usize>();
-        for (ix, (range, new_text)) in edits.enumerate() {
-            let new_text: Arc<str> = new_text.into();
-            let original_indent_column = original_indent_columns.get(ix).copied().unwrap_or(0);
-            cursor.seek(&range.start, Bias::Right, &());
-            if cursor.item().is_none() && range.start == *cursor.start() {
-                cursor.prev(&());
-            }
-            let start_excerpt = cursor.item().expect("start offset out of bounds");
-            let start_overshoot = range.start - cursor.start();
-            let buffer_start = start_excerpt
-                .range
-                .context
-                .start
-                .to_offset(&start_excerpt.buffer)
-                + start_overshoot;
-            edited_excerpt_ids.push(start_excerpt.id);
-
-            cursor.seek(&range.end, Bias::Right, &());
-            if cursor.item().is_none() && range.end == *cursor.start() {
-                cursor.prev(&());
-            }
-            let end_excerpt = cursor.item().expect("end offset out of bounds");
-            let end_overshoot = range.end - cursor.start();
-            let buffer_end = end_excerpt
-                .range
-                .context
-                .start
-                .to_offset(&end_excerpt.buffer)
-                + end_overshoot;
-
-            if start_excerpt.id == end_excerpt.id {
-                buffer_edits
-                    .entry(start_excerpt.buffer_id)
-                    .or_insert(Vec::new())
-                    .push(BufferEdit {
-                        range: buffer_start..buffer_end,
-                        new_text,
-                        is_insertion: true,
-                        original_indent_column,
-                    });
-            } else {
-                edited_excerpt_ids.push(end_excerpt.id);
-                let start_excerpt_range = buffer_start
-                    ..start_excerpt
-                        .range
-                        .context
-                        .end
-                        .to_offset(&start_excerpt.buffer);
-                let end_excerpt_range = end_excerpt
-                    .range
-                    .context
-                    .start
-                    .to_offset(&end_excerpt.buffer)
-                    ..buffer_end;
-                buffer_edits
-                    .entry(start_excerpt.buffer_id)
-                    .or_insert(Vec::new())
-                    .push(BufferEdit {
-                        range: start_excerpt_range,
-                        new_text: new_text.clone(),
-                        is_insertion: true,
-                        original_indent_column,
-                    });
-                buffer_edits
-                    .entry(end_excerpt.buffer_id)
-                    .or_insert(Vec::new())
-                    .push(BufferEdit {
-                        range: end_excerpt_range,
-                        new_text: new_text.clone(),
-                        is_insertion: false,
-                        original_indent_column,
-                    });
-
-                cursor.seek(&range.start, Bias::Right, &());
-                cursor.next(&());
-                while let Some(excerpt) = cursor.item() {
-                    if excerpt.id == end_excerpt.id {
-                        break;
-                    }
-                    buffer_edits
-                        .entry(excerpt.buffer_id)
-                        .or_insert(Vec::new())
-                        .push(BufferEdit {
-                            range: excerpt.range.context.to_offset(&excerpt.buffer),
-                            new_text: new_text.clone(),
-                            is_insertion: false,
-                            original_indent_column,
-                        });
-                    edited_excerpt_ids.push(excerpt.id);
-                    cursor.next(&());
-                }
-            }
-        }
-
-        drop(cursor);
-        drop(snapshot);
-        // Non-generic part of edit, hoisted out to avoid blowing up LLVM IR.
-        fn tail(
-            this: &mut MultiBuffer,
-            buffer_edits: HashMap<u64, Vec<BufferEdit>>,
-            autoindent_mode: Option<AutoindentMode>,
-            edited_excerpt_ids: Vec<ExcerptId>,
-            cx: &mut ModelContext<MultiBuffer>,
-        ) {
-            for (buffer_id, mut edits) in buffer_edits {
-                edits.sort_unstable_by_key(|edit| edit.range.start);
-                this.buffers.borrow()[&buffer_id]
-                    .buffer
-                    .update(cx, |buffer, cx| {
-                        let mut edits = edits.into_iter().peekable();
-                        let mut insertions = Vec::new();
-                        let mut original_indent_columns = Vec::new();
-                        let mut deletions = Vec::new();
-                        let empty_str: Arc<str> = "".into();
-                        while let Some(BufferEdit {
-                            mut range,
-                            new_text,
-                            mut is_insertion,
-                            original_indent_column,
-                        }) = edits.next()
-                        {
-                            while let Some(BufferEdit {
-                                range: next_range,
-                                is_insertion: next_is_insertion,
-                                ..
-                            }) = edits.peek()
-                            {
-                                if range.end >= next_range.start {
-                                    range.end = cmp::max(next_range.end, range.end);
-                                    is_insertion |= *next_is_insertion;
-                                    edits.next();
-                                } else {
-                                    break;
-                                }
-                            }
-
-                            if is_insertion {
-                                original_indent_columns.push(original_indent_column);
-                                insertions.push((
-                                    buffer.anchor_before(range.start)
-                                        ..buffer.anchor_before(range.end),
-                                    new_text.clone(),
-                                ));
-                            } else if !range.is_empty() {
-                                deletions.push((
-                                    buffer.anchor_before(range.start)
-                                        ..buffer.anchor_before(range.end),
-                                    empty_str.clone(),
-                                ));
-                            }
-                        }
-
-                        let deletion_autoindent_mode =
-                            if let Some(AutoindentMode::Block { .. }) = autoindent_mode {
-                                Some(AutoindentMode::Block {
-                                    original_indent_columns: Default::default(),
-                                })
-                            } else {
-                                None
-                            };
-                        let insertion_autoindent_mode =
-                            if let Some(AutoindentMode::Block { .. }) = autoindent_mode {
-                                Some(AutoindentMode::Block {
-                                    original_indent_columns,
-                                })
-                            } else {
-                                None
-                            };
-
-                        buffer.edit(deletions, deletion_autoindent_mode, cx);
-                        buffer.edit(insertions, insertion_autoindent_mode, cx);
-                    })
-            }
-
-            cx.emit(Event::ExcerptsEdited {
-                ids: edited_excerpt_ids,
-            });
-        }
-        tail(self, buffer_edits, autoindent_mode, edited_excerpt_ids, cx);
-    }
-
-    pub fn start_transaction(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
-        self.start_transaction_at(Instant::now(), cx)
-    }
-
-    pub fn start_transaction_at(
-        &mut self,
-        now: Instant,
-        cx: &mut ModelContext<Self>,
-    ) -> Option<TransactionId> {
-        if let Some(buffer) = self.as_singleton() {
-            return buffer.update(cx, |buffer, _| buffer.start_transaction_at(now));
-        }
-
-        for BufferState { buffer, .. } in self.buffers.borrow().values() {
-            buffer.update(cx, |buffer, _| buffer.start_transaction_at(now));
-        }
-        self.history.start_transaction(now)
-    }
-
-    pub fn end_transaction(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
-        self.end_transaction_at(Instant::now(), cx)
-    }
-
-    pub fn end_transaction_at(
-        &mut self,
-        now: Instant,
-        cx: &mut ModelContext<Self>,
-    ) -> Option<TransactionId> {
-        if let Some(buffer) = self.as_singleton() {
-            return buffer.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx));
-        }
-
-        let mut buffer_transactions = HashMap::default();
-        for BufferState { buffer, .. } in self.buffers.borrow().values() {
-            if let Some(transaction_id) =
-                buffer.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
-            {
-                buffer_transactions.insert(buffer.read(cx).remote_id(), transaction_id);
-            }
-        }
-
-        if self.history.end_transaction(now, buffer_transactions) {
-            let transaction_id = self.history.group().unwrap();
-            Some(transaction_id)
-        } else {
-            None
-        }
-    }
-
-    pub fn merge_transactions(
-        &mut self,
-        transaction: TransactionId,
-        destination: TransactionId,
-        cx: &mut ModelContext<Self>,
-    ) {
-        if let Some(buffer) = self.as_singleton() {
-            buffer.update(cx, |buffer, _| {
-                buffer.merge_transactions(transaction, destination)
-            });
-        } else {
-            if let Some(transaction) = self.history.forget(transaction) {
-                if let Some(destination) = self.history.transaction_mut(destination) {
-                    for (buffer_id, buffer_transaction_id) in transaction.buffer_transactions {
-                        if let Some(destination_buffer_transaction_id) =
-                            destination.buffer_transactions.get(&buffer_id)
-                        {
-                            if let Some(state) = self.buffers.borrow().get(&buffer_id) {
-                                state.buffer.update(cx, |buffer, _| {
-                                    buffer.merge_transactions(
-                                        buffer_transaction_id,
-                                        *destination_buffer_transaction_id,
-                                    )
-                                });
-                            }
-                        } else {
-                            destination
-                                .buffer_transactions
-                                .insert(buffer_id, buffer_transaction_id);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    pub fn finalize_last_transaction(&mut self, cx: &mut ModelContext<Self>) {
-        self.history.finalize_last_transaction();
-        for BufferState { buffer, .. } in self.buffers.borrow().values() {
-            buffer.update(cx, |buffer, _| {
-                buffer.finalize_last_transaction();
-            });
-        }
-    }
-
-    pub fn push_transaction<'a, T>(&mut self, buffer_transactions: T, cx: &mut ModelContext<Self>)
-    where
-        T: IntoIterator<Item = (&'a Model<Buffer>, &'a language::Transaction)>,
-    {
-        self.history
-            .push_transaction(buffer_transactions, Instant::now(), cx);
-        self.history.finalize_last_transaction();
-    }
-
-    pub fn group_until_transaction(
-        &mut self,
-        transaction_id: TransactionId,
-        cx: &mut ModelContext<Self>,
-    ) {
-        if let Some(buffer) = self.as_singleton() {
-            buffer.update(cx, |buffer, _| {
-                buffer.group_until_transaction(transaction_id)
-            });
-        } else {
-            self.history.group_until(transaction_id);
-        }
-    }
-
-    pub fn set_active_selections(
-        &mut self,
-        selections: &[Selection<Anchor>],
-        line_mode: bool,
-        cursor_shape: CursorShape,
-        cx: &mut ModelContext<Self>,
-    ) {
-        let mut selections_by_buffer: HashMap<u64, Vec<Selection<text::Anchor>>> =
-            Default::default();
-        let snapshot = self.read(cx);
-        let mut cursor = snapshot.excerpts.cursor::<Option<&Locator>>();
-        for selection in selections {
-            let start_locator = snapshot.excerpt_locator_for_id(selection.start.excerpt_id);
-            let end_locator = snapshot.excerpt_locator_for_id(selection.end.excerpt_id);
-
-            cursor.seek(&Some(start_locator), Bias::Left, &());
-            while let Some(excerpt) = cursor.item() {
-                if excerpt.locator > *end_locator {
-                    break;
-                }
-
-                let mut start = excerpt.range.context.start;
-                let mut end = excerpt.range.context.end;
-                if excerpt.id == selection.start.excerpt_id {
-                    start = selection.start.text_anchor;
-                }
-                if excerpt.id == selection.end.excerpt_id {
-                    end = selection.end.text_anchor;
-                }
-                selections_by_buffer
-                    .entry(excerpt.buffer_id)
-                    .or_default()
-                    .push(Selection {
-                        id: selection.id,
-                        start,
-                        end,
-                        reversed: selection.reversed,
-                        goal: selection.goal,
-                    });
-
-                cursor.next(&());
-            }
-        }
-
-        for (buffer_id, buffer_state) in self.buffers.borrow().iter() {
-            if !selections_by_buffer.contains_key(buffer_id) {
-                buffer_state
-                    .buffer
-                    .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
-            }
-        }
-
-        for (buffer_id, mut selections) in selections_by_buffer {
-            self.buffers.borrow()[&buffer_id]
-                .buffer
-                .update(cx, |buffer, cx| {
-                    selections.sort_unstable_by(|a, b| a.start.cmp(&b.start, buffer));
-                    let mut selections = selections.into_iter().peekable();
-                    let merged_selections = Arc::from_iter(iter::from_fn(|| {
-                        let mut selection = selections.next()?;
-                        while let Some(next_selection) = selections.peek() {
-                            if selection.end.cmp(&next_selection.start, buffer).is_ge() {
-                                let next_selection = selections.next().unwrap();
-                                if next_selection.end.cmp(&selection.end, buffer).is_ge() {
-                                    selection.end = next_selection.end;
-                                }
-                            } else {
-                                break;
-                            }
-                        }
-                        Some(selection)
-                    }));
-                    buffer.set_active_selections(merged_selections, line_mode, cursor_shape, cx);
-                });
-        }
-    }
-
-    pub fn remove_active_selections(&mut self, cx: &mut ModelContext<Self>) {
-        for buffer in self.buffers.borrow().values() {
-            buffer
-                .buffer
-                .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
-        }
-    }
-
-    pub fn undo(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
-        let mut transaction_id = None;
-        if let Some(buffer) = self.as_singleton() {
-            transaction_id = buffer.update(cx, |buffer, cx| buffer.undo(cx));
-        } else {
-            while let Some(transaction) = self.history.pop_undo() {
-                let mut undone = false;
-                for (buffer_id, buffer_transaction_id) in &mut transaction.buffer_transactions {
-                    if let Some(BufferState { buffer, .. }) = self.buffers.borrow().get(buffer_id) {
-                        undone |= buffer.update(cx, |buffer, cx| {
-                            let undo_to = *buffer_transaction_id;
-                            if let Some(entry) = buffer.peek_undo_stack() {
-                                *buffer_transaction_id = entry.transaction_id();
-                            }
-                            buffer.undo_to_transaction(undo_to, cx)
-                        });
-                    }
-                }
-
-                if undone {
-                    transaction_id = Some(transaction.id);
-                    break;
-                }
-            }
-        }
-
-        if let Some(transaction_id) = transaction_id {
-            cx.emit(Event::TransactionUndone { transaction_id });
-        }
-
-        transaction_id
-    }
-
-    pub fn redo(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
-        if let Some(buffer) = self.as_singleton() {
-            return buffer.update(cx, |buffer, cx| buffer.redo(cx));
-        }
-
-        while let Some(transaction) = self.history.pop_redo() {
-            let mut redone = false;
-            for (buffer_id, buffer_transaction_id) in &mut transaction.buffer_transactions {
-                if let Some(BufferState { buffer, .. }) = self.buffers.borrow().get(buffer_id) {
-                    redone |= buffer.update(cx, |buffer, cx| {
-                        let redo_to = *buffer_transaction_id;
-                        if let Some(entry) = buffer.peek_redo_stack() {
-                            *buffer_transaction_id = entry.transaction_id();
-                        }
-                        buffer.redo_to_transaction(redo_to, cx)
-                    });
-                }
-            }
-
-            if redone {
-                return Some(transaction.id);
-            }
-        }
-
-        None
-    }
-
-    pub fn undo_transaction(&mut self, transaction_id: TransactionId, cx: &mut ModelContext<Self>) {
-        if let Some(buffer) = self.as_singleton() {
-            buffer.update(cx, |buffer, cx| buffer.undo_transaction(transaction_id, cx));
-        } else if let Some(transaction) = self.history.remove_from_undo(transaction_id) {
-            for (buffer_id, transaction_id) in &transaction.buffer_transactions {
-                if let Some(BufferState { buffer, .. }) = self.buffers.borrow().get(buffer_id) {
-                    buffer.update(cx, |buffer, cx| {
-                        buffer.undo_transaction(*transaction_id, cx)
-                    });
-                }
-            }
-        }
-    }
-
-    pub fn stream_excerpts_with_context_lines(
-        &mut self,
-        buffer: Model<Buffer>,
-        ranges: Vec<Range<text::Anchor>>,
-        context_line_count: u32,
-        cx: &mut ModelContext<Self>,
-    ) -> mpsc::Receiver<Range<Anchor>> {
-        let (buffer_id, buffer_snapshot) =
-            buffer.update(cx, |buffer, _| (buffer.remote_id(), buffer.snapshot()));
-
-        let (mut tx, rx) = mpsc::channel(256);
-        cx.spawn(move |this, mut cx| async move {
-            let mut excerpt_ranges = Vec::new();
-            let mut range_counts = Vec::new();
-            cx.background_executor()
-                .scoped(|scope| {
-                    scope.spawn(async {
-                        let (ranges, counts) =
-                            build_excerpt_ranges(&buffer_snapshot, &ranges, context_line_count);
-                        excerpt_ranges = ranges;
-                        range_counts = counts;
-                    });
-                })
-                .await;
-
-            let mut ranges = ranges.into_iter();
-            let mut range_counts = range_counts.into_iter();
-            for excerpt_ranges in excerpt_ranges.chunks(100) {
-                let excerpt_ids = match this.update(&mut cx, |this, cx| {
-                    this.push_excerpts(buffer.clone(), excerpt_ranges.iter().cloned(), cx)
-                }) {
-                    Ok(excerpt_ids) => excerpt_ids,
-                    Err(_) => return,
-                };
-
-                for (excerpt_id, range_count) in excerpt_ids.into_iter().zip(range_counts.by_ref())
-                {
-                    for range in ranges.by_ref().take(range_count) {
-                        let start = Anchor {
-                            buffer_id: Some(buffer_id),
-                            excerpt_id: excerpt_id.clone(),
-                            text_anchor: range.start,
-                        };
-                        let end = Anchor {
-                            buffer_id: Some(buffer_id),
-                            excerpt_id: excerpt_id.clone(),
-                            text_anchor: range.end,
-                        };
-                        if tx.send(start..end).await.is_err() {
-                            break;
-                        }
-                    }
-                }
-            }
-        })
-        .detach();
-
-        rx
-    }
-
-    pub fn push_excerpts<O>(
-        &mut self,
-        buffer: Model<Buffer>,
-        ranges: impl IntoIterator<Item = ExcerptRange<O>>,
-        cx: &mut ModelContext<Self>,
-    ) -> Vec<ExcerptId>
-    where
-        O: text::ToOffset,
-    {
-        self.insert_excerpts_after(ExcerptId::max(), buffer, ranges, cx)
-    }
-
-    pub fn push_excerpts_with_context_lines<O>(
-        &mut self,
-        buffer: Model<Buffer>,
-        ranges: Vec<Range<O>>,
-        context_line_count: u32,
-        cx: &mut ModelContext<Self>,
-    ) -> Vec<Range<Anchor>>
-    where
-        O: text::ToPoint + text::ToOffset,
-    {
-        let buffer_id = buffer.read(cx).remote_id();
-        let buffer_snapshot = buffer.read(cx).snapshot();
-        let (excerpt_ranges, range_counts) =
-            build_excerpt_ranges(&buffer_snapshot, &ranges, context_line_count);
-
-        let excerpt_ids = self.push_excerpts(buffer, excerpt_ranges, cx);
-
-        let mut anchor_ranges = Vec::new();
-        let mut ranges = ranges.into_iter();
-        for (excerpt_id, range_count) in excerpt_ids.into_iter().zip(range_counts.into_iter()) {
-            anchor_ranges.extend(ranges.by_ref().take(range_count).map(|range| {
-                let start = Anchor {
-                    buffer_id: Some(buffer_id),
-                    excerpt_id: excerpt_id.clone(),
-                    text_anchor: buffer_snapshot.anchor_after(range.start),
-                };
-                let end = Anchor {
-                    buffer_id: Some(buffer_id),
-                    excerpt_id: excerpt_id.clone(),
-                    text_anchor: buffer_snapshot.anchor_after(range.end),
-                };
-                start..end
-            }))
-        }
-        anchor_ranges
-    }
-
-    pub fn insert_excerpts_after<O>(
-        &mut self,
-        prev_excerpt_id: ExcerptId,
-        buffer: Model<Buffer>,
-        ranges: impl IntoIterator<Item = ExcerptRange<O>>,
-        cx: &mut ModelContext<Self>,
-    ) -> Vec<ExcerptId>
-    where
-        O: text::ToOffset,
-    {
-        let mut ids = Vec::new();
-        let mut next_excerpt_id = self.next_excerpt_id;
-        self.insert_excerpts_with_ids_after(
-            prev_excerpt_id,
-            buffer,
-            ranges.into_iter().map(|range| {
-                let id = ExcerptId(post_inc(&mut next_excerpt_id));
-                ids.push(id);
-                (id, range)
-            }),
-            cx,
-        );
-        ids
-    }
-
-    pub fn insert_excerpts_with_ids_after<O>(
-        &mut self,
-        prev_excerpt_id: ExcerptId,
-        buffer: Model<Buffer>,
-        ranges: impl IntoIterator<Item = (ExcerptId, ExcerptRange<O>)>,
-        cx: &mut ModelContext<Self>,
-    ) where
-        O: text::ToOffset,
-    {
-        assert_eq!(self.history.transaction_depth, 0);
-        let mut ranges = ranges.into_iter().peekable();
-        if ranges.peek().is_none() {
-            return Default::default();
-        }
-
-        self.sync(cx);
-
-        let buffer_id = buffer.read(cx).remote_id();
-        let buffer_snapshot = buffer.read(cx).snapshot();
-
-        let mut buffers = self.buffers.borrow_mut();
-        let buffer_state = buffers.entry(buffer_id).or_insert_with(|| BufferState {
-            last_version: buffer_snapshot.version().clone(),
-            last_parse_count: buffer_snapshot.parse_count(),
-            last_selections_update_count: buffer_snapshot.selections_update_count(),
-            last_diagnostics_update_count: buffer_snapshot.diagnostics_update_count(),
-            last_file_update_count: buffer_snapshot.file_update_count(),
-            last_git_diff_update_count: buffer_snapshot.git_diff_update_count(),
-            excerpts: Default::default(),
-            _subscriptions: [
-                cx.observe(&buffer, |_, _, cx| cx.notify()),
-                cx.subscribe(&buffer, Self::on_buffer_event),
-            ],
-            buffer: buffer.clone(),
-        });
-
-        let mut snapshot = self.snapshot.borrow_mut();
-
-        let mut prev_locator = snapshot.excerpt_locator_for_id(prev_excerpt_id).clone();
-        let mut new_excerpt_ids = mem::take(&mut snapshot.excerpt_ids);
-        let mut cursor = snapshot.excerpts.cursor::<Option<&Locator>>();
-        let mut new_excerpts = cursor.slice(&prev_locator, Bias::Right, &());
-        prev_locator = cursor.start().unwrap_or(Locator::min_ref()).clone();
-
-        let edit_start = new_excerpts.summary().text.len;
-        new_excerpts.update_last(
-            |excerpt| {
-                excerpt.has_trailing_newline = true;
-            },
-            &(),
-        );
-
-        let next_locator = if let Some(excerpt) = cursor.item() {
-            excerpt.locator.clone()
-        } else {
-            Locator::max()
-        };
-
-        let mut excerpts = Vec::new();
-        while let Some((id, range)) = ranges.next() {
-            let locator = Locator::between(&prev_locator, &next_locator);
-            if let Err(ix) = buffer_state.excerpts.binary_search(&locator) {
-                buffer_state.excerpts.insert(ix, locator.clone());
-            }
-            let range = ExcerptRange {
-                context: buffer_snapshot.anchor_before(&range.context.start)
-                    ..buffer_snapshot.anchor_after(&range.context.end),
-                primary: range.primary.map(|primary| {
-                    buffer_snapshot.anchor_before(&primary.start)
-                        ..buffer_snapshot.anchor_after(&primary.end)
-                }),
-            };
-            if id.0 >= self.next_excerpt_id {
-                self.next_excerpt_id = id.0 + 1;
-            }
-            excerpts.push((id, range.clone()));
-            let excerpt = Excerpt::new(
-                id,
-                locator.clone(),
-                buffer_id,
-                buffer_snapshot.clone(),
-                range,
-                ranges.peek().is_some() || cursor.item().is_some(),
-            );
-            new_excerpts.push(excerpt, &());
-            prev_locator = locator.clone();
-            new_excerpt_ids.push(ExcerptIdMapping { id, locator }, &());
-        }
-
-        let edit_end = new_excerpts.summary().text.len;
-
-        let suffix = cursor.suffix(&());
-        let changed_trailing_excerpt = suffix.is_empty();
-        new_excerpts.append(suffix, &());
-        drop(cursor);
-        snapshot.excerpts = new_excerpts;
-        snapshot.excerpt_ids = new_excerpt_ids;
-        if changed_trailing_excerpt {
-            snapshot.trailing_excerpt_update_count += 1;
-        }
-
-        self.subscriptions.publish_mut([Edit {
-            old: edit_start..edit_start,
-            new: edit_start..edit_end,
-        }]);
-        cx.emit(Event::Edited {
-            sigleton_buffer_edited: false,
-        });
-        cx.emit(Event::ExcerptsAdded {
-            buffer,
-            predecessor: prev_excerpt_id,
-            excerpts,
-        });
-        cx.notify();
-    }
-
-    pub fn clear(&mut self, cx: &mut ModelContext<Self>) {
-        self.sync(cx);
-        let ids = self.excerpt_ids();
-        self.buffers.borrow_mut().clear();
-        let mut snapshot = self.snapshot.borrow_mut();
-        let prev_len = snapshot.len();
-        snapshot.excerpts = Default::default();
-        snapshot.trailing_excerpt_update_count += 1;
-        snapshot.is_dirty = false;
-        snapshot.has_conflict = false;
-
-        self.subscriptions.publish_mut([Edit {
-            old: 0..prev_len,
-            new: 0..0,
-        }]);
-        cx.emit(Event::Edited {
-            sigleton_buffer_edited: false,
-        });
-        cx.emit(Event::ExcerptsRemoved { ids });
-        cx.notify();
-    }
-
-    pub fn excerpts_for_buffer(
-        &self,
-        buffer: &Model<Buffer>,
-        cx: &AppContext,
-    ) -> Vec<(ExcerptId, ExcerptRange<text::Anchor>)> {
-        let mut excerpts = Vec::new();
-        let snapshot = self.read(cx);
-        let buffers = self.buffers.borrow();
-        let mut cursor = snapshot.excerpts.cursor::<Option<&Locator>>();
-        for locator in buffers
-            .get(&buffer.read(cx).remote_id())
-            .map(|state| &state.excerpts)
-            .into_iter()
-            .flatten()
-        {
-            cursor.seek_forward(&Some(locator), Bias::Left, &());
-            if let Some(excerpt) = cursor.item() {
-                if excerpt.locator == *locator {
-                    excerpts.push((excerpt.id.clone(), excerpt.range.clone()));
-                }
-            }
-        }
-
-        excerpts
-    }
-
-    pub fn excerpt_ids(&self) -> Vec<ExcerptId> {
-        self.snapshot
-            .borrow()
-            .excerpts
-            .iter()
-            .map(|entry| entry.id)
-            .collect()
-    }
-
-    pub fn excerpt_containing(
-        &self,
-        position: impl ToOffset,
-        cx: &AppContext,
-    ) -> Option<(ExcerptId, Model<Buffer>, Range<text::Anchor>)> {
-        let snapshot = self.read(cx);
-        let position = position.to_offset(&snapshot);
-
-        let mut cursor = snapshot.excerpts.cursor::<usize>();
-        cursor.seek(&position, Bias::Right, &());
-        cursor
-            .item()
-            .or_else(|| snapshot.excerpts.last())
-            .map(|excerpt| {
-                (
-                    excerpt.id.clone(),
-                    self.buffers
-                        .borrow()
-                        .get(&excerpt.buffer_id)
-                        .unwrap()
-                        .buffer
-                        .clone(),
-                    excerpt.range.context.clone(),
-                )
-            })
-    }
-
-    // If point is at the end of the buffer, the last excerpt is returned
-    pub fn point_to_buffer_offset<T: ToOffset>(
-        &self,
-        point: T,
-        cx: &AppContext,
-    ) -> Option<(Model<Buffer>, usize, ExcerptId)> {
-        let snapshot = self.read(cx);
-        let offset = point.to_offset(&snapshot);
-        let mut cursor = snapshot.excerpts.cursor::<usize>();
-        cursor.seek(&offset, Bias::Right, &());
-        if cursor.item().is_none() {
-            cursor.prev(&());
-        }
-
-        cursor.item().map(|excerpt| {
-            let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
-            let buffer_point = excerpt_start + offset - *cursor.start();
-            let buffer = self.buffers.borrow()[&excerpt.buffer_id].buffer.clone();
-
-            (buffer, buffer_point, excerpt.id)
-        })
-    }
-
-    pub fn range_to_buffer_ranges<T: ToOffset>(
-        &self,
-        range: Range<T>,
-        cx: &AppContext,
-    ) -> Vec<(Model<Buffer>, Range<usize>, ExcerptId)> {
-        let snapshot = self.read(cx);
-        let start = range.start.to_offset(&snapshot);
-        let end = range.end.to_offset(&snapshot);
-
-        let mut result = Vec::new();
-        let mut cursor = snapshot.excerpts.cursor::<usize>();
-        cursor.seek(&start, Bias::Right, &());
-        if cursor.item().is_none() {
-            cursor.prev(&());
-        }
-
-        while let Some(excerpt) = cursor.item() {
-            if *cursor.start() > end {
-                break;
-            }
-
-            let mut end_before_newline = cursor.end(&());
-            if excerpt.has_trailing_newline {
-                end_before_newline -= 1;
-            }
-            let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
-            let start = excerpt_start + (cmp::max(start, *cursor.start()) - *cursor.start());
-            let end = excerpt_start + (cmp::min(end, end_before_newline) - *cursor.start());
-            let buffer = self.buffers.borrow()[&excerpt.buffer_id].buffer.clone();
-            result.push((buffer, start..end, excerpt.id));
-            cursor.next(&());
-        }
-
-        result
-    }
-
-    pub fn remove_excerpts(
-        &mut self,
-        excerpt_ids: impl IntoIterator<Item = ExcerptId>,
-        cx: &mut ModelContext<Self>,
-    ) {
-        self.sync(cx);
-        let ids = excerpt_ids.into_iter().collect::<Vec<_>>();
-        if ids.is_empty() {
-            return;
-        }
-
-        let mut buffers = self.buffers.borrow_mut();
-        let mut snapshot = self.snapshot.borrow_mut();
-        let mut new_excerpts = SumTree::new();
-        let mut cursor = snapshot.excerpts.cursor::<(Option<&Locator>, usize)>();
-        let mut edits = Vec::new();
-        let mut excerpt_ids = ids.iter().copied().peekable();
-
-        while let Some(excerpt_id) = excerpt_ids.next() {
-            // Seek to the next excerpt to remove, preserving any preceding excerpts.
-            let locator = snapshot.excerpt_locator_for_id(excerpt_id);
-            new_excerpts.append(cursor.slice(&Some(locator), Bias::Left, &()), &());
-
-            if let Some(mut excerpt) = cursor.item() {
-                if excerpt.id != excerpt_id {
-                    continue;
-                }
-                let mut old_start = cursor.start().1;
-
-                // Skip over the removed excerpt.
-                'remove_excerpts: loop {
-                    if let Some(buffer_state) = buffers.get_mut(&excerpt.buffer_id) {
-                        buffer_state.excerpts.retain(|l| l != &excerpt.locator);
-                        if buffer_state.excerpts.is_empty() {
-                            buffers.remove(&excerpt.buffer_id);
-                        }
-                    }
-                    cursor.next(&());
-
-                    // Skip over any subsequent excerpts that are also removed.
-                    while let Some(&next_excerpt_id) = excerpt_ids.peek() {
-                        let next_locator = snapshot.excerpt_locator_for_id(next_excerpt_id);
-                        if let Some(next_excerpt) = cursor.item() {
-                            if next_excerpt.locator == *next_locator {
-                                excerpt_ids.next();
-                                excerpt = next_excerpt;
-                                continue 'remove_excerpts;
-                            }
-                        }
-                        break;
-                    }
-
-                    break;
-                }
-
-                // When removing the last excerpt, remove the trailing newline from
-                // the previous excerpt.
-                if cursor.item().is_none() && old_start > 0 {
-                    old_start -= 1;
-                    new_excerpts.update_last(|e| e.has_trailing_newline = false, &());
-                }
-
-                // Push an edit for the removal of this run of excerpts.
-                let old_end = cursor.start().1;
-                let new_start = new_excerpts.summary().text.len;
-                edits.push(Edit {
-                    old: old_start..old_end,
-                    new: new_start..new_start,
-                });
-            }
-        }
-        let suffix = cursor.suffix(&());
-        let changed_trailing_excerpt = suffix.is_empty();
-        new_excerpts.append(suffix, &());
-        drop(cursor);
-        snapshot.excerpts = new_excerpts;
-
-        if changed_trailing_excerpt {
-            snapshot.trailing_excerpt_update_count += 1;
-        }
-
-        self.subscriptions.publish_mut(edits);
-        cx.emit(Event::Edited {
-            sigleton_buffer_edited: false,
-        });
-        cx.emit(Event::ExcerptsRemoved { ids });
-        cx.notify();
-    }
-
-    pub fn wait_for_anchors<'a>(
-        &self,
-        anchors: impl 'a + Iterator<Item = Anchor>,
-        cx: &mut ModelContext<Self>,
-    ) -> impl 'static + Future<Output = Result<()>> {
-        let borrow = self.buffers.borrow();
-        let mut error = None;
-        let mut futures = Vec::new();
-        for anchor in anchors {
-            if let Some(buffer_id) = anchor.buffer_id {
-                if let Some(buffer) = borrow.get(&buffer_id) {
-                    buffer.buffer.update(cx, |buffer, _| {
-                        futures.push(buffer.wait_for_anchors([anchor.text_anchor]))
-                    });
-                } else {
-                    error = Some(anyhow!(
-                        "buffer {buffer_id} is not part of this multi-buffer"
-                    ));
-                    break;
-                }
-            }
-        }
-        async move {
-            if let Some(error) = error {
-                Err(error)?;
-            }
-            for future in futures {
-                future.await?;
-            }
-            Ok(())
-        }
-    }
-
-    pub fn text_anchor_for_position<T: ToOffset>(
-        &self,
-        position: T,
-        cx: &AppContext,
-    ) -> Option<(Model<Buffer>, language::Anchor)> {
-        let snapshot = self.read(cx);
-        let anchor = snapshot.anchor_before(position);
-        let buffer = self
-            .buffers
-            .borrow()
-            .get(&anchor.buffer_id?)?
-            .buffer
-            .clone();
-        Some((buffer, anchor.text_anchor))
-    }
-
-    fn on_buffer_event(
-        &mut self,
-        _: Model<Buffer>,
-        event: &language::Event,
-        cx: &mut ModelContext<Self>,
-    ) {
-        cx.emit(match event {
-            language::Event::Edited => Event::Edited {
-                sigleton_buffer_edited: true,
-            },
-            language::Event::DirtyChanged => Event::DirtyChanged,
-            language::Event::Saved => Event::Saved,
-            language::Event::FileHandleChanged => Event::FileHandleChanged,
-            language::Event::Reloaded => Event::Reloaded,
-            language::Event::DiffBaseChanged => Event::DiffBaseChanged,
-            language::Event::LanguageChanged => Event::LanguageChanged,
-            language::Event::Reparsed => Event::Reparsed,
-            language::Event::DiagnosticsUpdated => Event::DiagnosticsUpdated,
-            language::Event::Closed => Event::Closed,
-
-            //
-            language::Event::Operation(_) => return,
-        });
-    }
-
-    pub fn all_buffers(&self) -> HashSet<Model<Buffer>> {
-        self.buffers
-            .borrow()
-            .values()
-            .map(|state| state.buffer.clone())
-            .collect()
-    }
-
-    pub fn buffer(&self, buffer_id: u64) -> Option<Model<Buffer>> {
-        self.buffers
-            .borrow()
-            .get(&buffer_id)
-            .map(|state| state.buffer.clone())
-    }
-
-    pub fn is_completion_trigger(&self, position: Anchor, text: &str, cx: &AppContext) -> bool {
-        let mut chars = text.chars();
-        let char = if let Some(char) = chars.next() {
-            char
-        } else {
-            return false;
-        };
-        if chars.next().is_some() {
-            return false;
-        }
-
-        let snapshot = self.snapshot(cx);
-        let position = position.to_offset(&snapshot);
-        let scope = snapshot.language_scope_at(position);
-        if char_kind(&scope, char) == CharKind::Word {
-            return true;
-        }
-
-        let anchor = snapshot.anchor_before(position);
-        anchor
-            .buffer_id
-            .and_then(|buffer_id| {
-                let buffer = self.buffers.borrow().get(&buffer_id)?.buffer.clone();
-                Some(
-                    buffer
-                        .read(cx)
-                        .completion_triggers()
-                        .iter()
-                        .any(|string| string == text),
-                )
-            })
-            .unwrap_or(false)
-    }
-
-    pub fn language_at<'a, T: ToOffset>(
-        &self,
-        point: T,
-        cx: &'a AppContext,
-    ) -> Option<Arc<Language>> {
-        self.point_to_buffer_offset(point, cx)
-            .and_then(|(buffer, offset, _)| buffer.read(cx).language_at(offset))
-    }
-
-    pub fn settings_at<'a, T: ToOffset>(
-        &self,
-        point: T,
-        cx: &'a AppContext,
-    ) -> &'a LanguageSettings {
-        let mut language = None;
-        let mut file = None;
-        if let Some((buffer, offset, _)) = self.point_to_buffer_offset(point, cx) {
-            let buffer = buffer.read(cx);
-            language = buffer.language_at(offset);
-            file = buffer.file();
-        }
-        language_settings(language.as_ref(), file, cx)
-    }
-
-    pub fn for_each_buffer(&self, mut f: impl FnMut(&Model<Buffer>)) {
-        self.buffers
-            .borrow()
-            .values()
-            .for_each(|state| f(&state.buffer))
-    }
-
-    pub fn title<'a>(&'a self, cx: &'a AppContext) -> Cow<'a, str> {
-        if let Some(title) = self.title.as_ref() {
-            return title.into();
-        }
-
-        if let Some(buffer) = self.as_singleton() {
-            if let Some(file) = buffer.read(cx).file() {
-                return file.file_name(cx).to_string_lossy();
-            }
-        }
-
-        "untitled".into()
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn is_parsing(&self, cx: &AppContext) -> bool {
-        self.as_singleton().unwrap().read(cx).is_parsing()
-    }
-
-    fn sync(&self, cx: &AppContext) {
-        let mut snapshot = self.snapshot.borrow_mut();
-        let mut excerpts_to_edit = Vec::new();
-        let mut reparsed = false;
-        let mut diagnostics_updated = false;
-        let mut git_diff_updated = false;
-        let mut is_dirty = false;
-        let mut has_conflict = false;
-        let mut edited = false;
-        let mut buffers = self.buffers.borrow_mut();
-        for buffer_state in buffers.values_mut() {
-            let buffer = buffer_state.buffer.read(cx);
-            let version = buffer.version();
-            let parse_count = buffer.parse_count();
-            let selections_update_count = buffer.selections_update_count();
-            let diagnostics_update_count = buffer.diagnostics_update_count();
-            let file_update_count = buffer.file_update_count();
-            let git_diff_update_count = buffer.git_diff_update_count();
-
-            let buffer_edited = version.changed_since(&buffer_state.last_version);
-            let buffer_reparsed = parse_count > buffer_state.last_parse_count;
-            let buffer_selections_updated =
-                selections_update_count > buffer_state.last_selections_update_count;
-            let buffer_diagnostics_updated =
-                diagnostics_update_count > buffer_state.last_diagnostics_update_count;
-            let buffer_file_updated = file_update_count > buffer_state.last_file_update_count;
-            let buffer_git_diff_updated =
-                git_diff_update_count > buffer_state.last_git_diff_update_count;
-            if buffer_edited
-                || buffer_reparsed
-                || buffer_selections_updated
-                || buffer_diagnostics_updated
-                || buffer_file_updated
-                || buffer_git_diff_updated
-            {
-                buffer_state.last_version = version;
-                buffer_state.last_parse_count = parse_count;
-                buffer_state.last_selections_update_count = selections_update_count;
-                buffer_state.last_diagnostics_update_count = diagnostics_update_count;
-                buffer_state.last_file_update_count = file_update_count;
-                buffer_state.last_git_diff_update_count = git_diff_update_count;
-                excerpts_to_edit.extend(
-                    buffer_state
-                        .excerpts
-                        .iter()
-                        .map(|locator| (locator, buffer_state.buffer.clone(), buffer_edited)),
-                );
-            }
-
-            edited |= buffer_edited;
-            reparsed |= buffer_reparsed;
-            diagnostics_updated |= buffer_diagnostics_updated;
-            git_diff_updated |= buffer_git_diff_updated;
-            is_dirty |= buffer.is_dirty();
-            has_conflict |= buffer.has_conflict();
-        }
-        if edited {
-            snapshot.edit_count += 1;
-        }
-        if reparsed {
-            snapshot.parse_count += 1;
-        }
-        if diagnostics_updated {
-            snapshot.diagnostics_update_count += 1;
-        }
-        if git_diff_updated {
-            snapshot.git_diff_update_count += 1;
-        }
-        snapshot.is_dirty = is_dirty;
-        snapshot.has_conflict = has_conflict;
-
-        excerpts_to_edit.sort_unstable_by_key(|(locator, _, _)| *locator);
-
-        let mut edits = Vec::new();
-        let mut new_excerpts = SumTree::new();
-        let mut cursor = snapshot.excerpts.cursor::<(Option<&Locator>, usize)>();
-
-        for (locator, buffer, buffer_edited) in excerpts_to_edit {
-            new_excerpts.append(cursor.slice(&Some(locator), Bias::Left, &()), &());
-            let old_excerpt = cursor.item().unwrap();
-            let buffer = buffer.read(cx);
-            let buffer_id = buffer.remote_id();
-
-            let mut new_excerpt;
-            if buffer_edited {
-                edits.extend(
-                    buffer
-                        .edits_since_in_range::<usize>(
-                            old_excerpt.buffer.version(),
-                            old_excerpt.range.context.clone(),
-                        )
-                        .map(|mut edit| {
-                            let excerpt_old_start = cursor.start().1;
-                            let excerpt_new_start = new_excerpts.summary().text.len;
-                            edit.old.start += excerpt_old_start;
-                            edit.old.end += excerpt_old_start;
-                            edit.new.start += excerpt_new_start;
-                            edit.new.end += excerpt_new_start;
-                            edit
-                        }),
-                );
-
-                new_excerpt = Excerpt::new(
-                    old_excerpt.id,
-                    locator.clone(),
-                    buffer_id,
-                    buffer.snapshot(),
-                    old_excerpt.range.clone(),
-                    old_excerpt.has_trailing_newline,
-                );
-            } else {
-                new_excerpt = old_excerpt.clone();
-                new_excerpt.buffer = buffer.snapshot();
-            }
-
-            new_excerpts.push(new_excerpt, &());
-            cursor.next(&());
-        }
-        new_excerpts.append(cursor.suffix(&()), &());
-
-        drop(cursor);
-        snapshot.excerpts = new_excerpts;
-
-        self.subscriptions.publish(edits);
-    }
-}
-
-#[cfg(any(test, feature = "test-support"))]
-impl MultiBuffer {
-    pub fn build_simple(text: &str, cx: &mut gpui::AppContext) -> Model<Self> {
-        let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text));
-        cx.new_model(|cx| Self::singleton(buffer, cx))
-    }
-
-    pub fn build_multi<const COUNT: usize>(
-        excerpts: [(&str, Vec<Range<Point>>); COUNT],
-        cx: &mut gpui::AppContext,
-    ) -> Model<Self> {
-        let multi = cx.new_model(|_| Self::new(0));
-        for (text, ranges) in excerpts {
-            let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text));
-            let excerpt_ranges = ranges.into_iter().map(|range| ExcerptRange {
-                context: range,
-                primary: None,
-            });
-            multi.update(cx, |multi, cx| {
-                multi.push_excerpts(buffer, excerpt_ranges, cx)
-            });
-        }
-
-        multi
-    }
-
-    pub fn build_from_buffer(buffer: Model<Buffer>, cx: &mut gpui::AppContext) -> Model<Self> {
-        cx.new_model(|cx| Self::singleton(buffer, cx))
-    }
-
-    pub fn build_random(rng: &mut impl rand::Rng, cx: &mut gpui::AppContext) -> Model<Self> {
-        cx.new_model(|cx| {
-            let mut multibuffer = MultiBuffer::new(0);
-            let mutation_count = rng.gen_range(1..=5);
-            multibuffer.randomly_edit_excerpts(rng, mutation_count, cx);
-            multibuffer
-        })
-    }
-
-    pub fn randomly_edit(
-        &mut self,
-        rng: &mut impl rand::Rng,
-        edit_count: usize,
-        cx: &mut ModelContext<Self>,
-    ) {
-        use util::RandomCharIter;
-
-        let snapshot = self.read(cx);
-        let mut edits: Vec<(Range<usize>, Arc<str>)> = Vec::new();
-        let mut last_end = None;
-        for _ in 0..edit_count {
-            if last_end.map_or(false, |last_end| last_end >= snapshot.len()) {
-                break;
-            }
-
-            let new_start = last_end.map_or(0, |last_end| last_end + 1);
-            let end = snapshot.clip_offset(rng.gen_range(new_start..=snapshot.len()), Bias::Right);
-            let start = snapshot.clip_offset(rng.gen_range(new_start..=end), Bias::Right);
-            last_end = Some(end);
-
-            let mut range = start..end;
-            if rng.gen_bool(0.2) {
-                mem::swap(&mut range.start, &mut range.end);
-            }
-
-            let new_text_len = rng.gen_range(0..10);
-            let new_text: String = RandomCharIter::new(&mut *rng).take(new_text_len).collect();
-
-            edits.push((range, new_text.into()));
-        }
-        log::info!("mutating multi-buffer with {:?}", edits);
-        drop(snapshot);
-
-        self.edit(edits, None, cx);
-    }
-
-    pub fn randomly_edit_excerpts(
-        &mut self,
-        rng: &mut impl rand::Rng,
-        mutation_count: usize,
-        cx: &mut ModelContext<Self>,
-    ) {
-        use rand::prelude::*;
-        use std::env;
-        use util::RandomCharIter;
-
-        let max_excerpts = env::var("MAX_EXCERPTS")
-            .map(|i| i.parse().expect("invalid `MAX_EXCERPTS` variable"))
-            .unwrap_or(5);
-
-        let mut buffers = Vec::new();
-        for _ in 0..mutation_count {
-            if rng.gen_bool(0.05) {
-                log::info!("Clearing multi-buffer");
-                self.clear(cx);
-                continue;
-            }
-
-            let excerpt_ids = self.excerpt_ids();
-            if excerpt_ids.is_empty() || (rng.gen() && excerpt_ids.len() < max_excerpts) {
-                let buffer_handle = if rng.gen() || self.buffers.borrow().is_empty() {
-                    let text = RandomCharIter::new(&mut *rng).take(10).collect::<String>();
-                    buffers.push(cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text)));
-                    let buffer = buffers.last().unwrap().read(cx);
-                    log::info!(
-                        "Creating new buffer {} with text: {:?}",
-                        buffer.remote_id(),
-                        buffer.text()
-                    );
-                    buffers.last().unwrap().clone()
-                } else {
-                    self.buffers
-                        .borrow()
-                        .values()
-                        .choose(rng)
-                        .unwrap()
-                        .buffer
-                        .clone()
-                };
-
-                let buffer = buffer_handle.read(cx);
-                let buffer_text = buffer.text();
-                let ranges = (0..rng.gen_range(0..5))
-                    .map(|_| {
-                        let end_ix =
-                            buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right);
-                        let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
-                        ExcerptRange {
-                            context: start_ix..end_ix,
-                            primary: None,
-                        }
-                    })
-                    .collect::<Vec<_>>();
-                log::info!(
-                    "Inserting excerpts from buffer {} and ranges {:?}: {:?}",
-                    buffer_handle.read(cx).remote_id(),
-                    ranges.iter().map(|r| &r.context).collect::<Vec<_>>(),
-                    ranges
-                        .iter()
-                        .map(|r| &buffer_text[r.context.clone()])
-                        .collect::<Vec<_>>()
-                );
-
-                let excerpt_id = self.push_excerpts(buffer_handle.clone(), ranges, cx);
-                log::info!("Inserted with ids: {:?}", excerpt_id);
-            } else {
-                let remove_count = rng.gen_range(1..=excerpt_ids.len());
-                let mut excerpts_to_remove = excerpt_ids
-                    .choose_multiple(rng, remove_count)
-                    .cloned()
-                    .collect::<Vec<_>>();
-                let snapshot = self.snapshot.borrow();
-                excerpts_to_remove.sort_unstable_by(|a, b| a.cmp(b, &*snapshot));
-                drop(snapshot);
-                log::info!("Removing excerpts {:?}", excerpts_to_remove);
-                self.remove_excerpts(excerpts_to_remove, cx);
-            }
-        }
-    }
-
-    pub fn randomly_mutate(
-        &mut self,
-        rng: &mut impl rand::Rng,
-        mutation_count: usize,
-        cx: &mut ModelContext<Self>,
-    ) {
-        use rand::prelude::*;
-
-        if rng.gen_bool(0.7) || self.singleton {
-            let buffer = self
-                .buffers
-                .borrow()
-                .values()
-                .choose(rng)
-                .map(|state| state.buffer.clone());
-
-            if let Some(buffer) = buffer {
-                buffer.update(cx, |buffer, cx| {
-                    if rng.gen() {
-                        buffer.randomly_edit(rng, mutation_count, cx);
-                    } else {
-                        buffer.randomly_undo_redo(rng, cx);
-                    }
-                });
-            } else {
-                self.randomly_edit(rng, mutation_count, cx);
-            }
-        } else {
-            self.randomly_edit_excerpts(rng, mutation_count, cx);
-        }
-
-        self.check_invariants(cx);
-    }
-
-    fn check_invariants(&self, cx: &mut ModelContext<Self>) {
-        let snapshot = self.read(cx);
-        let excerpts = snapshot.excerpts.items(&());
-        let excerpt_ids = snapshot.excerpt_ids.items(&());
-
-        for (ix, excerpt) in excerpts.iter().enumerate() {
-            if ix == 0 {
-                if excerpt.locator <= Locator::min() {
-                    panic!("invalid first excerpt locator {:?}", excerpt.locator);
-                }
-            } else {
-                if excerpt.locator <= excerpts[ix - 1].locator {
-                    panic!("excerpts are out-of-order: {:?}", excerpts);
-                }
-            }
-        }
-
-        for (ix, entry) in excerpt_ids.iter().enumerate() {
-            if ix == 0 {
-                if entry.id.cmp(&ExcerptId::min(), &*snapshot).is_le() {
-                    panic!("invalid first excerpt id {:?}", entry.id);
-                }
-            } else {
-                if entry.id <= excerpt_ids[ix - 1].id {
-                    panic!("excerpt ids are out-of-order: {:?}", excerpt_ids);
-                }
-            }
-        }
-    }
-}
-
-impl EventEmitter<Event> for MultiBuffer {}
-
-impl MultiBufferSnapshot {
-    pub fn text(&self) -> String {
-        self.chunks(0..self.len(), false)
-            .map(|chunk| chunk.text)
-            .collect()
-    }
-
-    pub fn reversed_chars_at<T: ToOffset>(&self, position: T) -> impl Iterator<Item = char> + '_ {
-        let mut offset = position.to_offset(self);
-        let mut cursor = self.excerpts.cursor::<usize>();
-        cursor.seek(&offset, Bias::Left, &());
-        let mut excerpt_chunks = cursor.item().map(|excerpt| {
-            let end_before_footer = cursor.start() + excerpt.text_summary.len;
-            let start = excerpt.range.context.start.to_offset(&excerpt.buffer);
-            let end = start + (cmp::min(offset, end_before_footer) - cursor.start());
-            excerpt.buffer.reversed_chunks_in_range(start..end)
-        });
-        iter::from_fn(move || {
-            if offset == *cursor.start() {
-                cursor.prev(&());
-                let excerpt = cursor.item()?;
-                excerpt_chunks = Some(
-                    excerpt
-                        .buffer
-                        .reversed_chunks_in_range(excerpt.range.context.clone()),
-                );
-            }
-
-            let excerpt = cursor.item().unwrap();
-            if offset == cursor.end(&()) && excerpt.has_trailing_newline {
-                offset -= 1;
-                Some("\n")
-            } else {
-                let chunk = excerpt_chunks.as_mut().unwrap().next().unwrap();
-                offset -= chunk.len();
-                Some(chunk)
-            }
-        })
-        .flat_map(|c| c.chars().rev())
-    }
-
-    pub fn chars_at<T: ToOffset>(&self, position: T) -> impl Iterator<Item = char> + '_ {
-        let offset = position.to_offset(self);
-        self.text_for_range(offset..self.len())
-            .flat_map(|chunk| chunk.chars())
-    }
-
-    pub fn text_for_range<T: ToOffset>(&self, range: Range<T>) -> impl Iterator<Item = &str> + '_ {
-        self.chunks(range, false).map(|chunk| chunk.text)
-    }
-
-    pub fn is_line_blank(&self, row: u32) -> bool {
-        self.text_for_range(Point::new(row, 0)..Point::new(row, self.line_len(row)))
-            .all(|chunk| chunk.matches(|c: char| !c.is_whitespace()).next().is_none())
-    }
-
-    pub fn contains_str_at<T>(&self, position: T, needle: &str) -> bool
-    where
-        T: ToOffset,
-    {
-        let position = position.to_offset(self);
-        position == self.clip_offset(position, Bias::Left)
-            && self
-                .bytes_in_range(position..self.len())
-                .flatten()
-                .copied()
-                .take(needle.len())
-                .eq(needle.bytes())
-    }
-
-    pub fn surrounding_word<T: ToOffset>(&self, start: T) -> (Range<usize>, Option<CharKind>) {
-        let mut start = start.to_offset(self);
-        let mut end = start;
-        let mut next_chars = self.chars_at(start).peekable();
-        let mut prev_chars = self.reversed_chars_at(start).peekable();
-
-        let scope = self.language_scope_at(start);
-        let kind = |c| char_kind(&scope, c);
-        let word_kind = cmp::max(
-            prev_chars.peek().copied().map(kind),
-            next_chars.peek().copied().map(kind),
-        );
-
-        for ch in prev_chars {
-            if Some(kind(ch)) == word_kind && ch != '\n' {
-                start -= ch.len_utf8();
-            } else {
-                break;
-            }
-        }
-
-        for ch in next_chars {
-            if Some(kind(ch)) == word_kind && ch != '\n' {
-                end += ch.len_utf8();
-            } else {
-                break;
-            }
-        }
-
-        (start..end, word_kind)
-    }
-
-    pub fn as_singleton(&self) -> Option<(&ExcerptId, u64, &BufferSnapshot)> {
-        if self.singleton {
-            self.excerpts
-                .iter()
-                .next()
-                .map(|e| (&e.id, e.buffer_id, &e.buffer))
-        } else {
-            None
-        }
-    }
-
-    pub fn len(&self) -> usize {
-        self.excerpts.summary().text.len
-    }
-
-    pub fn is_empty(&self) -> bool {
-        self.excerpts.summary().text.len == 0
-    }
-
-    pub fn max_buffer_row(&self) -> u32 {
-        self.excerpts.summary().max_buffer_row
-    }
-
-    pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
-        if let Some((_, _, buffer)) = self.as_singleton() {
-            return buffer.clip_offset(offset, bias);
-        }
-
-        let mut cursor = self.excerpts.cursor::<usize>();
-        cursor.seek(&offset, Bias::Right, &());
-        let overshoot = if let Some(excerpt) = cursor.item() {
-            let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
-            let buffer_offset = excerpt
-                .buffer
-                .clip_offset(excerpt_start + (offset - cursor.start()), bias);
-            buffer_offset.saturating_sub(excerpt_start)
-        } else {
-            0
-        };
-        cursor.start() + overshoot
-    }
-
-    pub fn clip_point(&self, point: Point, bias: Bias) -> Point {
-        if let Some((_, _, buffer)) = self.as_singleton() {
-            return buffer.clip_point(point, bias);
-        }
-
-        let mut cursor = self.excerpts.cursor::<Point>();
-        cursor.seek(&point, Bias::Right, &());
-        let overshoot = if let Some(excerpt) = cursor.item() {
-            let excerpt_start = excerpt.range.context.start.to_point(&excerpt.buffer);
-            let buffer_point = excerpt
-                .buffer
-                .clip_point(excerpt_start + (point - cursor.start()), bias);
-            buffer_point.saturating_sub(excerpt_start)
-        } else {
-            Point::zero()
-        };
-        *cursor.start() + overshoot
-    }
-
-    pub fn clip_offset_utf16(&self, offset: OffsetUtf16, bias: Bias) -> OffsetUtf16 {
-        if let Some((_, _, buffer)) = self.as_singleton() {
-            return buffer.clip_offset_utf16(offset, bias);
-        }
-
-        let mut cursor = self.excerpts.cursor::<OffsetUtf16>();
-        cursor.seek(&offset, Bias::Right, &());
-        let overshoot = if let Some(excerpt) = cursor.item() {
-            let excerpt_start = excerpt.range.context.start.to_offset_utf16(&excerpt.buffer);
-            let buffer_offset = excerpt
-                .buffer
-                .clip_offset_utf16(excerpt_start + (offset - cursor.start()), bias);
-            OffsetUtf16(buffer_offset.0.saturating_sub(excerpt_start.0))
-        } else {
-            OffsetUtf16(0)
-        };
-        *cursor.start() + overshoot
-    }
-
-    pub fn clip_point_utf16(&self, point: Unclipped<PointUtf16>, bias: Bias) -> PointUtf16 {
-        if let Some((_, _, buffer)) = self.as_singleton() {
-            return buffer.clip_point_utf16(point, bias);
-        }
-
-        let mut cursor = self.excerpts.cursor::<PointUtf16>();
-        cursor.seek(&point.0, Bias::Right, &());
-        let overshoot = if let Some(excerpt) = cursor.item() {
-            let excerpt_start = excerpt
-                .buffer
-                .offset_to_point_utf16(excerpt.range.context.start.to_offset(&excerpt.buffer));
-            let buffer_point = excerpt
-                .buffer
-                .clip_point_utf16(Unclipped(excerpt_start + (point.0 - cursor.start())), bias);
-            buffer_point.saturating_sub(excerpt_start)
-        } else {
-            PointUtf16::zero()
-        };
-        *cursor.start() + overshoot
-    }
-
-    pub fn bytes_in_range<T: ToOffset>(&self, range: Range<T>) -> MultiBufferBytes {
-        let range = range.start.to_offset(self)..range.end.to_offset(self);
-        let mut excerpts = self.excerpts.cursor::<usize>();
-        excerpts.seek(&range.start, Bias::Right, &());
-
-        let mut chunk = &[][..];
-        let excerpt_bytes = if let Some(excerpt) = excerpts.item() {
-            let mut excerpt_bytes = excerpt
-                .bytes_in_range(range.start - excerpts.start()..range.end - excerpts.start());
-            chunk = excerpt_bytes.next().unwrap_or(&[][..]);
-            Some(excerpt_bytes)
-        } else {
-            None
-        };
-        MultiBufferBytes {
-            range,
-            excerpts,
-            excerpt_bytes,
-            chunk,
-        }
-    }
-
-    pub fn reversed_bytes_in_range<T: ToOffset>(
-        &self,
-        range: Range<T>,
-    ) -> ReversedMultiBufferBytes {
-        let range = range.start.to_offset(self)..range.end.to_offset(self);
-        let mut excerpts = self.excerpts.cursor::<usize>();
-        excerpts.seek(&range.end, Bias::Left, &());
-
-        let mut chunk = &[][..];
-        let excerpt_bytes = if let Some(excerpt) = excerpts.item() {
-            let mut excerpt_bytes = excerpt.reversed_bytes_in_range(
-                range.start - excerpts.start()..range.end - excerpts.start(),
-            );
-            chunk = excerpt_bytes.next().unwrap_or(&[][..]);
-            Some(excerpt_bytes)
-        } else {
-            None
-        };
-
-        ReversedMultiBufferBytes {
-            range,
-            excerpts,
-            excerpt_bytes,
-            chunk,
-        }
-    }
-
-    pub fn buffer_rows(&self, start_row: u32) -> MultiBufferRows {
-        let mut result = MultiBufferRows {
-            buffer_row_range: 0..0,
-            excerpts: self.excerpts.cursor(),
-        };
-        result.seek(start_row);
-        result
-    }
-
-    pub fn chunks<T: ToOffset>(&self, range: Range<T>, language_aware: bool) -> MultiBufferChunks {
-        let range = range.start.to_offset(self)..range.end.to_offset(self);
-        let mut chunks = MultiBufferChunks {
-            range: range.clone(),
-            excerpts: self.excerpts.cursor(),
-            excerpt_chunks: None,
-            language_aware,
-        };
-        chunks.seek(range.start);
-        chunks
-    }
-
-    pub fn offset_to_point(&self, offset: usize) -> Point {
-        if let Some((_, _, buffer)) = self.as_singleton() {
-            return buffer.offset_to_point(offset);
-        }
-
-        let mut cursor = self.excerpts.cursor::<(usize, Point)>();
-        cursor.seek(&offset, Bias::Right, &());
-        if let Some(excerpt) = cursor.item() {
-            let (start_offset, start_point) = cursor.start();
-            let overshoot = offset - start_offset;
-            let excerpt_start_offset = excerpt.range.context.start.to_offset(&excerpt.buffer);
-            let excerpt_start_point = excerpt.range.context.start.to_point(&excerpt.buffer);
-            let buffer_point = excerpt
-                .buffer
-                .offset_to_point(excerpt_start_offset + overshoot);
-            *start_point + (buffer_point - excerpt_start_point)
-        } else {
-            self.excerpts.summary().text.lines
-        }
-    }
-
-    pub fn offset_to_point_utf16(&self, offset: usize) -> PointUtf16 {
-        if let Some((_, _, buffer)) = self.as_singleton() {
-            return buffer.offset_to_point_utf16(offset);
-        }
-
-        let mut cursor = self.excerpts.cursor::<(usize, PointUtf16)>();
-        cursor.seek(&offset, Bias::Right, &());
-        if let Some(excerpt) = cursor.item() {
-            let (start_offset, start_point) = cursor.start();
-            let overshoot = offset - start_offset;
-            let excerpt_start_offset = excerpt.range.context.start.to_offset(&excerpt.buffer);
-            let excerpt_start_point = excerpt.range.context.start.to_point_utf16(&excerpt.buffer);
-            let buffer_point = excerpt
-                .buffer
-                .offset_to_point_utf16(excerpt_start_offset + overshoot);
-            *start_point + (buffer_point - excerpt_start_point)
-        } else {
-            self.excerpts.summary().text.lines_utf16()
-        }
-    }
-
-    pub fn point_to_point_utf16(&self, point: Point) -> PointUtf16 {
-        if let Some((_, _, buffer)) = self.as_singleton() {
-            return buffer.point_to_point_utf16(point);
-        }
-
-        let mut cursor = self.excerpts.cursor::<(Point, PointUtf16)>();
-        cursor.seek(&point, Bias::Right, &());
-        if let Some(excerpt) = cursor.item() {
-            let (start_offset, start_point) = cursor.start();
-            let overshoot = point - start_offset;
-            let excerpt_start_point = excerpt.range.context.start.to_point(&excerpt.buffer);
-            let excerpt_start_point_utf16 =
-                excerpt.range.context.start.to_point_utf16(&excerpt.buffer);
-            let buffer_point = excerpt
-                .buffer
-                .point_to_point_utf16(excerpt_start_point + overshoot);
-            *start_point + (buffer_point - excerpt_start_point_utf16)
-        } else {
-            self.excerpts.summary().text.lines_utf16()
-        }
-    }
-
-    pub fn point_to_offset(&self, point: Point) -> usize {
-        if let Some((_, _, buffer)) = self.as_singleton() {
-            return buffer.point_to_offset(point);
-        }
-
-        let mut cursor = self.excerpts.cursor::<(Point, usize)>();
-        cursor.seek(&point, Bias::Right, &());
-        if let Some(excerpt) = cursor.item() {
-            let (start_point, start_offset) = cursor.start();
-            let overshoot = point - start_point;
-            let excerpt_start_offset = excerpt.range.context.start.to_offset(&excerpt.buffer);
-            let excerpt_start_point = excerpt.range.context.start.to_point(&excerpt.buffer);
-            let buffer_offset = excerpt
-                .buffer
-                .point_to_offset(excerpt_start_point + overshoot);
-            *start_offset + buffer_offset - excerpt_start_offset
-        } else {
-            self.excerpts.summary().text.len
-        }
-    }
-
-    pub fn offset_utf16_to_offset(&self, offset_utf16: OffsetUtf16) -> usize {
-        if let Some((_, _, buffer)) = self.as_singleton() {
-            return buffer.offset_utf16_to_offset(offset_utf16);
-        }
-
-        let mut cursor = self.excerpts.cursor::<(OffsetUtf16, usize)>();
-        cursor.seek(&offset_utf16, Bias::Right, &());
-        if let Some(excerpt) = cursor.item() {
-            let (start_offset_utf16, start_offset) = cursor.start();
-            let overshoot = offset_utf16 - start_offset_utf16;
-            let excerpt_start_offset = excerpt.range.context.start.to_offset(&excerpt.buffer);
-            let excerpt_start_offset_utf16 =
-                excerpt.buffer.offset_to_offset_utf16(excerpt_start_offset);
-            let buffer_offset = excerpt
-                .buffer
-                .offset_utf16_to_offset(excerpt_start_offset_utf16 + overshoot);
-            *start_offset + (buffer_offset - excerpt_start_offset)
-        } else {
-            self.excerpts.summary().text.len
-        }
-    }
-
-    pub fn offset_to_offset_utf16(&self, offset: usize) -> OffsetUtf16 {
-        if let Some((_, _, buffer)) = self.as_singleton() {
-            return buffer.offset_to_offset_utf16(offset);
-        }
-
-        let mut cursor = self.excerpts.cursor::<(usize, OffsetUtf16)>();
-        cursor.seek(&offset, Bias::Right, &());
-        if let Some(excerpt) = cursor.item() {
-            let (start_offset, start_offset_utf16) = cursor.start();
-            let overshoot = offset - start_offset;
-            let excerpt_start_offset_utf16 =
-                excerpt.range.context.start.to_offset_utf16(&excerpt.buffer);
-            let excerpt_start_offset = excerpt
-                .buffer
-                .offset_utf16_to_offset(excerpt_start_offset_utf16);
-            let buffer_offset_utf16 = excerpt
-                .buffer
-                .offset_to_offset_utf16(excerpt_start_offset + overshoot);
-            *start_offset_utf16 + (buffer_offset_utf16 - excerpt_start_offset_utf16)
-        } else {
-            self.excerpts.summary().text.len_utf16
-        }
-    }
-
-    pub fn point_utf16_to_offset(&self, point: PointUtf16) -> usize {
-        if let Some((_, _, buffer)) = self.as_singleton() {
-            return buffer.point_utf16_to_offset(point);
-        }
-
-        let mut cursor = self.excerpts.cursor::<(PointUtf16, usize)>();
-        cursor.seek(&point, Bias::Right, &());
-        if let Some(excerpt) = cursor.item() {
-            let (start_point, start_offset) = cursor.start();
-            let overshoot = point - start_point;
-            let excerpt_start_offset = excerpt.range.context.start.to_offset(&excerpt.buffer);
-            let excerpt_start_point = excerpt
-                .buffer
-                .offset_to_point_utf16(excerpt.range.context.start.to_offset(&excerpt.buffer));
-            let buffer_offset = excerpt
-                .buffer
-                .point_utf16_to_offset(excerpt_start_point + overshoot);
-            *start_offset + (buffer_offset - excerpt_start_offset)
-        } else {
-            self.excerpts.summary().text.len
-        }
-    }
-
-    pub fn point_to_buffer_offset<T: ToOffset>(
-        &self,
-        point: T,
-    ) -> Option<(&BufferSnapshot, usize)> {
-        let offset = point.to_offset(&self);
-        let mut cursor = self.excerpts.cursor::<usize>();
-        cursor.seek(&offset, Bias::Right, &());
-        if cursor.item().is_none() {
-            cursor.prev(&());
-        }
-
-        cursor.item().map(|excerpt| {
-            let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
-            let buffer_point = excerpt_start + offset - *cursor.start();
-            (&excerpt.buffer, buffer_point)
-        })
-    }
-
-    pub fn suggested_indents(
-        &self,
-        rows: impl IntoIterator<Item = u32>,
-        cx: &AppContext,
-    ) -> BTreeMap<u32, IndentSize> {
-        let mut result = BTreeMap::new();
-
-        let mut rows_for_excerpt = Vec::new();
-        let mut cursor = self.excerpts.cursor::<Point>();
-        let mut rows = rows.into_iter().peekable();
-        let mut prev_row = u32::MAX;
-        let mut prev_language_indent_size = IndentSize::default();
-
-        while let Some(row) = rows.next() {
-            cursor.seek(&Point::new(row, 0), Bias::Right, &());
-            let excerpt = match cursor.item() {
-                Some(excerpt) => excerpt,
-                _ => continue,
-            };
-
-            // Retrieve the language and indent size once for each disjoint region being indented.
-            let single_indent_size = if row.saturating_sub(1) == prev_row {
-                prev_language_indent_size
-            } else {
-                excerpt
-                    .buffer
-                    .language_indent_size_at(Point::new(row, 0), cx)
-            };
-            prev_language_indent_size = single_indent_size;
-            prev_row = row;
-
-            let start_buffer_row = excerpt.range.context.start.to_point(&excerpt.buffer).row;
-            let start_multibuffer_row = cursor.start().row;
-
-            rows_for_excerpt.push(row);
-            while let Some(next_row) = rows.peek().copied() {
-                if cursor.end(&()).row > next_row {
-                    rows_for_excerpt.push(next_row);
-                    rows.next();
-                } else {
-                    break;
-                }
-            }
-
-            let buffer_rows = rows_for_excerpt
-                .drain(..)
-                .map(|row| start_buffer_row + row - start_multibuffer_row);
-            let buffer_indents = excerpt
-                .buffer
-                .suggested_indents(buffer_rows, single_indent_size);
-            let multibuffer_indents = buffer_indents
-                .into_iter()
-                .map(|(row, indent)| (start_multibuffer_row + row - start_buffer_row, indent));
-            result.extend(multibuffer_indents);
-        }
-
-        result
-    }
-
-    pub fn indent_size_for_line(&self, row: u32) -> IndentSize {
-        if let Some((buffer, range)) = self.buffer_line_for_row(row) {
-            let mut size = buffer.indent_size_for_line(range.start.row);
-            size.len = size
-                .len
-                .min(range.end.column)
-                .saturating_sub(range.start.column);
-            size
-        } else {
-            IndentSize::spaces(0)
-        }
-    }
-
-    pub fn prev_non_blank_row(&self, mut row: u32) -> Option<u32> {
-        while row > 0 {
-            row -= 1;
-            if !self.is_line_blank(row) {
-                return Some(row);
-            }
-        }
-        None
-    }
-
-    pub fn line_len(&self, row: u32) -> u32 {
-        if let Some((_, range)) = self.buffer_line_for_row(row) {
-            range.end.column - range.start.column
-        } else {
-            0
-        }
-    }
-
-    pub fn buffer_line_for_row(&self, row: u32) -> Option<(&BufferSnapshot, Range<Point>)> {
-        let mut cursor = self.excerpts.cursor::<Point>();
-        let point = Point::new(row, 0);
-        cursor.seek(&point, Bias::Right, &());
-        if cursor.item().is_none() && *cursor.start() == point {
-            cursor.prev(&());
-        }
-        if let Some(excerpt) = cursor.item() {
-            let overshoot = row - cursor.start().row;
-            let excerpt_start = excerpt.range.context.start.to_point(&excerpt.buffer);
-            let excerpt_end = excerpt.range.context.end.to_point(&excerpt.buffer);
-            let buffer_row = excerpt_start.row + overshoot;
-            let line_start = Point::new(buffer_row, 0);
-            let line_end = Point::new(buffer_row, excerpt.buffer.line_len(buffer_row));
-            return Some((
-                &excerpt.buffer,
-                line_start.max(excerpt_start)..line_end.min(excerpt_end),
-            ));
-        }
-        None
-    }
-
-    pub fn max_point(&self) -> Point {
-        self.text_summary().lines
-    }
-
-    pub fn text_summary(&self) -> TextSummary {
-        self.excerpts.summary().text.clone()
-    }
-
-    pub fn text_summary_for_range<D, O>(&self, range: Range<O>) -> D
-    where
-        D: TextDimension,
-        O: ToOffset,
-    {
-        let mut summary = D::default();
-        let mut range = range.start.to_offset(self)..range.end.to_offset(self);
-        let mut cursor = self.excerpts.cursor::<usize>();
-        cursor.seek(&range.start, Bias::Right, &());
-        if let Some(excerpt) = cursor.item() {
-            let mut end_before_newline = cursor.end(&());
-            if excerpt.has_trailing_newline {
-                end_before_newline -= 1;
-            }
-
-            let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
-            let start_in_excerpt = excerpt_start + (range.start - cursor.start());
-            let end_in_excerpt =
-                excerpt_start + (cmp::min(end_before_newline, range.end) - cursor.start());
-            summary.add_assign(
-                &excerpt
-                    .buffer
-                    .text_summary_for_range(start_in_excerpt..end_in_excerpt),
-            );
-
-            if range.end > end_before_newline {
-                summary.add_assign(&D::from_text_summary(&TextSummary::from("\n")));
-            }
-
-            cursor.next(&());
-        }
-
-        if range.end > *cursor.start() {
-            summary.add_assign(&D::from_text_summary(&cursor.summary::<_, TextSummary>(
-                &range.end,
-                Bias::Right,
-                &(),
-            )));
-            if let Some(excerpt) = cursor.item() {
-                range.end = cmp::max(*cursor.start(), range.end);
-
-                let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
-                let end_in_excerpt = excerpt_start + (range.end - cursor.start());
-                summary.add_assign(
-                    &excerpt
-                        .buffer
-                        .text_summary_for_range(excerpt_start..end_in_excerpt),
-                );
-            }
-        }
-
-        summary
-    }
-
-    pub fn summary_for_anchor<D>(&self, anchor: &Anchor) -> D
-    where
-        D: TextDimension + Ord + Sub<D, Output = D>,
-    {
-        let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
-        let locator = self.excerpt_locator_for_id(anchor.excerpt_id);
-
-        cursor.seek(locator, Bias::Left, &());
-        if cursor.item().is_none() {
-            cursor.next(&());
-        }
-
-        let mut position = D::from_text_summary(&cursor.start().text);
-        if let Some(excerpt) = cursor.item() {
-            if excerpt.id == anchor.excerpt_id {
-                let excerpt_buffer_start =
-                    excerpt.range.context.start.summary::<D>(&excerpt.buffer);
-                let excerpt_buffer_end = excerpt.range.context.end.summary::<D>(&excerpt.buffer);
-                let buffer_position = cmp::min(
-                    excerpt_buffer_end,
-                    anchor.text_anchor.summary::<D>(&excerpt.buffer),
-                );
-                if buffer_position > excerpt_buffer_start {
-                    position.add_assign(&(buffer_position - excerpt_buffer_start));
-                }
-            }
-        }
-        position
-    }
-
-    pub fn summaries_for_anchors<'a, D, I>(&'a self, anchors: I) -> Vec<D>
-    where
-        D: TextDimension + Ord + Sub<D, Output = D>,
-        I: 'a + IntoIterator<Item = &'a Anchor>,
-    {
-        if let Some((_, _, buffer)) = self.as_singleton() {
-            return buffer
-                .summaries_for_anchors(anchors.into_iter().map(|a| &a.text_anchor))
-                .collect();
-        }
-
-        let mut anchors = anchors.into_iter().peekable();
-        let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
-        let mut summaries = Vec::new();
-        while let Some(anchor) = anchors.peek() {
-            let excerpt_id = anchor.excerpt_id;
-            let excerpt_anchors = iter::from_fn(|| {
-                let anchor = anchors.peek()?;
-                if anchor.excerpt_id == excerpt_id {
-                    Some(&anchors.next().unwrap().text_anchor)
-                } else {
-                    None
-                }
-            });
-
-            let locator = self.excerpt_locator_for_id(excerpt_id);
-            cursor.seek_forward(locator, Bias::Left, &());
-            if cursor.item().is_none() {
-                cursor.next(&());
-            }
-
-            let position = D::from_text_summary(&cursor.start().text);
-            if let Some(excerpt) = cursor.item() {
-                if excerpt.id == excerpt_id {
-                    let excerpt_buffer_start =
-                        excerpt.range.context.start.summary::<D>(&excerpt.buffer);
-                    let excerpt_buffer_end =
-                        excerpt.range.context.end.summary::<D>(&excerpt.buffer);
-                    summaries.extend(
-                        excerpt
-                            .buffer
-                            .summaries_for_anchors::<D, _>(excerpt_anchors)
-                            .map(move |summary| {
-                                let summary = cmp::min(excerpt_buffer_end.clone(), summary);
-                                let mut position = position.clone();
-                                let excerpt_buffer_start = excerpt_buffer_start.clone();
-                                if summary > excerpt_buffer_start {
-                                    position.add_assign(&(summary - excerpt_buffer_start));
-                                }
-                                position
-                            }),
-                    );
-                    continue;
-                }
-            }
-
-            summaries.extend(excerpt_anchors.map(|_| position.clone()));
-        }
-
-        summaries
-    }
-
-    pub fn refresh_anchors<'a, I>(&'a self, anchors: I) -> Vec<(usize, Anchor, bool)>
-    where
-        I: 'a + IntoIterator<Item = &'a Anchor>,
-    {
-        let mut anchors = anchors.into_iter().enumerate().peekable();
-        let mut cursor = self.excerpts.cursor::<Option<&Locator>>();
-        cursor.next(&());
-
-        let mut result = Vec::new();
-
-        while let Some((_, anchor)) = anchors.peek() {
-            let old_excerpt_id = anchor.excerpt_id;
-
-            // Find the location where this anchor's excerpt should be.
-            let old_locator = self.excerpt_locator_for_id(old_excerpt_id);
-            cursor.seek_forward(&Some(old_locator), Bias::Left, &());
-
-            if cursor.item().is_none() {
-                cursor.next(&());
-            }
-
-            let next_excerpt = cursor.item();
-            let prev_excerpt = cursor.prev_item();
-
-            // Process all of the anchors for this excerpt.
-            while let Some((_, anchor)) = anchors.peek() {
-                if anchor.excerpt_id != old_excerpt_id {
-                    break;
-                }
-                let (anchor_ix, anchor) = anchors.next().unwrap();
-                let mut anchor = *anchor;
-
-                // Leave min and max anchors unchanged if invalid or
-                // if the old excerpt still exists at this location
-                let mut kept_position = next_excerpt
-                    .map_or(false, |e| e.id == old_excerpt_id && e.contains(&anchor))
-                    || old_excerpt_id == ExcerptId::max()
-                    || old_excerpt_id == ExcerptId::min();
-
-                // If the old excerpt no longer exists at this location, then attempt to
-                // find an equivalent position for this anchor in an adjacent excerpt.
-                if !kept_position {
-                    for excerpt in [next_excerpt, prev_excerpt].iter().filter_map(|e| *e) {
-                        if excerpt.contains(&anchor) {
-                            anchor.excerpt_id = excerpt.id.clone();
-                            kept_position = true;
-                            break;
-                        }
-                    }
-                }
-
-                // If there's no adjacent excerpt that contains the anchor's position,
-                // then report that the anchor has lost its position.
-                if !kept_position {
-                    anchor = if let Some(excerpt) = next_excerpt {
-                        let mut text_anchor = excerpt
-                            .range
-                            .context
-                            .start
-                            .bias(anchor.text_anchor.bias, &excerpt.buffer);
-                        if text_anchor
-                            .cmp(&excerpt.range.context.end, &excerpt.buffer)
-                            .is_gt()
-                        {
-                            text_anchor = excerpt.range.context.end;
-                        }
-                        Anchor {
-                            buffer_id: Some(excerpt.buffer_id),
-                            excerpt_id: excerpt.id.clone(),
-                            text_anchor,
-                        }
-                    } else if let Some(excerpt) = prev_excerpt {
-                        let mut text_anchor = excerpt
-                            .range
-                            .context
-                            .end
-                            .bias(anchor.text_anchor.bias, &excerpt.buffer);
-                        if text_anchor
-                            .cmp(&excerpt.range.context.start, &excerpt.buffer)
-                            .is_lt()
-                        {
-                            text_anchor = excerpt.range.context.start;
-                        }
-                        Anchor {
-                            buffer_id: Some(excerpt.buffer_id),
-                            excerpt_id: excerpt.id.clone(),
-                            text_anchor,
-                        }
-                    } else if anchor.text_anchor.bias == Bias::Left {
-                        Anchor::min()
-                    } else {
-                        Anchor::max()
-                    };
-                }
-
-                result.push((anchor_ix, anchor, kept_position));
-            }
-        }
-        result.sort_unstable_by(|a, b| a.1.cmp(&b.1, self));
-        result
-    }
-
-    pub fn anchor_before<T: ToOffset>(&self, position: T) -> Anchor {
-        self.anchor_at(position, Bias::Left)
-    }
-
-    pub fn anchor_after<T: ToOffset>(&self, position: T) -> Anchor {
-        self.anchor_at(position, Bias::Right)
-    }
-
-    pub fn anchor_at<T: ToOffset>(&self, position: T, mut bias: Bias) -> Anchor {
-        let offset = position.to_offset(self);
-        if let Some((excerpt_id, buffer_id, buffer)) = self.as_singleton() {
-            return Anchor {
-                buffer_id: Some(buffer_id),
-                excerpt_id: excerpt_id.clone(),
-                text_anchor: buffer.anchor_at(offset, bias),
-            };
-        }
-
-        let mut cursor = self.excerpts.cursor::<(usize, Option<ExcerptId>)>();
-        cursor.seek(&offset, Bias::Right, &());
-        if cursor.item().is_none() && offset == cursor.start().0 && bias == Bias::Left {
-            cursor.prev(&());
-        }
-        if let Some(excerpt) = cursor.item() {
-            let mut overshoot = offset.saturating_sub(cursor.start().0);
-            if excerpt.has_trailing_newline && offset == cursor.end(&()).0 {
-                overshoot -= 1;
-                bias = Bias::Right;
-            }
-
-            let buffer_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
-            let text_anchor =
-                excerpt.clip_anchor(excerpt.buffer.anchor_at(buffer_start + overshoot, bias));
-            Anchor {
-                buffer_id: Some(excerpt.buffer_id),
-                excerpt_id: excerpt.id.clone(),
-                text_anchor,
-            }
-        } else if offset == 0 && bias == Bias::Left {
-            Anchor::min()
-        } else {
-            Anchor::max()
-        }
-    }
-
-    pub fn anchor_in_excerpt(&self, excerpt_id: ExcerptId, text_anchor: text::Anchor) -> Anchor {
-        let locator = self.excerpt_locator_for_id(excerpt_id);
-        let mut cursor = self.excerpts.cursor::<Option<&Locator>>();
-        cursor.seek(locator, Bias::Left, &());
-        if let Some(excerpt) = cursor.item() {
-            if excerpt.id == excerpt_id {
-                let text_anchor = excerpt.clip_anchor(text_anchor);
-                drop(cursor);
-                return Anchor {
-                    buffer_id: Some(excerpt.buffer_id),
-                    excerpt_id,
-                    text_anchor,
-                };
-            }
-        }
-        panic!("excerpt not found");
-    }
-
-    pub fn can_resolve(&self, anchor: &Anchor) -> bool {
-        if anchor.excerpt_id == ExcerptId::min() || anchor.excerpt_id == ExcerptId::max() {
-            true
-        } else if let Some(excerpt) = self.excerpt(anchor.excerpt_id) {
-            excerpt.buffer.can_resolve(&anchor.text_anchor)
-        } else {
-            false
-        }
-    }
-
-    pub fn excerpts(
-        &self,
-    ) -> impl Iterator<Item = (ExcerptId, &BufferSnapshot, ExcerptRange<text::Anchor>)> {
-        self.excerpts
-            .iter()
-            .map(|excerpt| (excerpt.id, &excerpt.buffer, excerpt.range.clone()))
-    }
-
-    pub fn excerpt_boundaries_in_range<R, T>(
-        &self,
-        range: R,
-    ) -> impl Iterator<Item = ExcerptBoundary> + '_
-    where
-        R: RangeBounds<T>,
-        T: ToOffset,
-    {
-        let start_offset;
-        let start = match range.start_bound() {
-            Bound::Included(start) => {
-                start_offset = start.to_offset(self);
-                Bound::Included(start_offset)
-            }
-            Bound::Excluded(start) => {
-                start_offset = start.to_offset(self);
-                Bound::Excluded(start_offset)
-            }
-            Bound::Unbounded => {
-                start_offset = 0;
-                Bound::Unbounded
-            }
-        };
-        let end = match range.end_bound() {
-            Bound::Included(end) => Bound::Included(end.to_offset(self)),
-            Bound::Excluded(end) => Bound::Excluded(end.to_offset(self)),
-            Bound::Unbounded => Bound::Unbounded,
-        };
-        let bounds = (start, end);
-
-        let mut cursor = self.excerpts.cursor::<(usize, Point)>();
-        cursor.seek(&start_offset, Bias::Right, &());
-        if cursor.item().is_none() {
-            cursor.prev(&());
-        }
-        if !bounds.contains(&cursor.start().0) {
-            cursor.next(&());
-        }
-
-        let mut prev_buffer_id = cursor.prev_item().map(|excerpt| excerpt.buffer_id);
-        std::iter::from_fn(move || {
-            if self.singleton {
-                None
-            } else if bounds.contains(&cursor.start().0) {
-                let excerpt = cursor.item()?;
-                let starts_new_buffer = Some(excerpt.buffer_id) != prev_buffer_id;
-                let boundary = ExcerptBoundary {
-                    id: excerpt.id.clone(),
-                    row: cursor.start().1.row,
-                    buffer: excerpt.buffer.clone(),
-                    range: excerpt.range.clone(),
-                    starts_new_buffer,
-                };
-
-                prev_buffer_id = Some(excerpt.buffer_id);
-                cursor.next(&());
-                Some(boundary)
-            } else {
-                None
-            }
-        })
-    }
-
-    pub fn edit_count(&self) -> usize {
-        self.edit_count
-    }
-
-    pub fn parse_count(&self) -> usize {
-        self.parse_count
-    }
-
-    /// Returns the smallest enclosing bracket ranges containing the given range or
-    /// None if no brackets contain range or the range is not contained in a single
-    /// excerpt
-    pub fn innermost_enclosing_bracket_ranges<T: ToOffset>(
-        &self,
-        range: Range<T>,
-    ) -> Option<(Range<usize>, Range<usize>)> {
-        let range = range.start.to_offset(self)..range.end.to_offset(self);
-
-        // Get the ranges of the innermost pair of brackets.
-        let mut result: Option<(Range<usize>, Range<usize>)> = None;
-
-        let Some(enclosing_bracket_ranges) = self.enclosing_bracket_ranges(range.clone()) else {
-            return None;
-        };
-
-        for (open, close) in enclosing_bracket_ranges {
-            let len = close.end - open.start;
-
-            if let Some((existing_open, existing_close)) = &result {
-                let existing_len = existing_close.end - existing_open.start;
-                if len > existing_len {
-                    continue;
-                }
-            }
-
-            result = Some((open, close));
-        }
-
-        result
-    }
-
-    /// Returns enclosing bracket ranges containing the given range or returns None if the range is
-    /// not contained in a single excerpt
-    pub fn enclosing_bracket_ranges<'a, T: ToOffset>(
-        &'a self,
-        range: Range<T>,
-    ) -> Option<impl Iterator<Item = (Range<usize>, Range<usize>)> + 'a> {
-        let range = range.start.to_offset(self)..range.end.to_offset(self);
-
-        self.bracket_ranges(range.clone()).map(|range_pairs| {
-            range_pairs
-                .filter(move |(open, close)| open.start <= range.start && close.end >= range.end)
-        })
-    }
-
-    /// Returns bracket range pairs overlapping the given `range` or returns None if the `range` is
-    /// not contained in a single excerpt
-    pub fn bracket_ranges<'a, T: ToOffset>(
-        &'a self,
-        range: Range<T>,
-    ) -> Option<impl Iterator<Item = (Range<usize>, Range<usize>)> + 'a> {
-        let range = range.start.to_offset(self)..range.end.to_offset(self);
-        let excerpt = self.excerpt_containing(range.clone());
-        excerpt.map(|(excerpt, excerpt_offset)| {
-            let excerpt_buffer_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
-            let excerpt_buffer_end = excerpt_buffer_start + excerpt.text_summary.len;
-
-            let start_in_buffer = excerpt_buffer_start + range.start.saturating_sub(excerpt_offset);
-            let end_in_buffer = excerpt_buffer_start + range.end.saturating_sub(excerpt_offset);
-
-            excerpt
-                .buffer
-                .bracket_ranges(start_in_buffer..end_in_buffer)
-                .filter_map(move |(start_bracket_range, end_bracket_range)| {
-                    if start_bracket_range.start < excerpt_buffer_start
-                        || end_bracket_range.end > excerpt_buffer_end
-                    {
-                        return None;
-                    }
-
-                    let mut start_bracket_range = start_bracket_range.clone();
-                    start_bracket_range.start =
-                        excerpt_offset + (start_bracket_range.start - excerpt_buffer_start);
-                    start_bracket_range.end =
-                        excerpt_offset + (start_bracket_range.end - excerpt_buffer_start);
-
-                    let mut end_bracket_range = end_bracket_range.clone();
-                    end_bracket_range.start =
-                        excerpt_offset + (end_bracket_range.start - excerpt_buffer_start);
-                    end_bracket_range.end =
-                        excerpt_offset + (end_bracket_range.end - excerpt_buffer_start);
-                    Some((start_bracket_range, end_bracket_range))
-                })
-        })
-    }
-
-    pub fn diagnostics_update_count(&self) -> usize {
-        self.diagnostics_update_count
-    }
-
-    pub fn git_diff_update_count(&self) -> usize {
-        self.git_diff_update_count
-    }
-
-    pub fn trailing_excerpt_update_count(&self) -> usize {
-        self.trailing_excerpt_update_count
-    }
-
-    pub fn file_at<'a, T: ToOffset>(&'a self, point: T) -> Option<&'a Arc<dyn File>> {
-        self.point_to_buffer_offset(point)
-            .and_then(|(buffer, _)| buffer.file())
-    }
-
-    pub fn language_at<'a, T: ToOffset>(&'a self, point: T) -> Option<&'a Arc<Language>> {
-        self.point_to_buffer_offset(point)
-            .and_then(|(buffer, offset)| buffer.language_at(offset))
-    }
-
-    pub fn settings_at<'a, T: ToOffset>(
-        &'a self,
-        point: T,
-        cx: &'a AppContext,
-    ) -> &'a LanguageSettings {
-        let mut language = None;
-        let mut file = None;
-        if let Some((buffer, offset)) = self.point_to_buffer_offset(point) {
-            language = buffer.language_at(offset);
-            file = buffer.file();
-        }
-        language_settings(language, file, cx)
-    }
-
-    pub fn language_scope_at<'a, T: ToOffset>(&'a self, point: T) -> Option<LanguageScope> {
-        self.point_to_buffer_offset(point)
-            .and_then(|(buffer, offset)| buffer.language_scope_at(offset))
-    }
-
-    pub fn language_indent_size_at<T: ToOffset>(
-        &self,
-        position: T,
-        cx: &AppContext,
-    ) -> Option<IndentSize> {
-        let (buffer_snapshot, offset) = self.point_to_buffer_offset(position)?;
-        Some(buffer_snapshot.language_indent_size_at(offset, cx))
-    }
-
-    pub fn is_dirty(&self) -> bool {
-        self.is_dirty
-    }
-
-    pub fn has_conflict(&self) -> bool {
-        self.has_conflict
-    }
-
-    pub fn diagnostic_group<'a, O>(
-        &'a self,
-        group_id: usize,
-    ) -> impl Iterator<Item = DiagnosticEntry<O>> + 'a
-    where
-        O: text::FromAnchor + 'a,
-    {
-        self.as_singleton()
-            .into_iter()
-            .flat_map(move |(_, _, buffer)| buffer.diagnostic_group(group_id))
-    }
-
-    pub fn diagnostics_in_range<'a, T, O>(
-        &'a self,
-        range: Range<T>,
-        reversed: bool,
-    ) -> impl Iterator<Item = DiagnosticEntry<O>> + 'a
-    where
-        T: 'a + ToOffset,
-        O: 'a + text::FromAnchor + Ord,
-    {
-        self.as_singleton()
-            .into_iter()
-            .flat_map(move |(_, _, buffer)| {
-                buffer.diagnostics_in_range(
-                    range.start.to_offset(self)..range.end.to_offset(self),
-                    reversed,
-                )
-            })
-    }
-
-    pub fn has_git_diffs(&self) -> bool {
-        for excerpt in self.excerpts.iter() {
-            if !excerpt.buffer.git_diff.is_empty() {
-                return true;
-            }
-        }
-        false
-    }
-
-    pub fn git_diff_hunks_in_range_rev<'a>(
-        &'a self,
-        row_range: Range<u32>,
-    ) -> impl 'a + Iterator<Item = DiffHunk<u32>> {
-        let mut cursor = self.excerpts.cursor::<Point>();
-
-        cursor.seek(&Point::new(row_range.end, 0), Bias::Left, &());
-        if cursor.item().is_none() {
-            cursor.prev(&());
-        }
-
-        std::iter::from_fn(move || {
-            let excerpt = cursor.item()?;
-            let multibuffer_start = *cursor.start();
-            let multibuffer_end = multibuffer_start + excerpt.text_summary.lines;
-            if multibuffer_start.row >= row_range.end {
-                return None;
-            }
-
-            let mut buffer_start = excerpt.range.context.start;
-            let mut buffer_end = excerpt.range.context.end;
-            let excerpt_start_point = buffer_start.to_point(&excerpt.buffer);
-            let excerpt_end_point = excerpt_start_point + excerpt.text_summary.lines;
-
-            if row_range.start > multibuffer_start.row {
-                let buffer_start_point =
-                    excerpt_start_point + Point::new(row_range.start - multibuffer_start.row, 0);
-                buffer_start = excerpt.buffer.anchor_before(buffer_start_point);
-            }
-
-            if row_range.end < multibuffer_end.row {
-                let buffer_end_point =
-                    excerpt_start_point + Point::new(row_range.end - multibuffer_start.row, 0);
-                buffer_end = excerpt.buffer.anchor_before(buffer_end_point);
-            }
-
-            let buffer_hunks = excerpt
-                .buffer
-                .git_diff_hunks_intersecting_range_rev(buffer_start..buffer_end)
-                .filter_map(move |hunk| {
-                    let start = multibuffer_start.row
-                        + hunk
-                            .buffer_range
-                            .start
-                            .saturating_sub(excerpt_start_point.row);
-                    let end = multibuffer_start.row
-                        + hunk
-                            .buffer_range
-                            .end
-                            .min(excerpt_end_point.row + 1)
-                            .saturating_sub(excerpt_start_point.row);
-
-                    Some(DiffHunk {
-                        buffer_range: start..end,
-                        diff_base_byte_range: hunk.diff_base_byte_range.clone(),
-                    })
-                });
-
-            cursor.prev(&());
-
-            Some(buffer_hunks)
-        })
-        .flatten()
-    }
-
-    pub fn git_diff_hunks_in_range<'a>(
-        &'a self,
-        row_range: Range<u32>,
-    ) -> impl 'a + Iterator<Item = DiffHunk<u32>> {
-        let mut cursor = self.excerpts.cursor::<Point>();
-
-        cursor.seek(&Point::new(row_range.start, 0), Bias::Right, &());
-
-        std::iter::from_fn(move || {
-            let excerpt = cursor.item()?;
-            let multibuffer_start = *cursor.start();
-            let multibuffer_end = multibuffer_start + excerpt.text_summary.lines;
-            if multibuffer_start.row >= row_range.end {
-                return None;
-            }
-
-            let mut buffer_start = excerpt.range.context.start;
-            let mut buffer_end = excerpt.range.context.end;
-            let excerpt_start_point = buffer_start.to_point(&excerpt.buffer);
-            let excerpt_end_point = excerpt_start_point + excerpt.text_summary.lines;
-
-            if row_range.start > multibuffer_start.row {
-                let buffer_start_point =
-                    excerpt_start_point + Point::new(row_range.start - multibuffer_start.row, 0);
-                buffer_start = excerpt.buffer.anchor_before(buffer_start_point);
-            }
-
-            if row_range.end < multibuffer_end.row {
-                let buffer_end_point =
-                    excerpt_start_point + Point::new(row_range.end - multibuffer_start.row, 0);
-                buffer_end = excerpt.buffer.anchor_before(buffer_end_point);
-            }
-
-            let buffer_hunks = excerpt
-                .buffer
-                .git_diff_hunks_intersecting_range(buffer_start..buffer_end)
-                .filter_map(move |hunk| {
-                    let start = multibuffer_start.row
-                        + hunk
-                            .buffer_range
-                            .start
-                            .saturating_sub(excerpt_start_point.row);
-                    let end = multibuffer_start.row
-                        + hunk
-                            .buffer_range
-                            .end
-                            .min(excerpt_end_point.row + 1)
-                            .saturating_sub(excerpt_start_point.row);
-
-                    Some(DiffHunk {
-                        buffer_range: start..end,
-                        diff_base_byte_range: hunk.diff_base_byte_range.clone(),
-                    })
-                });
-
-            cursor.next(&());
-
-            Some(buffer_hunks)
-        })
-        .flatten()
-    }
-
-    pub fn range_for_syntax_ancestor<T: ToOffset>(&self, range: Range<T>) -> Option<Range<usize>> {
-        let range = range.start.to_offset(self)..range.end.to_offset(self);
-
-        self.excerpt_containing(range.clone())
-            .and_then(|(excerpt, excerpt_offset)| {
-                let excerpt_buffer_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
-                let excerpt_buffer_end = excerpt_buffer_start + excerpt.text_summary.len;
-
-                let start_in_buffer =
-                    excerpt_buffer_start + range.start.saturating_sub(excerpt_offset);
-                let end_in_buffer = excerpt_buffer_start + range.end.saturating_sub(excerpt_offset);
-                let mut ancestor_buffer_range = excerpt
-                    .buffer
-                    .range_for_syntax_ancestor(start_in_buffer..end_in_buffer)?;
-                ancestor_buffer_range.start =
-                    cmp::max(ancestor_buffer_range.start, excerpt_buffer_start);
-                ancestor_buffer_range.end = cmp::min(ancestor_buffer_range.end, excerpt_buffer_end);
-
-                let start = excerpt_offset + (ancestor_buffer_range.start - excerpt_buffer_start);
-                let end = excerpt_offset + (ancestor_buffer_range.end - excerpt_buffer_start);
-                Some(start..end)
-            })
-    }
-
-    pub fn outline(&self, theme: Option<&SyntaxTheme>) -> Option<Outline<Anchor>> {
-        let (excerpt_id, _, buffer) = self.as_singleton()?;
-        let outline = buffer.outline(theme)?;
-        Some(Outline::new(
-            outline
-                .items
-                .into_iter()
-                .map(|item| OutlineItem {
-                    depth: item.depth,
-                    range: self.anchor_in_excerpt(excerpt_id.clone(), item.range.start)
-                        ..self.anchor_in_excerpt(excerpt_id.clone(), item.range.end),
-                    text: item.text,
-                    highlight_ranges: item.highlight_ranges,
-                    name_ranges: item.name_ranges,
-                })
-                .collect(),
-        ))
-    }
-
-    pub fn symbols_containing<T: ToOffset>(
-        &self,
-        offset: T,
-        theme: Option<&SyntaxTheme>,
-    ) -> Option<(u64, Vec<OutlineItem<Anchor>>)> {
-        let anchor = self.anchor_before(offset);
-        let excerpt_id = anchor.excerpt_id;
-        let excerpt = self.excerpt(excerpt_id)?;
-        Some((
-            excerpt.buffer_id,
-            excerpt
-                .buffer
-                .symbols_containing(anchor.text_anchor, theme)
-                .into_iter()
-                .flatten()
-                .map(|item| OutlineItem {
-                    depth: item.depth,
-                    range: self.anchor_in_excerpt(excerpt_id, item.range.start)
-                        ..self.anchor_in_excerpt(excerpt_id, item.range.end),
-                    text: item.text,
-                    highlight_ranges: item.highlight_ranges,
-                    name_ranges: item.name_ranges,
-                })
-                .collect(),
-        ))
-    }
-
-    fn excerpt_locator_for_id<'a>(&'a self, id: ExcerptId) -> &'a Locator {
-        if id == ExcerptId::min() {
-            Locator::min_ref()
-        } else if id == ExcerptId::max() {
-            Locator::max_ref()
-        } else {
-            let mut cursor = self.excerpt_ids.cursor::<ExcerptId>();
-            cursor.seek(&id, Bias::Left, &());
-            if let Some(entry) = cursor.item() {
-                if entry.id == id {
-                    return &entry.locator;
-                }
-            }
-            panic!("invalid excerpt id {:?}", id)
-        }
-    }
-
-    pub fn buffer_id_for_excerpt(&self, excerpt_id: ExcerptId) -> Option<u64> {
-        Some(self.excerpt(excerpt_id)?.buffer_id)
-    }
-
-    pub fn buffer_for_excerpt(&self, excerpt_id: ExcerptId) -> Option<&BufferSnapshot> {
-        Some(&self.excerpt(excerpt_id)?.buffer)
-    }
-
-    fn excerpt<'a>(&'a self, excerpt_id: ExcerptId) -> Option<&'a Excerpt> {
-        let mut cursor = self.excerpts.cursor::<Option<&Locator>>();
-        let locator = self.excerpt_locator_for_id(excerpt_id);
-        cursor.seek(&Some(locator), Bias::Left, &());
-        if let Some(excerpt) = cursor.item() {
-            if excerpt.id == excerpt_id {
-                return Some(excerpt);
-            }
-        }
-        None
-    }
-
-    /// Returns the excerpt containing range and its offset start within the multibuffer or none if `range` spans multiple excerpts
-    fn excerpt_containing<'a, T: ToOffset>(
-        &'a self,
-        range: Range<T>,
-    ) -> Option<(&'a Excerpt, usize)> {
-        let range = range.start.to_offset(self)..range.end.to_offset(self);
-
-        let mut cursor = self.excerpts.cursor::<usize>();
-        cursor.seek(&range.start, Bias::Right, &());
-        let start_excerpt = cursor.item();
-
-        if range.start == range.end {
-            return start_excerpt.map(|excerpt| (excerpt, *cursor.start()));
-        }
-
-        cursor.seek(&range.end, Bias::Right, &());
-        let end_excerpt = cursor.item();
-
-        start_excerpt
-            .zip(end_excerpt)
-            .and_then(|(start_excerpt, end_excerpt)| {
-                if start_excerpt.id != end_excerpt.id {
-                    return None;
-                }
-
-                Some((start_excerpt, *cursor.start()))
-            })
-    }
-
-    pub fn remote_selections_in_range<'a>(
-        &'a self,
-        range: &'a Range<Anchor>,
-    ) -> impl 'a + Iterator<Item = (ReplicaId, bool, CursorShape, Selection<Anchor>)> {
-        let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
-        let start_locator = self.excerpt_locator_for_id(range.start.excerpt_id);
-        let end_locator = self.excerpt_locator_for_id(range.end.excerpt_id);
-        cursor.seek(start_locator, Bias::Left, &());
-        cursor
-            .take_while(move |excerpt| excerpt.locator <= *end_locator)
-            .flat_map(move |excerpt| {
-                let mut query_range = excerpt.range.context.start..excerpt.range.context.end;
-                if excerpt.id == range.start.excerpt_id {
-                    query_range.start = range.start.text_anchor;
-                }
-                if excerpt.id == range.end.excerpt_id {
-                    query_range.end = range.end.text_anchor;
-                }
-
-                excerpt
-                    .buffer
-                    .remote_selections_in_range(query_range)
-                    .flat_map(move |(replica_id, line_mode, cursor_shape, selections)| {
-                        selections.map(move |selection| {
-                            let mut start = Anchor {
-                                buffer_id: Some(excerpt.buffer_id),
-                                excerpt_id: excerpt.id.clone(),
-                                text_anchor: selection.start,
-                            };
-                            let mut end = Anchor {
-                                buffer_id: Some(excerpt.buffer_id),
-                                excerpt_id: excerpt.id.clone(),
-                                text_anchor: selection.end,
-                            };
-                            if range.start.cmp(&start, self).is_gt() {
-                                start = range.start.clone();
-                            }
-                            if range.end.cmp(&end, self).is_lt() {
-                                end = range.end.clone();
-                            }
-
-                            (
-                                replica_id,
-                                line_mode,
-                                cursor_shape,
-                                Selection {
-                                    id: selection.id,
-                                    start,
-                                    end,
-                                    reversed: selection.reversed,
-                                    goal: selection.goal,
-                                },
-                            )
-                        })
-                    })
-            })
-    }
-}
-
-#[cfg(any(test, feature = "test-support"))]
-impl MultiBufferSnapshot {
-    pub fn random_byte_range(&self, start_offset: usize, rng: &mut impl rand::Rng) -> Range<usize> {
-        let end = self.clip_offset(rng.gen_range(start_offset..=self.len()), Bias::Right);
-        let start = self.clip_offset(rng.gen_range(start_offset..=end), Bias::Right);
-        start..end
-    }
-}
-
-impl History {
-    fn start_transaction(&mut self, now: Instant) -> Option<TransactionId> {
-        self.transaction_depth += 1;
-        if self.transaction_depth == 1 {
-            let id = self.next_transaction_id.tick();
-            self.undo_stack.push(Transaction {
-                id,
-                buffer_transactions: Default::default(),
-                first_edit_at: now,
-                last_edit_at: now,
-                suppress_grouping: false,
-            });
-            Some(id)
-        } else {
-            None
-        }
-    }
-
-    fn end_transaction(
-        &mut self,
-        now: Instant,
-        buffer_transactions: HashMap<u64, TransactionId>,
-    ) -> bool {
-        assert_ne!(self.transaction_depth, 0);
-        self.transaction_depth -= 1;
-        if self.transaction_depth == 0 {
-            if buffer_transactions.is_empty() {
-                self.undo_stack.pop();
-                false
-            } else {
-                self.redo_stack.clear();
-                let transaction = self.undo_stack.last_mut().unwrap();
-                transaction.last_edit_at = now;
-                for (buffer_id, transaction_id) in buffer_transactions {
-                    transaction
-                        .buffer_transactions
-                        .entry(buffer_id)
-                        .or_insert(transaction_id);
-                }
-                true
-            }
-        } else {
-            false
-        }
-    }
-
-    fn push_transaction<'a, T>(
-        &mut self,
-        buffer_transactions: T,
-        now: Instant,
-        cx: &mut ModelContext<MultiBuffer>,
-    ) where
-        T: IntoIterator<Item = (&'a Model<Buffer>, &'a language::Transaction)>,
-    {
-        assert_eq!(self.transaction_depth, 0);
-        let transaction = Transaction {
-            id: self.next_transaction_id.tick(),
-            buffer_transactions: buffer_transactions
-                .into_iter()
-                .map(|(buffer, transaction)| (buffer.read(cx).remote_id(), transaction.id))
-                .collect(),
-            first_edit_at: now,
-            last_edit_at: now,
-            suppress_grouping: false,
-        };
-        if !transaction.buffer_transactions.is_empty() {
-            self.undo_stack.push(transaction);
-            self.redo_stack.clear();
-        }
-    }
-
-    fn finalize_last_transaction(&mut self) {
-        if let Some(transaction) = self.undo_stack.last_mut() {
-            transaction.suppress_grouping = true;
-        }
-    }
-
-    fn forget(&mut self, transaction_id: TransactionId) -> Option<Transaction> {
-        if let Some(ix) = self
-            .undo_stack
-            .iter()
-            .rposition(|transaction| transaction.id == transaction_id)
-        {
-            Some(self.undo_stack.remove(ix))
-        } else if let Some(ix) = self
-            .redo_stack
-            .iter()
-            .rposition(|transaction| transaction.id == transaction_id)
-        {
-            Some(self.redo_stack.remove(ix))
-        } else {
-            None
-        }
-    }
-
-    fn transaction_mut(&mut self, transaction_id: TransactionId) -> Option<&mut Transaction> {
-        self.undo_stack
-            .iter_mut()
-            .find(|transaction| transaction.id == transaction_id)
-            .or_else(|| {
-                self.redo_stack
-                    .iter_mut()
-                    .find(|transaction| transaction.id == transaction_id)
-            })
-    }
-
-    fn pop_undo(&mut self) -> Option<&mut Transaction> {
-        assert_eq!(self.transaction_depth, 0);
-        if let Some(transaction) = self.undo_stack.pop() {
-            self.redo_stack.push(transaction);
-            self.redo_stack.last_mut()
-        } else {
-            None
-        }
-    }
-
-    fn pop_redo(&mut self) -> Option<&mut Transaction> {
-        assert_eq!(self.transaction_depth, 0);
-        if let Some(transaction) = self.redo_stack.pop() {
-            self.undo_stack.push(transaction);
-            self.undo_stack.last_mut()
-        } else {
-            None
-        }
-    }
-
-    fn remove_from_undo(&mut self, transaction_id: TransactionId) -> Option<&Transaction> {
-        let ix = self
-            .undo_stack
-            .iter()
-            .rposition(|transaction| transaction.id == transaction_id)?;
-        let transaction = self.undo_stack.remove(ix);
-        self.redo_stack.push(transaction);
-        self.redo_stack.last()
-    }
-
-    fn group(&mut self) -> Option<TransactionId> {
-        let mut count = 0;
-        let mut transactions = self.undo_stack.iter();
-        if let Some(mut transaction) = transactions.next_back() {
-            while let Some(prev_transaction) = transactions.next_back() {
-                if !prev_transaction.suppress_grouping
-                    && transaction.first_edit_at - prev_transaction.last_edit_at
-                        <= self.group_interval
-                {
-                    transaction = prev_transaction;
-                    count += 1;
-                } else {
-                    break;
-                }
-            }
-        }
-        self.group_trailing(count)
-    }
-
-    fn group_until(&mut self, transaction_id: TransactionId) {
-        let mut count = 0;
-        for transaction in self.undo_stack.iter().rev() {
-            if transaction.id == transaction_id {
-                self.group_trailing(count);
-                break;
-            } else if transaction.suppress_grouping {
-                break;
-            } else {
-                count += 1;
-            }
-        }
-    }
-
-    fn group_trailing(&mut self, n: usize) -> Option<TransactionId> {
-        let new_len = self.undo_stack.len() - n;
-        let (transactions_to_keep, transactions_to_merge) = self.undo_stack.split_at_mut(new_len);
-        if let Some(last_transaction) = transactions_to_keep.last_mut() {
-            if let Some(transaction) = transactions_to_merge.last() {
-                last_transaction.last_edit_at = transaction.last_edit_at;
-            }
-            for to_merge in transactions_to_merge {
-                for (buffer_id, transaction_id) in &to_merge.buffer_transactions {
-                    last_transaction
-                        .buffer_transactions
-                        .entry(*buffer_id)
-                        .or_insert(*transaction_id);
-                }
-            }
-        }
-
-        self.undo_stack.truncate(new_len);
-        self.undo_stack.last().map(|t| t.id)
-    }
-}
-
-impl Excerpt {
-    fn new(
-        id: ExcerptId,
-        locator: Locator,
-        buffer_id: u64,
-        buffer: BufferSnapshot,
-        range: ExcerptRange<text::Anchor>,
-        has_trailing_newline: bool,
-    ) -> Self {
-        Excerpt {
-            id,
-            locator,
-            max_buffer_row: range.context.end.to_point(&buffer).row,
-            text_summary: buffer
-                .text_summary_for_range::<TextSummary, _>(range.context.to_offset(&buffer)),
-            buffer_id,
-            buffer,
-            range,
-            has_trailing_newline,
-        }
-    }
-
-    fn chunks_in_range(&self, range: Range<usize>, language_aware: bool) -> ExcerptChunks {
-        let content_start = self.range.context.start.to_offset(&self.buffer);
-        let chunks_start = content_start + range.start;
-        let chunks_end = content_start + cmp::min(range.end, self.text_summary.len);
-
-        let footer_height = if self.has_trailing_newline
-            && range.start <= self.text_summary.len
-            && range.end > self.text_summary.len
-        {
-            1
-        } else {
-            0
-        };
-
-        let content_chunks = self.buffer.chunks(chunks_start..chunks_end, language_aware);
-
-        ExcerptChunks {
-            content_chunks,
-            footer_height,
-        }
-    }
-
-    fn bytes_in_range(&self, range: Range<usize>) -> ExcerptBytes {
-        let content_start = self.range.context.start.to_offset(&self.buffer);
-        let bytes_start = content_start + range.start;
-        let bytes_end = content_start + cmp::min(range.end, self.text_summary.len);
-        let footer_height = if self.has_trailing_newline
-            && range.start <= self.text_summary.len
-            && range.end > self.text_summary.len
-        {
-            1
-        } else {
-            0
-        };
-        let content_bytes = self.buffer.bytes_in_range(bytes_start..bytes_end);
-
-        ExcerptBytes {
-            content_bytes,
-            footer_height,
-        }
-    }
-
-    fn reversed_bytes_in_range(&self, range: Range<usize>) -> ExcerptBytes {
-        let content_start = self.range.context.start.to_offset(&self.buffer);
-        let bytes_start = content_start + range.start;
-        let bytes_end = content_start + cmp::min(range.end, self.text_summary.len);
-        let footer_height = if self.has_trailing_newline
-            && range.start <= self.text_summary.len
-            && range.end > self.text_summary.len
-        {
-            1
-        } else {
-            0
-        };
-        let content_bytes = self.buffer.reversed_bytes_in_range(bytes_start..bytes_end);
-
-        ExcerptBytes {
-            content_bytes,
-            footer_height,
-        }
-    }
-
-    fn clip_anchor(&self, text_anchor: text::Anchor) -> text::Anchor {
-        if text_anchor
-            .cmp(&self.range.context.start, &self.buffer)
-            .is_lt()
-        {
-            self.range.context.start
-        } else if text_anchor
-            .cmp(&self.range.context.end, &self.buffer)
-            .is_gt()
-        {
-            self.range.context.end
-        } else {
-            text_anchor
-        }
-    }
-
-    fn contains(&self, anchor: &Anchor) -> bool {
-        Some(self.buffer_id) == anchor.buffer_id
-            && self
-                .range
-                .context
-                .start
-                .cmp(&anchor.text_anchor, &self.buffer)
-                .is_le()
-            && self
-                .range
-                .context
-                .end
-                .cmp(&anchor.text_anchor, &self.buffer)
-                .is_ge()
-    }
-}
-
-impl ExcerptId {
-    pub fn min() -> Self {
-        Self(0)
-    }
-
-    pub fn max() -> Self {
-        Self(usize::MAX)
-    }
-
-    pub fn to_proto(&self) -> u64 {
-        self.0 as _
-    }
-
-    pub fn from_proto(proto: u64) -> Self {
-        Self(proto as _)
-    }
-
-    pub fn cmp(&self, other: &Self, snapshot: &MultiBufferSnapshot) -> cmp::Ordering {
-        let a = snapshot.excerpt_locator_for_id(*self);
-        let b = snapshot.excerpt_locator_for_id(*other);
-        a.cmp(&b).then_with(|| self.0.cmp(&other.0))
-    }
-}
-
-impl Into<usize> for ExcerptId {
-    fn into(self) -> usize {
-        self.0
-    }
-}
-
-impl fmt::Debug for Excerpt {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("Excerpt")
-            .field("id", &self.id)
-            .field("locator", &self.locator)
-            .field("buffer_id", &self.buffer_id)
-            .field("range", &self.range)
-            .field("text_summary", &self.text_summary)
-            .field("has_trailing_newline", &self.has_trailing_newline)
-            .finish()
-    }
-}
-
-impl sum_tree::Item for Excerpt {
-    type Summary = ExcerptSummary;
-
-    fn summary(&self) -> Self::Summary {
-        let mut text = self.text_summary.clone();
-        if self.has_trailing_newline {
-            text += TextSummary::from("\n");
-        }
-        ExcerptSummary {
-            excerpt_id: self.id,
-            excerpt_locator: self.locator.clone(),
-            max_buffer_row: self.max_buffer_row,
-            text,
-        }
-    }
-}
-
-impl sum_tree::Item for ExcerptIdMapping {
-    type Summary = ExcerptId;
-
-    fn summary(&self) -> Self::Summary {
-        self.id
-    }
-}
-
-impl sum_tree::KeyedItem for ExcerptIdMapping {
-    type Key = ExcerptId;
-
-    fn key(&self) -> Self::Key {
-        self.id
-    }
-}
-
-impl sum_tree::Summary for ExcerptId {
-    type Context = ();
-
-    fn add_summary(&mut self, other: &Self, _: &()) {
-        *self = *other;
-    }
-}
-
-impl sum_tree::Summary for ExcerptSummary {
-    type Context = ();
-
-    fn add_summary(&mut self, summary: &Self, _: &()) {
-        debug_assert!(summary.excerpt_locator > self.excerpt_locator);
-        self.excerpt_locator = summary.excerpt_locator.clone();
-        self.text.add_summary(&summary.text, &());
-        self.max_buffer_row = cmp::max(self.max_buffer_row, summary.max_buffer_row);
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for TextSummary {
-    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
-        *self += &summary.text;
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for usize {
-    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
-        *self += summary.text.len;
-    }
-}
-
-impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for usize {
-    fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
-        Ord::cmp(self, &cursor_location.text.len)
-    }
-}
-
-impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, Option<&'a Locator>> for Locator {
-    fn cmp(&self, cursor_location: &Option<&'a Locator>, _: &()) -> cmp::Ordering {
-        Ord::cmp(&Some(self), cursor_location)
-    }
-}
-
-impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for Locator {
-    fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
-        Ord::cmp(self, &cursor_location.excerpt_locator)
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for OffsetUtf16 {
-    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
-        *self += summary.text.len_utf16;
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Point {
-    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
-        *self += summary.text.lines;
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for PointUtf16 {
-    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
-        *self += summary.text.lines_utf16()
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option<&'a Locator> {
-    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
-        *self = Some(&summary.excerpt_locator);
-    }
-}
-
-impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option<ExcerptId> {
-    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
-        *self = Some(summary.excerpt_id);
-    }
-}
-
-impl<'a> MultiBufferRows<'a> {
-    pub fn seek(&mut self, row: u32) {
-        self.buffer_row_range = 0..0;
-
-        self.excerpts
-            .seek_forward(&Point::new(row, 0), Bias::Right, &());
-        if self.excerpts.item().is_none() {
-            self.excerpts.prev(&());
-
-            if self.excerpts.item().is_none() && row == 0 {
-                self.buffer_row_range = 0..1;
-                return;
-            }
-        }
-
-        if let Some(excerpt) = self.excerpts.item() {
-            let overshoot = row - self.excerpts.start().row;
-            let excerpt_start = excerpt.range.context.start.to_point(&excerpt.buffer).row;
-            self.buffer_row_range.start = excerpt_start + overshoot;
-            self.buffer_row_range.end = excerpt_start + excerpt.text_summary.lines.row + 1;
-        }
-    }
-}
-
-impl<'a> Iterator for MultiBufferRows<'a> {
-    type Item = Option<u32>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        loop {
-            if !self.buffer_row_range.is_empty() {
-                let row = Some(self.buffer_row_range.start);
-                self.buffer_row_range.start += 1;
-                return Some(row);
-            }
-            self.excerpts.item()?;
-            self.excerpts.next(&());
-            let excerpt = self.excerpts.item()?;
-            self.buffer_row_range.start = excerpt.range.context.start.to_point(&excerpt.buffer).row;
-            self.buffer_row_range.end =
-                self.buffer_row_range.start + excerpt.text_summary.lines.row + 1;
-        }
-    }
-}
-
-impl<'a> MultiBufferChunks<'a> {
-    pub fn offset(&self) -> usize {
-        self.range.start
-    }
-
-    pub fn seek(&mut self, offset: usize) {
-        self.range.start = offset;
-        self.excerpts.seek(&offset, Bias::Right, &());
-        if let Some(excerpt) = self.excerpts.item() {
-            self.excerpt_chunks = Some(excerpt.chunks_in_range(
-                self.range.start - self.excerpts.start()..self.range.end - self.excerpts.start(),
-                self.language_aware,
-            ));
-        } else {
-            self.excerpt_chunks = None;
-        }
-    }
-}
-
-impl<'a> Iterator for MultiBufferChunks<'a> {
-    type Item = Chunk<'a>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if self.range.is_empty() {
-            None
-        } else if let Some(chunk) = self.excerpt_chunks.as_mut()?.next() {
-            self.range.start += chunk.text.len();
-            Some(chunk)
-        } else {
-            self.excerpts.next(&());
-            let excerpt = self.excerpts.item()?;
-            self.excerpt_chunks = Some(excerpt.chunks_in_range(
-                0..self.range.end - self.excerpts.start(),
-                self.language_aware,
-            ));
-            self.next()
-        }
-    }
-}
-
-impl<'a> MultiBufferBytes<'a> {
-    fn consume(&mut self, len: usize) {
-        self.range.start += len;
-        self.chunk = &self.chunk[len..];
-
-        if !self.range.is_empty() && self.chunk.is_empty() {
-            if let Some(chunk) = self.excerpt_bytes.as_mut().and_then(|bytes| bytes.next()) {
-                self.chunk = chunk;
-            } else {
-                self.excerpts.next(&());
-                if let Some(excerpt) = self.excerpts.item() {
-                    let mut excerpt_bytes =
-                        excerpt.bytes_in_range(0..self.range.end - self.excerpts.start());
-                    self.chunk = excerpt_bytes.next().unwrap();
-                    self.excerpt_bytes = Some(excerpt_bytes);
-                }
-            }
-        }
-    }
-}
-
-impl<'a> Iterator for MultiBufferBytes<'a> {
-    type Item = &'a [u8];
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let chunk = self.chunk;
-        if chunk.is_empty() {
-            None
-        } else {
-            self.consume(chunk.len());
-            Some(chunk)
-        }
-    }
-}
-
-impl<'a> io::Read for MultiBufferBytes<'a> {
-    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
-        let len = cmp::min(buf.len(), self.chunk.len());
-        buf[..len].copy_from_slice(&self.chunk[..len]);
-        if len > 0 {
-            self.consume(len);
-        }
-        Ok(len)
-    }
-}
-
-impl<'a> ReversedMultiBufferBytes<'a> {
-    fn consume(&mut self, len: usize) {
-        self.range.end -= len;
-        self.chunk = &self.chunk[..self.chunk.len() - len];
-
-        if !self.range.is_empty() && self.chunk.is_empty() {
-            if let Some(chunk) = self.excerpt_bytes.as_mut().and_then(|bytes| bytes.next()) {
-                self.chunk = chunk;
-            } else {
-                self.excerpts.next(&());
-                if let Some(excerpt) = self.excerpts.item() {
-                    let mut excerpt_bytes =
-                        excerpt.bytes_in_range(0..self.range.end - self.excerpts.start());
-                    self.chunk = excerpt_bytes.next().unwrap();
-                    self.excerpt_bytes = Some(excerpt_bytes);
-                }
-            }
-        }
-    }
-}
-
-impl<'a> io::Read for ReversedMultiBufferBytes<'a> {
-    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
-        let len = cmp::min(buf.len(), self.chunk.len());
-        buf[..len].copy_from_slice(&self.chunk[..len]);
-        buf[..len].reverse();
-        if len > 0 {
-            self.consume(len);
-        }
-        Ok(len)
-    }
-}
-impl<'a> Iterator for ExcerptBytes<'a> {
-    type Item = &'a [u8];
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if let Some(chunk) = self.content_bytes.next() {
-            if !chunk.is_empty() {
-                return Some(chunk);
-            }
-        }
-
-        if self.footer_height > 0 {
-            let result = &NEWLINES[..self.footer_height];
-            self.footer_height = 0;
-            return Some(result);
-        }
-
-        None
-    }
-}
-
-impl<'a> Iterator for ExcerptChunks<'a> {
-    type Item = Chunk<'a>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if let Some(chunk) = self.content_chunks.next() {
-            return Some(chunk);
-        }
-
-        if self.footer_height > 0 {
-            let text = unsafe { str::from_utf8_unchecked(&NEWLINES[..self.footer_height]) };
-            self.footer_height = 0;
-            return Some(Chunk {
-                text,
-                ..Default::default()
-            });
-        }
-
-        None
-    }
-}
-
-impl ToOffset for Point {
-    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
-        snapshot.point_to_offset(*self)
-    }
-}
-
-impl ToOffset for usize {
-    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
-        assert!(*self <= snapshot.len(), "offset is out of range");
-        *self
-    }
-}
-
-impl ToOffset for OffsetUtf16 {
-    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
-        snapshot.offset_utf16_to_offset(*self)
-    }
-}
-
-impl ToOffset for PointUtf16 {
-    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
-        snapshot.point_utf16_to_offset(*self)
-    }
-}
-
-impl ToOffsetUtf16 for OffsetUtf16 {
-    fn to_offset_utf16(&self, _snapshot: &MultiBufferSnapshot) -> OffsetUtf16 {
-        *self
-    }
-}
-
-impl ToOffsetUtf16 for usize {
-    fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16 {
-        snapshot.offset_to_offset_utf16(*self)
-    }
-}
-
-impl ToPoint for usize {
-    fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
-        snapshot.offset_to_point(*self)
-    }
-}
-
-impl ToPoint for Point {
-    fn to_point<'a>(&self, _: &MultiBufferSnapshot) -> Point {
-        *self
-    }
-}
-
-impl ToPointUtf16 for usize {
-    fn to_point_utf16<'a>(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16 {
-        snapshot.offset_to_point_utf16(*self)
-    }
-}
-
-impl ToPointUtf16 for Point {
-    fn to_point_utf16<'a>(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16 {
-        snapshot.point_to_point_utf16(*self)
-    }
-}
-
-impl ToPointUtf16 for PointUtf16 {
-    fn to_point_utf16<'a>(&self, _: &MultiBufferSnapshot) -> PointUtf16 {
-        *self
-    }
-}
-
-fn build_excerpt_ranges<T>(
-    buffer: &BufferSnapshot,
-    ranges: &[Range<T>],
-    context_line_count: u32,
-) -> (Vec<ExcerptRange<Point>>, Vec<usize>)
-where
-    T: text::ToPoint,
-{
-    let max_point = buffer.max_point();
-    let mut range_counts = Vec::new();
-    let mut excerpt_ranges = Vec::new();
-    let mut range_iter = ranges
-        .iter()
-        .map(|range| range.start.to_point(buffer)..range.end.to_point(buffer))
-        .peekable();
-    while let Some(range) = range_iter.next() {
-        let excerpt_start = Point::new(range.start.row.saturating_sub(context_line_count), 0);
-        let mut excerpt_end = Point::new(range.end.row + 1 + context_line_count, 0).min(max_point);
-        let mut ranges_in_excerpt = 1;
-
-        while let Some(next_range) = range_iter.peek() {
-            if next_range.start.row <= excerpt_end.row + context_line_count {
-                excerpt_end =
-                    Point::new(next_range.end.row + 1 + context_line_count, 0).min(max_point);
-                ranges_in_excerpt += 1;
-                range_iter.next();
-            } else {
-                break;
-            }
-        }
-
-        excerpt_ranges.push(ExcerptRange {
-            context: excerpt_start..excerpt_end,
-            primary: Some(range),
-        });
-        range_counts.push(ranges_in_excerpt);
-    }
-
-    (excerpt_ranges, range_counts)
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use futures::StreamExt;
-    use gpui::{AppContext, Context, TestAppContext};
-    use language::{Buffer, Rope};
-    use parking_lot::RwLock;
-    use rand::prelude::*;
-    use settings::SettingsStore;
-    use std::env;
-    use util::test::sample_text;
-
-    #[gpui::test]
-    fn test_singleton(cx: &mut AppContext) {
-        let buffer =
-            cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(6, 6, 'a')));
-        let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
-
-        let snapshot = multibuffer.read(cx).snapshot(cx);
-        assert_eq!(snapshot.text(), buffer.read(cx).text());
-
-        assert_eq!(
-            snapshot.buffer_rows(0).collect::<Vec<_>>(),
-            (0..buffer.read(cx).row_count())
-                .map(Some)
-                .collect::<Vec<_>>()
-        );
-
-        buffer.update(cx, |buffer, cx| buffer.edit([(1..3, "XXX\n")], None, cx));
-        let snapshot = multibuffer.read(cx).snapshot(cx);
-
-        assert_eq!(snapshot.text(), buffer.read(cx).text());
-        assert_eq!(
-            snapshot.buffer_rows(0).collect::<Vec<_>>(),
-            (0..buffer.read(cx).row_count())
-                .map(Some)
-                .collect::<Vec<_>>()
-        );
-    }
-
-    #[gpui::test]
-    fn test_remote(cx: &mut AppContext) {
-        let host_buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "a"));
-        let guest_buffer = cx.new_model(|cx| {
-            let state = host_buffer.read(cx).to_proto();
-            let ops = cx
-                .background_executor()
-                .block(host_buffer.read(cx).serialize_ops(None, cx));
-            let mut buffer = Buffer::from_proto(1, state, None).unwrap();
-            buffer
-                .apply_ops(
-                    ops.into_iter()
-                        .map(|op| language::proto::deserialize_operation(op).unwrap()),
-                    cx,
-                )
-                .unwrap();
-            buffer
-        });
-        let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(guest_buffer.clone(), cx));
-        let snapshot = multibuffer.read(cx).snapshot(cx);
-        assert_eq!(snapshot.text(), "a");
-
-        guest_buffer.update(cx, |buffer, cx| buffer.edit([(1..1, "b")], None, cx));
-        let snapshot = multibuffer.read(cx).snapshot(cx);
-        assert_eq!(snapshot.text(), "ab");
-
-        guest_buffer.update(cx, |buffer, cx| buffer.edit([(2..2, "c")], None, cx));
-        let snapshot = multibuffer.read(cx).snapshot(cx);
-        assert_eq!(snapshot.text(), "abc");
-    }
-
-    #[gpui::test]
-    fn test_excerpt_boundaries_and_clipping(cx: &mut AppContext) {
-        let buffer_1 =
-            cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(6, 6, 'a')));
-        let buffer_2 =
-            cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(6, 6, 'g')));
-        let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
-
-        let events = Arc::new(RwLock::new(Vec::<Event>::new()));
-        multibuffer.update(cx, |_, cx| {
-            let events = events.clone();
-            cx.subscribe(&multibuffer, move |_, _, event, _| {
-                if let Event::Edited { .. } = event {
-                    events.write().push(event.clone())
-                }
-            })
-            .detach();
-        });
-
-        let subscription = multibuffer.update(cx, |multibuffer, cx| {
-            let subscription = multibuffer.subscribe();
-            multibuffer.push_excerpts(
-                buffer_1.clone(),
-                [ExcerptRange {
-                    context: Point::new(1, 2)..Point::new(2, 5),
-                    primary: None,
-                }],
-                cx,
-            );
-            assert_eq!(
-                subscription.consume().into_inner(),
-                [Edit {
-                    old: 0..0,
-                    new: 0..10
-                }]
-            );
-
-            multibuffer.push_excerpts(
-                buffer_1.clone(),
-                [ExcerptRange {
-                    context: Point::new(3, 3)..Point::new(4, 4),
-                    primary: None,
-                }],
-                cx,
-            );
-            multibuffer.push_excerpts(
-                buffer_2.clone(),
-                [ExcerptRange {
-                    context: Point::new(3, 1)..Point::new(3, 3),
-                    primary: None,
-                }],
-                cx,
-            );
-            assert_eq!(
-                subscription.consume().into_inner(),
-                [Edit {
-                    old: 10..10,
-                    new: 10..22
-                }]
-            );
-
-            subscription
-        });
-
-        // Adding excerpts emits an edited event.
-        assert_eq!(
-            events.read().as_slice(),
-            &[
-                Event::Edited {
-                    sigleton_buffer_edited: false
-                },
-                Event::Edited {
-                    sigleton_buffer_edited: false
-                },
-                Event::Edited {
-                    sigleton_buffer_edited: false
-                }
-            ]
-        );
-
-        let snapshot = multibuffer.read(cx).snapshot(cx);
-        assert_eq!(
-            snapshot.text(),
-            concat!(
-                "bbbb\n",  // Preserve newlines
-                "ccccc\n", //
-                "ddd\n",   //
-                "eeee\n",  //
-                "jj"       //
-            )
-        );
-        assert_eq!(
-            snapshot.buffer_rows(0).collect::<Vec<_>>(),
-            [Some(1), Some(2), Some(3), Some(4), Some(3)]
-        );
-        assert_eq!(
-            snapshot.buffer_rows(2).collect::<Vec<_>>(),
-            [Some(3), Some(4), Some(3)]
-        );
-        assert_eq!(snapshot.buffer_rows(4).collect::<Vec<_>>(), [Some(3)]);
-        assert_eq!(snapshot.buffer_rows(5).collect::<Vec<_>>(), []);
-
-        assert_eq!(
-            boundaries_in_range(Point::new(0, 0)..Point::new(4, 2), &snapshot),
-            &[
-                (0, "bbbb\nccccc".to_string(), true),
-                (2, "ddd\neeee".to_string(), false),
-                (4, "jj".to_string(), true),
-            ]
-        );
-        assert_eq!(
-            boundaries_in_range(Point::new(0, 0)..Point::new(2, 0), &snapshot),
-            &[(0, "bbbb\nccccc".to_string(), true)]
-        );
-        assert_eq!(
-            boundaries_in_range(Point::new(1, 0)..Point::new(1, 5), &snapshot),
-            &[]
-        );
-        assert_eq!(
-            boundaries_in_range(Point::new(1, 0)..Point::new(2, 0), &snapshot),
-            &[]
-        );
-        assert_eq!(
-            boundaries_in_range(Point::new(1, 0)..Point::new(4, 0), &snapshot),
-            &[(2, "ddd\neeee".to_string(), false)]
-        );
-        assert_eq!(
-            boundaries_in_range(Point::new(1, 0)..Point::new(4, 0), &snapshot),
-            &[(2, "ddd\neeee".to_string(), false)]
-        );
-        assert_eq!(
-            boundaries_in_range(Point::new(2, 0)..Point::new(3, 0), &snapshot),
-            &[(2, "ddd\neeee".to_string(), false)]
-        );
-        assert_eq!(
-            boundaries_in_range(Point::new(4, 0)..Point::new(4, 2), &snapshot),
-            &[(4, "jj".to_string(), true)]
-        );
-        assert_eq!(
-            boundaries_in_range(Point::new(4, 2)..Point::new(4, 2), &snapshot),
-            &[]
-        );
-
-        buffer_1.update(cx, |buffer, cx| {
-            let text = "\n";
-            buffer.edit(
-                [
-                    (Point::new(0, 0)..Point::new(0, 0), text),
-                    (Point::new(2, 1)..Point::new(2, 3), text),
-                ],
-                None,
-                cx,
-            );
-        });
-
-        let snapshot = multibuffer.read(cx).snapshot(cx);
-        assert_eq!(
-            snapshot.text(),
-            concat!(
-                "bbbb\n", // Preserve newlines
-                "c\n",    //
-                "cc\n",   //
-                "ddd\n",  //
-                "eeee\n", //
-                "jj"      //
-            )
-        );
-
-        assert_eq!(
-            subscription.consume().into_inner(),
-            [Edit {
-                old: 6..8,
-                new: 6..7
-            }]
-        );
-
-        let snapshot = multibuffer.read(cx).snapshot(cx);
-        assert_eq!(
-            snapshot.clip_point(Point::new(0, 5), Bias::Left),
-            Point::new(0, 4)
-        );
-        assert_eq!(
-            snapshot.clip_point(Point::new(0, 5), Bias::Right),
-            Point::new(0, 4)
-        );
-        assert_eq!(
-            snapshot.clip_point(Point::new(5, 1), Bias::Right),
-            Point::new(5, 1)
-        );
-        assert_eq!(
-            snapshot.clip_point(Point::new(5, 2), Bias::Right),
-            Point::new(5, 2)
-        );
-        assert_eq!(
-            snapshot.clip_point(Point::new(5, 3), Bias::Right),
-            Point::new(5, 2)
-        );
-
-        let snapshot = multibuffer.update(cx, |multibuffer, cx| {
-            let (buffer_2_excerpt_id, _) =
-                multibuffer.excerpts_for_buffer(&buffer_2, cx)[0].clone();
-            multibuffer.remove_excerpts([buffer_2_excerpt_id], cx);
-            multibuffer.snapshot(cx)
-        });
-
-        assert_eq!(
-            snapshot.text(),
-            concat!(
-                "bbbb\n", // Preserve newlines
-                "c\n",    //
-                "cc\n",   //
-                "ddd\n",  //
-                "eeee",   //
-            )
-        );
-
-        fn boundaries_in_range(
-            range: Range<Point>,
-            snapshot: &MultiBufferSnapshot,
-        ) -> Vec<(u32, String, bool)> {
-            snapshot
-                .excerpt_boundaries_in_range(range)
-                .map(|boundary| {
-                    (
-                        boundary.row,
-                        boundary
-                            .buffer
-                            .text_for_range(boundary.range.context)
-                            .collect::<String>(),
-                        boundary.starts_new_buffer,
-                    )
-                })
-                .collect::<Vec<_>>()
-        }
-    }
-
-    #[gpui::test]
-    fn test_excerpt_events(cx: &mut AppContext) {
-        let buffer_1 =
-            cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(10, 3, 'a')));
-        let buffer_2 =
-            cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(10, 3, 'm')));
-
-        let leader_multibuffer = cx.new_model(|_| MultiBuffer::new(0));
-        let follower_multibuffer = cx.new_model(|_| MultiBuffer::new(0));
-        let follower_edit_event_count = Arc::new(RwLock::new(0));
-
-        follower_multibuffer.update(cx, |_, cx| {
-            let follower_edit_event_count = follower_edit_event_count.clone();
-            cx.subscribe(
-                &leader_multibuffer,
-                move |follower, _, event, cx| match event.clone() {
-                    Event::ExcerptsAdded {
-                        buffer,
-                        predecessor,
-                        excerpts,
-                    } => follower.insert_excerpts_with_ids_after(predecessor, buffer, excerpts, cx),
-                    Event::ExcerptsRemoved { ids } => follower.remove_excerpts(ids, cx),
-                    Event::Edited { .. } => {
-                        *follower_edit_event_count.write() += 1;
-                    }
-                    _ => {}
-                },
-            )
-            .detach();
-        });
-
-        leader_multibuffer.update(cx, |leader, cx| {
-            leader.push_excerpts(
-                buffer_1.clone(),
-                [
-                    ExcerptRange {
-                        context: 0..8,
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: 12..16,
-                        primary: None,
-                    },
-                ],
-                cx,
-            );
-            leader.insert_excerpts_after(
-                leader.excerpt_ids()[0],
-                buffer_2.clone(),
-                [
-                    ExcerptRange {
-                        context: 0..5,
-                        primary: None,
-                    },
-                    ExcerptRange {
-                        context: 10..15,
-                        primary: None,
-                    },
-                ],
-                cx,
-            )
-        });
-        assert_eq!(
-            leader_multibuffer.read(cx).snapshot(cx).text(),
-            follower_multibuffer.read(cx).snapshot(cx).text(),
-        );
-        assert_eq!(*follower_edit_event_count.read(), 2);
-
-        leader_multibuffer.update(cx, |leader, cx| {
-            let excerpt_ids = leader.excerpt_ids();
-            leader.remove_excerpts([excerpt_ids[1], excerpt_ids[3]], cx);
-        });
-        assert_eq!(
-            leader_multibuffer.read(cx).snapshot(cx).text(),
-            follower_multibuffer.read(cx).snapshot(cx).text(),
-        );
-        assert_eq!(*follower_edit_event_count.read(), 3);
-
-        // Removing an empty set of excerpts is a noop.
-        leader_multibuffer.update(cx, |leader, cx| {
-            leader.remove_excerpts([], cx);
-        });
-        assert_eq!(
-            leader_multibuffer.read(cx).snapshot(cx).text(),
-            follower_multibuffer.read(cx).snapshot(cx).text(),
-        );
-        assert_eq!(*follower_edit_event_count.read(), 3);
-
-        // Adding an empty set of excerpts is a noop.
-        leader_multibuffer.update(cx, |leader, cx| {
-            leader.push_excerpts::<usize>(buffer_2.clone(), [], cx);
-        });
-        assert_eq!(
-            leader_multibuffer.read(cx).snapshot(cx).text(),
-            follower_multibuffer.read(cx).snapshot(cx).text(),
-        );
-        assert_eq!(*follower_edit_event_count.read(), 3);
-
-        leader_multibuffer.update(cx, |leader, cx| {
-            leader.clear(cx);
-        });
-        assert_eq!(
-            leader_multibuffer.read(cx).snapshot(cx).text(),
-            follower_multibuffer.read(cx).snapshot(cx).text(),
-        );
-        assert_eq!(*follower_edit_event_count.read(), 4);
-    }
-
-    #[gpui::test]
-    fn test_push_excerpts_with_context_lines(cx: &mut AppContext) {
-        let buffer =
-            cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(20, 3, 'a')));
-        let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
-        let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| {
-            multibuffer.push_excerpts_with_context_lines(
-                buffer.clone(),
-                vec![
-                    Point::new(3, 2)..Point::new(4, 2),
-                    Point::new(7, 1)..Point::new(7, 3),
-                    Point::new(15, 0)..Point::new(15, 0),
-                ],
-                2,
-                cx,
-            )
-        });
-
-        let snapshot = multibuffer.read(cx).snapshot(cx);
-        assert_eq!(
-            snapshot.text(),
-            "bbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\n\nnnn\nooo\nppp\nqqq\nrrr\n"
-        );
-
-        assert_eq!(
-            anchor_ranges
-                .iter()
-                .map(|range| range.to_point(&snapshot))
-                .collect::<Vec<_>>(),
-            vec![
-                Point::new(2, 2)..Point::new(3, 2),
-                Point::new(6, 1)..Point::new(6, 3),
-                Point::new(12, 0)..Point::new(12, 0)
-            ]
-        );
-    }
-
-    #[gpui::test]
-    async fn test_stream_excerpts_with_context_lines(cx: &mut TestAppContext) {
-        let buffer =
-            cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(20, 3, 'a')));
-        let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
-        let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| {
-            let snapshot = buffer.read(cx);
-            let ranges = vec![
-                snapshot.anchor_before(Point::new(3, 2))..snapshot.anchor_before(Point::new(4, 2)),
-                snapshot.anchor_before(Point::new(7, 1))..snapshot.anchor_before(Point::new(7, 3)),
-                snapshot.anchor_before(Point::new(15, 0))
-                    ..snapshot.anchor_before(Point::new(15, 0)),
-            ];
-            multibuffer.stream_excerpts_with_context_lines(buffer.clone(), ranges, 2, cx)
-        });
-
-        let anchor_ranges = anchor_ranges.collect::<Vec<_>>().await;
-
-        let snapshot = multibuffer.update(cx, |multibuffer, cx| multibuffer.snapshot(cx));
-        assert_eq!(
-            snapshot.text(),
-            "bbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\n\nnnn\nooo\nppp\nqqq\nrrr\n"
-        );
-
-        assert_eq!(
-            anchor_ranges
-                .iter()
-                .map(|range| range.to_point(&snapshot))
-                .collect::<Vec<_>>(),
-            vec![
-                Point::new(2, 2)..Point::new(3, 2),
-                Point::new(6, 1)..Point::new(6, 3),
-                Point::new(12, 0)..Point::new(12, 0)
-            ]
-        );
-    }
-
-    #[gpui::test]
-    fn test_empty_multibuffer(cx: &mut AppContext) {
-        let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
-
-        let snapshot = multibuffer.read(cx).snapshot(cx);
-        assert_eq!(snapshot.text(), "");
-        assert_eq!(snapshot.buffer_rows(0).collect::<Vec<_>>(), &[Some(0)]);
-        assert_eq!(snapshot.buffer_rows(1).collect::<Vec<_>>(), &[]);
-    }
-
-    #[gpui::test]
-    fn test_singleton_multibuffer_anchors(cx: &mut AppContext) {
-        let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcd"));
-        let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
-        let old_snapshot = multibuffer.read(cx).snapshot(cx);
-        buffer.update(cx, |buffer, cx| {
-            buffer.edit([(0..0, "X")], None, cx);
-            buffer.edit([(5..5, "Y")], None, cx);
-        });
-        let new_snapshot = multibuffer.read(cx).snapshot(cx);
-
-        assert_eq!(old_snapshot.text(), "abcd");
-        assert_eq!(new_snapshot.text(), "XabcdY");
-
-        assert_eq!(old_snapshot.anchor_before(0).to_offset(&new_snapshot), 0);
-        assert_eq!(old_snapshot.anchor_after(0).to_offset(&new_snapshot), 1);
-        assert_eq!(old_snapshot.anchor_before(4).to_offset(&new_snapshot), 5);
-        assert_eq!(old_snapshot.anchor_after(4).to_offset(&new_snapshot), 6);
-    }
-
-    #[gpui::test]
-    fn test_multibuffer_anchors(cx: &mut AppContext) {
-        let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcd"));
-        let buffer_2 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "efghi"));
-        let multibuffer = cx.new_model(|cx| {
-            let mut multibuffer = MultiBuffer::new(0);
-            multibuffer.push_excerpts(
-                buffer_1.clone(),
-                [ExcerptRange {
-                    context: 0..4,
-                    primary: None,
-                }],
-                cx,
-            );
-            multibuffer.push_excerpts(
-                buffer_2.clone(),
-                [ExcerptRange {
-                    context: 0..5,
-                    primary: None,
-                }],
-                cx,
-            );
-            multibuffer
-        });
-        let old_snapshot = multibuffer.read(cx).snapshot(cx);
-
-        assert_eq!(old_snapshot.anchor_before(0).to_offset(&old_snapshot), 0);
-        assert_eq!(old_snapshot.anchor_after(0).to_offset(&old_snapshot), 0);
-        assert_eq!(Anchor::min().to_offset(&old_snapshot), 0);
-        assert_eq!(Anchor::min().to_offset(&old_snapshot), 0);
-        assert_eq!(Anchor::max().to_offset(&old_snapshot), 10);
-        assert_eq!(Anchor::max().to_offset(&old_snapshot), 10);
-
-        buffer_1.update(cx, |buffer, cx| {
-            buffer.edit([(0..0, "W")], None, cx);
-            buffer.edit([(5..5, "X")], None, cx);
-        });
-        buffer_2.update(cx, |buffer, cx| {
-            buffer.edit([(0..0, "Y")], None, cx);
-            buffer.edit([(6..6, "Z")], None, cx);
-        });
-        let new_snapshot = multibuffer.read(cx).snapshot(cx);
-
-        assert_eq!(old_snapshot.text(), "abcd\nefghi");
-        assert_eq!(new_snapshot.text(), "WabcdX\nYefghiZ");
-
-        assert_eq!(old_snapshot.anchor_before(0).to_offset(&new_snapshot), 0);
-        assert_eq!(old_snapshot.anchor_after(0).to_offset(&new_snapshot), 1);
-        assert_eq!(old_snapshot.anchor_before(1).to_offset(&new_snapshot), 2);
-        assert_eq!(old_snapshot.anchor_after(1).to_offset(&new_snapshot), 2);
-        assert_eq!(old_snapshot.anchor_before(2).to_offset(&new_snapshot), 3);
-        assert_eq!(old_snapshot.anchor_after(2).to_offset(&new_snapshot), 3);
-        assert_eq!(old_snapshot.anchor_before(5).to_offset(&new_snapshot), 7);
-        assert_eq!(old_snapshot.anchor_after(5).to_offset(&new_snapshot), 8);
-        assert_eq!(old_snapshot.anchor_before(10).to_offset(&new_snapshot), 13);
-        assert_eq!(old_snapshot.anchor_after(10).to_offset(&new_snapshot), 14);
-    }
-
-    #[gpui::test]
-    fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut AppContext) {
-        let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcd"));
-        let buffer_2 =
-            cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "ABCDEFGHIJKLMNOP"));
-        let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
-
-        // Create an insertion id in buffer 1 that doesn't exist in buffer 2.
-        // Add an excerpt from buffer 1 that spans this new insertion.
-        buffer_1.update(cx, |buffer, cx| buffer.edit([(4..4, "123")], None, cx));
-        let excerpt_id_1 = multibuffer.update(cx, |multibuffer, cx| {
-            multibuffer
-                .push_excerpts(
-                    buffer_1.clone(),
-                    [ExcerptRange {
-                        context: 0..7,
-                        primary: None,
-                    }],
-                    cx,
-                )
-                .pop()
-                .unwrap()
-        });
-
-        let snapshot_1 = multibuffer.read(cx).snapshot(cx);
-        assert_eq!(snapshot_1.text(), "abcd123");
-
-        // Replace the buffer 1 excerpt with new excerpts from buffer 2.
-        let (excerpt_id_2, excerpt_id_3) = multibuffer.update(cx, |multibuffer, cx| {
-            multibuffer.remove_excerpts([excerpt_id_1], cx);
-            let mut ids = multibuffer
-                .push_excerpts(
-                    buffer_2.clone(),
-                    [
-                        ExcerptRange {
-                            context: 0..4,
-                            primary: None,
-                        },
-                        ExcerptRange {
-                            context: 6..10,
-                            primary: None,
-                        },
-                        ExcerptRange {
-                            context: 12..16,
-                            primary: None,
-                        },
-                    ],
-                    cx,
-                )
-                .into_iter();
-            (ids.next().unwrap(), ids.next().unwrap())
-        });
-        let snapshot_2 = multibuffer.read(cx).snapshot(cx);
-        assert_eq!(snapshot_2.text(), "ABCD\nGHIJ\nMNOP");
-
-        // The old excerpt id doesn't get reused.
-        assert_ne!(excerpt_id_2, excerpt_id_1);
-
-        // Resolve some anchors from the previous snapshot in the new snapshot.
-        // The current excerpts are from a different buffer, so we don't attempt to
-        // resolve the old text anchor in the new buffer.
-        assert_eq!(
-            snapshot_2.summary_for_anchor::<usize>(&snapshot_1.anchor_before(2)),
-            0
-        );
-        assert_eq!(
-            snapshot_2.summaries_for_anchors::<usize, _>(&[
-                snapshot_1.anchor_before(2),
-                snapshot_1.anchor_after(3)
-            ]),
-            vec![0, 0]
-        );
-
-        // Refresh anchors from the old snapshot. The return value indicates that both
-        // anchors lost their original excerpt.
-        let refresh =
-            snapshot_2.refresh_anchors(&[snapshot_1.anchor_before(2), snapshot_1.anchor_after(3)]);
-        assert_eq!(
-            refresh,
-            &[
-                (0, snapshot_2.anchor_before(0), false),
-                (1, snapshot_2.anchor_after(0), false),
-            ]
-        );
-
-        // Replace the middle excerpt with a smaller excerpt in buffer 2,
-        // that intersects the old excerpt.
-        let excerpt_id_5 = multibuffer.update(cx, |multibuffer, cx| {
-            multibuffer.remove_excerpts([excerpt_id_3], cx);
-            multibuffer
-                .insert_excerpts_after(
-                    excerpt_id_2,
-                    buffer_2.clone(),
-                    [ExcerptRange {
-                        context: 5..8,
-                        primary: None,
-                    }],
-                    cx,
-                )
-                .pop()
-                .unwrap()
-        });
-
-        let snapshot_3 = multibuffer.read(cx).snapshot(cx);
-        assert_eq!(snapshot_3.text(), "ABCD\nFGH\nMNOP");
-        assert_ne!(excerpt_id_5, excerpt_id_3);
-
-        // Resolve some anchors from the previous snapshot in the new snapshot.
-        // The third anchor can't be resolved, since its excerpt has been removed,
-        // so it resolves to the same position as its predecessor.
-        let anchors = [
-            snapshot_2.anchor_before(0),
-            snapshot_2.anchor_after(2),
-            snapshot_2.anchor_after(6),
-            snapshot_2.anchor_after(14),
-        ];
-        assert_eq!(
-            snapshot_3.summaries_for_anchors::<usize, _>(&anchors),
-            &[0, 2, 9, 13]
-        );
-
-        let new_anchors = snapshot_3.refresh_anchors(&anchors);
-        assert_eq!(
-            new_anchors.iter().map(|a| (a.0, a.2)).collect::<Vec<_>>(),
-            &[(0, true), (1, true), (2, true), (3, true)]
-        );
-        assert_eq!(
-            snapshot_3.summaries_for_anchors::<usize, _>(new_anchors.iter().map(|a| &a.1)),
-            &[0, 2, 7, 13]
-        );
-    }
-
-    #[gpui::test(iterations = 100)]
-    fn test_random_multibuffer(cx: &mut AppContext, mut rng: StdRng) {
-        let operations = env::var("OPERATIONS")
-            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
-            .unwrap_or(10);
-
-        let mut buffers: Vec<Model<Buffer>> = Vec::new();
-        let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
-        let mut excerpt_ids = Vec::<ExcerptId>::new();
-        let mut expected_excerpts = Vec::<(Model<Buffer>, Range<text::Anchor>)>::new();
-        let mut anchors = Vec::new();
-        let mut old_versions = Vec::new();
-
-        for _ in 0..operations {
-            match rng.gen_range(0..100) {
-                0..=19 if !buffers.is_empty() => {
-                    let buffer = buffers.choose(&mut rng).unwrap();
-                    buffer.update(cx, |buf, cx| buf.randomly_edit(&mut rng, 5, cx));
-                }
-                20..=29 if !expected_excerpts.is_empty() => {
-                    let mut ids_to_remove = vec![];
-                    for _ in 0..rng.gen_range(1..=3) {
-                        if expected_excerpts.is_empty() {
-                            break;
-                        }
-
-                        let ix = rng.gen_range(0..expected_excerpts.len());
-                        ids_to_remove.push(excerpt_ids.remove(ix));
-                        let (buffer, range) = expected_excerpts.remove(ix);
-                        let buffer = buffer.read(cx);
-                        log::info!(
-                            "Removing excerpt {}: {:?}",
-                            ix,
-                            buffer
-                                .text_for_range(range.to_offset(buffer))
-                                .collect::<String>(),
-                        );
-                    }
-                    let snapshot = multibuffer.read(cx).read(cx);
-                    ids_to_remove.sort_unstable_by(|a, b| a.cmp(&b, &snapshot));
-                    drop(snapshot);
-                    multibuffer.update(cx, |multibuffer, cx| {
-                        multibuffer.remove_excerpts(ids_to_remove, cx)
-                    });
-                }
-                30..=39 if !expected_excerpts.is_empty() => {
-                    let multibuffer = multibuffer.read(cx).read(cx);
-                    let offset =
-                        multibuffer.clip_offset(rng.gen_range(0..=multibuffer.len()), Bias::Left);
-                    let bias = if rng.gen() { Bias::Left } else { Bias::Right };
-                    log::info!("Creating anchor at {} with bias {:?}", offset, bias);
-                    anchors.push(multibuffer.anchor_at(offset, bias));
-                    anchors.sort_by(|a, b| a.cmp(b, &multibuffer));
-                }
-                40..=44 if !anchors.is_empty() => {
-                    let multibuffer = multibuffer.read(cx).read(cx);
-                    let prev_len = anchors.len();
-                    anchors = multibuffer
-                        .refresh_anchors(&anchors)
-                        .into_iter()
-                        .map(|a| a.1)
-                        .collect();
-
-                    // Ensure the newly-refreshed anchors point to a valid excerpt and don't
-                    // overshoot its boundaries.
-                    assert_eq!(anchors.len(), prev_len);
-                    for anchor in &anchors {
-                        if anchor.excerpt_id == ExcerptId::min()
-                            || anchor.excerpt_id == ExcerptId::max()
-                        {
-                            continue;
-                        }
-
-                        let excerpt = multibuffer.excerpt(anchor.excerpt_id).unwrap();
-                        assert_eq!(excerpt.id, anchor.excerpt_id);
-                        assert!(excerpt.contains(anchor));
-                    }
-                }
-                _ => {
-                    let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) {
-                        let base_text = util::RandomCharIter::new(&mut rng)
-                            .take(10)
-                            .collect::<String>();
-                        buffers.push(
-                            cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), base_text)),
-                        );
-                        buffers.last().unwrap()
-                    } else {
-                        buffers.choose(&mut rng).unwrap()
-                    };
-
-                    let buffer = buffer_handle.read(cx);
-                    let end_ix = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right);
-                    let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
-                    let anchor_range = buffer.anchor_before(start_ix)..buffer.anchor_after(end_ix);
-                    let prev_excerpt_ix = rng.gen_range(0..=expected_excerpts.len());
-                    let prev_excerpt_id = excerpt_ids
-                        .get(prev_excerpt_ix)
-                        .cloned()
-                        .unwrap_or_else(ExcerptId::max);
-                    let excerpt_ix = (prev_excerpt_ix + 1).min(expected_excerpts.len());
-
-                    log::info!(
-                        "Inserting excerpt at {} of {} for buffer {}: {:?}[{:?}] = {:?}",
-                        excerpt_ix,
-                        expected_excerpts.len(),
-                        buffer_handle.read(cx).remote_id(),
-                        buffer.text(),
-                        start_ix..end_ix,
-                        &buffer.text()[start_ix..end_ix]
-                    );
-
-                    let excerpt_id = multibuffer.update(cx, |multibuffer, cx| {
-                        multibuffer
-                            .insert_excerpts_after(
-                                prev_excerpt_id,
-                                buffer_handle.clone(),
-                                [ExcerptRange {
-                                    context: start_ix..end_ix,
-                                    primary: None,
-                                }],
-                                cx,
-                            )
-                            .pop()
-                            .unwrap()
-                    });
-
-                    excerpt_ids.insert(excerpt_ix, excerpt_id);
-                    expected_excerpts.insert(excerpt_ix, (buffer_handle.clone(), anchor_range));
-                }
-            }
-
-            if rng.gen_bool(0.3) {
-                multibuffer.update(cx, |multibuffer, cx| {
-                    old_versions.push((multibuffer.snapshot(cx), multibuffer.subscribe()));
-                })
-            }
-
-            let snapshot = multibuffer.read(cx).snapshot(cx);
-
-            let mut excerpt_starts = Vec::new();
-            let mut expected_text = String::new();
-            let mut expected_buffer_rows = Vec::new();
-            for (buffer, range) in &expected_excerpts {
-                let buffer = buffer.read(cx);
-                let buffer_range = range.to_offset(buffer);
-
-                excerpt_starts.push(TextSummary::from(expected_text.as_str()));
-                expected_text.extend(buffer.text_for_range(buffer_range.clone()));
-                expected_text.push('\n');
-
-                let buffer_row_range = buffer.offset_to_point(buffer_range.start).row
-                    ..=buffer.offset_to_point(buffer_range.end).row;
-                for row in buffer_row_range {
-                    expected_buffer_rows.push(Some(row));
-                }
-            }
-            // Remove final trailing newline.
-            if !expected_excerpts.is_empty() {
-                expected_text.pop();
-            }
-
-            // Always report one buffer row
-            if expected_buffer_rows.is_empty() {
-                expected_buffer_rows.push(Some(0));
-            }
-
-            assert_eq!(snapshot.text(), expected_text);
-            log::info!("MultiBuffer text: {:?}", expected_text);
-
-            assert_eq!(
-                snapshot.buffer_rows(0).collect::<Vec<_>>(),
-                expected_buffer_rows,
-            );
-
-            for _ in 0..5 {
-                let start_row = rng.gen_range(0..=expected_buffer_rows.len());
-                assert_eq!(
-                    snapshot.buffer_rows(start_row as u32).collect::<Vec<_>>(),
-                    &expected_buffer_rows[start_row..],
-                    "buffer_rows({})",
-                    start_row
-                );
-            }
-
-            assert_eq!(
-                snapshot.max_buffer_row(),
-                expected_buffer_rows.into_iter().flatten().max().unwrap()
-            );
-
-            let mut excerpt_starts = excerpt_starts.into_iter();
-            for (buffer, range) in &expected_excerpts {
-                let buffer = buffer.read(cx);
-                let buffer_id = buffer.remote_id();
-                let buffer_range = range.to_offset(buffer);
-                let buffer_start_point = buffer.offset_to_point(buffer_range.start);
-                let buffer_start_point_utf16 =
-                    buffer.text_summary_for_range::<PointUtf16, _>(0..buffer_range.start);
-
-                let excerpt_start = excerpt_starts.next().unwrap();
-                let mut offset = excerpt_start.len;
-                let mut buffer_offset = buffer_range.start;
-                let mut point = excerpt_start.lines;
-                let mut buffer_point = buffer_start_point;
-                let mut point_utf16 = excerpt_start.lines_utf16();
-                let mut buffer_point_utf16 = buffer_start_point_utf16;
-                for ch in buffer
-                    .snapshot()
-                    .chunks(buffer_range.clone(), false)
-                    .flat_map(|c| c.text.chars())
-                {
-                    for _ in 0..ch.len_utf8() {
-                        let left_offset = snapshot.clip_offset(offset, Bias::Left);
-                        let right_offset = snapshot.clip_offset(offset, Bias::Right);
-                        let buffer_left_offset = buffer.clip_offset(buffer_offset, Bias::Left);
-                        let buffer_right_offset = buffer.clip_offset(buffer_offset, Bias::Right);
-                        assert_eq!(
-                            left_offset,
-                            excerpt_start.len + (buffer_left_offset - buffer_range.start),
-                            "clip_offset({:?}, Left). buffer: {:?}, buffer offset: {:?}",
-                            offset,
-                            buffer_id,
-                            buffer_offset,
-                        );
-                        assert_eq!(
-                            right_offset,
-                            excerpt_start.len + (buffer_right_offset - buffer_range.start),
-                            "clip_offset({:?}, Right). buffer: {:?}, buffer offset: {:?}",
-                            offset,
-                            buffer_id,
-                            buffer_offset,
-                        );
-
-                        let left_point = snapshot.clip_point(point, Bias::Left);
-                        let right_point = snapshot.clip_point(point, Bias::Right);
-                        let buffer_left_point = buffer.clip_point(buffer_point, Bias::Left);
-                        let buffer_right_point = buffer.clip_point(buffer_point, Bias::Right);
-                        assert_eq!(
-                            left_point,
-                            excerpt_start.lines + (buffer_left_point - buffer_start_point),
-                            "clip_point({:?}, Left). buffer: {:?}, buffer point: {:?}",
-                            point,
-                            buffer_id,
-                            buffer_point,
-                        );
-                        assert_eq!(
-                            right_point,
-                            excerpt_start.lines + (buffer_right_point - buffer_start_point),
-                            "clip_point({:?}, Right). buffer: {:?}, buffer point: {:?}",
-                            point,
-                            buffer_id,
-                            buffer_point,
-                        );
-
-                        assert_eq!(
-                            snapshot.point_to_offset(left_point),
-                            left_offset,
-                            "point_to_offset({:?})",
-                            left_point,
-                        );
-                        assert_eq!(
-                            snapshot.offset_to_point(left_offset),
-                            left_point,
-                            "offset_to_point({:?})",
-                            left_offset,
-                        );
-
-                        offset += 1;
-                        buffer_offset += 1;
-                        if ch == '\n' {
-                            point += Point::new(1, 0);
-                            buffer_point += Point::new(1, 0);
-                        } else {
-                            point += Point::new(0, 1);
-                            buffer_point += Point::new(0, 1);
-                        }
-                    }
-
-                    for _ in 0..ch.len_utf16() {
-                        let left_point_utf16 =
-                            snapshot.clip_point_utf16(Unclipped(point_utf16), Bias::Left);
-                        let right_point_utf16 =
-                            snapshot.clip_point_utf16(Unclipped(point_utf16), Bias::Right);
-                        let buffer_left_point_utf16 =
-                            buffer.clip_point_utf16(Unclipped(buffer_point_utf16), Bias::Left);
-                        let buffer_right_point_utf16 =
-                            buffer.clip_point_utf16(Unclipped(buffer_point_utf16), Bias::Right);
-                        assert_eq!(
-                            left_point_utf16,
-                            excerpt_start.lines_utf16()
-                                + (buffer_left_point_utf16 - buffer_start_point_utf16),
-                            "clip_point_utf16({:?}, Left). buffer: {:?}, buffer point_utf16: {:?}",
-                            point_utf16,
-                            buffer_id,
-                            buffer_point_utf16,
-                        );
-                        assert_eq!(
-                            right_point_utf16,
-                            excerpt_start.lines_utf16()
-                                + (buffer_right_point_utf16 - buffer_start_point_utf16),
-                            "clip_point_utf16({:?}, Right). buffer: {:?}, buffer point_utf16: {:?}",
-                            point_utf16,
-                            buffer_id,
-                            buffer_point_utf16,
-                        );
-
-                        if ch == '\n' {
-                            point_utf16 += PointUtf16::new(1, 0);
-                            buffer_point_utf16 += PointUtf16::new(1, 0);
-                        } else {
-                            point_utf16 += PointUtf16::new(0, 1);
-                            buffer_point_utf16 += PointUtf16::new(0, 1);
-                        }
-                    }
-                }
-            }
-
-            for (row, line) in expected_text.split('\n').enumerate() {
-                assert_eq!(
-                    snapshot.line_len(row as u32),
-                    line.len() as u32,
-                    "line_len({}).",
-                    row
-                );
-            }
-
-            let text_rope = Rope::from(expected_text.as_str());
-            for _ in 0..10 {
-                let end_ix = text_rope.clip_offset(rng.gen_range(0..=text_rope.len()), Bias::Right);
-                let start_ix = text_rope.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
-
-                let text_for_range = snapshot
-                    .text_for_range(start_ix..end_ix)
-                    .collect::<String>();
-                assert_eq!(
-                    text_for_range,
-                    &expected_text[start_ix..end_ix],
-                    "incorrect text for range {:?}",
-                    start_ix..end_ix
-                );
-
-                let excerpted_buffer_ranges = multibuffer
-                    .read(cx)
-                    .range_to_buffer_ranges(start_ix..end_ix, cx);
-                let excerpted_buffers_text = excerpted_buffer_ranges
-                    .iter()
-                    .map(|(buffer, buffer_range, _)| {
-                        buffer
-                            .read(cx)
-                            .text_for_range(buffer_range.clone())
-                            .collect::<String>()
-                    })
-                    .collect::<Vec<_>>()
-                    .join("\n");
-                assert_eq!(excerpted_buffers_text, text_for_range);
-                if !expected_excerpts.is_empty() {
-                    assert!(!excerpted_buffer_ranges.is_empty());
-                }
-
-                let expected_summary = TextSummary::from(&expected_text[start_ix..end_ix]);
-                assert_eq!(
-                    snapshot.text_summary_for_range::<TextSummary, _>(start_ix..end_ix),
-                    expected_summary,
-                    "incorrect summary for range {:?}",
-                    start_ix..end_ix
-                );
-            }
-
-            // Anchor resolution
-            let summaries = snapshot.summaries_for_anchors::<usize, _>(&anchors);
-            assert_eq!(anchors.len(), summaries.len());
-            for (anchor, resolved_offset) in anchors.iter().zip(summaries) {
-                assert!(resolved_offset <= snapshot.len());
-                assert_eq!(
-                    snapshot.summary_for_anchor::<usize>(anchor),
-                    resolved_offset
-                );
-            }
-
-            for _ in 0..10 {
-                let end_ix = text_rope.clip_offset(rng.gen_range(0..=text_rope.len()), Bias::Right);
-                assert_eq!(
-                    snapshot.reversed_chars_at(end_ix).collect::<String>(),
-                    expected_text[..end_ix].chars().rev().collect::<String>(),
-                );
-            }
-
-            for _ in 0..10 {
-                let end_ix = rng.gen_range(0..=text_rope.len());
-                let start_ix = rng.gen_range(0..=end_ix);
-                assert_eq!(
-                    snapshot
-                        .bytes_in_range(start_ix..end_ix)
-                        .flatten()
-                        .copied()
-                        .collect::<Vec<_>>(),
-                    expected_text.as_bytes()[start_ix..end_ix].to_vec(),
-                    "bytes_in_range({:?})",
-                    start_ix..end_ix,
-                );
-            }
-        }
-
-        let snapshot = multibuffer.read(cx).snapshot(cx);
-        for (old_snapshot, subscription) in old_versions {
-            let edits = subscription.consume().into_inner();
-
-            log::info!(
-                "applying subscription edits to old text: {:?}: {:?}",
-                old_snapshot.text(),
-                edits,
-            );
-
-            let mut text = old_snapshot.text();
-            for edit in edits {
-                let new_text: String = snapshot.text_for_range(edit.new.clone()).collect();
-                text.replace_range(edit.new.start..edit.new.start + edit.old.len(), &new_text);
-            }
-            assert_eq!(text.to_string(), snapshot.text());
-        }
-    }
-
-    #[gpui::test]
-    fn test_history(cx: &mut AppContext) {
-        let test_settings = SettingsStore::test(cx);
-        cx.set_global(test_settings);
-
-        let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "1234"));
-        let buffer_2 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "5678"));
-        let multibuffer = cx.new_model(|_| MultiBuffer::new(0));
-        let group_interval = multibuffer.read(cx).history.group_interval;
-        multibuffer.update(cx, |multibuffer, cx| {
-            multibuffer.push_excerpts(
-                buffer_1.clone(),
-                [ExcerptRange {
-                    context: 0..buffer_1.read(cx).len(),
-                    primary: None,
-                }],
-                cx,
-            );
-            multibuffer.push_excerpts(
-                buffer_2.clone(),
-                [ExcerptRange {
-                    context: 0..buffer_2.read(cx).len(),
-                    primary: None,
-                }],
-                cx,
-            );
-        });
-
-        let mut now = Instant::now();
-
-        multibuffer.update(cx, |multibuffer, cx| {
-            let transaction_1 = multibuffer.start_transaction_at(now, cx).unwrap();
-            multibuffer.edit(
-                [
-                    (Point::new(0, 0)..Point::new(0, 0), "A"),
-                    (Point::new(1, 0)..Point::new(1, 0), "A"),
-                ],
-                None,
-                cx,
-            );
-            multibuffer.edit(
-                [
-                    (Point::new(0, 1)..Point::new(0, 1), "B"),
-                    (Point::new(1, 1)..Point::new(1, 1), "B"),
-                ],
-                None,
-                cx,
-            );
-            multibuffer.end_transaction_at(now, cx);
-            assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
-
-            // Edit buffer 1 through the multibuffer
-            now += 2 * group_interval;
-            multibuffer.start_transaction_at(now, cx);
-            multibuffer.edit([(2..2, "C")], None, cx);
-            multibuffer.end_transaction_at(now, cx);
-            assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678");
-
-            // Edit buffer 1 independently
-            buffer_1.update(cx, |buffer_1, cx| {
-                buffer_1.start_transaction_at(now);
-                buffer_1.edit([(3..3, "D")], None, cx);
-                buffer_1.end_transaction_at(now, cx);
-
-                now += 2 * group_interval;
-                buffer_1.start_transaction_at(now);
-                buffer_1.edit([(4..4, "E")], None, cx);
-                buffer_1.end_transaction_at(now, cx);
-            });
-            assert_eq!(multibuffer.read(cx).text(), "ABCDE1234\nAB5678");
-
-            // An undo in the multibuffer undoes the multibuffer transaction
-            // and also any individual buffer edits that have occurred since
-            // that transaction.
-            multibuffer.undo(cx);
-            assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
-
-            multibuffer.undo(cx);
-            assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
-
-            multibuffer.redo(cx);
-            assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
-
-            multibuffer.redo(cx);
-            assert_eq!(multibuffer.read(cx).text(), "ABCDE1234\nAB5678");
-
-            // Undo buffer 2 independently.
-            buffer_2.update(cx, |buffer_2, cx| buffer_2.undo(cx));
-            assert_eq!(multibuffer.read(cx).text(), "ABCDE1234\n5678");
-
-            // An undo in the multibuffer undoes the components of the
-            // the last multibuffer transaction that are not already undone.
-            multibuffer.undo(cx);
-            assert_eq!(multibuffer.read(cx).text(), "AB1234\n5678");
-
-            multibuffer.undo(cx);
-            assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
-
-            multibuffer.redo(cx);
-            assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
-
-            buffer_1.update(cx, |buffer_1, cx| buffer_1.redo(cx));
-            assert_eq!(multibuffer.read(cx).text(), "ABCD1234\nAB5678");
-
-            // Redo stack gets cleared after an edit.
-            now += 2 * group_interval;
-            multibuffer.start_transaction_at(now, cx);
-            multibuffer.edit([(0..0, "X")], None, cx);
-            multibuffer.end_transaction_at(now, cx);
-            assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678");
-            multibuffer.redo(cx);
-            assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678");
-            multibuffer.undo(cx);
-            assert_eq!(multibuffer.read(cx).text(), "ABCD1234\nAB5678");
-            multibuffer.undo(cx);
-            assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
-
-            // Transactions can be grouped manually.
-            multibuffer.redo(cx);
-            multibuffer.redo(cx);
-            assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678");
-            multibuffer.group_until_transaction(transaction_1, cx);
-            multibuffer.undo(cx);
-            assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
-            multibuffer.redo(cx);
-            assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678");
-        });
-    }
-}

crates/outline/Cargo.toml 🔗

@@ -10,14 +10,16 @@ doctest = false
 
 [dependencies]
 editor = { path = "../editor" }
-fuzzy = { path = "../fuzzy" }
-gpui = { path = "../gpui" }
-language = { path = "../language" }
+fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
+gpui = { package = "gpui2", path = "../gpui2" }
+ui = { package = "ui2", path = "../ui2" }
+language = { package = "language2", path = "../language2" }
 picker = { path = "../picker" }
-settings = { path = "../settings" }
-text = { path = "../text" }
-theme = { path = "../theme" }
-workspace = { path = "../workspace" }
+settings = { package = "settings2", path = "../settings2" }
+text = { package = "text2", path = "../text2" }
+theme = { package = "theme2", path = "../theme2" }
+workspace = { package = "workspace2", path = "../workspace2" }
+util = { path = "../util" }
 
 ordered-float.workspace = true
 postage.workspace = true

crates/outline/src/outline.rs 🔗

@@ -1,68 +1,109 @@
 use editor::{
-    combine_syntax_and_fuzzy_match_highlights, display_map::ToDisplayPoint,
-    scroll::autoscroll::Autoscroll, Anchor, AnchorRangeExt, DisplayPoint, Editor, ToPoint,
+    display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Anchor, AnchorRangeExt,
+    DisplayPoint, Editor, EditorMode, ToPoint,
 };
 use fuzzy::StringMatch;
 use gpui::{
-    actions, elements::*, geometry::vector::Vector2F, AppContext, MouseState, Task, ViewContext,
-    ViewHandle, WindowContext,
+    actions, div, rems, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView,
+    FontStyle, FontWeight, HighlightStyle, ParentElement, Point, Render, Styled, StyledText, Task,
+    TextStyle, View, ViewContext, VisualContext, WeakView, WhiteSpace, WindowContext,
 };
 use language::Outline;
 use ordered_float::OrderedFloat;
-use picker::{Picker, PickerDelegate, PickerEvent};
+use picker::{Picker, PickerDelegate};
+use settings::Settings;
 use std::{
     cmp::{self, Reverse},
     sync::Arc,
 };
-use workspace::Workspace;
+
+use theme::{color_alpha, ActiveTheme, ThemeSettings};
+use ui::{prelude::*, ListItem, ListItemSpacing};
+use util::ResultExt;
+use workspace::ModalView;
 
 actions!(outline, [Toggle]);
 
 pub fn init(cx: &mut AppContext) {
-    cx.add_action(toggle);
-    OutlineView::init(cx);
+    cx.observe_new_views(OutlineView::register).detach();
 }
 
-pub fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
-    if let Some(editor) = workspace
-        .active_item(cx)
-        .and_then(|item| item.downcast::<Editor>())
-    {
-        let outline = editor
-            .read(cx)
-            .buffer()
-            .read(cx)
-            .snapshot(cx)
-            .outline(Some(theme::current(cx).editor.syntax.as_ref()));
-        if let Some(outline) = outline {
-            workspace.toggle_modal(cx, |_, cx| {
-                cx.add_view(|cx| {
-                    OutlineView::new(OutlineViewDelegate::new(outline, editor, cx), cx)
-                        .with_max_size(800., 1200.)
-                })
+pub fn toggle(editor: View<Editor>, _: &Toggle, cx: &mut WindowContext) {
+    let outline = editor
+        .read(cx)
+        .buffer()
+        .read(cx)
+        .snapshot(cx)
+        .outline(Some(&cx.theme().syntax()));
+
+    if let Some((workspace, outline)) = editor.read(cx).workspace().zip(outline) {
+        workspace.update(cx, |workspace, cx| {
+            workspace.toggle_modal(cx, |cx| OutlineView::new(outline, editor, cx));
+        })
+    }
+}
+
+pub struct OutlineView {
+    picker: View<Picker<OutlineViewDelegate>>,
+}
+
+impl FocusableView for OutlineView {
+    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+        self.picker.focus_handle(cx)
+    }
+}
+
+impl EventEmitter<DismissEvent> for OutlineView {}
+impl ModalView for OutlineView {}
+
+impl Render for OutlineView {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+        v_stack().w(rems(34.)).child(self.picker.clone())
+    }
+}
+
+impl OutlineView {
+    fn register(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
+        if editor.mode() == EditorMode::Full {
+            let handle = cx.view().downgrade();
+            editor.register_action(move |action, cx| {
+                if let Some(editor) = handle.upgrade() {
+                    toggle(editor, action, cx);
+                }
             });
         }
     }
-}
 
-type OutlineView = Picker<OutlineViewDelegate>;
+    fn new(
+        outline: Outline<Anchor>,
+        editor: View<Editor>,
+        cx: &mut ViewContext<Self>,
+    ) -> OutlineView {
+        let delegate = OutlineViewDelegate::new(cx.view().downgrade(), outline, editor, cx);
+        let picker = cx.new_view(|cx| Picker::new(delegate, cx).max_height(vh(0.75, cx)));
+        OutlineView { picker }
+    }
+}
 
 struct OutlineViewDelegate {
-    active_editor: ViewHandle<Editor>,
+    outline_view: WeakView<OutlineView>,
+    active_editor: View<Editor>,
     outline: Outline<Anchor>,
     selected_match_index: usize,
-    prev_scroll_position: Option<Vector2F>,
+    prev_scroll_position: Option<Point<f32>>,
     matches: Vec<StringMatch>,
     last_query: String,
 }
 
 impl OutlineViewDelegate {
     fn new(
+        outline_view: WeakView<OutlineView>,
         outline: Outline<Anchor>,
-        editor: ViewHandle<Editor>,
+        editor: View<Editor>,
         cx: &mut ViewContext<OutlineView>,
     ) -> Self {
         Self {
+            outline_view,
             last_query: Default::default(),
             matches: Default::default(),
             selected_match_index: 0,
@@ -81,11 +122,18 @@ impl OutlineViewDelegate {
         })
     }
 
-    fn set_selected_index(&mut self, ix: usize, navigate: bool, cx: &mut ViewContext<OutlineView>) {
+    fn set_selected_index(
+        &mut self,
+        ix: usize,
+        navigate: bool,
+        cx: &mut ViewContext<Picker<OutlineViewDelegate>>,
+    ) {
         self.selected_match_index = ix;
+
         if navigate && !self.matches.is_empty() {
             let selected_match = &self.matches[self.selected_match_index];
             let outline_item = &self.outline.items[selected_match.candidate_id];
+
             self.active_editor.update(cx, |active_editor, cx| {
                 let snapshot = active_editor.snapshot(cx).display_snapshot;
                 let buffer_snapshot = &snapshot.buffer_snapshot;
@@ -101,6 +149,8 @@ impl OutlineViewDelegate {
 }
 
 impl PickerDelegate for OutlineViewDelegate {
+    type ListItem = ListItem;
+
     fn placeholder_text(&self) -> Arc<str> {
         "Search buffer symbols...".into()
     }
@@ -113,15 +163,15 @@ impl PickerDelegate for OutlineViewDelegate {
         self.selected_match_index
     }
 
-    fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<OutlineView>) {
+    fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<OutlineViewDelegate>>) {
         self.set_selected_index(ix, true, cx);
     }
 
-    fn center_selection_after_match_updates(&self) -> bool {
-        true
-    }
-
-    fn update_matches(&mut self, query: String, cx: &mut ViewContext<OutlineView>) -> Task<()> {
+    fn update_matches(
+        &mut self,
+        query: String,
+        cx: &mut ViewContext<Picker<OutlineViewDelegate>>,
+    ) -> Task<()> {
         let selected_index;
         if query.is_empty() {
             self.restore_active_editor(cx);
@@ -163,7 +213,10 @@ impl PickerDelegate for OutlineViewDelegate {
                 .map(|(ix, _, _)| ix)
                 .unwrap_or(0);
         } else {
-            self.matches = smol::block_on(self.outline.search(&query, cx.background().clone()));
+            self.matches = smol::block_on(
+                self.outline
+                    .search(&query, cx.background_executor().clone()),
+            );
             selected_index = self
                 .matches
                 .iter()
@@ -177,8 +230,9 @@ impl PickerDelegate for OutlineViewDelegate {
         Task::ready(())
     }
 
-    fn confirm(&mut self, _: bool, cx: &mut ViewContext<OutlineView>) {
+    fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<OutlineViewDelegate>>) {
         self.prev_scroll_position.take();
+
         self.active_editor.update(cx, |active_editor, cx| {
             if let Some(rows) = active_editor.highlighted_rows() {
                 let snapshot = active_editor.snapshot(cx).display_snapshot;
@@ -187,39 +241,69 @@ impl PickerDelegate for OutlineViewDelegate {
                     s.select_ranges([position..position])
                 });
                 active_editor.highlight_rows(None);
+                active_editor.focus(cx);
             }
         });
-        cx.emit(PickerEvent::Dismiss);
+
+        self.dismissed(cx);
     }
 
-    fn dismissed(&mut self, cx: &mut ViewContext<OutlineView>) {
+    fn dismissed(&mut self, cx: &mut ViewContext<Picker<OutlineViewDelegate>>) {
+        self.outline_view
+            .update(cx, |_, cx| cx.emit(DismissEvent))
+            .log_err();
         self.restore_active_editor(cx);
     }
 
     fn render_match(
         &self,
         ix: usize,
-        mouse_state: &mut MouseState,
         selected: bool,
-        cx: &AppContext,
-    ) -> AnyElement<Picker<Self>> {
-        let theme = theme::current(cx);
-        let style = theme.picker.item.in_state(selected).style_for(mouse_state);
-        let string_match = &self.matches[ix];
-        let outline_item = &self.outline.items[string_match.candidate_id];
-
-        Text::new(outline_item.text.clone(), style.label.text.clone())
-            .with_soft_wrap(false)
-            .with_highlights(combine_syntax_and_fuzzy_match_highlights(
-                &outline_item.text,
-                style.label.text.clone().into(),
-                outline_item.highlight_ranges.iter().cloned(),
-                &string_match.positions,
-            ))
-            .contained()
-            .with_padding_left(20. * outline_item.depth as f32)
-            .contained()
-            .with_style(style.container)
-            .into_any()
+        cx: &mut ViewContext<Picker<Self>>,
+    ) -> Option<Self::ListItem> {
+        let settings = ThemeSettings::get_global(cx);
+
+        // TODO: We probably shouldn't need to build a whole new text style here
+        // but I'm not sure how to get the current one and modify it.
+        // Before this change TextStyle::default() was used here, which was giving us the wrong font and text color.
+        let text_style = TextStyle {
+            color: cx.theme().colors().text,
+            font_family: settings.buffer_font.family.clone(),
+            font_features: settings.buffer_font.features,
+            font_size: settings.buffer_font_size(cx).into(),
+            font_weight: FontWeight::NORMAL,
+            font_style: FontStyle::Normal,
+            line_height: relative(1.).into(),
+            background_color: None,
+            underline: None,
+            white_space: WhiteSpace::Normal,
+        };
+
+        let mut highlight_style = HighlightStyle::default();
+        highlight_style.background_color = Some(color_alpha(cx.theme().colors().text_accent, 0.3));
+
+        let mat = &self.matches[ix];
+        let outline_item = &self.outline.items[mat.candidate_id];
+
+        let highlights = gpui::combine_highlights(
+            mat.ranges().map(|range| (range, highlight_style)),
+            outline_item.highlight_ranges.iter().cloned(),
+        );
+
+        let styled_text =
+            StyledText::new(outline_item.text.clone()).with_highlights(&text_style, highlights);
+
+        Some(
+            ListItem::new(ix)
+                .inset(true)
+                .spacing(ListItemSpacing::Sparse)
+                .selected(selected)
+                .child(
+                    div()
+                        .text_ui()
+                        .pl(rems(outline_item.depth as f32))
+                        .child(styled_text),
+                ),
+        )
     }
 }

crates/outline2/Cargo.toml 🔗

@@ -1,29 +0,0 @@
-[package]
-name = "outline2"
-version = "0.1.0"
-edition = "2021"
-publish = false
-
-[lib]
-path = "src/outline.rs"
-doctest = false
-
-[dependencies]
-editor = { path = "../editor" }
-fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
-gpui = { package = "gpui2", path = "../gpui2" }
-ui = { package = "ui2", path = "../ui2" }
-language = { package = "language2", path = "../language2" }
-picker = { path = "../picker" }
-settings = { package = "settings2", path = "../settings2" }
-text = { package = "text2", path = "../text2" }
-theme = { package = "theme2", path = "../theme2" }
-workspace = { package = "workspace2", path = "../workspace2" }
-util = { path = "../util" }
-
-ordered-float.workspace = true
-postage.workspace = true
-smol.workspace = true
-
-[dev-dependencies]
-editor = { path = "../editor", features = ["test-support"] }

crates/outline2/src/outline.rs 🔗

@@ -1,309 +0,0 @@
-use editor::{
-    display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Anchor, AnchorRangeExt,
-    DisplayPoint, Editor, EditorMode, ToPoint,
-};
-use fuzzy::StringMatch;
-use gpui::{
-    actions, div, rems, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView,
-    FontStyle, FontWeight, HighlightStyle, ParentElement, Point, Render, Styled, StyledText, Task,
-    TextStyle, View, ViewContext, VisualContext, WeakView, WhiteSpace, WindowContext,
-};
-use language::Outline;
-use ordered_float::OrderedFloat;
-use picker::{Picker, PickerDelegate};
-use settings::Settings;
-use std::{
-    cmp::{self, Reverse},
-    sync::Arc,
-};
-
-use theme::{color_alpha, ActiveTheme, ThemeSettings};
-use ui::{prelude::*, ListItem, ListItemSpacing};
-use util::ResultExt;
-use workspace::ModalView;
-
-actions!(outline, [Toggle]);
-
-pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(OutlineView::register).detach();
-}
-
-pub fn toggle(editor: View<Editor>, _: &Toggle, cx: &mut WindowContext) {
-    let outline = editor
-        .read(cx)
-        .buffer()
-        .read(cx)
-        .snapshot(cx)
-        .outline(Some(&cx.theme().syntax()));
-
-    if let Some((workspace, outline)) = editor.read(cx).workspace().zip(outline) {
-        workspace.update(cx, |workspace, cx| {
-            workspace.toggle_modal(cx, |cx| OutlineView::new(outline, editor, cx));
-        })
-    }
-}
-
-pub struct OutlineView {
-    picker: View<Picker<OutlineViewDelegate>>,
-}
-
-impl FocusableView for OutlineView {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
-        self.picker.focus_handle(cx)
-    }
-}
-
-impl EventEmitter<DismissEvent> for OutlineView {}
-impl ModalView for OutlineView {}
-
-impl Render for OutlineView {
-    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
-        v_stack().w(rems(34.)).child(self.picker.clone())
-    }
-}
-
-impl OutlineView {
-    fn register(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
-        if editor.mode() == EditorMode::Full {
-            let handle = cx.view().downgrade();
-            editor.register_action(move |action, cx| {
-                if let Some(editor) = handle.upgrade() {
-                    toggle(editor, action, cx);
-                }
-            });
-        }
-    }
-
-    fn new(
-        outline: Outline<Anchor>,
-        editor: View<Editor>,
-        cx: &mut ViewContext<Self>,
-    ) -> OutlineView {
-        let delegate = OutlineViewDelegate::new(cx.view().downgrade(), outline, editor, cx);
-        let picker = cx.new_view(|cx| Picker::new(delegate, cx).max_height(vh(0.75, cx)));
-        OutlineView { picker }
-    }
-}
-
-struct OutlineViewDelegate {
-    outline_view: WeakView<OutlineView>,
-    active_editor: View<Editor>,
-    outline: Outline<Anchor>,
-    selected_match_index: usize,
-    prev_scroll_position: Option<Point<f32>>,
-    matches: Vec<StringMatch>,
-    last_query: String,
-}
-
-impl OutlineViewDelegate {
-    fn new(
-        outline_view: WeakView<OutlineView>,
-        outline: Outline<Anchor>,
-        editor: View<Editor>,
-        cx: &mut ViewContext<OutlineView>,
-    ) -> Self {
-        Self {
-            outline_view,
-            last_query: Default::default(),
-            matches: Default::default(),
-            selected_match_index: 0,
-            prev_scroll_position: Some(editor.update(cx, |editor, cx| editor.scroll_position(cx))),
-            active_editor: editor,
-            outline,
-        }
-    }
-
-    fn restore_active_editor(&mut self, cx: &mut WindowContext) {
-        self.active_editor.update(cx, |editor, cx| {
-            editor.highlight_rows(None);
-            if let Some(scroll_position) = self.prev_scroll_position {
-                editor.set_scroll_position(scroll_position, cx);
-            }
-        })
-    }
-
-    fn set_selected_index(
-        &mut self,
-        ix: usize,
-        navigate: bool,
-        cx: &mut ViewContext<Picker<OutlineViewDelegate>>,
-    ) {
-        self.selected_match_index = ix;
-
-        if navigate && !self.matches.is_empty() {
-            let selected_match = &self.matches[self.selected_match_index];
-            let outline_item = &self.outline.items[selected_match.candidate_id];
-
-            self.active_editor.update(cx, |active_editor, cx| {
-                let snapshot = active_editor.snapshot(cx).display_snapshot;
-                let buffer_snapshot = &snapshot.buffer_snapshot;
-                let start = outline_item.range.start.to_point(buffer_snapshot);
-                let end = outline_item.range.end.to_point(buffer_snapshot);
-                let display_rows = start.to_display_point(&snapshot).row()
-                    ..end.to_display_point(&snapshot).row() + 1;
-                active_editor.highlight_rows(Some(display_rows));
-                active_editor.request_autoscroll(Autoscroll::center(), cx);
-            });
-        }
-    }
-}
-
-impl PickerDelegate for OutlineViewDelegate {
-    type ListItem = ListItem;
-
-    fn placeholder_text(&self) -> Arc<str> {
-        "Search buffer symbols...".into()
-    }
-
-    fn match_count(&self) -> usize {
-        self.matches.len()
-    }
-
-    fn selected_index(&self) -> usize {
-        self.selected_match_index
-    }
-
-    fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<OutlineViewDelegate>>) {
-        self.set_selected_index(ix, true, cx);
-    }
-
-    fn update_matches(
-        &mut self,
-        query: String,
-        cx: &mut ViewContext<Picker<OutlineViewDelegate>>,
-    ) -> Task<()> {
-        let selected_index;
-        if query.is_empty() {
-            self.restore_active_editor(cx);
-            self.matches = self
-                .outline
-                .items
-                .iter()
-                .enumerate()
-                .map(|(index, _)| StringMatch {
-                    candidate_id: index,
-                    score: Default::default(),
-                    positions: Default::default(),
-                    string: Default::default(),
-                })
-                .collect();
-
-            let editor = self.active_editor.read(cx);
-            let cursor_offset = editor.selections.newest::<usize>(cx).head();
-            let buffer = editor.buffer().read(cx).snapshot(cx);
-            selected_index = self
-                .outline
-                .items
-                .iter()
-                .enumerate()
-                .map(|(ix, item)| {
-                    let range = item.range.to_offset(&buffer);
-                    let distance_to_closest_endpoint = cmp::min(
-                        (range.start as isize - cursor_offset as isize).abs(),
-                        (range.end as isize - cursor_offset as isize).abs(),
-                    );
-                    let depth = if range.contains(&cursor_offset) {
-                        Some(item.depth)
-                    } else {
-                        None
-                    };
-                    (ix, depth, distance_to_closest_endpoint)
-                })
-                .max_by_key(|(_, depth, distance)| (*depth, Reverse(*distance)))
-                .map(|(ix, _, _)| ix)
-                .unwrap_or(0);
-        } else {
-            self.matches = smol::block_on(
-                self.outline
-                    .search(&query, cx.background_executor().clone()),
-            );
-            selected_index = self
-                .matches
-                .iter()
-                .enumerate()
-                .max_by_key(|(_, m)| OrderedFloat(m.score))
-                .map(|(ix, _)| ix)
-                .unwrap_or(0);
-        }
-        self.last_query = query;
-        self.set_selected_index(selected_index, !self.last_query.is_empty(), cx);
-        Task::ready(())
-    }
-
-    fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<OutlineViewDelegate>>) {
-        self.prev_scroll_position.take();
-
-        self.active_editor.update(cx, |active_editor, cx| {
-            if let Some(rows) = active_editor.highlighted_rows() {
-                let snapshot = active_editor.snapshot(cx).display_snapshot;
-                let position = DisplayPoint::new(rows.start, 0).to_point(&snapshot);
-                active_editor.change_selections(Some(Autoscroll::center()), cx, |s| {
-                    s.select_ranges([position..position])
-                });
-                active_editor.highlight_rows(None);
-                active_editor.focus(cx);
-            }
-        });
-
-        self.dismissed(cx);
-    }
-
-    fn dismissed(&mut self, cx: &mut ViewContext<Picker<OutlineViewDelegate>>) {
-        self.outline_view
-            .update(cx, |_, cx| cx.emit(DismissEvent))
-            .log_err();
-        self.restore_active_editor(cx);
-    }
-
-    fn render_match(
-        &self,
-        ix: usize,
-        selected: bool,
-        cx: &mut ViewContext<Picker<Self>>,
-    ) -> Option<Self::ListItem> {
-        let settings = ThemeSettings::get_global(cx);
-
-        // TODO: We probably shouldn't need to build a whole new text style here
-        // but I'm not sure how to get the current one and modify it.
-        // Before this change TextStyle::default() was used here, which was giving us the wrong font and text color.
-        let text_style = TextStyle {
-            color: cx.theme().colors().text,
-            font_family: settings.buffer_font.family.clone(),
-            font_features: settings.buffer_font.features,
-            font_size: settings.buffer_font_size(cx).into(),
-            font_weight: FontWeight::NORMAL,
-            font_style: FontStyle::Normal,
-            line_height: relative(1.).into(),
-            background_color: None,
-            underline: None,
-            white_space: WhiteSpace::Normal,
-        };
-
-        let mut highlight_style = HighlightStyle::default();
-        highlight_style.background_color = Some(color_alpha(cx.theme().colors().text_accent, 0.3));
-
-        let mat = &self.matches[ix];
-        let outline_item = &self.outline.items[mat.candidate_id];
-
-        let highlights = gpui::combine_highlights(
-            mat.ranges().map(|range| (range, highlight_style)),
-            outline_item.highlight_ranges.iter().cloned(),
-        );
-
-        let styled_text =
-            StyledText::new(outline_item.text.clone()).with_highlights(&text_style, highlights);
-
-        Some(
-            ListItem::new(ix)
-                .inset(true)
-                .spacing(ListItemSpacing::Sparse)
-                .selected(selected)
-                .child(
-                    div()
-                        .text_ui()
-                        .pl(rems(outline_item.depth as f32))
-                        .child(styled_text),
-                ),
-        )
-    }
-}

crates/project2/Cargo.toml 🔗

@@ -21,7 +21,7 @@ test-support = [
 
 [dependencies]
 text = { package = "text2", path = "../text2" }
-copilot = { package = "copilot2", path = "../copilot2" }
+copilot = { path = "../copilot" }
 client = { package = "client2", path = "../client2" }
 clock = { path = "../clock" }
 collections = { path = "../collections" }

crates/zed/Cargo.toml 🔗

@@ -29,7 +29,7 @@ command_palette = { path = "../command_palette" }
 # component_test = { path = "../component_test" }
 client = { package = "client2", path = "../client2" }
 # clock = { path = "../clock" }
-copilot = { package = "copilot2", path = "../copilot2" }
+copilot = { path = "../copilot" }
 copilot_button = { path = "../copilot_button" }
 diagnostics = { path = "../diagnostics" }
 db = { package = "db2", path = "../db2" }
@@ -51,7 +51,7 @@ language_tools = { path = "../language_tools" }
 node_runtime = { path = "../node_runtime" }
 notifications = { package = "notifications2", path = "../notifications2" }
 assistant = { package = "assistant2", path = "../assistant2" }
-outline = { package = "outline2", path = "../outline2" }
+outline = { path = "../outline" }
 # plugin_runtime = { path = "../plugin_runtime",optional = true }
 project = { package = "project2", path = "../project2" }
 project_panel = { path = "../project_panel" }