From 46019f85379c48a47e1405e8593548af98baced8 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 19 Sep 2022 18:01:34 +0200 Subject: [PATCH 001/314] WIP --- Cargo.lock | 11 +++++++++ crates/room/Cargo.toml | 27 ++++++++++++++++++++ crates/room/src/participant.rs | 15 ++++++++++++ crates/room/src/room.rs | 45 ++++++++++++++++++++++++++++++++++ 4 files changed, 98 insertions(+) create mode 100644 crates/room/Cargo.toml create mode 100644 crates/room/src/participant.rs create mode 100644 crates/room/src/room.rs diff --git a/Cargo.lock b/Cargo.lock index 925a4323a4..d30cbcee69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4451,6 +4451,17 @@ dependencies = [ "librocksdb-sys", ] +[[package]] +name = "room" +version = "0.1.0" +dependencies = [ + "anyhow", + "client", + "gpui", + "project", + "workspace", +] + [[package]] name = "roxmltree" version = "0.14.1" diff --git a/crates/room/Cargo.toml b/crates/room/Cargo.toml new file mode 100644 index 0000000000..767ba399d6 --- /dev/null +++ b/crates/room/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "room" +version = "0.1.0" +edition = "2021" + +[lib] +path = "src/room.rs" +doctest = false + +[features] +test-support = [ + "client/test-support", + "gpui/test-support", + "project/test-support", +] + +[dependencies] +anyhow = "1.0.38" +client = { path = "../client" } +gpui = { path = "../gpui" } +project = { path = "../project" } +workspace = { path = "../workspace" } + +[dev-dependencies] +client = { path = "../client", features = ["test-support"] } +gpui = { path = "../gpui", features = ["test-support"] } +project = { path = "../project", features = ["test-support"] } diff --git a/crates/room/src/participant.rs b/crates/room/src/participant.rs new file mode 100644 index 0000000000..a5b02b05a6 --- /dev/null +++ b/crates/room/src/participant.rs @@ -0,0 +1,15 @@ +use client::User; +use gpui::{ModelHandle, ViewHandle}; +use project::Project; +use workspace::Workspace; + +pub struct LocalParticipant { + user: User, + workspaces: Vec>, +} + +pub struct RemoteParticipant { + user: User, + workspaces: Vec>, + active_workspace_id: usize, +} diff --git a/crates/room/src/room.rs b/crates/room/src/room.rs new file mode 100644 index 0000000000..b18cb800e4 --- /dev/null +++ b/crates/room/src/room.rs @@ -0,0 +1,45 @@ +mod participant; + +use anyhow::Result; +use client::Client; +use gpui::ModelHandle; +use participant::{LocalParticipant, RemoteParticipant}; +use project::Project; +use std::sync::Arc; + +pub struct Room { + id: u64, + local_participant: LocalParticipant, + remote_participants: Vec, + client: Arc, +} + +impl Room { + pub async fn create(client: Arc) -> Result { + todo!() + } + + pub async fn join(id: u64, client: Arc) -> Result { + todo!() + } + + pub async fn invite(&mut self, user_id: u64) -> Result<()> { + todo!() + } + + pub async fn share(&mut self) -> Result<()> { + todo!() + } + + pub async fn unshare(&mut self) -> Result<()> { + todo!() + } + + pub async fn mute(&mut self) -> Result<()> { + todo!() + } + + pub async fn unmute(&mut self) -> Result<()> { + todo!() + } +} From 8fec7da7998d0c297b1c4dbbabaa5b02fb64c177 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 22 Sep 2022 17:08:36 +0200 Subject: [PATCH 002/314] WIP --- Cargo.lock | 1 - crates/collab/src/integration_tests.rs | 36 ++++++++++++++++++++++++++ crates/collab/src/rpc/store.rs | 14 ++++++++++ crates/room/Cargo.toml | 1 - crates/room/src/participant.rs | 14 ++++++---- crates/room/src/room.rs | 27 ++++++++++++++----- 6 files changed, 80 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d30cbcee69..2d25f79fe2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4459,7 +4459,6 @@ dependencies = [ "client", "gpui", "project", - "workspace", ] [[package]] diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 0735474728..c91e3e4ea5 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -60,6 +60,42 @@ fn init_logger() { } } +#[gpui::test(iterations = 10)] +async fn test_share_project_in_room( + deterministic: Arc, + cx_a: &mut TestAppContext, + cx_b: &mut TestAppContext, +) { + deterministic.forbid_parking(); + let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; + let client_a = server.create_client(cx_a, "user_a").await; + let client_b = server.create_client(cx_b, "user_b").await; + server + .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + .await; + + client_a + .fs + .insert_tree( + "/a", + json!({ + ".gitignore": "ignored-dir", + "a.txt": "a-contents", + "b.txt": "b-contents", + "ignored-dir": { + "c.txt": "", + "d.txt": "", + } + }), + ) + .await; + + let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; + let project_id = project_a.update(cx_a, |project, cx| project.share(cx)).await.unwrap(); + + +} + #[gpui::test(iterations = 10)] async fn test_share_project( deterministic: Arc, diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index fe18e0404b..4d23a4d741 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -7,10 +7,13 @@ use std::{mem, path::PathBuf, str, time::Duration}; use time::OffsetDateTime; use tracing::instrument; +pub type RoomId = u64; + #[derive(Default, Serialize)] pub struct Store { connections: BTreeMap, connections_by_user_id: BTreeMap>, + rooms: BTreeMap, projects: BTreeMap, #[serde(skip)] channels: BTreeMap, @@ -25,6 +28,17 @@ struct ConnectionState { channels: HashSet, } +#[derive(Serialize)] +struct Room { + participants: HashMap, +} + +#[derive(Serialize)] +struct Participant { + user_id: UserId, + shared_projects: HashSet, +} + #[derive(Serialize)] pub struct Project { pub online: bool, diff --git a/crates/room/Cargo.toml b/crates/room/Cargo.toml index 767ba399d6..80b0a1f459 100644 --- a/crates/room/Cargo.toml +++ b/crates/room/Cargo.toml @@ -19,7 +19,6 @@ anyhow = "1.0.38" client = { path = "../client" } gpui = { path = "../gpui" } project = { path = "../project" } -workspace = { path = "../workspace" } [dev-dependencies] client = { path = "../client", features = ["test-support"] } diff --git a/crates/room/src/participant.rs b/crates/room/src/participant.rs index a5b02b05a6..50b873a781 100644 --- a/crates/room/src/participant.rs +++ b/crates/room/src/participant.rs @@ -1,15 +1,19 @@ use client::User; -use gpui::{ModelHandle, ViewHandle}; +use gpui::ModelHandle; use project::Project; -use workspace::Workspace; + +pub enum Location { + Project { project_id: usize }, + External, +} pub struct LocalParticipant { user: User, - workspaces: Vec>, + projects: Vec>, } pub struct RemoteParticipant { user: User, - workspaces: Vec>, - active_workspace_id: usize, + projects: Vec>, + location: Location, } diff --git a/crates/room/src/room.rs b/crates/room/src/room.rs index b18cb800e4..e4017e9642 100644 --- a/crates/room/src/room.rs +++ b/crates/room/src/room.rs @@ -1,19 +1,27 @@ mod participant; use anyhow::Result; -use client::Client; -use gpui::ModelHandle; +use client::{Client, PeerId}; +use gpui::{Entity, ModelHandle}; use participant::{LocalParticipant, RemoteParticipant}; use project::Project; -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; + +pub enum Event { + PeerChangedActiveProject, +} pub struct Room { id: u64, local_participant: LocalParticipant, - remote_participants: Vec, + remote_participants: HashMap, client: Arc, } +impl Entity for Room { + type Event = Event; +} + impl Room { pub async fn create(client: Arc) -> Result { todo!() @@ -27,11 +35,18 @@ impl Room { todo!() } - pub async fn share(&mut self) -> Result<()> { + pub async fn share_project(&mut self, project: ModelHandle) -> Result<()> { todo!() } - pub async fn unshare(&mut self) -> Result<()> { + pub async fn unshare_project(&mut self, project: ModelHandle) -> Result<()> { + todo!() + } + + pub async fn set_active_project( + &mut self, + project: Option<&ModelHandle>, + ) -> Result<()> { todo!() } From 0b1e372d11eb401b5f86481e5df9ca96bd548350 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 22 Sep 2022 19:41:51 +0200 Subject: [PATCH 003/314] Start sketching out an integration test for calls Co-Authored-By: Nathan Sobo --- Cargo.lock | 1 + crates/collab/Cargo.toml | 3 ++- crates/collab/src/integration_tests.rs | 12 +++++++----- crates/room/src/room.rs | 4 ++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2d25f79fe2..f853a2059c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1040,6 +1040,7 @@ dependencies = [ "prometheus", "rand 0.8.5", "reqwest", + "room", "rpc", "scrypt", "serde", diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index 9b3603e6e4..8603f67557 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -55,13 +55,14 @@ features = ["runtime-tokio-rustls", "postgres", "time", "uuid"] [dev-dependencies] collections = { path = "../collections", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } -rpc = { path = "../rpc", features = ["test-support"] } client = { path = "../client", features = ["test-support"] } editor = { path = "../editor", features = ["test-support"] } language = { path = "../language", features = ["test-support"] } log = { version = "0.4.16", features = ["kv_unstable_serde"] } lsp = { path = "../lsp", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } +room = { path = "../room", features = ["test-support"] } +rpc = { path = "../rpc", features = ["test-support"] } settings = { path = "../settings", features = ["test-support"] } theme = { path = "../theme" } workspace = { path = "../workspace", features = ["test-support"] } diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index c91e3e4ea5..36ecaf41b4 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -90,10 +90,15 @@ async fn test_share_project_in_room( ) .await; + let room_a = Room::create(client_a.clone()).await.unwrap(); let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; - let project_id = project_a.update(cx_a, |project, cx| project.share(cx)).await.unwrap(); - + // room.publish_project(project_a.clone()).await.unwrap(); + let incoming_calls_b = client_b.user_store.incoming_calls(); + let user_b_joined = room_a.invite(client_b.user_id().unwrap()); + let call_b = incoming_calls_b.next().await.unwrap(); + let room_b = Room::join(call_b.room_id, client_b.clone()).await.unwrap(); + user_b_joined.await.unwrap(); } #[gpui::test(iterations = 10)] @@ -5469,9 +5474,6 @@ impl TestClient { worktree .read_with(cx, |tree, _| tree.as_local().unwrap().scan_complete()) .await; - project - .update(cx, |project, _| project.next_remote_id()) - .await; (project, worktree.read_with(cx, |tree, _| tree.id())) } diff --git a/crates/room/src/room.rs b/crates/room/src/room.rs index e4017e9642..965804efc8 100644 --- a/crates/room/src/room.rs +++ b/crates/room/src/room.rs @@ -35,11 +35,11 @@ impl Room { todo!() } - pub async fn share_project(&mut self, project: ModelHandle) -> Result<()> { + pub async fn publish_project(&mut self, project: ModelHandle) -> Result<()> { todo!() } - pub async fn unshare_project(&mut self, project: ModelHandle) -> Result<()> { + pub async fn unpublish_project(&mut self, project: ModelHandle) -> Result<()> { todo!() } From ebb5ffcedc645f1f4e6984bcfc632825c9999325 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 23 Sep 2022 15:05:32 +0200 Subject: [PATCH 004/314] Introduce the ability of creating rooms on the server --- Cargo.lock | 1 + crates/client/src/call.rs | 8 + crates/client/src/client.rs | 1 + crates/client/src/user.rs | 19 +- crates/collab/src/integration_tests.rs | 19 +- crates/collab/src/rpc.rs | 11 ++ crates/collab/src/rpc/store.rs | 40 +++-- crates/room/Cargo.toml | 3 + crates/room/src/participant.rs | 28 ++- crates/room/src/room.rs | 62 +++++-- crates/rpc/proto/zed.proto | 230 +++++++++++++++---------- crates/rpc/src/proto.rs | 8 +- 12 files changed, 302 insertions(+), 128 deletions(-) create mode 100644 crates/client/src/call.rs diff --git a/Cargo.lock b/Cargo.lock index f853a2059c..08e183810d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4458,6 +4458,7 @@ version = "0.1.0" dependencies = [ "anyhow", "client", + "collections", "gpui", "project", ] diff --git a/crates/client/src/call.rs b/crates/client/src/call.rs new file mode 100644 index 0000000000..2e7bd799f0 --- /dev/null +++ b/crates/client/src/call.rs @@ -0,0 +1,8 @@ +use crate::User; +use std::sync::Arc; + +#[derive(Clone)] +pub struct Call { + pub from: Vec>, + pub room_id: u64, +} diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index e328108a52..20563272ab 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -1,6 +1,7 @@ #[cfg(any(test, feature = "test-support"))] pub mod test; +pub mod call; pub mod channel; pub mod http; pub mod user; diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 149d22e77a..71e8de12e5 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -1,9 +1,11 @@ +use crate::call::Call; + use super::{http::HttpClient, proto, Client, Status, TypedEnvelope}; use anyhow::{anyhow, Context, Result}; use collections::{hash_map::Entry, BTreeSet, HashMap, HashSet}; -use futures::{channel::mpsc, future, AsyncReadExt, Future, StreamExt}; +use futures::{channel::mpsc, future, AsyncReadExt, Future, Stream, StreamExt}; use gpui::{AsyncAppContext, Entity, ImageData, ModelContext, ModelHandle, Task}; -use postage::{prelude::Stream, sink::Sink, watch}; +use postage::{broadcast, sink::Sink, watch}; use rpc::proto::{RequestMessage, UsersResponse}; use std::sync::{Arc, Weak}; use util::TryFutureExt as _; @@ -66,6 +68,7 @@ pub struct UserStore { outgoing_contact_requests: Vec>, pending_contact_requests: HashMap, invite_info: Option, + incoming_calls: broadcast::Sender, client: Weak, http: Arc, _maintain_contacts: Task<()>, @@ -116,6 +119,7 @@ impl UserStore { client.add_message_handler(cx.handle(), Self::handle_update_invite_info), client.add_message_handler(cx.handle(), Self::handle_show_contacts), ]; + let (incoming_calls, _) = broadcast::channel(32); Self { users: Default::default(), current_user: current_user_rx, @@ -123,6 +127,7 @@ impl UserStore { incoming_contact_requests: Default::default(), outgoing_contact_requests: Default::default(), invite_info: None, + incoming_calls, client: Arc::downgrade(&client), update_contacts_tx, http, @@ -138,7 +143,7 @@ impl UserStore { }), _maintain_current_user: cx.spawn_weak(|this, mut cx| async move { let mut status = client.status(); - while let Some(status) = status.recv().await { + while let Some(status) = status.next().await { match status { Status::Connected { .. } => { if let Some((this, user_id)) = this.upgrade(&cx).zip(client.user_id()) { @@ -198,6 +203,10 @@ impl UserStore { self.invite_info.as_ref() } + pub fn incoming_calls(&self) -> impl 'static + Stream { + self.incoming_calls.subscribe() + } + async fn handle_update_contacts( this: ModelHandle, message: TypedEnvelope, @@ -493,7 +502,7 @@ impl UserStore { .unbounded_send(UpdateContacts::Clear(tx)) .unwrap(); async move { - rx.recv().await; + rx.next().await; } } @@ -503,7 +512,7 @@ impl UserStore { .unbounded_send(UpdateContacts::Wait(tx)) .unwrap(); async move { - rx.recv().await; + rx.next().await; } } diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 36ecaf41b4..cc80f96ad8 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -34,6 +34,7 @@ use project::{ DiagnosticSummary, Project, ProjectPath, ProjectStore, WorktreeId, }; use rand::prelude::*; +use room::Room; use rpc::PeerId; use serde_json::json; use settings::{Formatter, Settings}; @@ -90,14 +91,24 @@ async fn test_share_project_in_room( ) .await; - let room_a = Room::create(client_a.clone()).await.unwrap(); + let room_a = cx_a + .update(|cx| Room::create(client_a.clone(), cx)) + .await + .unwrap(); let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; // room.publish_project(project_a.clone()).await.unwrap(); - let incoming_calls_b = client_b.user_store.incoming_calls(); - let user_b_joined = room_a.invite(client_b.user_id().unwrap()); + let mut incoming_calls_b = client_b + .user_store + .read_with(cx_b, |user, _| user.incoming_calls()); + let user_b_joined = room_a.update(cx_a, |room, cx| { + room.invite(client_b.user_id().unwrap(), cx) + }); let call_b = incoming_calls_b.next().await.unwrap(); - let room_b = Room::join(call_b.room_id, client_b.clone()).await.unwrap(); + let room_b = cx_b + .update(|cx| Room::join(call_b.room_id, client_b.clone(), cx)) + .await + .unwrap(); user_b_joined.await.unwrap(); } diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index dab7df3e67..6434a97de5 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -151,6 +151,7 @@ impl Server { server .add_request_handler(Server::ping) + .add_request_handler(Server::create_room) .add_request_handler(Server::register_project) .add_request_handler(Server::unregister_project) .add_request_handler(Server::join_project) @@ -593,6 +594,16 @@ impl Server { Ok(()) } + async fn create_room( + self: Arc, + request: TypedEnvelope, + response: Response, + ) -> Result<()> { + let room_id = self.store().await.create_room(request.sender_id)?; + response.send(proto::CreateRoomResponse { id: room_id })?; + Ok(()) + } + async fn register_project( self: Arc, request: TypedEnvelope, diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index 4d23a4d741..9ce6931477 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -6,6 +6,7 @@ use serde::Serialize; use std::{mem, path::PathBuf, str, time::Duration}; use time::OffsetDateTime; use tracing::instrument; +use util::post_inc; pub type RoomId = u64; @@ -13,7 +14,8 @@ pub type RoomId = u64; pub struct Store { connections: BTreeMap, connections_by_user_id: BTreeMap>, - rooms: BTreeMap, + next_room_id: RoomId, + rooms: BTreeMap, projects: BTreeMap, #[serde(skip)] channels: BTreeMap, @@ -23,22 +25,12 @@ pub struct Store { struct ConnectionState { user_id: UserId, admin: bool, + rooms: BTreeSet, projects: BTreeSet, requested_projects: HashSet, channels: HashSet, } -#[derive(Serialize)] -struct Room { - participants: HashMap, -} - -#[derive(Serialize)] -struct Participant { - user_id: UserId, - shared_projects: HashSet, -} - #[derive(Serialize)] pub struct Project { pub online: bool, @@ -148,6 +140,7 @@ impl Store { ConnectionState { user_id, admin, + rooms: Default::default(), projects: Default::default(), requested_projects: Default::default(), channels: Default::default(), @@ -335,6 +328,29 @@ impl Store { metadata } + pub fn create_room(&mut self, creator_connection_id: ConnectionId) -> Result { + let connection = self + .connections + .get_mut(&creator_connection_id) + .ok_or_else(|| anyhow!("no such connection"))?; + let mut room = proto::Room::default(); + room.participants.push(proto::Participant { + user_id: connection.user_id.to_proto(), + peer_id: creator_connection_id.0, + project_ids: Default::default(), + location: Some(proto::ParticipantLocation { + variant: Some(proto::participant_location::Variant::External( + proto::participant_location::External {}, + )), + }), + }); + + let room_id = post_inc(&mut self.next_room_id); + self.rooms.insert(room_id, room); + connection.rooms.insert(room_id); + Ok(room_id) + } + pub fn register_project( &mut self, host_connection_id: ConnectionId, diff --git a/crates/room/Cargo.toml b/crates/room/Cargo.toml index 80b0a1f459..f329d5ae87 100644 --- a/crates/room/Cargo.toml +++ b/crates/room/Cargo.toml @@ -10,6 +10,7 @@ doctest = false [features] test-support = [ "client/test-support", + "collections/test-support", "gpui/test-support", "project/test-support", ] @@ -17,10 +18,12 @@ test-support = [ [dependencies] anyhow = "1.0.38" client = { path = "../client" } +collections = { path = "../collections" } gpui = { path = "../gpui" } project = { path = "../project" } [dev-dependencies] client = { path = "../client", features = ["test-support"] } +collections = { path = "../collections", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } diff --git a/crates/room/src/participant.rs b/crates/room/src/participant.rs index 50b873a781..cde17b45c2 100644 --- a/crates/room/src/participant.rs +++ b/crates/room/src/participant.rs @@ -1,19 +1,31 @@ -use client::User; +use anyhow::{anyhow, Result}; +use client::proto; use gpui::ModelHandle; use project::Project; -pub enum Location { - Project { project_id: usize }, +pub enum ParticipantLocation { + Project { project_id: u64 }, External, } +impl ParticipantLocation { + pub fn from_proto(location: Option) -> Result { + match location.and_then(|l| l.variant) { + Some(proto::participant_location::Variant::Project(project)) => Ok(Self::Project { + project_id: project.id, + }), + Some(proto::participant_location::Variant::External(_)) => Ok(Self::External), + None => Err(anyhow!("participant location was not provided")), + } + } +} + pub struct LocalParticipant { - user: User, - projects: Vec>, + pub projects: Vec>, } pub struct RemoteParticipant { - user: User, - projects: Vec>, - location: Location, + pub user_id: u64, + pub projects: Vec>, + pub location: ParticipantLocation, } diff --git a/crates/room/src/room.rs b/crates/room/src/room.rs index 965804efc8..c444db4316 100644 --- a/crates/room/src/room.rs +++ b/crates/room/src/room.rs @@ -1,11 +1,12 @@ mod participant; -use anyhow::Result; -use client::{Client, PeerId}; -use gpui::{Entity, ModelHandle}; -use participant::{LocalParticipant, RemoteParticipant}; +use anyhow::{anyhow, Result}; +use client::{proto, Client, PeerId}; +use collections::HashMap; +use gpui::{Entity, ModelContext, ModelHandle, MutableAppContext, Task}; +use participant::{LocalParticipant, ParticipantLocation, RemoteParticipant}; use project::Project; -use std::{collections::HashMap, sync::Arc}; +use std::sync::Arc; pub enum Event { PeerChangedActiveProject, @@ -23,15 +24,56 @@ impl Entity for Room { } impl Room { - pub async fn create(client: Arc) -> Result { - todo!() + pub fn create( + client: Arc, + cx: &mut MutableAppContext, + ) -> Task>> { + cx.spawn(|mut cx| async move { + let room = client.request(proto::CreateRoom {}).await?; + Ok(cx.add_model(|cx| Self::new(room.id, client, cx))) + }) } - pub async fn join(id: u64, client: Arc) -> Result { - todo!() + pub fn join( + id: u64, + client: Arc, + cx: &mut MutableAppContext, + ) -> Task>> { + cx.spawn(|mut cx| async move { + let response = client.request(proto::JoinRoom { id }).await?; + let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?; + let room = cx.add_model(|cx| Self::new(id, client, cx)); + room.update(&mut cx, |room, cx| room.refresh(room_proto, cx))?; + Ok(room) + }) } - pub async fn invite(&mut self, user_id: u64) -> Result<()> { + fn new(id: u64, client: Arc, _: &mut ModelContext) -> Self { + Self { + id, + local_participant: LocalParticipant { + projects: Default::default(), + }, + remote_participants: Default::default(), + client, + } + } + + fn refresh(&mut self, room: proto::Room, cx: &mut ModelContext) -> Result<()> { + for participant in room.participants { + self.remote_participants.insert( + PeerId(participant.peer_id), + RemoteParticipant { + user_id: participant.user_id, + projects: Default::default(), // TODO: populate projects + location: ParticipantLocation::from_proto(participant.location)?, + }, + ); + } + Ok(()) + } + + pub fn invite(&mut self, user_id: u64, cx: &mut ModelContext) -> Task> { todo!() } diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 7840829b44..3d8f7f9a6e 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -10,104 +10,112 @@ message Envelope { Error error = 5; Ping ping = 6; Test test = 7; + + CreateRoom create_room = 8; + CreateRoomResponse create_room_response = 9; + JoinRoom join_room = 10; + JoinRoomResponse join_room_response = 11; + Call call = 12; + CallResponse call_response = 13; + RoomUpdated room_updated = 14; - RegisterProject register_project = 8; - RegisterProjectResponse register_project_response = 9; - UnregisterProject unregister_project = 10; - RequestJoinProject request_join_project = 11; - RespondToJoinProjectRequest respond_to_join_project_request = 12; - JoinProjectRequestCancelled join_project_request_cancelled = 13; - JoinProject join_project = 14; - JoinProjectResponse join_project_response = 15; - LeaveProject leave_project = 16; - AddProjectCollaborator add_project_collaborator = 17; - RemoveProjectCollaborator remove_project_collaborator = 18; - ProjectUnshared project_unshared = 19; + RegisterProject register_project = 15; + RegisterProjectResponse register_project_response = 16; + UnregisterProject unregister_project = 17; + RequestJoinProject request_join_project = 18; + RespondToJoinProjectRequest respond_to_join_project_request = 19; + JoinProjectRequestCancelled join_project_request_cancelled = 20; + JoinProject join_project = 21; + JoinProjectResponse join_project_response = 22; + LeaveProject leave_project = 23; + AddProjectCollaborator add_project_collaborator = 24; + RemoveProjectCollaborator remove_project_collaborator = 25; + ProjectUnshared project_unshared = 26; - GetDefinition get_definition = 20; - GetDefinitionResponse get_definition_response = 21; - GetTypeDefinition get_type_definition = 22; - GetTypeDefinitionResponse get_type_definition_response = 23; - GetReferences get_references = 24; - GetReferencesResponse get_references_response = 25; - GetDocumentHighlights get_document_highlights = 26; - GetDocumentHighlightsResponse get_document_highlights_response = 27; - GetProjectSymbols get_project_symbols = 28; - GetProjectSymbolsResponse get_project_symbols_response = 29; - OpenBufferForSymbol open_buffer_for_symbol = 30; - OpenBufferForSymbolResponse open_buffer_for_symbol_response = 31; + GetDefinition get_definition = 27; + GetDefinitionResponse get_definition_response = 28; + GetTypeDefinition get_type_definition = 29; + GetTypeDefinitionResponse get_type_definition_response = 30; + GetReferences get_references = 31; + GetReferencesResponse get_references_response = 32; + GetDocumentHighlights get_document_highlights = 33; + GetDocumentHighlightsResponse get_document_highlights_response = 34; + GetProjectSymbols get_project_symbols = 35; + GetProjectSymbolsResponse get_project_symbols_response = 36; + OpenBufferForSymbol open_buffer_for_symbol = 37; + OpenBufferForSymbolResponse open_buffer_for_symbol_response = 38; - UpdateProject update_project = 32; - RegisterProjectActivity register_project_activity = 33; - UpdateWorktree update_worktree = 34; - UpdateWorktreeExtensions update_worktree_extensions = 35; + UpdateProject update_project = 39; + RegisterProjectActivity register_project_activity = 40; + UpdateWorktree update_worktree = 41; + UpdateWorktreeExtensions update_worktree_extensions = 42; - CreateProjectEntry create_project_entry = 36; - RenameProjectEntry rename_project_entry = 37; - CopyProjectEntry copy_project_entry = 38; - DeleteProjectEntry delete_project_entry = 39; - ProjectEntryResponse project_entry_response = 40; + CreateProjectEntry create_project_entry = 43; + RenameProjectEntry rename_project_entry = 44; + CopyProjectEntry copy_project_entry = 45; + DeleteProjectEntry delete_project_entry = 46; + ProjectEntryResponse project_entry_response = 47; - UpdateDiagnosticSummary update_diagnostic_summary = 41; - StartLanguageServer start_language_server = 42; - UpdateLanguageServer update_language_server = 43; + UpdateDiagnosticSummary update_diagnostic_summary = 48; + StartLanguageServer start_language_server = 49; + UpdateLanguageServer update_language_server = 50; - OpenBufferById open_buffer_by_id = 44; - OpenBufferByPath open_buffer_by_path = 45; - OpenBufferResponse open_buffer_response = 46; - CreateBufferForPeer create_buffer_for_peer = 47; - UpdateBuffer update_buffer = 48; - UpdateBufferFile update_buffer_file = 49; - SaveBuffer save_buffer = 50; - BufferSaved buffer_saved = 51; - BufferReloaded buffer_reloaded = 52; - ReloadBuffers reload_buffers = 53; - ReloadBuffersResponse reload_buffers_response = 54; - FormatBuffers format_buffers = 55; - FormatBuffersResponse format_buffers_response = 56; - GetCompletions get_completions = 57; - GetCompletionsResponse get_completions_response = 58; - ApplyCompletionAdditionalEdits apply_completion_additional_edits = 59; - ApplyCompletionAdditionalEditsResponse apply_completion_additional_edits_response = 60; - GetCodeActions get_code_actions = 61; - GetCodeActionsResponse get_code_actions_response = 62; - GetHover get_hover = 63; - GetHoverResponse get_hover_response = 64; - ApplyCodeAction apply_code_action = 65; - ApplyCodeActionResponse apply_code_action_response = 66; - PrepareRename prepare_rename = 67; - PrepareRenameResponse prepare_rename_response = 68; - PerformRename perform_rename = 69; - PerformRenameResponse perform_rename_response = 70; - SearchProject search_project = 71; - SearchProjectResponse search_project_response = 72; + OpenBufferById open_buffer_by_id = 51; + OpenBufferByPath open_buffer_by_path = 52; + OpenBufferResponse open_buffer_response = 53; + CreateBufferForPeer create_buffer_for_peer = 54; + UpdateBuffer update_buffer = 55; + UpdateBufferFile update_buffer_file = 56; + SaveBuffer save_buffer = 57; + BufferSaved buffer_saved = 58; + BufferReloaded buffer_reloaded = 59; + ReloadBuffers reload_buffers = 60; + ReloadBuffersResponse reload_buffers_response = 61; + FormatBuffers format_buffers = 62; + FormatBuffersResponse format_buffers_response = 63; + GetCompletions get_completions = 64; + GetCompletionsResponse get_completions_response = 65; + ApplyCompletionAdditionalEdits apply_completion_additional_edits = 66; + ApplyCompletionAdditionalEditsResponse apply_completion_additional_edits_response = 67; + GetCodeActions get_code_actions = 68; + GetCodeActionsResponse get_code_actions_response = 69; + GetHover get_hover = 70; + GetHoverResponse get_hover_response = 71; + ApplyCodeAction apply_code_action = 72; + ApplyCodeActionResponse apply_code_action_response = 73; + PrepareRename prepare_rename = 74; + PrepareRenameResponse prepare_rename_response = 75; + PerformRename perform_rename = 76; + PerformRenameResponse perform_rename_response = 77; + SearchProject search_project = 78; + SearchProjectResponse search_project_response = 79; - GetChannels get_channels = 73; - GetChannelsResponse get_channels_response = 74; - JoinChannel join_channel = 75; - JoinChannelResponse join_channel_response = 76; - LeaveChannel leave_channel = 77; - SendChannelMessage send_channel_message = 78; - SendChannelMessageResponse send_channel_message_response = 79; - ChannelMessageSent channel_message_sent = 80; - GetChannelMessages get_channel_messages = 81; - GetChannelMessagesResponse get_channel_messages_response = 82; + GetChannels get_channels = 80; + GetChannelsResponse get_channels_response = 81; + JoinChannel join_channel = 82; + JoinChannelResponse join_channel_response = 83; + LeaveChannel leave_channel = 84; + SendChannelMessage send_channel_message = 85; + SendChannelMessageResponse send_channel_message_response = 86; + ChannelMessageSent channel_message_sent = 87; + GetChannelMessages get_channel_messages = 88; + GetChannelMessagesResponse get_channel_messages_response = 89; - UpdateContacts update_contacts = 83; - UpdateInviteInfo update_invite_info = 84; - ShowContacts show_contacts = 85; + UpdateContacts update_contacts = 90; + UpdateInviteInfo update_invite_info = 91; + ShowContacts show_contacts = 92; - GetUsers get_users = 86; - FuzzySearchUsers fuzzy_search_users = 87; - UsersResponse users_response = 88; - RequestContact request_contact = 89; - RespondToContactRequest respond_to_contact_request = 90; - RemoveContact remove_contact = 91; + GetUsers get_users = 93; + FuzzySearchUsers fuzzy_search_users = 94; + UsersResponse users_response = 95; + RequestContact request_contact = 96; + RespondToContactRequest respond_to_contact_request = 97; + RemoveContact remove_contact = 98; - Follow follow = 92; - FollowResponse follow_response = 93; - UpdateFollowers update_followers = 94; - Unfollow unfollow = 95; + Follow follow = 99; + FollowResponse follow_response = 100; + UpdateFollowers update_followers = 101; + Unfollow unfollow = 102; } } @@ -125,6 +133,52 @@ message Test { uint64 id = 1; } +message CreateRoom {} + +message CreateRoomResponse { + uint64 id = 1; +} + +message JoinRoom { + uint64 id = 1; +} + +message JoinRoomResponse { + Room room = 1; +} + +message Room { + repeated Participant participants = 1; +} + +message Participant { + uint64 user_id = 1; + uint32 peer_id = 2; + repeated uint64 project_ids = 3; + ParticipantLocation location = 4; +} + +message ParticipantLocation { + oneof variant { + Project project = 1; + External external = 2; + } + + message Project { + uint64 id = 1; + } + + message External {} +} + +message Call {} + +message CallResponse {} + +message RoomUpdated { + Room room = 1; +} + message RegisterProject { bool online = 1; } diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 2ba3fa18ba..0e3de8878d 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -83,11 +83,12 @@ messages!( (ApplyCompletionAdditionalEditsResponse, Background), (BufferReloaded, Foreground), (BufferSaved, Foreground), - (RemoveContact, Foreground), (ChannelMessageSent, Foreground), (CopyProjectEntry, Foreground), (CreateBufferForPeer, Foreground), (CreateProjectEntry, Foreground), + (CreateRoom, Foreground), + (CreateRoomResponse, Foreground), (DeleteProjectEntry, Foreground), (Error, Foreground), (Follow, Foreground), @@ -122,6 +123,8 @@ messages!( (JoinProject, Foreground), (JoinProjectResponse, Foreground), (JoinProjectRequestCancelled, Foreground), + (JoinRoom, Foreground), + (JoinRoomResponse, Foreground), (LeaveChannel, Foreground), (LeaveProject, Foreground), (OpenBufferById, Background), @@ -136,6 +139,7 @@ messages!( (ProjectEntryResponse, Foreground), (ProjectUnshared, Foreground), (RegisterProjectResponse, Foreground), + (RemoveContact, Foreground), (Ping, Foreground), (RegisterProject, Foreground), (RegisterProjectActivity, Foreground), @@ -177,6 +181,7 @@ request_messages!( ), (CopyProjectEntry, ProjectEntryResponse), (CreateProjectEntry, ProjectEntryResponse), + (CreateRoom, CreateRoomResponse), (DeleteProjectEntry, ProjectEntryResponse), (Follow, FollowResponse), (FormatBuffers, FormatBuffersResponse), @@ -194,6 +199,7 @@ request_messages!( (GetUsers, UsersResponse), (JoinChannel, JoinChannelResponse), (JoinProject, JoinProjectResponse), + (JoinRoom, JoinRoomResponse), (OpenBufferById, OpenBufferResponse), (OpenBufferByPath, OpenBufferResponse), (OpenBufferForSymbol, OpenBufferForSymbolResponse), From 4a9bf8f4fe174bdb97a7834ad3467c350fa80915 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 23 Sep 2022 18:16:57 +0200 Subject: [PATCH 005/314] Introduce call infrastructure Co-Authored-By: Nathan Sobo --- crates/client/src/call.rs | 3 +- crates/client/src/channel.rs | 2 +- crates/client/src/client.rs | 23 ++++++ crates/client/src/user.rs | 97 +++++++++++++++++++------- crates/collab/src/integration_tests.rs | 12 ++-- crates/collab/src/rpc.rs | 63 +++++++++++++++++ crates/collab/src/rpc/store.rs | 39 +++++++++++ crates/project/src/project.rs | 6 +- crates/room/src/room.rs | 24 +++++-- crates/rpc/proto/zed.proto | 20 +++++- crates/rpc/src/proto.rs | 5 ++ 11 files changed, 249 insertions(+), 45 deletions(-) diff --git a/crates/client/src/call.rs b/crates/client/src/call.rs index 2e7bd799f0..3111a04949 100644 --- a/crates/client/src/call.rs +++ b/crates/client/src/call.rs @@ -3,6 +3,7 @@ use std::sync::Arc; #[derive(Clone)] pub struct Call { - pub from: Vec>, pub room_id: u64, + pub from: Arc, + pub participants: Vec>, } diff --git a/crates/client/src/channel.rs b/crates/client/src/channel.rs index a88f872d11..99e4d5fa96 100644 --- a/crates/client/src/channel.rs +++ b/crates/client/src/channel.rs @@ -530,7 +530,7 @@ impl ChannelMessage { ) -> Result { let sender = user_store .update(cx, |user_store, cx| { - user_store.fetch_user(message.sender_id, cx) + user_store.get_user(message.sender_id, cx) }) .await?; Ok(ChannelMessage { diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 20563272ab..16f91f0680 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -422,6 +422,29 @@ impl Client { } } + pub fn add_request_handler( + self: &Arc, + model: ModelHandle, + handler: H, + ) -> Subscription + where + M: RequestMessage, + E: Entity, + H: 'static + + Send + + Sync + + Fn(ModelHandle, TypedEnvelope, Arc, AsyncAppContext) -> F, + F: 'static + Future>, + { + self.add_message_handler(model, move |handle, envelope, this, cx| { + Self::respond_to_request( + envelope.receipt(), + handler(handle, envelope, this.clone(), cx), + this, + ) + }) + } + pub fn add_view_message_handler(self: &Arc, handler: H) where M: EntityMessage, diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 71e8de12e5..f025642b21 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -5,7 +5,7 @@ use anyhow::{anyhow, Context, Result}; use collections::{hash_map::Entry, BTreeSet, HashMap, HashSet}; use futures::{channel::mpsc, future, AsyncReadExt, Future, Stream, StreamExt}; use gpui::{AsyncAppContext, Entity, ImageData, ModelContext, ModelHandle, Task}; -use postage::{broadcast, sink::Sink, watch}; +use postage::{sink::Sink, watch}; use rpc::proto::{RequestMessage, UsersResponse}; use std::sync::{Arc, Weak}; use util::TryFutureExt as _; @@ -68,7 +68,7 @@ pub struct UserStore { outgoing_contact_requests: Vec>, pending_contact_requests: HashMap, invite_info: Option, - incoming_calls: broadcast::Sender, + incoming_calls: Vec>, client: Weak, http: Arc, _maintain_contacts: Task<()>, @@ -118,8 +118,8 @@ impl UserStore { client.add_message_handler(cx.handle(), Self::handle_update_contacts), client.add_message_handler(cx.handle(), Self::handle_update_invite_info), client.add_message_handler(cx.handle(), Self::handle_show_contacts), + client.add_request_handler(cx.handle(), Self::handle_incoming_call), ]; - let (incoming_calls, _) = broadcast::channel(32); Self { users: Default::default(), current_user: current_user_rx, @@ -127,7 +127,7 @@ impl UserStore { incoming_contact_requests: Default::default(), outgoing_contact_requests: Default::default(), invite_info: None, - incoming_calls, + incoming_calls: Default::default(), client: Arc::downgrade(&client), update_contacts_tx, http, @@ -148,7 +148,7 @@ impl UserStore { Status::Connected { .. } => { if let Some((this, user_id)) = this.upgrade(&cx).zip(client.user_id()) { let user = this - .update(&mut cx, |this, cx| this.fetch_user(user_id, cx)) + .update(&mut cx, |this, cx| this.get_user(user_id, cx)) .log_err() .await; current_user_tx.send(user).await.ok(); @@ -199,12 +199,41 @@ impl UserStore { Ok(()) } + async fn handle_incoming_call( + this: ModelHandle, + envelope: TypedEnvelope, + _: Arc, + mut cx: AsyncAppContext, + ) -> Result { + let call = Call { + room_id: envelope.payload.room_id, + participants: this + .update(&mut cx, |this, cx| { + this.get_users(envelope.payload.participant_user_ids, cx) + }) + .await?, + from: this + .update(&mut cx, |this, cx| { + this.get_user(envelope.payload.from_user_id, cx) + }) + .await?, + }; + this.update(&mut cx, |this, _| { + this.incoming_calls + .retain(|tx| tx.unbounded_send(call.clone()).is_ok()); + }); + + Ok(proto::Ack {}) + } + pub fn invite_info(&self) -> Option<&InviteInfo> { self.invite_info.as_ref() } - pub fn incoming_calls(&self) -> impl 'static + Stream { - self.incoming_calls.subscribe() + pub fn incoming_calls(&mut self) -> impl 'static + Stream { + let (tx, rx) = mpsc::unbounded(); + self.incoming_calls.push(tx); + rx } async fn handle_update_contacts( @@ -266,9 +295,7 @@ impl UserStore { for request in message.incoming_requests { incoming_requests.push({ let user = this - .update(&mut cx, |this, cx| { - this.fetch_user(request.requester_id, cx) - }) + .update(&mut cx, |this, cx| this.get_user(request.requester_id, cx)) .await?; (user, request.should_notify) }); @@ -277,7 +304,7 @@ impl UserStore { let mut outgoing_requests = Vec::new(); for requested_user_id in message.outgoing_requests { outgoing_requests.push( - this.update(&mut cx, |this, cx| this.fetch_user(requested_user_id, cx)) + this.update(&mut cx, |this, cx| this.get_user(requested_user_id, cx)) .await?, ); } @@ -518,19 +545,37 @@ impl UserStore { pub fn get_users( &mut self, - mut user_ids: Vec, + user_ids: Vec, cx: &mut ModelContext, - ) -> Task> { - user_ids.retain(|id| !self.users.contains_key(id)); - if user_ids.is_empty() { - Task::ready(Ok(())) - } else { - let load = self.load_users(proto::GetUsers { user_ids }, cx); - cx.foreground().spawn(async move { - load.await?; - Ok(()) + ) -> Task>>> { + let mut user_ids_to_fetch = user_ids.clone(); + user_ids_to_fetch.retain(|id| !self.users.contains_key(id)); + + cx.spawn(|this, mut cx| async move { + if !user_ids_to_fetch.is_empty() { + this.update(&mut cx, |this, cx| { + this.load_users( + proto::GetUsers { + user_ids: user_ids_to_fetch, + }, + cx, + ) + }) + .await?; + } + + this.read_with(&cx, |this, _| { + user_ids + .iter() + .map(|user_id| { + this.users + .get(user_id) + .cloned() + .ok_or_else(|| anyhow!("user {} not found", user_id)) + }) + .collect() }) - } + }) } pub fn fuzzy_search_users( @@ -541,7 +586,7 @@ impl UserStore { self.load_users(proto::FuzzySearchUsers { query }, cx) } - pub fn fetch_user( + pub fn get_user( &mut self, user_id: u64, cx: &mut ModelContext, @@ -621,7 +666,7 @@ impl Contact { ) -> Result { let user = user_store .update(cx, |user_store, cx| { - user_store.fetch_user(contact.user_id, cx) + user_store.get_user(contact.user_id, cx) }) .await?; let mut projects = Vec::new(); @@ -630,9 +675,7 @@ impl Contact { for participant_id in project.guests { guests.insert( user_store - .update(cx, |user_store, cx| { - user_store.fetch_user(participant_id, cx) - }) + .update(cx, |user_store, cx| user_store.get_user(participant_id, cx)) .await?, ); } diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index cc80f96ad8..741d53801a 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -100,16 +100,16 @@ async fn test_share_project_in_room( let mut incoming_calls_b = client_b .user_store - .read_with(cx_b, |user, _| user.incoming_calls()); - let user_b_joined = room_a.update(cx_a, |room, cx| { - room.invite(client_b.user_id().unwrap(), cx) - }); + .update(cx_b, |user, _| user.incoming_calls()); + room_a + .update(cx_a, |room, cx| room.call(client_b.user_id().unwrap(), cx)) + .await + .unwrap(); let call_b = incoming_calls_b.next().await.unwrap(); let room_b = cx_b .update(|cx| Room::join(call_b.room_id, client_b.clone(), cx)) .await .unwrap(); - user_b_joined.await.unwrap(); } #[gpui::test(iterations = 10)] @@ -512,7 +512,7 @@ async fn test_cancel_join_request( let user_b = client_a .user_store .update(cx_a, |store, cx| { - store.fetch_user(client_b.user_id().unwrap(), cx) + store.get_user(client_b.user_id().unwrap(), cx) }) .await .unwrap(); diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 6434a97de5..8de2d643c6 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -152,6 +152,7 @@ impl Server { server .add_request_handler(Server::ping) .add_request_handler(Server::create_room) + .add_request_handler(Server::call) .add_request_handler(Server::register_project) .add_request_handler(Server::unregister_project) .add_request_handler(Server::join_project) @@ -604,6 +605,68 @@ impl Server { Ok(()) } + async fn call( + self: Arc, + request: TypedEnvelope, + response: Response, + ) -> Result<()> { + let to_user_id = UserId::from_proto(request.payload.to_user_id); + let room_id = request.payload.room_id; + let (from_user_id, receiver_ids, room) = + self.store() + .await + .call(room_id, request.sender_id, to_user_id)?; + for participant in &room.participants { + self.peer + .send( + ConnectionId(participant.peer_id), + proto::RoomUpdated { + room: Some(room.clone()), + }, + ) + .trace_err(); + } + + let mut calls = receiver_ids + .into_iter() + .map(|receiver_id| { + self.peer.request( + receiver_id, + proto::IncomingCall { + room_id, + from_user_id: from_user_id.to_proto(), + participant_user_ids: room.participants.iter().map(|p| p.user_id).collect(), + }, + ) + }) + .collect::>(); + + while let Some(call_response) = calls.next().await { + match call_response.as_ref() { + Ok(_) => { + response.send(proto::Ack {})?; + return Ok(()); + } + Err(_) => { + call_response.trace_err(); + } + } + } + + let room = self.store().await.call_failed(room_id, to_user_id)?; + for participant in &room.participants { + self.peer + .send( + ConnectionId(participant.peer_id), + proto::RoomUpdated { + room: Some(room.clone()), + }, + ) + .trace_err(); + } + Err(anyhow!("failed to ring call recipient"))? + } + async fn register_project( self: Arc, request: TypedEnvelope, diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index 9ce6931477..20057aa8da 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -351,6 +351,45 @@ impl Store { Ok(room_id) } + pub fn call( + &mut self, + room_id: RoomId, + from_connection_id: ConnectionId, + to_user_id: UserId, + ) -> Result<(UserId, Vec, proto::Room)> { + let from_user_id = self.user_id_for_connection(from_connection_id)?; + let to_connection_ids = self.connection_ids_for_user(to_user_id).collect::>(); + let room = self + .rooms + .get_mut(&room_id) + .ok_or_else(|| anyhow!("no such room"))?; + anyhow::ensure!( + room.participants + .iter() + .any(|participant| participant.peer_id == from_connection_id.0), + "no such room" + ); + anyhow::ensure!( + room.pending_calls_to_user_ids + .iter() + .all(|user_id| UserId::from_proto(*user_id) != to_user_id), + "cannot call the same user more than once" + ); + room.pending_calls_to_user_ids.push(to_user_id.to_proto()); + + Ok((from_user_id, to_connection_ids, room.clone())) + } + + pub fn call_failed(&mut self, room_id: RoomId, to_user_id: UserId) -> Result { + let room = self + .rooms + .get_mut(&room_id) + .ok_or_else(|| anyhow!("no such room"))?; + room.pending_calls_to_user_ids + .retain(|user_id| UserId::from_proto(*user_id) != to_user_id); + Ok(room.clone()) + } + pub fn register_project( &mut self, host_connection_id: ConnectionId, diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 36d0b4835a..f6c20ff837 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4744,7 +4744,7 @@ impl Project { } else { let user_store = this.read_with(&cx, |this, _| this.user_store.clone()); let user = user_store - .update(&mut cx, |store, cx| store.fetch_user(user_id, cx)) + .update(&mut cx, |store, cx| store.get_user(user_id, cx)) .await?; this.update(&mut cx, |_, cx| cx.emit(Event::ContactRequestedJoin(user))); } @@ -4828,7 +4828,7 @@ impl Project { let user = this .update(&mut cx, |this, cx| { this.user_store.update(cx, |user_store, cx| { - user_store.fetch_user(envelope.payload.requester_id, cx) + user_store.get_user(envelope.payload.requester_id, cx) }) }) .await?; @@ -6258,7 +6258,7 @@ impl Collaborator { cx: &mut AsyncAppContext, ) -> impl Future> { let user = user_store.update(cx, |user_store, cx| { - user_store.fetch_user(message.user_id, cx) + user_store.get_user(message.user_id, cx) }); async move { diff --git a/crates/room/src/room.rs b/crates/room/src/room.rs index c444db4316..db114452c7 100644 --- a/crates/room/src/room.rs +++ b/crates/room/src/room.rs @@ -12,6 +12,11 @@ pub enum Event { PeerChangedActiveProject, } +pub enum CallResponse { + Accepted, + Rejected, +} + pub struct Room { id: u64, local_participant: LocalParticipant, @@ -43,7 +48,7 @@ impl Room { let response = client.request(proto::JoinRoom { id }).await?; let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?; let room = cx.add_model(|cx| Self::new(id, client, cx)); - room.update(&mut cx, |room, cx| room.refresh(room_proto, cx))?; + room.update(&mut cx, |room, cx| room.apply_update(room_proto, cx))?; Ok(room) }) } @@ -59,7 +64,7 @@ impl Room { } } - fn refresh(&mut self, room: proto::Room, cx: &mut ModelContext) -> Result<()> { + fn apply_update(&mut self, room: proto::Room, cx: &mut ModelContext) -> Result<()> { for participant in room.participants { self.remote_participants.insert( PeerId(participant.peer_id), @@ -70,11 +75,22 @@ impl Room { }, ); } + cx.notify(); Ok(()) } - pub fn invite(&mut self, user_id: u64, cx: &mut ModelContext) -> Task> { - todo!() + pub fn call(&mut self, to_user_id: u64, cx: &mut ModelContext) -> Task> { + let client = self.client.clone(); + let room_id = self.id; + cx.foreground().spawn(async move { + client + .request(proto::Call { + room_id, + to_user_id, + }) + .await?; + Ok(()) + }) } pub async fn publish_project(&mut self, project: ModelHandle) -> Result<()> { diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 3d8f7f9a6e..95a8ab2efb 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -16,7 +16,8 @@ message Envelope { JoinRoom join_room = 10; JoinRoomResponse join_room_response = 11; Call call = 12; - CallResponse call_response = 13; + IncomingCall incoming_call = 1000; + RespondToCall respond_to_call = 13; RoomUpdated room_updated = 14; RegisterProject register_project = 15; @@ -149,6 +150,7 @@ message JoinRoomResponse { message Room { repeated Participant participants = 1; + repeated uint64 pending_calls_to_user_ids = 2; } message Participant { @@ -171,9 +173,21 @@ message ParticipantLocation { message External {} } -message Call {} +message Call { + uint64 room_id = 1; + uint64 to_user_id = 2; +} -message CallResponse {} +message IncomingCall { + uint64 room_id = 1; + uint64 from_user_id = 2; + repeated uint64 participant_user_ids = 3; +} + +message RespondToCall { + uint64 room_id = 1; + bool accept = 2; +} message RoomUpdated { Room room = 1; diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 0e3de8878d..46247cc46a 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -83,6 +83,7 @@ messages!( (ApplyCompletionAdditionalEditsResponse, Background), (BufferReloaded, Foreground), (BufferSaved, Foreground), + (Call, Foreground), (ChannelMessageSent, Foreground), (CopyProjectEntry, Foreground), (CreateBufferForPeer, Foreground), @@ -117,6 +118,7 @@ messages!( (GetProjectSymbols, Background), (GetProjectSymbolsResponse, Background), (GetUsers, Foreground), + (IncomingCall, Foreground), (UsersResponse, Foreground), (JoinChannel, Foreground), (JoinChannelResponse, Foreground), @@ -151,6 +153,7 @@ messages!( (RequestJoinProject, Foreground), (RespondToContactRequest, Foreground), (RespondToJoinProjectRequest, Foreground), + (RoomUpdated, Foreground), (SaveBuffer, Foreground), (SearchProject, Background), (SearchProjectResponse, Background), @@ -179,6 +182,7 @@ request_messages!( ApplyCompletionAdditionalEdits, ApplyCompletionAdditionalEditsResponse ), + (Call, Ack), (CopyProjectEntry, ProjectEntryResponse), (CreateProjectEntry, ProjectEntryResponse), (CreateRoom, CreateRoomResponse), @@ -200,6 +204,7 @@ request_messages!( (JoinChannel, JoinChannelResponse), (JoinProject, JoinProjectResponse), (JoinRoom, JoinRoomResponse), + (IncomingCall, Ack), (OpenBufferById, OpenBufferResponse), (OpenBufferByPath, OpenBufferResponse), (OpenBufferForSymbol, OpenBufferForSymbolResponse), From 55b095cbd39bfef35d7f2ba2f06847c1acfd8f8d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 26 Sep 2022 10:34:26 +0200 Subject: [PATCH 006/314] Implement joining a room and sending updates after people join/leave --- crates/client/src/user.rs | 31 ++++++---- crates/collab/src/integration_tests.rs | 39 ++++++++++-- crates/collab/src/rpc.rs | 84 +++++++++++++++++--------- crates/collab/src/rpc/store.rs | 60 +++++++++++++++--- crates/room/src/room.rs | 57 ++++++++++++----- crates/rpc/proto/zed.proto | 8 ++- crates/rpc/src/proto.rs | 3 + 7 files changed, 211 insertions(+), 71 deletions(-) diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index f025642b21..0e09c7636a 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -1,9 +1,8 @@ -use crate::call::Call; - use super::{http::HttpClient, proto, Client, Status, TypedEnvelope}; +use crate::call::Call; use anyhow::{anyhow, Context, Result}; use collections::{hash_map::Entry, BTreeSet, HashMap, HashSet}; -use futures::{channel::mpsc, future, AsyncReadExt, Future, Stream, StreamExt}; +use futures::{channel::mpsc, future, AsyncReadExt, Future, StreamExt}; use gpui::{AsyncAppContext, Entity, ImageData, ModelContext, ModelHandle, Task}; use postage::{sink::Sink, watch}; use rpc::proto::{RequestMessage, UsersResponse}; @@ -68,7 +67,7 @@ pub struct UserStore { outgoing_contact_requests: Vec>, pending_contact_requests: HashMap, invite_info: Option, - incoming_calls: Vec>, + incoming_call: (watch::Sender>, watch::Receiver>), client: Weak, http: Arc, _maintain_contacts: Task<()>, @@ -119,6 +118,7 @@ impl UserStore { client.add_message_handler(cx.handle(), Self::handle_update_invite_info), client.add_message_handler(cx.handle(), Self::handle_show_contacts), client.add_request_handler(cx.handle(), Self::handle_incoming_call), + client.add_message_handler(cx.handle(), Self::handle_cancel_call), ]; Self { users: Default::default(), @@ -127,7 +127,7 @@ impl UserStore { incoming_contact_requests: Default::default(), outgoing_contact_requests: Default::default(), invite_info: None, - incoming_calls: Default::default(), + incoming_call: watch::channel(), client: Arc::downgrade(&client), update_contacts_tx, http, @@ -219,21 +219,30 @@ impl UserStore { .await?, }; this.update(&mut cx, |this, _| { - this.incoming_calls - .retain(|tx| tx.unbounded_send(call.clone()).is_ok()); + *this.incoming_call.0.borrow_mut() = Some(call); }); Ok(proto::Ack {}) } + async fn handle_cancel_call( + this: ModelHandle, + _: TypedEnvelope, + _: Arc, + mut cx: AsyncAppContext, + ) -> Result<()> { + this.update(&mut cx, |this, _| { + *this.incoming_call.0.borrow_mut() = None; + }); + Ok(()) + } + pub fn invite_info(&self) -> Option<&InviteInfo> { self.invite_info.as_ref() } - pub fn incoming_calls(&mut self) -> impl 'static + Stream { - let (tx, rx) = mpsc::unbounded(); - self.incoming_calls.push(tx); - rx + pub fn incoming_call(&self) -> watch::Receiver> { + self.incoming_call.1.clone() } async fn handle_update_contacts( diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 741d53801a..63a2efa0fb 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -98,18 +98,49 @@ async fn test_share_project_in_room( let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; // room.publish_project(project_a.clone()).await.unwrap(); - let mut incoming_calls_b = client_b + let mut incoming_call_b = client_b .user_store - .update(cx_b, |user, _| user.incoming_calls()); + .update(cx_b, |user, _| user.incoming_call()); room_a .update(cx_a, |room, cx| room.call(client_b.user_id().unwrap(), cx)) .await .unwrap(); - let call_b = incoming_calls_b.next().await.unwrap(); + let call_b = incoming_call_b.next().await.unwrap().unwrap(); let room_b = cx_b - .update(|cx| Room::join(call_b.room_id, client_b.clone(), cx)) + .update(|cx| Room::join(&call_b, client_b.clone(), cx)) .await .unwrap(); + assert!(incoming_call_b.next().await.unwrap().is_none()); + assert_eq!( + remote_participants(&room_a, &client_a, cx_a).await, + vec!["user_b"] + ); + assert_eq!( + remote_participants(&room_b, &client_b, cx_b).await, + vec!["user_a"] + ); + + async fn remote_participants( + room: &ModelHandle, + client: &TestClient, + cx: &mut TestAppContext, + ) -> Vec { + let users = room.update(cx, |room, cx| { + room.remote_participants() + .values() + .map(|participant| { + client + .user_store + .update(cx, |users, cx| users.get_user(participant.user_id, cx)) + }) + .collect::>() + }); + let users = futures::future::try_join_all(users).await.unwrap(); + users + .into_iter() + .map(|user| user.github_login.clone()) + .collect() + } } #[gpui::test(iterations = 10)] diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 8de2d643c6..dfaaa8a03d 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -152,6 +152,7 @@ impl Server { server .add_request_handler(Server::ping) .add_request_handler(Server::create_room) + .add_request_handler(Server::join_room) .add_request_handler(Server::call) .add_request_handler(Server::register_project) .add_request_handler(Server::unregister_project) @@ -605,6 +606,26 @@ impl Server { Ok(()) } + async fn join_room( + self: Arc, + request: TypedEnvelope, + response: Response, + ) -> Result<()> { + let room_id = request.payload.id; + let mut store = self.store().await; + let (room, recipient_ids) = store.join_room(room_id, request.sender_id)?; + for receiver_id in recipient_ids { + self.peer + .send(receiver_id, proto::CancelCall {}) + .trace_err(); + } + response.send(proto::JoinRoomResponse { + room: Some(room.clone()), + })?; + self.room_updated(room); + Ok(()) + } + async fn call( self: Arc, request: TypedEnvelope, @@ -612,34 +633,29 @@ impl Server { ) -> Result<()> { let to_user_id = UserId::from_proto(request.payload.to_user_id); let room_id = request.payload.room_id; - let (from_user_id, receiver_ids, room) = - self.store() - .await - .call(room_id, request.sender_id, to_user_id)?; - for participant in &room.participants { - self.peer - .send( - ConnectionId(participant.peer_id), - proto::RoomUpdated { - room: Some(room.clone()), - }, - ) - .trace_err(); - } - - let mut calls = receiver_ids - .into_iter() - .map(|receiver_id| { - self.peer.request( - receiver_id, - proto::IncomingCall { - room_id, - from_user_id: from_user_id.to_proto(), - participant_user_ids: room.participants.iter().map(|p| p.user_id).collect(), - }, - ) - }) - .collect::>(); + let mut calls = { + let mut store = self.store().await; + let (from_user_id, recipient_ids, room) = + store.call(room_id, request.sender_id, to_user_id)?; + self.room_updated(room); + recipient_ids + .into_iter() + .map(|recipient_id| { + self.peer.request( + recipient_id, + proto::IncomingCall { + room_id, + from_user_id: from_user_id.to_proto(), + participant_user_ids: room + .participants + .iter() + .map(|p| p.user_id) + .collect(), + }, + ) + }) + .collect::>() + }; while let Some(call_response) = calls.next().await { match call_response.as_ref() { @@ -653,7 +669,16 @@ impl Server { } } - let room = self.store().await.call_failed(room_id, to_user_id)?; + { + let mut store = self.store().await; + let room = store.call_failed(room_id, to_user_id)?; + self.room_updated(&room); + } + + Err(anyhow!("failed to ring call recipient"))? + } + + fn room_updated(&self, room: &proto::Room) { for participant in &room.participants { self.peer .send( @@ -664,7 +689,6 @@ impl Server { ) .trace_err(); } - Err(anyhow!("failed to ring call recipient"))? } async fn register_project( diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index 20057aa8da..6b75691802 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -25,7 +25,7 @@ pub struct Store { struct ConnectionState { user_id: UserId, admin: bool, - rooms: BTreeSet, + room: Option, projects: BTreeSet, requested_projects: HashSet, channels: HashSet, @@ -140,7 +140,7 @@ impl Store { ConnectionState { user_id, admin, - rooms: Default::default(), + room: Default::default(), projects: Default::default(), requested_projects: Default::default(), channels: Default::default(), @@ -333,6 +333,11 @@ impl Store { .connections .get_mut(&creator_connection_id) .ok_or_else(|| anyhow!("no such connection"))?; + anyhow::ensure!( + connection.room.is_none(), + "cannot participate in more than one room at once" + ); + let mut room = proto::Room::default(); room.participants.push(proto::Participant { user_id: connection.user_id.to_proto(), @@ -347,16 +352,57 @@ impl Store { let room_id = post_inc(&mut self.next_room_id); self.rooms.insert(room_id, room); - connection.rooms.insert(room_id); + connection.room = Some(room_id); Ok(room_id) } + pub fn join_room( + &mut self, + room_id: u64, + connection_id: ConnectionId, + ) -> Result<(&proto::Room, Vec)> { + let connection = self + .connections + .get_mut(&connection_id) + .ok_or_else(|| anyhow!("no such connection"))?; + anyhow::ensure!( + connection.room.is_none(), + "cannot participate in more than one room at once" + ); + + let user_id = connection.user_id; + let recipient_ids = self.connection_ids_for_user(user_id).collect::>(); + + let room = self + .rooms + .get_mut(&room_id) + .ok_or_else(|| anyhow!("no such room"))?; + anyhow::ensure!( + room.pending_calls_to_user_ids.contains(&user_id.to_proto()), + anyhow!("no such room") + ); + room.pending_calls_to_user_ids + .retain(|pending| *pending != user_id.to_proto()); + room.participants.push(proto::Participant { + user_id: user_id.to_proto(), + peer_id: connection_id.0, + project_ids: Default::default(), + location: Some(proto::ParticipantLocation { + variant: Some(proto::participant_location::Variant::External( + proto::participant_location::External {}, + )), + }), + }); + + Ok((room, recipient_ids)) + } + pub fn call( &mut self, room_id: RoomId, from_connection_id: ConnectionId, to_user_id: UserId, - ) -> Result<(UserId, Vec, proto::Room)> { + ) -> Result<(UserId, Vec, &proto::Room)> { let from_user_id = self.user_id_for_connection(from_connection_id)?; let to_connection_ids = self.connection_ids_for_user(to_user_id).collect::>(); let room = self @@ -377,17 +423,17 @@ impl Store { ); room.pending_calls_to_user_ids.push(to_user_id.to_proto()); - Ok((from_user_id, to_connection_ids, room.clone())) + Ok((from_user_id, to_connection_ids, room)) } - pub fn call_failed(&mut self, room_id: RoomId, to_user_id: UserId) -> Result { + pub fn call_failed(&mut self, room_id: RoomId, to_user_id: UserId) -> Result<&proto::Room> { let room = self .rooms .get_mut(&room_id) .ok_or_else(|| anyhow!("no such room"))?; room.pending_calls_to_user_ids .retain(|user_id| UserId::from_proto(*user_id) != to_user_id); - Ok(room.clone()) + Ok(room) } pub fn register_project( diff --git a/crates/room/src/room.rs b/crates/room/src/room.rs index db114452c7..78de994978 100644 --- a/crates/room/src/room.rs +++ b/crates/room/src/room.rs @@ -1,9 +1,9 @@ mod participant; use anyhow::{anyhow, Result}; -use client::{proto, Client, PeerId}; +use client::{call::Call, proto, Client, PeerId, TypedEnvelope}; use collections::HashMap; -use gpui::{Entity, ModelContext, ModelHandle, MutableAppContext, Task}; +use gpui::{AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task}; use participant::{LocalParticipant, ParticipantLocation, RemoteParticipant}; use project::Project; use std::sync::Arc; @@ -22,6 +22,7 @@ pub struct Room { local_participant: LocalParticipant, remote_participants: HashMap, client: Arc, + _subscriptions: Vec, } impl Entity for Room { @@ -40,40 +41,64 @@ impl Room { } pub fn join( - id: u64, + call: &Call, client: Arc, cx: &mut MutableAppContext, ) -> Task>> { + let room_id = call.room_id; cx.spawn(|mut cx| async move { - let response = client.request(proto::JoinRoom { id }).await?; + let response = client.request(proto::JoinRoom { id: room_id }).await?; let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?; - let room = cx.add_model(|cx| Self::new(id, client, cx)); - room.update(&mut cx, |room, cx| room.apply_update(room_proto, cx))?; + let room = cx.add_model(|cx| Self::new(room_id, client, cx)); + room.update(&mut cx, |room, cx| room.apply_room_update(room_proto, cx))?; Ok(room) }) } - fn new(id: u64, client: Arc, _: &mut ModelContext) -> Self { + fn new(id: u64, client: Arc, cx: &mut ModelContext) -> Self { Self { id, local_participant: LocalParticipant { projects: Default::default(), }, remote_participants: Default::default(), + _subscriptions: vec![client.add_message_handler(cx.handle(), Self::handle_room_updated)], client, } } - fn apply_update(&mut self, room: proto::Room, cx: &mut ModelContext) -> Result<()> { + pub fn remote_participants(&self) -> &HashMap { + &self.remote_participants + } + + async fn handle_room_updated( + this: ModelHandle, + envelope: TypedEnvelope, + _: Arc, + mut cx: AsyncAppContext, + ) -> Result<()> { + let room = envelope + .payload + .room + .ok_or_else(|| anyhow!("invalid room"))?; + this.update(&mut cx, |this, cx| this.apply_room_update(room, cx))?; + Ok(()) + } + + fn apply_room_update(&mut self, room: proto::Room, cx: &mut ModelContext) -> Result<()> { + // TODO: compute diff instead of clearing participants + self.remote_participants.clear(); for participant in room.participants { - self.remote_participants.insert( - PeerId(participant.peer_id), - RemoteParticipant { - user_id: participant.user_id, - projects: Default::default(), // TODO: populate projects - location: ParticipantLocation::from_proto(participant.location)?, - }, - ); + if Some(participant.user_id) != self.client.user_id() { + self.remote_participants.insert( + PeerId(participant.peer_id), + RemoteParticipant { + user_id: participant.user_id, + projects: Default::default(), // TODO: populate projects + location: ParticipantLocation::from_proto(participant.location)?, + }, + ); + } } cx.notify(); Ok(()) diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 95a8ab2efb..6c0c929f82 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -17,7 +17,8 @@ message Envelope { JoinRoomResponse join_room_response = 11; Call call = 12; IncomingCall incoming_call = 1000; - RespondToCall respond_to_call = 13; + CancelCall cancel_call = 1001; + DeclineCall decline_call = 13; RoomUpdated room_updated = 14; RegisterProject register_project = 15; @@ -184,9 +185,10 @@ message IncomingCall { repeated uint64 participant_user_ids = 3; } -message RespondToCall { +message CancelCall {} + +message DeclineCall { uint64 room_id = 1; - bool accept = 2; } message RoomUpdated { diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 46247cc46a..94690e29e1 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -84,12 +84,14 @@ messages!( (BufferReloaded, Foreground), (BufferSaved, Foreground), (Call, Foreground), + (CancelCall, Foreground), (ChannelMessageSent, Foreground), (CopyProjectEntry, Foreground), (CreateBufferForPeer, Foreground), (CreateProjectEntry, Foreground), (CreateRoom, Foreground), (CreateRoomResponse, Foreground), + (DeclineCall, Foreground), (DeleteProjectEntry, Foreground), (Error, Foreground), (Follow, Foreground), @@ -186,6 +188,7 @@ request_messages!( (CopyProjectEntry, ProjectEntryResponse), (CreateProjectEntry, ProjectEntryResponse), (CreateRoom, CreateRoomResponse), + (DeclineCall, Ack), (DeleteProjectEntry, ProjectEntryResponse), (Follow, FollowResponse), (FormatBuffers, FormatBuffersResponse), From f4697ff4d14300b9a18619b1a113faa2cac2e8c7 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 26 Sep 2022 11:13:34 +0200 Subject: [PATCH 007/314] Prevent the same user from being called more than once --- crates/collab/src/rpc/store.rs | 85 ++++++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 20 deletions(-) diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index 6b75691802..d19ae122e0 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -13,7 +13,7 @@ pub type RoomId = u64; #[derive(Default, Serialize)] pub struct Store { connections: BTreeMap, - connections_by_user_id: BTreeMap>, + connections_by_user_id: BTreeMap, next_room_id: RoomId, rooms: BTreeMap, projects: BTreeMap, @@ -21,16 +21,27 @@ pub struct Store { channels: BTreeMap, } +#[derive(Default, Serialize)] +struct UserConnectionState { + connection_ids: HashSet, + room: Option, +} + #[derive(Serialize)] struct ConnectionState { user_id: UserId, admin: bool, - room: Option, projects: BTreeSet, requested_projects: HashSet, channels: HashSet, } +#[derive(Copy, Clone, Eq, PartialEq, Serialize)] +enum RoomState { + Joined, + Calling { room_id: RoomId }, +} + #[derive(Serialize)] pub struct Project { pub online: bool, @@ -140,7 +151,6 @@ impl Store { ConnectionState { user_id, admin, - room: Default::default(), projects: Default::default(), requested_projects: Default::default(), channels: Default::default(), @@ -149,6 +159,7 @@ impl Store { self.connections_by_user_id .entry(user_id) .or_default() + .connection_ids .insert(connection_id); } @@ -185,9 +196,9 @@ impl Store { } } - let user_connections = self.connections_by_user_id.get_mut(&user_id).unwrap(); - user_connections.remove(&connection_id); - if user_connections.is_empty() { + let user_connection_state = self.connections_by_user_id.get_mut(&user_id).unwrap(); + user_connection_state.connection_ids.remove(&connection_id); + if user_connection_state.connection_ids.is_empty() { self.connections_by_user_id.remove(&user_id); } @@ -239,6 +250,7 @@ impl Store { self.connections_by_user_id .get(&user_id) .into_iter() + .map(|state| &state.connection_ids) .flatten() .copied() } @@ -248,6 +260,7 @@ impl Store { .connections_by_user_id .get(&user_id) .unwrap_or(&Default::default()) + .connection_ids .is_empty() } @@ -295,9 +308,10 @@ impl Store { } pub fn project_metadata_for_user(&self, user_id: UserId) -> Vec { - let connection_ids = self.connections_by_user_id.get(&user_id); - let project_ids = connection_ids.iter().flat_map(|connection_ids| { - connection_ids + let user_connection_state = self.connections_by_user_id.get(&user_id); + let project_ids = user_connection_state.iter().flat_map(|state| { + state + .connection_ids .iter() .filter_map(|connection_id| self.connections.get(connection_id)) .flat_map(|connection| connection.projects.iter().copied()) @@ -333,8 +347,12 @@ impl Store { .connections .get_mut(&creator_connection_id) .ok_or_else(|| anyhow!("no such connection"))?; + let user_connection_state = self + .connections_by_user_id + .get_mut(&connection.user_id) + .ok_or_else(|| anyhow!("no such connection"))?; anyhow::ensure!( - connection.room.is_none(), + user_connection_state.room.is_none(), "cannot participate in more than one room at once" ); @@ -352,7 +370,7 @@ impl Store { let room_id = post_inc(&mut self.next_room_id); self.rooms.insert(room_id, room); - connection.room = Some(room_id); + user_connection_state.room = Some(RoomState::Joined); Ok(room_id) } @@ -365,14 +383,20 @@ impl Store { .connections .get_mut(&connection_id) .ok_or_else(|| anyhow!("no such connection"))?; - anyhow::ensure!( - connection.room.is_none(), - "cannot participate in more than one room at once" - ); - let user_id = connection.user_id; let recipient_ids = self.connection_ids_for_user(user_id).collect::>(); + let mut user_connection_state = self + .connections_by_user_id + .get_mut(&user_id) + .ok_or_else(|| anyhow!("no such connection"))?; + anyhow::ensure!( + user_connection_state + .room + .map_or(true, |room| room == RoomState::Calling { room_id }), + "cannot participate in more than one room at once" + ); + let room = self .rooms .get_mut(&room_id) @@ -393,6 +417,7 @@ impl Store { )), }), }); + user_connection_state.room = Some(RoomState::Joined); Ok((room, recipient_ids)) } @@ -404,7 +429,17 @@ impl Store { to_user_id: UserId, ) -> Result<(UserId, Vec, &proto::Room)> { let from_user_id = self.user_id_for_connection(from_connection_id)?; + let to_connection_ids = self.connection_ids_for_user(to_user_id).collect::>(); + let mut to_user_connection_state = self + .connections_by_user_id + .get_mut(&to_user_id) + .ok_or_else(|| anyhow!("no such connection"))?; + anyhow::ensure!( + to_user_connection_state.room.is_none(), + "recipient is already on another call" + ); + let room = self .rooms .get_mut(&room_id) @@ -422,11 +457,18 @@ impl Store { "cannot call the same user more than once" ); room.pending_calls_to_user_ids.push(to_user_id.to_proto()); + to_user_connection_state.room = Some(RoomState::Calling { room_id }); Ok((from_user_id, to_connection_ids, room)) } pub fn call_failed(&mut self, room_id: RoomId, to_user_id: UserId) -> Result<&proto::Room> { + let mut to_user_connection_state = self + .connections_by_user_id + .get_mut(&to_user_id) + .ok_or_else(|| anyhow!("no such connection"))?; + anyhow::ensure!(to_user_connection_state.room == Some(RoomState::Calling { room_id })); + to_user_connection_state.room = None; let room = self .rooms .get_mut(&room_id) @@ -548,10 +590,12 @@ impl Store { } for requester_user_id in project.join_requests.keys() { - if let Some(requester_connection_ids) = + if let Some(requester_user_connection_state) = self.connections_by_user_id.get_mut(requester_user_id) { - for requester_connection_id in requester_connection_ids.iter() { + for requester_connection_id in + &requester_user_connection_state.connection_ids + { if let Some(requester_connection) = self.connections.get_mut(requester_connection_id) { @@ -907,11 +951,12 @@ impl Store { .connections_by_user_id .get(&connection.user_id) .unwrap() + .connection_ids .contains(connection_id)); } - for (user_id, connection_ids) in &self.connections_by_user_id { - for connection_id in connection_ids { + for (user_id, state) in &self.connections_by_user_id { + for connection_id in &state.connection_ids { assert_eq!( self.connections.get(connection_id).unwrap().user_id, *user_id From bb9ce86a29c300fdcf227dcc35a6d3748371d078 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 26 Sep 2022 11:56:19 +0200 Subject: [PATCH 008/314] Introduce the ability of declining calls --- crates/client/src/user.rs | 11 +++ crates/collab/src/integration_tests.rs | 120 ++++++++++++++++++++++--- crates/collab/src/rpc.rs | 26 ++++-- crates/collab/src/rpc/store.rs | 40 +++++++-- crates/room/src/room.rs | 7 ++ crates/rpc/proto/zed.proto | 6 +- 6 files changed, 181 insertions(+), 29 deletions(-) diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 0e09c7636a..5be0125ff8 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -245,6 +245,17 @@ impl UserStore { self.incoming_call.1.clone() } + pub fn decline_call(&mut self) -> Result<()> { + let mut incoming_call = self.incoming_call.0.borrow_mut(); + if incoming_call.is_some() { + if let Some(client) = self.client.upgrade() { + client.send(proto::DeclineCall {})?; + } + *incoming_call = None; + } + Ok(()) + } + async fn handle_update_contacts( this: ModelHandle, message: TypedEnvelope, diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 63a2efa0fb..550a13a2a9 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -66,13 +66,15 @@ async fn test_share_project_in_room( deterministic: Arc, cx_a: &mut TestAppContext, cx_b: &mut TestAppContext, + cx_c: &mut TestAppContext, ) { deterministic.forbid_parking(); let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; + let client_c = server.create_client(cx_c, "user_c").await; server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; client_a @@ -95,6 +97,13 @@ async fn test_share_project_in_room( .update(|cx| Room::create(client_a.clone(), cx)) .await .unwrap(); + assert_eq!( + participants(&room_a, &client_a, cx_a).await, + RoomParticipants { + remote: Default::default(), + pending: Default::default() + } + ); let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; // room.publish_project(project_a.clone()).await.unwrap(); @@ -105,27 +114,94 @@ async fn test_share_project_in_room( .update(cx_a, |room, cx| room.call(client_b.user_id().unwrap(), cx)) .await .unwrap(); + assert_eq!( + participants(&room_a, &client_a, cx_a).await, + RoomParticipants { + remote: Default::default(), + pending: vec!["user_b".to_string()] + } + ); + let call_b = incoming_call_b.next().await.unwrap().unwrap(); let room_b = cx_b .update(|cx| Room::join(&call_b, client_b.clone(), cx)) .await .unwrap(); assert!(incoming_call_b.next().await.unwrap().is_none()); + + deterministic.run_until_parked(); assert_eq!( - remote_participants(&room_a, &client_a, cx_a).await, - vec!["user_b"] + participants(&room_a, &client_a, cx_a).await, + RoomParticipants { + remote: vec!["user_b".to_string()], + pending: Default::default() + } ); assert_eq!( - remote_participants(&room_b, &client_b, cx_b).await, - vec!["user_a"] + participants(&room_b, &client_b, cx_b).await, + RoomParticipants { + remote: vec!["user_a".to_string()], + pending: Default::default() + } ); - async fn remote_participants( + let mut incoming_call_c = client_c + .user_store + .update(cx_c, |user, _| user.incoming_call()); + room_a + .update(cx_a, |room, cx| room.call(client_c.user_id().unwrap(), cx)) + .await + .unwrap(); + assert_eq!( + participants(&room_a, &client_a, cx_a).await, + RoomParticipants { + remote: vec!["user_b".to_string()], + pending: vec!["user_c".to_string()] + } + ); + assert_eq!( + participants(&room_b, &client_b, cx_b).await, + RoomParticipants { + remote: vec!["user_a".to_string()], + pending: vec!["user_c".to_string()] + } + ); + let _call_c = incoming_call_c.next().await.unwrap().unwrap(); + + client_c + .user_store + .update(cx_c, |user, _| user.decline_call()) + .unwrap(); + assert!(incoming_call_c.next().await.unwrap().is_none()); + + deterministic.run_until_parked(); + assert_eq!( + participants(&room_a, &client_a, cx_a).await, + RoomParticipants { + remote: vec!["user_b".to_string()], + pending: Default::default() + } + ); + assert_eq!( + participants(&room_b, &client_b, cx_b).await, + RoomParticipants { + remote: vec!["user_a".to_string()], + pending: Default::default() + } + ); + + #[derive(Debug, Eq, PartialEq)] + struct RoomParticipants { + remote: Vec, + pending: Vec, + } + + async fn participants( room: &ModelHandle, client: &TestClient, cx: &mut TestAppContext, - ) -> Vec { - let users = room.update(cx, |room, cx| { + ) -> RoomParticipants { + let remote_users = room.update(cx, |room, cx| { room.remote_participants() .values() .map(|participant| { @@ -135,11 +211,29 @@ async fn test_share_project_in_room( }) .collect::>() }); - let users = futures::future::try_join_all(users).await.unwrap(); - users - .into_iter() - .map(|user| user.github_login.clone()) - .collect() + let remote_users = futures::future::try_join_all(remote_users).await.unwrap(); + let pending_users = room.update(cx, |room, cx| { + room.pending_user_ids() + .iter() + .map(|user_id| { + client + .user_store + .update(cx, |users, cx| users.get_user(*user_id, cx)) + }) + .collect::>() + }); + let pending_users = futures::future::try_join_all(pending_users).await.unwrap(); + + RoomParticipants { + remote: remote_users + .into_iter() + .map(|user| user.github_login.clone()) + .collect(), + pending: pending_users + .into_iter() + .map(|user| user.github_login.clone()) + .collect(), + } } } diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index dfaaa8a03d..fb8bbdb85a 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -154,6 +154,7 @@ impl Server { .add_request_handler(Server::create_room) .add_request_handler(Server::join_room) .add_request_handler(Server::call) + .add_message_handler(Server::decline_call) .add_request_handler(Server::register_project) .add_request_handler(Server::unregister_project) .add_request_handler(Server::join_project) @@ -613,10 +614,10 @@ impl Server { ) -> Result<()> { let room_id = request.payload.id; let mut store = self.store().await; - let (room, recipient_ids) = store.join_room(room_id, request.sender_id)?; - for receiver_id in recipient_ids { + let (room, recipient_connection_ids) = store.join_room(room_id, request.sender_id)?; + for recipient_id in recipient_connection_ids { self.peer - .send(receiver_id, proto::CancelCall {}) + .send(recipient_id, proto::CancelCall {}) .trace_err(); } response.send(proto::JoinRoomResponse { @@ -635,10 +636,10 @@ impl Server { let room_id = request.payload.room_id; let mut calls = { let mut store = self.store().await; - let (from_user_id, recipient_ids, room) = + let (from_user_id, recipient_connection_ids, room) = store.call(room_id, request.sender_id, to_user_id)?; self.room_updated(room); - recipient_ids + recipient_connection_ids .into_iter() .map(|recipient_id| { self.peer.request( @@ -678,6 +679,21 @@ impl Server { Err(anyhow!("failed to ring call recipient"))? } + async fn decline_call( + self: Arc, + message: TypedEnvelope, + ) -> Result<()> { + let mut store = self.store().await; + let (room, recipient_connection_ids) = store.call_declined(message.sender_id)?; + for recipient_id in recipient_connection_ids { + self.peer + .send(recipient_id, proto::CancelCall {}) + .trace_err(); + } + self.room_updated(room); + Ok(()) + } + fn room_updated(&self, room: &proto::Room) { for participant in &room.participants { self.peer diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index d19ae122e0..fc8576224b 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -384,7 +384,7 @@ impl Store { .get_mut(&connection_id) .ok_or_else(|| anyhow!("no such connection"))?; let user_id = connection.user_id; - let recipient_ids = self.connection_ids_for_user(user_id).collect::>(); + let recipient_connection_ids = self.connection_ids_for_user(user_id).collect::>(); let mut user_connection_state = self .connections_by_user_id @@ -402,10 +402,10 @@ impl Store { .get_mut(&room_id) .ok_or_else(|| anyhow!("no such room"))?; anyhow::ensure!( - room.pending_calls_to_user_ids.contains(&user_id.to_proto()), + room.pending_user_ids.contains(&user_id.to_proto()), anyhow!("no such room") ); - room.pending_calls_to_user_ids + room.pending_user_ids .retain(|pending| *pending != user_id.to_proto()); room.participants.push(proto::Participant { user_id: user_id.to_proto(), @@ -419,7 +419,7 @@ impl Store { }); user_connection_state.room = Some(RoomState::Joined); - Ok((room, recipient_ids)) + Ok((room, recipient_connection_ids)) } pub fn call( @@ -451,12 +451,12 @@ impl Store { "no such room" ); anyhow::ensure!( - room.pending_calls_to_user_ids + room.pending_user_ids .iter() .all(|user_id| UserId::from_proto(*user_id) != to_user_id), "cannot call the same user more than once" ); - room.pending_calls_to_user_ids.push(to_user_id.to_proto()); + room.pending_user_ids.push(to_user_id.to_proto()); to_user_connection_state.room = Some(RoomState::Calling { room_id }); Ok((from_user_id, to_connection_ids, room)) @@ -473,11 +473,37 @@ impl Store { .rooms .get_mut(&room_id) .ok_or_else(|| anyhow!("no such room"))?; - room.pending_calls_to_user_ids + room.pending_user_ids .retain(|user_id| UserId::from_proto(*user_id) != to_user_id); Ok(room) } + pub fn call_declined( + &mut self, + recipient_connection_id: ConnectionId, + ) -> Result<(&proto::Room, Vec)> { + let recipient_user_id = self.user_id_for_connection(recipient_connection_id)?; + let mut to_user_connection_state = self + .connections_by_user_id + .get_mut(&recipient_user_id) + .ok_or_else(|| anyhow!("no such connection"))?; + if let Some(RoomState::Calling { room_id }) = to_user_connection_state.room { + to_user_connection_state.room = None; + let recipient_connection_ids = self + .connection_ids_for_user(recipient_user_id) + .collect::>(); + let room = self + .rooms + .get_mut(&room_id) + .ok_or_else(|| anyhow!("no such room"))?; + room.pending_user_ids + .retain(|user_id| UserId::from_proto(*user_id) != recipient_user_id); + Ok((room, recipient_connection_ids)) + } else { + Err(anyhow!("user is not being called")) + } + } + pub fn register_project( &mut self, host_connection_id: ConnectionId, diff --git a/crates/room/src/room.rs b/crates/room/src/room.rs index 78de994978..6dddfeda3f 100644 --- a/crates/room/src/room.rs +++ b/crates/room/src/room.rs @@ -21,6 +21,7 @@ pub struct Room { id: u64, local_participant: LocalParticipant, remote_participants: HashMap, + pending_user_ids: Vec, client: Arc, _subscriptions: Vec, } @@ -62,6 +63,7 @@ impl Room { projects: Default::default(), }, remote_participants: Default::default(), + pending_user_ids: Default::default(), _subscriptions: vec![client.add_message_handler(cx.handle(), Self::handle_room_updated)], client, } @@ -71,6 +73,10 @@ impl Room { &self.remote_participants } + pub fn pending_user_ids(&self) -> &[u64] { + &self.pending_user_ids + } + async fn handle_room_updated( this: ModelHandle, envelope: TypedEnvelope, @@ -100,6 +106,7 @@ impl Room { ); } } + self.pending_user_ids = room.pending_user_ids; cx.notify(); Ok(()) } diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 6c0c929f82..bcc762283d 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -151,7 +151,7 @@ message JoinRoomResponse { message Room { repeated Participant participants = 1; - repeated uint64 pending_calls_to_user_ids = 2; + repeated uint64 pending_user_ids = 2; } message Participant { @@ -187,9 +187,7 @@ message IncomingCall { message CancelCall {} -message DeclineCall { - uint64 room_id = 1; -} +message DeclineCall {} message RoomUpdated { Room room = 1; From df285def59c816fb7966ba05a50744d8aee48789 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 26 Sep 2022 12:02:54 +0200 Subject: [PATCH 009/314] :lipstick: --- crates/collab/src/integration_tests.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 550a13a2a9..2432618199 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -74,7 +74,11 @@ async fn test_share_project_in_room( let client_b = server.create_client(cx_b, "user_b").await; let client_c = server.create_client(cx_c, "user_c").await; server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) + .make_contacts(vec![ + (&client_a, cx_a), + (&client_b, cx_b), + (&client_c, cx_c), + ]) .await; client_a @@ -166,8 +170,8 @@ async fn test_share_project_in_room( pending: vec!["user_c".to_string()] } ); - let _call_c = incoming_call_c.next().await.unwrap().unwrap(); + let _call_c = incoming_call_c.next().await.unwrap().unwrap(); client_c .user_store .update(cx_c, |user, _| user.decline_call()) From 573086eed2fb0ae81703b8a66bce0f4cc5cf81c4 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 26 Sep 2022 12:05:59 +0200 Subject: [PATCH 010/314] Always rely on the server to cancel the incoming call --- crates/client/src/user.rs | 8 ++------ crates/collab/src/integration_tests.rs | 4 ++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 5be0125ff8..57a403ebad 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -246,12 +246,8 @@ impl UserStore { } pub fn decline_call(&mut self) -> Result<()> { - let mut incoming_call = self.incoming_call.0.borrow_mut(); - if incoming_call.is_some() { - if let Some(client) = self.client.upgrade() { - client.send(proto::DeclineCall {})?; - } - *incoming_call = None; + if let Some(client) = self.client.upgrade() { + client.send(proto::DeclineCall {})?; } Ok(()) } diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 2432618199..9372bf5b29 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -118,6 +118,8 @@ async fn test_share_project_in_room( .update(cx_a, |room, cx| room.call(client_b.user_id().unwrap(), cx)) .await .unwrap(); + + deterministic.run_until_parked(); assert_eq!( participants(&room_a, &client_a, cx_a).await, RoomParticipants { @@ -156,6 +158,8 @@ async fn test_share_project_in_room( .update(cx_a, |room, cx| room.call(client_c.user_id().unwrap(), cx)) .await .unwrap(); + + deterministic.run_until_parked(); assert_eq!( participants(&room_a, &client_a, cx_a).await, RoomParticipants { From e55e7e48443687fea0afe701855f46527d06e33b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 26 Sep 2022 14:27:52 +0200 Subject: [PATCH 011/314] Leave room when `Room` entity is dropped --- crates/collab/src/integration_tests.rs | 21 +++++++++++-- crates/collab/src/rpc.rs | 9 ++++++ crates/collab/src/rpc/store.rs | 41 +++++++++++++++++++++++--- crates/room/src/room.rs | 32 ++++++++++++-------- crates/rpc/proto/zed.proto | 5 ++++ crates/rpc/src/proto.rs | 1 + 6 files changed, 89 insertions(+), 20 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 9372bf5b29..d7a8d2bcfe 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -62,7 +62,7 @@ fn init_logger() { } #[gpui::test(iterations = 10)] -async fn test_share_project_in_room( +async fn test_basic_calls( deterministic: Arc, cx_a: &mut TestAppContext, cx_b: &mut TestAppContext, @@ -111,6 +111,7 @@ async fn test_share_project_in_room( let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; // room.publish_project(project_a.clone()).await.unwrap(); + // Call user B from client A. let mut incoming_call_b = client_b .user_store .update(cx_b, |user, _| user.incoming_call()); @@ -128,6 +129,7 @@ async fn test_share_project_in_room( } ); + // User B receives the call and joins the room. let call_b = incoming_call_b.next().await.unwrap().unwrap(); let room_b = cx_b .update(|cx| Room::join(&call_b, client_b.clone(), cx)) @@ -151,11 +153,12 @@ async fn test_share_project_in_room( } ); + // Call user C from client B. let mut incoming_call_c = client_c .user_store .update(cx_c, |user, _| user.incoming_call()); - room_a - .update(cx_a, |room, cx| room.call(client_c.user_id().unwrap(), cx)) + room_b + .update(cx_b, |room, cx| room.call(client_c.user_id().unwrap(), cx)) .await .unwrap(); @@ -175,6 +178,7 @@ async fn test_share_project_in_room( } ); + // User C receives the call, but declines it. let _call_c = incoming_call_c.next().await.unwrap().unwrap(); client_c .user_store @@ -198,6 +202,17 @@ async fn test_share_project_in_room( } ); + // User A leaves the room. + cx_a.update(|_| drop(room_a)); + deterministic.run_until_parked(); + assert_eq!( + participants(&room_b, &client_b, cx_b).await, + RoomParticipants { + remote: Default::default(), + pending: Default::default() + } + ); + #[derive(Debug, Eq, PartialEq)] struct RoomParticipants { remote: Vec, diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index fb8bbdb85a..4b366387e4 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -153,6 +153,7 @@ impl Server { .add_request_handler(Server::ping) .add_request_handler(Server::create_room) .add_request_handler(Server::join_room) + .add_message_handler(Server::leave_room) .add_request_handler(Server::call) .add_message_handler(Server::decline_call) .add_request_handler(Server::register_project) @@ -627,6 +628,14 @@ impl Server { Ok(()) } + async fn leave_room(self: Arc, message: TypedEnvelope) -> Result<()> { + let room_id = message.payload.id; + let mut store = self.store().await; + let room = store.leave_room(room_id, message.sender_id)?; + self.room_updated(room); + Ok(()) + } + async fn call( self: Arc, request: TypedEnvelope, diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index fc8576224b..ba4e644f5c 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -38,7 +38,7 @@ struct ConnectionState { #[derive(Copy, Clone, Eq, PartialEq, Serialize)] enum RoomState { - Joined, + Joined { room_id: RoomId }, Calling { room_id: RoomId }, } @@ -370,13 +370,13 @@ impl Store { let room_id = post_inc(&mut self.next_room_id); self.rooms.insert(room_id, room); - user_connection_state.room = Some(RoomState::Joined); + user_connection_state.room = Some(RoomState::Joined { room_id }); Ok(room_id) } pub fn join_room( &mut self, - room_id: u64, + room_id: RoomId, connection_id: ConnectionId, ) -> Result<(&proto::Room, Vec)> { let connection = self @@ -417,11 +417,44 @@ impl Store { )), }), }); - user_connection_state.room = Some(RoomState::Joined); + user_connection_state.room = Some(RoomState::Joined { room_id }); Ok((room, recipient_connection_ids)) } + pub fn leave_room( + &mut self, + room_id: RoomId, + connection_id: ConnectionId, + ) -> Result<&proto::Room> { + let connection = self + .connections + .get_mut(&connection_id) + .ok_or_else(|| anyhow!("no such connection"))?; + let user_id = connection.user_id; + + let mut user_connection_state = self + .connections_by_user_id + .get_mut(&user_id) + .ok_or_else(|| anyhow!("no such connection"))?; + anyhow::ensure!( + user_connection_state + .room + .map_or(false, |room| room == RoomState::Joined { room_id }), + "cannot leave a room before joining it" + ); + + let room = self + .rooms + .get_mut(&room_id) + .ok_or_else(|| anyhow!("no such room"))?; + room.participants + .retain(|participant| participant.peer_id != connection_id.0); + user_connection_state.room = None; + + Ok(room) + } + pub fn call( &mut self, room_id: RoomId, diff --git a/crates/room/src/room.rs b/crates/room/src/room.rs index 6dddfeda3f..2a9318f1d7 100644 --- a/crates/room/src/room.rs +++ b/crates/room/src/room.rs @@ -31,6 +31,19 @@ impl Entity for Room { } impl Room { + fn new(id: u64, client: Arc, cx: &mut ModelContext) -> Self { + Self { + id, + local_participant: LocalParticipant { + projects: Default::default(), + }, + remote_participants: Default::default(), + pending_user_ids: Default::default(), + _subscriptions: vec![client.add_message_handler(cx.handle(), Self::handle_room_updated)], + client, + } + } + pub fn create( client: Arc, cx: &mut MutableAppContext, @@ -56,19 +69,6 @@ impl Room { }) } - fn new(id: u64, client: Arc, cx: &mut ModelContext) -> Self { - Self { - id, - local_participant: LocalParticipant { - projects: Default::default(), - }, - remote_participants: Default::default(), - pending_user_ids: Default::default(), - _subscriptions: vec![client.add_message_handler(cx.handle(), Self::handle_room_updated)], - client, - } - } - pub fn remote_participants(&self) -> &HashMap { &self.remote_participants } @@ -148,3 +148,9 @@ impl Room { todo!() } } + +impl Drop for Room { + fn drop(&mut self) { + let _ = self.client.send(proto::LeaveRoom { id: self.id }); + } +} diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index bcc762283d..47f33a5d25 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -15,6 +15,7 @@ message Envelope { CreateRoomResponse create_room_response = 9; JoinRoom join_room = 10; JoinRoomResponse join_room_response = 11; + LeaveRoom leave_room = 1002; Call call = 12; IncomingCall incoming_call = 1000; CancelCall cancel_call = 1001; @@ -149,6 +150,10 @@ message JoinRoomResponse { Room room = 1; } +message LeaveRoom { + uint64 id = 1; +} + message Room { repeated Participant participants = 1; repeated uint64 pending_user_ids = 2; diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 94690e29e1..814983938c 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -131,6 +131,7 @@ messages!( (JoinRoomResponse, Foreground), (LeaveChannel, Foreground), (LeaveProject, Foreground), + (LeaveRoom, Foreground), (OpenBufferById, Background), (OpenBufferByPath, Background), (OpenBufferForSymbol, Background), From f0c45cbceb6bd59ddecd75118216a628bab8aa9c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 26 Sep 2022 14:28:58 +0200 Subject: [PATCH 012/314] Remove projects from basic calls test for now --- crates/collab/src/integration_tests.rs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index d7a8d2bcfe..30513172ad 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -81,22 +81,6 @@ async fn test_basic_calls( ]) .await; - client_a - .fs - .insert_tree( - "/a", - json!({ - ".gitignore": "ignored-dir", - "a.txt": "a-contents", - "b.txt": "b-contents", - "ignored-dir": { - "c.txt": "", - "d.txt": "", - } - }), - ) - .await; - let room_a = cx_a .update(|cx| Room::create(client_a.clone(), cx)) .await @@ -108,8 +92,6 @@ async fn test_basic_calls( pending: Default::default() } ); - let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; - // room.publish_project(project_a.clone()).await.unwrap(); // Call user B from client A. let mut incoming_call_b = client_b From 6aa0f0b200e339c2229d205fb2db1cb981e1fc6d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 26 Sep 2022 17:15:53 +0200 Subject: [PATCH 013/314] Leave room automatically on disconnection Co-Authored-By: Nathan Sobo --- Cargo.lock | 1 + crates/collab/src/integration_tests.rs | 183 +++++++++++++++++-------- crates/collab/src/rpc.rs | 7 + crates/collab/src/rpc/store.rs | 147 ++++++++++++-------- crates/room/Cargo.toml | 4 +- crates/room/src/room.rs | 83 +++++++++-- 6 files changed, 302 insertions(+), 123 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 08e183810d..4738e69852 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4459,6 +4459,7 @@ dependencies = [ "anyhow", "client", "collections", + "futures", "gpui", "project", ] diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 30513172ad..7834c3da7f 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -86,7 +86,7 @@ async fn test_basic_calls( .await .unwrap(); assert_eq!( - participants(&room_a, &client_a, cx_a).await, + room_participants(&room_a, &client_a, cx_a).await, RoomParticipants { remote: Default::default(), pending: Default::default() @@ -104,7 +104,7 @@ async fn test_basic_calls( deterministic.run_until_parked(); assert_eq!( - participants(&room_a, &client_a, cx_a).await, + room_participants(&room_a, &client_a, cx_a).await, RoomParticipants { remote: Default::default(), pending: vec!["user_b".to_string()] @@ -121,14 +121,14 @@ async fn test_basic_calls( deterministic.run_until_parked(); assert_eq!( - participants(&room_a, &client_a, cx_a).await, + room_participants(&room_a, &client_a, cx_a).await, RoomParticipants { remote: vec!["user_b".to_string()], pending: Default::default() } ); assert_eq!( - participants(&room_b, &client_b, cx_b).await, + room_participants(&room_b, &client_b, cx_b).await, RoomParticipants { remote: vec!["user_a".to_string()], pending: Default::default() @@ -146,14 +146,14 @@ async fn test_basic_calls( deterministic.run_until_parked(); assert_eq!( - participants(&room_a, &client_a, cx_a).await, + room_participants(&room_a, &client_a, cx_a).await, RoomParticipants { remote: vec!["user_b".to_string()], pending: vec!["user_c".to_string()] } ); assert_eq!( - participants(&room_b, &client_b, cx_b).await, + room_participants(&room_b, &client_b, cx_b).await, RoomParticipants { remote: vec!["user_a".to_string()], pending: vec!["user_c".to_string()] @@ -170,14 +170,14 @@ async fn test_basic_calls( deterministic.run_until_parked(); assert_eq!( - participants(&room_a, &client_a, cx_a).await, + room_participants(&room_a, &client_a, cx_a).await, RoomParticipants { remote: vec!["user_b".to_string()], pending: Default::default() } ); assert_eq!( - participants(&room_b, &client_b, cx_b).await, + room_participants(&room_b, &client_b, cx_b).await, RoomParticipants { remote: vec!["user_a".to_string()], pending: Default::default() @@ -185,61 +185,90 @@ async fn test_basic_calls( ); // User A leaves the room. - cx_a.update(|_| drop(room_a)); + room_a.update(cx_a, |room, cx| room.leave(cx)).unwrap(); deterministic.run_until_parked(); assert_eq!( - participants(&room_b, &client_b, cx_b).await, + room_participants(&room_a, &client_a, cx_a).await, RoomParticipants { remote: Default::default(), pending: Default::default() } ); - - #[derive(Debug, Eq, PartialEq)] - struct RoomParticipants { - remote: Vec, - pending: Vec, - } - - async fn participants( - room: &ModelHandle, - client: &TestClient, - cx: &mut TestAppContext, - ) -> RoomParticipants { - let remote_users = room.update(cx, |room, cx| { - room.remote_participants() - .values() - .map(|participant| { - client - .user_store - .update(cx, |users, cx| users.get_user(participant.user_id, cx)) - }) - .collect::>() - }); - let remote_users = futures::future::try_join_all(remote_users).await.unwrap(); - let pending_users = room.update(cx, |room, cx| { - room.pending_user_ids() - .iter() - .map(|user_id| { - client - .user_store - .update(cx, |users, cx| users.get_user(*user_id, cx)) - }) - .collect::>() - }); - let pending_users = futures::future::try_join_all(pending_users).await.unwrap(); - + assert_eq!( + room_participants(&room_b, &client_b, cx_b).await, RoomParticipants { - remote: remote_users - .into_iter() - .map(|user| user.github_login.clone()) - .collect(), - pending: pending_users - .into_iter() - .map(|user| user.github_login.clone()) - .collect(), + remote: Default::default(), + pending: Default::default() } - } + ); +} + +#[gpui::test(iterations = 10)] +async fn test_leaving_room_on_disconnection( + deterministic: Arc, + cx_a: &mut TestAppContext, + cx_b: &mut TestAppContext, +) { + deterministic.forbid_parking(); + let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; + let client_a = server.create_client(cx_a, "user_a").await; + let client_b = server.create_client(cx_b, "user_b").await; + server + .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + .await; + + let room_a = cx_a + .update(|cx| Room::create(client_a.clone(), cx)) + .await + .unwrap(); + + // Call user B from client A. + let mut incoming_call_b = client_b + .user_store + .update(cx_b, |user, _| user.incoming_call()); + room_a + .update(cx_a, |room, cx| room.call(client_b.user_id().unwrap(), cx)) + .await + .unwrap(); + + // User B receives the call and joins the room. + let call_b = incoming_call_b.next().await.unwrap().unwrap(); + let room_b = cx_b + .update(|cx| Room::join(&call_b, client_b.clone(), cx)) + .await + .unwrap(); + deterministic.run_until_parked(); + assert_eq!( + room_participants(&room_a, &client_a, cx_a).await, + RoomParticipants { + remote: vec!["user_b".to_string()], + pending: Default::default() + } + ); + assert_eq!( + room_participants(&room_b, &client_b, cx_b).await, + RoomParticipants { + remote: vec!["user_a".to_string()], + pending: Default::default() + } + ); + + server.disconnect_client(client_a.current_user_id(cx_a)); + cx_a.foreground().advance_clock(rpc::RECEIVE_TIMEOUT); + assert_eq!( + room_participants(&room_a, &client_a, cx_a).await, + RoomParticipants { + remote: Default::default(), + pending: Default::default() + } + ); + assert_eq!( + room_participants(&room_b, &client_b, cx_b).await, + RoomParticipants { + remote: Default::default(), + pending: Default::default() + } + ); } #[gpui::test(iterations = 10)] @@ -6169,3 +6198,49 @@ fn channel_messages(channel: &Channel) -> Vec<(String, String, bool)> { }) .collect() } + +#[derive(Debug, Eq, PartialEq)] +struct RoomParticipants { + remote: Vec, + pending: Vec, +} + +async fn room_participants( + room: &ModelHandle, + client: &TestClient, + cx: &mut TestAppContext, +) -> RoomParticipants { + let remote_users = room.update(cx, |room, cx| { + room.remote_participants() + .values() + .map(|participant| { + client + .user_store + .update(cx, |users, cx| users.get_user(participant.user_id, cx)) + }) + .collect::>() + }); + let remote_users = futures::future::try_join_all(remote_users).await.unwrap(); + let pending_users = room.update(cx, |room, cx| { + room.pending_user_ids() + .iter() + .map(|user_id| { + client + .user_store + .update(cx, |users, cx| users.get_user(*user_id, cx)) + }) + .collect::>() + }); + let pending_users = futures::future::try_join_all(pending_users).await.unwrap(); + + RoomParticipants { + remote: remote_users + .into_iter() + .map(|user| user.github_login.clone()) + .collect(), + pending: pending_users + .into_iter() + .map(|user| user.github_login.clone()) + .collect(), + } +} diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 4b366387e4..04eaad4edb 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -528,6 +528,13 @@ impl Server { } } + if let Some(room) = removed_connection + .room_id + .and_then(|room_id| store.room(room_id)) + { + self.room_updated(room); + } + removed_user_id = removed_connection.user_id; }; diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index ba4e644f5c..f55da1763b 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -13,7 +13,7 @@ pub type RoomId = u64; #[derive(Default, Serialize)] pub struct Store { connections: BTreeMap, - connections_by_user_id: BTreeMap, + connected_users: BTreeMap, next_room_id: RoomId, rooms: BTreeMap, projects: BTreeMap, @@ -22,9 +22,9 @@ pub struct Store { } #[derive(Default, Serialize)] -struct UserConnectionState { +struct ConnectedUser { connection_ids: HashSet, - room: Option, + active_call: Option, } #[derive(Serialize)] @@ -37,9 +37,9 @@ struct ConnectionState { } #[derive(Copy, Clone, Eq, PartialEq, Serialize)] -enum RoomState { - Joined { room_id: RoomId }, - Calling { room_id: RoomId }, +struct CallState { + room_id: RoomId, + joined: bool, } #[derive(Serialize)] @@ -89,6 +89,7 @@ pub struct RemovedConnectionState { pub hosted_projects: HashMap, pub guest_project_ids: HashSet, pub contact_ids: HashSet, + pub room_id: Option, } pub struct LeftProject { @@ -156,7 +157,7 @@ impl Store { channels: Default::default(), }, ); - self.connections_by_user_id + self.connected_users .entry(user_id) .or_default() .connection_ids @@ -196,10 +197,32 @@ impl Store { } } - let user_connection_state = self.connections_by_user_id.get_mut(&user_id).unwrap(); - user_connection_state.connection_ids.remove(&connection_id); - if user_connection_state.connection_ids.is_empty() { - self.connections_by_user_id.remove(&user_id); + let connected_user = self.connected_users.get_mut(&user_id).unwrap(); + connected_user.connection_ids.remove(&connection_id); + if let Some(active_call) = connected_user.active_call.as_ref() { + if let Some(room) = self.rooms.get_mut(&active_call.room_id) { + let prev_participant_count = room.participants.len(); + room.participants + .retain(|participant| participant.peer_id != connection_id.0); + if prev_participant_count == room.participants.len() { + if connected_user.connection_ids.is_empty() { + room.pending_user_ids + .retain(|pending_user_id| *pending_user_id != user_id.to_proto()); + result.room_id = Some(active_call.room_id); + connected_user.active_call = None; + } + } else { + result.room_id = Some(active_call.room_id); + connected_user.active_call = None; + } + } else { + tracing::error!("disconnected user claims to be in a room that does not exist"); + connected_user.active_call = None; + } + } + + if connected_user.connection_ids.is_empty() { + self.connected_users.remove(&user_id); } self.connections.remove(&connection_id).unwrap(); @@ -247,7 +270,7 @@ impl Store { &self, user_id: UserId, ) -> impl Iterator + '_ { - self.connections_by_user_id + self.connected_users .get(&user_id) .into_iter() .map(|state| &state.connection_ids) @@ -257,7 +280,7 @@ impl Store { pub fn is_user_online(&self, user_id: UserId) -> bool { !self - .connections_by_user_id + .connected_users .get(&user_id) .unwrap_or(&Default::default()) .connection_ids @@ -308,7 +331,7 @@ impl Store { } pub fn project_metadata_for_user(&self, user_id: UserId) -> Vec { - let user_connection_state = self.connections_by_user_id.get(&user_id); + let user_connection_state = self.connected_users.get(&user_id); let project_ids = user_connection_state.iter().flat_map(|state| { state .connection_ids @@ -347,13 +370,13 @@ impl Store { .connections .get_mut(&creator_connection_id) .ok_or_else(|| anyhow!("no such connection"))?; - let user_connection_state = self - .connections_by_user_id + let connected_user = self + .connected_users .get_mut(&connection.user_id) .ok_or_else(|| anyhow!("no such connection"))?; anyhow::ensure!( - user_connection_state.room.is_none(), - "cannot participate in more than one room at once" + connected_user.active_call.is_none(), + "can't create a room with an active call" ); let mut room = proto::Room::default(); @@ -370,7 +393,10 @@ impl Store { let room_id = post_inc(&mut self.next_room_id); self.rooms.insert(room_id, room); - user_connection_state.room = Some(RoomState::Joined { room_id }); + connected_user.active_call = Some(CallState { + room_id, + joined: true, + }); Ok(room_id) } @@ -386,15 +412,15 @@ impl Store { let user_id = connection.user_id; let recipient_connection_ids = self.connection_ids_for_user(user_id).collect::>(); - let mut user_connection_state = self - .connections_by_user_id + let mut connected_user = self + .connected_users .get_mut(&user_id) .ok_or_else(|| anyhow!("no such connection"))?; anyhow::ensure!( - user_connection_state - .room - .map_or(true, |room| room == RoomState::Calling { room_id }), - "cannot participate in more than one room at once" + connected_user + .active_call + .map_or(false, |call| call.room_id == room_id && !call.joined), + "not being called on this room" ); let room = self @@ -417,7 +443,10 @@ impl Store { )), }), }); - user_connection_state.room = Some(RoomState::Joined { room_id }); + connected_user.active_call = Some(CallState { + room_id, + joined: true, + }); Ok((room, recipient_connection_ids)) } @@ -433,14 +462,14 @@ impl Store { .ok_or_else(|| anyhow!("no such connection"))?; let user_id = connection.user_id; - let mut user_connection_state = self - .connections_by_user_id + let mut connected_user = self + .connected_users .get_mut(&user_id) .ok_or_else(|| anyhow!("no such connection"))?; anyhow::ensure!( - user_connection_state - .room - .map_or(false, |room| room == RoomState::Joined { room_id }), + connected_user + .active_call + .map_or(false, |call| call.room_id == room_id && call.joined), "cannot leave a room before joining it" ); @@ -450,26 +479,32 @@ impl Store { .ok_or_else(|| anyhow!("no such room"))?; room.participants .retain(|participant| participant.peer_id != connection_id.0); - user_connection_state.room = None; + connected_user.active_call = None; Ok(room) } + pub fn room(&self, room_id: RoomId) -> Option<&proto::Room> { + self.rooms.get(&room_id) + } + pub fn call( &mut self, room_id: RoomId, from_connection_id: ConnectionId, - to_user_id: UserId, + recipient_id: UserId, ) -> Result<(UserId, Vec, &proto::Room)> { let from_user_id = self.user_id_for_connection(from_connection_id)?; - let to_connection_ids = self.connection_ids_for_user(to_user_id).collect::>(); - let mut to_user_connection_state = self - .connections_by_user_id - .get_mut(&to_user_id) + let recipient_connection_ids = self + .connection_ids_for_user(recipient_id) + .collect::>(); + let mut recipient = self + .connected_users + .get_mut(&recipient_id) .ok_or_else(|| anyhow!("no such connection"))?; anyhow::ensure!( - to_user_connection_state.room.is_none(), + recipient.active_call.is_none(), "recipient is already on another call" ); @@ -486,22 +521,27 @@ impl Store { anyhow::ensure!( room.pending_user_ids .iter() - .all(|user_id| UserId::from_proto(*user_id) != to_user_id), + .all(|user_id| UserId::from_proto(*user_id) != recipient_id), "cannot call the same user more than once" ); - room.pending_user_ids.push(to_user_id.to_proto()); - to_user_connection_state.room = Some(RoomState::Calling { room_id }); + room.pending_user_ids.push(recipient_id.to_proto()); + recipient.active_call = Some(CallState { + room_id, + joined: false, + }); - Ok((from_user_id, to_connection_ids, room)) + Ok((from_user_id, recipient_connection_ids, room)) } pub fn call_failed(&mut self, room_id: RoomId, to_user_id: UserId) -> Result<&proto::Room> { - let mut to_user_connection_state = self - .connections_by_user_id + let mut recipient = self + .connected_users .get_mut(&to_user_id) .ok_or_else(|| anyhow!("no such connection"))?; - anyhow::ensure!(to_user_connection_state.room == Some(RoomState::Calling { room_id })); - to_user_connection_state.room = None; + anyhow::ensure!(recipient + .active_call + .map_or(false, |call| call.room_id == room_id && !call.joined)); + recipient.active_call = None; let room = self .rooms .get_mut(&room_id) @@ -516,18 +556,17 @@ impl Store { recipient_connection_id: ConnectionId, ) -> Result<(&proto::Room, Vec)> { let recipient_user_id = self.user_id_for_connection(recipient_connection_id)?; - let mut to_user_connection_state = self - .connections_by_user_id + let recipient = self + .connected_users .get_mut(&recipient_user_id) .ok_or_else(|| anyhow!("no such connection"))?; - if let Some(RoomState::Calling { room_id }) = to_user_connection_state.room { - to_user_connection_state.room = None; + if let Some(active_call) = recipient.active_call.take() { let recipient_connection_ids = self .connection_ids_for_user(recipient_user_id) .collect::>(); let room = self .rooms - .get_mut(&room_id) + .get_mut(&active_call.room_id) .ok_or_else(|| anyhow!("no such room"))?; room.pending_user_ids .retain(|user_id| UserId::from_proto(*user_id) != recipient_user_id); @@ -650,7 +689,7 @@ impl Store { for requester_user_id in project.join_requests.keys() { if let Some(requester_user_connection_state) = - self.connections_by_user_id.get_mut(requester_user_id) + self.connected_users.get_mut(requester_user_id) { for requester_connection_id in &requester_user_connection_state.connection_ids @@ -1007,14 +1046,14 @@ impl Store { assert!(channel.connection_ids.contains(connection_id)); } assert!(self - .connections_by_user_id + .connected_users .get(&connection.user_id) .unwrap() .connection_ids .contains(connection_id)); } - for (user_id, state) in &self.connections_by_user_id { + for (user_id, state) in &self.connected_users { for connection_id in &state.connection_ids { assert_eq!( self.connections.get(connection_id).unwrap().user_id, diff --git a/crates/room/Cargo.toml b/crates/room/Cargo.toml index f329d5ae87..33b6620b27 100644 --- a/crates/room/Cargo.toml +++ b/crates/room/Cargo.toml @@ -16,12 +16,14 @@ test-support = [ ] [dependencies] -anyhow = "1.0.38" client = { path = "../client" } collections = { path = "../collections" } gpui = { path = "../gpui" } project = { path = "../project" } +anyhow = "1.0.38" +futures = "0.3" + [dev-dependencies] client = { path = "../client", features = ["test-support"] } collections = { path = "../collections", features = ["test-support"] } diff --git a/crates/room/src/room.rs b/crates/room/src/room.rs index 2a9318f1d7..8d80b47508 100644 --- a/crates/room/src/room.rs +++ b/crates/room/src/room.rs @@ -3,6 +3,7 @@ mod participant; use anyhow::{anyhow, Result}; use client::{call::Call, proto, Client, PeerId, TypedEnvelope}; use collections::HashMap; +use futures::StreamExt; use gpui::{AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task}; use participant::{LocalParticipant, ParticipantLocation, RemoteParticipant}; use project::Project; @@ -12,13 +13,9 @@ pub enum Event { PeerChangedActiveProject, } -pub enum CallResponse { - Accepted, - Rejected, -} - pub struct Room { id: u64, + status: RoomStatus, local_participant: LocalParticipant, remote_participants: HashMap, pending_user_ids: Vec, @@ -32,8 +29,24 @@ impl Entity for Room { impl Room { fn new(id: u64, client: Arc, cx: &mut ModelContext) -> Self { + let mut client_status = client.status(); + cx.spawn_weak(|this, mut cx| async move { + let is_connected = client_status + .next() + .await + .map_or(false, |s| s.is_connected()); + // Even if we're initially connected, any future change of the status means we momentarily disconnected. + if !is_connected || client_status.next().await.is_some() { + if let Some(this) = this.upgrade(&cx) { + let _ = this.update(&mut cx, |this, cx| this.leave(cx)); + } + } + }) + .detach(); + Self { id, + status: RoomStatus::Online, local_participant: LocalParticipant { projects: Default::default(), }, @@ -69,6 +82,18 @@ impl Room { }) } + pub fn leave(&mut self, cx: &mut ModelContext) -> Result<()> { + if self.status.is_offline() { + return Err(anyhow!("room is offline")); + } + + self.status = RoomStatus::Offline; + self.remote_participants.clear(); + self.client.send(proto::LeaveRoom { id: self.id })?; + cx.notify(); + Ok(()) + } + pub fn remote_participants(&self) -> &HashMap { &self.remote_participants } @@ -112,6 +137,10 @@ impl Room { } pub fn call(&mut self, to_user_id: u64, cx: &mut ModelContext) -> Task> { + if self.status.is_offline() { + return Task::ready(Err(anyhow!("room is offline"))); + } + let client = self.client.clone(); let room_id = self.id; cx.foreground().spawn(async move { @@ -125,32 +154,58 @@ impl Room { }) } - pub async fn publish_project(&mut self, project: ModelHandle) -> Result<()> { + pub fn publish_project(&mut self, project: ModelHandle) -> Task> { + if self.status.is_offline() { + return Task::ready(Err(anyhow!("room is offline"))); + } + todo!() } - pub async fn unpublish_project(&mut self, project: ModelHandle) -> Result<()> { + pub fn unpublish_project(&mut self, project: ModelHandle) -> Task> { + if self.status.is_offline() { + return Task::ready(Err(anyhow!("room is offline"))); + } + todo!() } - pub async fn set_active_project( + pub fn set_active_project( &mut self, project: Option<&ModelHandle>, - ) -> Result<()> { + ) -> Task> { + if self.status.is_offline() { + return Task::ready(Err(anyhow!("room is offline"))); + } + todo!() } - pub async fn mute(&mut self) -> Result<()> { + pub fn mute(&mut self) -> Task> { + if self.status.is_offline() { + return Task::ready(Err(anyhow!("room is offline"))); + } + todo!() } - pub async fn unmute(&mut self) -> Result<()> { + pub fn unmute(&mut self) -> Task> { + if self.status.is_offline() { + return Task::ready(Err(anyhow!("room is offline"))); + } + todo!() } } -impl Drop for Room { - fn drop(&mut self) { - let _ = self.client.send(proto::LeaveRoom { id: self.id }); +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum RoomStatus { + Online, + Offline, +} + +impl RoomStatus { + fn is_offline(&self) -> bool { + matches!(self, RoomStatus::Offline) } } From 80ab144bf3f5d1c45739d76390ca97836020fcc9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 26 Sep 2022 17:37:58 +0200 Subject: [PATCH 014/314] Ring users upon connection if somebody was calling them before connecting Co-Authored-By: Nathan Sobo --- crates/client/src/user.rs | 2 +- crates/collab/src/integration_tests.rs | 13 ++++- crates/collab/src/rpc.rs | 30 ++++------ crates/collab/src/rpc/store.rs | 80 ++++++++++++++++++-------- crates/room/src/room.rs | 8 ++- crates/rpc/proto/zed.proto | 4 +- 6 files changed, 90 insertions(+), 47 deletions(-) diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 57a403ebad..0dbb8bb198 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -214,7 +214,7 @@ impl UserStore { .await?, from: this .update(&mut cx, |this, cx| { - this.get_user(envelope.payload.from_user_id, cx) + this.get_user(envelope.payload.caller_user_id, cx) }) .await?, }; diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 7834c3da7f..c235a31c55 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -66,6 +66,7 @@ async fn test_basic_calls( deterministic: Arc, cx_a: &mut TestAppContext, cx_b: &mut TestAppContext, + cx_b2: &mut TestAppContext, cx_c: &mut TestAppContext, ) { deterministic.forbid_parking(); @@ -111,8 +112,18 @@ async fn test_basic_calls( } ); - // User B receives the call and joins the room. + // User B receives the call. let call_b = incoming_call_b.next().await.unwrap().unwrap(); + + // User B connects via another client and also receives a ring on the newly-connected client. + let client_b2 = server.create_client(cx_b2, "user_b").await; + let mut incoming_call_b2 = client_b2 + .user_store + .update(cx_b2, |user, _| user.incoming_call()); + deterministic.run_until_parked(); + let _call_b2 = incoming_call_b2.next().await.unwrap().unwrap(); + + // User B joins the room using the first client. let room_b = cx_b .update(|cx| Room::join(&call_b, client_b.clone(), cx)) .await diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 04eaad4edb..55c3414d85 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -388,7 +388,11 @@ impl Server { { let mut store = this.store().await; - store.add_connection(connection_id, user_id, user.admin); + let incoming_call = store.add_connection(connection_id, user_id, user.admin); + if let Some(incoming_call) = incoming_call { + this.peer.send(connection_id, incoming_call)?; + } + this.peer.send(connection_id, store.build_initial_contacts_update(contacts))?; if let Some((code, count)) = invite_code { @@ -648,28 +652,18 @@ impl Server { request: TypedEnvelope, response: Response, ) -> Result<()> { - let to_user_id = UserId::from_proto(request.payload.to_user_id); + let recipient_user_id = UserId::from_proto(request.payload.recipient_user_id); let room_id = request.payload.room_id; let mut calls = { let mut store = self.store().await; - let (from_user_id, recipient_connection_ids, room) = - store.call(room_id, request.sender_id, to_user_id)?; + let (room, recipient_connection_ids, incoming_call) = + store.call(room_id, request.sender_id, recipient_user_id)?; self.room_updated(room); recipient_connection_ids .into_iter() - .map(|recipient_id| { - self.peer.request( - recipient_id, - proto::IncomingCall { - room_id, - from_user_id: from_user_id.to_proto(), - participant_user_ids: room - .participants - .iter() - .map(|p| p.user_id) - .collect(), - }, - ) + .map(|recipient_connection_id| { + self.peer + .request(recipient_connection_id, incoming_call.clone()) }) .collect::>() }; @@ -688,7 +682,7 @@ impl Server { { let mut store = self.store().await; - let room = store.call_failed(room_id, to_user_id)?; + let room = store.call_failed(room_id, recipient_user_id)?; self.room_updated(&room); } diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index f55da1763b..1c69a7c2f8 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -24,7 +24,7 @@ pub struct Store { #[derive(Default, Serialize)] struct ConnectedUser { connection_ids: HashSet, - active_call: Option, + active_call: Option, } #[derive(Serialize)] @@ -37,9 +37,10 @@ struct ConnectionState { } #[derive(Copy, Clone, Eq, PartialEq, Serialize)] -struct CallState { - room_id: RoomId, - joined: bool, +pub struct Call { + pub caller_user_id: UserId, + pub room_id: RoomId, + pub joined: bool, } #[derive(Serialize)] @@ -146,7 +147,12 @@ impl Store { } #[instrument(skip(self))] - pub fn add_connection(&mut self, connection_id: ConnectionId, user_id: UserId, admin: bool) { + pub fn add_connection( + &mut self, + connection_id: ConnectionId, + user_id: UserId, + admin: bool, + ) -> Option { self.connections.insert( connection_id, ConnectionState { @@ -157,11 +163,26 @@ impl Store { channels: Default::default(), }, ); - self.connected_users - .entry(user_id) - .or_default() - .connection_ids - .insert(connection_id); + let connected_user = self.connected_users.entry(user_id).or_default(); + connected_user.connection_ids.insert(connection_id); + if let Some(active_call) = connected_user.active_call { + if active_call.joined { + None + } else { + let room = self.room(active_call.room_id)?; + Some(proto::IncomingCall { + room_id: active_call.room_id, + caller_user_id: active_call.caller_user_id.to_proto(), + participant_user_ids: room + .participants + .iter() + .map(|participant| participant.user_id) + .collect(), + }) + } + } else { + None + } } #[instrument(skip(self))] @@ -393,7 +414,8 @@ impl Store { let room_id = post_inc(&mut self.next_room_id); self.rooms.insert(room_id, room); - connected_user.active_call = Some(CallState { + connected_user.active_call = Some(Call { + caller_user_id: connection.user_id, room_id, joined: true, }); @@ -412,14 +434,16 @@ impl Store { let user_id = connection.user_id; let recipient_connection_ids = self.connection_ids_for_user(user_id).collect::>(); - let mut connected_user = self + let connected_user = self .connected_users .get_mut(&user_id) .ok_or_else(|| anyhow!("no such connection"))?; + let active_call = connected_user + .active_call + .as_mut() + .ok_or_else(|| anyhow!("not being called"))?; anyhow::ensure!( - connected_user - .active_call - .map_or(false, |call| call.room_id == room_id && !call.joined), + active_call.room_id == room_id && !active_call.joined, "not being called on this room" ); @@ -443,10 +467,7 @@ impl Store { )), }), }); - connected_user.active_call = Some(CallState { - room_id, - joined: true, - }); + active_call.joined = true; Ok((room, recipient_connection_ids)) } @@ -493,8 +514,8 @@ impl Store { room_id: RoomId, from_connection_id: ConnectionId, recipient_id: UserId, - ) -> Result<(UserId, Vec, &proto::Room)> { - let from_user_id = self.user_id_for_connection(from_connection_id)?; + ) -> Result<(&proto::Room, Vec, proto::IncomingCall)> { + let caller_user_id = self.user_id_for_connection(from_connection_id)?; let recipient_connection_ids = self .connection_ids_for_user(recipient_id) @@ -525,12 +546,25 @@ impl Store { "cannot call the same user more than once" ); room.pending_user_ids.push(recipient_id.to_proto()); - recipient.active_call = Some(CallState { + recipient.active_call = Some(Call { + caller_user_id, room_id, joined: false, }); - Ok((from_user_id, recipient_connection_ids, room)) + Ok(( + room, + recipient_connection_ids, + proto::IncomingCall { + room_id, + caller_user_id: caller_user_id.to_proto(), + participant_user_ids: room + .participants + .iter() + .map(|participant| participant.user_id) + .collect(), + }, + )) } pub fn call_failed(&mut self, room_id: RoomId, to_user_id: UserId) -> Result<&proto::Room> { diff --git a/crates/room/src/room.rs b/crates/room/src/room.rs index 8d80b47508..c6daacd7e2 100644 --- a/crates/room/src/room.rs +++ b/crates/room/src/room.rs @@ -136,7 +136,11 @@ impl Room { Ok(()) } - pub fn call(&mut self, to_user_id: u64, cx: &mut ModelContext) -> Task> { + pub fn call( + &mut self, + recipient_user_id: u64, + cx: &mut ModelContext, + ) -> Task> { if self.status.is_offline() { return Task::ready(Err(anyhow!("room is offline"))); } @@ -147,7 +151,7 @@ impl Room { client .request(proto::Call { room_id, - to_user_id, + recipient_user_id, }) .await?; Ok(()) diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 47f33a5d25..1125c2b3ad 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -181,12 +181,12 @@ message ParticipantLocation { message Call { uint64 room_id = 1; - uint64 to_user_id = 2; + uint64 recipient_user_id = 2; } message IncomingCall { uint64 room_id = 1; - uint64 from_user_id = 2; + uint64 caller_user_id = 2; repeated uint64 participant_user_ids = 3; } From c8a48e8990f643b735d579c9af42175ac2635556 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 27 Sep 2022 12:17:00 +0200 Subject: [PATCH 015/314] Extract contacts titlebar item into a separate crate This allows us to implement a new contacts popover that uses the `editor` crate. --- Cargo.lock | 24 +- crates/contacts_titlebar_item/Cargo.toml | 48 +++ .../src/contacts_titlebar_item.rs | 304 ++++++++++++++++++ crates/workspace/Cargo.toml | 1 - crates/workspace/src/workspace.rs | 289 ++--------------- crates/zed/Cargo.toml | 1 + crates/zed/src/zed.rs | 7 +- 7 files changed, 408 insertions(+), 266 deletions(-) create mode 100644 crates/contacts_titlebar_item/Cargo.toml create mode 100644 crates/contacts_titlebar_item/src/contacts_titlebar_item.rs diff --git a/Cargo.lock b/Cargo.lock index 4738e69852..8537c51611 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1151,6 +1151,28 @@ dependencies = [ "workspace", ] +[[package]] +name = "contacts_titlebar_item" +version = "0.1.0" +dependencies = [ + "anyhow", + "client", + "clock", + "collections", + "editor", + "futures", + "fuzzy", + "gpui", + "log", + "postage", + "project", + "serde", + "settings", + "theme", + "util", + "workspace", +] + [[package]] name = "context_menu" version = "0.1.0" @@ -7084,7 +7106,6 @@ version = "0.1.0" dependencies = [ "anyhow", "client", - "clock", "collections", "context_menu", "drag_and_drop", @@ -7163,6 +7184,7 @@ dependencies = [ "command_palette", "contacts_panel", "contacts_status_item", + "contacts_titlebar_item", "context_menu", "ctor", "diagnostics", diff --git a/crates/contacts_titlebar_item/Cargo.toml b/crates/contacts_titlebar_item/Cargo.toml new file mode 100644 index 0000000000..771e364218 --- /dev/null +++ b/crates/contacts_titlebar_item/Cargo.toml @@ -0,0 +1,48 @@ +[package] +name = "contacts_titlebar_item" +version = "0.1.0" +edition = "2021" + +[lib] +path = "src/contacts_titlebar_item.rs" +doctest = false + +[features] +test-support = [ + "client/test-support", + "collections/test-support", + "editor/test-support", + "gpui/test-support", + "project/test-support", + "settings/test-support", + "util/test-support", + "workspace/test-support", +] + +[dependencies] +client = { path = "../client" } +clock = { path = "../clock" } +collections = { path = "../collections" } +editor = { path = "../editor" } +fuzzy = { path = "../fuzzy" } +gpui = { path = "../gpui" } +project = { path = "../project" } +settings = { path = "../settings" } +theme = { path = "../theme" } +util = { path = "../util" } +workspace = { path = "../workspace" } +anyhow = "1.0" +futures = "0.3" +log = "0.4" +postage = { version = "0.4.1", features = ["futures-traits"] } +serde = { version = "1.0", features = ["derive", "rc"] } + +[dev-dependencies] +client = { path = "../client", features = ["test-support"] } +collections = { path = "../collections", features = ["test-support"] } +editor = { path = "../editor", features = ["test-support"] } +gpui = { path = "../gpui", features = ["test-support"] } +project = { path = "../project", features = ["test-support"] } +settings = { path = "../settings", features = ["test-support"] } +util = { path = "../util", features = ["test-support"] } +workspace = { path = "../workspace", features = ["test-support"] } diff --git a/crates/contacts_titlebar_item/src/contacts_titlebar_item.rs b/crates/contacts_titlebar_item/src/contacts_titlebar_item.rs new file mode 100644 index 0000000000..7035585a2f --- /dev/null +++ b/crates/contacts_titlebar_item/src/contacts_titlebar_item.rs @@ -0,0 +1,304 @@ +use client::{Authenticate, PeerId}; +use clock::ReplicaId; +use gpui::{ + color::Color, + elements::*, + geometry::{rect::RectF, vector::vec2f, PathBuilder}, + json::{self, ToJson}, + Border, CursorStyle, Entity, ImageData, MouseButton, RenderContext, Subscription, View, + ViewContext, ViewHandle, WeakViewHandle, +}; +use settings::Settings; +use std::{ops::Range, sync::Arc}; +use theme::Theme; +use workspace::{FollowNextCollaborator, ToggleFollow, Workspace}; + +pub struct ContactsTitlebarItem { + workspace: WeakViewHandle, + _subscriptions: Vec, +} + +impl Entity for ContactsTitlebarItem { + type Event = (); +} + +impl View for ContactsTitlebarItem { + fn ui_name() -> &'static str { + "ContactsTitlebarItem" + } + + fn render(&mut self, cx: &mut RenderContext) -> ElementBox { + let workspace = if let Some(workspace) = self.workspace.upgrade(cx) { + workspace + } else { + return Empty::new().boxed(); + }; + + let theme = cx.global::().theme.clone(); + Flex::row() + .with_children(self.render_collaborators(&workspace, &theme, cx)) + .with_children(self.render_current_user(&workspace, &theme, cx)) + .with_children(self.render_connection_status(&workspace, cx)) + .boxed() + } +} + +impl ContactsTitlebarItem { + pub fn new(workspace: &ViewHandle, cx: &mut ViewContext) -> Self { + let observe_workspace = cx.observe(workspace, |_, _, cx| cx.notify()); + Self { + workspace: workspace.downgrade(), + _subscriptions: vec![observe_workspace], + } + } + + fn render_collaborators( + &self, + workspace: &ViewHandle, + theme: &Theme, + cx: &mut RenderContext, + ) -> Vec { + let mut collaborators = workspace + .read(cx) + .project() + .read(cx) + .collaborators() + .values() + .cloned() + .collect::>(); + collaborators.sort_unstable_by_key(|collaborator| collaborator.replica_id); + collaborators + .into_iter() + .filter_map(|collaborator| { + Some(self.render_avatar( + collaborator.user.avatar.clone()?, + collaborator.replica_id, + Some((collaborator.peer_id, &collaborator.user.github_login)), + workspace, + theme, + cx, + )) + }) + .collect() + } + + fn render_current_user( + &self, + workspace: &ViewHandle, + theme: &Theme, + cx: &mut RenderContext, + ) -> Option { + let user = workspace.read(cx).user_store().read(cx).current_user(); + let replica_id = workspace.read(cx).project().read(cx).replica_id(); + let status = *workspace.read(cx).client().status().borrow(); + if let Some(avatar) = user.and_then(|user| user.avatar.clone()) { + Some(self.render_avatar(avatar, replica_id, None, workspace, theme, cx)) + } else if matches!(status, client::Status::UpgradeRequired) { + None + } else { + Some( + MouseEventHandler::::new(0, cx, |state, _| { + let style = theme + .workspace + .titlebar + .sign_in_prompt + .style_for(state, false); + Label::new("Sign in".to_string(), style.text.clone()) + .contained() + .with_style(style.container) + .boxed() + }) + .on_click(MouseButton::Left, |_, cx| cx.dispatch_action(Authenticate)) + .with_cursor_style(CursorStyle::PointingHand) + .aligned() + .boxed(), + ) + } + } + + fn render_avatar( + &self, + avatar: Arc, + replica_id: ReplicaId, + peer: Option<(PeerId, &str)>, + workspace: &ViewHandle, + theme: &Theme, + cx: &mut RenderContext, + ) -> ElementBox { + let replica_color = theme.editor.replica_selection_style(replica_id).cursor; + let is_followed = peer.map_or(false, |(peer_id, _)| { + workspace.read(cx).is_following(peer_id) + }); + let mut avatar_style = theme.workspace.titlebar.avatar; + if is_followed { + avatar_style.border = Border::all(1.0, replica_color); + } + let content = Stack::new() + .with_child( + Image::new(avatar) + .with_style(avatar_style) + .constrained() + .with_width(theme.workspace.titlebar.avatar_width) + .aligned() + .boxed(), + ) + .with_child( + AvatarRibbon::new(replica_color) + .constrained() + .with_width(theme.workspace.titlebar.avatar_ribbon.width) + .with_height(theme.workspace.titlebar.avatar_ribbon.height) + .aligned() + .bottom() + .boxed(), + ) + .constrained() + .with_width(theme.workspace.titlebar.avatar_width) + .contained() + .with_margin_left(theme.workspace.titlebar.avatar_margin) + .boxed(); + + if let Some((peer_id, peer_github_login)) = peer { + MouseEventHandler::::new(replica_id.into(), cx, move |_, _| content) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, cx| { + cx.dispatch_action(ToggleFollow(peer_id)) + }) + .with_tooltip::( + peer_id.0 as usize, + if is_followed { + format!("Unfollow {}", peer_github_login) + } else { + format!("Follow {}", peer_github_login) + }, + Some(Box::new(FollowNextCollaborator)), + theme.tooltip.clone(), + cx, + ) + .boxed() + } else { + content + } + } + + fn render_connection_status( + &self, + workspace: &ViewHandle, + cx: &mut RenderContext, + ) -> Option { + let theme = &cx.global::().theme; + match &*workspace.read(cx).client().status().borrow() { + client::Status::ConnectionError + | client::Status::ConnectionLost + | client::Status::Reauthenticating { .. } + | client::Status::Reconnecting { .. } + | client::Status::ReconnectionError { .. } => Some( + Container::new( + Align::new( + ConstrainedBox::new( + Svg::new("icons/cloud_slash_12.svg") + .with_color(theme.workspace.titlebar.offline_icon.color) + .boxed(), + ) + .with_width(theme.workspace.titlebar.offline_icon.width) + .boxed(), + ) + .boxed(), + ) + .with_style(theme.workspace.titlebar.offline_icon.container) + .boxed(), + ), + client::Status::UpgradeRequired => Some( + Label::new( + "Please update Zed to collaborate".to_string(), + theme.workspace.titlebar.outdated_warning.text.clone(), + ) + .contained() + .with_style(theme.workspace.titlebar.outdated_warning.container) + .aligned() + .boxed(), + ), + _ => None, + } + } +} + +pub struct AvatarRibbon { + color: Color, +} + +impl AvatarRibbon { + pub fn new(color: Color) -> AvatarRibbon { + AvatarRibbon { color } + } +} + +impl Element for AvatarRibbon { + type LayoutState = (); + + type PaintState = (); + + fn layout( + &mut self, + constraint: gpui::SizeConstraint, + _: &mut gpui::LayoutContext, + ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) { + (constraint.max, ()) + } + + fn paint( + &mut self, + bounds: gpui::geometry::rect::RectF, + _: gpui::geometry::rect::RectF, + _: &mut Self::LayoutState, + cx: &mut gpui::PaintContext, + ) -> Self::PaintState { + let mut path = PathBuilder::new(); + path.reset(bounds.lower_left()); + path.curve_to( + bounds.origin() + vec2f(bounds.height(), 0.), + bounds.origin(), + ); + path.line_to(bounds.upper_right() - vec2f(bounds.height(), 0.)); + path.curve_to(bounds.lower_right(), bounds.upper_right()); + path.line_to(bounds.lower_left()); + cx.scene.push_path(path.build(self.color, None)); + } + + fn dispatch_event( + &mut self, + _: &gpui::Event, + _: RectF, + _: RectF, + _: &mut Self::LayoutState, + _: &mut Self::PaintState, + _: &mut gpui::EventContext, + ) -> bool { + false + } + + fn rect_for_text_range( + &self, + _: Range, + _: RectF, + _: RectF, + _: &Self::LayoutState, + _: &Self::PaintState, + _: &gpui::MeasurementContext, + ) -> Option { + None + } + + fn debug( + &self, + bounds: gpui::geometry::rect::RectF, + _: &Self::LayoutState, + _: &Self::PaintState, + _: &gpui::DebugContext, + ) -> gpui::json::Value { + json::json!({ + "type": "AvatarRibbon", + "bounds": bounds.to_json(), + "color": self.color.to_json(), + }) + } +} diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index c40ce56389..759bff2cbd 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -12,7 +12,6 @@ test-support = ["client/test-support", "project/test-support", "settings/test-su [dependencies] client = { path = "../client" } -clock = { path = "../clock" } collections = { path = "../collections" } context_menu = { path = "../context_menu" } drag_and_drop = { path = "../drag_and_drop" } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 017964d9a1..04bbc094b2 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -13,25 +13,19 @@ mod toolbar; mod waiting_room; use anyhow::{anyhow, Context, Result}; -use client::{ - proto, Authenticate, Client, Contact, PeerId, Subscription, TypedEnvelope, User, UserStore, -}; -use clock::ReplicaId; +use client::{proto, Client, Contact, PeerId, Subscription, TypedEnvelope, UserStore}; use collections::{hash_map, HashMap, HashSet}; use dock::{DefaultItemFactory, Dock, ToggleDockButton}; use drag_and_drop::DragAndDrop; use futures::{channel::oneshot, FutureExt}; use gpui::{ actions, - color::Color, elements::*, - geometry::{rect::RectF, vector::vec2f, PathBuilder}, impl_actions, impl_internal_actions, - json::{self, ToJson}, platform::{CursorStyle, WindowOptions}, - AnyModelHandle, AnyViewHandle, AppContext, AsyncAppContext, Border, Entity, ImageData, - ModelContext, ModelHandle, MouseButton, MutableAppContext, PathPromptOptions, PromptLevel, - RenderContext, Task, View, ViewContext, ViewHandle, WeakViewHandle, + AnyModelHandle, AnyViewHandle, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, + MouseButton, MutableAppContext, PathPromptOptions, PromptLevel, RenderContext, Task, View, + ViewContext, ViewHandle, WeakViewHandle, }; use language::LanguageRegistry; use log::{error, warn}; @@ -53,7 +47,6 @@ use std::{ fmt, future::Future, mem, - ops::Range, path::{Path, PathBuf}, rc::Rc, sync::{ @@ -895,6 +888,7 @@ pub struct Workspace { active_pane: ViewHandle, last_active_center_pane: Option>, status_bar: ViewHandle, + titlebar_item: Option, dock: Dock, notifications: Vec<(TypeId, usize, Box)>, project: ModelHandle, @@ -1024,6 +1018,7 @@ impl Workspace { active_pane: center_pane.clone(), last_active_center_pane: Some(center_pane.clone()), status_bar, + titlebar_item: None, notifications: Default::default(), client, remote_entity_subscription: None, @@ -1068,6 +1063,19 @@ impl Workspace { &self.project } + pub fn client(&self) -> &Arc { + &self.client + } + + pub fn set_titlebar_item( + &mut self, + item: impl Into, + cx: &mut ViewContext, + ) { + self.titlebar_item = Some(item.into()); + cx.notify(); + } + /// Call the given callback with a workspace whose project is local. /// /// If the given workspace has a local project, then it will be passed @@ -1968,46 +1976,12 @@ impl Workspace { None } - fn render_connection_status(&self, cx: &mut RenderContext) -> Option { - let theme = &cx.global::().theme; - match &*self.client.status().borrow() { - client::Status::ConnectionError - | client::Status::ConnectionLost - | client::Status::Reauthenticating { .. } - | client::Status::Reconnecting { .. } - | client::Status::ReconnectionError { .. } => Some( - Container::new( - Align::new( - ConstrainedBox::new( - Svg::new("icons/cloud_slash_12.svg") - .with_color(theme.workspace.titlebar.offline_icon.color) - .boxed(), - ) - .with_width(theme.workspace.titlebar.offline_icon.width) - .boxed(), - ) - .boxed(), - ) - .with_style(theme.workspace.titlebar.offline_icon.container) - .boxed(), - ), - client::Status::UpgradeRequired => Some( - Label::new( - "Please update Zed to collaborate".to_string(), - theme.workspace.titlebar.outdated_warning.text.clone(), - ) - .contained() - .with_style(theme.workspace.titlebar.outdated_warning.container) - .aligned() - .boxed(), - ), - _ => None, - } + pub fn is_following(&self, peer_id: PeerId) -> bool { + self.follower_states_by_leader.contains_key(&peer_id) } fn render_titlebar(&self, theme: &Theme, cx: &mut RenderContext) -> ElementBox { let project = &self.project.read(cx); - let replica_id = project.replica_id(); let mut worktree_root_names = String::new(); for (i, name) in project.worktree_root_names(cx).enumerate() { if i > 0 { @@ -2029,7 +2003,7 @@ impl Workspace { enum TitleBar {} ConstrainedBox::new( - MouseEventHandler::::new(0, cx, |_, cx| { + MouseEventHandler::::new(0, cx, |_, _| { Container::new( Stack::new() .with_child( @@ -2038,21 +2012,10 @@ impl Workspace { .left() .boxed(), ) - .with_child( - Align::new( - Flex::row() - .with_children(self.render_collaborators(theme, cx)) - .with_children(self.render_current_user( - self.user_store.read(cx).current_user().as_ref(), - replica_id, - theme, - cx, - )) - .with_children(self.render_connection_status(cx)) - .boxed(), - ) - .right() - .boxed(), + .with_children( + self.titlebar_item + .as_ref() + .map(|item| ChildView::new(item).aligned().right().boxed()), ) .boxed(), ) @@ -2121,125 +2084,6 @@ impl Workspace { } } - fn render_collaborators(&self, theme: &Theme, cx: &mut RenderContext) -> Vec { - let mut collaborators = self - .project - .read(cx) - .collaborators() - .values() - .cloned() - .collect::>(); - collaborators.sort_unstable_by_key(|collaborator| collaborator.replica_id); - collaborators - .into_iter() - .filter_map(|collaborator| { - Some(self.render_avatar( - collaborator.user.avatar.clone()?, - collaborator.replica_id, - Some((collaborator.peer_id, &collaborator.user.github_login)), - theme, - cx, - )) - }) - .collect() - } - - fn render_current_user( - &self, - user: Option<&Arc>, - replica_id: ReplicaId, - theme: &Theme, - cx: &mut RenderContext, - ) -> Option { - let status = *self.client.status().borrow(); - if let Some(avatar) = user.and_then(|user| user.avatar.clone()) { - Some(self.render_avatar(avatar, replica_id, None, theme, cx)) - } else if matches!(status, client::Status::UpgradeRequired) { - None - } else { - Some( - MouseEventHandler::::new(0, cx, |state, _| { - let style = theme - .workspace - .titlebar - .sign_in_prompt - .style_for(state, false); - Label::new("Sign in".to_string(), style.text.clone()) - .contained() - .with_style(style.container) - .boxed() - }) - .on_click(MouseButton::Left, |_, cx| cx.dispatch_action(Authenticate)) - .with_cursor_style(CursorStyle::PointingHand) - .aligned() - .boxed(), - ) - } - } - - fn render_avatar( - &self, - avatar: Arc, - replica_id: ReplicaId, - peer: Option<(PeerId, &str)>, - theme: &Theme, - cx: &mut RenderContext, - ) -> ElementBox { - let replica_color = theme.editor.replica_selection_style(replica_id).cursor; - let is_followed = peer.map_or(false, |(peer_id, _)| { - self.follower_states_by_leader.contains_key(&peer_id) - }); - let mut avatar_style = theme.workspace.titlebar.avatar; - if is_followed { - avatar_style.border = Border::all(1.0, replica_color); - } - let content = Stack::new() - .with_child( - Image::new(avatar) - .with_style(avatar_style) - .constrained() - .with_width(theme.workspace.titlebar.avatar_width) - .aligned() - .boxed(), - ) - .with_child( - AvatarRibbon::new(replica_color) - .constrained() - .with_width(theme.workspace.titlebar.avatar_ribbon.width) - .with_height(theme.workspace.titlebar.avatar_ribbon.height) - .aligned() - .bottom() - .boxed(), - ) - .constrained() - .with_width(theme.workspace.titlebar.avatar_width) - .contained() - .with_margin_left(theme.workspace.titlebar.avatar_margin) - .boxed(); - - if let Some((peer_id, peer_github_login)) = peer { - MouseEventHandler::::new(replica_id.into(), cx, move |_, _| content) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, cx| { - cx.dispatch_action(ToggleFollow(peer_id)) - }) - .with_tooltip::( - peer_id.0 as usize, - if is_followed { - format!("Unfollow {}", peer_github_login) - } else { - format!("Follow {}", peer_github_login) - }, - Some(Box::new(FollowNextCollaborator)), - theme.tooltip.clone(), - cx, - ) - .boxed() - } else { - content - } - } - fn render_disconnected_overlay(&self, cx: &mut RenderContext) -> Option { if self.project.read(cx).is_read_only() { enum DisconnectedOverlay {} @@ -2714,87 +2558,6 @@ impl WorkspaceHandle for ViewHandle { } } -pub struct AvatarRibbon { - color: Color, -} - -impl AvatarRibbon { - pub fn new(color: Color) -> AvatarRibbon { - AvatarRibbon { color } - } -} - -impl Element for AvatarRibbon { - type LayoutState = (); - - type PaintState = (); - - fn layout( - &mut self, - constraint: gpui::SizeConstraint, - _: &mut gpui::LayoutContext, - ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) { - (constraint.max, ()) - } - - fn paint( - &mut self, - bounds: gpui::geometry::rect::RectF, - _: gpui::geometry::rect::RectF, - _: &mut Self::LayoutState, - cx: &mut gpui::PaintContext, - ) -> Self::PaintState { - let mut path = PathBuilder::new(); - path.reset(bounds.lower_left()); - path.curve_to( - bounds.origin() + vec2f(bounds.height(), 0.), - bounds.origin(), - ); - path.line_to(bounds.upper_right() - vec2f(bounds.height(), 0.)); - path.curve_to(bounds.lower_right(), bounds.upper_right()); - path.line_to(bounds.lower_left()); - cx.scene.push_path(path.build(self.color, None)); - } - - fn dispatch_event( - &mut self, - _: &gpui::Event, - _: RectF, - _: RectF, - _: &mut Self::LayoutState, - _: &mut Self::PaintState, - _: &mut gpui::EventContext, - ) -> bool { - false - } - - fn rect_for_text_range( - &self, - _: Range, - _: RectF, - _: RectF, - _: &Self::LayoutState, - _: &Self::PaintState, - _: &gpui::MeasurementContext, - ) -> Option { - None - } - - fn debug( - &self, - bounds: gpui::geometry::rect::RectF, - _: &Self::LayoutState, - _: &Self::PaintState, - _: &gpui::DebugContext, - ) -> gpui::json::Value { - json::json!({ - "type": "AvatarRibbon", - "bounds": bounds.to_json(), - "color": self.color.to_json(), - }) - } -} - impl std::fmt::Debug for OpenPaths { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("OpenPaths") diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index dc2b0abd03..170f554814 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -27,6 +27,7 @@ context_menu = { path = "../context_menu" } client = { path = "../client" } clock = { path = "../clock" } contacts_panel = { path = "../contacts_panel" } +contacts_titlebar_item = { path = "../contacts_titlebar_item" } contacts_status_item = { path = "../contacts_status_item" } diagnostics = { path = "../diagnostics" } editor = { path = "../editor" } diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index cd906500ee..42bcd6b9bd 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -13,6 +13,7 @@ pub use client; use collections::VecDeque; pub use contacts_panel; use contacts_panel::ContactsPanel; +use contacts_titlebar_item::ContactsTitlebarItem; pub use editor; use editor::{Editor, MultiBuffer}; use gpui::{ @@ -224,7 +225,8 @@ pub fn initialize_workspace( app_state: &Arc, cx: &mut ViewContext, ) { - cx.subscribe(&cx.handle(), { + let workspace_handle = cx.handle(); + cx.subscribe(&workspace_handle, { move |_, _, event, cx| { if let workspace::Event::PaneAdded(pane) = event { pane.update(cx, |pane, cx| { @@ -278,6 +280,9 @@ pub fn initialize_workspace( })); }); + let contacts_titlebar_item = cx.add_view(|cx| ContactsTitlebarItem::new(&workspace_handle, cx)); + workspace.set_titlebar_item(contacts_titlebar_item, cx); + let project_panel = ProjectPanel::new(workspace.project().clone(), cx); let contact_panel = cx.add_view(|cx| { ContactsPanel::new( From 5a3a85b2c860f8d3bdb5daa463021f5f8c567632 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 27 Sep 2022 13:45:11 +0200 Subject: [PATCH 016/314] Introduce a `+` button in the titlebar --- .../src/contacts_titlebar_item.rs | 65 ++++++++++++++++++- crates/room/src/room.rs | 18 +---- crates/theme/src/theme.rs | 1 + crates/zed/src/main.rs | 1 + styles/src/styleTree/workspace.ts | 13 +++- 5 files changed, 78 insertions(+), 20 deletions(-) diff --git a/crates/contacts_titlebar_item/src/contacts_titlebar_item.rs b/crates/contacts_titlebar_item/src/contacts_titlebar_item.rs index 7035585a2f..a32b2923af 100644 --- a/crates/contacts_titlebar_item/src/contacts_titlebar_item.rs +++ b/crates/contacts_titlebar_item/src/contacts_titlebar_item.rs @@ -4,15 +4,27 @@ use gpui::{ color::Color, elements::*, geometry::{rect::RectF, vector::vec2f, PathBuilder}, + impl_internal_actions, json::{self, ToJson}, - Border, CursorStyle, Entity, ImageData, MouseButton, RenderContext, Subscription, View, - ViewContext, ViewHandle, WeakViewHandle, + Border, CursorStyle, Entity, ImageData, MouseButton, MutableAppContext, RenderContext, + Subscription, View, ViewContext, ViewHandle, WeakViewHandle, }; use settings::Settings; use std::{ops::Range, sync::Arc}; use theme::Theme; use workspace::{FollowNextCollaborator, ToggleFollow, Workspace}; +impl_internal_actions!(contacts_titlebar_item, [ToggleAddContactsPopover]); + +pub fn init(cx: &mut MutableAppContext) { + cx.add_action(ContactsTitlebarItem::toggle_add_contacts_popover); +} + +#[derive(Clone, PartialEq)] +struct ToggleAddContactsPopover { + button_rect: RectF, +} + pub struct ContactsTitlebarItem { workspace: WeakViewHandle, _subscriptions: Vec, @@ -36,6 +48,7 @@ impl View for ContactsTitlebarItem { let theme = cx.global::().theme.clone(); Flex::row() + .with_children(self.render_toggle_contacts_button(&workspace, &theme, cx)) .with_children(self.render_collaborators(&workspace, &theme, cx)) .with_children(self.render_current_user(&workspace, &theme, cx)) .with_children(self.render_connection_status(&workspace, cx)) @@ -52,6 +65,54 @@ impl ContactsTitlebarItem { } } + fn toggle_add_contacts_popover( + &mut self, + _action: &ToggleAddContactsPopover, + _cx: &mut ViewContext, + ) { + dbg!("!!!!!!!!!"); + } + + fn render_toggle_contacts_button( + &self, + workspace: &ViewHandle, + theme: &Theme, + cx: &mut RenderContext, + ) -> Option { + if !workspace.read(cx).client().status().borrow().is_connected() { + return None; + } + + Some( + MouseEventHandler::::new(0, cx, |state, _| { + let style = theme + .workspace + .titlebar + .add_collaborator_button + .style_for(state, false); + Svg::new("icons/plus_8.svg") + .with_color(style.color) + .constrained() + .with_width(style.icon_width) + .aligned() + .constrained() + .with_width(style.button_width) + .with_height(style.button_width) + .contained() + .with_style(style.container) + .boxed() + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, |event, cx| { + cx.dispatch_action(ToggleAddContactsPopover { + button_rect: event.region, + }); + }) + .aligned() + .boxed(), + ) + } + fn render_collaborators( &self, workspace: &ViewHandle, diff --git a/crates/room/src/room.rs b/crates/room/src/room.rs index c6daacd7e2..82363d4b19 100644 --- a/crates/room/src/room.rs +++ b/crates/room/src/room.rs @@ -87,10 +87,10 @@ impl Room { return Err(anyhow!("room is offline")); } + cx.notify(); self.status = RoomStatus::Offline; self.remote_participants.clear(); self.client.send(proto::LeaveRoom { id: self.id })?; - cx.notify(); Ok(()) } @@ -184,22 +184,6 @@ impl Room { todo!() } - - pub fn mute(&mut self) -> Task> { - if self.status.is_offline() { - return Task::ready(Err(anyhow!("room is offline"))); - } - - todo!() - } - - pub fn unmute(&mut self) -> Task> { - if self.status.is_offline() { - return Task::ready(Err(anyhow!("room is offline"))); - } - - todo!() - } } #[derive(Copy, Clone, PartialEq, Eq)] diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 739a4c7686..ca952a27fe 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -74,6 +74,7 @@ pub struct Titlebar { pub avatar: ImageStyle, pub sign_in_prompt: Interactive, pub outdated_warning: ContainedText, + pub add_collaborator_button: Interactive, } #[derive(Clone, Deserialize, Default)] diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 3bfd5e6e1a..aa84d6475b 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -108,6 +108,7 @@ fn main() { client::Channel::init(&client); client::init(client.clone(), cx); command_palette::init(cx); + contacts_titlebar_item::init(cx); editor::init(cx); go_to_line::init(cx); file_finder::init(cx); diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 9e81a06d3f..0473c974f7 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -16,6 +16,7 @@ export function workspaceBackground(theme: Theme) { export default function workspace(theme: Theme) { const titlebarPadding = 6; + const titlebarHeight = 33; return { background: backgroundColor(theme, 300), @@ -54,7 +55,7 @@ export default function workspace(theme: Theme) { titlebar: { avatarWidth: 18, avatarMargin: 8, - height: 33, + height: titlebarHeight, background: backgroundColor(theme, 100), padding: { left: 80, @@ -118,6 +119,16 @@ export default function workspace(theme: Theme) { }, cornerRadius: 6, }, + addCollaboratorButton: { + cornerRadius: 6, + color: iconColor(theme, "secondary"), + iconWidth: 8, + buttonWidth: 20, + hover: { + background: backgroundColor(theme, "on300", "hovered"), + color: iconColor(theme, "active"), + }, + }, }, toolbar: { height: 34, From 782309f369f366815fca39c886aef3db745ec82e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 27 Sep 2022 13:51:21 +0200 Subject: [PATCH 017/314] Rename `contacts_titlebar_item` to `collab_titlebar_item` --- Cargo.lock | 46 +++++++++---------- .../Cargo.toml | 4 +- .../src/collab_titlebar_item.rs} | 24 +++++----- crates/zed/Cargo.toml | 2 +- crates/zed/src/main.rs | 2 +- crates/zed/src/zed.rs | 6 +-- 6 files changed, 42 insertions(+), 42 deletions(-) rename crates/{contacts_titlebar_item => collab_titlebar_item}/Cargo.toml (95%) rename crates/{contacts_titlebar_item/src/contacts_titlebar_item.rs => collab_titlebar_item/src/collab_titlebar_item.rs} (95%) diff --git a/Cargo.lock b/Cargo.lock index 8537c51611..b895cd669e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1062,6 +1062,28 @@ dependencies = [ "workspace", ] +[[package]] +name = "collab_titlebar_item" +version = "0.1.0" +dependencies = [ + "anyhow", + "client", + "clock", + "collections", + "editor", + "futures", + "fuzzy", + "gpui", + "log", + "postage", + "project", + "serde", + "settings", + "theme", + "util", + "workspace", +] + [[package]] name = "collections" version = "0.1.0" @@ -1151,28 +1173,6 @@ dependencies = [ "workspace", ] -[[package]] -name = "contacts_titlebar_item" -version = "0.1.0" -dependencies = [ - "anyhow", - "client", - "clock", - "collections", - "editor", - "futures", - "fuzzy", - "gpui", - "log", - "postage", - "project", - "serde", - "settings", - "theme", - "util", - "workspace", -] - [[package]] name = "context_menu" version = "0.1.0" @@ -7180,11 +7180,11 @@ dependencies = [ "cli", "client", "clock", + "collab_titlebar_item", "collections", "command_palette", "contacts_panel", "contacts_status_item", - "contacts_titlebar_item", "context_menu", "ctor", "diagnostics", diff --git a/crates/contacts_titlebar_item/Cargo.toml b/crates/collab_titlebar_item/Cargo.toml similarity index 95% rename from crates/contacts_titlebar_item/Cargo.toml rename to crates/collab_titlebar_item/Cargo.toml index 771e364218..f165753992 100644 --- a/crates/contacts_titlebar_item/Cargo.toml +++ b/crates/collab_titlebar_item/Cargo.toml @@ -1,10 +1,10 @@ [package] -name = "contacts_titlebar_item" +name = "collab_titlebar_item" version = "0.1.0" edition = "2021" [lib] -path = "src/contacts_titlebar_item.rs" +path = "src/collab_titlebar_item.rs" doctest = false [features] diff --git a/crates/contacts_titlebar_item/src/contacts_titlebar_item.rs b/crates/collab_titlebar_item/src/collab_titlebar_item.rs similarity index 95% rename from crates/contacts_titlebar_item/src/contacts_titlebar_item.rs rename to crates/collab_titlebar_item/src/collab_titlebar_item.rs index a32b2923af..c3810992b6 100644 --- a/crates/contacts_titlebar_item/src/contacts_titlebar_item.rs +++ b/crates/collab_titlebar_item/src/collab_titlebar_item.rs @@ -14,29 +14,29 @@ use std::{ops::Range, sync::Arc}; use theme::Theme; use workspace::{FollowNextCollaborator, ToggleFollow, Workspace}; -impl_internal_actions!(contacts_titlebar_item, [ToggleAddContactsPopover]); +impl_internal_actions!(contacts_titlebar_item, [ToggleAddParticipantPopover]); pub fn init(cx: &mut MutableAppContext) { - cx.add_action(ContactsTitlebarItem::toggle_add_contacts_popover); + cx.add_action(CollabTitlebarItem::toggle_add_participant_popover); } #[derive(Clone, PartialEq)] -struct ToggleAddContactsPopover { +struct ToggleAddParticipantPopover { button_rect: RectF, } -pub struct ContactsTitlebarItem { +pub struct CollabTitlebarItem { workspace: WeakViewHandle, _subscriptions: Vec, } -impl Entity for ContactsTitlebarItem { +impl Entity for CollabTitlebarItem { type Event = (); } -impl View for ContactsTitlebarItem { +impl View for CollabTitlebarItem { fn ui_name() -> &'static str { - "ContactsTitlebarItem" + "CollabTitlebarItem" } fn render(&mut self, cx: &mut RenderContext) -> ElementBox { @@ -56,7 +56,7 @@ impl View for ContactsTitlebarItem { } } -impl ContactsTitlebarItem { +impl CollabTitlebarItem { pub fn new(workspace: &ViewHandle, cx: &mut ViewContext) -> Self { let observe_workspace = cx.observe(workspace, |_, _, cx| cx.notify()); Self { @@ -65,9 +65,9 @@ impl ContactsTitlebarItem { } } - fn toggle_add_contacts_popover( + fn toggle_add_participant_popover( &mut self, - _action: &ToggleAddContactsPopover, + _action: &ToggleAddParticipantPopover, _cx: &mut ViewContext, ) { dbg!("!!!!!!!!!"); @@ -84,7 +84,7 @@ impl ContactsTitlebarItem { } Some( - MouseEventHandler::::new(0, cx, |state, _| { + MouseEventHandler::::new(0, cx, |state, _| { let style = theme .workspace .titlebar @@ -104,7 +104,7 @@ impl ContactsTitlebarItem { }) .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, |event, cx| { - cx.dispatch_action(ToggleAddContactsPopover { + cx.dispatch_action(ToggleAddParticipantPopover { button_rect: event.region, }); }) diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 170f554814..6c2ce8ff07 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -21,13 +21,13 @@ auto_update = { path = "../auto_update" } breadcrumbs = { path = "../breadcrumbs" } chat_panel = { path = "../chat_panel" } cli = { path = "../cli" } +collab_titlebar_item = { path = "../collab_titlebar_item" } collections = { path = "../collections" } command_palette = { path = "../command_palette" } context_menu = { path = "../context_menu" } client = { path = "../client" } clock = { path = "../clock" } contacts_panel = { path = "../contacts_panel" } -contacts_titlebar_item = { path = "../contacts_titlebar_item" } contacts_status_item = { path = "../contacts_status_item" } diagnostics = { path = "../diagnostics" } editor = { path = "../editor" } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index aa84d6475b..99983c4d6d 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -107,8 +107,8 @@ fn main() { project::Project::init(&client); client::Channel::init(&client); client::init(client.clone(), cx); + collab_titlebar_item::init(cx); command_palette::init(cx); - contacts_titlebar_item::init(cx); editor::init(cx); go_to_line::init(cx); file_finder::init(cx); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 42bcd6b9bd..26888dc0d7 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -10,10 +10,10 @@ use anyhow::{anyhow, Context, Result}; use assets::Assets; use breadcrumbs::Breadcrumbs; pub use client; +use collab_titlebar_item::CollabTitlebarItem; use collections::VecDeque; pub use contacts_panel; use contacts_panel::ContactsPanel; -use contacts_titlebar_item::ContactsTitlebarItem; pub use editor; use editor::{Editor, MultiBuffer}; use gpui::{ @@ -280,8 +280,8 @@ pub fn initialize_workspace( })); }); - let contacts_titlebar_item = cx.add_view(|cx| ContactsTitlebarItem::new(&workspace_handle, cx)); - workspace.set_titlebar_item(contacts_titlebar_item, cx); + let collab_titlebar_item = cx.add_view(|cx| CollabTitlebarItem::new(&workspace_handle, cx)); + workspace.set_titlebar_item(collab_titlebar_item, cx); let project_panel = ProjectPanel::new(workspace.project().clone(), cx); let contact_panel = cx.add_view(|cx| { From 0db6eb2fb8afc395da70385abcdf05e23e4587d3 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 27 Sep 2022 14:27:06 +0200 Subject: [PATCH 018/314] Show add participant popover on click --- .../src/add_participant_popover.rs | 36 ++++++++ .../src/collab_titlebar_item.rs | 89 ++++++++++++------- crates/theme/src/theme.rs | 11 ++- styles/src/styleTree/workspace.ts | 16 +++- 4 files changed, 114 insertions(+), 38 deletions(-) create mode 100644 crates/collab_titlebar_item/src/add_participant_popover.rs diff --git a/crates/collab_titlebar_item/src/add_participant_popover.rs b/crates/collab_titlebar_item/src/add_participant_popover.rs new file mode 100644 index 0000000000..8c30501629 --- /dev/null +++ b/crates/collab_titlebar_item/src/add_participant_popover.rs @@ -0,0 +1,36 @@ +use gpui::{elements::*, Entity, RenderContext, View}; +use settings::Settings; + +pub struct AddParticipantPopover {} + +impl Entity for AddParticipantPopover { + type Event = (); +} + +impl View for AddParticipantPopover { + fn ui_name() -> &'static str { + "AddParticipantPopover" + } + + fn render(&mut self, cx: &mut RenderContext) -> ElementBox { + let theme = &cx + .global::() + .theme + .workspace + .titlebar + .add_participant_popover; + Empty::new() + .contained() + .with_style(theme.container) + .constrained() + .with_width(theme.width) + .with_height(theme.height) + .boxed() + } +} + +impl AddParticipantPopover { + pub fn new() -> Self { + Self {} + } +} diff --git a/crates/collab_titlebar_item/src/collab_titlebar_item.rs b/crates/collab_titlebar_item/src/collab_titlebar_item.rs index c3810992b6..de27fd4eb8 100644 --- a/crates/collab_titlebar_item/src/collab_titlebar_item.rs +++ b/crates/collab_titlebar_item/src/collab_titlebar_item.rs @@ -1,10 +1,13 @@ +mod add_participant_popover; + +use add_participant_popover::AddParticipantPopover; use client::{Authenticate, PeerId}; use clock::ReplicaId; use gpui::{ + actions, color::Color, elements::*, geometry::{rect::RectF, vector::vec2f, PathBuilder}, - impl_internal_actions, json::{self, ToJson}, Border, CursorStyle, Entity, ImageData, MouseButton, MutableAppContext, RenderContext, Subscription, View, ViewContext, ViewHandle, WeakViewHandle, @@ -14,19 +17,15 @@ use std::{ops::Range, sync::Arc}; use theme::Theme; use workspace::{FollowNextCollaborator, ToggleFollow, Workspace}; -impl_internal_actions!(contacts_titlebar_item, [ToggleAddParticipantPopover]); +actions!(contacts_titlebar_item, [ToggleAddParticipantPopover]); pub fn init(cx: &mut MutableAppContext) { cx.add_action(CollabTitlebarItem::toggle_add_participant_popover); } -#[derive(Clone, PartialEq)] -struct ToggleAddParticipantPopover { - button_rect: RectF, -} - pub struct CollabTitlebarItem { workspace: WeakViewHandle, + add_participant_popover: Option>, _subscriptions: Vec, } @@ -61,16 +60,24 @@ impl CollabTitlebarItem { let observe_workspace = cx.observe(workspace, |_, _, cx| cx.notify()); Self { workspace: workspace.downgrade(), + add_participant_popover: None, _subscriptions: vec![observe_workspace], } } fn toggle_add_participant_popover( &mut self, - _action: &ToggleAddParticipantPopover, - _cx: &mut ViewContext, + _: &ToggleAddParticipantPopover, + cx: &mut ViewContext, ) { - dbg!("!!!!!!!!!"); + match self.add_participant_popover.take() { + Some(_) => {} + None => { + let view = cx.add_view(|_| AddParticipantPopover::new()); + self.add_participant_popover = Some(view); + } + } + cx.notify(); } fn render_toggle_contacts_button( @@ -83,33 +90,47 @@ impl CollabTitlebarItem { return None; } + let titlebar = &theme.workspace.titlebar; + Some( - MouseEventHandler::::new(0, cx, |state, _| { - let style = theme - .workspace - .titlebar - .add_collaborator_button - .style_for(state, false); - Svg::new("icons/plus_8.svg") - .with_color(style.color) - .constrained() - .with_width(style.icon_width) + Stack::new() + .with_child( + MouseEventHandler::::new(0, cx, |state, _| { + let style = titlebar.add_participant_button.style_for(state, false); + Svg::new("icons/plus_8.svg") + .with_color(style.color) + .constrained() + .with_width(style.icon_width) + .aligned() + .constrained() + .with_width(style.button_width) + .with_height(style.button_width) + .contained() + .with_style(style.container) + .boxed() + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, |_, cx| { + cx.dispatch_action(ToggleAddParticipantPopover); + }) .aligned() - .constrained() - .with_width(style.button_width) - .with_height(style.button_width) - .contained() - .with_style(style.container) + .boxed(), + ) + .with_children(self.add_participant_popover.as_ref().map(|popover| { + Overlay::new( + ChildView::new(popover) + .contained() + .with_margin_top(titlebar.height) + .with_margin_right( + -titlebar.add_participant_button.default.button_width, + ) + .boxed(), + ) + .with_fit_mode(OverlayFitMode::SwitchAnchor) + .with_anchor_corner(AnchorCorner::BottomLeft) .boxed() - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, |event, cx| { - cx.dispatch_action(ToggleAddParticipantPopover { - button_rect: event.region, - }); - }) - .aligned() - .boxed(), + })) + .boxed(), ) } diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index ca952a27fe..4446e4e06f 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -74,7 +74,16 @@ pub struct Titlebar { pub avatar: ImageStyle, pub sign_in_prompt: Interactive, pub outdated_warning: ContainedText, - pub add_collaborator_button: Interactive, + pub add_participant_button: Interactive, + pub add_participant_popover: AddParticipantPopover, +} + +#[derive(Clone, Deserialize, Default)] +pub struct AddParticipantPopover { + #[serde(flatten)] + pub container: ContainerStyle, + pub height: f32, + pub width: f32, } #[derive(Clone, Deserialize, Default)] diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 0473c974f7..b10828828b 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -5,6 +5,7 @@ import { border, iconColor, modalShadow, + popoverShadow, text, } from "./components"; import statusBar from "./statusBar"; @@ -16,7 +17,6 @@ export function workspaceBackground(theme: Theme) { export default function workspace(theme: Theme) { const titlebarPadding = 6; - const titlebarHeight = 33; return { background: backgroundColor(theme, 300), @@ -55,7 +55,7 @@ export default function workspace(theme: Theme) { titlebar: { avatarWidth: 18, avatarMargin: 8, - height: titlebarHeight, + height: 33, background: backgroundColor(theme, 100), padding: { left: 80, @@ -119,7 +119,7 @@ export default function workspace(theme: Theme) { }, cornerRadius: 6, }, - addCollaboratorButton: { + addParticipantButton: { cornerRadius: 6, color: iconColor(theme, "secondary"), iconWidth: 8, @@ -129,6 +129,16 @@ export default function workspace(theme: Theme) { color: iconColor(theme, "active"), }, }, + addParticipantPopover: { + background: backgroundColor(theme, 300, "base"), + cornerRadius: 6, + padding: 6, + shadow: popoverShadow(theme), + border: border(theme, "primary"), + margin: { top: -5 }, + width: 255, + height: 200 + } }, toolbar: { height: 34, From 0a29e13d4ac9ac1351bbb7f5fda7996709fe520a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 27 Sep 2022 14:34:13 +0200 Subject: [PATCH 019/314] Add active style when participant popover is open --- crates/collab_titlebar_item/src/collab_titlebar_item.rs | 4 +++- styles/src/styleTree/workspace.ts | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/collab_titlebar_item/src/collab_titlebar_item.rs b/crates/collab_titlebar_item/src/collab_titlebar_item.rs index de27fd4eb8..a48318d204 100644 --- a/crates/collab_titlebar_item/src/collab_titlebar_item.rs +++ b/crates/collab_titlebar_item/src/collab_titlebar_item.rs @@ -96,7 +96,9 @@ impl CollabTitlebarItem { Stack::new() .with_child( MouseEventHandler::::new(0, cx, |state, _| { - let style = titlebar.add_participant_button.style_for(state, false); + let style = titlebar + .add_participant_button + .style_for(state, self.add_participant_popover.is_some()); Svg::new("icons/plus_8.svg") .with_color(style.color) .constrained() diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index b10828828b..75f11b3942 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -124,6 +124,10 @@ export default function workspace(theme: Theme) { color: iconColor(theme, "secondary"), iconWidth: 8, buttonWidth: 20, + active: { + background: backgroundColor(theme, "on300", "active"), + color: iconColor(theme, "active"), + }, hover: { background: backgroundColor(theme, "on300", "hovered"), color: iconColor(theme, "active"), From 4b7323997225e9a9ceb922d965f49cacf3033cb3 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 27 Sep 2022 15:55:02 +0200 Subject: [PATCH 020/314] WIP: Start moving contacts panel into "add participants" popover --- Cargo.lock | 1 + crates/collab_titlebar_item/Cargo.toml | 1 + .../src/add_participant_popover.rs | 706 +++++++++++++++++- .../src/collab_titlebar_item.rs | 20 +- styles/src/styleTree/workspace.ts | 6 +- 5 files changed, 711 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b895cd669e..a8a89478fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1075,6 +1075,7 @@ dependencies = [ "fuzzy", "gpui", "log", + "menu", "postage", "project", "serde", diff --git a/crates/collab_titlebar_item/Cargo.toml b/crates/collab_titlebar_item/Cargo.toml index f165753992..4f85ddd8d9 100644 --- a/crates/collab_titlebar_item/Cargo.toml +++ b/crates/collab_titlebar_item/Cargo.toml @@ -26,6 +26,7 @@ collections = { path = "../collections" } editor = { path = "../editor" } fuzzy = { path = "../fuzzy" } gpui = { path = "../gpui" } +menu = { path = "../menu" } project = { path = "../project" } settings = { path = "../settings" } theme = { path = "../theme" } diff --git a/crates/collab_titlebar_item/src/add_participant_popover.rs b/crates/collab_titlebar_item/src/add_participant_popover.rs index 8c30501629..95d37849cb 100644 --- a/crates/collab_titlebar_item/src/add_participant_popover.rs +++ b/crates/collab_titlebar_item/src/add_participant_popover.rs @@ -1,10 +1,577 @@ -use gpui::{elements::*, Entity, RenderContext, View}; -use settings::Settings; +use std::sync::Arc; -pub struct AddParticipantPopover {} +use client::{Contact, User, UserStore}; +use editor::{Cancel, Editor}; +use fuzzy::{match_strings, StringMatchCandidate}; +use gpui::{ + elements::*, impl_internal_actions, keymap, AppContext, ClipboardItem, CursorStyle, Entity, + ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, View, ViewContext, + ViewHandle, +}; +use menu::{Confirm, SelectNext, SelectPrev}; +use settings::Settings; +use theme::IconButton; + +impl_internal_actions!(contacts_panel, [ToggleExpanded]); + +pub fn init(cx: &mut MutableAppContext) { + cx.add_action(AddParticipantPopover::clear_filter); + cx.add_action(AddParticipantPopover::select_next); + cx.add_action(AddParticipantPopover::select_prev); + cx.add_action(AddParticipantPopover::confirm); + cx.add_action(AddParticipantPopover::toggle_expanded); +} + +#[derive(Clone, PartialEq)] +struct ToggleExpanded(Section); + +#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)] +enum Section { + Requests, + Online, + Offline, +} + +#[derive(Clone)] +enum ContactEntry { + Header(Section), + IncomingRequest(Arc), + OutgoingRequest(Arc), + Contact(Arc), +} + +impl PartialEq for ContactEntry { + fn eq(&self, other: &Self) -> bool { + match self { + ContactEntry::Header(section_1) => { + if let ContactEntry::Header(section_2) = other { + return section_1 == section_2; + } + } + ContactEntry::IncomingRequest(user_1) => { + if let ContactEntry::IncomingRequest(user_2) = other { + return user_1.id == user_2.id; + } + } + ContactEntry::OutgoingRequest(user_1) => { + if let ContactEntry::OutgoingRequest(user_2) = other { + return user_1.id == user_2.id; + } + } + ContactEntry::Contact(contact_1) => { + if let ContactEntry::Contact(contact_2) = other { + return contact_1.user.id == contact_2.user.id; + } + } + } + false + } +} + +pub enum Event { + Dismissed, +} + +pub struct AddParticipantPopover { + entries: Vec, + match_candidates: Vec, + list_state: ListState, + user_store: ModelHandle, + filter_editor: ViewHandle, + collapsed_sections: Vec
, + selection: Option, + _maintain_contacts: Subscription, +} + +impl AddParticipantPopover { + pub fn new(user_store: ModelHandle, cx: &mut ViewContext) -> Self { + let filter_editor = cx.add_view(|cx| { + let mut editor = Editor::single_line( + Some(|theme| theme.contacts_panel.user_query_editor.clone()), + cx, + ); + editor.set_placeholder_text("Filter contacts", cx); + editor + }); + + cx.subscribe(&filter_editor, |this, _, event, cx| { + if let editor::Event::BufferEdited = event { + let query = this.filter_editor.read(cx).text(cx); + if !query.is_empty() { + this.selection.take(); + } + this.update_entries(cx); + if !query.is_empty() { + this.selection = this + .entries + .iter() + .position(|entry| !matches!(entry, ContactEntry::Header(_))); + } + } + }) + .detach(); + + let list_state = ListState::new(0, Orientation::Top, 1000., cx, move |this, ix, cx| { + let theme = cx.global::().theme.clone(); + let is_selected = this.selection == Some(ix); + + match &this.entries[ix] { + ContactEntry::Header(section) => { + let is_collapsed = this.collapsed_sections.contains(section); + Self::render_header( + *section, + &theme.contacts_panel, + is_selected, + is_collapsed, + cx, + ) + } + ContactEntry::IncomingRequest(user) => Self::render_contact_request( + user.clone(), + this.user_store.clone(), + &theme.contacts_panel, + true, + is_selected, + cx, + ), + ContactEntry::OutgoingRequest(user) => Self::render_contact_request( + user.clone(), + this.user_store.clone(), + &theme.contacts_panel, + false, + is_selected, + cx, + ), + ContactEntry::Contact(contact) => { + Self::render_contact(&contact.user, &theme.contacts_panel, is_selected) + } + } + }); + + let mut this = Self { + list_state, + selection: None, + collapsed_sections: Default::default(), + entries: Default::default(), + match_candidates: Default::default(), + filter_editor, + _maintain_contacts: cx.observe(&user_store, |this, _, cx| this.update_entries(cx)), + user_store, + }; + this.update_entries(cx); + this + } + + fn clear_filter(&mut self, _: &Cancel, cx: &mut ViewContext) { + let did_clear = self.filter_editor.update(cx, |editor, cx| { + if editor.buffer().read(cx).len(cx) > 0 { + editor.set_text("", cx); + true + } else { + false + } + }); + if !did_clear { + cx.emit(Event::Dismissed); + } + } + + fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext) { + if let Some(ix) = self.selection { + if self.entries.len() > ix + 1 { + self.selection = Some(ix + 1); + } + } else if !self.entries.is_empty() { + self.selection = Some(0); + } + cx.notify(); + self.list_state.reset(self.entries.len()); + } + + fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext) { + if let Some(ix) = self.selection { + if ix > 0 { + self.selection = Some(ix - 1); + } else { + self.selection = None; + } + } + cx.notify(); + self.list_state.reset(self.entries.len()); + } + + fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext) { + if let Some(selection) = self.selection { + if let Some(entry) = self.entries.get(selection) { + match entry { + ContactEntry::Header(section) => { + let section = *section; + self.toggle_expanded(&ToggleExpanded(section), cx); + } + _ => {} + } + } + } + } + + fn toggle_expanded(&mut self, action: &ToggleExpanded, cx: &mut ViewContext) { + let section = action.0; + if let Some(ix) = self.collapsed_sections.iter().position(|s| *s == section) { + self.collapsed_sections.remove(ix); + } else { + self.collapsed_sections.push(section); + } + self.update_entries(cx); + } + + fn update_entries(&mut self, cx: &mut ViewContext) { + let user_store = self.user_store.read(cx); + let query = self.filter_editor.read(cx).text(cx); + let executor = cx.background().clone(); + + let prev_selected_entry = self.selection.and_then(|ix| self.entries.get(ix).cloned()); + self.entries.clear(); + + let mut request_entries = Vec::new(); + let incoming = user_store.incoming_contact_requests(); + if !incoming.is_empty() { + self.match_candidates.clear(); + self.match_candidates + .extend( + incoming + .iter() + .enumerate() + .map(|(ix, user)| StringMatchCandidate { + id: ix, + string: user.github_login.clone(), + char_bag: user.github_login.chars().collect(), + }), + ); + let matches = executor.block(match_strings( + &self.match_candidates, + &query, + true, + usize::MAX, + &Default::default(), + executor.clone(), + )); + request_entries.extend( + matches + .iter() + .map(|mat| ContactEntry::IncomingRequest(incoming[mat.candidate_id].clone())), + ); + } + + let outgoing = user_store.outgoing_contact_requests(); + if !outgoing.is_empty() { + self.match_candidates.clear(); + self.match_candidates + .extend( + outgoing + .iter() + .enumerate() + .map(|(ix, user)| StringMatchCandidate { + id: ix, + string: user.github_login.clone(), + char_bag: user.github_login.chars().collect(), + }), + ); + let matches = executor.block(match_strings( + &self.match_candidates, + &query, + true, + usize::MAX, + &Default::default(), + executor.clone(), + )); + request_entries.extend( + matches + .iter() + .map(|mat| ContactEntry::OutgoingRequest(outgoing[mat.candidate_id].clone())), + ); + } + + if !request_entries.is_empty() { + self.entries.push(ContactEntry::Header(Section::Requests)); + if !self.collapsed_sections.contains(&Section::Requests) { + self.entries.append(&mut request_entries); + } + } + + let current_user = user_store.current_user(); + + let contacts = user_store.contacts(); + if !contacts.is_empty() { + // Always put the current user first. + self.match_candidates.clear(); + self.match_candidates.reserve(contacts.len()); + self.match_candidates.push(StringMatchCandidate { + id: 0, + string: Default::default(), + char_bag: Default::default(), + }); + for (ix, contact) in contacts.iter().enumerate() { + let candidate = StringMatchCandidate { + id: ix, + string: contact.user.github_login.clone(), + char_bag: contact.user.github_login.chars().collect(), + }; + if current_user + .as_ref() + .map_or(false, |current_user| current_user.id == contact.user.id) + { + self.match_candidates[0] = candidate; + } else { + self.match_candidates.push(candidate); + } + } + if self.match_candidates[0].string.is_empty() { + self.match_candidates.remove(0); + } + + let matches = executor.block(match_strings( + &self.match_candidates, + &query, + true, + usize::MAX, + &Default::default(), + executor.clone(), + )); + + let (online_contacts, offline_contacts) = matches + .iter() + .partition::, _>(|mat| contacts[mat.candidate_id].online); + + for (matches, section) in [ + (online_contacts, Section::Online), + (offline_contacts, Section::Offline), + ] { + if !matches.is_empty() { + self.entries.push(ContactEntry::Header(section)); + if !self.collapsed_sections.contains(§ion) { + for mat in matches { + let contact = &contacts[mat.candidate_id]; + self.entries.push(ContactEntry::Contact(contact.clone())); + } + } + } + } + } + + if let Some(prev_selected_entry) = prev_selected_entry { + self.selection.take(); + for (ix, entry) in self.entries.iter().enumerate() { + if *entry == prev_selected_entry { + self.selection = Some(ix); + break; + } + } + } + + self.list_state.reset(self.entries.len()); + cx.notify(); + } + + fn render_header( + section: Section, + theme: &theme::ContactsPanel, + is_selected: bool, + is_collapsed: bool, + cx: &mut RenderContext, + ) -> ElementBox { + enum Header {} + + let header_style = theme.header_row.style_for(Default::default(), is_selected); + let text = match section { + Section::Requests => "Requests", + Section::Online => "Online", + Section::Offline => "Offline", + }; + let icon_size = theme.section_icon_size; + MouseEventHandler::
::new(section as usize, cx, |_, _| { + Flex::row() + .with_child( + Svg::new(if is_collapsed { + "icons/chevron_right_8.svg" + } else { + "icons/chevron_down_8.svg" + }) + .with_color(header_style.text.color) + .constrained() + .with_max_width(icon_size) + .with_max_height(icon_size) + .aligned() + .constrained() + .with_width(icon_size) + .boxed(), + ) + .with_child( + Label::new(text.to_string(), header_style.text.clone()) + .aligned() + .left() + .contained() + .with_margin_left(theme.contact_username.container.margin.left) + .flex(1., true) + .boxed(), + ) + .constrained() + .with_height(theme.row_height) + .contained() + .with_style(header_style.container) + .boxed() + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, cx| { + cx.dispatch_action(ToggleExpanded(section)) + }) + .boxed() + } + + fn render_contact(user: &User, theme: &theme::ContactsPanel, is_selected: bool) -> ElementBox { + Flex::row() + .with_children(user.avatar.clone().map(|avatar| { + Image::new(avatar) + .with_style(theme.contact_avatar) + .aligned() + .left() + .boxed() + })) + .with_child( + Label::new( + user.github_login.clone(), + theme.contact_username.text.clone(), + ) + .contained() + .with_style(theme.contact_username.container) + .aligned() + .left() + .flex(1., true) + .boxed(), + ) + .constrained() + .with_height(theme.row_height) + .contained() + .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) + .boxed() + } + + fn render_contact_request( + user: Arc, + user_store: ModelHandle, + theme: &theme::ContactsPanel, + is_incoming: bool, + is_selected: bool, + cx: &mut RenderContext, + ) -> ElementBox { + enum Decline {} + enum Accept {} + enum Cancel {} + + let mut row = Flex::row() + .with_children(user.avatar.clone().map(|avatar| { + Image::new(avatar) + .with_style(theme.contact_avatar) + .aligned() + .left() + .boxed() + })) + .with_child( + Label::new( + user.github_login.clone(), + theme.contact_username.text.clone(), + ) + .contained() + .with_style(theme.contact_username.container) + .aligned() + .left() + .flex(1., true) + .boxed(), + ); + + let user_id = user.id; + let is_contact_request_pending = user_store.read(cx).is_contact_request_pending(&user); + let button_spacing = theme.contact_button_spacing; + + if is_incoming { + row.add_children([ + MouseEventHandler::::new(user.id as usize, cx, |mouse_state, _| { + let button_style = if is_contact_request_pending { + &theme.disabled_button + } else { + theme.contact_button.style_for(mouse_state, false) + }; + render_icon_button(button_style, "icons/x_mark_8.svg") + .aligned() + // .flex_float() + .boxed() + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, cx| { + todo!(); + // cx.dispatch_action(RespondToContactRequest { + // user_id, + // accept: false, + // }) + }) + // .flex_float() + .contained() + .with_margin_right(button_spacing) + .boxed(), + MouseEventHandler::::new(user.id as usize, cx, |mouse_state, _| { + let button_style = if is_contact_request_pending { + &theme.disabled_button + } else { + theme.contact_button.style_for(mouse_state, false) + }; + render_icon_button(button_style, "icons/check_8.svg") + .aligned() + .flex_float() + .boxed() + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, cx| { + todo!() + // cx.dispatch_action(RespondToContactRequest { + // user_id, + // accept: true, + // }) + }) + .boxed(), + ]); + } else { + row.add_child( + MouseEventHandler::::new(user.id as usize, cx, |mouse_state, _| { + let button_style = if is_contact_request_pending { + &theme.disabled_button + } else { + theme.contact_button.style_for(mouse_state, false) + }; + render_icon_button(button_style, "icons/x_mark_8.svg") + .aligned() + .flex_float() + .boxed() + }) + .with_padding(Padding::uniform(2.)) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, cx| { + todo!() + // cx.dispatch_action(RemoveContact(user_id)) + }) + .flex_float() + .boxed(), + ); + } + + row.constrained() + .with_height(theme.row_height) + .contained() + .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) + .boxed() + } +} impl Entity for AddParticipantPopover { - type Event = (); + type Event = Event; } impl View for AddParticipantPopover { @@ -12,25 +579,128 @@ impl View for AddParticipantPopover { "AddParticipantPopover" } + fn keymap_context(&self, _: &AppContext) -> keymap::Context { + let mut cx = Self::default_keymap_context(); + cx.set.insert("menu".into()); + cx + } + fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - let theme = &cx - .global::() - .theme - .workspace - .titlebar - .add_participant_popover; - Empty::new() + enum AddContact {} + let theme = cx.global::().theme.clone(); + + Flex::column() + .with_child( + Flex::row() + .with_child( + ChildView::new(self.filter_editor.clone()) + .contained() + .with_style(theme.contacts_panel.user_query_editor.container) + .flex(1., true) + .boxed(), + ) + .with_child( + MouseEventHandler::::new(0, cx, |_, _| { + Svg::new("icons/user_plus_16.svg") + .with_color(theme.contacts_panel.add_contact_button.color) + .constrained() + .with_height(16.) + .contained() + .with_style(theme.contacts_panel.add_contact_button.container) + .aligned() + .boxed() + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, |_, cx| { + todo!() + // cx.dispatch_action(contact_finder::Toggle) + }) + .boxed(), + ) + .constrained() + .with_height(theme.contacts_panel.user_query_editor_height) + .boxed(), + ) + .with_child(List::new(self.list_state.clone()).flex(1., false).boxed()) + .with_children( + self.user_store + .read(cx) + .invite_info() + .cloned() + .and_then(|info| { + enum InviteLink {} + + if info.count > 0 { + Some( + MouseEventHandler::::new(0, cx, |state, cx| { + let style = theme + .contacts_panel + .invite_row + .style_for(state, false) + .clone(); + + let copied = cx.read_from_clipboard().map_or(false, |item| { + item.text().as_str() == info.url.as_ref() + }); + + Label::new( + format!( + "{} invite link ({} left)", + if copied { "Copied" } else { "Copy" }, + info.count + ), + style.label.clone(), + ) + .aligned() + .left() + .constrained() + .with_height(theme.contacts_panel.row_height) + .contained() + .with_style(style.container) + .boxed() + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, cx| { + cx.write_to_clipboard(ClipboardItem::new(info.url.to_string())); + cx.notify(); + }) + .boxed(), + ) + } else { + None + } + }), + ) .contained() - .with_style(theme.container) + .with_style(theme.workspace.titlebar.add_participant_popover.container) .constrained() - .with_width(theme.width) - .with_height(theme.height) + .with_width(theme.workspace.titlebar.add_participant_popover.width) + .with_height(theme.workspace.titlebar.add_participant_popover.height) .boxed() } + + fn on_focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext) { + if !self.filter_editor.is_focused(cx) { + cx.focus(&self.filter_editor); + } + } + + fn on_focus_out(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext) { + if !self.filter_editor.is_focused(cx) { + cx.emit(Event::Dismissed); + } + } } -impl AddParticipantPopover { - pub fn new() -> Self { - Self {} - } +fn render_icon_button(style: &IconButton, svg_path: &'static str) -> impl Element { + Svg::new(svg_path) + .with_color(style.color) + .constrained() + .with_width(style.icon_width) + .aligned() + .contained() + .with_style(style.container) + .constrained() + .with_width(style.button_width) + .with_height(style.button_width) } diff --git a/crates/collab_titlebar_item/src/collab_titlebar_item.rs b/crates/collab_titlebar_item/src/collab_titlebar_item.rs index a48318d204..b080242a8f 100644 --- a/crates/collab_titlebar_item/src/collab_titlebar_item.rs +++ b/crates/collab_titlebar_item/src/collab_titlebar_item.rs @@ -20,6 +20,7 @@ use workspace::{FollowNextCollaborator, ToggleFollow, Workspace}; actions!(contacts_titlebar_item, [ToggleAddParticipantPopover]); pub fn init(cx: &mut MutableAppContext) { + add_participant_popover::init(cx); cx.add_action(CollabTitlebarItem::toggle_add_participant_popover); } @@ -73,8 +74,23 @@ impl CollabTitlebarItem { match self.add_participant_popover.take() { Some(_) => {} None => { - let view = cx.add_view(|_| AddParticipantPopover::new()); - self.add_participant_popover = Some(view); + if let Some(workspace) = self.workspace.upgrade(cx) { + let user_store = workspace.read(cx).user_store().clone(); + let view = cx.add_view(|cx| AddParticipantPopover::new(user_store, cx)); + cx.focus(&view); + cx.subscribe(&view, |this, _, event, cx| { + match event { + add_participant_popover::Event::Dismissed => { + dbg!("dismissed"); + this.add_participant_popover = None; + } + } + + cx.notify(); + }) + .detach(); + self.add_participant_popover = Some(view); + } } } cx.notify(); diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 75f11b3942..156ed62fba 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -136,12 +136,12 @@ export default function workspace(theme: Theme) { addParticipantPopover: { background: backgroundColor(theme, 300, "base"), cornerRadius: 6, - padding: 6, + padding: { top: 6 }, shadow: popoverShadow(theme), border: border(theme, "primary"), margin: { top: -5 }, - width: 255, - height: 200 + width: 250, + height: 300 } }, toolbar: { From 1d1bd3975a50fa513ec5ada150a218eb2c248c9b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 27 Sep 2022 18:24:22 +0200 Subject: [PATCH 021/314] Remove current user from contacts Co-Authored-By: Nathan Sobo Co-Authored-By: Mikayla Maki --- crates/collab/src/db.rs | 220 +++-------- crates/collab/src/integration_tests.rs | 342 ++++++++++-------- .../src/add_participant_popover.rs | 37 +- .../src/collab_titlebar_item.rs | 1 - 4 files changed, 264 insertions(+), 336 deletions(-) diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs index eeb598413e..876d16b60b 100644 --- a/crates/collab/src/db.rs +++ b/crates/collab/src/db.rs @@ -842,10 +842,7 @@ impl Db for PostgresDb { .bind(user_id) .fetch(&self.pool); - let mut contacts = vec![Contact::Accepted { - user_id, - should_notify: false, - }]; + let mut contacts = Vec::new(); while let Some(row) = rows.next().await { let (user_id_a, user_id_b, a_to_b, accepted, should_notify) = row?; @@ -2026,13 +2023,7 @@ pub mod tests { let user_3 = db.create_user("user3", None, false).await.unwrap(); // User starts with no contacts - assert_eq!( - db.get_contacts(user_1).await.unwrap(), - vec![Contact::Accepted { - user_id: user_1, - should_notify: false - }], - ); + assert_eq!(db.get_contacts(user_1).await.unwrap(), vec![]); // User requests a contact. Both users see the pending request. db.send_contact_request(user_1, user_2).await.unwrap(); @@ -2040,26 +2031,14 @@ pub mod tests { assert!(!db.has_contact(user_2, user_1).await.unwrap()); assert_eq!( db.get_contacts(user_1).await.unwrap(), - &[ - Contact::Accepted { - user_id: user_1, - should_notify: false - }, - Contact::Outgoing { user_id: user_2 } - ], + &[Contact::Outgoing { user_id: user_2 }], ); assert_eq!( db.get_contacts(user_2).await.unwrap(), - &[ - Contact::Incoming { - user_id: user_1, - should_notify: true - }, - Contact::Accepted { - user_id: user_2, - should_notify: false - }, - ] + &[Contact::Incoming { + user_id: user_1, + should_notify: true + }] ); // User 2 dismisses the contact request notification without accepting or rejecting. @@ -2072,16 +2051,10 @@ pub mod tests { .unwrap(); assert_eq!( db.get_contacts(user_2).await.unwrap(), - &[ - Contact::Incoming { - user_id: user_1, - should_notify: false - }, - Contact::Accepted { - user_id: user_2, - should_notify: false - }, - ] + &[Contact::Incoming { + user_id: user_1, + should_notify: false + }] ); // User can't accept their own contact request @@ -2095,31 +2068,19 @@ pub mod tests { .unwrap(); assert_eq!( db.get_contacts(user_1).await.unwrap(), - &[ - Contact::Accepted { - user_id: user_1, - should_notify: false - }, - Contact::Accepted { - user_id: user_2, - should_notify: true - } - ], + &[Contact::Accepted { + user_id: user_2, + should_notify: true + }], ); assert!(db.has_contact(user_1, user_2).await.unwrap()); assert!(db.has_contact(user_2, user_1).await.unwrap()); assert_eq!( db.get_contacts(user_2).await.unwrap(), - &[ - Contact::Accepted { - user_id: user_1, - should_notify: false, - }, - Contact::Accepted { - user_id: user_2, - should_notify: false, - }, - ] + &[Contact::Accepted { + user_id: user_1, + should_notify: false, + }] ); // Users cannot re-request existing contacts. @@ -2132,16 +2093,10 @@ pub mod tests { .unwrap_err(); assert_eq!( db.get_contacts(user_1).await.unwrap(), - &[ - Contact::Accepted { - user_id: user_1, - should_notify: false - }, - Contact::Accepted { - user_id: user_2, - should_notify: true, - }, - ] + &[Contact::Accepted { + user_id: user_2, + should_notify: true, + }] ); // Users can dismiss notifications of other users accepting their requests. @@ -2150,16 +2105,10 @@ pub mod tests { .unwrap(); assert_eq!( db.get_contacts(user_1).await.unwrap(), - &[ - Contact::Accepted { - user_id: user_1, - should_notify: false - }, - Contact::Accepted { - user_id: user_2, - should_notify: false, - }, - ] + &[Contact::Accepted { + user_id: user_2, + should_notify: false, + },] ); // Users send each other concurrent contact requests and @@ -2169,10 +2118,6 @@ pub mod tests { assert_eq!( db.get_contacts(user_1).await.unwrap(), &[ - Contact::Accepted { - user_id: user_1, - should_notify: false - }, Contact::Accepted { user_id: user_2, should_notify: false, @@ -2185,16 +2130,10 @@ pub mod tests { ); assert_eq!( db.get_contacts(user_3).await.unwrap(), - &[ - Contact::Accepted { - user_id: user_1, - should_notify: false - }, - Contact::Accepted { - user_id: user_3, - should_notify: false - } - ], + &[Contact::Accepted { + user_id: user_1, + should_notify: false + }], ); // User declines a contact request. Both users see that it is gone. @@ -2206,29 +2145,17 @@ pub mod tests { assert!(!db.has_contact(user_3, user_2).await.unwrap()); assert_eq!( db.get_contacts(user_2).await.unwrap(), - &[ - Contact::Accepted { - user_id: user_1, - should_notify: false - }, - Contact::Accepted { - user_id: user_2, - should_notify: false - } - ] + &[Contact::Accepted { + user_id: user_1, + should_notify: false + }] ); assert_eq!( db.get_contacts(user_3).await.unwrap(), - &[ - Contact::Accepted { - user_id: user_1, - should_notify: false - }, - Contact::Accepted { - user_id: user_3, - should_notify: false - } - ], + &[Contact::Accepted { + user_id: user_1, + should_notify: false + }], ); } } @@ -2261,29 +2188,17 @@ pub mod tests { assert_eq!(invite_count, 1); assert_eq!( db.get_contacts(user1).await.unwrap(), - [ - Contact::Accepted { - user_id: user1, - should_notify: false - }, - Contact::Accepted { - user_id: user2, - should_notify: true - } - ] + [Contact::Accepted { + user_id: user2, + should_notify: true + }] ); assert_eq!( db.get_contacts(user2).await.unwrap(), - [ - Contact::Accepted { - user_id: user1, - should_notify: false - }, - Contact::Accepted { - user_id: user2, - should_notify: false - } - ] + [Contact::Accepted { + user_id: user1, + should_notify: false + }] ); // User 3 redeems the invite code and becomes a contact of user 1. @@ -2296,10 +2211,6 @@ pub mod tests { assert_eq!( db.get_contacts(user1).await.unwrap(), [ - Contact::Accepted { - user_id: user1, - should_notify: false - }, Contact::Accepted { user_id: user2, should_notify: true @@ -2312,16 +2223,10 @@ pub mod tests { ); assert_eq!( db.get_contacts(user3).await.unwrap(), - [ - Contact::Accepted { - user_id: user1, - should_notify: false - }, - Contact::Accepted { - user_id: user3, - should_notify: false - }, - ] + [Contact::Accepted { + user_id: user1, + should_notify: false + }] ); // Trying to reedem the code for the third time results in an error. @@ -2346,10 +2251,6 @@ pub mod tests { assert_eq!( db.get_contacts(user1).await.unwrap(), [ - Contact::Accepted { - user_id: user1, - should_notify: false - }, Contact::Accepted { user_id: user2, should_notify: true @@ -2366,16 +2267,10 @@ pub mod tests { ); assert_eq!( db.get_contacts(user4).await.unwrap(), - [ - Contact::Accepted { - user_id: user1, - should_notify: false - }, - Contact::Accepted { - user_id: user4, - should_notify: false - }, - ] + [Contact::Accepted { + user_id: user1, + should_notify: false + }] ); // An existing user cannot redeem invite codes. @@ -2704,10 +2599,7 @@ pub mod tests { async fn get_contacts(&self, id: UserId) -> Result> { self.background.simulate_random_delay().await; - let mut contacts = vec![Contact::Accepted { - user_id: id, - should_notify: false, - }]; + let mut contacts = Vec::new(); for contact in self.contacts.lock().iter() { if contact.requester_id == id { diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index c235a31c55..e04bd80c79 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -3909,78 +3909,122 @@ async fn test_contacts( .await; deterministic.run_until_parked(); - for (client, cx) in [(&client_a, &cx_a), (&client_b, &cx_b), (&client_c, &cx_c)] { - client.user_store.read_with(*cx, |store, _| { - assert_eq!( - contacts(store), - [ - ("user_a", true, vec![]), - ("user_b", true, vec![]), - ("user_c", true, vec![]) - ], - "{} has the wrong contacts", - client.username - ) - }); - } + assert_eq!( + contacts(&client_a, cx_a), + [ + ("user_b".to_string(), true, vec![]), + ("user_c".to_string(), true, vec![]) + ] + ); + assert_eq!( + contacts(&client_b, cx_b), + [ + ("user_a".to_string(), true, vec![]), + ("user_c".to_string(), true, vec![]) + ] + ); + assert_eq!( + contacts(&client_c, cx_c), + [ + ("user_a".to_string(), true, vec![]), + ("user_b".to_string(), true, vec![]) + ] + ); // Share a project as client A. client_a.fs.create_dir(Path::new("/a")).await.unwrap(); let (project_a, _) = client_a.build_local_project("/a", cx_a).await; deterministic.run_until_parked(); - for (client, cx) in [(&client_a, &cx_a), (&client_b, &cx_b), (&client_c, &cx_c)] { - client.user_store.read_with(*cx, |store, _| { - assert_eq!( - contacts(store), - [ - ("user_a", true, vec![("a", vec![])]), - ("user_b", true, vec![]), - ("user_c", true, vec![]) - ], - "{} has the wrong contacts", - client.username - ) - }); - } + assert_eq!( + contacts(&client_a, cx_a), + [ + ("user_b".to_string(), true, vec![]), + ("user_c".to_string(), true, vec![]) + ] + ); + assert_eq!( + contacts(&client_b, cx_b), + [ + ("user_a".to_string(), true, vec![("a".to_string(), vec![])]), + ("user_c".to_string(), true, vec![]) + ] + ); + assert_eq!( + contacts(&client_c, cx_c), + [ + ("user_a".to_string(), true, vec![("a".to_string(), vec![])]), + ("user_b".to_string(), true, vec![]) + ] + ); let _project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; deterministic.run_until_parked(); - for (client, cx) in [(&client_a, &cx_a), (&client_b, &cx_b), (&client_c, &cx_c)] { - client.user_store.read_with(*cx, |store, _| { - assert_eq!( - contacts(store), - [ - ("user_a", true, vec![("a", vec!["user_b"])]), - ("user_b", true, vec![]), - ("user_c", true, vec![]) - ], - "{} has the wrong contacts", - client.username - ) - }); - } + assert_eq!( + contacts(&client_a, cx_a), + [ + ("user_b".to_string(), true, vec![]), + ("user_c".to_string(), true, vec![]) + ] + ); + assert_eq!( + contacts(&client_b, cx_b), + [ + ( + "user_a".to_string(), + true, + vec![("a".to_string(), vec!["user_b".to_string()])] + ), + ("user_c".to_string(), true, vec![]) + ] + ); + assert_eq!( + contacts(&client_c, cx_c), + [ + ( + "user_a".to_string(), + true, + vec![("a".to_string(), vec!["user_b".to_string()])] + ), + ("user_b".to_string(), true, vec![]) + ] + ); // Add a local project as client B client_a.fs.create_dir("/b".as_ref()).await.unwrap(); let (_project_b, _) = client_b.build_local_project("/b", cx_b).await; deterministic.run_until_parked(); - for (client, cx) in [(&client_a, &cx_a), (&client_b, &cx_b), (&client_c, &cx_c)] { - client.user_store.read_with(*cx, |store, _| { - assert_eq!( - contacts(store), - [ - ("user_a", true, vec![("a", vec!["user_b"])]), - ("user_b", true, vec![("b", vec![])]), - ("user_c", true, vec![]) - ], - "{} has the wrong contacts", - client.username - ) - }); - } + assert_eq!( + contacts(&client_a, cx_a), + [ + ("user_b".to_string(), true, vec![("b".to_string(), vec![])]), + ("user_c".to_string(), true, vec![]) + ] + ); + assert_eq!( + contacts(&client_b, cx_b), + [ + ( + "user_a".to_string(), + true, + vec![("a".to_string(), vec!["user_b".to_string()])] + ), + ("user_c".to_string(), true, vec![]) + ] + ); + assert_eq!( + contacts(&client_c, cx_c), + [ + ( + "user_a".to_string(), + true, + vec![("a".to_string(), vec!["user_b".to_string()])] + ), + ("user_b".to_string(), true, vec![("b".to_string(), vec![])]) + ] + ); project_a .condition(cx_a, |project, _| { @@ -3990,41 +4034,46 @@ async fn test_contacts( cx_a.update(move |_| drop(project_a)); deterministic.run_until_parked(); - for (client, cx) in [(&client_a, &cx_a), (&client_b, &cx_b), (&client_c, &cx_c)] { - client.user_store.read_with(*cx, |store, _| { - assert_eq!( - contacts(store), - [ - ("user_a", true, vec![]), - ("user_b", true, vec![("b", vec![])]), - ("user_c", true, vec![]) - ], - "{} has the wrong contacts", - client.username - ) - }); - } + assert_eq!( + contacts(&client_a, cx_a), + [ + ("user_b".to_string(), true, vec![("b".to_string(), vec![])]), + ("user_c".to_string(), true, vec![]) + ] + ); + assert_eq!( + contacts(&client_b, cx_b), + [ + ("user_a".to_string(), true, vec![]), + ("user_c".to_string(), true, vec![]) + ] + ); + assert_eq!( + contacts(&client_c, cx_c), + [ + ("user_a".to_string(), true, vec![]), + ("user_b".to_string(), true, vec![("b".to_string(), vec![])]) + ] + ); server.disconnect_client(client_c.current_user_id(cx_c)); server.forbid_connections(); deterministic.advance_clock(rpc::RECEIVE_TIMEOUT); - for (client, cx) in [(&client_a, &cx_a), (&client_b, &cx_b)] { - client.user_store.read_with(*cx, |store, _| { - assert_eq!( - contacts(store), - [ - ("user_a", true, vec![]), - ("user_b", true, vec![("b", vec![])]), - ("user_c", false, vec![]) - ], - "{} has the wrong contacts", - client.username - ) - }); - } - client_c - .user_store - .read_with(cx_c, |store, _| assert_eq!(contacts(store), [])); + assert_eq!( + contacts(&client_a, cx_a), + [ + ("user_b".to_string(), true, vec![("b".to_string(), vec![])]), + ("user_c".to_string(), false, vec![]) + ] + ); + assert_eq!( + contacts(&client_b, cx_b), + [ + ("user_a".to_string(), true, vec![]), + ("user_c".to_string(), false, vec![]) + ] + ); + assert_eq!(contacts(&client_c, cx_c), []); server.allow_connections(); client_c @@ -4033,40 +4082,52 @@ async fn test_contacts( .unwrap(); deterministic.run_until_parked(); - for (client, cx) in [(&client_a, &cx_a), (&client_b, &cx_b), (&client_c, &cx_c)] { - client.user_store.read_with(*cx, |store, _| { - assert_eq!( - contacts(store), - [ - ("user_a", true, vec![]), - ("user_b", true, vec![("b", vec![])]), - ("user_c", true, vec![]) - ], - "{} has the wrong contacts", - client.username - ) - }); - } + assert_eq!( + contacts(&client_a, cx_a), + [ + ("user_b".to_string(), true, vec![("b".to_string(), vec![])]), + ("user_c".to_string(), true, vec![]) + ] + ); + assert_eq!( + contacts(&client_b, cx_b), + [ + ("user_a".to_string(), true, vec![]), + ("user_c".to_string(), true, vec![]) + ] + ); + assert_eq!( + contacts(&client_c, cx_c), + [ + ("user_a".to_string(), true, vec![]), + ("user_b".to_string(), true, vec![("b".to_string(), vec![])]) + ] + ); #[allow(clippy::type_complexity)] - fn contacts(user_store: &UserStore) -> Vec<(&str, bool, Vec<(&str, Vec<&str>)>)> { - user_store - .contacts() - .iter() - .map(|contact| { - let projects = contact - .projects - .iter() - .map(|p| { - ( - p.visible_worktree_root_names[0].as_str(), - p.guests.iter().map(|p| p.github_login.as_str()).collect(), - ) - }) - .collect(); - (contact.user.github_login.as_str(), contact.online, projects) - }) - .collect() + fn contacts( + client: &TestClient, + cx: &TestAppContext, + ) -> Vec<(String, bool, Vec<(String, Vec)>)> { + client.user_store.read_with(cx, |store, _| { + store + .contacts() + .iter() + .map(|contact| { + let projects = contact + .projects + .iter() + .map(|p| { + ( + p.visible_worktree_root_names[0].clone(), + p.guests.iter().map(|p| p.github_login.clone()).collect(), + ) + }) + .collect(); + (contact.user.github_login.clone(), contact.online, projects) + }) + .collect() + }) } } @@ -4169,18 +4230,18 @@ async fn test_contact_requests( // User B sees user A as their contact now in all client, and the incoming request from them is removed. let contacts_b = client_b.summarize_contacts(cx_b); - assert_eq!(contacts_b.current, &["user_a", "user_b"]); + assert_eq!(contacts_b.current, &["user_a"]); assert_eq!(contacts_b.incoming_requests, &["user_c"]); let contacts_b2 = client_b2.summarize_contacts(cx_b2); - assert_eq!(contacts_b2.current, &["user_a", "user_b"]); + assert_eq!(contacts_b2.current, &["user_a"]); assert_eq!(contacts_b2.incoming_requests, &["user_c"]); // User A sees user B as their contact now in all clients, and the outgoing request to them is removed. let contacts_a = client_a.summarize_contacts(cx_a); - assert_eq!(contacts_a.current, &["user_a", "user_b"]); + assert_eq!(contacts_a.current, &["user_b"]); assert!(contacts_a.outgoing_requests.is_empty()); let contacts_a2 = client_a2.summarize_contacts(cx_a2); - assert_eq!(contacts_a2.current, &["user_a", "user_b"]); + assert_eq!(contacts_a2.current, &["user_b"]); assert!(contacts_a2.outgoing_requests.is_empty()); // Contacts are present upon connecting (tested here via disconnect/reconnect) @@ -4188,19 +4249,13 @@ async fn test_contact_requests( disconnect_and_reconnect(&client_b, cx_b).await; disconnect_and_reconnect(&client_c, cx_c).await; executor.run_until_parked(); - assert_eq!( - client_a.summarize_contacts(cx_a).current, - &["user_a", "user_b"] - ); - assert_eq!( - client_b.summarize_contacts(cx_b).current, - &["user_a", "user_b"] - ); + assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]); + assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]); assert_eq!( client_b.summarize_contacts(cx_b).incoming_requests, &["user_c"] ); - assert_eq!(client_c.summarize_contacts(cx_c).current, &["user_c"]); + assert!(client_c.summarize_contacts(cx_c).current.is_empty()); assert_eq!( client_c.summarize_contacts(cx_c).outgoing_requests, &["user_b"] @@ -4219,18 +4274,18 @@ async fn test_contact_requests( // User B doesn't see user C as their contact, and the incoming request from them is removed. let contacts_b = client_b.summarize_contacts(cx_b); - assert_eq!(contacts_b.current, &["user_a", "user_b"]); + assert_eq!(contacts_b.current, &["user_a"]); assert!(contacts_b.incoming_requests.is_empty()); let contacts_b2 = client_b2.summarize_contacts(cx_b2); - assert_eq!(contacts_b2.current, &["user_a", "user_b"]); + assert_eq!(contacts_b2.current, &["user_a"]); assert!(contacts_b2.incoming_requests.is_empty()); // User C doesn't see user B as their contact, and the outgoing request to them is removed. let contacts_c = client_c.summarize_contacts(cx_c); - assert_eq!(contacts_c.current, &["user_c"]); + assert!(contacts_c.current.is_empty()); assert!(contacts_c.outgoing_requests.is_empty()); let contacts_c2 = client_c2.summarize_contacts(cx_c2); - assert_eq!(contacts_c2.current, &["user_c"]); + assert!(contacts_c2.current.is_empty()); assert!(contacts_c2.outgoing_requests.is_empty()); // Incoming/outgoing requests are not present upon connecting (tested here via disconnect/reconnect) @@ -4238,19 +4293,13 @@ async fn test_contact_requests( disconnect_and_reconnect(&client_b, cx_b).await; disconnect_and_reconnect(&client_c, cx_c).await; executor.run_until_parked(); - assert_eq!( - client_a.summarize_contacts(cx_a).current, - &["user_a", "user_b"] - ); - assert_eq!( - client_b.summarize_contacts(cx_b).current, - &["user_a", "user_b"] - ); + assert_eq!(client_a.summarize_contacts(cx_a).current, &["user_b"]); + assert_eq!(client_b.summarize_contacts(cx_b).current, &["user_a"]); assert!(client_b .summarize_contacts(cx_b) .incoming_requests .is_empty()); - assert_eq!(client_c.summarize_contacts(cx_c).current, &["user_c"]); + assert!(client_c.summarize_contacts(cx_c).current.is_empty()); assert!(client_c .summarize_contacts(cx_c) .outgoing_requests @@ -5655,6 +5704,9 @@ impl TestClient { worktree .read_with(cx, |tree, _| tree.as_local().unwrap().scan_complete()) .await; + project + .update(cx, |project, _| project.next_remote_id()) + .await; (project, worktree.read_with(cx, |tree, _| tree.id())) } diff --git a/crates/collab_titlebar_item/src/add_participant_popover.rs b/crates/collab_titlebar_item/src/add_participant_popover.rs index 95d37849cb..b6fa8125f7 100644 --- a/crates/collab_titlebar_item/src/add_participant_popover.rs +++ b/crates/collab_titlebar_item/src/add_participant_popover.rs @@ -298,36 +298,21 @@ impl AddParticipantPopover { } } - let current_user = user_store.current_user(); - let contacts = user_store.contacts(); if !contacts.is_empty() { // Always put the current user first. self.match_candidates.clear(); - self.match_candidates.reserve(contacts.len()); - self.match_candidates.push(StringMatchCandidate { - id: 0, - string: Default::default(), - char_bag: Default::default(), - }); - for (ix, contact) in contacts.iter().enumerate() { - let candidate = StringMatchCandidate { - id: ix, - string: contact.user.github_login.clone(), - char_bag: contact.user.github_login.chars().collect(), - }; - if current_user - .as_ref() - .map_or(false, |current_user| current_user.id == contact.user.id) - { - self.match_candidates[0] = candidate; - } else { - self.match_candidates.push(candidate); - } - } - if self.match_candidates[0].string.is_empty() { - self.match_candidates.remove(0); - } + self.match_candidates + .extend( + contacts + .iter() + .enumerate() + .map(|(ix, contact)| StringMatchCandidate { + id: ix, + string: contact.user.github_login.clone(), + char_bag: contact.user.github_login.chars().collect(), + }), + ); let matches = executor.block(match_strings( &self.match_candidates, diff --git a/crates/collab_titlebar_item/src/collab_titlebar_item.rs b/crates/collab_titlebar_item/src/collab_titlebar_item.rs index b080242a8f..e0ad253251 100644 --- a/crates/collab_titlebar_item/src/collab_titlebar_item.rs +++ b/crates/collab_titlebar_item/src/collab_titlebar_item.rs @@ -81,7 +81,6 @@ impl CollabTitlebarItem { cx.subscribe(&view, |this, _, event, cx| { match event { add_participant_popover::Event::Dismissed => { - dbg!("dismissed"); this.add_participant_popover = None; } } From f5b2d56efd2ade03a9491aa4a57c92423be57275 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 28 Sep 2022 09:06:28 -0600 Subject: [PATCH 022/314] Remove contacts menu bar extra Co-Authored-By: Antonio Scandurra --- Cargo.lock | 25 ----- crates/contacts_status_item/Cargo.toml | 32 ------- .../src/contacts_popover.rs | 94 ------------------- .../src/contacts_status_item.rs | 94 ------------------- crates/theme/src/theme.rs | 6 -- crates/zed/Cargo.toml | 1 - 6 files changed, 252 deletions(-) delete mode 100644 crates/contacts_status_item/Cargo.toml delete mode 100644 crates/contacts_status_item/src/contacts_popover.rs delete mode 100644 crates/contacts_status_item/src/contacts_status_item.rs diff --git a/Cargo.lock b/Cargo.lock index a8a89478fe..c06a3ee93c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1150,30 +1150,6 @@ dependencies = [ "workspace", ] -[[package]] -name = "contacts_status_item" -version = "0.1.0" -dependencies = [ - "anyhow", - "client", - "collections", - "editor", - "futures", - "fuzzy", - "gpui", - "language", - "log", - "menu", - "picker", - "postage", - "project", - "serde", - "settings", - "theme", - "util", - "workspace", -] - [[package]] name = "context_menu" version = "0.1.0" @@ -7185,7 +7161,6 @@ dependencies = [ "collections", "command_palette", "contacts_panel", - "contacts_status_item", "context_menu", "ctor", "diagnostics", diff --git a/crates/contacts_status_item/Cargo.toml b/crates/contacts_status_item/Cargo.toml deleted file mode 100644 index df115a3842..0000000000 --- a/crates/contacts_status_item/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -name = "contacts_status_item" -version = "0.1.0" -edition = "2021" - -[lib] -path = "src/contacts_status_item.rs" -doctest = false - -[dependencies] -client = { path = "../client" } -collections = { path = "../collections" } -editor = { path = "../editor" } -fuzzy = { path = "../fuzzy" } -gpui = { path = "../gpui" } -menu = { path = "../menu" } -picker = { path = "../picker" } -project = { path = "../project" } -settings = { path = "../settings" } -theme = { path = "../theme" } -util = { path = "../util" } -workspace = { path = "../workspace" } -anyhow = "1.0" -futures = "0.3" -log = "0.4" -postage = { version = "0.4.1", features = ["futures-traits"] } -serde = { version = "1.0", features = ["derive", "rc"] } - -[dev-dependencies] -language = { path = "../language", features = ["test-support"] } -project = { path = "../project", features = ["test-support"] } -workspace = { path = "../workspace", features = ["test-support"] } diff --git a/crates/contacts_status_item/src/contacts_popover.rs b/crates/contacts_status_item/src/contacts_popover.rs deleted file mode 100644 index 2998d74ed8..0000000000 --- a/crates/contacts_status_item/src/contacts_popover.rs +++ /dev/null @@ -1,94 +0,0 @@ -use editor::Editor; -use gpui::{elements::*, Entity, RenderContext, View, ViewContext, ViewHandle}; -use settings::Settings; - -pub enum Event { - Deactivated, -} - -pub struct ContactsPopover { - filter_editor: ViewHandle, -} - -impl Entity for ContactsPopover { - type Event = Event; -} - -impl View for ContactsPopover { - fn ui_name() -> &'static str { - "ContactsPopover" - } - - fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - let theme = &cx.global::().theme.contacts_popover; - - Flex::row() - .with_child( - ChildView::new(self.filter_editor.clone()) - .contained() - .with_style( - cx.global::() - .theme - .contacts_panel - .user_query_editor - .container, - ) - .flex(1., true) - .boxed(), - ) - // .with_child( - // MouseEventHandler::::new(0, cx, |_, _| { - // Svg::new("icons/user_plus_16.svg") - // .with_color(theme.add_contact_button.color) - // .constrained() - // .with_height(16.) - // .contained() - // .with_style(theme.add_contact_button.container) - // .aligned() - // .boxed() - // }) - // .with_cursor_style(CursorStyle::PointingHand) - // .on_click(MouseButton::Left, |_, cx| { - // cx.dispatch_action(contact_finder::Toggle) - // }) - // .boxed(), - // ) - .constrained() - .with_height( - cx.global::() - .theme - .contacts_panel - .user_query_editor_height, - ) - .aligned() - .top() - .contained() - .with_background_color(theme.background) - .with_uniform_padding(4.) - .boxed() - } -} - -impl ContactsPopover { - pub fn new(cx: &mut ViewContext) -> Self { - cx.observe_window_activation(Self::window_activation_changed) - .detach(); - - let filter_editor = cx.add_view(|cx| { - let mut editor = Editor::single_line( - Some(|theme| theme.contacts_panel.user_query_editor.clone()), - cx, - ); - editor.set_placeholder_text("Filter contacts", cx); - editor - }); - - Self { filter_editor } - } - - fn window_activation_changed(&mut self, is_active: bool, cx: &mut ViewContext) { - if !is_active { - cx.emit(Event::Deactivated); - } - } -} diff --git a/crates/contacts_status_item/src/contacts_status_item.rs b/crates/contacts_status_item/src/contacts_status_item.rs deleted file mode 100644 index 5d471abcdf..0000000000 --- a/crates/contacts_status_item/src/contacts_status_item.rs +++ /dev/null @@ -1,94 +0,0 @@ -mod contacts_popover; - -use contacts_popover::ContactsPopover; -use gpui::{ - actions, - color::Color, - elements::*, - geometry::{rect::RectF, vector::vec2f}, - Appearance, Entity, MouseButton, MutableAppContext, RenderContext, View, ViewContext, - ViewHandle, WindowKind, -}; - -actions!(contacts_status_item, [ToggleContactsPopover]); - -pub fn init(cx: &mut MutableAppContext) { - cx.add_action(ContactsStatusItem::toggle_contacts_popover); -} - -pub struct ContactsStatusItem { - popover: Option>, -} - -impl Entity for ContactsStatusItem { - type Event = (); -} - -impl View for ContactsStatusItem { - fn ui_name() -> &'static str { - "ContactsStatusItem" - } - - fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - let color = match cx.appearance { - Appearance::Light | Appearance::VibrantLight => Color::black(), - Appearance::Dark | Appearance::VibrantDark => Color::white(), - }; - MouseEventHandler::::new(0, cx, |_, _| { - Svg::new("icons/zed_22.svg") - .with_color(color) - .aligned() - .boxed() - }) - .on_click(MouseButton::Left, |_, cx| { - cx.dispatch_action(ToggleContactsPopover); - }) - .boxed() - } -} - -impl ContactsStatusItem { - pub fn new() -> Self { - Self { popover: None } - } - - fn toggle_contacts_popover(&mut self, _: &ToggleContactsPopover, cx: &mut ViewContext) { - match self.popover.take() { - Some(popover) => { - cx.remove_window(popover.window_id()); - } - None => { - let window_bounds = cx.window_bounds(); - let size = vec2f(360., 460.); - let origin = window_bounds.lower_left() - + vec2f(window_bounds.width() / 2. - size.x() / 2., 0.); - let (_, popover) = cx.add_window( - gpui::WindowOptions { - bounds: gpui::WindowBounds::Fixed(RectF::new(origin, size)), - titlebar: None, - center: false, - kind: WindowKind::PopUp, - is_movable: false, - }, - |cx| ContactsPopover::new(cx), - ); - cx.subscribe(&popover, Self::on_popover_event).detach(); - self.popover = Some(popover); - } - } - } - - fn on_popover_event( - &mut self, - popover: ViewHandle, - event: &contacts_popover::Event, - cx: &mut ViewContext, - ) { - match event { - contacts_popover::Event::Deactivated => { - self.popover.take(); - cx.remove_window(popover.window_id()); - } - } - } -} diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 4446e4e06f..4192bc0752 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -19,7 +19,6 @@ pub struct Theme { pub workspace: Workspace, pub context_menu: ContextMenu, pub chat_panel: ChatPanel, - pub contacts_popover: ContactsPopover, pub contacts_panel: ContactsPanel, pub contact_finder: ContactFinder, pub project_panel: ProjectPanel, @@ -325,11 +324,6 @@ pub struct CommandPalette { pub keystroke_spacing: f32, } -#[derive(Deserialize, Default)] -pub struct ContactsPopover { - pub background: Color, -} - #[derive(Deserialize, Default)] pub struct ContactsPanel { #[serde(flatten)] diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 6c2ce8ff07..82629705a1 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -28,7 +28,6 @@ context_menu = { path = "../context_menu" } client = { path = "../client" } clock = { path = "../clock" } contacts_panel = { path = "../contacts_panel" } -contacts_status_item = { path = "../contacts_status_item" } diagnostics = { path = "../diagnostics" } editor = { path = "../editor" } file_finder = { path = "../file_finder" } From 815cf4464760c499b89f3b999a14fe72f11dff37 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 28 Sep 2022 09:10:01 -0600 Subject: [PATCH 023/314] Rename AddParticipantPopover to ContactsPopover Co-Authored-By: Antonio Scandurra --- .../src/collab_titlebar_item.rs | 42 +++++++++---------- ...icipant_popover.rs => contacts_popover.rs} | 26 ++++++------ crates/theme/src/theme.rs | 4 +- styles/src/styleTree/workspace.ts | 4 +- 4 files changed, 36 insertions(+), 40 deletions(-) rename crates/collab_titlebar_item/src/{add_participant_popover.rs => contacts_popover.rs} (97%) diff --git a/crates/collab_titlebar_item/src/collab_titlebar_item.rs b/crates/collab_titlebar_item/src/collab_titlebar_item.rs index e0ad253251..c27a2b3452 100644 --- a/crates/collab_titlebar_item/src/collab_titlebar_item.rs +++ b/crates/collab_titlebar_item/src/collab_titlebar_item.rs @@ -1,8 +1,8 @@ -mod add_participant_popover; +mod contacts_popover; -use add_participant_popover::AddParticipantPopover; use client::{Authenticate, PeerId}; use clock::ReplicaId; +use contacts_popover::ContactsPopover; use gpui::{ actions, color::Color, @@ -17,16 +17,16 @@ use std::{ops::Range, sync::Arc}; use theme::Theme; use workspace::{FollowNextCollaborator, ToggleFollow, Workspace}; -actions!(contacts_titlebar_item, [ToggleAddParticipantPopover]); +actions!(contacts_titlebar_item, [ToggleContactsPopover]); pub fn init(cx: &mut MutableAppContext) { - add_participant_popover::init(cx); - cx.add_action(CollabTitlebarItem::toggle_add_participant_popover); + contacts_popover::init(cx); + cx.add_action(CollabTitlebarItem::toggle_contacts_popover); } pub struct CollabTitlebarItem { workspace: WeakViewHandle, - add_participant_popover: Option>, + contacts_popover: Option>, _subscriptions: Vec, } @@ -61,34 +61,30 @@ impl CollabTitlebarItem { let observe_workspace = cx.observe(workspace, |_, _, cx| cx.notify()); Self { workspace: workspace.downgrade(), - add_participant_popover: None, + contacts_popover: None, _subscriptions: vec![observe_workspace], } } - fn toggle_add_participant_popover( - &mut self, - _: &ToggleAddParticipantPopover, - cx: &mut ViewContext, - ) { - match self.add_participant_popover.take() { + fn toggle_contacts_popover(&mut self, _: &ToggleContactsPopover, cx: &mut ViewContext) { + match self.contacts_popover.take() { Some(_) => {} None => { if let Some(workspace) = self.workspace.upgrade(cx) { let user_store = workspace.read(cx).user_store().clone(); - let view = cx.add_view(|cx| AddParticipantPopover::new(user_store, cx)); + let view = cx.add_view(|cx| ContactsPopover::new(user_store, cx)); cx.focus(&view); cx.subscribe(&view, |this, _, event, cx| { match event { - add_participant_popover::Event::Dismissed => { - this.add_participant_popover = None; + contacts_popover::Event::Dismissed => { + this.contacts_popover = None; } } cx.notify(); }) .detach(); - self.add_participant_popover = Some(view); + self.contacts_popover = Some(view); } } } @@ -110,10 +106,10 @@ impl CollabTitlebarItem { Some( Stack::new() .with_child( - MouseEventHandler::::new(0, cx, |state, _| { + MouseEventHandler::::new(0, cx, |state, _| { let style = titlebar - .add_participant_button - .style_for(state, self.add_participant_popover.is_some()); + .toggle_contacts_button + .style_for(state, self.contacts_popover.is_some()); Svg::new("icons/plus_8.svg") .with_color(style.color) .constrained() @@ -128,18 +124,18 @@ impl CollabTitlebarItem { }) .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, |_, cx| { - cx.dispatch_action(ToggleAddParticipantPopover); + cx.dispatch_action(ToggleContactsPopover); }) .aligned() .boxed(), ) - .with_children(self.add_participant_popover.as_ref().map(|popover| { + .with_children(self.contacts_popover.as_ref().map(|popover| { Overlay::new( ChildView::new(popover) .contained() .with_margin_top(titlebar.height) .with_margin_right( - -titlebar.add_participant_button.default.button_width, + -titlebar.toggle_contacts_button.default.button_width, ) .boxed(), ) diff --git a/crates/collab_titlebar_item/src/add_participant_popover.rs b/crates/collab_titlebar_item/src/contacts_popover.rs similarity index 97% rename from crates/collab_titlebar_item/src/add_participant_popover.rs rename to crates/collab_titlebar_item/src/contacts_popover.rs index b6fa8125f7..e44f6a2331 100644 --- a/crates/collab_titlebar_item/src/add_participant_popover.rs +++ b/crates/collab_titlebar_item/src/contacts_popover.rs @@ -15,11 +15,11 @@ use theme::IconButton; impl_internal_actions!(contacts_panel, [ToggleExpanded]); pub fn init(cx: &mut MutableAppContext) { - cx.add_action(AddParticipantPopover::clear_filter); - cx.add_action(AddParticipantPopover::select_next); - cx.add_action(AddParticipantPopover::select_prev); - cx.add_action(AddParticipantPopover::confirm); - cx.add_action(AddParticipantPopover::toggle_expanded); + cx.add_action(ContactsPopover::clear_filter); + cx.add_action(ContactsPopover::select_next); + cx.add_action(ContactsPopover::select_prev); + cx.add_action(ContactsPopover::confirm); + cx.add_action(ContactsPopover::toggle_expanded); } #[derive(Clone, PartialEq)] @@ -72,7 +72,7 @@ pub enum Event { Dismissed, } -pub struct AddParticipantPopover { +pub struct ContactsPopover { entries: Vec, match_candidates: Vec, list_state: ListState, @@ -83,7 +83,7 @@ pub struct AddParticipantPopover { _maintain_contacts: Subscription, } -impl AddParticipantPopover { +impl ContactsPopover { pub fn new(user_store: ModelHandle, cx: &mut ViewContext) -> Self { let filter_editor = cx.add_view(|cx| { let mut editor = Editor::single_line( @@ -555,13 +555,13 @@ impl AddParticipantPopover { } } -impl Entity for AddParticipantPopover { +impl Entity for ContactsPopover { type Event = Event; } -impl View for AddParticipantPopover { +impl View for ContactsPopover { fn ui_name() -> &'static str { - "AddParticipantPopover" + "ContactsPopover" } fn keymap_context(&self, _: &AppContext) -> keymap::Context { @@ -657,10 +657,10 @@ impl View for AddParticipantPopover { }), ) .contained() - .with_style(theme.workspace.titlebar.add_participant_popover.container) + .with_style(theme.workspace.titlebar.contacts_popover.container) .constrained() - .with_width(theme.workspace.titlebar.add_participant_popover.width) - .with_height(theme.workspace.titlebar.add_participant_popover.height) + .with_width(theme.workspace.titlebar.contacts_popover.width) + .with_height(theme.workspace.titlebar.contacts_popover.height) .boxed() } diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 4192bc0752..28c8eb3091 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -73,8 +73,8 @@ pub struct Titlebar { pub avatar: ImageStyle, pub sign_in_prompt: Interactive, pub outdated_warning: ContainedText, - pub add_participant_button: Interactive, - pub add_participant_popover: AddParticipantPopover, + pub toggle_contacts_button: Interactive, + pub contacts_popover: AddParticipantPopover, } #[derive(Clone, Deserialize, Default)] diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 156ed62fba..8bd1e3800f 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -119,7 +119,7 @@ export default function workspace(theme: Theme) { }, cornerRadius: 6, }, - addParticipantButton: { + toggleContactsButton: { cornerRadius: 6, color: iconColor(theme, "secondary"), iconWidth: 8, @@ -133,7 +133,7 @@ export default function workspace(theme: Theme) { color: iconColor(theme, "active"), }, }, - addParticipantPopover: { + contactsPopover: { background: backgroundColor(theme, 300, "base"), cornerRadius: 6, padding: { top: 6 }, From 8ff4f044b724d5bcdbe72b4bb42f2c14c9e10f58 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 28 Sep 2022 11:02:26 -0600 Subject: [PATCH 024/314] Start a call when clicking on a contact in the contacts popover Co-Authored-By: Antonio Scandurra --- Cargo.lock | 2 + crates/collab/src/integration_tests.rs | 20 +-- crates/collab_titlebar_item/Cargo.toml | 3 + .../src/collab_titlebar_item.rs | 3 +- .../src/contacts_popover.rs | 163 +++++++++++++++--- crates/gpui/src/app.rs | 11 ++ crates/room/Cargo.toml | 3 + crates/room/src/room.rs | 74 +++++++- crates/zed/src/zed.rs | 3 +- 9 files changed, 229 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c06a3ee93c..64202ed1c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1078,6 +1078,7 @@ dependencies = [ "menu", "postage", "project", + "room", "serde", "settings", "theme", @@ -4461,6 +4462,7 @@ dependencies = [ "futures", "gpui", "project", + "util", ] [[package]] diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index e04bd80c79..bd60893a57 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -83,7 +83,7 @@ async fn test_basic_calls( .await; let room_a = cx_a - .update(|cx| Room::create(client_a.clone(), cx)) + .update(|cx| Room::create(client_a.clone(), client_a.user_store.clone(), cx)) .await .unwrap(); assert_eq!( @@ -125,7 +125,7 @@ async fn test_basic_calls( // User B joins the room using the first client. let room_b = cx_b - .update(|cx| Room::join(&call_b, client_b.clone(), cx)) + .update(|cx| Room::join(&call_b, client_b.clone(), client_b.user_store.clone(), cx)) .await .unwrap(); assert!(incoming_call_b.next().await.unwrap().is_none()); @@ -229,7 +229,7 @@ async fn test_leaving_room_on_disconnection( .await; let room_a = cx_a - .update(|cx| Room::create(client_a.clone(), cx)) + .update(|cx| Room::create(client_a.clone(), client_a.user_store.clone(), cx)) .await .unwrap(); @@ -245,7 +245,7 @@ async fn test_leaving_room_on_disconnection( // User B receives the call and joins the room. let call_b = incoming_call_b.next().await.unwrap().unwrap(); let room_b = cx_b - .update(|cx| Room::join(&call_b, client_b.clone(), cx)) + .update(|cx| Room::join(&call_b, client_b.clone(), client_b.user_store.clone(), cx)) .await .unwrap(); deterministic.run_until_parked(); @@ -6284,17 +6284,9 @@ async fn room_participants( .collect::>() }); let remote_users = futures::future::try_join_all(remote_users).await.unwrap(); - let pending_users = room.update(cx, |room, cx| { - room.pending_user_ids() - .iter() - .map(|user_id| { - client - .user_store - .update(cx, |users, cx| users.get_user(*user_id, cx)) - }) - .collect::>() + let pending_users = room.read_with(cx, |room, _| { + room.pending_users().iter().cloned().collect::>() }); - let pending_users = futures::future::try_join_all(pending_users).await.unwrap(); RoomParticipants { remote: remote_users diff --git a/crates/collab_titlebar_item/Cargo.toml b/crates/collab_titlebar_item/Cargo.toml index 4f85ddd8d9..fbdfb34386 100644 --- a/crates/collab_titlebar_item/Cargo.toml +++ b/crates/collab_titlebar_item/Cargo.toml @@ -14,6 +14,7 @@ test-support = [ "editor/test-support", "gpui/test-support", "project/test-support", + "room/test-support", "settings/test-support", "util/test-support", "workspace/test-support", @@ -28,6 +29,7 @@ fuzzy = { path = "../fuzzy" } gpui = { path = "../gpui" } menu = { path = "../menu" } project = { path = "../project" } +room = { path = "../room" } settings = { path = "../settings" } theme = { path = "../theme" } util = { path = "../util" } @@ -44,6 +46,7 @@ collections = { path = "../collections", features = ["test-support"] } editor = { path = "../editor", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } +room = { path = "../room", features = ["test-support"] } settings = { path = "../settings", features = ["test-support"] } util = { path = "../util", features = ["test-support"] } workspace = { path = "../workspace", features = ["test-support"] } diff --git a/crates/collab_titlebar_item/src/collab_titlebar_item.rs b/crates/collab_titlebar_item/src/collab_titlebar_item.rs index c27a2b3452..f9da9e5b7a 100644 --- a/crates/collab_titlebar_item/src/collab_titlebar_item.rs +++ b/crates/collab_titlebar_item/src/collab_titlebar_item.rs @@ -71,8 +71,9 @@ impl CollabTitlebarItem { Some(_) => {} None => { if let Some(workspace) = self.workspace.upgrade(cx) { + let client = workspace.read(cx).client().clone(); let user_store = workspace.read(cx).user_store().clone(); - let view = cx.add_view(|cx| ContactsPopover::new(user_store, cx)); + let view = cx.add_view(|cx| ContactsPopover::new(client, user_store, cx)); cx.focus(&view); cx.subscribe(&view, |this, _, event, cx| { match event { diff --git a/crates/collab_titlebar_item/src/contacts_popover.rs b/crates/collab_titlebar_item/src/contacts_popover.rs index e44f6a2331..26c1194d74 100644 --- a/crates/collab_titlebar_item/src/contacts_popover.rs +++ b/crates/collab_titlebar_item/src/contacts_popover.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use client::{Contact, User, UserStore}; +use client::{Client, Contact, User, UserStore}; use editor::{Cancel, Editor}; use fuzzy::{match_strings, StringMatchCandidate}; use gpui::{ @@ -9,10 +9,11 @@ use gpui::{ ViewHandle, }; use menu::{Confirm, SelectNext, SelectPrev}; +use room::Room; use settings::Settings; use theme::IconButton; -impl_internal_actions!(contacts_panel, [ToggleExpanded]); +impl_internal_actions!(contacts_panel, [ToggleExpanded, Call]); pub fn init(cx: &mut MutableAppContext) { cx.add_action(ContactsPopover::clear_filter); @@ -20,11 +21,17 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action(ContactsPopover::select_prev); cx.add_action(ContactsPopover::confirm); cx.add_action(ContactsPopover::toggle_expanded); + cx.add_action(ContactsPopover::call); } #[derive(Clone, PartialEq)] struct ToggleExpanded(Section); +#[derive(Clone, PartialEq)] +struct Call { + recipient_user_id: u64, +} + #[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)] enum Section { Requests, @@ -73,18 +80,24 @@ pub enum Event { } pub struct ContactsPopover { + room: Option<(ModelHandle, Subscription)>, entries: Vec, match_candidates: Vec, list_state: ListState, + client: Arc, user_store: ModelHandle, filter_editor: ViewHandle, collapsed_sections: Vec
, selection: Option, - _maintain_contacts: Subscription, + _subscriptions: Vec, } impl ContactsPopover { - pub fn new(user_store: ModelHandle, cx: &mut ViewContext) -> Self { + pub fn new( + client: Arc, + user_store: ModelHandle, + cx: &mut ViewContext, + ) -> Self { let filter_editor = cx.add_view(|cx| { let mut editor = Editor::single_line( Some(|theme| theme.contacts_panel.user_query_editor.clone()), @@ -143,25 +156,52 @@ impl ContactsPopover { cx, ), ContactEntry::Contact(contact) => { - Self::render_contact(&contact.user, &theme.contacts_panel, is_selected) + Self::render_contact(contact, &theme.contacts_panel, is_selected, cx) } } }); + let mut subscriptions = Vec::new(); + subscriptions.push(cx.observe(&user_store, |this, _, cx| this.update_entries(cx))); + + let weak_self = cx.weak_handle(); + subscriptions.push(Room::observe(cx, move |room, cx| { + if let Some(this) = weak_self.upgrade(cx) { + this.update(cx, |this, cx| this.set_room(room, cx)); + } + })); + let mut this = Self { + room: None, list_state, selection: None, collapsed_sections: Default::default(), entries: Default::default(), match_candidates: Default::default(), filter_editor, - _maintain_contacts: cx.observe(&user_store, |this, _, cx| this.update_entries(cx)), + _subscriptions: subscriptions, + client, user_store, }; this.update_entries(cx); this } + fn set_room(&mut self, room: Option>, cx: &mut ViewContext) { + if let Some(room) = room { + let observation = cx.observe(&room, |this, room, cx| this.room_updated(room, cx)); + self.room = Some((room, observation)); + } else { + self.room = None; + } + + cx.notify(); + } + + fn room_updated(&mut self, room: ModelHandle, cx: &mut ViewContext) { + cx.notify(); + } + fn clear_filter(&mut self, _: &Cancel, cx: &mut ViewContext) { let did_clear = self.filter_editor.update(cx, |editor, cx| { if editor.buffer().read(cx).len(cx) > 0 { @@ -357,6 +397,43 @@ impl ContactsPopover { cx.notify(); } + fn render_active_call(&self, cx: &mut RenderContext) -> Option { + let (room, _) = self.room.as_ref()?; + let theme = &cx.global::().theme.contacts_panel; + + Some( + Flex::column() + .with_children(room.read(cx).pending_users().iter().map(|user| { + Flex::row() + .with_children(user.avatar.clone().map(|avatar| { + Image::new(avatar) + .with_style(theme.contact_avatar) + .aligned() + .left() + .boxed() + })) + .with_child( + Label::new( + user.github_login.clone(), + theme.contact_username.text.clone(), + ) + .contained() + .with_style(theme.contact_username.container) + .aligned() + .left() + .flex(1., true) + .boxed(), + ) + .constrained() + .with_height(theme.row_height) + .contained() + .with_style(theme.contact_row.default) + .boxed() + })) + .boxed(), + ) + } + fn render_header( section: Section, theme: &theme::ContactsPanel, @@ -412,32 +489,46 @@ impl ContactsPopover { .boxed() } - fn render_contact(user: &User, theme: &theme::ContactsPanel, is_selected: bool) -> ElementBox { - Flex::row() - .with_children(user.avatar.clone().map(|avatar| { - Image::new(avatar) - .with_style(theme.contact_avatar) + fn render_contact( + contact: &Contact, + theme: &theme::ContactsPanel, + is_selected: bool, + cx: &mut RenderContext, + ) -> ElementBox { + let user_id = contact.user.id; + MouseEventHandler::::new(contact.user.id as usize, cx, |_, _| { + Flex::row() + .with_children(contact.user.avatar.clone().map(|avatar| { + Image::new(avatar) + .with_style(theme.contact_avatar) + .aligned() + .left() + .boxed() + })) + .with_child( + Label::new( + contact.user.github_login.clone(), + theme.contact_username.text.clone(), + ) + .contained() + .with_style(theme.contact_username.container) .aligned() .left() - .boxed() - })) - .with_child( - Label::new( - user.github_login.clone(), - theme.contact_username.text.clone(), + .flex(1., true) + .boxed(), ) + .constrained() + .with_height(theme.row_height) .contained() - .with_style(theme.contact_username.container) - .aligned() - .left() - .flex(1., true) - .boxed(), - ) - .constrained() - .with_height(theme.row_height) - .contained() - .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) - .boxed() + .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) + .boxed() + }) + .on_click(MouseButton::Left, move |_, cx| { + cx.dispatch_action(Call { + recipient_user_id: user_id, + }) + }) + .boxed() } fn render_contact_request( @@ -553,6 +644,21 @@ impl ContactsPopover { .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) .boxed() } + + fn call(&mut self, action: &Call, cx: &mut ViewContext) { + let client = self.client.clone(); + let user_store = self.user_store.clone(); + let recipient_user_id = action.recipient_user_id; + cx.spawn_weak(|_, mut cx| async move { + let room = cx + .update(|cx| Room::get_or_create(&client, &user_store, cx)) + .await?; + room.update(&mut cx, |room, cx| room.call(recipient_user_id, cx)) + .await?; + anyhow::Ok(()) + }) + .detach(); + } } impl Entity for ContactsPopover { @@ -606,6 +712,7 @@ impl View for ContactsPopover { .with_height(theme.contacts_panel.user_query_editor_height) .boxed(), ) + .with_children(self.render_active_call(cx)) .with_child(List::new(self.list_state.clone()).flex(1., false).boxed()) .with_children( self.user_store diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 308ea6c831..18a2f8a4d0 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1519,6 +1519,17 @@ impl MutableAppContext { } } + pub fn observe_default_global(&mut self, observe: F) -> Subscription + where + G: Any + Default, + F: 'static + FnMut(&mut MutableAppContext), + { + if !self.has_global::() { + self.set_global(G::default()); + } + self.observe_global::(observe) + } + pub fn observe_release(&mut self, handle: &H, callback: F) -> Subscription where E: Entity, diff --git a/crates/room/Cargo.toml b/crates/room/Cargo.toml index 33b6620b27..169f04d352 100644 --- a/crates/room/Cargo.toml +++ b/crates/room/Cargo.toml @@ -13,6 +13,7 @@ test-support = [ "collections/test-support", "gpui/test-support", "project/test-support", + "util/test-support" ] [dependencies] @@ -20,6 +21,7 @@ client = { path = "../client" } collections = { path = "../collections" } gpui = { path = "../gpui" } project = { path = "../project" } +util = { path = "../util" } anyhow = "1.0.38" futures = "0.3" @@ -29,3 +31,4 @@ client = { path = "../client", features = ["test-support"] } collections = { path = "../collections", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } +util = { path = "../util", features = ["test-support"] } diff --git a/crates/room/src/room.rs b/crates/room/src/room.rs index 82363d4b19..f7d5a58fa6 100644 --- a/crates/room/src/room.rs +++ b/crates/room/src/room.rs @@ -1,13 +1,14 @@ mod participant; use anyhow::{anyhow, Result}; -use client::{call::Call, proto, Client, PeerId, TypedEnvelope}; +use client::{call::Call, proto, Client, PeerId, TypedEnvelope, User, UserStore}; use collections::HashMap; use futures::StreamExt; use gpui::{AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task}; use participant::{LocalParticipant, ParticipantLocation, RemoteParticipant}; use project::Project; use std::sync::Arc; +use util::ResultExt; pub enum Event { PeerChangedActiveProject, @@ -18,9 +19,11 @@ pub struct Room { status: RoomStatus, local_participant: LocalParticipant, remote_participants: HashMap, - pending_user_ids: Vec, + pending_users: Vec>, client: Arc, + user_store: ModelHandle, _subscriptions: Vec, + _load_pending_users: Option>, } impl Entity for Room { @@ -28,7 +31,44 @@ impl Entity for Room { } impl Room { - fn new(id: u64, client: Arc, cx: &mut ModelContext) -> Self { + pub fn observe(cx: &mut MutableAppContext, mut callback: F) -> gpui::Subscription + where + F: 'static + FnMut(Option>, &mut MutableAppContext), + { + cx.observe_default_global::>, _>(move |cx| { + let room = cx.global::>>().clone(); + callback(room, cx); + }) + } + + pub fn get_or_create( + client: &Arc, + user_store: &ModelHandle, + cx: &mut MutableAppContext, + ) -> Task>> { + if let Some(room) = cx.global::>>() { + Task::ready(Ok(room.clone())) + } else { + let client = client.clone(); + let user_store = user_store.clone(); + cx.spawn(|mut cx| async move { + let room = cx.update(|cx| Room::create(client, user_store, cx)).await?; + cx.update(|cx| cx.set_global(Some(room.clone()))); + Ok(room) + }) + } + } + + pub fn clear(cx: &mut MutableAppContext) { + cx.set_global::>>(None); + } + + fn new( + id: u64, + client: Arc, + user_store: ModelHandle, + cx: &mut ModelContext, + ) -> Self { let mut client_status = client.status(); cx.spawn_weak(|this, mut cx| async move { let is_connected = client_status @@ -51,32 +91,36 @@ impl Room { projects: Default::default(), }, remote_participants: Default::default(), - pending_user_ids: Default::default(), + pending_users: Default::default(), _subscriptions: vec![client.add_message_handler(cx.handle(), Self::handle_room_updated)], + _load_pending_users: None, client, + user_store, } } pub fn create( client: Arc, + user_store: ModelHandle, cx: &mut MutableAppContext, ) -> Task>> { cx.spawn(|mut cx| async move { let room = client.request(proto::CreateRoom {}).await?; - Ok(cx.add_model(|cx| Self::new(room.id, client, cx))) + Ok(cx.add_model(|cx| Self::new(room.id, client, user_store, cx))) }) } pub fn join( call: &Call, client: Arc, + user_store: ModelHandle, cx: &mut MutableAppContext, ) -> Task>> { let room_id = call.room_id; cx.spawn(|mut cx| async move { let response = client.request(proto::JoinRoom { id: room_id }).await?; let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?; - let room = cx.add_model(|cx| Self::new(room_id, client, cx)); + let room = cx.add_model(|cx| Self::new(room_id, client, user_store, cx)); room.update(&mut cx, |room, cx| room.apply_room_update(room_proto, cx))?; Ok(room) }) @@ -98,8 +142,8 @@ impl Room { &self.remote_participants } - pub fn pending_user_ids(&self) -> &[u64] { - &self.pending_user_ids + pub fn pending_users(&self) -> &[Arc] { + &self.pending_users } async fn handle_room_updated( @@ -131,7 +175,19 @@ impl Room { ); } } - self.pending_user_ids = room.pending_user_ids; + + let pending_users = self.user_store.update(cx, move |user_store, cx| { + user_store.get_users(room.pending_user_ids, cx) + }); + self._load_pending_users = Some(cx.spawn(|this, mut cx| async move { + if let Some(pending_users) = pending_users.await.log_err() { + this.update(&mut cx, |this, cx| { + this.pending_users = pending_users; + cx.notify(); + }); + } + })); + cx.notify(); Ok(()) } diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 26888dc0d7..bd6f28a402 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -21,10 +21,11 @@ use gpui::{ geometry::vector::vec2f, impl_actions, platform::{WindowBounds, WindowOptions}, - AssetSource, AsyncAppContext, TitlebarOptions, ViewContext, WindowKind, + AssetSource, AsyncAppContext, ModelHandle, TitlebarOptions, ViewContext, WindowKind, }; use language::Rope; pub use lsp; +use postage::watch; pub use project::{self, fs}; use project_panel::ProjectPanel; use search::{BufferSearchBar, ProjectSearchBar}; From aa3cb8e35e1d89c3542acffde6a83f7e915a1bb1 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 28 Sep 2022 19:14:31 +0200 Subject: [PATCH 025/314] Rename `collab_titlebar_item` crate to `collab_ui` Co-Authored-By: Nathan Sobo --- Cargo.lock | 4 ++-- crates/{collab_titlebar_item => collab_ui}/Cargo.toml | 4 ++-- .../src/collab_titlebar_item.rs | 4 +--- crates/collab_ui/src/collab_ui.rs | 10 ++++++++++ .../src/contacts_popover.rs | 6 +----- crates/zed/Cargo.toml | 2 +- crates/zed/src/main.rs | 2 +- crates/zed/src/zed.rs | 5 ++--- 8 files changed, 20 insertions(+), 17 deletions(-) rename crates/{collab_titlebar_item => collab_ui}/Cargo.toml (95%) rename crates/{collab_titlebar_item => collab_ui}/src/collab_titlebar_item.rs (99%) create mode 100644 crates/collab_ui/src/collab_ui.rs rename crates/{collab_titlebar_item => collab_ui}/src/contacts_popover.rs (99%) diff --git a/Cargo.lock b/Cargo.lock index 64202ed1c7..655ec8ab03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1063,7 +1063,7 @@ dependencies = [ ] [[package]] -name = "collab_titlebar_item" +name = "collab_ui" version = "0.1.0" dependencies = [ "anyhow", @@ -7159,7 +7159,7 @@ dependencies = [ "cli", "client", "clock", - "collab_titlebar_item", + "collab_ui", "collections", "command_palette", "contacts_panel", diff --git a/crates/collab_titlebar_item/Cargo.toml b/crates/collab_ui/Cargo.toml similarity index 95% rename from crates/collab_titlebar_item/Cargo.toml rename to crates/collab_ui/Cargo.toml index fbdfb34386..aa2cdb8628 100644 --- a/crates/collab_titlebar_item/Cargo.toml +++ b/crates/collab_ui/Cargo.toml @@ -1,10 +1,10 @@ [package] -name = "collab_titlebar_item" +name = "collab_ui" version = "0.1.0" edition = "2021" [lib] -path = "src/collab_titlebar_item.rs" +path = "src/collab_ui.rs" doctest = false [features] diff --git a/crates/collab_titlebar_item/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs similarity index 99% rename from crates/collab_titlebar_item/src/collab_titlebar_item.rs rename to crates/collab_ui/src/collab_titlebar_item.rs index f9da9e5b7a..00f37fddee 100644 --- a/crates/collab_titlebar_item/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -1,5 +1,4 @@ -mod contacts_popover; - +use crate::contacts_popover; use client::{Authenticate, PeerId}; use clock::ReplicaId; use contacts_popover::ContactsPopover; @@ -20,7 +19,6 @@ use workspace::{FollowNextCollaborator, ToggleFollow, Workspace}; actions!(contacts_titlebar_item, [ToggleContactsPopover]); pub fn init(cx: &mut MutableAppContext) { - contacts_popover::init(cx); cx.add_action(CollabTitlebarItem::toggle_contacts_popover); } diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs new file mode 100644 index 0000000000..312c7478e2 --- /dev/null +++ b/crates/collab_ui/src/collab_ui.rs @@ -0,0 +1,10 @@ +mod collab_titlebar_item; +mod contacts_popover; + +pub use collab_titlebar_item::CollabTitlebarItem; +use gpui::MutableAppContext; + +pub fn init(cx: &mut MutableAppContext) { + contacts_popover::init(cx); + collab_titlebar_item::init(cx); +} diff --git a/crates/collab_titlebar_item/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs similarity index 99% rename from crates/collab_titlebar_item/src/contacts_popover.rs rename to crates/collab_ui/src/contacts_popover.rs index 26c1194d74..624030fe46 100644 --- a/crates/collab_titlebar_item/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -189,7 +189,7 @@ impl ContactsPopover { fn set_room(&mut self, room: Option>, cx: &mut ViewContext) { if let Some(room) = room { - let observation = cx.observe(&room, |this, room, cx| this.room_updated(room, cx)); + let observation = cx.observe(&room, |_, _, cx| cx.notify()); self.room = Some((room, observation)); } else { self.room = None; @@ -198,10 +198,6 @@ impl ContactsPopover { cx.notify(); } - fn room_updated(&mut self, room: ModelHandle, cx: &mut ViewContext) { - cx.notify(); - } - fn clear_filter(&mut self, _: &Cancel, cx: &mut ViewContext) { let did_clear = self.filter_editor.update(cx, |editor, cx| { if editor.buffer().read(cx).len(cx) > 0 { diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 82629705a1..667e3d7984 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -21,7 +21,7 @@ auto_update = { path = "../auto_update" } breadcrumbs = { path = "../breadcrumbs" } chat_panel = { path = "../chat_panel" } cli = { path = "../cli" } -collab_titlebar_item = { path = "../collab_titlebar_item" } +collab_ui = { path = "../collab_ui" } collections = { path = "../collections" } command_palette = { path = "../command_palette" } context_menu = { path = "../context_menu" } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 99983c4d6d..6233f0a037 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -107,7 +107,7 @@ fn main() { project::Project::init(&client); client::Channel::init(&client); client::init(client.clone(), cx); - collab_titlebar_item::init(cx); + collab_ui::init(cx); command_palette::init(cx); editor::init(cx); go_to_line::init(cx); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index bd6f28a402..a4cc8da633 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -10,7 +10,7 @@ use anyhow::{anyhow, Context, Result}; use assets::Assets; use breadcrumbs::Breadcrumbs; pub use client; -use collab_titlebar_item::CollabTitlebarItem; +use collab_ui::CollabTitlebarItem; use collections::VecDeque; pub use contacts_panel; use contacts_panel::ContactsPanel; @@ -21,11 +21,10 @@ use gpui::{ geometry::vector::vec2f, impl_actions, platform::{WindowBounds, WindowOptions}, - AssetSource, AsyncAppContext, ModelHandle, TitlebarOptions, ViewContext, WindowKind, + AssetSource, AsyncAppContext, TitlebarOptions, ViewContext, WindowKind, }; use language::Rope; pub use lsp; -use postage::watch; pub use project::{self, fs}; use project_panel::ProjectPanel; use search::{BufferSearchBar, ProjectSearchBar}; From 46b61feb9aad46bfc8ddd35e2d52d30c9e4bb361 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 28 Sep 2022 19:35:24 +0200 Subject: [PATCH 026/314] Open popup window when receiving a call We still need to style and allow people to accept the call but this is a good starting point. Co-Authored-By: Nathan Sobo --- crates/collab_ui/src/collab_ui.rs | 80 ++++++++++++++++++++++++++++++- crates/gpui/src/app.rs | 4 ++ crates/zed/src/main.rs | 2 +- 3 files changed, 83 insertions(+), 3 deletions(-) diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 312c7478e2..d3f12fdf6f 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -1,10 +1,86 @@ mod collab_titlebar_item; mod contacts_popover; +use client::{call::Call, UserStore}; pub use collab_titlebar_item::CollabTitlebarItem; -use gpui::MutableAppContext; +use futures::StreamExt; +use gpui::{ + elements::*, + geometry::{rect::RectF, vector::vec2f}, + Entity, ModelHandle, MutableAppContext, View, WindowBounds, WindowKind, WindowOptions, +}; +use settings::Settings; -pub fn init(cx: &mut MutableAppContext) { +pub fn init(user_store: ModelHandle, cx: &mut MutableAppContext) { contacts_popover::init(cx); collab_titlebar_item::init(cx); + + let mut incoming_call = user_store.read(cx).incoming_call(); + cx.spawn(|mut cx| async move { + let mut notification_window = None; + while let Some(incoming_call) = incoming_call.next().await { + if let Some(window_id) = notification_window.take() { + cx.remove_window(window_id); + } + + if let Some(incoming_call) = incoming_call { + let (window_id, _) = cx.add_window( + WindowOptions { + bounds: WindowBounds::Fixed(RectF::new(vec2f(0., 0.), vec2f(300., 400.))), + titlebar: None, + center: true, + kind: WindowKind::PopUp, + is_movable: false, + }, + |_| IncomingCallNotification::new(incoming_call), + ); + notification_window = Some(window_id); + } + } + }) + .detach(); +} + +struct IncomingCallNotification { + call: Call, +} + +impl IncomingCallNotification { + fn new(call: Call) -> Self { + Self { call } + } +} + +impl Entity for IncomingCallNotification { + type Event = (); +} + +impl View for IncomingCallNotification { + fn ui_name() -> &'static str { + "IncomingCallNotification" + } + + fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> gpui::ElementBox { + let theme = &cx.global::().theme.contacts_panel; + Flex::row() + .with_children(self.call.from.avatar.clone().map(|avatar| { + Image::new(avatar) + .with_style(theme.contact_avatar) + .aligned() + .left() + .boxed() + })) + .with_child( + Label::new( + self.call.from.github_login.clone(), + theme.contact_username.text.clone(), + ) + .contained() + .aligned() + .left() + .flex(1., true) + .boxed(), + ) + .boxed() + } } diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 18a2f8a4d0..04e27a8279 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -786,6 +786,10 @@ impl AsyncAppContext { self.update(|cx| cx.add_window(window_options, build_root_view)) } + pub fn remove_window(&mut self, window_id: usize) { + self.update(|cx| cx.remove_window(window_id)) + } + pub fn platform(&self) -> Arc { self.0.borrow().platform() } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 6233f0a037..de769a6e5e 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -107,7 +107,7 @@ fn main() { project::Project::init(&client); client::Channel::init(&client); client::init(client.clone(), cx); - collab_ui::init(cx); + collab_ui::init(user_store.clone(), cx); command_palette::init(cx); editor::init(cx); go_to_line::init(cx); From 04d194924e471eed785e13446fb9d372b0643817 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 28 Sep 2022 19:50:13 +0200 Subject: [PATCH 027/314] WIP: Start on `ActiveCall` Co-Authored-By: Nathan Sobo --- crates/room/src/active_call.rs | 55 ++++++++++++++++++++++++++++++++++ crates/room/src/room.rs | 1 + 2 files changed, 56 insertions(+) create mode 100644 crates/room/src/active_call.rs diff --git a/crates/room/src/active_call.rs b/crates/room/src/active_call.rs new file mode 100644 index 0000000000..63ca9583c2 --- /dev/null +++ b/crates/room/src/active_call.rs @@ -0,0 +1,55 @@ +use crate::Room; +use gpui::{Entity, ModelHandle, MutableAppContext}; + +#[derive(Default)] +pub struct ActiveCall { + room: Option>, +} + +impl Entity for ActiveCall { + type Event = (); +} + +impl ActiveCall { + pub fn global(cx: &mut MutableAppContext) -> ModelHandle { + if cx.has_global::>() { + let active_call = cx.add_model(|_| ActiveCall::default()); + cx.set_global(active_call.clone()); + active_call + } else { + cx.global::>().clone() + } + } + + pub fn observe(cx: &mut MutableAppContext, mut callback: F) -> gpui::Subscription + where + F: 'static + FnMut(Option>, &mut MutableAppContext), + { + cx.observe_default_global::>, _>(move |cx| { + let room = cx.global::>>().clone(); + callback(room, cx); + }) + } + + pub fn get_or_create( + client: &Arc, + user_store: &ModelHandle, + cx: &mut MutableAppContext, + ) -> Task>> { + if let Some(room) = cx.global::>>() { + Task::ready(Ok(room.clone())) + } else { + let client = client.clone(); + let user_store = user_store.clone(); + cx.spawn(|mut cx| async move { + let room = cx.update(|cx| Room::create(client, user_store, cx)).await?; + cx.update(|cx| cx.set_global(Some(room.clone()))); + Ok(room) + }) + } + } + + pub fn clear(cx: &mut MutableAppContext) { + cx.set_global::>>(None); + } +} diff --git a/crates/room/src/room.rs b/crates/room/src/room.rs index f7d5a58fa6..ba0a37e980 100644 --- a/crates/room/src/room.rs +++ b/crates/room/src/room.rs @@ -1,3 +1,4 @@ +mod active_call; mod participant; use anyhow::{anyhow, Result}; From a48995c782da8b5db09645240eea5821d1f776cb Mon Sep 17 00:00:00 2001 From: Isaac Clayton Date: Fri, 22 Jul 2022 12:25:07 +0200 Subject: [PATCH 028/314] Basic html highlighting + lsp support --- Cargo.lock | 11 ++ crates/zed/Cargo.toml | 1 + crates/zed/src/languages.rs | 6 ++ crates/zed/src/languages/html.rs | 101 +++++++++++++++++++ crates/zed/src/languages/html/brackets.scm | 2 + crates/zed/src/languages/html/config.toml | 10 ++ crates/zed/src/languages/html/highlights.scm | 11 ++ crates/zed/src/languages/html/indents.scm | 1 + crates/zed/src/languages/html/outline.scm | 0 9 files changed, 143 insertions(+) create mode 100644 crates/zed/src/languages/html.rs create mode 100644 crates/zed/src/languages/html/brackets.scm create mode 100644 crates/zed/src/languages/html/config.toml create mode 100644 crates/zed/src/languages/html/highlights.scm create mode 100644 crates/zed/src/languages/html/indents.scm create mode 100644 crates/zed/src/languages/html/outline.scm diff --git a/Cargo.lock b/Cargo.lock index 363ee93c14..5023de1f75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6043,6 +6043,16 @@ dependencies = [ "tree-sitter", ] +[[package]] +name = "tree-sitter-html" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "184e6b77953a354303dc87bf5fe36558c83569ce92606e7b382a0dc1b7443443" +dependencies = [ + "cc", + "tree-sitter", +] + [[package]] name = "tree-sitter-json" version = "0.19.0" @@ -7229,6 +7239,7 @@ dependencies = [ "tree-sitter-cpp", "tree-sitter-elixir", "tree-sitter-go", + "tree-sitter-html", "tree-sitter-json 0.20.0", "tree-sitter-markdown", "tree-sitter-python", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index dc2b0abd03..f84c8836bd 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -100,6 +100,7 @@ tree-sitter-markdown = { git = "https://github.com/MDeiml/tree-sitter-markdown", tree-sitter-python = "0.20.2" tree-sitter-toml = { git = "https://github.com/tree-sitter/tree-sitter-toml", rev = "342d9be207c2dba869b9967124c679b5e6fd0ebe" } tree-sitter-typescript = "0.20.1" +tree-sitter-html = "0.19.0" url = "2.2" [dev-dependencies] diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index 6e57106e87..ba1945f316 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -7,6 +7,7 @@ use std::{borrow::Cow, str, sync::Arc}; mod c; mod elixir; mod go; +mod html; mod installation; mod json; mod language_plugin; @@ -96,6 +97,11 @@ pub async fn init(languages: Arc, _executor: Arc) tree_sitter_typescript::language_tsx(), Some(CachedLspAdapter::new(typescript::TypeScriptLspAdapter).await), ), + ( + "html", + tree_sitter_html::language(), + Some(CachedLspAdapter::new(html::HtmlLspAdapter).await), + ), ] { languages.add(Arc::new(language(name, grammar, lsp_adapter))); } diff --git a/crates/zed/src/languages/html.rs b/crates/zed/src/languages/html.rs new file mode 100644 index 0000000000..5497841d88 --- /dev/null +++ b/crates/zed/src/languages/html.rs @@ -0,0 +1,101 @@ +use super::installation::{npm_install_packages, npm_package_latest_version}; +use anyhow::{anyhow, Context, Result}; +use async_trait::async_trait; +use client::http::HttpClient; +use futures::StreamExt; +use language::{LanguageServerName, LspAdapter}; +use serde_json::json; +use smol::fs; +use std::{any::Any, path::PathBuf, sync::Arc}; +use util::ResultExt; + +pub struct HtmlLspAdapter; + +impl HtmlLspAdapter { + const BIN_PATH: &'static str = + "node_modules/vscode-langservers-extracted/bin/vscode-html-language-server"; +} + +#[async_trait] +impl LspAdapter for HtmlLspAdapter { + async fn name(&self) -> LanguageServerName { + LanguageServerName("vscode-html-language-server".into()) + } + + async fn server_args(&self) -> Vec { + vec!["--stdio".into()] + } + + async fn fetch_latest_server_version( + &self, + _: Arc, + ) -> Result> { + Ok(Box::new(npm_package_latest_version("vscode-langservers-extracted").await?) as Box<_>) + } + + async fn fetch_server_binary( + &self, + version: Box, + _: Arc, + container_dir: PathBuf, + ) -> Result { + let version = version.downcast::().unwrap(); + let version_dir = container_dir.join(version.as_str()); + fs::create_dir_all(&version_dir) + .await + .context("failed to create version directory")?; + let binary_path = version_dir.join(Self::BIN_PATH); + + if fs::metadata(&binary_path).await.is_err() { + npm_install_packages( + [("vscode-langservers-extracted", version.as_str())], + &version_dir, + ) + .await?; + + if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() { + while let Some(entry) = entries.next().await { + if let Some(entry) = entry.log_err() { + let entry_path = entry.path(); + if entry_path.as_path() != version_dir { + fs::remove_dir_all(&entry_path).await.log_err(); + } + } + } + } + } + + Ok(binary_path) + } + + async fn cached_server_binary(&self, container_dir: PathBuf) -> Option { + (|| async move { + let mut last_version_dir = None; + let mut entries = fs::read_dir(&container_dir).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 bin_path = last_version_dir.join(Self::BIN_PATH); + if bin_path.exists() { + Ok(bin_path) + } else { + Err(anyhow!( + "missing executable in directory {:?}", + last_version_dir + )) + } + })() + .await + .log_err() + } + + async fn initialization_options(&self) -> Option { + Some(json!({ + "provideFormatter": true + })) + } +} diff --git a/crates/zed/src/languages/html/brackets.scm b/crates/zed/src/languages/html/brackets.scm new file mode 100644 index 0000000000..2d12b17daa --- /dev/null +++ b/crates/zed/src/languages/html/brackets.scm @@ -0,0 +1,2 @@ +("<" @open ">" @close) +("\"" @open "\"" @close) diff --git a/crates/zed/src/languages/html/config.toml b/crates/zed/src/languages/html/config.toml new file mode 100644 index 0000000000..0680717b2c --- /dev/null +++ b/crates/zed/src/languages/html/config.toml @@ -0,0 +1,10 @@ +name = "HTML" +path_suffixes = ["html"] +autoclose_before = ">" +brackets = [ + { start = "<", end = ">", close = true, newline = true }, + { start = "{", end = "}", close = true, newline = true }, + { start = "(", end = ")", close = true, newline = true }, + { start = "\"", end = "\"", close = true, newline = false }, + { start = "!--", end = " --", close = true, newline = false }, +] diff --git a/crates/zed/src/languages/html/highlights.scm b/crates/zed/src/languages/html/highlights.scm new file mode 100644 index 0000000000..d6d361ca49 --- /dev/null +++ b/crates/zed/src/languages/html/highlights.scm @@ -0,0 +1,11 @@ +(tag_name) @keyword +(doctype) @constant +(attribute_name) @property +(attribute_value) @string +(comment) @comment + +[ + "<" + ">" + " Date: Tue, 30 Aug 2022 16:52:41 -0700 Subject: [PATCH 029/314] Add JavaScript language injection in HTML --- crates/zed/src/languages/html/injections.scm | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 crates/zed/src/languages/html/injections.scm diff --git a/crates/zed/src/languages/html/injections.scm b/crates/zed/src/languages/html/injections.scm new file mode 100644 index 0000000000..6d2b8e9377 --- /dev/null +++ b/crates/zed/src/languages/html/injections.scm @@ -0,0 +1,4 @@ +(script_element + (raw_text) @content + (#set! "language" "javascript")) + From 21fb2b9bf1eea8ac3f6042478dfd31f78497c3f8 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 30 Aug 2022 17:23:09 -0700 Subject: [PATCH 030/314] Tweak HTML indents and highlights --- crates/language/src/buffer.rs | 2 ++ crates/language/src/language.rs | 4 ++++ crates/zed/src/languages/html/highlights.scm | 4 ++++ crates/zed/src/languages/html/indents.scm | 7 ++++++- crates/zed/src/languages/html/injections.scm | 1 - 5 files changed, 16 insertions(+), 2 deletions(-) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 08843aacfe..c8730be452 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1631,6 +1631,8 @@ impl BufferSnapshot { if capture.index == config.indent_capture_ix { start.get_or_insert(Point::from_ts_point(capture.node.start_position())); end.get_or_insert(Point::from_ts_point(capture.node.end_position())); + } else if Some(capture.index) == config.start_capture_ix { + start = Some(Point::from_ts_point(capture.node.end_position())); } else if Some(capture.index) == config.end_capture_ix { end = Some(Point::from_ts_point(capture.node.start_position())); } diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 780f6e75b5..0366bdf669 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -304,6 +304,7 @@ pub struct Grammar { struct IndentConfig { query: Query, indent_capture_ix: u32, + start_capture_ix: Option, end_capture_ix: Option, } @@ -661,11 +662,13 @@ impl Language { let grammar = self.grammar_mut(); let query = Query::new(grammar.ts_language, source)?; let mut indent_capture_ix = None; + let mut start_capture_ix = None; let mut end_capture_ix = None; get_capture_indices( &query, &mut [ ("indent", &mut indent_capture_ix), + ("start", &mut start_capture_ix), ("end", &mut end_capture_ix), ], ); @@ -673,6 +676,7 @@ impl Language { grammar.indents_config = Some(IndentConfig { query, indent_capture_ix, + start_capture_ix, end_capture_ix, }); } diff --git a/crates/zed/src/languages/html/highlights.scm b/crates/zed/src/languages/html/highlights.scm index d6d361ca49..0ce535fad4 100644 --- a/crates/zed/src/languages/html/highlights.scm +++ b/crates/zed/src/languages/html/highlights.scm @@ -1,11 +1,15 @@ (tag_name) @keyword +(erroneous_end_tag_name) @keyword (doctype) @constant (attribute_name) @property (attribute_value) @string (comment) @comment +"=" @operator + [ "<" ">" "" ] @punctuation.bracket \ No newline at end of file diff --git a/crates/zed/src/languages/html/indents.scm b/crates/zed/src/languages/html/indents.scm index a1560bfea7..436663dba3 100644 --- a/crates/zed/src/languages/html/indents.scm +++ b/crates/zed/src/languages/html/indents.scm @@ -1 +1,6 @@ -(tag_name) @indent +(start_tag ">" @end) @indent +(self_closing_tag "/>" @end) @indent + +(element + (start_tag) @start + (end_tag)? @end) @indent diff --git a/crates/zed/src/languages/html/injections.scm b/crates/zed/src/languages/html/injections.scm index 6d2b8e9377..60688f599f 100644 --- a/crates/zed/src/languages/html/injections.scm +++ b/crates/zed/src/languages/html/injections.scm @@ -1,4 +1,3 @@ (script_element (raw_text) @content (#set! "language" "javascript")) - From a2e57e8d71e00bcb9059e6c79854937eeb488047 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 Aug 2022 11:53:52 -0700 Subject: [PATCH 031/314] Add basic syntax highlighting for CSS --- Cargo.lock | 10 +++ crates/zed/Cargo.toml | 1 + crates/zed/src/languages.rs | 5 ++ crates/zed/src/languages/css/brackets.scm | 3 + crates/zed/src/languages/css/config.toml | 9 +++ crates/zed/src/languages/css/highlights.scm | 76 ++++++++++++++++++++ crates/zed/src/languages/css/indents.scm | 1 + crates/zed/src/languages/html/injections.scm | 8 ++- 8 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 crates/zed/src/languages/css/brackets.scm create mode 100644 crates/zed/src/languages/css/config.toml create mode 100644 crates/zed/src/languages/css/highlights.scm create mode 100644 crates/zed/src/languages/css/indents.scm diff --git a/Cargo.lock b/Cargo.lock index 5023de1f75..3d74213062 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6025,6 +6025,15 @@ dependencies = [ "tree-sitter", ] +[[package]] +name = "tree-sitter-css" +version = "0.19.0" +source = "git+https://github.com/tree-sitter/tree-sitter-css?rev=769203d0f9abe1a9a691ac2b9fe4bb4397a73c51#769203d0f9abe1a9a691ac2b9fe4bb4397a73c51" +dependencies = [ + "cc", + "tree-sitter", +] + [[package]] name = "tree-sitter-elixir" version = "0.19.0" @@ -7237,6 +7246,7 @@ dependencies = [ "tree-sitter", "tree-sitter-c", "tree-sitter-cpp", + "tree-sitter-css", "tree-sitter-elixir", "tree-sitter-go", "tree-sitter-html", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index f84c8836bd..27c8477355 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -92,6 +92,7 @@ toml = "0.5" tree-sitter = "0.20" tree-sitter-c = "0.20.1" tree-sitter-cpp = "0.20.0" +tree-sitter-css = { git = "https://github.com/tree-sitter/tree-sitter-css", rev = "769203d0f9abe1a9a691ac2b9fe4bb4397a73c51" } tree-sitter-elixir = { git = "https://github.com/elixir-lang/tree-sitter-elixir", rev = "05e3631c6a0701c1fa518b0fee7be95a2ceef5e2" } tree-sitter-go = { git = "https://github.com/tree-sitter/tree-sitter-go", rev = "aeb2f33b366fd78d5789ff104956ce23508b85db" } tree-sitter-json = { git = "https://github.com/tree-sitter/tree-sitter-json", rev = "137e1ce6a02698fc246cdb9c6b886ed1de9a1ed8" } diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index ba1945f316..71a85af0c8 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -47,6 +47,11 @@ pub async fn init(languages: Arc, _executor: Arc) tree_sitter_cpp::language(), Some(CachedLspAdapter::new(c::CLspAdapter).await), ), + ( + "css", + tree_sitter_css::language(), + None, // + ), ( "elixir", tree_sitter_elixir::language(), diff --git a/crates/zed/src/languages/css/brackets.scm b/crates/zed/src/languages/css/brackets.scm new file mode 100644 index 0000000000..191fd9c084 --- /dev/null +++ b/crates/zed/src/languages/css/brackets.scm @@ -0,0 +1,3 @@ +("(" @open ")" @close) +("[" @open "]" @close) +("{" @open "}" @close) diff --git a/crates/zed/src/languages/css/config.toml b/crates/zed/src/languages/css/config.toml new file mode 100644 index 0000000000..28def3abd5 --- /dev/null +++ b/crates/zed/src/languages/css/config.toml @@ -0,0 +1,9 @@ +name = "CSS" +path_suffixes = ["css"] +autoclose_before = ";:.,=}])>" +brackets = [ + { start = "{", end = "}", close = true, newline = true }, + { start = "[", end = "]", close = true, newline = true }, + { start = "(", end = ")", close = true, newline = true }, + { start = "\"", end = "\"", close = true, newline = false } +] diff --git a/crates/zed/src/languages/css/highlights.scm b/crates/zed/src/languages/css/highlights.scm new file mode 100644 index 0000000000..3638837af7 --- /dev/null +++ b/crates/zed/src/languages/css/highlights.scm @@ -0,0 +1,76 @@ +(comment) @comment + +[ + (tag_name) + (nesting_selector) + (universal_selector) +] @tag + +[ + "~" + ">" + "+" + "-" + "*" + "/" + "=" + "^=" + "|=" + "~=" + "$=" + "*=" + "and" + "or" + "not" + "only" +] @operator + +(attribute_selector (plain_value) @string) + +(attribute_name) @attribute +(pseudo_element_selector (tag_name) @attribute) +(pseudo_class_selector (class_name) @attribute) + +[ + (class_name) + (id_name) + (namespace_name) + (property_name) + (feature_name) +] @property + +(function_name) @function + +((property_name) @variable + (#match? @variable "^--")) +((plain_value) @variable + (#match? @variable "^--")) + +[ + "@media" + "@import" + "@charset" + "@namespace" + "@supports" + "@keyframes" + (at_keyword) + (to) + (from) + (important) +] @keyword + +(string_value) @string +(color_value) @string.special + +[ + (integer_value) + (float_value) +] @number + +(unit) @type + +[ + "#" + "," + ":" +] @punctuation.delimiter diff --git a/crates/zed/src/languages/css/indents.scm b/crates/zed/src/languages/css/indents.scm new file mode 100644 index 0000000000..e975469092 --- /dev/null +++ b/crates/zed/src/languages/css/indents.scm @@ -0,0 +1 @@ +(_ "{" "}" @end) @indent diff --git a/crates/zed/src/languages/html/injections.scm b/crates/zed/src/languages/html/injections.scm index 60688f599f..9084e373f2 100644 --- a/crates/zed/src/languages/html/injections.scm +++ b/crates/zed/src/languages/html/injections.scm @@ -1,3 +1,7 @@ (script_element - (raw_text) @content - (#set! "language" "javascript")) + (raw_text) @content + (#set! "language" "javascript")) + +(style_element + (raw_text) @content + (#set! "language" "css")) From 67e188a015cc95dc4c84440bd9a63cb4903789be Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 31 Aug 2022 16:50:44 -0700 Subject: [PATCH 032/314] Add Buffer::language_at, update MultiBuffer to use it Co-authored-by: Julia Risley --- Cargo.lock | 12 ++++ crates/editor/Cargo.toml | 4 ++ crates/editor/src/editor.rs | 94 ++++++++++++++++++++++++-- crates/editor/src/multi_buffer.rs | 27 +++++++- crates/language/src/buffer.rs | 21 +++++- crates/language/src/language.rs | 29 ++++---- crates/language/src/syntax_map.rs | 91 ++++++++++++++++--------- crates/language/src/tests.rs | 2 +- crates/zed/src/languages.rs | 6 +- crates/zed/src/languages/c.rs | 7 +- crates/zed/src/languages/elixir.rs | 4 +- crates/zed/src/languages/go.rs | 4 +- crates/zed/src/languages/python.rs | 7 +- crates/zed/src/languages/rust.rs | 6 +- crates/zed/src/languages/typescript.rs | 8 +-- crates/zed/src/zed.rs | 4 +- 16 files changed, 245 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3d74213062..79eae80258 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1719,6 +1719,8 @@ dependencies = [ "text", "theme", "tree-sitter", + "tree-sitter-html", + "tree-sitter-javascript", "tree-sitter-rust", "unindent", "util", @@ -6062,6 +6064,16 @@ dependencies = [ "tree-sitter", ] +[[package]] +name = "tree-sitter-javascript" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2490fab08630b2c8943c320f7b63473cbf65511c8d83aec551beb9b4375906ed" +dependencies = [ + "cc", + "tree-sitter", +] + [[package]] name = "tree-sitter-json" version = "0.19.0" diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index dfd4938742..cfe244d4fa 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -51,6 +51,8 @@ serde = { version = "1.0", features = ["derive", "rc"] } smallvec = { version = "1.6", features = ["union"] } smol = "1.2" tree-sitter-rust = { version = "*", optional = true } +tree-sitter-html = { version = "*", optional = true } +tree-sitter-javascript = { version = "*", optional = true } [dev-dependencies] text = { path = "../text", features = ["test-support"] } @@ -67,3 +69,5 @@ rand = "0.8" unindent = "0.1.7" tree-sitter = "0.20" tree-sitter-rust = "0.20" +tree-sitter-html = "0.19" +tree-sitter-javascript = "0.20" diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index c6cfd887db..b313d2de55 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1116,7 +1116,7 @@ impl Editor { &self, point: T, cx: &'a AppContext, - ) -> Option<&'a Arc> { + ) -> Option> { self.buffer.read(cx).language_at(point, cx) } @@ -4501,9 +4501,9 @@ impl Editor { // as that portion won't be used for detecting if a line is a comment. let full_comment_prefix: Arc = if let Some(prefix) = buffer .language_at(selection.start, cx) - .and_then(|l| l.line_comment_prefix()) + .and_then(|l| l.line_comment_prefix().map(|p| p.into())) { - prefix.into() + prefix } else { return; }; @@ -6713,7 +6713,7 @@ mod tests { platform::{WindowBounds, WindowOptions}, }; use indoc::indoc; - use language::{FakeLspAdapter, LanguageConfig}; + use language::{FakeLspAdapter, LanguageConfig, LanguageRegistry}; use project::FakeFs; use settings::EditorSettings; use std::{cell::RefCell, rc::Rc, time::Instant}; @@ -9792,6 +9792,92 @@ mod tests { }); } + #[gpui::test] + async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) { + let mut cx = EditorTestContext::new(cx).await; + + let html_language = Arc::new( + Language::new( + LanguageConfig { + name: "HTML".into(), + brackets: vec![BracketPair { + start: "<".to_string(), + end: ">".to_string(), + close: true, + newline: true, + }], + autoclose_before: "})]".to_string(), + ..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: vec![BracketPair { + start: "/*".to_string(), + end: "*/".to_string(), + close: true, + newline: true, + }], + autoclose_before: "})]".to_string(), + ..Default::default() + }, + Some(tree_sitter_javascript::language()), + )); + + 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#" + ˇ + + + "# + .unindent(), + ); + + let cursors = cx.update_editor(|editor, cx| editor.selections.ranges::(cx)); + cx.update_buffer(|buffer, _| { + let snapshot = buffer.snapshot(); + assert_eq!( + snapshot + .language_at(cursors[0].start) + .unwrap() + .name() + .as_ref(), + "HTML" + ); + assert_eq!( + snapshot + .language_at(cursors[1].start) + .unwrap() + .name() + .as_ref(), + "JavaScript" + ); + }); + } + #[gpui::test] async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) { cx.update(|cx| cx.set_global(Settings::test(cx))); diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 4ee9526a67..3b43f99ca0 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -1212,9 +1212,9 @@ impl MultiBuffer { &self, point: T, cx: &'a AppContext, - ) -> Option<&'a Arc> { + ) -> Option> { self.point_to_buffer_offset(point, cx) - .and_then(|(buffer, _)| buffer.read(cx).language()) + .and_then(|(buffer, offset)| buffer.read(cx).language_at(offset)) } pub fn files<'a>(&'a self, cx: &'a AppContext) -> SmallVec<[&'a dyn File; 2]> { @@ -1940,6 +1940,24 @@ impl MultiBufferSnapshot { } } + pub fn point_to_buffer_offset( + &self, + point: T, + ) -> Option<(&BufferSnapshot, usize)> { + let offset = point.to_offset(&self); + let mut cursor = self.excerpts.cursor::(); + 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, @@ -2490,6 +2508,11 @@ impl MultiBufferSnapshot { .and_then(|excerpt| excerpt.buffer.language()) } + pub fn language_at<'a, T: ToOffset>(&'a self, point: T) -> Option<&'a Arc> { + self.point_to_buffer_offset(point) + .and_then(|(buffer, offset)| buffer.language_at(offset)) + } + pub fn is_dirty(&self) -> bool { self.is_dirty } diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index c8730be452..372f77cf20 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -641,6 +641,15 @@ impl Buffer { self.language.as_ref() } + pub fn language_at(&self, position: D) -> Option> { + let offset = position.to_offset(self); + self.syntax_map + .lock() + .layers_for_range(offset..offset, &self.text) + .last() + .map(|info| info.language.clone()) + } + pub fn parse_count(&self) -> usize { self.parse_count } @@ -1826,6 +1835,14 @@ impl BufferSnapshot { self.language.as_ref() } + pub fn language_at(&self, position: D) -> Option<&Arc> { + let offset = position.to_offset(self); + self.syntax + .layers_for_range(offset..offset, &self.text) + .last() + .map(|info| info.language) + } + pub fn surrounding_word(&self, start: T) -> (Range, Option) { let mut start = start.to_offset(self); let mut end = start; @@ -1858,8 +1875,8 @@ impl BufferSnapshot { pub fn range_for_syntax_ancestor(&self, range: Range) -> Option> { let range = range.start.to_offset(self)..range.end.to_offset(self); let mut result: Option> = None; - 'outer: for (_, _, node) in self.syntax.layers_for_range(range.clone(), &self.text) { - let mut cursor = node.walk(); + 'outer: for layer in self.syntax.layers_for_range(range.clone(), &self.text) { + let mut cursor = layer.node.walk(); // Descend to the first leaf that touches the start of the range, // and if the range is non-empty, extends beyond the start. diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 0366bdf669..341f70bff9 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -135,7 +135,7 @@ impl CachedLspAdapter { pub async fn label_for_completion( &self, completion_item: &lsp::CompletionItem, - language: &Language, + language: &Arc, ) -> Option { self.adapter .label_for_completion(completion_item, language) @@ -146,7 +146,7 @@ impl CachedLspAdapter { &self, name: &str, kind: lsp::SymbolKind, - language: &Language, + language: &Arc, ) -> Option { self.adapter.label_for_symbol(name, kind, language).await } @@ -175,7 +175,7 @@ pub trait LspAdapter: 'static + Send + Sync { async fn label_for_completion( &self, _: &lsp::CompletionItem, - _: &Language, + _: &Arc, ) -> Option { None } @@ -184,7 +184,7 @@ pub trait LspAdapter: 'static + Send + Sync { &self, _: &str, _: lsp::SymbolKind, - _: &Language, + _: &Arc, ) -> Option { None } @@ -793,7 +793,7 @@ impl Language { } pub async fn label_for_completion( - &self, + self: &Arc, completion: &lsp::CompletionItem, ) -> Option { self.adapter @@ -802,7 +802,11 @@ impl Language { .await } - pub async fn label_for_symbol(&self, name: &str, kind: lsp::SymbolKind) -> Option { + pub async fn label_for_symbol( + self: &Arc, + name: &str, + kind: lsp::SymbolKind, + ) -> Option { self.adapter .as_ref()? .label_for_symbol(name, kind, self) @@ -810,20 +814,17 @@ impl Language { } pub fn highlight_text<'a>( - &'a self, + self: &'a Arc, text: &'a Rope, range: Range, ) -> Vec<(Range, HighlightId)> { let mut result = Vec::new(); if let Some(grammar) = &self.grammar { let tree = grammar.parse_text(text, None); - let captures = SyntaxSnapshot::single_tree_captures( - range.clone(), - text, - &tree, - grammar, - |grammar| grammar.highlights_query.as_ref(), - ); + let captures = + SyntaxSnapshot::single_tree_captures(range.clone(), text, &tree, self, |grammar| { + grammar.highlights_query.as_ref() + }); let highlight_maps = vec![grammar.highlight_map()]; let mut offset = 0; for chunk in BufferChunks::new(text, range, Some((captures, highlight_maps)), vec![]) { diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index a8cac76ac7..a7d9101d7b 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -92,6 +92,12 @@ struct SyntaxLayer { language: Arc, } +pub struct SyntaxLayerInfo<'a> { + pub depth: usize, + pub node: Node<'a>, + pub language: &'a Arc, +} + #[derive(Debug, Clone)] struct SyntaxLayerSummary { min_depth: usize, @@ -473,13 +479,18 @@ impl SyntaxSnapshot { range: Range, text: &'a Rope, tree: &'a Tree, - grammar: &'a Grammar, + language: &'a Arc, query: fn(&Grammar) -> Option<&Query>, ) -> SyntaxMapCaptures<'a> { SyntaxMapCaptures::new( range.clone(), text, - [(grammar, 0, tree.root_node())].into_iter(), + [SyntaxLayerInfo { + language, + depth: 0, + node: tree.root_node(), + }] + .into_iter(), query, ) } @@ -513,7 +524,7 @@ impl SyntaxSnapshot { } #[cfg(test)] - pub fn layers(&self, buffer: &BufferSnapshot) -> Vec<(&Grammar, usize, Node)> { + pub fn layers(&self, buffer: &BufferSnapshot) -> Vec { self.layers_for_range(0..buffer.len(), buffer) } @@ -521,7 +532,7 @@ impl SyntaxSnapshot { &self, range: Range, buffer: &BufferSnapshot, - ) -> Vec<(&Grammar, usize, Node)> { + ) -> Vec { let start = buffer.anchor_before(range.start.to_offset(buffer)); let end = buffer.anchor_after(range.end.to_offset(buffer)); @@ -538,16 +549,14 @@ impl SyntaxSnapshot { let mut result = Vec::new(); cursor.next(buffer); while let Some(layer) = cursor.item() { - if let Some(grammar) = &layer.language.grammar { - result.push(( - grammar.as_ref(), - layer.depth, - layer.tree.root_node_with_offset( - layer.range.start.to_offset(buffer), - layer.range.start.to_point(buffer).to_ts_point(), - ), - )); - } + result.push(SyntaxLayerInfo { + language: &layer.language, + depth: layer.depth, + node: layer.tree.root_node_with_offset( + layer.range.start.to_offset(buffer), + layer.range.start.to_point(buffer).to_ts_point(), + ), + }); cursor.next(buffer) } @@ -559,7 +568,7 @@ impl<'a> SyntaxMapCaptures<'a> { fn new( range: Range, text: &'a Rope, - layers: impl Iterator)>, + layers: impl Iterator>, query: fn(&Grammar) -> Option<&Query>, ) -> Self { let mut result = Self { @@ -567,11 +576,19 @@ impl<'a> SyntaxMapCaptures<'a> { grammars: Vec::new(), active_layer_count: 0, }; - for (grammar, depth, node) in layers { - let query = if let Some(query) = query(grammar) { - query - } else { - continue; + for SyntaxLayerInfo { + language, + depth, + node, + } in layers + { + let grammar = match &language.grammar { + Some(grammer) => grammer, + None => continue, + }; + let query = match query(&grammar) { + Some(query) => query, + None => continue, }; let mut query_cursor = QueryCursorHandle::new(); @@ -678,15 +695,23 @@ impl<'a> SyntaxMapMatches<'a> { fn new( range: Range, text: &'a Rope, - layers: impl Iterator)>, + layers: impl Iterator>, query: fn(&Grammar) -> Option<&Query>, ) -> Self { let mut result = Self::default(); - for (grammar, depth, node) in layers { - let query = if let Some(query) = query(grammar) { - query - } else { - continue; + for SyntaxLayerInfo { + language, + depth, + node, + } in layers + { + let grammar = match &language.grammar { + Some(grammer) => grammer, + None => continue, + }; + let query = match query(&grammar) { + Some(query) => query, + None => continue, }; let mut query_cursor = QueryCursorHandle::new(); @@ -1624,8 +1649,8 @@ mod tests { let reference_layers = reference_syntax_map.layers(&buffer); for (edited_layer, reference_layer) in layers.into_iter().zip(reference_layers.into_iter()) { - assert_eq!(edited_layer.2.to_sexp(), reference_layer.2.to_sexp()); - assert_eq!(edited_layer.2.range(), reference_layer.2.range()); + assert_eq!(edited_layer.node.to_sexp(), reference_layer.node.to_sexp()); + assert_eq!(edited_layer.node.range(), reference_layer.node.range()); } } @@ -1770,13 +1795,13 @@ mod tests { mutated_layers.into_iter().zip(reference_layers.into_iter()) { assert_eq!( - edited_layer.2.to_sexp(), - reference_layer.2.to_sexp(), + edited_layer.node.to_sexp(), + reference_layer.node.to_sexp(), "different layer at step {i}" ); assert_eq!( - edited_layer.2.range(), - reference_layer.2.range(), + edited_layer.node.range(), + reference_layer.node.range(), "different layer at step {i}" ); } @@ -1828,7 +1853,7 @@ mod tests { expected_layers.len(), "wrong number of layers" ); - for (i, ((_, _, node), expected_s_exp)) in + for (i, (SyntaxLayerInfo { node, .. }, expected_s_exp)) in layers.iter().zip(expected_layers.iter()).enumerate() { let actual_s_exp = node.to_sexp(); diff --git a/crates/language/src/tests.rs b/crates/language/src/tests.rs index 821bbc9968..8f56f3287e 100644 --- a/crates/language/src/tests.rs +++ b/crates/language/src/tests.rs @@ -1449,7 +1449,7 @@ fn get_tree_sexp(buffer: &ModelHandle, cx: &gpui::TestAppContext) -> Str buffer.read_with(cx, |buffer, _| { let snapshot = buffer.snapshot(); let layers = snapshot.syntax.layers(buffer.as_text_snapshot()); - layers[0].2.to_sexp() + layers[0].node.to_sexp() }) } diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index 71a85af0c8..2745fa824a 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -108,7 +108,7 @@ pub async fn init(languages: Arc, _executor: Arc) Some(CachedLspAdapter::new(html::HtmlLspAdapter).await), ), ] { - languages.add(Arc::new(language(name, grammar, lsp_adapter))); + languages.add(language(name, grammar, lsp_adapter)); } } @@ -116,7 +116,7 @@ pub(crate) fn language( name: &str, grammar: tree_sitter::Language, lsp_adapter: Option>, -) -> Language { +) -> Arc { let config = toml::from_slice( &LanguageDir::get(&format!("{}/config.toml", name)) .unwrap() @@ -153,7 +153,7 @@ pub(crate) fn language( if let Some(lsp_adapter) = lsp_adapter { language = language.with_lsp_adapter(lsp_adapter) } - language + Arc::new(language) } fn load_query(name: &str, filename_prefix: &str) -> Option> { diff --git a/crates/zed/src/languages/c.rs b/crates/zed/src/languages/c.rs index 6aa750f6a0..712e87101b 100644 --- a/crates/zed/src/languages/c.rs +++ b/crates/zed/src/languages/c.rs @@ -112,7 +112,7 @@ impl super::LspAdapter for CLspAdapter { async fn label_for_completion( &self, completion: &lsp::CompletionItem, - language: &Language, + language: &Arc, ) -> Option { let label = completion .label @@ -190,7 +190,7 @@ impl super::LspAdapter for CLspAdapter { &self, name: &str, kind: lsp::SymbolKind, - language: &Language, + language: &Arc, ) -> Option { let (text, filter_range, display_range) = match kind { lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => { @@ -251,7 +251,6 @@ mod tests { use gpui::MutableAppContext; use language::{AutoindentMode, Buffer}; use settings::Settings; - use std::sync::Arc; #[gpui::test] fn test_c_autoindent(cx: &mut MutableAppContext) { @@ -262,7 +261,7 @@ mod tests { let language = crate::languages::language("c", tree_sitter_c::language(), None); cx.add_model(|cx| { - let mut buffer = Buffer::new(0, "", cx).with_language(Arc::new(language), cx); + let mut buffer = Buffer::new(0, "", cx).with_language(language, cx); // empty function buffer.edit([(0..0, "int main() {}")], None, cx); diff --git a/crates/zed/src/languages/elixir.rs b/crates/zed/src/languages/elixir.rs index 4959338522..75b35bb630 100644 --- a/crates/zed/src/languages/elixir.rs +++ b/crates/zed/src/languages/elixir.rs @@ -113,7 +113,7 @@ impl LspAdapter for ElixirLspAdapter { async fn label_for_completion( &self, completion: &lsp::CompletionItem, - language: &Language, + language: &Arc, ) -> Option { match completion.kind.zip(completion.detail.as_ref()) { Some((_, detail)) if detail.starts_with("(function)") => { @@ -168,7 +168,7 @@ impl LspAdapter for ElixirLspAdapter { &self, name: &str, kind: SymbolKind, - language: &Language, + language: &Arc, ) -> Option { let (text, filter_range, display_range) = match kind { SymbolKind::METHOD | SymbolKind::FUNCTION => { diff --git a/crates/zed/src/languages/go.rs b/crates/zed/src/languages/go.rs index 729d39b513..19692fdf44 100644 --- a/crates/zed/src/languages/go.rs +++ b/crates/zed/src/languages/go.rs @@ -134,7 +134,7 @@ impl super::LspAdapter for GoLspAdapter { async fn label_for_completion( &self, completion: &lsp::CompletionItem, - language: &Language, + language: &Arc, ) -> Option { let label = &completion.label; @@ -235,7 +235,7 @@ impl super::LspAdapter for GoLspAdapter { &self, name: &str, kind: lsp::SymbolKind, - language: &Language, + language: &Arc, ) -> Option { let (text, filter_range, display_range) = match kind { lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => { diff --git a/crates/zed/src/languages/python.rs b/crates/zed/src/languages/python.rs index 274fc3216c..e6e55eeac4 100644 --- a/crates/zed/src/languages/python.rs +++ b/crates/zed/src/languages/python.rs @@ -90,7 +90,7 @@ impl LspAdapter for PythonLspAdapter { async fn label_for_completion( &self, item: &lsp::CompletionItem, - language: &language::Language, + language: &Arc, ) -> Option { let label = &item.label; let grammar = language.grammar()?; @@ -112,7 +112,7 @@ impl LspAdapter for PythonLspAdapter { &self, name: &str, kind: lsp::SymbolKind, - language: &language::Language, + language: &Arc, ) -> Option { let (text, filter_range, display_range) = match kind { lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => { @@ -149,7 +149,6 @@ mod tests { use gpui::{ModelContext, MutableAppContext}; use language::{AutoindentMode, Buffer}; use settings::Settings; - use std::sync::Arc; #[gpui::test] fn test_python_autoindent(cx: &mut MutableAppContext) { @@ -160,7 +159,7 @@ mod tests { cx.set_global(settings); cx.add_model(|cx| { - let mut buffer = Buffer::new(0, "", cx).with_language(Arc::new(language), cx); + let mut buffer = Buffer::new(0, "", cx).with_language(language, cx); let append = |buffer: &mut Buffer, text: &str, cx: &mut ModelContext| { let ix = buffer.len(); buffer.edit([(ix..ix, text)], Some(AutoindentMode::EachLine), cx); diff --git a/crates/zed/src/languages/rust.rs b/crates/zed/src/languages/rust.rs index adbe431279..f5776f3420 100644 --- a/crates/zed/src/languages/rust.rs +++ b/crates/zed/src/languages/rust.rs @@ -119,7 +119,7 @@ impl LspAdapter for RustLspAdapter { async fn label_for_completion( &self, completion: &lsp::CompletionItem, - language: &Language, + language: &Arc, ) -> Option { match completion.kind { Some(lsp::CompletionItemKind::FIELD) if completion.detail.is_some() => { @@ -196,7 +196,7 @@ impl LspAdapter for RustLspAdapter { &self, name: &str, kind: lsp::SymbolKind, - language: &Language, + language: &Arc, ) -> Option { let (text, filter_range, display_range) = match kind { lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => { @@ -439,7 +439,7 @@ mod tests { cx.set_global(settings); cx.add_model(|cx| { - let mut buffer = Buffer::new(0, "", cx).with_language(Arc::new(language), cx); + let mut buffer = Buffer::new(0, "", cx).with_language(language, cx); // indent between braces buffer.set_text("fn a() {}", cx); diff --git a/crates/zed/src/languages/typescript.rs b/crates/zed/src/languages/typescript.rs index 85a1bd6400..95f56bce5b 100644 --- a/crates/zed/src/languages/typescript.rs +++ b/crates/zed/src/languages/typescript.rs @@ -115,7 +115,7 @@ impl LspAdapter for TypeScriptLspAdapter { async fn label_for_completion( &self, item: &lsp::CompletionItem, - language: &language::Language, + language: &Arc, ) -> Option { use lsp::CompletionItemKind as Kind; let len = item.label.len(); @@ -144,7 +144,6 @@ impl LspAdapter for TypeScriptLspAdapter { #[cfg(test)] mod tests { - use std::sync::Arc; use gpui::MutableAppContext; use unindent::Unindent; @@ -172,9 +171,8 @@ mod tests { "# .unindent(); - let buffer = cx.add_model(|cx| { - language::Buffer::new(0, text, cx).with_language(Arc::new(language), cx) - }); + let buffer = + cx.add_model(|cx| language::Buffer::new(0, text, cx).with_language(language, cx)); let outline = buffer.read(cx).snapshot().outline(None).unwrap(); assert_eq!( outline diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 76bc62e4cb..f86022e39c 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1133,7 +1133,7 @@ mod tests { assert!(!editor.is_dirty(cx)); assert_eq!(editor.title(cx), "untitled"); assert!(Arc::ptr_eq( - editor.language_at(0, cx).unwrap(), + &editor.language_at(0, cx).unwrap(), &languages::PLAIN_TEXT )); editor.handle_input("hi", cx); @@ -1220,7 +1220,7 @@ mod tests { editor.update(cx, |editor, cx| { assert!(Arc::ptr_eq( - editor.language_at(0, cx).unwrap(), + &editor.language_at(0, cx).unwrap(), &languages::PLAIN_TEXT )); editor.handle_input("hi", cx); From 2b0794f5ae76d1f1fdd6a8d16c3a0db17ddc9cb6 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 9 Sep 2022 17:40:34 -0700 Subject: [PATCH 033/314] Restructure autoclosing to account for multi-language documents --- crates/editor/src/editor.rs | 572 +++++++++++----------- crates/zed/src/languages/html/config.toml | 2 +- 2 files changed, 281 insertions(+), 293 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index b313d2de55..055b73d7dc 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -410,7 +410,7 @@ pub struct Editor { add_selections_state: Option, select_next_state: Option, selection_history: SelectionHistory, - autoclose_stack: InvalidationStack, + autoclose_regions: Vec, snippet_stack: InvalidationStack, select_larger_syntax_node_stack: Vec]>>, ime_transaction: Option, @@ -569,8 +569,9 @@ struct SelectNextState { done: bool, } -struct BracketPairState { - ranges: Vec>, +struct AutocloseRegion { + selection_id: usize, + range: Range, pair: BracketPair, } @@ -1010,7 +1011,7 @@ impl Editor { add_selections_state: None, select_next_state: None, selection_history: Default::default(), - autoclose_stack: Default::default(), + autoclose_regions: Default::default(), snippet_stack: Default::default(), select_larger_syntax_node_stack: Vec::new(), ime_transaction: Default::default(), @@ -1401,8 +1402,7 @@ impl Editor { self.add_selections_state = None; self.select_next_state = None; self.select_larger_syntax_node_stack.clear(); - self.autoclose_stack - .invalidate(&self.selections.disjoint_anchors(), buffer); + self.invalidate_autoclose_regions(&self.selections.disjoint_anchors(), buffer); self.snippet_stack .invalidate(&self.selections.disjoint_anchors(), buffer); self.take_rename(false, cx); @@ -1849,15 +1849,158 @@ impl Editor { return; } - if !self.skip_autoclose_end(text, cx) { - self.transact(cx, |this, cx| { - if !this.surround_with_bracket_pair(text, cx) { - this.insert(text, cx); - this.autoclose_bracket_pairs(cx); + let text: Arc = text.into(); + let selections = self.selections.all_adjusted(cx); + let mut edits = Vec::new(); + let mut new_selections = Vec::with_capacity(selections.len()); + let mut new_autoclose_regions = Vec::new(); + let snapshot = self.buffer.read(cx).read(cx); + + for (selection, autoclose_region) in + self.selections_with_autoclose_regions(selections, &snapshot) + { + if let Some(language) = snapshot.language_at(selection.head()) { + // Determine if the inserted text matches the opening or closing + // bracket of any of this language's bracket pairs. + let mut bracket_pair = None; + let mut is_bracket_pair_start = false; + for pair in language.brackets() { + if pair.start.ends_with(text.as_ref()) { + bracket_pair = Some(pair.clone()); + is_bracket_pair_start = true; + break; + } else if pair.end.as_str() == text.as_ref() { + bracket_pair = Some(pair.clone()); + break; + } } - }); - self.trigger_completion_on_input(text, cx); + + if let Some(bracket_pair) = bracket_pair { + if selection.is_empty() { + if is_bracket_pair_start { + let prefix_len = bracket_pair.start.len() - text.len(); + + // If the inserted text is a suffix of an opening bracket and the + // selection is preceded by the rest of the opening bracket, then + // insert the closing bracket. + let should_autoclose = selection.start.column > (prefix_len as u32) + && snapshot.contains_str_at( + Point::new( + selection.start.row, + selection.start.column - (prefix_len as u32), + ), + &bracket_pair.start[..prefix_len], + ) + && snapshot + .chars_at(selection.start) + .next() + .map_or(true, |c| language.should_autoclose_before(c)); + if should_autoclose { + let anchor = snapshot.anchor_before(selection.end); + new_selections + .push((selection.map(|_| anchor.clone()), text.len())); + new_autoclose_regions.push(( + anchor.clone(), + text.len(), + selection.id, + bracket_pair.clone(), + )); + edits.push(( + selection.range(), + format!("{}{}", text, bracket_pair.end).into(), + )); + continue; + } + } else if let Some(region) = autoclose_region { + // If the selection is followed by an auto-inserted closing bracket, + // then don't insert anything else; just move the selection past the + // closing bracket. + let should_skip = selection.end == region.range.end.to_point(&snapshot); + if should_skip { + let anchor = snapshot.anchor_after(selection.end); + new_selections.push(( + selection.map(|_| anchor.clone()), + region.pair.end.len(), + )); + continue; + } + } + } + // If an opening bracket is typed while text is selected, then + // surround that text with the bracket pair. + else if is_bracket_pair_start { + edits.push((selection.start..selection.start, text.clone())); + edits.push(( + selection.end..selection.end, + bracket_pair.end.as_str().into(), + )); + new_selections.push(( + Selection { + id: selection.id, + start: snapshot.anchor_after(selection.start), + end: snapshot.anchor_before(selection.end), + reversed: selection.reversed, + goal: selection.goal, + }, + 0, + )); + continue; + } + } + } + + // If not handling any auto-close operation, then just replace the selected + // text with the given input and move the selection to the end of the + // newly inserted text. + let anchor = snapshot.anchor_after(selection.end); + new_selections.push((selection.map(|_| anchor.clone()), 0)); + edits.push((selection.start..selection.end, text.clone())); } + + drop(snapshot); + self.transact(cx, |this, cx| { + this.buffer.update(cx, |buffer, cx| { + buffer.edit(edits, Some(AutoindentMode::EachLine), cx); + }); + + let new_anchor_selections = new_selections.iter().map(|e| &e.0); + let new_selection_deltas = new_selections.iter().map(|e| e.1); + let snapshot = this.buffer.read(cx).read(cx); + let new_selections = resolve_multiple::(new_anchor_selections, &snapshot) + .zip(new_selection_deltas) + .map(|(selection, delta)| selection.map(|e| e + delta)) + .collect::>(); + + let mut i = 0; + for (position, delta, selection_id, pair) in new_autoclose_regions { + let position = position.to_offset(&snapshot) + delta; + let start = snapshot.anchor_before(position); + let end = snapshot.anchor_after(position); + while let Some(existing_state) = this.autoclose_regions.get(i) { + match existing_state.range.start.cmp(&start, &snapshot) { + Ordering::Less => i += 1, + Ordering::Greater => break, + Ordering::Equal => match end.cmp(&existing_state.range.end, &snapshot) { + Ordering::Less => i += 1, + Ordering::Equal => break, + Ordering::Greater => break, + }, + } + } + this.autoclose_regions.insert( + i, + AutocloseRegion { + selection_id, + range: start..end, + pair, + }, + ); + } + + drop(snapshot); + this.change_selections(None, cx, |s| s.select(new_selections)); + this.trigger_completion_on_input(&text, cx); + }); } pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext) { @@ -2029,232 +2172,89 @@ impl Editor { } } - fn surround_with_bracket_pair(&mut self, text: &str, cx: &mut ViewContext) -> bool { - let snapshot = self.buffer.read(cx).snapshot(cx); - if let Some(pair) = snapshot - .language() - .and_then(|language| language.brackets().iter().find(|b| b.start == text)) - .cloned() - { - if self - .selections - .all::(cx) - .iter() - .any(|selection| selection.is_empty()) - { - return false; - } - - let mut selections = self.selections.disjoint_anchors().to_vec(); - for selection in &mut selections { - selection.end = selection.end.bias_left(&snapshot); - } - drop(snapshot); - - self.buffer.update(cx, |buffer, cx| { - let pair_start: Arc = pair.start.clone().into(); - let pair_end: Arc = pair.end.clone().into(); - buffer.edit( - selections.iter().flat_map(|s| { - [ - (s.start.clone()..s.start.clone(), pair_start.clone()), - (s.end.clone()..s.end.clone(), pair_end.clone()), - ] - }), - None, - cx, - ); - }); - - let snapshot = self.buffer.read(cx).read(cx); - for selection in &mut selections { - selection.end = selection.end.bias_right(&snapshot); - } - drop(snapshot); - - self.change_selections(None, cx, |s| s.select_anchors(selections)); - true - } else { - false - } - } - - fn autoclose_bracket_pairs(&mut self, cx: &mut ViewContext) { + /// If any empty selections is touching the start of its innermost containing autoclose + /// region, expand it to select the brackets. + fn select_autoclose_pair(&mut self, cx: &mut ViewContext) { let selections = self.selections.all::(cx); - let mut bracket_pair_state = None; - let mut new_selections = None; - self.buffer.update(cx, |buffer, cx| { - let mut snapshot = buffer.snapshot(cx); - let left_biased_selections = selections - .iter() - .map(|selection| selection.map(|p| snapshot.anchor_before(p))) - .collect::>(); - - let autoclose_pair = snapshot.language().and_then(|language| { - let first_selection_start = selections.first().unwrap().start; - let pair = language.brackets().iter().find(|pair| { - pair.close - && snapshot.contains_str_at( - first_selection_start.saturating_sub(pair.start.len()), - &pair.start, - ) - }); - pair.and_then(|pair| { - let should_autoclose = selections.iter().all(|selection| { - // Ensure all selections are parked at the end of a pair start. - if snapshot.contains_str_at( - selection.start.saturating_sub(pair.start.len()), - &pair.start, - ) { - snapshot - .chars_at(selection.start) - .next() - .map_or(true, |c| language.should_autoclose_before(c)) - } else { - false + let buffer = self.buffer.read(cx).read(cx); + let mut new_selections = Vec::new(); + for (mut selection, region) in self.selections_with_autoclose_regions(selections, &buffer) { + if let (Some(region), true) = (region, selection.is_empty()) { + let mut range = region.range.to_offset(&buffer); + if selection.start == range.start { + if range.start >= region.pair.start.len() { + range.start -= region.pair.start.len(); + if buffer.contains_str_at(range.start, ®ion.pair.start) { + if buffer.contains_str_at(range.end, ®ion.pair.end) { + range.end += region.pair.end.len(); + selection.start = range.start; + selection.end = range.end; + } } - }); - - if should_autoclose { - Some(pair.clone()) - } else { - None } - }) - }); - - if let Some(pair) = autoclose_pair { - let selection_ranges = selections - .iter() - .map(|selection| { - let start = selection.start.to_offset(&snapshot); - start..start - }) - .collect::>(); - - let pair_end: Arc = pair.end.clone().into(); - buffer.edit( - selection_ranges - .iter() - .map(|range| (range.clone(), pair_end.clone())), - None, - cx, - ); - snapshot = buffer.snapshot(cx); - - new_selections = Some( - resolve_multiple::(left_biased_selections.iter(), &snapshot) - .collect::>(), - ); - - if pair.end.len() == 1 { - let mut delta = 0; - bracket_pair_state = Some(BracketPairState { - ranges: selections - .iter() - .map(move |selection| { - let offset = selection.start + delta; - delta += 1; - snapshot.anchor_before(offset)..snapshot.anchor_after(offset) - }) - .collect(), - pair, - }); } } - }); + new_selections.push(selection); + } - if let Some(new_selections) = new_selections { - self.change_selections(None, cx, |s| { - s.select(new_selections); - }); - } - if let Some(bracket_pair_state) = bracket_pair_state { - self.autoclose_stack.push(bracket_pair_state); - } + drop(buffer); + self.change_selections(None, cx, |selections| selections.select(new_selections)); } - fn skip_autoclose_end(&mut self, text: &str, cx: &mut ViewContext) -> bool { - let buffer = self.buffer.read(cx).snapshot(cx); - let old_selections = self.selections.all::(cx); - let autoclose_pair = if let Some(autoclose_pair) = self.autoclose_stack.last() { - autoclose_pair - } else { - return false; - }; - if text != autoclose_pair.pair.end { - return false; - } + /// Iterate the given selections, and for each one, find the smallest surrounding + /// autoclose region. This uses the ordering of the selections and the autoclose + /// regions to avoid repeated comparisons. + fn selections_with_autoclose_regions<'a, D: ToOffset + Clone>( + &'a self, + selections: impl IntoIterator>, + buffer: &'a MultiBufferSnapshot, + ) -> impl Iterator, Option<&'a AutocloseRegion>)> { + let mut i = 0; + let mut pair_states = self.autoclose_regions.as_slice(); + selections.into_iter().map(move |selection| { + let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer); - debug_assert_eq!(old_selections.len(), autoclose_pair.ranges.len()); - - if old_selections - .iter() - .zip(autoclose_pair.ranges.iter().map(|r| r.to_offset(&buffer))) - .all(|(selection, autoclose_range)| { - let autoclose_range_end = autoclose_range.end.to_offset(&buffer); - selection.is_empty() && selection.start == autoclose_range_end - }) - { - let new_selections = old_selections - .into_iter() - .map(|selection| { - let cursor = selection.start + 1; - Selection { - id: selection.id, - start: cursor, - end: cursor, - reversed: false, - goal: SelectionGoal::None, - } - }) - .collect(); - self.autoclose_stack.pop(); - self.change_selections(Some(Autoscroll::Fit), cx, |s| { - s.select(new_selections); - }); - true - } else { - false - } - } - - fn select_autoclose_pair(&mut self, cx: &mut ViewContext) -> bool { - let buffer = self.buffer.read(cx).snapshot(cx); - let old_selections = self.selections.all::(cx); - let autoclose_pair = if let Some(autoclose_pair) = self.autoclose_stack.last() { - autoclose_pair - } else { - return false; - }; - - debug_assert_eq!(old_selections.len(), autoclose_pair.ranges.len()); - - let mut new_selections = Vec::new(); - for (selection, autoclose_range) in old_selections - .iter() - .zip(autoclose_pair.ranges.iter().map(|r| r.to_offset(&buffer))) - { - if selection.is_empty() - && autoclose_range.is_empty() - && selection.start == autoclose_range.start - { - new_selections.push(Selection { - id: selection.id, - start: selection.start - autoclose_pair.pair.start.len(), - end: selection.end + autoclose_pair.pair.end.len(), - reversed: true, - goal: selection.goal, - }); - } else { - return false; + let mut enclosing = None; + while let Some(pair_state) = pair_states.get(i) { + if pair_state.range.end.to_offset(buffer) < range.start { + pair_states = &pair_states[i + 1..]; + i = 0; + } else if pair_state.range.start.to_offset(buffer) > range.end { + break; + } else if pair_state.selection_id == selection.id { + enclosing = Some(pair_state); + i += 1; + } } - } - self.change_selections(Some(Autoscroll::Fit), cx, |selections| { - selections.select(new_selections) + (selection.clone(), enclosing) + }) + } + + /// Remove any autoclose regions that no longer contain their selection. + fn invalidate_autoclose_regions( + &mut self, + mut selections: &[Selection], + buffer: &MultiBufferSnapshot, + ) { + self.autoclose_regions.retain(|state| { + let mut i = 0; + while let Some(selection) = selections.get(i) { + if selection.end.cmp(&state.range.start, buffer).is_lt() { + selections = &selections[1..]; + continue; + } + if selection.start.cmp(&state.range.end, buffer).is_gt() { + break; + } + if selection.id == state.selection_id { + return true; + } else { + i += 1; + } + } + false }); - true } fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option { @@ -2909,51 +2909,47 @@ impl Editor { pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext) { self.transact(cx, |this, cx| { - if !this.select_autoclose_pair(cx) { - let mut selections = this.selections.all::(cx); - if !this.selections.line_mode { - let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx)); - for selection in &mut selections { - if selection.is_empty() { - let old_head = selection.head(); - let mut new_head = movement::left( - &display_map, - old_head.to_display_point(&display_map), - ) - .to_point(&display_map); - if let Some((buffer, line_buffer_range)) = display_map - .buffer_snapshot - .buffer_line_for_row(old_head.row) - { - let indent_size = - buffer.indent_size_for_line(line_buffer_range.start.row); - let language_name = - buffer.language().map(|language| language.name()); - let indent_len = match indent_size.kind { - IndentKind::Space => { - cx.global::().tab_size(language_name.as_deref()) - } - IndentKind::Tab => NonZeroU32::new(1).unwrap(), - }; - if old_head.column <= indent_size.len && old_head.column > 0 { - let indent_len = indent_len.get(); - new_head = cmp::min( - new_head, - Point::new( - old_head.row, - ((old_head.column - 1) / indent_len) * indent_len, - ), - ); + this.select_autoclose_pair(cx); + let mut selections = this.selections.all::(cx); + if !this.selections.line_mode { + let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx)); + for selection in &mut selections { + if selection.is_empty() { + let old_head = selection.head(); + let mut new_head = + movement::left(&display_map, old_head.to_display_point(&display_map)) + .to_point(&display_map); + if let Some((buffer, line_buffer_range)) = display_map + .buffer_snapshot + .buffer_line_for_row(old_head.row) + { + let indent_size = + buffer.indent_size_for_line(line_buffer_range.start.row); + let language_name = buffer.language().map(|language| language.name()); + let indent_len = match indent_size.kind { + IndentKind::Space => { + cx.global::().tab_size(language_name.as_deref()) } + IndentKind::Tab => NonZeroU32::new(1).unwrap(), + }; + if old_head.column <= indent_size.len && old_head.column > 0 { + let indent_len = indent_len.get(); + new_head = cmp::min( + new_head, + Point::new( + old_head.row, + ((old_head.column - 1) / indent_len) * indent_len, + ), + ); } - - selection.set_head(new_head, SelectionGoal::None); } + + selection.set_head(new_head, SelectionGoal::None); } } - - this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections)); } + + this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections)); this.insert("", cx); }); } @@ -3957,17 +3953,16 @@ impl Editor { cx: &mut ViewContext, ) { self.transact(cx, |this, cx| { - if !this.select_autoclose_pair(cx) { - this.change_selections(Some(Autoscroll::Fit), cx, |s| { - let line_mode = s.line_mode; - s.move_with(|map, selection| { - if selection.is_empty() && !line_mode { - let cursor = movement::previous_word_start(map, selection.head()); - selection.set_head(cursor, SelectionGoal::None); - } - }); + this.select_autoclose_pair(cx); + this.change_selections(Some(Autoscroll::Fit), cx, |s| { + let line_mode = s.line_mode; + s.move_with(|map, selection| { + if selection.is_empty() && !line_mode { + let cursor = movement::previous_word_start(map, selection.head()); + selection.set_head(cursor, SelectionGoal::None); + } }); - } + }); this.insert("", cx); }); } @@ -3978,17 +3973,16 @@ impl Editor { cx: &mut ViewContext, ) { self.transact(cx, |this, cx| { - if !this.select_autoclose_pair(cx) { - this.change_selections(Some(Autoscroll::Fit), cx, |s| { - let line_mode = s.line_mode; - s.move_with(|map, selection| { - if selection.is_empty() && !line_mode { - let cursor = movement::previous_subword_start(map, selection.head()); - selection.set_head(cursor, SelectionGoal::None); - } - }); + this.select_autoclose_pair(cx); + this.change_selections(Some(Autoscroll::Fit), cx, |s| { + let line_mode = s.line_mode; + s.move_with(|map, selection| { + if selection.is_empty() && !line_mode { + let cursor = movement::previous_subword_start(map, selection.head()); + selection.set_head(cursor, SelectionGoal::None); + } }); - } + }); this.insert("", cx); }); } @@ -6495,12 +6489,6 @@ impl DerefMut for InvalidationStack { } } -impl InvalidationRegion for BracketPairState { - fn ranges(&self) -> &[Range] { - &self.ranges - } -} - impl InvalidationRegion for SnippetState { fn ranges(&self) -> &[Range] { &self.ranges[self.active_index] diff --git a/crates/zed/src/languages/html/config.toml b/crates/zed/src/languages/html/config.toml index 0680717b2c..80b33b1243 100644 --- a/crates/zed/src/languages/html/config.toml +++ b/crates/zed/src/languages/html/config.toml @@ -1,6 +1,6 @@ name = "HTML" path_suffixes = ["html"] -autoclose_before = ">" +autoclose_before = ">})" brackets = [ { start = "<", end = ">", close = true, newline = true }, { start = "{", end = "}", close = true, newline = true }, From 2da32af340980dc3eddefc924666774fba087710 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 28 Sep 2022 12:36:55 -0700 Subject: [PATCH 034/314] Update EditorTestContext usage to reflect new synchronous constructor --- crates/editor/src/editor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 055b73d7dc..699b442a5d 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -9782,7 +9782,7 @@ mod tests { #[gpui::test] async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) { - let mut cx = EditorTestContext::new(cx).await; + let mut cx = EditorTestContext::new(cx); let html_language = Arc::new( Language::new( From 4f44375abd3d8a10ee2820db373d4992ab3df42b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 28 Sep 2022 13:38:54 -0700 Subject: [PATCH 035/314] Make Buffer::language_at fall back to Buffer::language For languages with no grammar (plain text), there will be no layers. --- crates/language/src/buffer.rs | 2 ++ crates/language/src/language.rs | 9 +++++++++ crates/language/src/syntax_map.rs | 1 + 3 files changed, 12 insertions(+) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 372f77cf20..4ff1b002b0 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -648,6 +648,7 @@ impl Buffer { .layers_for_range(offset..offset, &self.text) .last() .map(|info| info.language.clone()) + .or_else(|| self.language.clone()) } pub fn parse_count(&self) -> usize { @@ -1841,6 +1842,7 @@ impl BufferSnapshot { .layers_for_range(offset..offset, &self.text) .last() .map(|info| info.language) + .or(self.language.as_ref()) } pub fn surrounding_word(&self, start: T) -> (Range, Option) { diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 341f70bff9..b8d4ca309f 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -26,6 +26,7 @@ use serde_json::Value; use std::{ any::Any, cell::RefCell, + fmt::Debug, mem, ops::Range, path::{Path, PathBuf}, @@ -866,6 +867,14 @@ impl Language { } } +impl Debug for Language { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Language") + .field("name", &self.config.name) + .finish() + } +} + impl Grammar { pub fn id(&self) -> usize { self.id diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index a7d9101d7b..8983406690 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -92,6 +92,7 @@ struct SyntaxLayer { language: Arc, } +#[derive(Debug)] pub struct SyntaxLayerInfo<'a> { pub depth: usize, pub node: Node<'a>, From 64253e444124a0b2b4810f4e0a091874c582cc7a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 28 Sep 2022 14:16:35 -0700 Subject: [PATCH 036/314] 0.56.0 --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 363ee93c14..b32b6a47a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7151,7 +7151,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.55.0" +version = "0.56.0" dependencies = [ "activity_indicator", "anyhow", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index dc2b0abd03..c96163d99e 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] description = "The fast, collaborative code editor." edition = "2021" name = "zed" -version = "0.55.0" +version = "0.56.0" [lib] name = "zed" From af7c2b8b4744738ffd70c427406b6657638a700b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 28 Sep 2022 15:21:49 -0700 Subject: [PATCH 037/314] Set minimum user id length in amplitude calls --- crates/client/src/telemetry.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/crates/client/src/telemetry.rs b/crates/client/src/telemetry.rs index 77aa308f30..8b7be5ba80 100644 --- a/crates/client/src/telemetry.rs +++ b/crates/client/src/telemetry.rs @@ -52,6 +52,12 @@ lazy_static! { struct AmplitudeEventBatch { api_key: &'static str, events: Vec, + options: AmplitudeEventBatchOptions, +} + +#[derive(Serialize)] +struct AmplitudeEventBatchOptions { + min_id_length: usize, } #[derive(Serialize)] @@ -239,7 +245,11 @@ impl Telemetry { } } - let batch = AmplitudeEventBatch { api_key, events }; + let batch = AmplitudeEventBatch { + api_key, + events, + options: AmplitudeEventBatchOptions { min_id_length: 1 }, + }; json_bytes.clear(); serde_json::to_writer(&mut json_bytes, &batch)?; let request = From 0d3486ca82dc6812d05b3c6876240ee62cd1ab9d Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 28 Sep 2022 15:30:55 -0700 Subject: [PATCH 038/314] Remove TestTelemetry command --- crates/client/src/client.rs | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 0670add1af..b75be62308 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -15,11 +15,9 @@ use async_tungstenite::tungstenite::{ use db::Db; use futures::{future::LocalBoxFuture, FutureExt, SinkExt, StreamExt, TryStreamExt}; use gpui::{ - actions, - serde_json::{json, Value}, - AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AnyWeakViewHandle, AppContext, - AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task, View, ViewContext, - ViewHandle, + actions, serde_json::Value, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, + AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, + MutableAppContext, Task, View, ViewContext, ViewHandle, }; use http::HttpClient; use lazy_static::lazy_static; @@ -56,7 +54,7 @@ lazy_static! { pub const ZED_SECRET_CLIENT_TOKEN: &str = "618033988749894"; -actions!(client, [Authenticate, TestTelemetry]); +actions!(client, [Authenticate]); pub fn init(client: Arc, cx: &mut MutableAppContext) { cx.add_global_action({ @@ -69,17 +67,6 @@ pub fn init(client: Arc, cx: &mut MutableAppContext) { .detach(); } }); - cx.add_global_action({ - let client = client.clone(); - move |_: &TestTelemetry, _| { - client.report_event( - "test_telemetry", - json!({ - "test_property": "test_value" - }), - ) - } - }); } pub struct Client { From 634f9de7e6152d28108cac94ac266f2efc77953b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 29 Sep 2022 10:48:51 +0200 Subject: [PATCH 039/314] Avoid using global for `Room` and extract that logic into `ActiveCall` --- crates/collab_ui/src/contacts_popover.rs | 40 ++++++-------- crates/room/src/active_call.rs | 67 +++++++++++++++++------- crates/room/src/room.rs | 33 +----------- 3 files changed, 63 insertions(+), 77 deletions(-) diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index 624030fe46..2ea0c75623 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -9,7 +9,7 @@ use gpui::{ ViewHandle, }; use menu::{Confirm, SelectNext, SelectPrev}; -use room::Room; +use room::{ActiveCall, Room}; use settings::Settings; use theme::IconButton; @@ -80,7 +80,7 @@ pub enum Event { } pub struct ContactsPopover { - room: Option<(ModelHandle, Subscription)>, + room_subscription: Option, entries: Vec, match_candidates: Vec, list_state: ListState, @@ -161,18 +161,20 @@ impl ContactsPopover { } }); + let active_call = ActiveCall::global(cx); let mut subscriptions = Vec::new(); subscriptions.push(cx.observe(&user_store, |this, _, cx| this.update_entries(cx))); - - let weak_self = cx.weak_handle(); - subscriptions.push(Room::observe(cx, move |room, cx| { - if let Some(this) = weak_self.upgrade(cx) { - this.update(cx, |this, cx| this.set_room(room, cx)); + subscriptions.push(cx.observe(&active_call, |this, active_call, cx| { + if let Some(room) = active_call.read(cx).room().cloned() { + this.room_subscription = Some(cx.observe(&room, |_, _, cx| cx.notify())); + } else { + this.room_subscription = None; } + cx.notify(); })); let mut this = Self { - room: None, + room_subscription: None, list_state, selection: None, collapsed_sections: Default::default(), @@ -187,17 +189,6 @@ impl ContactsPopover { this } - fn set_room(&mut self, room: Option>, cx: &mut ViewContext) { - if let Some(room) = room { - let observation = cx.observe(&room, |_, _, cx| cx.notify()); - self.room = Some((room, observation)); - } else { - self.room = None; - } - - cx.notify(); - } - fn clear_filter(&mut self, _: &Cancel, cx: &mut ViewContext) { let did_clear = self.filter_editor.update(cx, |editor, cx| { if editor.buffer().read(cx).len(cx) > 0 { @@ -394,7 +385,7 @@ impl ContactsPopover { } fn render_active_call(&self, cx: &mut RenderContext) -> Option { - let (room, _) = self.room.as_ref()?; + let room = ActiveCall::global(cx).read(cx).room()?; let theme = &cx.global::().theme.contacts_panel; Some( @@ -642,13 +633,12 @@ impl ContactsPopover { } fn call(&mut self, action: &Call, cx: &mut ViewContext) { - let client = self.client.clone(); - let user_store = self.user_store.clone(); let recipient_user_id = action.recipient_user_id; + let room = ActiveCall::global(cx).update(cx, |active_call, cx| { + active_call.get_or_create(&self.client, &self.user_store, cx) + }); cx.spawn_weak(|_, mut cx| async move { - let room = cx - .update(|cx| Room::get_or_create(&client, &user_store, cx)) - .await?; + let room = room.await?; room.update(&mut cx, |room, cx| room.call(recipient_user_id, cx)) .await?; anyhow::Ok(()) diff --git a/crates/room/src/active_call.rs b/crates/room/src/active_call.rs index 63ca9583c2..de0bc5e639 100644 --- a/crates/room/src/active_call.rs +++ b/crates/room/src/active_call.rs @@ -1,5 +1,9 @@ use crate::Room; -use gpui::{Entity, ModelHandle, MutableAppContext}; +use anyhow::{anyhow, Result}; +use client::{call::Call, Client, UserStore}; +use gpui::{Entity, ModelContext, ModelHandle, MutableAppContext, Task}; +use std::sync::Arc; +use util::ResultExt; #[derive(Default)] pub struct ActiveCall { @@ -13,43 +17,66 @@ impl Entity for ActiveCall { impl ActiveCall { pub fn global(cx: &mut MutableAppContext) -> ModelHandle { if cx.has_global::>() { + cx.global::>().clone() + } else { let active_call = cx.add_model(|_| ActiveCall::default()); cx.set_global(active_call.clone()); active_call - } else { - cx.global::>().clone() } } - pub fn observe(cx: &mut MutableAppContext, mut callback: F) -> gpui::Subscription - where - F: 'static + FnMut(Option>, &mut MutableAppContext), - { - cx.observe_default_global::>, _>(move |cx| { - let room = cx.global::>>().clone(); - callback(room, cx); - }) - } - pub fn get_or_create( + &mut self, client: &Arc, user_store: &ModelHandle, - cx: &mut MutableAppContext, + cx: &mut ModelContext, ) -> Task>> { - if let Some(room) = cx.global::>>() { - Task::ready(Ok(room.clone())) + if let Some(room) = self.room.clone() { + Task::ready(Ok(room)) } else { let client = client.clone(); let user_store = user_store.clone(); - cx.spawn(|mut cx| async move { + cx.spawn(|this, mut cx| async move { let room = cx.update(|cx| Room::create(client, user_store, cx)).await?; - cx.update(|cx| cx.set_global(Some(room.clone()))); + this.update(&mut cx, |this, cx| { + this.room = Some(room.clone()); + cx.notify(); + }); Ok(room) }) } } - pub fn clear(cx: &mut MutableAppContext) { - cx.set_global::>>(None); + pub fn join( + &mut self, + call: &Call, + client: &Arc, + user_store: &ModelHandle, + cx: &mut ModelContext, + ) -> Task>> { + if self.room.is_some() { + return Task::ready(Err(anyhow!("cannot join while on another call"))); + } + + let join = Room::join(call, client.clone(), user_store.clone(), cx); + cx.spawn(|this, mut cx| async move { + let room = join.await?; + this.update(&mut cx, |this, cx| { + this.room = Some(room.clone()); + cx.notify(); + }); + Ok(room) + }) + } + + pub fn room(&self) -> Option<&ModelHandle> { + self.room.as_ref() + } + + pub fn clear(&mut self, cx: &mut ModelContext) { + if let Some(room) = self.room.take() { + room.update(cx, |room, cx| room.leave(cx)).log_err(); + cx.notify(); + } } } diff --git a/crates/room/src/room.rs b/crates/room/src/room.rs index ba0a37e980..229ee69aeb 100644 --- a/crates/room/src/room.rs +++ b/crates/room/src/room.rs @@ -1,6 +1,7 @@ mod active_call; mod participant; +pub use active_call::ActiveCall; use anyhow::{anyhow, Result}; use client::{call::Call, proto, Client, PeerId, TypedEnvelope, User, UserStore}; use collections::HashMap; @@ -32,38 +33,6 @@ impl Entity for Room { } impl Room { - pub fn observe(cx: &mut MutableAppContext, mut callback: F) -> gpui::Subscription - where - F: 'static + FnMut(Option>, &mut MutableAppContext), - { - cx.observe_default_global::>, _>(move |cx| { - let room = cx.global::>>().clone(); - callback(room, cx); - }) - } - - pub fn get_or_create( - client: &Arc, - user_store: &ModelHandle, - cx: &mut MutableAppContext, - ) -> Task>> { - if let Some(room) = cx.global::>>() { - Task::ready(Ok(room.clone())) - } else { - let client = client.clone(); - let user_store = user_store.clone(); - cx.spawn(|mut cx| async move { - let room = cx.update(|cx| Room::create(client, user_store, cx)).await?; - cx.update(|cx| cx.set_global(Some(room.clone()))); - Ok(room) - }) - } - } - - pub fn clear(cx: &mut MutableAppContext) { - cx.set_global::>>(None); - } - fn new( id: u64, client: Arc, From 1158911560586b6e5466d3188bc9d3e343971f33 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 29 Sep 2022 15:33:33 +0200 Subject: [PATCH 040/314] Wire up accepting/declining a call --- crates/client/src/call.rs | 2 +- crates/client/src/user.rs | 2 +- crates/collab_ui/src/collab_ui.rs | 84 +--------- crates/collab_ui/src/contacts_popover.rs | 2 +- .../src/incoming_call_notification.rs | 152 ++++++++++++++++++ crates/zed/src/main.rs | 2 +- 6 files changed, 162 insertions(+), 82 deletions(-) create mode 100644 crates/collab_ui/src/incoming_call_notification.rs diff --git a/crates/client/src/call.rs b/crates/client/src/call.rs index 3111a04949..9b7ce06df4 100644 --- a/crates/client/src/call.rs +++ b/crates/client/src/call.rs @@ -4,6 +4,6 @@ use std::sync::Arc; #[derive(Clone)] pub struct Call { pub room_id: u64, - pub from: Arc, + pub caller: Arc, pub participants: Vec>, } diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 0dbb8bb198..d285cb09db 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -212,7 +212,7 @@ impl UserStore { this.get_users(envelope.payload.participant_user_ids, cx) }) .await?, - from: this + caller: this .update(&mut cx, |this, cx| { this.get_user(envelope.payload.caller_user_id, cx) }) diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index d3f12fdf6f..b101bb991d 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -1,86 +1,14 @@ mod collab_titlebar_item; mod contacts_popover; +mod incoming_call_notification; -use client::{call::Call, UserStore}; +use client::{Client, UserStore}; pub use collab_titlebar_item::CollabTitlebarItem; -use futures::StreamExt; -use gpui::{ - elements::*, - geometry::{rect::RectF, vector::vec2f}, - Entity, ModelHandle, MutableAppContext, View, WindowBounds, WindowKind, WindowOptions, -}; -use settings::Settings; +use gpui::{ModelHandle, MutableAppContext}; +use std::sync::Arc; -pub fn init(user_store: ModelHandle, cx: &mut MutableAppContext) { +pub fn init(client: Arc, user_store: ModelHandle, cx: &mut MutableAppContext) { contacts_popover::init(cx); collab_titlebar_item::init(cx); - - let mut incoming_call = user_store.read(cx).incoming_call(); - cx.spawn(|mut cx| async move { - let mut notification_window = None; - while let Some(incoming_call) = incoming_call.next().await { - if let Some(window_id) = notification_window.take() { - cx.remove_window(window_id); - } - - if let Some(incoming_call) = incoming_call { - let (window_id, _) = cx.add_window( - WindowOptions { - bounds: WindowBounds::Fixed(RectF::new(vec2f(0., 0.), vec2f(300., 400.))), - titlebar: None, - center: true, - kind: WindowKind::PopUp, - is_movable: false, - }, - |_| IncomingCallNotification::new(incoming_call), - ); - notification_window = Some(window_id); - } - } - }) - .detach(); -} - -struct IncomingCallNotification { - call: Call, -} - -impl IncomingCallNotification { - fn new(call: Call) -> Self { - Self { call } - } -} - -impl Entity for IncomingCallNotification { - type Event = (); -} - -impl View for IncomingCallNotification { - fn ui_name() -> &'static str { - "IncomingCallNotification" - } - - fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> gpui::ElementBox { - let theme = &cx.global::().theme.contacts_panel; - Flex::row() - .with_children(self.call.from.avatar.clone().map(|avatar| { - Image::new(avatar) - .with_style(theme.contact_avatar) - .aligned() - .left() - .boxed() - })) - .with_child( - Label::new( - self.call.from.github_login.clone(), - theme.contact_username.text.clone(), - ) - .contained() - .aligned() - .left() - .flex(1., true) - .boxed(), - ) - .boxed() - } + incoming_call_notification::init(client, user_store, cx); } diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index 2ea0c75623..15a987d6c2 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -9,7 +9,7 @@ use gpui::{ ViewHandle, }; use menu::{Confirm, SelectNext, SelectPrev}; -use room::{ActiveCall, Room}; +use room::ActiveCall; use settings::Settings; use theme::IconButton; diff --git a/crates/collab_ui/src/incoming_call_notification.rs b/crates/collab_ui/src/incoming_call_notification.rs new file mode 100644 index 0000000000..ec959f01ea --- /dev/null +++ b/crates/collab_ui/src/incoming_call_notification.rs @@ -0,0 +1,152 @@ +use std::sync::Arc; + +use client::{call::Call, Client, UserStore}; +use futures::StreamExt; +use gpui::{ + elements::*, + geometry::{rect::RectF, vector::vec2f}, + impl_internal_actions, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, + View, ViewContext, WindowBounds, WindowKind, WindowOptions, +}; +use room::ActiveCall; +use settings::Settings; +use util::ResultExt; + +impl_internal_actions!(incoming_call_notification, [RespondToCall]); + +pub fn init(client: Arc, user_store: ModelHandle, cx: &mut MutableAppContext) { + cx.add_action(IncomingCallNotification::respond_to_call); + + let mut incoming_call = user_store.read(cx).incoming_call(); + cx.spawn(|mut cx| async move { + let mut notification_window = None; + while let Some(incoming_call) = incoming_call.next().await { + if let Some(window_id) = notification_window.take() { + cx.remove_window(window_id); + } + + if let Some(incoming_call) = incoming_call { + let (window_id, _) = cx.add_window( + WindowOptions { + bounds: WindowBounds::Fixed(RectF::new(vec2f(0., 0.), vec2f(300., 400.))), + titlebar: None, + center: true, + kind: WindowKind::PopUp, + is_movable: false, + }, + |_| { + IncomingCallNotification::new( + incoming_call, + client.clone(), + user_store.clone(), + ) + }, + ); + notification_window = Some(window_id); + } + } + }) + .detach(); +} + +#[derive(Clone, PartialEq)] +struct RespondToCall { + accept: bool, +} + +pub struct IncomingCallNotification { + call: Call, + client: Arc, + user_store: ModelHandle, +} + +impl IncomingCallNotification { + pub fn new(call: Call, client: Arc, user_store: ModelHandle) -> Self { + Self { + call, + client, + user_store, + } + } + + fn respond_to_call(&mut self, action: &RespondToCall, cx: &mut ViewContext) { + if action.accept { + ActiveCall::global(cx) + .update(cx, |active_call, cx| { + active_call.join(&self.call, &self.client, &self.user_store, cx) + }) + .detach_and_log_err(cx); + } else { + self.user_store + .update(cx, |user_store, _| user_store.decline_call().log_err()); + } + + let window_id = cx.window_id(); + cx.remove_window(window_id); + } + + fn render_caller(&self, cx: &mut RenderContext) -> ElementBox { + let theme = &cx.global::().theme.contacts_panel; + Flex::row() + .with_children( + self.call + .caller + .avatar + .clone() + .map(|avatar| Image::new(avatar).with_style(theme.contact_avatar).boxed()), + ) + .with_child( + Label::new( + self.call.caller.github_login.clone(), + theme.contact_username.text.clone(), + ) + .boxed(), + ) + .boxed() + } + + fn render_buttons(&self, cx: &mut RenderContext) -> ElementBox { + enum Accept {} + enum Decline {} + + Flex::row() + .with_child( + MouseEventHandler::::new(0, cx, |_, cx| { + let theme = &cx.global::().theme.contacts_panel; + Label::new("Accept".to_string(), theme.contact_username.text.clone()).boxed() + }) + .on_click(MouseButton::Left, |_, cx| { + cx.dispatch_action(RespondToCall { accept: true }); + }) + .boxed(), + ) + .with_child( + MouseEventHandler::::new(0, cx, |_, cx| { + let theme = &cx.global::().theme.contacts_panel; + Label::new("Decline".to_string(), theme.contact_username.text.clone()).boxed() + }) + .on_click(MouseButton::Left, |_, cx| { + cx.dispatch_action(RespondToCall { accept: false }); + }) + .boxed(), + ) + .boxed() + } +} + +impl Entity for IncomingCallNotification { + type Event = (); +} + +impl View for IncomingCallNotification { + fn ui_name() -> &'static str { + "IncomingCallNotification" + } + + fn render(&mut self, cx: &mut RenderContext) -> gpui::ElementBox { + Flex::column() + .with_child(self.render_caller(cx)) + .with_child(self.render_buttons(cx)) + .boxed() + } +} diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index de769a6e5e..29ad1c5eb0 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -107,7 +107,7 @@ fn main() { project::Project::init(&client); client::Channel::init(&client); client::init(client.clone(), cx); - collab_ui::init(user_store.clone(), cx); + collab_ui::init(client.clone(), user_store.clone(), cx); command_palette::init(cx); editor::init(cx); go_to_line::init(cx); From e0db62173aba87cfd2d9eb4245e77501cbb37bf5 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 29 Sep 2022 17:24:31 +0200 Subject: [PATCH 041/314] Rename `room` crate to `call` Also, rename `client::Call` to `client::IncomingCall`. Co-Authored-By: Nathan Sobo --- Cargo.lock | 30 +++++++++---------- crates/{room => call}/Cargo.toml | 4 +-- .../src/active_call.rs => call/src/call.rs} | 9 ++++-- crates/{room => call}/src/participant.rs | 0 crates/{room => call}/src/room.rs | 10 ++----- crates/client/src/client.rs | 2 +- .../client/src/{call.rs => incoming_call.rs} | 2 +- crates/client/src/user.rs | 11 ++++--- crates/collab/Cargo.toml | 2 +- crates/collab/src/integration_tests.rs | 2 +- crates/collab_ui/Cargo.toml | 6 ++-- crates/collab_ui/src/contacts_popover.rs | 2 +- .../src/incoming_call_notification.rs | 12 +++++--- 13 files changed, 49 insertions(+), 43 deletions(-) rename crates/{room => call}/Cargo.toml (95%) rename crates/{room/src/active_call.rs => call/src/call.rs} (94%) rename crates/{room => call}/src/participant.rs (100%) rename crates/{room => call}/src/room.rs (96%) rename crates/client/src/{call.rs => incoming_call.rs} (84%) diff --git a/Cargo.lock b/Cargo.lock index 655ec8ab03..b3fe2f899b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -684,6 +684,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" +[[package]] +name = "call" +version = "0.1.0" +dependencies = [ + "anyhow", + "client", + "collections", + "futures", + "gpui", + "project", + "util", +] + [[package]] name = "cap-fs-ext" version = "0.24.4" @@ -1019,6 +1032,7 @@ dependencies = [ "axum", "axum-extra", "base64", + "call", "clap 3.2.8", "client", "collections", @@ -1040,7 +1054,6 @@ dependencies = [ "prometheus", "rand 0.8.5", "reqwest", - "room", "rpc", "scrypt", "serde", @@ -1067,6 +1080,7 @@ name = "collab_ui" version = "0.1.0" dependencies = [ "anyhow", + "call", "client", "clock", "collections", @@ -1078,7 +1092,6 @@ dependencies = [ "menu", "postage", "project", - "room", "serde", "settings", "theme", @@ -4452,19 +4465,6 @@ dependencies = [ "librocksdb-sys", ] -[[package]] -name = "room" -version = "0.1.0" -dependencies = [ - "anyhow", - "client", - "collections", - "futures", - "gpui", - "project", - "util", -] - [[package]] name = "roxmltree" version = "0.14.1" diff --git a/crates/room/Cargo.toml b/crates/call/Cargo.toml similarity index 95% rename from crates/room/Cargo.toml rename to crates/call/Cargo.toml index 169f04d352..cf5e7d6702 100644 --- a/crates/room/Cargo.toml +++ b/crates/call/Cargo.toml @@ -1,10 +1,10 @@ [package] -name = "room" +name = "call" version = "0.1.0" edition = "2021" [lib] -path = "src/room.rs" +path = "src/call.rs" doctest = false [features] diff --git a/crates/room/src/active_call.rs b/crates/call/src/call.rs similarity index 94% rename from crates/room/src/active_call.rs rename to crates/call/src/call.rs index de0bc5e639..11dde75697 100644 --- a/crates/room/src/active_call.rs +++ b/crates/call/src/call.rs @@ -1,7 +1,10 @@ -use crate::Room; +mod participant; +mod room; + use anyhow::{anyhow, Result}; -use client::{call::Call, Client, UserStore}; +use client::{incoming_call::IncomingCall, Client, UserStore}; use gpui::{Entity, ModelContext, ModelHandle, MutableAppContext, Task}; +pub use room::Room; use std::sync::Arc; use util::ResultExt; @@ -49,7 +52,7 @@ impl ActiveCall { pub fn join( &mut self, - call: &Call, + call: &IncomingCall, client: &Arc, user_store: &ModelHandle, cx: &mut ModelContext, diff --git a/crates/room/src/participant.rs b/crates/call/src/participant.rs similarity index 100% rename from crates/room/src/participant.rs rename to crates/call/src/participant.rs diff --git a/crates/room/src/room.rs b/crates/call/src/room.rs similarity index 96% rename from crates/room/src/room.rs rename to crates/call/src/room.rs index 229ee69aeb..adf3a676aa 100644 --- a/crates/room/src/room.rs +++ b/crates/call/src/room.rs @@ -1,13 +1,9 @@ -mod active_call; -mod participant; - -pub use active_call::ActiveCall; +use crate::participant::{LocalParticipant, ParticipantLocation, RemoteParticipant}; use anyhow::{anyhow, Result}; -use client::{call::Call, proto, Client, PeerId, TypedEnvelope, User, UserStore}; +use client::{incoming_call::IncomingCall, proto, Client, PeerId, TypedEnvelope, User, UserStore}; use collections::HashMap; use futures::StreamExt; use gpui::{AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task}; -use participant::{LocalParticipant, ParticipantLocation, RemoteParticipant}; use project::Project; use std::sync::Arc; use util::ResultExt; @@ -81,7 +77,7 @@ impl Room { } pub fn join( - call: &Call, + call: &IncomingCall, client: Arc, user_store: ModelHandle, cx: &mut MutableAppContext, diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 16f91f0680..9c5b8e35c9 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -1,9 +1,9 @@ #[cfg(any(test, feature = "test-support"))] pub mod test; -pub mod call; pub mod channel; pub mod http; +pub mod incoming_call; pub mod user; use anyhow::{anyhow, Context, Result}; diff --git a/crates/client/src/call.rs b/crates/client/src/incoming_call.rs similarity index 84% rename from crates/client/src/call.rs rename to crates/client/src/incoming_call.rs index 9b7ce06df4..75d8411ec3 100644 --- a/crates/client/src/call.rs +++ b/crates/client/src/incoming_call.rs @@ -2,7 +2,7 @@ use crate::User; use std::sync::Arc; #[derive(Clone)] -pub struct Call { +pub struct IncomingCall { pub room_id: u64, pub caller: Arc, pub participants: Vec>, diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index d285cb09db..ff5f03d5ef 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -1,5 +1,5 @@ use super::{http::HttpClient, proto, Client, Status, TypedEnvelope}; -use crate::call::Call; +use crate::incoming_call::IncomingCall; use anyhow::{anyhow, Context, Result}; use collections::{hash_map::Entry, BTreeSet, HashMap, HashSet}; use futures::{channel::mpsc, future, AsyncReadExt, Future, StreamExt}; @@ -67,7 +67,10 @@ pub struct UserStore { outgoing_contact_requests: Vec>, pending_contact_requests: HashMap, invite_info: Option, - incoming_call: (watch::Sender>, watch::Receiver>), + incoming_call: ( + watch::Sender>, + watch::Receiver>, + ), client: Weak, http: Arc, _maintain_contacts: Task<()>, @@ -205,7 +208,7 @@ impl UserStore { _: Arc, mut cx: AsyncAppContext, ) -> Result { - let call = Call { + let call = IncomingCall { room_id: envelope.payload.room_id, participants: this .update(&mut cx, |this, cx| { @@ -241,7 +244,7 @@ impl UserStore { self.invite_info.as_ref() } - pub fn incoming_call(&self) -> watch::Receiver> { + pub fn incoming_call(&self) -> watch::Receiver> { self.incoming_call.1.clone() } diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index 8603f67557..88c3318416 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -55,13 +55,13 @@ features = ["runtime-tokio-rustls", "postgres", "time", "uuid"] [dev-dependencies] collections = { path = "../collections", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } +call = { path = "../call", features = ["test-support"] } client = { path = "../client", features = ["test-support"] } editor = { path = "../editor", features = ["test-support"] } language = { path = "../language", features = ["test-support"] } log = { version = "0.4.16", features = ["kv_unstable_serde"] } lsp = { path = "../lsp", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } -room = { path = "../room", features = ["test-support"] } rpc = { path = "../rpc", features = ["test-support"] } settings = { path = "../settings", features = ["test-support"] } theme = { path = "../theme" } diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index bd60893a57..d16bff2f37 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -5,6 +5,7 @@ use crate::{ }; use ::rpc::Peer; use anyhow::anyhow; +use call::Room; use client::{ self, proto, test::FakeHttpClient, Channel, ChannelDetails, ChannelList, Client, Connection, Credentials, EstablishConnectionError, ProjectMetadata, UserStore, RECEIVE_TIMEOUT, @@ -34,7 +35,6 @@ use project::{ DiagnosticSummary, Project, ProjectPath, ProjectStore, WorktreeId, }; use rand::prelude::*; -use room::Room; use rpc::PeerId; use serde_json::json; use settings::{Formatter, Settings}; diff --git a/crates/collab_ui/Cargo.toml b/crates/collab_ui/Cargo.toml index aa2cdb8628..cf3a78a0b5 100644 --- a/crates/collab_ui/Cargo.toml +++ b/crates/collab_ui/Cargo.toml @@ -9,18 +9,19 @@ doctest = false [features] test-support = [ + "call/test-support", "client/test-support", "collections/test-support", "editor/test-support", "gpui/test-support", "project/test-support", - "room/test-support", "settings/test-support", "util/test-support", "workspace/test-support", ] [dependencies] +call = { path = "../call" } client = { path = "../client" } clock = { path = "../clock" } collections = { path = "../collections" } @@ -29,7 +30,6 @@ fuzzy = { path = "../fuzzy" } gpui = { path = "../gpui" } menu = { path = "../menu" } project = { path = "../project" } -room = { path = "../room" } settings = { path = "../settings" } theme = { path = "../theme" } util = { path = "../util" } @@ -41,12 +41,12 @@ postage = { version = "0.4.1", features = ["futures-traits"] } serde = { version = "1.0", features = ["derive", "rc"] } [dev-dependencies] +call = { path = "../call", features = ["test-support"] } client = { path = "../client", features = ["test-support"] } collections = { path = "../collections", features = ["test-support"] } editor = { path = "../editor", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } -room = { path = "../room", features = ["test-support"] } settings = { path = "../settings", features = ["test-support"] } util = { path = "../util", features = ["test-support"] } workspace = { path = "../workspace", features = ["test-support"] } diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index 15a987d6c2..b728a92198 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use call::ActiveCall; use client::{Client, Contact, User, UserStore}; use editor::{Cancel, Editor}; use fuzzy::{match_strings, StringMatchCandidate}; @@ -9,7 +10,6 @@ use gpui::{ ViewHandle, }; use menu::{Confirm, SelectNext, SelectPrev}; -use room::ActiveCall; use settings::Settings; use theme::IconButton; diff --git a/crates/collab_ui/src/incoming_call_notification.rs b/crates/collab_ui/src/incoming_call_notification.rs index ec959f01ea..d1ea216195 100644 --- a/crates/collab_ui/src/incoming_call_notification.rs +++ b/crates/collab_ui/src/incoming_call_notification.rs @@ -1,6 +1,7 @@ use std::sync::Arc; -use client::{call::Call, Client, UserStore}; +use call::ActiveCall; +use client::{incoming_call::IncomingCall, Client, UserStore}; use futures::StreamExt; use gpui::{ elements::*, @@ -8,7 +9,6 @@ use gpui::{ impl_internal_actions, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, View, ViewContext, WindowBounds, WindowKind, WindowOptions, }; -use room::ActiveCall; use settings::Settings; use util::ResultExt; @@ -55,13 +55,17 @@ struct RespondToCall { } pub struct IncomingCallNotification { - call: Call, + call: IncomingCall, client: Arc, user_store: ModelHandle, } impl IncomingCallNotification { - pub fn new(call: Call, client: Arc, user_store: ModelHandle) -> Self { + pub fn new( + call: IncomingCall, + client: Arc, + user_store: ModelHandle, + ) -> Self { Self { call, client, From 1898e813f59984c755948dc1b34068b2478f15ec Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 29 Sep 2022 17:39:53 +0200 Subject: [PATCH 042/314] Encapsulate `Room` interaction within `ActiveCall` Co-Authored-By: Nathan Sobo --- Cargo.lock | 1 + crates/call/src/call.rs | 77 +++++++++---------- crates/collab_ui/src/collab_titlebar_item.rs | 3 +- crates/collab_ui/src/collab_ui.rs | 7 +- crates/collab_ui/src/contacts_popover.rs | 26 ++----- .../src/incoming_call_notification.rs | 31 ++------ crates/zed/Cargo.toml | 7 +- crates/zed/src/main.rs | 2 +- crates/zed/src/zed.rs | 1 + 9 files changed, 63 insertions(+), 92 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b3fe2f899b..0211f4d1e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7154,6 +7154,7 @@ dependencies = [ "auto_update", "backtrace", "breadcrumbs", + "call", "chat_panel", "chrono", "cli", diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 11dde75697..0fcf5d7698 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -6,11 +6,16 @@ use client::{incoming_call::IncomingCall, Client, UserStore}; use gpui::{Entity, ModelContext, ModelHandle, MutableAppContext, Task}; pub use room::Room; use std::sync::Arc; -use util::ResultExt; -#[derive(Default)] +pub fn init(client: Arc, user_store: ModelHandle, cx: &mut MutableAppContext) { + let active_call = cx.add_model(|_| ActiveCall::new(client, user_store)); + cx.set_global(active_call); +} + pub struct ActiveCall { room: Option>, + client: Arc, + user_store: ModelHandle, } impl Entity for ActiveCall { @@ -18,68 +23,62 @@ impl Entity for ActiveCall { } impl ActiveCall { - pub fn global(cx: &mut MutableAppContext) -> ModelHandle { - if cx.has_global::>() { - cx.global::>().clone() - } else { - let active_call = cx.add_model(|_| ActiveCall::default()); - cx.set_global(active_call.clone()); - active_call + fn new(client: Arc, user_store: ModelHandle) -> Self { + Self { + room: None, + client, + user_store, } } - pub fn get_or_create( + pub fn global(cx: &mut MutableAppContext) -> ModelHandle { + cx.global::>().clone() + } + + pub fn invite( &mut self, - client: &Arc, - user_store: &ModelHandle, + recipient_user_id: u64, cx: &mut ModelContext, - ) -> Task>> { - if let Some(room) = self.room.clone() { - Task::ready(Ok(room)) - } else { - let client = client.clone(); - let user_store = user_store.clone(); - cx.spawn(|this, mut cx| async move { + ) -> Task> { + let room = self.room.clone(); + + let client = self.client.clone(); + let user_store = self.user_store.clone(); + cx.spawn(|this, mut cx| async move { + let room = if let Some(room) = room { + room + } else { let room = cx.update(|cx| Room::create(client, user_store, cx)).await?; this.update(&mut cx, |this, cx| { this.room = Some(room.clone()); cx.notify(); }); - Ok(room) - }) - } + room + }; + room.update(&mut cx, |room, cx| room.call(recipient_user_id, cx)) + .await?; + + Ok(()) + }) } - pub fn join( - &mut self, - call: &IncomingCall, - client: &Arc, - user_store: &ModelHandle, - cx: &mut ModelContext, - ) -> Task>> { + pub fn join(&mut self, call: &IncomingCall, cx: &mut ModelContext) -> Task> { if self.room.is_some() { return Task::ready(Err(anyhow!("cannot join while on another call"))); } - let join = Room::join(call, client.clone(), user_store.clone(), cx); + let join = Room::join(call, self.client.clone(), self.user_store.clone(), cx); cx.spawn(|this, mut cx| async move { let room = join.await?; this.update(&mut cx, |this, cx| { - this.room = Some(room.clone()); + this.room = Some(room); cx.notify(); }); - Ok(room) + Ok(()) }) } pub fn room(&self) -> Option<&ModelHandle> { self.room.as_ref() } - - pub fn clear(&mut self, cx: &mut ModelContext) { - if let Some(room) = self.room.take() { - room.update(cx, |room, cx| room.leave(cx)).log_err(); - cx.notify(); - } - } } diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 00f37fddee..770b9f29e6 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -69,9 +69,8 @@ impl CollabTitlebarItem { Some(_) => {} None => { if let Some(workspace) = self.workspace.upgrade(cx) { - let client = workspace.read(cx).client().clone(); let user_store = workspace.read(cx).user_store().clone(); - let view = cx.add_view(|cx| ContactsPopover::new(client, user_store, cx)); + let view = cx.add_view(|cx| ContactsPopover::new(user_store, cx)); cx.focus(&view); cx.subscribe(&view, |this, _, event, cx| { match event { diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index b101bb991d..4bb0860704 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -2,13 +2,12 @@ mod collab_titlebar_item; mod contacts_popover; mod incoming_call_notification; -use client::{Client, UserStore}; +use client::UserStore; pub use collab_titlebar_item::CollabTitlebarItem; use gpui::{ModelHandle, MutableAppContext}; -use std::sync::Arc; -pub fn init(client: Arc, user_store: ModelHandle, cx: &mut MutableAppContext) { +pub fn init(user_store: ModelHandle, cx: &mut MutableAppContext) { contacts_popover::init(cx); collab_titlebar_item::init(cx); - incoming_call_notification::init(client, user_store, cx); + incoming_call_notification::init(user_store, cx); } diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index b728a92198..aff159127f 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use call::ActiveCall; -use client::{Client, Contact, User, UserStore}; +use client::{Contact, User, UserStore}; use editor::{Cancel, Editor}; use fuzzy::{match_strings, StringMatchCandidate}; use gpui::{ @@ -84,7 +84,6 @@ pub struct ContactsPopover { entries: Vec, match_candidates: Vec, list_state: ListState, - client: Arc, user_store: ModelHandle, filter_editor: ViewHandle, collapsed_sections: Vec
, @@ -93,11 +92,7 @@ pub struct ContactsPopover { } impl ContactsPopover { - pub fn new( - client: Arc, - user_store: ModelHandle, - cx: &mut ViewContext, - ) -> Self { + pub fn new(user_store: ModelHandle, cx: &mut ViewContext) -> Self { let filter_editor = cx.add_view(|cx| { let mut editor = Editor::single_line( Some(|theme| theme.contacts_panel.user_query_editor.clone()), @@ -182,7 +177,6 @@ impl ContactsPopover { match_candidates: Default::default(), filter_editor, _subscriptions: subscriptions, - client, user_store, }; this.update_entries(cx); @@ -633,17 +627,11 @@ impl ContactsPopover { } fn call(&mut self, action: &Call, cx: &mut ViewContext) { - let recipient_user_id = action.recipient_user_id; - let room = ActiveCall::global(cx).update(cx, |active_call, cx| { - active_call.get_or_create(&self.client, &self.user_store, cx) - }); - cx.spawn_weak(|_, mut cx| async move { - let room = room.await?; - room.update(&mut cx, |room, cx| room.call(recipient_user_id, cx)) - .await?; - anyhow::Ok(()) - }) - .detach(); + ActiveCall::global(cx) + .update(cx, |active_call, cx| { + active_call.invite(action.recipient_user_id, cx) + }) + .detach_and_log_err(cx); } } diff --git a/crates/collab_ui/src/incoming_call_notification.rs b/crates/collab_ui/src/incoming_call_notification.rs index d1ea216195..a239acc7e6 100644 --- a/crates/collab_ui/src/incoming_call_notification.rs +++ b/crates/collab_ui/src/incoming_call_notification.rs @@ -1,7 +1,5 @@ -use std::sync::Arc; - use call::ActiveCall; -use client::{incoming_call::IncomingCall, Client, UserStore}; +use client::{incoming_call::IncomingCall, UserStore}; use futures::StreamExt; use gpui::{ elements::*, @@ -14,7 +12,7 @@ use util::ResultExt; impl_internal_actions!(incoming_call_notification, [RespondToCall]); -pub fn init(client: Arc, user_store: ModelHandle, cx: &mut MutableAppContext) { +pub fn init(user_store: ModelHandle, cx: &mut MutableAppContext) { cx.add_action(IncomingCallNotification::respond_to_call); let mut incoming_call = user_store.read(cx).incoming_call(); @@ -34,13 +32,7 @@ pub fn init(client: Arc, user_store: ModelHandle, cx: &mut Mu kind: WindowKind::PopUp, is_movable: false, }, - |_| { - IncomingCallNotification::new( - incoming_call, - client.clone(), - user_store.clone(), - ) - }, + |_| IncomingCallNotification::new(incoming_call, user_store.clone()), ); notification_window = Some(window_id); } @@ -56,29 +48,18 @@ struct RespondToCall { pub struct IncomingCallNotification { call: IncomingCall, - client: Arc, user_store: ModelHandle, } impl IncomingCallNotification { - pub fn new( - call: IncomingCall, - client: Arc, - user_store: ModelHandle, - ) -> Self { - Self { - call, - client, - user_store, - } + pub fn new(call: IncomingCall, user_store: ModelHandle) -> Self { + Self { call, user_store } } fn respond_to_call(&mut self, action: &RespondToCall, cx: &mut ViewContext) { if action.accept { ActiveCall::global(cx) - .update(cx, |active_call, cx| { - active_call.join(&self.call, &self.client, &self.user_store, cx) - }) + .update(cx, |active_call, cx| active_call.join(&self.call, cx)) .detach_and_log_err(cx); } else { self.user_store diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 667e3d7984..f2eb765353 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -19,6 +19,7 @@ activity_indicator = { path = "../activity_indicator" } assets = { path = "../assets" } auto_update = { path = "../auto_update" } breadcrumbs = { path = "../breadcrumbs" } +call = { path = "../call" } chat_panel = { path = "../chat_panel" } cli = { path = "../cli" } collab_ui = { path = "../collab_ui" } @@ -103,17 +104,19 @@ tree-sitter-typescript = "0.20.1" url = "2.2" [dev-dependencies] -text = { path = "../text", features = ["test-support"] } +call = { path = "../call", features = ["test-support"] } +client = { path = "../client", features = ["test-support"] } editor = { path = "../editor", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } language = { path = "../language", features = ["test-support"] } lsp = { path = "../lsp", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } rpc = { path = "../rpc", features = ["test-support"] } -client = { path = "../client", features = ["test-support"] } settings = { path = "../settings", features = ["test-support"] } +text = { path = "../text", features = ["test-support"] } util = { path = "../util", features = ["test-support"] } workspace = { path = "../workspace", features = ["test-support"] } + env_logger = "0.9" serde_json = { version = "1.0", features = ["preserve_order"] } unindent = "0.1.7" diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 29ad1c5eb0..de769a6e5e 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -107,7 +107,7 @@ fn main() { project::Project::init(&client); client::Channel::init(&client); client::init(client.clone(), cx); - collab_ui::init(client.clone(), user_store.clone(), cx); + collab_ui::init(user_store.clone(), cx); command_palette::init(cx); editor::init(cx); go_to_line::init(cx); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index a4cc8da633..fcb6f8f74e 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -217,6 +217,7 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { ); activity_indicator::init(cx); + call::init(app_state.client.clone(), app_state.user_store.clone(), cx); settings::KeymapFileContent::load_defaults(cx); } From 2a14af4cdea11693943377964e613ed2b61d664c Mon Sep 17 00:00:00 2001 From: ForLoveOfCats Date: Tue, 30 Aug 2022 11:08:22 -0400 Subject: [PATCH 043/314] Load a file's head text on file load just to get started --- Cargo.lock | 44 ++++++++++++++++++++++++++++++++++ crates/language/src/buffer.rs | 10 ++++++-- crates/project/Cargo.toml | 1 + crates/project/src/fs.rs | 39 ++++++++++++++++++++++++++++++ crates/project/src/worktree.rs | 14 ++++++++--- crates/rpc/proto/zed.proto | 3 ++- 6 files changed, 105 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b32b6a47a2..31f5f30f38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2224,6 +2224,21 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "git2" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2994bee4a3a6a51eb90c218523be382fd7ea09b16380b9312e9dbe955ff7c7d1" +dependencies = [ + "bitflags", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + [[package]] name = "glob" version = "0.3.0" @@ -2894,6 +2909,20 @@ version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +[[package]] +name = "libgit2-sys" +version = "0.14.0+1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47a00859c70c8a4f7218e6d1cc32875c4b55f6799445b842b0d8ed5e4c3d959b" +dependencies = [ + "cc", + "libc", + "libssh2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", +] + [[package]] name = "libloading" version = "0.7.3" @@ -2934,6 +2963,20 @@ dependencies = [ "zstd-sys", ] +[[package]] +name = "libssh2-sys" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b094a36eb4b8b8c8a7b4b8ae43b2944502be3e59cd87687595cf6b0a71b3f4ca" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] + [[package]] name = "libz-sys" version = "1.1.8" @@ -3970,6 +4013,7 @@ dependencies = [ "fsevent", "futures", "fuzzy", + "git2", "gpui", "ignore", "language", diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 08843aacfe..ca86f9c172 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -47,6 +47,7 @@ pub use lsp::DiagnosticSeverity; pub struct Buffer { text: TextBuffer, + head_text: Option, file: Option>, saved_version: clock::Global, saved_version_fingerprint: String, @@ -328,17 +329,20 @@ impl Buffer { Self::build( TextBuffer::new(replica_id, cx.model_id() as u64, base_text.into()), None, + None, ) } pub fn from_file>( replica_id: ReplicaId, base_text: T, + head_text: Option, file: Arc, cx: &mut ModelContext, ) -> Self { Self::build( TextBuffer::new(replica_id, cx.model_id() as u64, base_text.into()), + head_text.map(|h| h.into()), Some(file), ) } @@ -349,7 +353,7 @@ impl Buffer { file: Option>, ) -> Result { let buffer = TextBuffer::new(replica_id, message.id, message.base_text); - let mut this = Self::build(buffer, file); + let mut this = Self::build(buffer, message.head_text, file); this.text.set_line_ending(proto::deserialize_line_ending( proto::LineEnding::from_i32(message.line_ending) .ok_or_else(|| anyhow!("missing line_ending"))?, @@ -362,6 +366,7 @@ impl Buffer { id: self.remote_id(), file: self.file.as_ref().map(|f| f.to_proto()), base_text: self.base_text().to_string(), + head_text: self.head_text.clone(), line_ending: proto::serialize_line_ending(self.line_ending()) as i32, } } @@ -404,7 +409,7 @@ impl Buffer { self } - fn build(buffer: TextBuffer, file: Option>) -> Self { + fn build(buffer: TextBuffer, head_text: Option, file: Option>) -> Self { let saved_mtime = if let Some(file) = file.as_ref() { file.mtime() } else { @@ -418,6 +423,7 @@ impl Buffer { transaction_depth: 0, was_dirty_before_starting_transaction: None, text: buffer, + head_text, file, syntax_map: Mutex::new(SyntaxMap::new()), parsing_in_background: false, diff --git a/crates/project/Cargo.toml b/crates/project/Cargo.toml index a4ea6f2286..4e7ff2d471 100644 --- a/crates/project/Cargo.toml +++ b/crates/project/Cargo.toml @@ -52,6 +52,7 @@ smol = "1.2.5" thiserror = "1.0.29" toml = "0.5" rocksdb = "0.18" +git2 = "0.15" [dev-dependencies] client = { path = "../client", features = ["test-support"] } diff --git a/crates/project/src/fs.rs b/crates/project/src/fs.rs index f2d62fae87..68d07c891c 100644 --- a/crates/project/src/fs.rs +++ b/crates/project/src/fs.rs @@ -1,9 +1,11 @@ use anyhow::{anyhow, Result}; use fsevent::EventStream; use futures::{future::BoxFuture, Stream, StreamExt}; +use git2::{Repository, RepositoryOpenFlags}; use language::LineEnding; use smol::io::{AsyncReadExt, AsyncWriteExt}; use std::{ + ffi::OsStr, io, os::unix::fs::MetadataExt, path::{Component, Path, PathBuf}, @@ -29,6 +31,7 @@ pub trait Fs: Send + Sync { async fn remove_file(&self, path: &Path, options: RemoveOptions) -> Result<()>; async fn open_sync(&self, path: &Path) -> Result>; async fn load(&self, path: &Path) -> Result; + async fn load_head_text(&self, path: &Path) -> Option; async fn save(&self, path: &Path, text: &Rope, line_ending: LineEnding) -> Result<()>; async fn canonicalize(&self, path: &Path) -> Result; async fn is_file(&self, path: &Path) -> bool; @@ -161,6 +164,38 @@ impl Fs for RealFs { Ok(text) } + async fn load_head_text(&self, path: &Path) -> Option { + fn logic(path: &Path) -> Result> { + let repo = Repository::open_ext(path, RepositoryOpenFlags::empty(), &[OsStr::new("")])?; + assert!(repo.path().ends_with(".git")); + let repo_root_path = match repo.path().parent() { + Some(root) => root, + None => return Ok(None), + }; + + let relative_path = path.strip_prefix(repo_root_path)?; + let object = repo + .head()? + .peel_to_tree()? + .get_path(relative_path)? + .to_object(&repo)?; + + let content = match object.as_blob() { + Some(blob) => blob.content().to_owned(), + None => return Ok(None), + }; + + let head_text = String::from_utf8(content.to_owned())?; + Ok(Some(head_text)) + } + + match logic(path) { + Ok(value) => return value, + Err(err) => log::error!("Error loading head text: {:?}", err), + } + None + } + async fn save(&self, path: &Path, text: &Rope, line_ending: LineEnding) -> Result<()> { let buffer_size = text.summary().len.min(10 * 1024); let file = smol::fs::File::create(path).await?; @@ -748,6 +783,10 @@ impl Fs for FakeFs { entry.file_content(&path).cloned() } + async fn load_head_text(&self, _: &Path) -> Option { + None + } + async fn save(&self, path: &Path, text: &Rope, line_ending: LineEnding) -> Result<()> { self.simulate_random_delay().await; let path = normalize_path(path); diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 74c50e0c5f..42d18eb3bb 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -446,10 +446,10 @@ impl LocalWorktree { ) -> Task>> { let path = Arc::from(path); cx.spawn(move |this, mut cx| async move { - let (file, contents) = this + let (file, contents, head_text) = this .update(&mut cx, |t, cx| t.as_local().unwrap().load(&path, cx)) .await?; - Ok(cx.add_model(|cx| Buffer::from_file(0, contents, Arc::new(file), cx))) + Ok(cx.add_model(|cx| Buffer::from_file(0, contents, head_text, Arc::new(file), cx))) }) } @@ -558,13 +558,19 @@ impl LocalWorktree { } } - fn load(&self, path: &Path, cx: &mut ModelContext) -> Task> { + fn load( + &self, + path: &Path, + cx: &mut ModelContext, + ) -> Task)>> { let handle = cx.handle(); let path = Arc::from(path); let abs_path = self.absolutize(&path); let fs = self.fs.clone(); cx.spawn(|this, mut cx| async move { let text = fs.load(&abs_path).await?; + let head_text = fs.load_head_text(&abs_path).await; + // Eagerly populate the snapshot with an updated entry for the loaded file let entry = this .update(&mut cx, |this, cx| { @@ -573,6 +579,7 @@ impl LocalWorktree { .refresh_entry(path, abs_path, None, cx) }) .await?; + Ok(( File { entry_id: Some(entry.id), @@ -582,6 +589,7 @@ impl LocalWorktree { is_local: true, }, text, + head_text, )) }) } diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 7840829b44..818f2cb7e1 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -821,7 +821,8 @@ message BufferState { uint64 id = 1; optional File file = 2; string base_text = 3; - LineEnding line_ending = 4; + optional string head_text = 4; + LineEnding line_ending = 5; } message BufferChunk { From 6fa2e62fa41175668fb62479063117682c9ecde9 Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 30 Aug 2022 16:29:20 -0400 Subject: [PATCH 044/314] Start asking Editors to update git after a debounced delay --- crates/editor/src/items.rs | 9 ++ crates/workspace/src/workspace.rs | 132 ++++++++++++++++++++++++------ 2 files changed, 115 insertions(+), 26 deletions(-) diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index f63ffc3d7c..22a069c5c0 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -478,6 +478,15 @@ impl Item for Editor { }) } + fn update_git( + &mut self, + _project: ModelHandle, + _cx: &mut ViewContext, + ) -> Task> { + println!("Editor::update_git"); + Task::ready(Ok(())) + } + fn to_item_events(event: &Self::Event) -> Vec { let mut result = Vec::new(); match event { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index b9cface656..3446dc0f0e 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -52,7 +52,6 @@ use std::{ cell::RefCell, fmt, future::Future, - mem, ops::Range, path::{Path, PathBuf}, rc::Rc, @@ -318,7 +317,23 @@ pub trait Item: View { project: ModelHandle, cx: &mut ViewContext, ) -> Task>; + fn update_git( + &mut self, + _project: ModelHandle, + _cx: &mut ViewContext, + ) -> Task> { + Task::ready(Ok(())) + } fn to_item_events(event: &Self::Event) -> Vec; + fn should_close_item_on_event(_: &Self::Event) -> bool { + false + } + fn should_update_tab_on_event(_: &Self::Event) -> bool { + false + } + fn is_edit_event(_: &Self::Event) -> bool { + false + } fn act_as_type( &self, type_id: TypeId, @@ -435,6 +450,57 @@ impl FollowableItemHandle for ViewHandle { } } +struct DelayedDebouncedEditAction { + task: Option>, + cancel_channel: Option>, +} + +impl DelayedDebouncedEditAction { + fn new() -> DelayedDebouncedEditAction { + DelayedDebouncedEditAction { + task: None, + cancel_channel: None, + } + } + + fn fire_new( + &mut self, + delay: Duration, + workspace: &Workspace, + cx: &mut ViewContext, + f: F, + ) where + F: FnOnce(ModelHandle, AsyncAppContext) -> Fut + 'static, + Fut: 'static + Future, + { + if let Some(channel) = self.cancel_channel.take() { + _ = channel.send(()); + } + + let project = workspace.project().downgrade(); + + let (sender, mut receiver) = oneshot::channel::<()>(); + self.cancel_channel = Some(sender); + + let previous_task = self.task.take(); + self.task = Some(cx.spawn_weak(|_, cx| async move { + let mut timer = cx.background().timer(delay).fuse(); + if let Some(previous_task) = previous_task { + previous_task.await; + } + + futures::select_biased! { + _ = receiver => return, + _ = timer => {} + } + + if let Some(project) = project.upgrade(&cx) { + (f)(project, cx).await; + } + })); + } +} + pub trait ItemHandle: 'static + fmt::Debug { fn subscribe_to_item_events( &self, @@ -473,6 +539,11 @@ pub trait ItemHandle: 'static + fmt::Debug { ) -> Task>; fn reload(&self, project: ModelHandle, cx: &mut MutableAppContext) -> Task>; + fn update_git( + &self, + project: ModelHandle, + cx: &mut MutableAppContext, + ) -> Task>; fn act_as_type(&self, type_id: TypeId, cx: &AppContext) -> Option; fn to_followable_item_handle(&self, cx: &AppContext) -> Option>; fn on_release( @@ -578,8 +649,8 @@ impl ItemHandle for ViewHandle { .insert(self.id(), pane.downgrade()) .is_none() { - let mut pending_autosave = None; - let mut cancel_pending_autosave = oneshot::channel::<()>().0; + let mut pending_autosave = DelayedDebouncedEditAction::new(); + let mut pending_git_update = DelayedDebouncedEditAction::new(); let pending_update = Rc::new(RefCell::new(None)); let pending_update_scheduled = Rc::new(AtomicBool::new(false)); @@ -637,45 +708,46 @@ impl ItemHandle for ViewHandle { .detach_and_log_err(cx); return; } + ItemEvent::UpdateTab => { pane.update(cx, |_, cx| { cx.emit(pane::Event::ChangeItemTitle); cx.notify(); }); } + ItemEvent::Edit => { if let Autosave::AfterDelay { milliseconds } = cx.global::().autosave { - let prev_autosave = pending_autosave - .take() - .unwrap_or_else(|| Task::ready(Some(()))); - let (cancel_tx, mut cancel_rx) = oneshot::channel::<()>(); - let prev_cancel_tx = - mem::replace(&mut cancel_pending_autosave, cancel_tx); - let project = workspace.project.downgrade(); - let _ = prev_cancel_tx.send(()); + let delay = Duration::from_millis(milliseconds); let item = item.clone(); - pending_autosave = - Some(cx.spawn_weak(|_, mut cx| async move { - let mut timer = cx - .background() - .timer(Duration::from_millis(milliseconds)) - .fuse(); - prev_autosave.await; - futures::select_biased! { - _ = cancel_rx => return None, - _ = timer => {} - } - - let project = project.upgrade(&cx)?; + pending_autosave.fire_new( + delay, + workspace, + cx, + |project, mut cx| async move { cx.update(|cx| Pane::autosave_item(&item, project, cx)) .await .log_err(); - None - })); + }, + ); } + + const GIT_DELAY: Duration = Duration::from_millis(800); + let item = item.clone(); + pending_git_update.fire_new( + GIT_DELAY, + workspace, + cx, + |project, mut cx| async move { + cx.update(|cx| item.update_git(project, cx)) + .await + .log_err(); + }, + ); } + _ => {} } } @@ -755,6 +827,14 @@ impl ItemHandle for ViewHandle { self.update(cx, |item, cx| item.reload(project, cx)) } + fn update_git( + &self, + project: ModelHandle, + cx: &mut MutableAppContext, + ) -> Task> { + self.update(cx, |item, cx| item.update_git(project, cx)) + } + fn act_as_type(&self, type_id: TypeId, cx: &AppContext) -> Option { self.read(cx).act_as_type(type_id, self, cx) } From 55ca02351c5e8c662f7f9e09a8ce93a4f69233c5 Mon Sep 17 00:00:00 2001 From: Julia Date: Thu, 1 Sep 2022 17:22:12 -0400 Subject: [PATCH 045/314] Start painting some sort of hunk info, it's wrong but it's close Co-Authored-By: Max Brunsfeld --- Cargo.lock | 2 +- crates/editor/src/element.rs | 35 ++++++ crates/editor/src/multi_buffer.rs | 16 ++- crates/language/Cargo.toml | 1 + crates/language/src/buffer.rs | 35 ++++++ crates/language/src/git.rs | 197 ++++++++++++++++++++++++++++++ crates/language/src/language.rs | 1 + crates/project/Cargo.toml | 1 - crates/project/src/fs.rs | 2 +- crates/project/src/project.rs | 4 +- 10 files changed, 286 insertions(+), 8 deletions(-) create mode 100644 crates/language/src/git.rs diff --git a/Cargo.lock b/Cargo.lock index 31f5f30f38..2872d83a94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2855,6 +2855,7 @@ dependencies = [ "env_logger", "futures", "fuzzy", + "git2", "gpui", "lazy_static", "log", @@ -4013,7 +4014,6 @@ dependencies = [ "fsevent", "futures", "fuzzy", - "git2", "gpui", "ignore", "language", diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 1e1ab83063..357f15432b 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -34,6 +34,7 @@ use gpui::{ WeakViewHandle, }; use json::json; +use language::git::{DiffHunk, DiffHunkStatus}; use language::{Bias, DiagnosticSeverity, OffsetUtf16, Selection}; use project::ProjectPath; use settings::Settings; @@ -543,6 +544,33 @@ impl EditorElement { } } + println!("painting from hunks: {:#?}\n", &layout.diff_hunks); + for hunk in &layout.diff_hunks { + let color = match hunk.status() { + DiffHunkStatus::Added => Color::green(), + DiffHunkStatus::Modified => Color::blue(), + _ => continue, + }; + + let start_row = hunk.buffer_range.start; + let end_row = hunk.buffer_range.end; + + let start_y = start_row as f32 * layout.line_height - (scroll_top % layout.line_height); + let end_y = end_row as f32 * layout.line_height - (scroll_top % layout.line_height) + + layout.line_height; + + let highlight_origin = bounds.origin() + vec2f(0., start_y); + let highlight_size = vec2f(6., end_y - start_y); + let highlight_bounds = RectF::new(highlight_origin, highlight_size); + + cx.scene.push_quad(Quad { + bounds: highlight_bounds, + background: Some(color), + border: Border::new(0., Color::transparent_black()), + corner_radius: 0., + }); + } + if let Some((row, indicator)) = layout.code_actions_indicator.as_mut() { let mut x = bounds.width() - layout.gutter_padding; let mut y = *row as f32 * layout.position_map.line_height - scroll_top; @@ -1425,6 +1453,11 @@ impl Element for EditorElement { let line_number_layouts = self.layout_line_numbers(start_row..end_row, &active_rows, &snapshot, cx); + let diff_hunks = snapshot + .buffer_snapshot + .diff_hunks_in_range(start_row..end_row) + .collect(); + let mut max_visible_line_width = 0.0; let line_layouts = self.layout_lines(start_row..end_row, &snapshot, cx); for line in &line_layouts { @@ -1573,6 +1606,7 @@ impl Element for EditorElement { highlighted_rows, highlighted_ranges, line_number_layouts, + diff_hunks, blocks, selections, context_menu, @@ -1710,6 +1744,7 @@ pub struct LayoutState { highlighted_ranges: Vec<(Range, Color)>, selections: Vec<(ReplicaId, Vec)>, context_menu: Option<(DisplayPoint, ElementBox)>, + diff_hunks: Vec>, code_actions_indicator: Option<(u32, ElementBox)>, hover_popovers: Option<(DisplayPoint, Vec)>, } diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 4ee9526a67..91de32bac9 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -7,9 +7,10 @@ use collections::{BTreeMap, Bound, HashMap, HashSet}; use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task}; pub use language::Completion; use language::{ - char_kind, AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, - DiagnosticEntry, Event, File, IndentSize, Language, OffsetRangeExt, Outline, OutlineItem, - Selection, ToOffset as _, ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, + char_kind, git::DiffHunk, AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, + Chunk, DiagnosticEntry, Event, File, IndentSize, Language, OffsetRangeExt, Outline, + OutlineItem, Selection, ToOffset as _, ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, + TransactionId, }; use smallvec::SmallVec; use std::{ @@ -2529,6 +2530,15 @@ impl MultiBufferSnapshot { }) } + pub fn diff_hunks_in_range<'a>( + &'a self, + row_range: Range, + ) -> impl 'a + Iterator> { + self.as_singleton() + .into_iter() + .flat_map(move |(_, _, buffer)| buffer.diff_hunks_in_range(row_range.clone())) + } + pub fn range_for_syntax_ancestor(&self, range: Range) -> Option> { let range = range.start.to_offset(self)..range.end.to_offset(self); diff --git a/crates/language/Cargo.toml b/crates/language/Cargo.toml index 6e9f368e77..6d347f3595 100644 --- a/crates/language/Cargo.toml +++ b/crates/language/Cargo.toml @@ -51,6 +51,7 @@ smol = "1.2" tree-sitter = "0.20" tree-sitter-rust = { version = "*", optional = true } tree-sitter-typescript = { version = "*", optional = true } +git2 = "0.15" [dev-dependencies] client = { path = "../client", features = ["test-support"] } diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index ca86f9c172..ad3d8978ad 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1,3 +1,4 @@ +use crate::git::{BufferDiff, DiffHunk}; pub use crate::{ diagnostic_set::DiagnosticSet, highlight_map::{HighlightId, HighlightMap}, @@ -48,6 +49,7 @@ pub use lsp::DiagnosticSeverity; pub struct Buffer { text: TextBuffer, head_text: Option, + diff: BufferDiff, file: Option>, saved_version: clock::Global, saved_version_fingerprint: String, @@ -74,6 +76,7 @@ pub struct Buffer { pub struct BufferSnapshot { text: text::BufferSnapshot, + git_hunks: Arc<[DiffHunk]>, pub(crate) syntax: SyntaxSnapshot, file: Option>, diagnostics: DiagnosticSet, @@ -416,6 +419,11 @@ impl Buffer { UNIX_EPOCH }; + let mut diff = BufferDiff::new(); + if let Some(head_text) = &head_text { + diff.update(head_text, &buffer); + } + Self { saved_mtime, saved_version: buffer.version(), @@ -424,6 +432,7 @@ impl Buffer { was_dirty_before_starting_transaction: None, text: buffer, head_text, + diff, file, syntax_map: Mutex::new(SyntaxMap::new()), parsing_in_background: false, @@ -453,6 +462,7 @@ impl Buffer { BufferSnapshot { text, syntax, + git_hunks: self.diff.hunks(), file: self.file.clone(), remote_selections: self.remote_selections.clone(), diagnostics: self.diagnostics.clone(), @@ -2145,6 +2155,30 @@ impl BufferSnapshot { }) } + pub fn diff_hunks_in_range<'a>( + &'a self, + query_row_range: Range, + ) -> impl 'a + Iterator> { + self.git_hunks.iter().filter_map(move |hunk| { + let range = hunk.buffer_range.to_point(&self.text); + + if range.start.row < query_row_range.end && query_row_range.start < range.end.row { + let end_row = if range.end.column > 0 { + range.end.row + 1 + } else { + range.end.row + }; + + Some(DiffHunk { + buffer_range: range.start.row..end_row, + head_range: hunk.head_range.clone(), + }) + } else { + None + } + }) + } + pub fn diagnostics_in_range<'a, T, O>( &'a self, search_range: Range, @@ -2218,6 +2252,7 @@ impl Clone for BufferSnapshot { fn clone(&self) -> Self { Self { text: self.text.clone(), + git_hunks: self.git_hunks.clone(), syntax: self.syntax.clone(), file: self.file.clone(), remote_selections: self.remote_selections.clone(), diff --git a/crates/language/src/git.rs b/crates/language/src/git.rs new file mode 100644 index 0000000000..5445396918 --- /dev/null +++ b/crates/language/src/git.rs @@ -0,0 +1,197 @@ +use std::ops::Range; +use std::sync::Arc; + +use sum_tree::Bias; +use text::{Anchor, Point}; + +pub use git2 as libgit; +use libgit::Patch as GitPatch; + +#[derive(Debug, Clone, Copy)] +pub enum DiffHunkStatus { + Added, + Modified, + Removed, +} + +#[derive(Debug)] +pub struct DiffHunk { + pub buffer_range: Range, + pub head_range: Range, +} + +impl DiffHunk { + pub fn status(&self) -> DiffHunkStatus { + if self.head_range.is_empty() { + DiffHunkStatus::Added + } else if self.buffer_range.is_empty() { + DiffHunkStatus::Removed + } else { + DiffHunkStatus::Modified + } + } +} + +pub struct BufferDiff { + hunks: Arc<[DiffHunk]>, +} + +impl BufferDiff { + pub fn new() -> BufferDiff { + BufferDiff { + hunks: Arc::new([]), + } + } + + pub fn hunks(&self) -> Arc<[DiffHunk]> { + self.hunks.clone() + } + + pub fn update(&mut self, head: &str, buffer: &text::BufferSnapshot) { + let current = buffer.as_rope().to_string().into_bytes(); + let patch = match GitPatch::from_buffers(head.as_bytes(), None, ¤t, None, None) { + Ok(patch) => patch, + Err(_) => { + //Reset hunks in case of failure to avoid showing a stale (potentially erroneous) diff + self.hunks = Arc::new([]); + return; + } + }; + + let mut hunks = Vec::new(); + for index in 0..patch.num_hunks() { + let (hunk, _) = match patch.hunk(index) { + Ok(it) => it, + Err(_) => break, + }; + + let new_start = hunk.new_start(); + let new_end = new_start + hunk.new_lines(); + let start_anchor = buffer.anchor_at(Point::new(new_start, 0), Bias::Left); + let end_anchor = buffer.anchor_at(Point::new(new_end, 0), Bias::Left); + let buffer_range = start_anchor..end_anchor; + + let old_start = hunk.old_start() as usize; + let old_end = old_start + hunk.old_lines() as usize; + let head_range = old_start..old_end; + + hunks.push(DiffHunk { + buffer_range, + head_range, + }); + } + + self.hunks = hunks.into(); + } +} + +#[derive(Debug, Clone, Copy)] +pub enum GitDiffEdit { + Added(u32), + Modified(u32), + Removed(u32), +} + +impl GitDiffEdit { + pub fn line(self) -> u32 { + use GitDiffEdit::*; + + match self { + Added(line) | Modified(line) | Removed(line) => line, + } + } +} + +// struct DiffTracker { +// track_line_num: u32, +// edits: Vec, +// } + +// impl DiffTracker { +// fn new() -> DiffTracker { +// DiffTracker { +// track_line_num: 0, +// edits: Vec::new(), +// } +// } + +// fn attempt_finalize_file(&mut self, base_path: &Path) -> Result<()> { +// let relative = if let Some(relative) = self.last_file_path.clone() { +// relative +// } else { +// return Ok(()); +// }; + +// let mut path = base_path.to_path_buf(); +// path.push(relative); +// path = canonicalize(path).map_err(Error::Io)?; + +// self.diffs.push(GitFileDiff { +// path, +// edits: take(&mut self.edits), +// }); + +// Ok(()) +// } + +// fn handle_diff_line( +// &mut self, +// delta: DiffDelta, +// line: DiffLine, +// base_path: &Path, +// ) -> Result<()> { +// let path = match (delta.old_file().path(), delta.new_file().path()) { +// (Some(old), _) => old, +// (_, Some(new)) => new, +// (_, _) => return Err(Error::DeltaMissingPath), +// }; + +// if self.last_file_path.as_deref() != Some(path) { +// self.attempt_finalize_file(base_path)?; +// self.last_file_path = Some(path.to_path_buf()); +// self.track_line_num = 0; +// } + +// match line.origin_value() { +// DiffLineType::Context => { +// self.track_line_num = line.new_lineno().ok_or(Error::ContextMissingLineNum)?; +// } + +// DiffLineType::Deletion => { +// self.track_line_num += 1; +// self.edits.push(GitDiffEdit::Removed(self.track_line_num)); +// } + +// DiffLineType::Addition => { +// let addition_line_num = line.new_lineno().ok_or(Error::AdditionMissingLineNum)?; +// self.track_line_num = addition_line_num; + +// let mut replaced = false; +// for rewind_index in (0..self.edits.len()).rev() { +// let edit = &mut self.edits[rewind_index]; + +// if let GitDiffEdit::Removed(removed_line_num) = *edit { +// match removed_line_num.cmp(&addition_line_num) { +// Ordering::Equal => { +// *edit = GitDiffEdit::Modified(addition_line_num); +// replaced = true; +// break; +// } + +// Ordering::Greater => continue, +// Ordering::Less => break, +// } +// } +// } + +// if !replaced { +// self.edits.push(GitDiffEdit::Added(addition_line_num)); +// } +// } + +// _ => {} +// } + +// Ok(()) +// } +// } diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 780f6e75b5..8e2fe601e7 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -1,5 +1,6 @@ mod buffer; mod diagnostic_set; +pub mod git; mod highlight_map; mod outline; pub mod proto; diff --git a/crates/project/Cargo.toml b/crates/project/Cargo.toml index 4e7ff2d471..a4ea6f2286 100644 --- a/crates/project/Cargo.toml +++ b/crates/project/Cargo.toml @@ -52,7 +52,6 @@ smol = "1.2.5" thiserror = "1.0.29" toml = "0.5" rocksdb = "0.18" -git2 = "0.15" [dev-dependencies] client = { path = "../client", features = ["test-support"] } diff --git a/crates/project/src/fs.rs b/crates/project/src/fs.rs index 68d07c891c..a983df0f4b 100644 --- a/crates/project/src/fs.rs +++ b/crates/project/src/fs.rs @@ -1,7 +1,7 @@ use anyhow::{anyhow, Result}; use fsevent::EventStream; use futures::{future::BoxFuture, Stream, StreamExt}; -use git2::{Repository, RepositoryOpenFlags}; +use language::git::libgit::{Repository, RepositoryOpenFlags}; use language::LineEnding; use smol::io::{AsyncReadExt, AsyncWriteExt}; use std::{ diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 6841c561d0..8fa1fe9622 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4533,8 +4533,8 @@ impl Project { fn add_worktree(&mut self, worktree: &ModelHandle, cx: &mut ModelContext) { cx.observe(worktree, |_, _, cx| cx.notify()).detach(); if worktree.read(cx).is_local() { - cx.subscribe(worktree, |this, worktree, _, cx| { - this.update_local_worktree_buffers(worktree, cx); + cx.subscribe(worktree, |this, worktree, event, cx| match event { + worktree::Event::UpdatedEntries => this.update_local_worktree_buffers(worktree, cx), }) .detach(); } From 641daf0a6eddd3b852886e73ee7a450f3a40359c Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 2 Sep 2022 10:39:32 -0400 Subject: [PATCH 046/314] Correct git gutter indicator scroll position & add rounded corner --- crates/editor/src/element.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 357f15432b..c82860cb72 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -544,7 +544,7 @@ impl EditorElement { } } - println!("painting from hunks: {:#?}\n", &layout.diff_hunks); + println!("painting from hunks: {:#?}\n", layout.diff_hunks); for hunk in &layout.diff_hunks { let color = match hunk.status() { DiffHunkStatus::Added => Color::green(), @@ -555,19 +555,19 @@ impl EditorElement { let start_row = hunk.buffer_range.start; let end_row = hunk.buffer_range.end; - let start_y = start_row as f32 * layout.line_height - (scroll_top % layout.line_height); - let end_y = end_row as f32 * layout.line_height - (scroll_top % layout.line_height) - + layout.line_height; + let start_y = start_row as f32 * layout.line_height - scroll_top; + let end_y = end_row as f32 * layout.line_height + layout.line_height - scroll_top; - let highlight_origin = bounds.origin() + vec2f(0., start_y); - let highlight_size = vec2f(6., end_y - start_y); + let width = 0.22 * layout.line_height; + let highlight_origin = bounds.origin() + vec2f(-width, start_y); + let highlight_size = vec2f(width * 2., end_y - start_y); let highlight_bounds = RectF::new(highlight_origin, highlight_size); cx.scene.push_quad(Quad { bounds: highlight_bounds, background: Some(color), border: Border::new(0., Color::transparent_black()), - corner_radius: 0., + corner_radius: 0.2 * layout.line_height, }); } From fdda2abb782a36075c825f91f774bf97386726f7 Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 2 Sep 2022 14:35:35 -0400 Subject: [PATCH 047/314] Correct start/end of git diff hunks --- crates/editor/src/element.rs | 4 +- crates/language/src/git.rs | 108 +++-------------------------------- 2 files changed, 11 insertions(+), 101 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index c82860cb72..b13a2a6ada 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -549,14 +549,14 @@ impl EditorElement { let color = match hunk.status() { DiffHunkStatus::Added => Color::green(), DiffHunkStatus::Modified => Color::blue(), - _ => continue, + DiffHunkStatus::Removed => continue, }; let start_row = hunk.buffer_range.start; let end_row = hunk.buffer_range.end; let start_y = start_row as f32 * layout.line_height - scroll_top; - let end_y = end_row as f32 * layout.line_height + layout.line_height - scroll_top; + let end_y = end_row as f32 * layout.line_height - scroll_top; let width = 0.22 * layout.line_height; let highlight_origin = bounds.origin() + vec2f(-width, start_y); diff --git a/crates/language/src/git.rs b/crates/language/src/git.rs index 5445396918..73e511ca48 100644 --- a/crates/language/src/git.rs +++ b/crates/language/src/git.rs @@ -5,7 +5,7 @@ use sum_tree::Bias; use text::{Anchor, Point}; pub use git2 as libgit; -use libgit::Patch as GitPatch; +use libgit::{DiffOptions as GitOptions, Patch as GitPatch}; #[derive(Debug, Clone, Copy)] pub enum DiffHunkStatus { @@ -48,8 +48,12 @@ impl BufferDiff { } pub fn update(&mut self, head: &str, buffer: &text::BufferSnapshot) { + let head = head.as_bytes(); let current = buffer.as_rope().to_string().into_bytes(); - let patch = match GitPatch::from_buffers(head.as_bytes(), None, ¤t, None, None) { + + let mut options = GitOptions::default(); + options.context_lines(0); + let patch = match GitPatch::from_buffers(head, None, ¤t, None, Some(&mut options)) { Ok(patch) => patch, Err(_) => { //Reset hunks in case of failure to avoid showing a stale (potentially erroneous) diff @@ -62,16 +66,16 @@ impl BufferDiff { for index in 0..patch.num_hunks() { let (hunk, _) = match patch.hunk(index) { Ok(it) => it, - Err(_) => break, + Err(_) => continue, }; - let new_start = hunk.new_start(); + let new_start = hunk.new_start() - 1; let new_end = new_start + hunk.new_lines(); let start_anchor = buffer.anchor_at(Point::new(new_start, 0), Bias::Left); let end_anchor = buffer.anchor_at(Point::new(new_end, 0), Bias::Left); let buffer_range = start_anchor..end_anchor; - let old_start = hunk.old_start() as usize; + let old_start = hunk.old_start() as usize - 1; let old_end = old_start + hunk.old_lines() as usize; let head_range = old_start..old_end; @@ -101,97 +105,3 @@ impl GitDiffEdit { } } } - -// struct DiffTracker { -// track_line_num: u32, -// edits: Vec, -// } - -// impl DiffTracker { -// fn new() -> DiffTracker { -// DiffTracker { -// track_line_num: 0, -// edits: Vec::new(), -// } -// } - -// fn attempt_finalize_file(&mut self, base_path: &Path) -> Result<()> { -// let relative = if let Some(relative) = self.last_file_path.clone() { -// relative -// } else { -// return Ok(()); -// }; - -// let mut path = base_path.to_path_buf(); -// path.push(relative); -// path = canonicalize(path).map_err(Error::Io)?; - -// self.diffs.push(GitFileDiff { -// path, -// edits: take(&mut self.edits), -// }); - -// Ok(()) -// } - -// fn handle_diff_line( -// &mut self, -// delta: DiffDelta, -// line: DiffLine, -// base_path: &Path, -// ) -> Result<()> { -// let path = match (delta.old_file().path(), delta.new_file().path()) { -// (Some(old), _) => old, -// (_, Some(new)) => new, -// (_, _) => return Err(Error::DeltaMissingPath), -// }; - -// if self.last_file_path.as_deref() != Some(path) { -// self.attempt_finalize_file(base_path)?; -// self.last_file_path = Some(path.to_path_buf()); -// self.track_line_num = 0; -// } - -// match line.origin_value() { -// DiffLineType::Context => { -// self.track_line_num = line.new_lineno().ok_or(Error::ContextMissingLineNum)?; -// } - -// DiffLineType::Deletion => { -// self.track_line_num += 1; -// self.edits.push(GitDiffEdit::Removed(self.track_line_num)); -// } - -// DiffLineType::Addition => { -// let addition_line_num = line.new_lineno().ok_or(Error::AdditionMissingLineNum)?; -// self.track_line_num = addition_line_num; - -// let mut replaced = false; -// for rewind_index in (0..self.edits.len()).rev() { -// let edit = &mut self.edits[rewind_index]; - -// if let GitDiffEdit::Removed(removed_line_num) = *edit { -// match removed_line_num.cmp(&addition_line_num) { -// Ordering::Equal => { -// *edit = GitDiffEdit::Modified(addition_line_num); -// replaced = true; -// break; -// } - -// Ordering::Greater => continue, -// Ordering::Less => break, -// } -// } -// } - -// if !replaced { -// self.edits.push(GitDiffEdit::Added(addition_line_num)); -// } -// } - -// _ => {} -// } - -// Ok(()) -// } -// } From 5157c71fa9f825c0d0fd48e5b7015f6baafa86ae Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 2 Sep 2022 15:22:15 -0400 Subject: [PATCH 048/314] Render deletion gutter markers --- crates/editor/src/element.rs | 42 +++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index b13a2a6ada..4ee14407b8 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -525,8 +525,9 @@ impl EditorElement { layout: &mut LayoutState, cx: &mut PaintContext, ) { - let scroll_top = - layout.position_map.snapshot.scroll_position().y() * layout.position_map.line_height; + let line_height = layout.position_map.line_height; + let scroll_position = layout.position_map.snapshot.scroll_position(); + let scroll_top = scroll_position.y() * line_height; for (ix, line) in layout.line_number_layouts.iter().enumerate() { if let Some(line) = line { let line_origin = bounds.origin() @@ -544,21 +545,42 @@ impl EditorElement { } } - println!("painting from hunks: {:#?}\n", layout.diff_hunks); for hunk in &layout.diff_hunks { let color = match hunk.status() { DiffHunkStatus::Added => Color::green(), DiffHunkStatus::Modified => Color::blue(), - DiffHunkStatus::Removed => continue, + + //TODO: This rendering is entirely a horrible hack + DiffHunkStatus::Removed => { + let row_above = hunk.buffer_range.start; + + let offset = line_height / 2.; + let start_y = row_above as f32 * line_height + offset - scroll_top; + let end_y = start_y + line_height; + + let width = 0.4 * line_height; + let highlight_origin = bounds.origin() + vec2f(-width, start_y); + let highlight_size = vec2f(width * 2., end_y - start_y); + let highlight_bounds = RectF::new(highlight_origin, highlight_size); + + cx.scene.push_quad(Quad { + bounds: highlight_bounds, + background: Some(Color::red()), + border: Border::new(0., Color::transparent_black()), + corner_radius: 1. * line_height, + }); + + continue; + } }; let start_row = hunk.buffer_range.start; let end_row = hunk.buffer_range.end; - let start_y = start_row as f32 * layout.line_height - scroll_top; - let end_y = end_row as f32 * layout.line_height - scroll_top; + let start_y = start_row as f32 * line_height - scroll_top; + let end_y = end_row as f32 * line_height - scroll_top; - let width = 0.22 * layout.line_height; + let width = 0.22 * line_height; let highlight_origin = bounds.origin() + vec2f(-width, start_y); let highlight_size = vec2f(width * 2., end_y - start_y); let highlight_bounds = RectF::new(highlight_origin, highlight_size); @@ -567,15 +589,15 @@ impl EditorElement { bounds: highlight_bounds, background: Some(color), border: Border::new(0., Color::transparent_black()), - corner_radius: 0.2 * layout.line_height, + corner_radius: 0.2 * line_height, }); } if let Some((row, indicator)) = layout.code_actions_indicator.as_mut() { let mut x = bounds.width() - layout.gutter_padding; - let mut y = *row as f32 * layout.position_map.line_height - scroll_top; + let mut y = *row as f32 * line_height - scroll_top; x += ((layout.gutter_padding + layout.gutter_margin) - indicator.size().x()) / 2.; - y += (layout.position_map.line_height - indicator.size().y()) / 2.; + y += (line_height - indicator.size().y()) / 2.; indicator.paint(bounds.origin() + vec2f(x, y), visible_bounds, cx); } } From 883d5b7a081d640afe7afcfd3729e610ecb874dc Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 6 Sep 2022 17:09:47 -0400 Subject: [PATCH 049/314] Update git gutter status after debounced delay Co-authored-by: Max Brunsfeld --- crates/editor/src/display_map/fold_map.rs | 1 + crates/editor/src/items.rs | 7 +++-- crates/editor/src/multi_buffer.rs | 26 ++++++++++++++++++ crates/language/src/buffer.rs | 33 ++++++++++++++++++----- crates/workspace/src/workspace.rs | 2 +- 5 files changed, 60 insertions(+), 9 deletions(-) diff --git a/crates/editor/src/display_map/fold_map.rs b/crates/editor/src/display_map/fold_map.rs index 970910f969..6ab5c6202e 100644 --- a/crates/editor/src/display_map/fold_map.rs +++ b/crates/editor/src/display_map/fold_map.rs @@ -274,6 +274,7 @@ impl FoldMap { if buffer.edit_count() != new_buffer.edit_count() || buffer.parse_count() != new_buffer.parse_count() || buffer.diagnostics_update_count() != new_buffer.diagnostics_update_count() + || buffer.diff_update_count() != new_buffer.diff_update_count() || buffer.trailing_excerpt_update_count() != new_buffer.trailing_excerpt_update_count() { diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 22a069c5c0..d208fc9c15 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -481,9 +481,12 @@ impl Item for Editor { fn update_git( &mut self, _project: ModelHandle, - _cx: &mut ViewContext, + cx: &mut ViewContext, ) -> Task> { - println!("Editor::update_git"); + self.buffer().update(cx, |multibuffer, cx| { + multibuffer.update_git(cx); + }); + cx.notify(); Task::ready(Ok(())) } diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 91de32bac9..1d09b7008f 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -91,6 +91,7 @@ struct BufferState { last_selections_update_count: usize, last_diagnostics_update_count: usize, last_file_update_count: usize, + last_diff_update_count: usize, excerpts: Vec, _subscriptions: [gpui::Subscription; 2], } @@ -102,6 +103,7 @@ pub struct MultiBufferSnapshot { parse_count: usize, diagnostics_update_count: usize, trailing_excerpt_update_count: usize, + diff_update_count: usize, edit_count: usize, is_dirty: bool, has_conflict: bool, @@ -203,6 +205,7 @@ impl MultiBuffer { 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_diff_update_count: buffer_state.last_diff_update_count, excerpts: buffer_state.excerpts.clone(), _subscriptions: [ new_cx.observe(&buffer_state.buffer, |_, _, cx| cx.notify()), @@ -309,6 +312,15 @@ impl MultiBuffer { self.read(cx).symbols_containing(offset, theme) } + pub fn update_git(&mut self, cx: &mut ModelContext) { + let mut buffers = self.buffers.borrow_mut(); + for buffer in buffers.values_mut() { + buffer.buffer.update(cx, |buffer, _| { + buffer.update_git(); + }) + } + } + pub fn edit( &mut self, edits: I, @@ -828,6 +840,7 @@ impl MultiBuffer { 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_diff_update_count: buffer_snapshot.diff_update_count(), excerpts: Default::default(), _subscriptions: [ cx.observe(&buffer, |_, _, cx| cx.notify()), @@ -1250,6 +1263,7 @@ impl MultiBuffer { let mut excerpts_to_edit = Vec::new(); let mut reparsed = false; let mut diagnostics_updated = false; + let mut diff_updated = false; let mut is_dirty = false; let mut has_conflict = false; let mut edited = false; @@ -1261,6 +1275,7 @@ impl MultiBuffer { 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 diff_update_count = buffer.diff_update_count(); let buffer_edited = version.changed_since(&buffer_state.last_version); let buffer_reparsed = parse_count > buffer_state.last_parse_count; @@ -1269,17 +1284,20 @@ impl MultiBuffer { 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_diff_updated = diff_update_count > buffer_state.last_diff_update_count; if buffer_edited || buffer_reparsed || buffer_selections_updated || buffer_diagnostics_updated || buffer_file_updated + || buffer_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_diff_update_count = diff_update_count; excerpts_to_edit.extend( buffer_state .excerpts @@ -1291,6 +1309,7 @@ impl MultiBuffer { edited |= buffer_edited; reparsed |= buffer_reparsed; diagnostics_updated |= buffer_diagnostics_updated; + diff_updated |= buffer_diff_updated; is_dirty |= buffer.is_dirty(); has_conflict |= buffer.has_conflict(); } @@ -1303,6 +1322,9 @@ impl MultiBuffer { if diagnostics_updated { snapshot.diagnostics_update_count += 1; } + if diff_updated { + snapshot.diff_update_count += 1; + } snapshot.is_dirty = is_dirty; snapshot.has_conflict = has_conflict; @@ -2480,6 +2502,10 @@ impl MultiBufferSnapshot { self.diagnostics_update_count } + pub fn diff_update_count(&self) -> usize { + self.diff_update_count + } + pub fn trailing_excerpt_update_count(&self) -> usize { self.trailing_excerpt_update_count } diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index ad3d8978ad..10d7fa5535 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -49,7 +49,7 @@ pub use lsp::DiagnosticSeverity; pub struct Buffer { text: TextBuffer, head_text: Option, - diff: BufferDiff, + git_diff: BufferDiff, file: Option>, saved_version: clock::Global, saved_version_fingerprint: String, @@ -69,6 +69,7 @@ pub struct Buffer { diagnostics_update_count: usize, diagnostics_timestamp: clock::Lamport, file_update_count: usize, + diff_update_count: usize, completion_triggers: Vec, completion_triggers_timestamp: clock::Lamport, deferred_ops: OperationQueue, @@ -76,12 +77,13 @@ pub struct Buffer { pub struct BufferSnapshot { text: text::BufferSnapshot, - git_hunks: Arc<[DiffHunk]>, + pub git_hunks: Arc<[DiffHunk]>, pub(crate) syntax: SyntaxSnapshot, file: Option>, diagnostics: DiagnosticSet, diagnostics_update_count: usize, file_update_count: usize, + diff_update_count: usize, remote_selections: TreeMap, selections_update_count: usize, language: Option>, @@ -419,9 +421,9 @@ impl Buffer { UNIX_EPOCH }; - let mut diff = BufferDiff::new(); + let mut git_diff = BufferDiff::new(); if let Some(head_text) = &head_text { - diff.update(head_text, &buffer); + git_diff.update(head_text, &buffer); } Self { @@ -432,7 +434,7 @@ impl Buffer { was_dirty_before_starting_transaction: None, text: buffer, head_text, - diff, + git_diff, file, syntax_map: Mutex::new(SyntaxMap::new()), parsing_in_background: false, @@ -447,6 +449,7 @@ impl Buffer { diagnostics_update_count: 0, diagnostics_timestamp: Default::default(), file_update_count: 0, + diff_update_count: 0, completion_triggers: Default::default(), completion_triggers_timestamp: Default::default(), deferred_ops: OperationQueue::new(), @@ -462,12 +465,13 @@ impl Buffer { BufferSnapshot { text, syntax, - git_hunks: self.diff.hunks(), + git_hunks: self.git_diff.hunks(), file: self.file.clone(), remote_selections: self.remote_selections.clone(), diagnostics: self.diagnostics.clone(), diagnostics_update_count: self.diagnostics_update_count, file_update_count: self.file_update_count, + diff_update_count: self.diff_update_count, language: self.language.clone(), parse_count: self.parse_count, selections_update_count: self.selections_update_count, @@ -649,6 +653,14 @@ impl Buffer { task } + pub fn update_git(&mut self) { + if let Some(head_text) = &self.head_text { + let snapshot = self.snapshot(); + self.git_diff.update(head_text, &snapshot); + self.diff_update_count += 1; + } + } + pub fn close(&mut self, cx: &mut ModelContext) { cx.emit(Event::Closed); } @@ -673,6 +685,10 @@ impl Buffer { self.file_update_count } + pub fn diff_update_count(&self) -> usize { + self.diff_update_count + } + #[cfg(any(test, feature = "test-support"))] pub fn is_parsing(&self) -> bool { self.parsing_in_background @@ -2226,6 +2242,10 @@ impl BufferSnapshot { pub fn file_update_count(&self) -> usize { self.file_update_count } + + pub fn diff_update_count(&self) -> usize { + self.diff_update_count + } } pub fn indent_size_for_line(text: &text::BufferSnapshot, row: u32) -> IndentSize { @@ -2260,6 +2280,7 @@ impl Clone for BufferSnapshot { selections_update_count: self.selections_update_count, diagnostics_update_count: self.diagnostics_update_count, file_update_count: self.file_update_count, + diff_update_count: self.diff_update_count, language: self.language.clone(), parse_count: self.parse_count, } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 3446dc0f0e..ad3862c56f 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -734,7 +734,7 @@ impl ItemHandle for ViewHandle { ); } - const GIT_DELAY: Duration = Duration::from_millis(800); + const GIT_DELAY: Duration = Duration::from_millis(600); let item = item.clone(); pending_git_update.fire_new( GIT_DELAY, From a86e93d46fa16b0c45acce1c3ba53ec728141157 Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 9 Sep 2022 11:52:42 -0400 Subject: [PATCH 050/314] Checkpoint on incremental diff sumtree shenanigans --- crates/language/src/buffer.rs | 18 +-- crates/language/src/git.rs | 263 ++++++++++++++++++++++++++++------ crates/text/src/rope.rs | 7 + 3 files changed, 233 insertions(+), 55 deletions(-) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 10d7fa5535..5159e316f9 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -35,7 +35,7 @@ use std::{ time::{Duration, Instant, SystemTime, UNIX_EPOCH}, vec, }; -use sum_tree::TreeMap; +use sum_tree::{SumTree, TreeMap}; use text::operation_queue::OperationQueue; pub use text::{Buffer as TextBuffer, BufferSnapshot as TextBufferSnapshot, Operation as _, *}; use theme::SyntaxTheme; @@ -48,7 +48,7 @@ pub use lsp::DiagnosticSeverity; pub struct Buffer { text: TextBuffer, - head_text: Option, + head_text: Option, git_diff: BufferDiff, file: Option>, saved_version: clock::Global, @@ -77,7 +77,7 @@ pub struct Buffer { pub struct BufferSnapshot { text: text::BufferSnapshot, - pub git_hunks: Arc<[DiffHunk]>, + pub git_hunks: SumTree>, pub(crate) syntax: SyntaxSnapshot, file: Option>, diagnostics: DiagnosticSet, @@ -371,7 +371,7 @@ impl Buffer { id: self.remote_id(), file: self.file.as_ref().map(|f| f.to_proto()), base_text: self.base_text().to_string(), - head_text: self.head_text.clone(), + head_text: self.head_text.as_ref().map(|h| h.to_string()), line_ending: proto::serialize_line_ending(self.line_ending()) as i32, } } @@ -421,10 +421,8 @@ impl Buffer { UNIX_EPOCH }; - let mut git_diff = BufferDiff::new(); - if let Some(head_text) = &head_text { - git_diff.update(head_text, &buffer); - } + let git_diff = BufferDiff::new(&head_text, &buffer); + let head_text = head_text.map(|h| Rope::from(h.as_str())); Self { saved_mtime, @@ -465,7 +463,7 @@ impl Buffer { BufferSnapshot { text, syntax, - git_hunks: self.git_diff.hunks(), + git_hunks: self.git_diff.hunks().clone(), file: self.file.clone(), remote_selections: self.remote_selections.clone(), diagnostics: self.diagnostics.clone(), @@ -2175,6 +2173,8 @@ impl BufferSnapshot { &'a self, query_row_range: Range, ) -> impl 'a + Iterator> { + println!("{} hunks overall", self.git_hunks.iter().count()); + //This is pretty terrible, find a way to utilize sumtree traversal to accelerate this self.git_hunks.iter().filter_map(move |hunk| { let range = hunk.buffer_range.to_point(&self.text); diff --git a/crates/language/src/git.rs b/crates/language/src/git.rs index 73e511ca48..4a227c904d 100644 --- a/crates/language/src/git.rs +++ b/crates/language/src/git.rs @@ -1,8 +1,7 @@ use std::ops::Range; -use std::sync::Arc; -use sum_tree::Bias; -use text::{Anchor, Point}; +use sum_tree::{Bias, SumTree}; +use text::{Anchor, BufferSnapshot, Point, Rope}; pub use git2 as libgit; use libgit::{DiffOptions as GitOptions, Patch as GitPatch}; @@ -14,7 +13,7 @@ pub enum DiffHunkStatus { Removed, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct DiffHunk { pub buffer_range: Range, pub head_range: Range, @@ -32,60 +31,232 @@ impl DiffHunk { } } +impl sum_tree::Item for DiffHunk { + type Summary = DiffHunkSummary; + + fn summary(&self) -> Self::Summary { + DiffHunkSummary { + head_range: self.head_range.clone(), + } + } +} + +#[derive(Debug, Default, Clone)] +pub struct DiffHunkSummary { + head_range: Range, +} + +impl sum_tree::Summary for DiffHunkSummary { + type Context = (); + + fn add_summary(&mut self, other: &Self, _: &Self::Context) { + self.head_range.start = self.head_range.start.min(other.head_range.start); + self.head_range.end = self.head_range.end.max(other.head_range.end); + } +} + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +struct HunkHeadEnd(usize); + +impl<'a> sum_tree::Dimension<'a, DiffHunkSummary> for HunkHeadEnd { + fn add_summary(&mut self, summary: &'a DiffHunkSummary, _: &()) { + self.0 = summary.head_range.end; + } + + fn from_summary(summary: &'a DiffHunkSummary, _: &()) -> Self { + HunkHeadEnd(summary.head_range.end) + } +} + +struct HunkIter<'a> { + index: usize, + patch: GitPatch<'a>, +} + +impl<'a> HunkIter<'a> { + fn diff(head: &'a [u8], current: &'a [u8]) -> Option { + let mut options = GitOptions::default(); + options.context_lines(0); + let patch = match GitPatch::from_buffers(head, None, current, None, Some(&mut options)) { + Ok(patch) => patch, + Err(_) => return None, + }; + + Some(HunkIter { index: 0, patch }) + } + + fn next(&mut self, buffer: &BufferSnapshot) -> Option> { + if self.index >= self.patch.num_hunks() { + return None; + } + + let (hunk, _) = match self.patch.hunk(self.index) { + Ok(it) => it, + Err(_) => return None, + }; + + let new_start = hunk.new_start() - 1; + let new_end = new_start + hunk.new_lines(); + let start_anchor = buffer.anchor_at(Point::new(new_start, 0), Bias::Left); + let end_anchor = buffer.anchor_at(Point::new(new_end, 0), Bias::Left); + let buffer_range = start_anchor..end_anchor; + + //This is probably wrong? When does this trigger? Should buffer range also do this? + let head_range = if hunk.old_start() == 0 { + 0..0 + } else { + let old_start = hunk.old_start() as usize - 1; + let old_end = old_start + hunk.old_lines() as usize; + old_start..old_end + }; + + self.index += 1; + Some(DiffHunk { + buffer_range, + head_range, + }) + } +} + pub struct BufferDiff { - hunks: Arc<[DiffHunk]>, + last_update_version: clock::Global, + hunks: SumTree>, } impl BufferDiff { - pub fn new() -> BufferDiff { - BufferDiff { - hunks: Arc::new([]), - } - } - - pub fn hunks(&self) -> Arc<[DiffHunk]> { - self.hunks.clone() - } - - pub fn update(&mut self, head: &str, buffer: &text::BufferSnapshot) { - let head = head.as_bytes(); - let current = buffer.as_rope().to_string().into_bytes(); - - let mut options = GitOptions::default(); - options.context_lines(0); - let patch = match GitPatch::from_buffers(head, None, ¤t, None, Some(&mut options)) { - Ok(patch) => patch, - Err(_) => { - //Reset hunks in case of failure to avoid showing a stale (potentially erroneous) diff - self.hunks = Arc::new([]); - return; + pub fn new(head_text: &Option, buffer: &text::BufferSnapshot) -> BufferDiff { + let hunks = if let Some(head_text) = head_text { + let buffer_string = buffer.as_rope().to_string(); + let buffer_bytes = buffer_string.as_bytes(); + let iter = HunkIter::diff(head_text.as_bytes(), buffer_bytes); + if let Some(mut iter) = iter { + println!("some iter"); + let mut hunks = SumTree::new(); + while let Some(hunk) = iter.next(buffer) { + println!("hunk"); + hunks.push(hunk, &()); + } + hunks + } else { + SumTree::new() } + } else { + SumTree::new() }; - let mut hunks = Vec::new(); - for index in 0..patch.num_hunks() { - let (hunk, _) = match patch.hunk(index) { - Ok(it) => it, - Err(_) => continue, - }; + BufferDiff { + last_update_version: buffer.version().clone(), + hunks, + } + } - let new_start = hunk.new_start() - 1; - let new_end = new_start + hunk.new_lines(); - let start_anchor = buffer.anchor_at(Point::new(new_start, 0), Bias::Left); - let end_anchor = buffer.anchor_at(Point::new(new_end, 0), Bias::Left); - let buffer_range = start_anchor..end_anchor; + pub fn hunks(&self) -> &SumTree> { + &self.hunks + } - let old_start = hunk.old_start() as usize - 1; - let old_end = old_start + hunk.old_lines() as usize; - let head_range = old_start..old_end; + pub fn update(&mut self, head: &Rope, buffer: &text::BufferSnapshot) { + let expand_by = 20; + let combine_distance = 5; - hunks.push(DiffHunk { - buffer_range, - head_range, - }); + struct EditRange { + head_start: u32, + head_end: u32, + buffer_start: u32, + buffer_end: u32, } - self.hunks = hunks.into(); + let mut ranges = Vec::::new(); + + for edit in buffer.edits_since::(&self.last_update_version) { + //This bit is extremely wrong, this is not where these row lines should come from + let head_start = edit.old.start.row.saturating_sub(expand_by); + let head_end = (edit.old.end.row + expand_by).min(head.summary().lines.row + 1); + + let buffer_start = edit.new.start.row.saturating_sub(expand_by); + let buffer_end = (edit.new.end.row + expand_by).min(buffer.row_count()); + + if let Some(last_range) = ranges.last_mut() { + let head_distance = last_range.head_end.abs_diff(head_end); + let buffer_distance = last_range.buffer_end.abs_diff(buffer_end); + + if head_distance <= combine_distance || buffer_distance <= combine_distance { + last_range.head_start = last_range.head_start.min(head_start); + last_range.head_end = last_range.head_end.max(head_end); + + last_range.buffer_start = last_range.buffer_start.min(buffer_start); + last_range.buffer_end = last_range.buffer_end.max(buffer_end); + } else { + ranges.push(EditRange { + head_start, + head_end, + buffer_start, + buffer_end, + }); + } + } else { + ranges.push(EditRange { + head_start, + head_end, + buffer_start, + buffer_end, + }); + } + } + + self.last_update_version = buffer.version().clone(); + + let mut new_hunks = SumTree::new(); + let mut cursor = self.hunks.cursor::(); + + for range in ranges { + let head_range = range.head_start..range.head_end; + let head_slice = head.slice_rows(head_range.clone()); + let head_str = head_slice.to_string(); + + let buffer_range = range.buffer_start..range.buffer_end; + let buffer_slice = buffer.as_rope().slice_rows(buffer_range.clone()); + let buffer_str = buffer_slice.to_string(); + + println!("diffing head {:?}, buffer {:?}", head_range, buffer_range); + + let mut iter = match HunkIter::diff(head_str.as_bytes(), buffer_str.as_bytes()) { + Some(iter) => iter, + None => continue, + }; + + while let Some(hunk) = iter.next(buffer) { + println!("hunk"); + let prefix = cursor.slice(&HunkHeadEnd(hunk.head_range.end), Bias::Right, &()); + println!("prefix len: {}", prefix.iter().count()); + new_hunks.extend(prefix.iter().cloned(), &()); + + new_hunks.push(hunk.clone(), &()); + + cursor.seek(&HunkHeadEnd(hunk.head_range.end), Bias::Right, &()); + println!("item: {:?}", cursor.item()); + if let Some(item) = cursor.item() { + if item.head_range.end <= hunk.head_range.end { + println!("skipping"); + cursor.next(&()); + } + } + } + } + + new_hunks.extend( + cursor + .suffix(&()) + .iter() + .map(|i| { + println!("extending with {i:?}"); + i + }) + .cloned(), + &(), + ); + drop(cursor); + + self.hunks = new_hunks; } } diff --git a/crates/text/src/rope.rs b/crates/text/src/rope.rs index d35ac46f45..e148c048bb 100644 --- a/crates/text/src/rope.rs +++ b/crates/text/src/rope.rs @@ -54,6 +54,13 @@ impl Rope { cursor.slice(range.end) } + pub fn slice_rows(&self, range: Range) -> Rope { + //This would be more efficient with a forward advance after the first, but it's fine + let start = self.point_to_offset(Point::new(range.start, 0)); + let end = self.point_to_offset(Point::new(range.end, 0)); + self.slice(start..end) + } + pub fn push(&mut self, text: &str) { let mut new_chunks = SmallVec::<[_; 16]>::new(); let mut new_chunk = ArrayString::new(); From 61ff24edc8f257e0ffbd691351653255cf2f0e66 Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 9 Sep 2022 15:40:33 -0400 Subject: [PATCH 051/314] Move cloneable diff state into new snapshot type Co-Authored-By: Max Brunsfeld --- crates/language/src/buffer.rs | 31 ++----- crates/language/src/git.rs | 154 ++++++++++++++++++++++++++------ crates/sum_tree/src/sum_tree.rs | 6 ++ crates/text/src/anchor.rs | 2 +- 4 files changed, 140 insertions(+), 53 deletions(-) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 5159e316f9..37f2151133 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1,4 +1,4 @@ -use crate::git::{BufferDiff, DiffHunk}; +use crate::git::{BufferDiff, BufferDiffSnapshot, DiffHunk}; pub use crate::{ diagnostic_set::DiagnosticSet, highlight_map::{HighlightId, HighlightMap}, @@ -35,7 +35,7 @@ use std::{ time::{Duration, Instant, SystemTime, UNIX_EPOCH}, vec, }; -use sum_tree::{SumTree, TreeMap}; +use sum_tree::TreeMap; use text::operation_queue::OperationQueue; pub use text::{Buffer as TextBuffer, BufferSnapshot as TextBufferSnapshot, Operation as _, *}; use theme::SyntaxTheme; @@ -77,7 +77,7 @@ pub struct Buffer { pub struct BufferSnapshot { text: text::BufferSnapshot, - pub git_hunks: SumTree>, + pub diff_snapshot: BufferDiffSnapshot, pub(crate) syntax: SyntaxSnapshot, file: Option>, diagnostics: DiagnosticSet, @@ -463,7 +463,7 @@ impl Buffer { BufferSnapshot { text, syntax, - git_hunks: self.git_diff.hunks().clone(), + diff_snapshot: self.git_diff.snapshot(), file: self.file.clone(), remote_selections: self.remote_selections.clone(), diagnostics: self.diagnostics.clone(), @@ -2173,26 +2173,7 @@ impl BufferSnapshot { &'a self, query_row_range: Range, ) -> impl 'a + Iterator> { - println!("{} hunks overall", self.git_hunks.iter().count()); - //This is pretty terrible, find a way to utilize sumtree traversal to accelerate this - self.git_hunks.iter().filter_map(move |hunk| { - let range = hunk.buffer_range.to_point(&self.text); - - if range.start.row < query_row_range.end && query_row_range.start < range.end.row { - let end_row = if range.end.column > 0 { - range.end.row + 1 - } else { - range.end.row - }; - - Some(DiffHunk { - buffer_range: range.start.row..end_row, - head_range: hunk.head_range.clone(), - }) - } else { - None - } - }) + self.diff_snapshot.hunks_in_range(query_row_range, self) } pub fn diagnostics_in_range<'a, T, O>( @@ -2272,7 +2253,7 @@ impl Clone for BufferSnapshot { fn clone(&self) -> Self { Self { text: self.text.clone(), - git_hunks: self.git_hunks.clone(), + diff_snapshot: self.diff_snapshot.clone(), syntax: self.syntax.clone(), file: self.file.clone(), remote_selections: self.remote_selections.clone(), diff --git a/crates/language/src/git.rs b/crates/language/src/git.rs index 4a227c904d..09d74ad9f3 100644 --- a/crates/language/src/git.rs +++ b/crates/language/src/git.rs @@ -1,7 +1,7 @@ use std::ops::Range; use sum_tree::{Bias, SumTree}; -use text::{Anchor, BufferSnapshot, Point, Rope}; +use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point, Rope, ToPoint}; pub use git2 as libgit; use libgit::{DiffOptions as GitOptions, Patch as GitPatch}; @@ -13,10 +13,10 @@ pub enum DiffHunkStatus { Removed, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct DiffHunk { pub buffer_range: Range, - pub head_range: Range, + pub head_range: Range, } impl DiffHunk { @@ -36,6 +36,7 @@ impl sum_tree::Item for DiffHunk { fn summary(&self) -> Self::Summary { DiffHunkSummary { + buffer_range: self.buffer_range.clone(), head_range: self.head_range.clone(), } } @@ -43,11 +44,12 @@ impl sum_tree::Item for DiffHunk { #[derive(Debug, Default, Clone)] pub struct DiffHunkSummary { - head_range: Range, + buffer_range: Range, + head_range: Range, } impl sum_tree::Summary for DiffHunkSummary { - type Context = (); + type Context = text::BufferSnapshot; fn add_summary(&mut self, other: &Self, _: &Self::Context) { self.head_range.start = self.head_range.start.min(other.head_range.start); @@ -55,19 +57,32 @@ impl sum_tree::Summary for DiffHunkSummary { } } -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -struct HunkHeadEnd(usize); +#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)] +struct HunkHeadEnd(u32); impl<'a> sum_tree::Dimension<'a, DiffHunkSummary> for HunkHeadEnd { - fn add_summary(&mut self, summary: &'a DiffHunkSummary, _: &()) { + fn add_summary(&mut self, summary: &'a DiffHunkSummary, _: &text::BufferSnapshot) { self.0 = summary.head_range.end; } - fn from_summary(summary: &'a DiffHunkSummary, _: &()) -> Self { + fn from_summary(summary: &'a DiffHunkSummary, _: &text::BufferSnapshot) -> Self { HunkHeadEnd(summary.head_range.end) } } +#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)] +struct HunkBufferEnd(u32); + +impl<'a> sum_tree::Dimension<'a, DiffHunkSummary> for HunkBufferEnd { + fn add_summary(&mut self, summary: &'a DiffHunkSummary, buffer: &text::BufferSnapshot) { + self.0 = summary.buffer_range.end.to_point(buffer).row; + } + + fn from_summary(summary: &'a DiffHunkSummary, buffer: &text::BufferSnapshot) -> Self { + HunkBufferEnd(summary.buffer_range.end.to_point(buffer).row) + } +} + struct HunkIter<'a> { index: usize, patch: GitPatch<'a>, @@ -105,8 +120,8 @@ impl<'a> HunkIter<'a> { let head_range = if hunk.old_start() == 0 { 0..0 } else { - let old_start = hunk.old_start() as usize - 1; - let old_end = old_start + hunk.old_lines() as usize; + let old_start = hunk.old_start() - 1; + let old_end = old_start + hunk.old_lines(); old_start..old_end }; @@ -118,9 +133,48 @@ impl<'a> HunkIter<'a> { } } +#[derive(Clone)] +pub struct BufferDiffSnapshot { + tree: SumTree>, +} + +impl BufferDiffSnapshot { + pub fn hunks_in_range<'a>( + &'a self, + query_row_range: Range, + buffer: &'a BufferSnapshot, + ) -> impl 'a + Iterator> { + println!("{} hunks overall", self.tree.iter().count()); + + self.tree.iter().filter_map(move |hunk| { + let range = hunk.buffer_range.to_point(&buffer); + + if range.start.row < query_row_range.end && query_row_range.start < range.end.row { + let end_row = if range.end.column > 0 { + range.end.row + 1 + } else { + range.end.row + }; + + Some(DiffHunk { + buffer_range: range.start.row..end_row, + head_range: hunk.head_range.clone(), + }) + } else { + None + } + }) + } + + #[cfg(test)] + fn hunks<'a>(&'a self, text: &'a BufferSnapshot) -> impl 'a + Iterator> { + self.hunks_in_range(0..u32::MAX, text) + } +} + pub struct BufferDiff { last_update_version: clock::Global, - hunks: SumTree>, + snapshot: BufferDiffSnapshot, } impl BufferDiff { @@ -128,13 +182,12 @@ impl BufferDiff { let hunks = if let Some(head_text) = head_text { let buffer_string = buffer.as_rope().to_string(); let buffer_bytes = buffer_string.as_bytes(); + let iter = HunkIter::diff(head_text.as_bytes(), buffer_bytes); if let Some(mut iter) = iter { - println!("some iter"); let mut hunks = SumTree::new(); while let Some(hunk) = iter.next(buffer) { - println!("hunk"); - hunks.push(hunk, &()); + hunks.push(hunk, buffer); } hunks } else { @@ -146,12 +199,12 @@ impl BufferDiff { BufferDiff { last_update_version: buffer.version().clone(), - hunks, + snapshot: BufferDiffSnapshot { tree: hunks }, } } - pub fn hunks(&self) -> &SumTree> { - &self.hunks + pub fn snapshot(&self) -> BufferDiffSnapshot { + self.snapshot.clone() } pub fn update(&mut self, head: &Rope, buffer: &text::BufferSnapshot) { @@ -206,7 +259,7 @@ impl BufferDiff { self.last_update_version = buffer.version().clone(); let mut new_hunks = SumTree::new(); - let mut cursor = self.hunks.cursor::(); + let mut cursor = self.snapshot.tree.cursor::(); for range in ranges { let head_range = range.head_start..range.head_end; @@ -226,18 +279,18 @@ impl BufferDiff { while let Some(hunk) = iter.next(buffer) { println!("hunk"); - let prefix = cursor.slice(&HunkHeadEnd(hunk.head_range.end), Bias::Right, &()); + let prefix = cursor.slice(&HunkHeadEnd(hunk.head_range.end), Bias::Right, buffer); println!("prefix len: {}", prefix.iter().count()); - new_hunks.extend(prefix.iter().cloned(), &()); + new_hunks.extend(prefix.iter().cloned(), buffer); - new_hunks.push(hunk.clone(), &()); + new_hunks.push(hunk.clone(), buffer); - cursor.seek(&HunkHeadEnd(hunk.head_range.end), Bias::Right, &()); + cursor.seek(&HunkHeadEnd(hunk.head_range.end), Bias::Right, buffer); println!("item: {:?}", cursor.item()); if let Some(item) = cursor.item() { if item.head_range.end <= hunk.head_range.end { println!("skipping"); - cursor.next(&()); + cursor.next(buffer); } } } @@ -245,18 +298,18 @@ impl BufferDiff { new_hunks.extend( cursor - .suffix(&()) + .suffix(buffer) .iter() .map(|i| { println!("extending with {i:?}"); i }) .cloned(), - &(), + buffer, ); drop(cursor); - self.hunks = new_hunks; + self.snapshot.tree = new_hunks; } } @@ -276,3 +329,50 @@ impl GitDiffEdit { } } } + +#[cfg(test)] +mod tests { + use super::*; + use text::Buffer; + use unindent::Unindent as _; + + #[gpui::test] + fn test_buffer_diff_simple() { + let head_text = " + one + two + three + " + .unindent(); + + let buffer_text = " + one + hello + three + " + .unindent(); + + let mut buffer = Buffer::new(0, 0, buffer_text); + let diff = BufferDiff::new(&Some(head_text.clone()), &buffer); + assert_eq!( + diff.snapshot.hunks(&buffer).collect::>(), + &[DiffHunk { + buffer_range: 1..2, + head_range: 1..2 + }] + ); + + buffer.edit([(0..0, "point five\n")]); + assert_eq!( + diff.snapshot.hunks(&buffer).collect::>(), + &[DiffHunk { + buffer_range: 2..3, + head_range: 1..2 + }] + ); + } + + // use rand::rngs::StdRng; + // #[gpui::test(iterations = 100)] + // fn test_buffer_diff_random(mut rng: StdRng) {} +} diff --git a/crates/sum_tree/src/sum_tree.rs b/crates/sum_tree/src/sum_tree.rs index cb05dff967..7beab3b7c5 100644 --- a/crates/sum_tree/src/sum_tree.rs +++ b/crates/sum_tree/src/sum_tree.rs @@ -101,6 +101,12 @@ pub enum Bias { Right, } +impl Default for Bias { + fn default() -> Self { + Bias::Left + } +} + impl PartialOrd for Bias { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) diff --git a/crates/text/src/anchor.rs b/crates/text/src/anchor.rs index dca95ce5d5..9f70ae1cc7 100644 --- a/crates/text/src/anchor.rs +++ b/crates/text/src/anchor.rs @@ -4,7 +4,7 @@ use anyhow::Result; use std::{cmp::Ordering, fmt::Debug, ops::Range}; use sum_tree::Bias; -#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] +#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, Default)] pub struct Anchor { pub timestamp: clock::Local, pub offset: usize, From a2e8fc79d9ff47686eb9b6442049f2f9331ba240 Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 9 Sep 2022 17:32:19 -0400 Subject: [PATCH 052/314] Switch head range from row range to byte offset range Co-Authored-By: Max Brunsfeld --- crates/language/src/git.rs | 405 +++++++++++++++++++++++-------------- 1 file changed, 255 insertions(+), 150 deletions(-) diff --git a/crates/language/src/git.rs b/crates/language/src/git.rs index 09d74ad9f3..b3e2f7da51 100644 --- a/crates/language/src/git.rs +++ b/crates/language/src/git.rs @@ -16,7 +16,7 @@ pub enum DiffHunkStatus { #[derive(Debug, Clone, PartialEq, Eq)] pub struct DiffHunk { pub buffer_range: Range, - pub head_range: Range, + pub head_range: Range, } impl DiffHunk { @@ -45,7 +45,7 @@ impl sum_tree::Item for DiffHunk { #[derive(Debug, Default, Clone)] pub struct DiffHunkSummary { buffer_range: Range, - head_range: Range, + head_range: Range, } impl sum_tree::Summary for DiffHunkSummary { @@ -58,7 +58,7 @@ impl sum_tree::Summary for DiffHunkSummary { } #[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)] -struct HunkHeadEnd(u32); +struct HunkHeadEnd(usize); impl<'a> sum_tree::Dimension<'a, DiffHunkSummary> for HunkHeadEnd { fn add_summary(&mut self, summary: &'a DiffHunkSummary, _: &text::BufferSnapshot) { @@ -83,55 +83,63 @@ impl<'a> sum_tree::Dimension<'a, DiffHunkSummary> for HunkBufferEnd { } } -struct HunkIter<'a> { - index: usize, - patch: GitPatch<'a>, -} +// struct HunkIter<'a> { +// index: usize, +// patch: GitPatch<'a>, +// } -impl<'a> HunkIter<'a> { - fn diff(head: &'a [u8], current: &'a [u8]) -> Option { - let mut options = GitOptions::default(); - options.context_lines(0); - let patch = match GitPatch::from_buffers(head, None, current, None, Some(&mut options)) { - Ok(patch) => patch, - Err(_) => return None, - }; +// impl<'a> HunkIter<'a> { +// fn diff(head: &'a [u8], current: &'a [u8]) -> Option { +// let mut options = GitOptions::default(); +// options.context_lines(0); +// let patch = match GitPatch::from_buffers(head, None, current, None, Some(&mut options)) { +// Ok(patch) => patch, +// Err(_) => return None, +// }; - Some(HunkIter { index: 0, patch }) - } +// Some(HunkIter { index: 0, patch }) +// } - fn next(&mut self, buffer: &BufferSnapshot) -> Option> { - if self.index >= self.patch.num_hunks() { - return None; - } +// fn next(&mut self, buffer: &BufferSnapshot) -> Option> { +// if self.index >= self.patch.num_hunks() { +// return None; +// } - let (hunk, _) = match self.patch.hunk(self.index) { - Ok(it) => it, - Err(_) => return None, - }; +// let (hunk, _) = match self.patch.hunk(self.index) { +// Ok(it) => it, +// Err(_) => return None, +// }; +// let hunk_line_count = self.patch.num_lines_in_hunk(self.index).unwrap(); - let new_start = hunk.new_start() - 1; - let new_end = new_start + hunk.new_lines(); - let start_anchor = buffer.anchor_at(Point::new(new_start, 0), Bias::Left); - let end_anchor = buffer.anchor_at(Point::new(new_end, 0), Bias::Left); - let buffer_range = start_anchor..end_anchor; +// println!("{hunk:#?}"); +// for index in 0..hunk_line_count { +// println!("{:?}", self.patch.line_in_hunk(self.index, index)); +// } - //This is probably wrong? When does this trigger? Should buffer range also do this? - let head_range = if hunk.old_start() == 0 { - 0..0 - } else { - let old_start = hunk.old_start() - 1; - let old_end = old_start + hunk.old_lines(); - old_start..old_end - }; +// let new_start = hunk.new_start() - 1; +// let new_end = new_start + hunk.new_lines(); +// let start_anchor = buffer.anchor_at(Point::new(new_start, 0), Bias::Left); +// let end_anchor = buffer.anchor_at(Point::new(new_end, 0), Bias::Left); +// let buffer_range = start_anchor..end_anchor; - self.index += 1; - Some(DiffHunk { - buffer_range, - head_range, - }) - } -} +// //This is probably wrong? When does this trigger? Should buffer range also do this? +// let head_range = if hunk.old_start() == 0 { +// 0..0 +// } else { +// let old_start = hunk.old_start() - 1; +// let old_end = old_start + hunk.old_lines(); +// old_start..old_end +// }; + +// // let head_start_index = self.patch.line_in_hunk(self.index, 0) + +// self.index += 1; +// Some(DiffHunk { +// buffer_range, +// head_range, +// }) +// } +// } #[derive(Clone)] pub struct BufferDiffSnapshot { @@ -144,7 +152,7 @@ impl BufferDiffSnapshot { query_row_range: Range, buffer: &'a BufferSnapshot, ) -> impl 'a + Iterator> { - println!("{} hunks overall", self.tree.iter().count()); + // println!("{} hunks overall", self.tree.iter().count()); self.tree.iter().filter_map(move |hunk| { let range = hunk.buffer_range.to_point(&buffer); @@ -183,16 +191,100 @@ impl BufferDiff { let buffer_string = buffer.as_rope().to_string(); let buffer_bytes = buffer_string.as_bytes(); - let iter = HunkIter::diff(head_text.as_bytes(), buffer_bytes); - if let Some(mut iter) = iter { - let mut hunks = SumTree::new(); - while let Some(hunk) = iter.next(buffer) { - hunks.push(hunk, buffer); + let mut options = GitOptions::default(); + options.context_lines(0); + let patch = match GitPatch::from_buffers( + head_text.as_bytes(), + None, + buffer_bytes, + None, + Some(&mut options), + ) { + Ok(patch) => patch, + Err(_) => todo!("This needs to be handled"), + }; + + let mut hunks = SumTree::>::new(); + let mut delta = 0i64; + for i in 0..patch.num_hunks() { + let diff_line_item_count = patch.num_lines_in_hunk(i).unwrap(); + + // if diff_line_item_count == 0 { + // continue; + // } + + // let calc_line_diff_hunk = || { + + // }; + + // let first_line = patch.line_in_hunk(0).unwrap(); + // let mut hunk = + + for j in 0..diff_line_item_count { + let line = patch.line_in_hunk(i, j).unwrap(); + + let hunk = match line.origin_value() { + libgit::DiffLineType::Addition => { + let buffer_start = line.content_offset(); + let buffer_end = buffer_start as usize + line.content().len(); + let head_offset = (buffer_start - delta) as usize; + delta += line.content().len() as i64; + DiffHunk { + buffer_range: buffer.anchor_before(buffer_start as usize) + ..buffer.anchor_after(buffer_end), + head_range: head_offset..head_offset, + } + } + libgit::DiffLineType::Deletion => { + let head_start = line.content_offset(); + let head_end = head_start as usize + line.content().len(); + let buffer_offset = (head_start + delta) as usize; + delta -= line.content().len() as i64; + DiffHunk { + buffer_range: buffer.anchor_before(buffer_offset) + ..buffer.anchor_after(buffer_offset), + head_range: (head_start as usize)..head_end, + } + } + + libgit::DiffLineType::AddEOFNL => todo!(), + libgit::DiffLineType::ContextEOFNL => todo!(), + libgit::DiffLineType::DeleteEOFNL => todo!(), + libgit::DiffLineType::Context => unreachable!(), + libgit::DiffLineType::FileHeader => continue, + libgit::DiffLineType::HunkHeader => continue, + libgit::DiffLineType::Binary => continue, + }; + + let mut combined = false; + hunks.update_last( + |last_hunk| { + if last_hunk.head_range.end == hunk.head_range.start { + last_hunk.head_range.end = hunk.head_range.end; + last_hunk.buffer_range.end = hunk.buffer_range.end; + combined = true; + } + }, + buffer, + ); + if !combined { + hunks.push(hunk, buffer); + } } - hunks - } else { - SumTree::new() } + + // let iter = HunkIter::diff(head_text.as_bytes(), buffer_bytes); + // if let Some(mut iter) = iter { + // let mut hunks = SumTree::new(); + // while let Some(hunk) = iter.next(buffer) { + // hunks.push(hunk, buffer); + // } + // println!("========"); + // hunks + // } else { + // SumTree::new() + // } + hunks } else { SumTree::new() }; @@ -208,108 +300,108 @@ impl BufferDiff { } pub fn update(&mut self, head: &Rope, buffer: &text::BufferSnapshot) { - let expand_by = 20; - let combine_distance = 5; + // let expand_by = 20; + // let combine_distance = 5; - struct EditRange { - head_start: u32, - head_end: u32, - buffer_start: u32, - buffer_end: u32, - } + // struct EditRange { + // head_start: u32, + // head_end: u32, + // buffer_start: u32, + // buffer_end: u32, + // } - let mut ranges = Vec::::new(); + // let mut ranges = Vec::::new(); - for edit in buffer.edits_since::(&self.last_update_version) { - //This bit is extremely wrong, this is not where these row lines should come from - let head_start = edit.old.start.row.saturating_sub(expand_by); - let head_end = (edit.old.end.row + expand_by).min(head.summary().lines.row + 1); + // for edit in buffer.edits_since::(&self.last_update_version) { + // //This bit is extremely wrong, this is not where these row lines should come from + // let head_start = edit.old.start.row.saturating_sub(expand_by); + // let head_end = (edit.old.end.row + expand_by).min(head.summary().lines.row + 1); - let buffer_start = edit.new.start.row.saturating_sub(expand_by); - let buffer_end = (edit.new.end.row + expand_by).min(buffer.row_count()); + // let buffer_start = edit.new.start.row.saturating_sub(expand_by); + // let buffer_end = (edit.new.end.row + expand_by).min(buffer.row_count()); - if let Some(last_range) = ranges.last_mut() { - let head_distance = last_range.head_end.abs_diff(head_end); - let buffer_distance = last_range.buffer_end.abs_diff(buffer_end); + // if let Some(last_range) = ranges.last_mut() { + // let head_distance = last_range.head_end.abs_diff(head_end); + // let buffer_distance = last_range.buffer_end.abs_diff(buffer_end); - if head_distance <= combine_distance || buffer_distance <= combine_distance { - last_range.head_start = last_range.head_start.min(head_start); - last_range.head_end = last_range.head_end.max(head_end); + // if head_distance <= combine_distance || buffer_distance <= combine_distance { + // last_range.head_start = last_range.head_start.min(head_start); + // last_range.head_end = last_range.head_end.max(head_end); - last_range.buffer_start = last_range.buffer_start.min(buffer_start); - last_range.buffer_end = last_range.buffer_end.max(buffer_end); - } else { - ranges.push(EditRange { - head_start, - head_end, - buffer_start, - buffer_end, - }); - } - } else { - ranges.push(EditRange { - head_start, - head_end, - buffer_start, - buffer_end, - }); - } - } + // last_range.buffer_start = last_range.buffer_start.min(buffer_start); + // last_range.buffer_end = last_range.buffer_end.max(buffer_end); + // } else { + // ranges.push(EditRange { + // head_start, + // head_end, + // buffer_start, + // buffer_end, + // }); + // } + // } else { + // ranges.push(EditRange { + // head_start, + // head_end, + // buffer_start, + // buffer_end, + // }); + // } + // } - self.last_update_version = buffer.version().clone(); + // self.last_update_version = buffer.version().clone(); - let mut new_hunks = SumTree::new(); - let mut cursor = self.snapshot.tree.cursor::(); + // let mut new_hunks = SumTree::new(); + // let mut cursor = self.snapshot.tree.cursor::(); - for range in ranges { - let head_range = range.head_start..range.head_end; - let head_slice = head.slice_rows(head_range.clone()); - let head_str = head_slice.to_string(); + // for range in ranges { + // let head_range = range.head_start..range.head_end; + // let head_slice = head.slice_rows(head_range.clone()); + // let head_str = head_slice.to_string(); - let buffer_range = range.buffer_start..range.buffer_end; - let buffer_slice = buffer.as_rope().slice_rows(buffer_range.clone()); - let buffer_str = buffer_slice.to_string(); + // let buffer_range = range.buffer_start..range.buffer_end; + // let buffer_slice = buffer.as_rope().slice_rows(buffer_range.clone()); + // let buffer_str = buffer_slice.to_string(); - println!("diffing head {:?}, buffer {:?}", head_range, buffer_range); + // println!("diffing head {:?}, buffer {:?}", head_range, buffer_range); - let mut iter = match HunkIter::diff(head_str.as_bytes(), buffer_str.as_bytes()) { - Some(iter) => iter, - None => continue, - }; + // let mut iter = match HunkIter::diff(head_str.as_bytes(), buffer_str.as_bytes()) { + // Some(iter) => iter, + // None => continue, + // }; - while let Some(hunk) = iter.next(buffer) { - println!("hunk"); - let prefix = cursor.slice(&HunkHeadEnd(hunk.head_range.end), Bias::Right, buffer); - println!("prefix len: {}", prefix.iter().count()); - new_hunks.extend(prefix.iter().cloned(), buffer); + // while let Some(hunk) = iter.next(buffer) { + // println!("hunk"); + // let prefix = cursor.slice(&HunkHeadEnd(hunk.head_range.end), Bias::Right, buffer); + // println!("prefix len: {}", prefix.iter().count()); + // new_hunks.extend(prefix.iter().cloned(), buffer); - new_hunks.push(hunk.clone(), buffer); + // new_hunks.push(hunk.clone(), buffer); - cursor.seek(&HunkHeadEnd(hunk.head_range.end), Bias::Right, buffer); - println!("item: {:?}", cursor.item()); - if let Some(item) = cursor.item() { - if item.head_range.end <= hunk.head_range.end { - println!("skipping"); - cursor.next(buffer); - } - } - } - } + // cursor.seek(&HunkHeadEnd(hunk.head_range.end), Bias::Right, buffer); + // println!("item: {:?}", cursor.item()); + // if let Some(item) = cursor.item() { + // if item.head_range.end <= hunk.head_range.end { + // println!("skipping"); + // cursor.next(buffer); + // } + // } + // } + // } - new_hunks.extend( - cursor - .suffix(buffer) - .iter() - .map(|i| { - println!("extending with {i:?}"); - i - }) - .cloned(), - buffer, - ); - drop(cursor); + // new_hunks.extend( + // cursor + // .suffix(buffer) + // .iter() + // .map(|i| { + // println!("extending with {i:?}"); + // i + // }) + // .cloned(), + // buffer, + // ); + // drop(cursor); - self.snapshot.tree = new_hunks; + // self.snapshot.tree = new_hunks; } } @@ -354,22 +446,35 @@ mod tests { let mut buffer = Buffer::new(0, 0, buffer_text); let diff = BufferDiff::new(&Some(head_text.clone()), &buffer); - assert_eq!( - diff.snapshot.hunks(&buffer).collect::>(), - &[DiffHunk { - buffer_range: 1..2, - head_range: 1..2 - }] - ); + assert_hunks(&diff, &buffer, &head_text, &[(1..2, "two\n")]); buffer.edit([(0..0, "point five\n")]); + assert_hunks(&diff, &buffer, &head_text, &[(2..3, "two\n")]); + } + + #[track_caller] + fn assert_hunks( + diff: &BufferDiff, + buffer: &BufferSnapshot, + head_text: &str, + expected_hunks: &[(Range, &str)], + ) { + let hunks = diff.snapshot.hunks(buffer).collect::>(); assert_eq!( - diff.snapshot.hunks(&buffer).collect::>(), - &[DiffHunk { - buffer_range: 2..3, - head_range: 1..2 - }] + hunks.len(), + expected_hunks.len(), + "actual hunks are {hunks:#?}" ); + + let diff_iter = hunks.iter().enumerate(); + for ((index, hunk), (expected_range, expected_str)) in diff_iter.zip(expected_hunks) { + assert_eq!(&hunk.buffer_range, expected_range, "for hunk {index}"); + assert_eq!( + &head_text[hunk.head_range.clone()], + *expected_str, + "for hunk {index}" + ); + } } // use rand::rngs::StdRng; From 4b2040a7ca3d12f0614ed8ea9a5f0cb78b521343 Mon Sep 17 00:00:00 2001 From: Julia Date: Mon, 12 Sep 2022 15:38:44 -0400 Subject: [PATCH 053/314] Move diff logic back into `BufferDiff::update` --- crates/language/src/buffer.rs | 3 +- crates/language/src/git.rs | 333 ++++++++-------------------------- 2 files changed, 79 insertions(+), 257 deletions(-) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 37f2151133..e75e17e541 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -48,7 +48,7 @@ pub use lsp::DiagnosticSeverity; pub struct Buffer { text: TextBuffer, - head_text: Option, + head_text: Option, git_diff: BufferDiff, file: Option>, saved_version: clock::Global, @@ -422,7 +422,6 @@ impl Buffer { }; let git_diff = BufferDiff::new(&head_text, &buffer); - let head_text = head_text.map(|h| Rope::from(h.as_str())); Self { saved_mtime, diff --git a/crates/language/src/git.rs b/crates/language/src/git.rs index b3e2f7da51..4025a2a42f 100644 --- a/crates/language/src/git.rs +++ b/crates/language/src/git.rs @@ -83,64 +83,6 @@ impl<'a> sum_tree::Dimension<'a, DiffHunkSummary> for HunkBufferEnd { } } -// struct HunkIter<'a> { -// index: usize, -// patch: GitPatch<'a>, -// } - -// impl<'a> HunkIter<'a> { -// fn diff(head: &'a [u8], current: &'a [u8]) -> Option { -// let mut options = GitOptions::default(); -// options.context_lines(0); -// let patch = match GitPatch::from_buffers(head, None, current, None, Some(&mut options)) { -// Ok(patch) => patch, -// Err(_) => return None, -// }; - -// Some(HunkIter { index: 0, patch }) -// } - -// fn next(&mut self, buffer: &BufferSnapshot) -> Option> { -// if self.index >= self.patch.num_hunks() { -// return None; -// } - -// let (hunk, _) = match self.patch.hunk(self.index) { -// Ok(it) => it, -// Err(_) => return None, -// }; -// let hunk_line_count = self.patch.num_lines_in_hunk(self.index).unwrap(); - -// println!("{hunk:#?}"); -// for index in 0..hunk_line_count { -// println!("{:?}", self.patch.line_in_hunk(self.index, index)); -// } - -// let new_start = hunk.new_start() - 1; -// let new_end = new_start + hunk.new_lines(); -// let start_anchor = buffer.anchor_at(Point::new(new_start, 0), Bias::Left); -// let end_anchor = buffer.anchor_at(Point::new(new_end, 0), Bias::Left); -// let buffer_range = start_anchor..end_anchor; - -// //This is probably wrong? When does this trigger? Should buffer range also do this? -// let head_range = if hunk.old_start() == 0 { -// 0..0 -// } else { -// let old_start = hunk.old_start() - 1; -// let old_end = old_start + hunk.old_lines(); -// old_start..old_end -// }; - -// // let head_start_index = self.patch.line_in_hunk(self.index, 0) - -// self.index += 1; -// Some(DiffHunk { -// buffer_range, -// head_range, -// }) -// } -// } - #[derive(Clone)] pub struct BufferDiffSnapshot { tree: SumTree>, @@ -187,221 +129,102 @@ pub struct BufferDiff { impl BufferDiff { pub fn new(head_text: &Option, buffer: &text::BufferSnapshot) -> BufferDiff { - let hunks = if let Some(head_text) = head_text { - let buffer_string = buffer.as_rope().to_string(); - let buffer_bytes = buffer_string.as_bytes(); - - let mut options = GitOptions::default(); - options.context_lines(0); - let patch = match GitPatch::from_buffers( - head_text.as_bytes(), - None, - buffer_bytes, - None, - Some(&mut options), - ) { - Ok(patch) => patch, - Err(_) => todo!("This needs to be handled"), - }; - - let mut hunks = SumTree::>::new(); - let mut delta = 0i64; - for i in 0..patch.num_hunks() { - let diff_line_item_count = patch.num_lines_in_hunk(i).unwrap(); - - // if diff_line_item_count == 0 { - // continue; - // } - - // let calc_line_diff_hunk = || { - - // }; - - // let first_line = patch.line_in_hunk(0).unwrap(); - // let mut hunk = - - for j in 0..diff_line_item_count { - let line = patch.line_in_hunk(i, j).unwrap(); - - let hunk = match line.origin_value() { - libgit::DiffLineType::Addition => { - let buffer_start = line.content_offset(); - let buffer_end = buffer_start as usize + line.content().len(); - let head_offset = (buffer_start - delta) as usize; - delta += line.content().len() as i64; - DiffHunk { - buffer_range: buffer.anchor_before(buffer_start as usize) - ..buffer.anchor_after(buffer_end), - head_range: head_offset..head_offset, - } - } - libgit::DiffLineType::Deletion => { - let head_start = line.content_offset(); - let head_end = head_start as usize + line.content().len(); - let buffer_offset = (head_start + delta) as usize; - delta -= line.content().len() as i64; - DiffHunk { - buffer_range: buffer.anchor_before(buffer_offset) - ..buffer.anchor_after(buffer_offset), - head_range: (head_start as usize)..head_end, - } - } - - libgit::DiffLineType::AddEOFNL => todo!(), - libgit::DiffLineType::ContextEOFNL => todo!(), - libgit::DiffLineType::DeleteEOFNL => todo!(), - libgit::DiffLineType::Context => unreachable!(), - libgit::DiffLineType::FileHeader => continue, - libgit::DiffLineType::HunkHeader => continue, - libgit::DiffLineType::Binary => continue, - }; - - let mut combined = false; - hunks.update_last( - |last_hunk| { - if last_hunk.head_range.end == hunk.head_range.start { - last_hunk.head_range.end = hunk.head_range.end; - last_hunk.buffer_range.end = hunk.buffer_range.end; - combined = true; - } - }, - buffer, - ); - if !combined { - hunks.push(hunk, buffer); - } - } - } - - // let iter = HunkIter::diff(head_text.as_bytes(), buffer_bytes); - // if let Some(mut iter) = iter { - // let mut hunks = SumTree::new(); - // while let Some(hunk) = iter.next(buffer) { - // hunks.push(hunk, buffer); - // } - // println!("========"); - // hunks - // } else { - // SumTree::new() - // } - hunks - } else { - SumTree::new() + let mut instance = BufferDiff { + last_update_version: buffer.version().clone(), + snapshot: BufferDiffSnapshot { + tree: SumTree::new(), + }, }; - BufferDiff { - last_update_version: buffer.version().clone(), - snapshot: BufferDiffSnapshot { tree: hunks }, + if let Some(head_text) = head_text { + instance.update(head_text, buffer); } + + instance } pub fn snapshot(&self) -> BufferDiffSnapshot { self.snapshot.clone() } - pub fn update(&mut self, head: &Rope, buffer: &text::BufferSnapshot) { - // let expand_by = 20; - // let combine_distance = 5; + pub fn update(&mut self, head_text: &str, buffer: &text::BufferSnapshot) { + let buffer_string = buffer.as_rope().to_string(); + let buffer_bytes = buffer_string.as_bytes(); - // struct EditRange { - // head_start: u32, - // head_end: u32, - // buffer_start: u32, - // buffer_end: u32, - // } + let mut options = GitOptions::default(); + options.context_lines(0); + let patch = match GitPatch::from_buffers( + head_text.as_bytes(), + None, + buffer_bytes, + None, + Some(&mut options), + ) { + Ok(patch) => patch, + Err(_) => todo!("This needs to be handled"), + }; - // let mut ranges = Vec::::new(); + let mut hunks = SumTree::>::new(); + let mut delta = 0i64; + for hunk_index in 0..patch.num_hunks() { + for line_index in 0..patch.num_lines_in_hunk(hunk_index).unwrap() { + let line = patch.line_in_hunk(hunk_index, line_index).unwrap(); - // for edit in buffer.edits_since::(&self.last_update_version) { - // //This bit is extremely wrong, this is not where these row lines should come from - // let head_start = edit.old.start.row.saturating_sub(expand_by); - // let head_end = (edit.old.end.row + expand_by).min(head.summary().lines.row + 1); + let hunk = match line.origin_value() { + libgit::DiffLineType::Addition => { + let buffer_start = line.content_offset(); + let buffer_end = buffer_start as usize + line.content().len(); + let head_offset = (buffer_start - delta) as usize; + delta += line.content().len() as i64; + DiffHunk { + buffer_range: buffer.anchor_before(buffer_start as usize) + ..buffer.anchor_after(buffer_end), + head_range: head_offset..head_offset, + } + } - // let buffer_start = edit.new.start.row.saturating_sub(expand_by); - // let buffer_end = (edit.new.end.row + expand_by).min(buffer.row_count()); + libgit::DiffLineType::Deletion => { + let head_start = line.content_offset(); + let head_end = head_start as usize + line.content().len(); + let buffer_offset = (head_start + delta) as usize; + delta -= line.content().len() as i64; + DiffHunk { + buffer_range: buffer.anchor_before(buffer_offset) + ..buffer.anchor_after(buffer_offset), + head_range: (head_start as usize)..head_end, + } + } - // if let Some(last_range) = ranges.last_mut() { - // let head_distance = last_range.head_end.abs_diff(head_end); - // let buffer_distance = last_range.buffer_end.abs_diff(buffer_end); + libgit::DiffLineType::AddEOFNL => todo!(), + libgit::DiffLineType::ContextEOFNL => todo!(), + libgit::DiffLineType::DeleteEOFNL => todo!(), - // if head_distance <= combine_distance || buffer_distance <= combine_distance { - // last_range.head_start = last_range.head_start.min(head_start); - // last_range.head_end = last_range.head_end.max(head_end); + libgit::DiffLineType::FileHeader => continue, + libgit::DiffLineType::HunkHeader => continue, + libgit::DiffLineType::Binary => continue, - // last_range.buffer_start = last_range.buffer_start.min(buffer_start); - // last_range.buffer_end = last_range.buffer_end.max(buffer_end); - // } else { - // ranges.push(EditRange { - // head_start, - // head_end, - // buffer_start, - // buffer_end, - // }); - // } - // } else { - // ranges.push(EditRange { - // head_start, - // head_end, - // buffer_start, - // buffer_end, - // }); - // } - // } + //We specifically tell git to not give us context lines + libgit::DiffLineType::Context => unreachable!(), + }; - // self.last_update_version = buffer.version().clone(); + let mut combined = false; + hunks.update_last( + |last_hunk| { + if last_hunk.head_range.end == hunk.head_range.start { + last_hunk.head_range.end = hunk.head_range.end; + last_hunk.buffer_range.end = hunk.buffer_range.end; + combined = true; + } + }, + buffer, + ); + if !combined { + hunks.push(hunk, buffer); + } + } + } - // let mut new_hunks = SumTree::new(); - // let mut cursor = self.snapshot.tree.cursor::(); - - // for range in ranges { - // let head_range = range.head_start..range.head_end; - // let head_slice = head.slice_rows(head_range.clone()); - // let head_str = head_slice.to_string(); - - // let buffer_range = range.buffer_start..range.buffer_end; - // let buffer_slice = buffer.as_rope().slice_rows(buffer_range.clone()); - // let buffer_str = buffer_slice.to_string(); - - // println!("diffing head {:?}, buffer {:?}", head_range, buffer_range); - - // let mut iter = match HunkIter::diff(head_str.as_bytes(), buffer_str.as_bytes()) { - // Some(iter) => iter, - // None => continue, - // }; - - // while let Some(hunk) = iter.next(buffer) { - // println!("hunk"); - // let prefix = cursor.slice(&HunkHeadEnd(hunk.head_range.end), Bias::Right, buffer); - // println!("prefix len: {}", prefix.iter().count()); - // new_hunks.extend(prefix.iter().cloned(), buffer); - - // new_hunks.push(hunk.clone(), buffer); - - // cursor.seek(&HunkHeadEnd(hunk.head_range.end), Bias::Right, buffer); - // println!("item: {:?}", cursor.item()); - // if let Some(item) = cursor.item() { - // if item.head_range.end <= hunk.head_range.end { - // println!("skipping"); - // cursor.next(buffer); - // } - // } - // } - // } - - // new_hunks.extend( - // cursor - // .suffix(buffer) - // .iter() - // .map(|i| { - // println!("extending with {i:?}"); - // i - // }) - // .cloned(), - // buffer, - // ); - // drop(cursor); - - // self.snapshot.tree = new_hunks; + self.snapshot.tree = hunks; } } From e0ea932fa7d345ddfee557cd29b577b98d515f31 Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 13 Sep 2022 20:41:38 -0400 Subject: [PATCH 054/314] Checkpoint preparing for a more organized approach to incremental diff --- crates/language/src/git.rs | 380 ++++++++++++++++++++++++++++--------- 1 file changed, 292 insertions(+), 88 deletions(-) diff --git a/crates/language/src/git.rs b/crates/language/src/git.rs index 4025a2a42f..642ed5f297 100644 --- a/crates/language/src/git.rs +++ b/crates/language/src/git.rs @@ -1,10 +1,14 @@ use std::ops::Range; +use client::proto::create_buffer_for_peer; use sum_tree::{Bias, SumTree}; use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point, Rope, ToPoint}; pub use git2 as libgit; -use libgit::{DiffOptions as GitOptions, Patch as GitPatch}; +use libgit::{ + DiffLine as GitDiffLine, DiffLineType as GitDiffLineType, DiffOptions as GitOptions, + Patch as GitPatch, +}; #[derive(Debug, Clone, Copy)] pub enum DiffHunkStatus { @@ -16,12 +20,12 @@ pub enum DiffHunkStatus { #[derive(Debug, Clone, PartialEq, Eq)] pub struct DiffHunk { pub buffer_range: Range, - pub head_range: Range, + pub head_byte_range: Range, } impl DiffHunk { pub fn status(&self) -> DiffHunkStatus { - if self.head_range.is_empty() { + if self.head_byte_range.is_empty() { DiffHunkStatus::Added } else if self.buffer_range.is_empty() { DiffHunkStatus::Removed @@ -37,7 +41,7 @@ impl sum_tree::Item for DiffHunk { fn summary(&self) -> Self::Summary { DiffHunkSummary { buffer_range: self.buffer_range.clone(), - head_range: self.head_range.clone(), + head_range: self.head_byte_range.clone(), } } } @@ -70,6 +74,19 @@ impl<'a> sum_tree::Dimension<'a, DiffHunkSummary> for HunkHeadEnd { } } +#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)] +struct HunkBufferStart(u32); + +impl<'a> sum_tree::Dimension<'a, DiffHunkSummary> for HunkBufferStart { + fn add_summary(&mut self, summary: &'a DiffHunkSummary, buffer: &text::BufferSnapshot) { + self.0 = summary.buffer_range.start.to_point(buffer).row; + } + + fn from_summary(summary: &'a DiffHunkSummary, buffer: &text::BufferSnapshot) -> Self { + HunkBufferStart(summary.buffer_range.start.to_point(buffer).row) + } +} + #[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)] struct HunkBufferEnd(u32); @@ -83,6 +100,40 @@ impl<'a> sum_tree::Dimension<'a, DiffHunkSummary> for HunkBufferEnd { } } +struct HunkLineIter<'a, 'b> { + patch: &'a GitPatch<'b>, + hunk_index: usize, + line_index: usize, +} + +impl<'a, 'b> HunkLineIter<'a, 'b> { + fn new(patch: &'a GitPatch<'b>, hunk_index: usize) -> Self { + HunkLineIter { + patch, + hunk_index, + line_index: 0, + } + } +} + +impl<'a, 'b> std::iter::Iterator for HunkLineIter<'a, 'b> { + type Item = GitDiffLine<'b>; + + fn next(&mut self) -> Option { + if self.line_index >= self.patch.num_lines_in_hunk(self.hunk_index).unwrap() { + return None; + } + + let line_index = self.line_index; + self.line_index += 1; + Some( + self.patch + .line_in_hunk(self.hunk_index, line_index) + .unwrap(), + ) + } +} + #[derive(Clone)] pub struct BufferDiffSnapshot { tree: SumTree>, @@ -94,8 +145,6 @@ impl BufferDiffSnapshot { query_row_range: Range, buffer: &'a BufferSnapshot, ) -> impl 'a + Iterator> { - // println!("{} hunks overall", self.tree.iter().count()); - self.tree.iter().filter_map(move |hunk| { let range = hunk.buffer_range.to_point(&buffer); @@ -108,7 +157,7 @@ impl BufferDiffSnapshot { Some(DiffHunk { buffer_range: range.start.row..end_row, - head_range: hunk.head_range.clone(), + head_byte_range: hunk.head_byte_range.clone(), }) } else { None @@ -129,18 +178,32 @@ pub struct BufferDiff { impl BufferDiff { pub fn new(head_text: &Option, buffer: &text::BufferSnapshot) -> BufferDiff { - let mut instance = BufferDiff { - last_update_version: buffer.version().clone(), - snapshot: BufferDiffSnapshot { - tree: SumTree::new(), - }, - }; + let mut tree = SumTree::new(); if let Some(head_text) = head_text { - instance.update(head_text, buffer); + let buffer_text = buffer.as_rope().to_string(); + let patch = Self::diff(&head_text, &buffer_text); + + if let Some(patch) = patch { + let mut buffer_divergence = 0; + + for hunk_index in 0..patch.num_hunks() { + let patch = Self::process_patch_hunk( + &mut buffer_divergence, + &patch, + hunk_index, + buffer, + ); + + tree.push(patch, buffer); + } + } } - instance + BufferDiff { + last_update_version: buffer.version().clone(), + snapshot: BufferDiffSnapshot { tree }, + } } pub fn snapshot(&self) -> BufferDiffSnapshot { @@ -148,100 +211,241 @@ impl BufferDiff { } pub fn update(&mut self, head_text: &str, buffer: &text::BufferSnapshot) { - let buffer_string = buffer.as_rope().to_string(); - let buffer_bytes = buffer_string.as_bytes(); + // let buffer_string = buffer.as_rope().to_string(); + // let buffer_bytes = buffer_string.as_bytes(); + // let mut options = GitOptions::default(); + // options.context_lines(0); + // let patch = match GitPatch::from_buffers( + // head_text.as_bytes(), + // None, + // buffer_bytes, + // None, + // Some(&mut options), + // ) { + // Ok(patch) => patch, + // Err(_) => todo!("This needs to be handled"), + // }; + + // let mut hunks = SumTree::>::new(); + // let mut delta = 0i64; + // for hunk_index in 0..patch.num_hunks() { + // for line_index in 0..patch.num_lines_in_hunk(hunk_index).unwrap() { + // let line = patch.line_in_hunk(hunk_index, line_index).unwrap(); + + // let hunk = match line.origin_value() { + // GitDiffLineType::Addition => { + // let buffer_start = line.content_offset(); + // let buffer_end = buffer_start as usize + line.content().len(); + // let head_offset = (buffer_start - delta) as usize; + // delta += line.content().len() as i64; + // DiffHunk { + // buffer_range: buffer.anchor_before(buffer_start as usize) + // ..buffer.anchor_after(buffer_end), + // head_byte_range: head_offset..head_offset, + // } + // } + + // GitDiffLineType::Deletion => { + // let head_start = line.content_offset(); + // let head_end = head_start as usize + line.content().len(); + // let buffer_offset = (head_start + delta) as usize; + // delta -= line.content().len() as i64; + // DiffHunk { + // buffer_range: buffer.anchor_before(buffer_offset) + // ..buffer.anchor_after(buffer_offset), + // head_byte_range: (head_start as usize)..head_end, + // } + // } + + // _ => continue, + // }; + + // let mut combined = false; + // hunks.update_last( + // |last_hunk| { + // if last_hunk.head_byte_range.end == hunk.head_byte_range.start { + // last_hunk.head_byte_range.end = hunk.head_byte_range.end; + // last_hunk.buffer_range.end = hunk.buffer_range.end; + // combined = true; + // } + // }, + // buffer, + // ); + // if !combined { + // hunks.push(hunk, buffer); + // } + // } + // } + + // println!("====="); + // for hunk in hunks.iter() { + // let buffer_range = hunk.buffer_range.to_point(&buffer); + // println!( + // "hunk in buffer range {buffer_range:?}, head slice {:?}", + // &head_text[hunk.head_byte_range.clone()] + // ); + // } + // println!("====="); + + // self.snapshot.tree = hunks; + } + + pub fn actual_update( + &mut self, + head_text: &str, + buffer: &BufferSnapshot, + ) -> Option> { + for edit_range in self.group_edit_ranges(buffer) { + // let patch = self.diff(head, current)?; + } + + None + } + + fn diff<'a>(head: &'a str, current: &'a str) -> Option> { let mut options = GitOptions::default(); options.context_lines(0); - let patch = match GitPatch::from_buffers( - head_text.as_bytes(), + + let patch = GitPatch::from_buffers( + head.as_bytes(), None, - buffer_bytes, + current.as_bytes(), None, Some(&mut options), - ) { - Ok(patch) => patch, - Err(_) => todo!("This needs to be handled"), - }; + ); - let mut hunks = SumTree::>::new(); - let mut delta = 0i64; - for hunk_index in 0..patch.num_hunks() { - for line_index in 0..patch.num_lines_in_hunk(hunk_index).unwrap() { - let line = patch.line_in_hunk(hunk_index, line_index).unwrap(); + match patch { + Ok(patch) => Some(patch), - let hunk = match line.origin_value() { - libgit::DiffLineType::Addition => { - let buffer_start = line.content_offset(); - let buffer_end = buffer_start as usize + line.content().len(); - let head_offset = (buffer_start - delta) as usize; - delta += line.content().len() as i64; - DiffHunk { - buffer_range: buffer.anchor_before(buffer_start as usize) - ..buffer.anchor_after(buffer_end), - head_range: head_offset..head_offset, - } - } + Err(err) => { + log::error!("`GitPatch::from_buffers` failed: {}", err); + None + } + } + } - libgit::DiffLineType::Deletion => { - let head_start = line.content_offset(); - let head_end = head_start as usize + line.content().len(); - let buffer_offset = (head_start + delta) as usize; - delta -= line.content().len() as i64; - DiffHunk { - buffer_range: buffer.anchor_before(buffer_offset) - ..buffer.anchor_after(buffer_offset), - head_range: (head_start as usize)..head_end, - } - } + fn group_edit_ranges(&mut self, buffer: &text::BufferSnapshot) -> Vec> { + const EXPAND_BY: u32 = 20; + const COMBINE_DISTANCE: u32 = 5; - libgit::DiffLineType::AddEOFNL => todo!(), - libgit::DiffLineType::ContextEOFNL => todo!(), - libgit::DiffLineType::DeleteEOFNL => todo!(), + // let mut cursor = self.snapshot.tree.cursor::(); - libgit::DiffLineType::FileHeader => continue, - libgit::DiffLineType::HunkHeader => continue, - libgit::DiffLineType::Binary => continue, + let mut ranges = Vec::>::new(); - //We specifically tell git to not give us context lines - libgit::DiffLineType::Context => unreachable!(), - }; + for edit in buffer.edits_since::(&self.last_update_version) { + let buffer_start = edit.new.start.row.saturating_sub(EXPAND_BY); + let buffer_end = (edit.new.end.row + EXPAND_BY).min(buffer.row_count()); - let mut combined = false; - hunks.update_last( - |last_hunk| { - if last_hunk.head_range.end == hunk.head_range.start { - last_hunk.head_range.end = hunk.head_range.end; - last_hunk.buffer_range.end = hunk.buffer_range.end; - combined = true; - } - }, - buffer, - ); - if !combined { - hunks.push(hunk, buffer); + match ranges.last_mut() { + Some(last_range) if last_range.end.abs_diff(buffer_end) <= COMBINE_DISTANCE => { + last_range.start = last_range.start.min(buffer_start); + last_range.end = last_range.end.max(buffer_end); } + + _ => ranges.push(buffer_start..buffer_end), } } - self.snapshot.tree = hunks; + self.last_update_version = buffer.version().clone(); + ranges } -} -#[derive(Debug, Clone, Copy)] -pub enum GitDiffEdit { - Added(u32), - Modified(u32), - Removed(u32), -} + fn process_patch_hunk<'a>( + buffer_divergence: &mut isize, + patch: &GitPatch<'a>, + hunk_index: usize, + buffer: &text::BufferSnapshot, + ) -> DiffHunk { + let mut buffer_byte_range: Option> = None; + let mut head_byte_range: Option> = None; -impl GitDiffEdit { - pub fn line(self) -> u32 { - use GitDiffEdit::*; + for line_index in 0..patch.num_lines_in_hunk(hunk_index).unwrap() { + let line = patch.line_in_hunk(hunk_index, line_index).unwrap(); + let kind = line.origin_value(); + println!("line index: {line_index}, kind: {kind:?}"); + let content_offset = line.content_offset() as isize; - match self { - Added(line) | Modified(line) | Removed(line) => line, + match (kind, &mut buffer_byte_range, &mut head_byte_range) { + (GitDiffLineType::Addition, None, _) => { + let start = *buffer_divergence + content_offset; + let end = start + line.content().len() as isize; + buffer_byte_range = Some(start as usize..end as usize); + } + + (GitDiffLineType::Addition, Some(buffer_byte_range), _) => { + buffer_byte_range.end = content_offset as usize; + } + + (GitDiffLineType::Deletion, _, None) => { + let end = content_offset + line.content().len() as isize; + head_byte_range = Some(content_offset as usize..end as usize); + } + + (GitDiffLineType::Deletion, _, Some(head_byte_range)) => { + let end = content_offset + line.content().len() as isize; + head_byte_range.end = end as usize; + } + + _ => {} + } } + + //unwrap_or deletion without addition + let buffer_byte_range = buffer_byte_range.unwrap_or(0..0); + //unwrap_or addition without deletion + let head_byte_range = head_byte_range.unwrap_or(0..0); + + *buffer_divergence += buffer_byte_range.len() as isize - head_byte_range.len() as isize; + + DiffHunk { + buffer_range: buffer.anchor_before(buffer_byte_range.start) + ..buffer.anchor_before(buffer_byte_range.end), + head_byte_range, + } + } + + fn name() { + // if self.hunk_index >= self.patch.num_hunks() { + // return None; + // } + + // let mut line_iter = HunkLineIter::new(&self.patch, self.hunk_index); + // let line = line_iter.find(|line| { + // matches!( + // line.origin_value(), + // GitDiffLineType::Addition | GitDiffLineType::Deletion + // ) + // })?; + + // //For the first line of a hunk the content offset is equally valid for an addition or deletion + // let content_offset = line.content_offset() as usize; + + // let mut buffer_range = content_offset..content_offset; + // let mut head_byte_range = match line.origin_value() { + // GitDiffLineType::Addition => content_offset..content_offset, + // GitDiffLineType::Deletion => content_offset..content_offset + line.content().len(), + // _ => unreachable!(), + // }; + + // for line in line_iter { + // match line.origin_value() { + // GitDiffLineType::Addition => { + // // buffer_range.end = + // } + + // GitDiffLineType::Deletion => {} + + // _ => continue, + // } + // } + + // self.hunk_index += 1; + // Some(DiffHunk { + // buffer_range: buffer.anchor_before(buffer_range.start) + // ..buffer.anchor_before(buffer_range.end), + // head_byte_range, + // }) } } @@ -293,7 +497,7 @@ mod tests { for ((index, hunk), (expected_range, expected_str)) in diff_iter.zip(expected_hunks) { assert_eq!(&hunk.buffer_range, expected_range, "for hunk {index}"); assert_eq!( - &head_text[hunk.head_range.clone()], + &head_text[hunk.head_byte_range.clone()], *expected_str, "for hunk {index}" ); From 2f7283fd13e111452dd63b3c3bb5599ff90a2d5e Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 14 Sep 2022 11:18:33 -0400 Subject: [PATCH 055/314] buffer_divergence doesn't seem to be a concept that needs to be tracked --- crates/language/src/git.rs | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/crates/language/src/git.rs b/crates/language/src/git.rs index 642ed5f297..2ac0400da8 100644 --- a/crates/language/src/git.rs +++ b/crates/language/src/git.rs @@ -185,16 +185,8 @@ impl BufferDiff { let patch = Self::diff(&head_text, &buffer_text); if let Some(patch) = patch { - let mut buffer_divergence = 0; - for hunk_index in 0..patch.num_hunks() { - let patch = Self::process_patch_hunk( - &mut buffer_divergence, - &patch, - hunk_index, - buffer, - ); - + let patch = Self::process_patch_hunk(&patch, hunk_index, buffer); tree.push(patch, buffer); } } @@ -352,7 +344,6 @@ impl BufferDiff { } fn process_patch_hunk<'a>( - buffer_divergence: &mut isize, patch: &GitPatch<'a>, hunk_index: usize, buffer: &text::BufferSnapshot, @@ -363,18 +354,17 @@ impl BufferDiff { for line_index in 0..patch.num_lines_in_hunk(hunk_index).unwrap() { let line = patch.line_in_hunk(hunk_index, line_index).unwrap(); let kind = line.origin_value(); - println!("line index: {line_index}, kind: {kind:?}"); let content_offset = line.content_offset() as isize; match (kind, &mut buffer_byte_range, &mut head_byte_range) { (GitDiffLineType::Addition, None, _) => { - let start = *buffer_divergence + content_offset; - let end = start + line.content().len() as isize; - buffer_byte_range = Some(start as usize..end as usize); + let end = content_offset + line.content().len() as isize; + buffer_byte_range = Some(content_offset as usize..end as usize); } (GitDiffLineType::Addition, Some(buffer_byte_range), _) => { - buffer_byte_range.end = content_offset as usize; + let end = content_offset + line.content().len() as isize; + buffer_byte_range.end = end as usize; } (GitDiffLineType::Deletion, _, None) => { @@ -396,8 +386,6 @@ impl BufferDiff { //unwrap_or addition without deletion let head_byte_range = head_byte_range.unwrap_or(0..0); - *buffer_divergence += buffer_byte_range.len() as isize - head_byte_range.len() as isize; - DiffHunk { buffer_range: buffer.anchor_before(buffer_byte_range.start) ..buffer.anchor_before(buffer_byte_range.end), From 96917a8007018c08a373a8fc09608abc73cb3c1d Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 14 Sep 2022 11:20:01 -0400 Subject: [PATCH 056/314] Small clean --- crates/language/src/git.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/language/src/git.rs b/crates/language/src/git.rs index 2ac0400da8..361a44f377 100644 --- a/crates/language/src/git.rs +++ b/crates/language/src/git.rs @@ -355,25 +355,26 @@ impl BufferDiff { let line = patch.line_in_hunk(hunk_index, line_index).unwrap(); let kind = line.origin_value(); let content_offset = line.content_offset() as isize; + let content_len = line.content().len() as isize; match (kind, &mut buffer_byte_range, &mut head_byte_range) { (GitDiffLineType::Addition, None, _) => { - let end = content_offset + line.content().len() as isize; + let end = content_offset + content_len; buffer_byte_range = Some(content_offset as usize..end as usize); } (GitDiffLineType::Addition, Some(buffer_byte_range), _) => { - let end = content_offset + line.content().len() as isize; + let end = content_offset + content_len; buffer_byte_range.end = end as usize; } (GitDiffLineType::Deletion, _, None) => { - let end = content_offset + line.content().len() as isize; + let end = content_offset + content_len; head_byte_range = Some(content_offset as usize..end as usize); } (GitDiffLineType::Deletion, _, Some(head_byte_range)) => { - let end = content_offset + line.content().len() as isize; + let end = content_offset + content_len; head_byte_range.end = end as usize; } From c1249a3d84195869cbff0cfecaeff4ab8b0b2a52 Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 14 Sep 2022 12:38:23 -0400 Subject: [PATCH 057/314] Handle deletions more robustly and correctly --- crates/editor/src/element.rs | 4 ++-- crates/language/src/git.rs | 31 ++++++++++++++++++++++++------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 4ee14407b8..3a5166e17e 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -552,10 +552,10 @@ impl EditorElement { //TODO: This rendering is entirely a horrible hack DiffHunkStatus::Removed => { - let row_above = hunk.buffer_range.start; + let row = hunk.buffer_range.start as i64 - 1; let offset = line_height / 2.; - let start_y = row_above as f32 * line_height + offset - scroll_top; + let start_y = row as f32 * line_height + offset - scroll_top; let end_y = start_y + line_height; let width = 0.4 * line_height; diff --git a/crates/language/src/git.rs b/crates/language/src/git.rs index 361a44f377..f2adee42fa 100644 --- a/crates/language/src/git.rs +++ b/crates/language/src/git.rs @@ -1,8 +1,7 @@ use std::ops::Range; -use client::proto::create_buffer_for_peer; use sum_tree::{Bias, SumTree}; -use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point, Rope, ToPoint}; +use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point, Rope, ToOffset, ToPoint}; pub use git2 as libgit; use libgit::{ @@ -148,7 +147,7 @@ impl BufferDiffSnapshot { self.tree.iter().filter_map(move |hunk| { let range = hunk.buffer_range.to_point(&buffer); - if range.start.row < query_row_range.end && query_row_range.start < range.end.row { + if range.start.row <= query_row_range.end && query_row_range.start <= range.end.row { let end_row = if range.end.column > 0 { range.end.row + 1 } else { @@ -186,8 +185,8 @@ impl BufferDiff { if let Some(patch) = patch { for hunk_index in 0..patch.num_hunks() { - let patch = Self::process_patch_hunk(&patch, hunk_index, buffer); - tree.push(patch, buffer); + let hunk = Self::process_patch_hunk(&patch, hunk_index, buffer); + tree.push(hunk, buffer); } } } @@ -348,10 +347,14 @@ impl BufferDiff { hunk_index: usize, buffer: &text::BufferSnapshot, ) -> DiffHunk { + let line_item_count = patch.num_lines_in_hunk(hunk_index).unwrap(); + assert!(line_item_count > 0); + + let mut first_deletion_buffer_row: Option = None; let mut buffer_byte_range: Option> = None; let mut head_byte_range: Option> = None; - for line_index in 0..patch.num_lines_in_hunk(hunk_index).unwrap() { + for line_index in 0..line_item_count { let line = patch.line_in_hunk(hunk_index, line_index).unwrap(); let kind = line.origin_value(); let content_offset = line.content_offset() as isize; @@ -380,10 +383,24 @@ impl BufferDiff { _ => {} } + + if kind == GitDiffLineType::Deletion && first_deletion_buffer_row.is_none() { + //old_lineno is guarenteed to be Some for deletions + //libgit gives us line numbers that are 1-indexed but also returns a 0 for some states + let row = line.old_lineno().unwrap().saturating_sub(1); + first_deletion_buffer_row = Some(row); + } } //unwrap_or deletion without addition - let buffer_byte_range = buffer_byte_range.unwrap_or(0..0); + let buffer_byte_range = buffer_byte_range.unwrap_or_else(|| { + //we cannot have an addition-less hunk without deletion(s) or else there would be no hunk + let row = first_deletion_buffer_row.unwrap(); + let anchor = buffer.anchor_before(Point::new(row, 0)); + let offset = anchor.to_offset(buffer); + offset..offset + }); + //unwrap_or addition without deletion let head_byte_range = head_byte_range.unwrap_or(0..0); From e72e132ce29588f7cfa712c013a209fa5e19af75 Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 14 Sep 2022 18:44:00 -0400 Subject: [PATCH 058/314] Clear out commented code & once again perform full file diff on update --- crates/language/src/git.rs | 142 +++---------------------------------- 1 file changed, 11 insertions(+), 131 deletions(-) diff --git a/crates/language/src/git.rs b/crates/language/src/git.rs index f2adee42fa..02cf3ca141 100644 --- a/crates/language/src/git.rs +++ b/crates/language/src/git.rs @@ -202,96 +202,20 @@ impl BufferDiff { } pub fn update(&mut self, head_text: &str, buffer: &text::BufferSnapshot) { - // let buffer_string = buffer.as_rope().to_string(); - // let buffer_bytes = buffer_string.as_bytes(); + let mut tree = SumTree::new(); - // let mut options = GitOptions::default(); - // options.context_lines(0); - // let patch = match GitPatch::from_buffers( - // head_text.as_bytes(), - // None, - // buffer_bytes, - // None, - // Some(&mut options), - // ) { - // Ok(patch) => patch, - // Err(_) => todo!("This needs to be handled"), - // }; + let buffer_text = buffer.as_rope().to_string(); + let patch = Self::diff(&head_text, &buffer_text); - // let mut hunks = SumTree::>::new(); - // let mut delta = 0i64; - // for hunk_index in 0..patch.num_hunks() { - // for line_index in 0..patch.num_lines_in_hunk(hunk_index).unwrap() { - // let line = patch.line_in_hunk(hunk_index, line_index).unwrap(); - - // let hunk = match line.origin_value() { - // GitDiffLineType::Addition => { - // let buffer_start = line.content_offset(); - // let buffer_end = buffer_start as usize + line.content().len(); - // let head_offset = (buffer_start - delta) as usize; - // delta += line.content().len() as i64; - // DiffHunk { - // buffer_range: buffer.anchor_before(buffer_start as usize) - // ..buffer.anchor_after(buffer_end), - // head_byte_range: head_offset..head_offset, - // } - // } - - // GitDiffLineType::Deletion => { - // let head_start = line.content_offset(); - // let head_end = head_start as usize + line.content().len(); - // let buffer_offset = (head_start + delta) as usize; - // delta -= line.content().len() as i64; - // DiffHunk { - // buffer_range: buffer.anchor_before(buffer_offset) - // ..buffer.anchor_after(buffer_offset), - // head_byte_range: (head_start as usize)..head_end, - // } - // } - - // _ => continue, - // }; - - // let mut combined = false; - // hunks.update_last( - // |last_hunk| { - // if last_hunk.head_byte_range.end == hunk.head_byte_range.start { - // last_hunk.head_byte_range.end = hunk.head_byte_range.end; - // last_hunk.buffer_range.end = hunk.buffer_range.end; - // combined = true; - // } - // }, - // buffer, - // ); - // if !combined { - // hunks.push(hunk, buffer); - // } - // } - // } - - // println!("====="); - // for hunk in hunks.iter() { - // let buffer_range = hunk.buffer_range.to_point(&buffer); - // println!( - // "hunk in buffer range {buffer_range:?}, head slice {:?}", - // &head_text[hunk.head_byte_range.clone()] - // ); - // } - // println!("====="); - - // self.snapshot.tree = hunks; - } - - pub fn actual_update( - &mut self, - head_text: &str, - buffer: &BufferSnapshot, - ) -> Option> { - for edit_range in self.group_edit_ranges(buffer) { - // let patch = self.diff(head, current)?; + if let Some(patch) = patch { + for hunk_index in 0..patch.num_hunks() { + let hunk = Self::process_patch_hunk(&patch, hunk_index, buffer); + tree.push(hunk, buffer); + } } - None + self.last_update_version = buffer.version().clone(); + self.snapshot.tree = tree; } fn diff<'a>(head: &'a str, current: &'a str) -> Option> { @@ -316,7 +240,7 @@ impl BufferDiff { } } - fn group_edit_ranges(&mut self, buffer: &text::BufferSnapshot) -> Vec> { + fn group_edit_ranges(&self, buffer: &text::BufferSnapshot) -> Vec> { const EXPAND_BY: u32 = 20; const COMBINE_DISTANCE: u32 = 5; @@ -338,7 +262,6 @@ impl BufferDiff { } } - self.last_update_version = buffer.version().clone(); ranges } @@ -410,49 +333,6 @@ impl BufferDiff { head_byte_range, } } - - fn name() { - // if self.hunk_index >= self.patch.num_hunks() { - // return None; - // } - - // let mut line_iter = HunkLineIter::new(&self.patch, self.hunk_index); - // let line = line_iter.find(|line| { - // matches!( - // line.origin_value(), - // GitDiffLineType::Addition | GitDiffLineType::Deletion - // ) - // })?; - - // //For the first line of a hunk the content offset is equally valid for an addition or deletion - // let content_offset = line.content_offset() as usize; - - // let mut buffer_range = content_offset..content_offset; - // let mut head_byte_range = match line.origin_value() { - // GitDiffLineType::Addition => content_offset..content_offset, - // GitDiffLineType::Deletion => content_offset..content_offset + line.content().len(), - // _ => unreachable!(), - // }; - - // for line in line_iter { - // match line.origin_value() { - // GitDiffLineType::Addition => { - // // buffer_range.end = - // } - - // GitDiffLineType::Deletion => {} - - // _ => continue, - // } - // } - - // self.hunk_index += 1; - // Some(DiffHunk { - // buffer_range: buffer.anchor_before(buffer_range.start) - // ..buffer.anchor_before(buffer_range.end), - // head_byte_range, - // }) - } } #[cfg(test)] From 03b6f3e0bf2628b238a8e34f40242ddc387a62ca Mon Sep 17 00:00:00 2001 From: Julia Date: Thu, 15 Sep 2022 13:57:57 -0400 Subject: [PATCH 059/314] Reorganize for for purely file level invalidation --- crates/language/src/git.rs | 91 +++++--------------------------------- 1 file changed, 10 insertions(+), 81 deletions(-) diff --git a/crates/language/src/git.rs b/crates/language/src/git.rs index 02cf3ca141..c3f43e54e1 100644 --- a/crates/language/src/git.rs +++ b/crates/language/src/git.rs @@ -1,13 +1,10 @@ use std::ops::Range; -use sum_tree::{Bias, SumTree}; -use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point, Rope, ToOffset, ToPoint}; +use sum_tree::SumTree; +use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point, ToOffset, ToPoint}; pub use git2 as libgit; -use libgit::{ - DiffLine as GitDiffLine, DiffLineType as GitDiffLineType, DiffOptions as GitOptions, - Patch as GitPatch, -}; +use libgit::{DiffLineType as GitDiffLineType, DiffOptions as GitOptions, Patch as GitPatch}; #[derive(Debug, Clone, Copy)] pub enum DiffHunkStatus { @@ -99,40 +96,6 @@ impl<'a> sum_tree::Dimension<'a, DiffHunkSummary> for HunkBufferEnd { } } -struct HunkLineIter<'a, 'b> { - patch: &'a GitPatch<'b>, - hunk_index: usize, - line_index: usize, -} - -impl<'a, 'b> HunkLineIter<'a, 'b> { - fn new(patch: &'a GitPatch<'b>, hunk_index: usize) -> Self { - HunkLineIter { - patch, - hunk_index, - line_index: 0, - } - } -} - -impl<'a, 'b> std::iter::Iterator for HunkLineIter<'a, 'b> { - type Item = GitDiffLine<'b>; - - fn next(&mut self) -> Option { - if self.line_index >= self.patch.num_lines_in_hunk(self.hunk_index).unwrap() { - return None; - } - - let line_index = self.line_index; - self.line_index += 1; - Some( - self.patch - .line_in_hunk(self.hunk_index, line_index) - .unwrap(), - ) - } -} - #[derive(Clone)] pub struct BufferDiffSnapshot { tree: SumTree>, @@ -171,30 +134,22 @@ impl BufferDiffSnapshot { } pub struct BufferDiff { - last_update_version: clock::Global, snapshot: BufferDiffSnapshot, } impl BufferDiff { pub fn new(head_text: &Option, buffer: &text::BufferSnapshot) -> BufferDiff { - let mut tree = SumTree::new(); + let mut instance = BufferDiff { + snapshot: BufferDiffSnapshot { + tree: SumTree::new(), + }, + }; if let Some(head_text) = head_text { - let buffer_text = buffer.as_rope().to_string(); - let patch = Self::diff(&head_text, &buffer_text); - - if let Some(patch) = patch { - for hunk_index in 0..patch.num_hunks() { - let hunk = Self::process_patch_hunk(&patch, hunk_index, buffer); - tree.push(hunk, buffer); - } - } + instance.update(head_text, buffer); } - BufferDiff { - last_update_version: buffer.version().clone(), - snapshot: BufferDiffSnapshot { tree }, - } + instance } pub fn snapshot(&self) -> BufferDiffSnapshot { @@ -214,7 +169,6 @@ impl BufferDiff { } } - self.last_update_version = buffer.version().clone(); self.snapshot.tree = tree; } @@ -240,31 +194,6 @@ impl BufferDiff { } } - fn group_edit_ranges(&self, buffer: &text::BufferSnapshot) -> Vec> { - const EXPAND_BY: u32 = 20; - const COMBINE_DISTANCE: u32 = 5; - - // let mut cursor = self.snapshot.tree.cursor::(); - - let mut ranges = Vec::>::new(); - - for edit in buffer.edits_since::(&self.last_update_version) { - let buffer_start = edit.new.start.row.saturating_sub(EXPAND_BY); - let buffer_end = (edit.new.end.row + EXPAND_BY).min(buffer.row_count()); - - match ranges.last_mut() { - Some(last_range) if last_range.end.abs_diff(buffer_end) <= COMBINE_DISTANCE => { - last_range.start = last_range.start.min(buffer_start); - last_range.end = last_range.end.max(buffer_end); - } - - _ => ranges.push(buffer_start..buffer_end), - } - } - - ranges - } - fn process_patch_hunk<'a>( patch: &GitPatch<'a>, hunk_index: usize, From 446bf886555c52d9871f7214b35cd5fc1455e6ac Mon Sep 17 00:00:00 2001 From: Julia Date: Thu, 15 Sep 2022 16:17:17 -0400 Subject: [PATCH 060/314] Use row range while building buffer range during diff line iteration --- crates/language/src/git.rs | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/crates/language/src/git.rs b/crates/language/src/git.rs index c3f43e54e1..040121fcf2 100644 --- a/crates/language/src/git.rs +++ b/crates/language/src/git.rs @@ -1,7 +1,7 @@ use std::ops::Range; use sum_tree::SumTree; -use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point, ToOffset, ToPoint}; +use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point, ToPoint}; pub use git2 as libgit; use libgit::{DiffLineType as GitDiffLineType, DiffOptions as GitOptions, Patch as GitPatch}; @@ -203,7 +203,7 @@ impl BufferDiff { assert!(line_item_count > 0); let mut first_deletion_buffer_row: Option = None; - let mut buffer_byte_range: Option> = None; + let mut buffer_row_range: Option> = None; let mut head_byte_range: Option> = None; for line_index in 0..line_item_count { @@ -212,15 +212,16 @@ impl BufferDiff { let content_offset = line.content_offset() as isize; let content_len = line.content().len() as isize; - match (kind, &mut buffer_byte_range, &mut head_byte_range) { + match (kind, &mut buffer_row_range, &mut head_byte_range) { (GitDiffLineType::Addition, None, _) => { - let end = content_offset + content_len; - buffer_byte_range = Some(content_offset as usize..end as usize); + //guarenteed to be present for additions + let row = line.new_lineno().unwrap().saturating_sub(1); + buffer_row_range = Some(row..row + 1); } (GitDiffLineType::Addition, Some(buffer_byte_range), _) => { - let end = content_offset + content_len; - buffer_byte_range.end = end as usize; + let row = line.new_lineno().unwrap().saturating_sub(1); + buffer_byte_range.end = row + 1; } (GitDiffLineType::Deletion, _, None) => { @@ -245,20 +246,20 @@ impl BufferDiff { } //unwrap_or deletion without addition - let buffer_byte_range = buffer_byte_range.unwrap_or_else(|| { + let buffer_byte_range = buffer_row_range.unwrap_or_else(|| { //we cannot have an addition-less hunk without deletion(s) or else there would be no hunk let row = first_deletion_buffer_row.unwrap(); - let anchor = buffer.anchor_before(Point::new(row, 0)); - let offset = anchor.to_offset(buffer); - offset..offset + row..row }); //unwrap_or addition without deletion let head_byte_range = head_byte_range.unwrap_or(0..0); + let start = Point::new(buffer_byte_range.start, 0); + let end = Point::new(buffer_byte_range.end, 0); + let buffer_range = buffer.anchor_before(start)..buffer.anchor_before(end); DiffHunk { - buffer_range: buffer.anchor_before(buffer_byte_range.start) - ..buffer.anchor_before(buffer_byte_range.end), + buffer_range, head_byte_range, } } From b9d84df1274a6a75f17715d6b5c32870e6edb2d6 Mon Sep 17 00:00:00 2001 From: Julia Date: Thu, 15 Sep 2022 17:44:01 -0400 Subject: [PATCH 061/314] Track buffer row divergence while iterating through diff lines This allows for offsetting head row index of deleted lines to normalize into buffer row space --- crates/editor/src/element.rs | 2 +- crates/language/src/git.rs | 56 +++++++++++++++++------------------- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 3a5166e17e..40b1e62adf 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -552,7 +552,7 @@ impl EditorElement { //TODO: This rendering is entirely a horrible hack DiffHunkStatus::Removed => { - let row = hunk.buffer_range.start as i64 - 1; + let row = hunk.buffer_range.start; let offset = line_height / 2.; let start_y = row as f32 * line_height + offset - scroll_top; diff --git a/crates/language/src/git.rs b/crates/language/src/git.rs index 040121fcf2..9065ef5606 100644 --- a/crates/language/src/git.rs +++ b/crates/language/src/git.rs @@ -163,8 +163,9 @@ impl BufferDiff { let patch = Self::diff(&head_text, &buffer_text); if let Some(patch) = patch { + let mut divergence = 0; for hunk_index in 0..patch.num_hunks() { - let hunk = Self::process_patch_hunk(&patch, hunk_index, buffer); + let hunk = Self::process_patch_hunk(&patch, hunk_index, buffer, &mut divergence); tree.push(hunk, buffer); } } @@ -198,6 +199,7 @@ impl BufferDiff { patch: &GitPatch<'a>, hunk_index: usize, buffer: &text::BufferSnapshot, + buffer_row_divergence: &mut i64, ) -> DiffHunk { let line_item_count = patch.num_lines_in_hunk(hunk_index).unwrap(); assert!(line_item_count > 0); @@ -212,41 +214,35 @@ impl BufferDiff { let content_offset = line.content_offset() as isize; let content_len = line.content().len() as isize; - match (kind, &mut buffer_row_range, &mut head_byte_range) { - (GitDiffLineType::Addition, None, _) => { - //guarenteed to be present for additions - let row = line.new_lineno().unwrap().saturating_sub(1); - buffer_row_range = Some(row..row + 1); - } + if kind == GitDiffLineType::Addition { + *buffer_row_divergence += 1; + let row = line.new_lineno().unwrap().saturating_sub(1); - (GitDiffLineType::Addition, Some(buffer_byte_range), _) => { - let row = line.new_lineno().unwrap().saturating_sub(1); - buffer_byte_range.end = row + 1; + match &mut buffer_row_range { + Some(buffer_row_range) => buffer_row_range.end = row + 1, + None => buffer_row_range = Some(row..row + 1), } - - (GitDiffLineType::Deletion, _, None) => { - let end = content_offset + content_len; - head_byte_range = Some(content_offset as usize..end as usize); - } - - (GitDiffLineType::Deletion, _, Some(head_byte_range)) => { - let end = content_offset + content_len; - head_byte_range.end = end as usize; - } - - _ => {} } - if kind == GitDiffLineType::Deletion && first_deletion_buffer_row.is_none() { - //old_lineno is guarenteed to be Some for deletions - //libgit gives us line numbers that are 1-indexed but also returns a 0 for some states - let row = line.old_lineno().unwrap().saturating_sub(1); - first_deletion_buffer_row = Some(row); + if kind == GitDiffLineType::Deletion { + *buffer_row_divergence -= 1; + let end = content_offset + content_len; + + match &mut head_byte_range { + Some(head_byte_range) => head_byte_range.end = end as usize, + None => head_byte_range = Some(content_offset as usize..end as usize), + } + + if first_deletion_buffer_row.is_none() { + let old_row = line.old_lineno().unwrap().saturating_sub(1); + let row = old_row as i64 + *buffer_row_divergence; + first_deletion_buffer_row = Some(row as u32); + } } } //unwrap_or deletion without addition - let buffer_byte_range = buffer_row_range.unwrap_or_else(|| { + let buffer_row_range = buffer_row_range.unwrap_or_else(|| { //we cannot have an addition-less hunk without deletion(s) or else there would be no hunk let row = first_deletion_buffer_row.unwrap(); row..row @@ -255,8 +251,8 @@ impl BufferDiff { //unwrap_or addition without deletion let head_byte_range = head_byte_range.unwrap_or(0..0); - let start = Point::new(buffer_byte_range.start, 0); - let end = Point::new(buffer_byte_range.end, 0); + let start = Point::new(buffer_row_range.start, 0); + let end = Point::new(buffer_row_range.end, 0); let buffer_range = buffer.anchor_before(start)..buffer.anchor_before(end); DiffHunk { buffer_range, From c4da8c46f70ba8e3c2e475547e3c97bba785dec4 Mon Sep 17 00:00:00 2001 From: Julia Date: Thu, 15 Sep 2022 18:50:31 -0400 Subject: [PATCH 062/314] Disable unnecessary libgit2 cargo features Co-Authored-By: Mikayla Maki --- Cargo.lock | 18 ------------------ crates/language/Cargo.toml | 2 +- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2872d83a94..1f60f1d36c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2234,8 +2234,6 @@ dependencies = [ "libc", "libgit2-sys", "log", - "openssl-probe", - "openssl-sys", "url", ] @@ -2918,9 +2916,7 @@ checksum = "47a00859c70c8a4f7218e6d1cc32875c4b55f6799445b842b0d8ed5e4c3d959b" dependencies = [ "cc", "libc", - "libssh2-sys", "libz-sys", - "openssl-sys", "pkg-config", ] @@ -2964,20 +2960,6 @@ dependencies = [ "zstd-sys", ] -[[package]] -name = "libssh2-sys" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b094a36eb4b8b8c8a7b4b8ae43b2944502be3e59cd87687595cf6b0a71b3f4ca" -dependencies = [ - "cc", - "libc", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", -] - [[package]] name = "libz-sys" version = "1.1.8" diff --git a/crates/language/Cargo.toml b/crates/language/Cargo.toml index 6d347f3595..034b10e89c 100644 --- a/crates/language/Cargo.toml +++ b/crates/language/Cargo.toml @@ -51,7 +51,7 @@ smol = "1.2" tree-sitter = "0.20" tree-sitter-rust = { version = "*", optional = true } tree-sitter-typescript = { version = "*", optional = true } -git2 = "0.15" +git2 = { version = "0.15", default-features = false } [dev-dependencies] client = { path = "../client", features = ["test-support"] } From 9c8295487752b7982a0277a2b949abd98e81e004 Mon Sep 17 00:00:00 2001 From: Julia Date: Thu, 15 Sep 2022 19:06:45 -0400 Subject: [PATCH 063/314] Changed diffs to be async and dropped git delay --- crates/editor/src/multi_buffer.rs | 12 +++--- crates/language/src/buffer.rs | 44 +++++++++++++++------ crates/language/src/git.rs | 63 ++++++++++++++----------------- crates/workspace/src/workspace.rs | 2 +- 4 files changed, 70 insertions(+), 51 deletions(-) diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 1d09b7008f..72b88f837d 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -313,11 +313,13 @@ impl MultiBuffer { } pub fn update_git(&mut self, cx: &mut ModelContext) { - let mut buffers = self.buffers.borrow_mut(); - for buffer in buffers.values_mut() { - buffer.buffer.update(cx, |buffer, _| { - buffer.update_git(); - }) + let buffers = self.buffers.borrow(); + for buffer_state in buffers.values() { + if buffer_state.buffer.read(cx).needs_git_update() { + buffer_state + .buffer + .update(cx, |buffer, cx| buffer.update_git(cx)) + } } } diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index e75e17e541..2cff3796bc 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1,4 +1,4 @@ -use crate::git::{BufferDiff, BufferDiffSnapshot, DiffHunk}; +use crate::git::{BufferDiff, DiffHunk}; pub use crate::{ diagnostic_set::DiagnosticSet, highlight_map::{HighlightId, HighlightMap}, @@ -48,7 +48,7 @@ pub use lsp::DiagnosticSeverity; pub struct Buffer { text: TextBuffer, - head_text: Option, + head_text: Option>, git_diff: BufferDiff, file: Option>, saved_version: clock::Global, @@ -77,7 +77,7 @@ pub struct Buffer { pub struct BufferSnapshot { text: text::BufferSnapshot, - pub diff_snapshot: BufferDiffSnapshot, + pub diff_snapshot: BufferDiff, pub(crate) syntax: SyntaxSnapshot, file: Option>, diagnostics: DiagnosticSet, @@ -347,7 +347,7 @@ impl Buffer { ) -> Self { Self::build( TextBuffer::new(replica_id, cx.model_id() as u64, base_text.into()), - head_text.map(|h| h.into()), + head_text.map(|h| Arc::new(h.into())), Some(file), ) } @@ -358,7 +358,7 @@ impl Buffer { file: Option>, ) -> Result { let buffer = TextBuffer::new(replica_id, message.id, message.base_text); - let mut this = Self::build(buffer, message.head_text, file); + let mut this = Self::build(buffer, message.head_text.map(|text| Arc::new(text)), file); this.text.set_line_ending(proto::deserialize_line_ending( proto::LineEnding::from_i32(message.line_ending) .ok_or_else(|| anyhow!("missing line_ending"))?, @@ -414,14 +414,18 @@ impl Buffer { self } - fn build(buffer: TextBuffer, head_text: Option, file: Option>) -> Self { + fn build( + buffer: TextBuffer, + head_text: Option>, + file: Option>, + ) -> Self { let saved_mtime = if let Some(file) = file.as_ref() { file.mtime() } else { UNIX_EPOCH }; - let git_diff = BufferDiff::new(&head_text, &buffer); + let git_diff = smol::block_on(BufferDiff::new(head_text.clone(), &buffer)); Self { saved_mtime, @@ -462,7 +466,7 @@ impl Buffer { BufferSnapshot { text, syntax, - diff_snapshot: self.git_diff.snapshot(), + diff_snapshot: self.git_diff.clone(), file: self.file.clone(), remote_selections: self.remote_selections.clone(), diagnostics: self.diagnostics.clone(), @@ -650,11 +654,29 @@ impl Buffer { task } - pub fn update_git(&mut self) { - if let Some(head_text) = &self.head_text { + pub fn needs_git_update(&self) -> bool { + self.git_diff.needs_update(self) + } + + pub fn update_git(&mut self, cx: &mut ModelContext) { + if self.head_text.is_some() { let snapshot = self.snapshot(); - self.git_diff.update(head_text, &snapshot); + let head_text = self.head_text.clone(); self.diff_update_count += 1; + + let buffer_diff = cx + .background() + .spawn(async move { BufferDiff::new(head_text, &snapshot).await }); + + cx.spawn_weak(|this, mut cx| async move { + let buffer_diff = buffer_diff.await; + if let Some(this) = this.upgrade(&cx) { + this.update(&mut cx, |this, _| { + this.git_diff = buffer_diff; + }) + } + }) + .detach() } } diff --git a/crates/language/src/git.rs b/crates/language/src/git.rs index 9065ef5606..65ac373f7a 100644 --- a/crates/language/src/git.rs +++ b/crates/language/src/git.rs @@ -1,4 +1,4 @@ -use std::ops::Range; +use std::{ops::Range, sync::Arc}; use sum_tree::SumTree; use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point, ToPoint}; @@ -97,11 +97,25 @@ impl<'a> sum_tree::Dimension<'a, DiffHunkSummary> for HunkBufferEnd { } #[derive(Clone)] -pub struct BufferDiffSnapshot { +pub struct BufferDiff { + last_buffer_version: clock::Global, tree: SumTree>, } -impl BufferDiffSnapshot { +impl BufferDiff { + pub async fn new(head_text: Option>, buffer: &text::BufferSnapshot) -> BufferDiff { + let mut instance = BufferDiff { + last_buffer_version: buffer.version().clone(), + tree: SumTree::new(), + }; + + if let Some(head_text) = head_text { + instance.update(&*head_text, buffer); + } + + instance + } + pub fn hunks_in_range<'a>( &'a self, query_row_range: Range, @@ -127,36 +141,11 @@ impl BufferDiffSnapshot { }) } - #[cfg(test)] - fn hunks<'a>(&'a self, text: &'a BufferSnapshot) -> impl 'a + Iterator> { - self.hunks_in_range(0..u32::MAX, text) - } -} - -pub struct BufferDiff { - snapshot: BufferDiffSnapshot, -} - -impl BufferDiff { - pub fn new(head_text: &Option, buffer: &text::BufferSnapshot) -> BufferDiff { - let mut instance = BufferDiff { - snapshot: BufferDiffSnapshot { - tree: SumTree::new(), - }, - }; - - if let Some(head_text) = head_text { - instance.update(head_text, buffer); - } - - instance + pub fn needs_update(&self, buffer: &text::BufferSnapshot) -> bool { + buffer.version().changed_since(&self.last_buffer_version) } - pub fn snapshot(&self) -> BufferDiffSnapshot { - self.snapshot.clone() - } - - pub fn update(&mut self, head_text: &str, buffer: &text::BufferSnapshot) { + fn update(&mut self, head_text: &str, buffer: &text::BufferSnapshot) { let mut tree = SumTree::new(); let buffer_text = buffer.as_rope().to_string(); @@ -170,7 +159,13 @@ impl BufferDiff { } } - self.snapshot.tree = tree; + self.tree = tree; + self.last_buffer_version = buffer.version().clone(); + } + + #[cfg(test)] + fn hunks<'a>(&'a self, text: &'a BufferSnapshot) -> impl 'a + Iterator> { + self.hunks_in_range(0..u32::MAX, text) } fn diff<'a>(head: &'a str, current: &'a str) -> Option> { @@ -284,7 +279,7 @@ mod tests { .unindent(); let mut buffer = Buffer::new(0, 0, buffer_text); - let diff = BufferDiff::new(&Some(head_text.clone()), &buffer); + let diff = smol::block_on(BufferDiff::new(Some(Arc::new(head_text.clone())), &buffer)); assert_hunks(&diff, &buffer, &head_text, &[(1..2, "two\n")]); buffer.edit([(0..0, "point five\n")]); @@ -298,7 +293,7 @@ mod tests { head_text: &str, expected_hunks: &[(Range, &str)], ) { - let hunks = diff.snapshot.hunks(buffer).collect::>(); + let hunks = diff.hunks(buffer).collect::>(); assert_eq!( hunks.len(), expected_hunks.len(), diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index ad3862c56f..e28e4d66d1 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -734,7 +734,7 @@ impl ItemHandle for ViewHandle { ); } - const GIT_DELAY: Duration = Duration::from_millis(600); + const GIT_DELAY: Duration = Duration::from_millis(10); let item = item.clone(); pending_git_update.fire_new( GIT_DELAY, From 6825b6077aa8120f4b5978c64d68fde122b39419 Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 16 Sep 2022 12:20:31 -0400 Subject: [PATCH 064/314] Properly invalidate when async git diff completes --- crates/editor/src/items.rs | 1 - crates/language/src/buffer.rs | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index d208fc9c15..76e1480180 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -486,7 +486,6 @@ impl Item for Editor { self.buffer().update(cx, |multibuffer, cx| { multibuffer.update_git(cx); }); - cx.notify(); Task::ready(Ok(())) } diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 2cff3796bc..5ddebcaff6 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -662,7 +662,6 @@ impl Buffer { if self.head_text.is_some() { let snapshot = self.snapshot(); let head_text = self.head_text.clone(); - self.diff_update_count += 1; let buffer_diff = cx .background() @@ -671,8 +670,10 @@ impl Buffer { cx.spawn_weak(|this, mut cx| async move { let buffer_diff = buffer_diff.await; if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, _| { + this.update(&mut cx, |this, cx| { this.git_diff = buffer_diff; + this.diff_update_count += 1; + cx.notify(); }) } }) From 6633c0b3287484bd7b051f2de5bb49fba8fc6379 Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 16 Sep 2022 14:49:24 -0400 Subject: [PATCH 065/314] Perform initial file load git diff async --- crates/language/src/buffer.rs | 18 +++++++++--------- crates/language/src/git.rs | 28 +++++++++++++--------------- crates/project/src/worktree.rs | 6 +++++- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 5ddebcaff6..90e86a20c4 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -425,8 +425,6 @@ impl Buffer { UNIX_EPOCH }; - let git_diff = smol::block_on(BufferDiff::new(head_text.clone(), &buffer)); - Self { saved_mtime, saved_version: buffer.version(), @@ -435,7 +433,7 @@ impl Buffer { was_dirty_before_starting_transaction: None, text: buffer, head_text, - git_diff, + git_diff: BufferDiff::new(), file, syntax_map: Mutex::new(SyntaxMap::new()), parsing_in_background: false, @@ -659,16 +657,18 @@ impl Buffer { } pub fn update_git(&mut self, cx: &mut ModelContext) { - if self.head_text.is_some() { + if let Some(head_text) = &self.head_text { let snapshot = self.snapshot(); - let head_text = self.head_text.clone(); + let head_text = head_text.clone(); - let buffer_diff = cx - .background() - .spawn(async move { BufferDiff::new(head_text, &snapshot).await }); + let mut diff = self.git_diff.clone(); + let diff = cx.background().spawn(async move { + diff.update(&head_text, &snapshot).await; + diff + }); cx.spawn_weak(|this, mut cx| async move { - let buffer_diff = buffer_diff.await; + let buffer_diff = diff.await; if let Some(this) = this.upgrade(&cx) { this.update(&mut cx, |this, cx| { this.git_diff = buffer_diff; diff --git a/crates/language/src/git.rs b/crates/language/src/git.rs index 65ac373f7a..d713dcbc14 100644 --- a/crates/language/src/git.rs +++ b/crates/language/src/git.rs @@ -1,4 +1,4 @@ -use std::{ops::Range, sync::Arc}; +use std::ops::Range; use sum_tree::SumTree; use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point, ToPoint}; @@ -98,22 +98,16 @@ impl<'a> sum_tree::Dimension<'a, DiffHunkSummary> for HunkBufferEnd { #[derive(Clone)] pub struct BufferDiff { - last_buffer_version: clock::Global, + last_buffer_version: Option, tree: SumTree>, } impl BufferDiff { - pub async fn new(head_text: Option>, buffer: &text::BufferSnapshot) -> BufferDiff { - let mut instance = BufferDiff { - last_buffer_version: buffer.version().clone(), + pub fn new() -> BufferDiff { + BufferDiff { + last_buffer_version: None, tree: SumTree::new(), - }; - - if let Some(head_text) = head_text { - instance.update(&*head_text, buffer); } - - instance } pub fn hunks_in_range<'a>( @@ -142,10 +136,13 @@ impl BufferDiff { } pub fn needs_update(&self, buffer: &text::BufferSnapshot) -> bool { - buffer.version().changed_since(&self.last_buffer_version) + match &self.last_buffer_version { + Some(last) => buffer.version().changed_since(last), + None => true, + } } - fn update(&mut self, head_text: &str, buffer: &text::BufferSnapshot) { + pub async fn update(&mut self, head_text: &str, buffer: &text::BufferSnapshot) { let mut tree = SumTree::new(); let buffer_text = buffer.as_rope().to_string(); @@ -160,7 +157,7 @@ impl BufferDiff { } self.tree = tree; - self.last_buffer_version = buffer.version().clone(); + self.last_buffer_version = Some(buffer.version().clone()); } #[cfg(test)] @@ -279,7 +276,8 @@ mod tests { .unindent(); let mut buffer = Buffer::new(0, 0, buffer_text); - let diff = smol::block_on(BufferDiff::new(Some(Arc::new(head_text.clone())), &buffer)); + let mut diff = BufferDiff::new(); + smol::block_on(diff.update(&head_text, &buffer)); assert_hunks(&diff, &buffer, &head_text, &[(1..2, "two\n")]); buffer.edit([(0..0, "point five\n")]); diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 42d18eb3bb..2ff3e6fe04 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -449,7 +449,11 @@ impl LocalWorktree { let (file, contents, head_text) = this .update(&mut cx, |t, cx| t.as_local().unwrap().load(&path, cx)) .await?; - Ok(cx.add_model(|cx| Buffer::from_file(0, contents, head_text, Arc::new(file), cx))) + Ok(cx.add_model(|cx| { + let mut buffer = Buffer::from_file(0, contents, head_text, Arc::new(file), cx); + buffer.update_git(cx); + buffer + })) }) } From 8edee9b2a8680bc5db074e76eec7a400ca92caf1 Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 16 Sep 2022 16:48:43 -0400 Subject: [PATCH 066/314] Async-ify head text loading --- crates/project/src/worktree.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 2ff3e6fe04..79e2ed9da9 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -573,7 +573,13 @@ impl LocalWorktree { let fs = self.fs.clone(); cx.spawn(|this, mut cx| async move { let text = fs.load(&abs_path).await?; - let head_text = fs.load_head_text(&abs_path).await; + + let head_text = { + let fs = fs.clone(); + let abs_path = abs_path.clone(); + let task = async move { fs.load_head_text(&abs_path).await }; + cx.background().spawn(task).await + }; // Eagerly populate the snapshot with an updated entry for the loaded file let entry = this From b18dd8fcff7a87f46fbc98b247b9e2d3198abc4f Mon Sep 17 00:00:00 2001 From: Julia Date: Mon, 19 Sep 2022 15:16:04 -0400 Subject: [PATCH 067/314] Fully qualify outside git-related code when a diff is a git diff --- crates/editor/src/display_map/fold_map.rs | 2 +- crates/editor/src/element.rs | 2 +- crates/editor/src/multi_buffer.rs | 33 ++++++++++---------- crates/language/src/buffer.rs | 38 +++++++++++------------ 4 files changed, 38 insertions(+), 37 deletions(-) diff --git a/crates/editor/src/display_map/fold_map.rs b/crates/editor/src/display_map/fold_map.rs index 6ab5c6202e..c17cfa39f2 100644 --- a/crates/editor/src/display_map/fold_map.rs +++ b/crates/editor/src/display_map/fold_map.rs @@ -274,7 +274,7 @@ impl FoldMap { if buffer.edit_count() != new_buffer.edit_count() || buffer.parse_count() != new_buffer.parse_count() || buffer.diagnostics_update_count() != new_buffer.diagnostics_update_count() - || buffer.diff_update_count() != new_buffer.diff_update_count() + || buffer.git_diff_update_count() != new_buffer.git_diff_update_count() || buffer.trailing_excerpt_update_count() != new_buffer.trailing_excerpt_update_count() { diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 40b1e62adf..a293514559 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1477,7 +1477,7 @@ impl Element for EditorElement { let diff_hunks = snapshot .buffer_snapshot - .diff_hunks_in_range(start_row..end_row) + .git_diff_hunks_in_range(start_row..end_row) .collect(); let mut max_visible_line_width = 0.0; diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 72b88f837d..2f93bc5b09 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -91,7 +91,7 @@ struct BufferState { last_selections_update_count: usize, last_diagnostics_update_count: usize, last_file_update_count: usize, - last_diff_update_count: usize, + last_git_diff_update_count: usize, excerpts: Vec, _subscriptions: [gpui::Subscription; 2], } @@ -103,7 +103,7 @@ pub struct MultiBufferSnapshot { parse_count: usize, diagnostics_update_count: usize, trailing_excerpt_update_count: usize, - diff_update_count: usize, + git_diff_update_count: usize, edit_count: usize, is_dirty: bool, has_conflict: bool, @@ -205,7 +205,7 @@ impl MultiBuffer { 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_diff_update_count: buffer_state.last_diff_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()), @@ -842,7 +842,7 @@ impl MultiBuffer { 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_diff_update_count: buffer_snapshot.diff_update_count(), + last_git_diff_update_count: buffer_snapshot.git_diff_update_count(), excerpts: Default::default(), _subscriptions: [ cx.observe(&buffer, |_, _, cx| cx.notify()), @@ -1265,7 +1265,7 @@ impl MultiBuffer { let mut excerpts_to_edit = Vec::new(); let mut reparsed = false; let mut diagnostics_updated = false; - let mut diff_updated = false; + let mut git_diff_updated = false; let mut is_dirty = false; let mut has_conflict = false; let mut edited = false; @@ -1277,7 +1277,7 @@ impl MultiBuffer { 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 diff_update_count = buffer.diff_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; @@ -1286,20 +1286,21 @@ impl MultiBuffer { 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_diff_updated = diff_update_count > buffer_state.last_diff_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_diff_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_diff_update_count = diff_update_count; + buffer_state.last_git_diff_update_count = git_diff_update_count; excerpts_to_edit.extend( buffer_state .excerpts @@ -1311,7 +1312,7 @@ impl MultiBuffer { edited |= buffer_edited; reparsed |= buffer_reparsed; diagnostics_updated |= buffer_diagnostics_updated; - diff_updated |= buffer_diff_updated; + git_diff_updated |= buffer_git_diff_updated; is_dirty |= buffer.is_dirty(); has_conflict |= buffer.has_conflict(); } @@ -1324,8 +1325,8 @@ impl MultiBuffer { if diagnostics_updated { snapshot.diagnostics_update_count += 1; } - if diff_updated { - snapshot.diff_update_count += 1; + if git_diff_updated { + snapshot.git_diff_update_count += 1; } snapshot.is_dirty = is_dirty; snapshot.has_conflict = has_conflict; @@ -2504,8 +2505,8 @@ impl MultiBufferSnapshot { self.diagnostics_update_count } - pub fn diff_update_count(&self) -> usize { - self.diff_update_count + pub fn git_diff_update_count(&self) -> usize { + self.git_diff_update_count } pub fn trailing_excerpt_update_count(&self) -> usize { @@ -2558,13 +2559,13 @@ impl MultiBufferSnapshot { }) } - pub fn diff_hunks_in_range<'a>( + pub fn git_diff_hunks_in_range<'a>( &'a self, row_range: Range, ) -> impl 'a + Iterator> { self.as_singleton() .into_iter() - .flat_map(move |(_, _, buffer)| buffer.diff_hunks_in_range(row_range.clone())) + .flat_map(move |(_, _, buffer)| buffer.git_diff_hunks_in_range(row_range.clone())) } pub fn range_for_syntax_ancestor(&self, range: Range) -> Option> { diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 90e86a20c4..53bfe4a10c 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1,4 +1,4 @@ -use crate::git::{BufferDiff, DiffHunk}; +use crate::git; pub use crate::{ diagnostic_set::DiagnosticSet, highlight_map::{HighlightId, HighlightMap}, @@ -49,7 +49,7 @@ pub use lsp::DiagnosticSeverity; pub struct Buffer { text: TextBuffer, head_text: Option>, - git_diff: BufferDiff, + git_diff: git::BufferDiff, file: Option>, saved_version: clock::Global, saved_version_fingerprint: String, @@ -69,7 +69,7 @@ pub struct Buffer { diagnostics_update_count: usize, diagnostics_timestamp: clock::Lamport, file_update_count: usize, - diff_update_count: usize, + git_diff_update_count: usize, completion_triggers: Vec, completion_triggers_timestamp: clock::Lamport, deferred_ops: OperationQueue, @@ -77,13 +77,13 @@ pub struct Buffer { pub struct BufferSnapshot { text: text::BufferSnapshot, - pub diff_snapshot: BufferDiff, + pub git_diff_snapshot: git::BufferDiff, pub(crate) syntax: SyntaxSnapshot, file: Option>, diagnostics: DiagnosticSet, diagnostics_update_count: usize, file_update_count: usize, - diff_update_count: usize, + git_diff_update_count: usize, remote_selections: TreeMap, selections_update_count: usize, language: Option>, @@ -433,7 +433,7 @@ impl Buffer { was_dirty_before_starting_transaction: None, text: buffer, head_text, - git_diff: BufferDiff::new(), + git_diff: git::BufferDiff::new(), file, syntax_map: Mutex::new(SyntaxMap::new()), parsing_in_background: false, @@ -448,7 +448,7 @@ impl Buffer { diagnostics_update_count: 0, diagnostics_timestamp: Default::default(), file_update_count: 0, - diff_update_count: 0, + git_diff_update_count: 0, completion_triggers: Default::default(), completion_triggers_timestamp: Default::default(), deferred_ops: OperationQueue::new(), @@ -464,13 +464,13 @@ impl Buffer { BufferSnapshot { text, syntax, - diff_snapshot: self.git_diff.clone(), + git_diff_snapshot: self.git_diff.clone(), file: self.file.clone(), remote_selections: self.remote_selections.clone(), diagnostics: self.diagnostics.clone(), diagnostics_update_count: self.diagnostics_update_count, file_update_count: self.file_update_count, - diff_update_count: self.diff_update_count, + git_diff_update_count: self.git_diff_update_count, language: self.language.clone(), parse_count: self.parse_count, selections_update_count: self.selections_update_count, @@ -672,7 +672,7 @@ impl Buffer { if let Some(this) = this.upgrade(&cx) { this.update(&mut cx, |this, cx| { this.git_diff = buffer_diff; - this.diff_update_count += 1; + this.git_diff_update_count += 1; cx.notify(); }) } @@ -705,8 +705,8 @@ impl Buffer { self.file_update_count } - pub fn diff_update_count(&self) -> usize { - self.diff_update_count + pub fn git_diff_update_count(&self) -> usize { + self.git_diff_update_count } #[cfg(any(test, feature = "test-support"))] @@ -2191,11 +2191,11 @@ impl BufferSnapshot { }) } - pub fn diff_hunks_in_range<'a>( + pub fn git_diff_hunks_in_range<'a>( &'a self, query_row_range: Range, - ) -> impl 'a + Iterator> { - self.diff_snapshot.hunks_in_range(query_row_range, self) + ) -> impl 'a + Iterator> { + self.git_diff_snapshot.hunks_in_range(query_row_range, self) } pub fn diagnostics_in_range<'a, T, O>( @@ -2246,8 +2246,8 @@ impl BufferSnapshot { self.file_update_count } - pub fn diff_update_count(&self) -> usize { - self.diff_update_count + pub fn git_diff_update_count(&self) -> usize { + self.git_diff_update_count } } @@ -2275,7 +2275,7 @@ impl Clone for BufferSnapshot { fn clone(&self) -> Self { Self { text: self.text.clone(), - diff_snapshot: self.diff_snapshot.clone(), + git_diff_snapshot: self.git_diff_snapshot.clone(), syntax: self.syntax.clone(), file: self.file.clone(), remote_selections: self.remote_selections.clone(), @@ -2283,7 +2283,7 @@ impl Clone for BufferSnapshot { selections_update_count: self.selections_update_count, diagnostics_update_count: self.diagnostics_update_count, file_update_count: self.file_update_count, - diff_update_count: self.diff_update_count, + git_diff_update_count: self.git_diff_update_count, language: self.language.clone(), parse_count: self.parse_count, } From a679557e40f7eafd9aa615d2741a266e9c5987b9 Mon Sep 17 00:00:00 2001 From: Julia Date: Mon, 19 Sep 2022 18:22:39 -0400 Subject: [PATCH 068/314] Avoid racing git diffs & allow for "as fast as possible" diff updating Co-Authored-By: Mikayla Maki --- assets/settings/default.json | 4 +++ crates/language/src/buffer.rs | 38 +++++++++++++++++++------- crates/settings/src/settings.rs | 18 +++++++++++++ crates/workspace/src/workspace.rs | 45 +++++++++++++++++++++++-------- 4 files changed, 85 insertions(+), 20 deletions(-) diff --git a/assets/settings/default.json b/assets/settings/default.json index a12cf44d94..4ebc1e702f 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -74,6 +74,10 @@ "hard_tabs": false, // How many columns a tab should occupy. "tab_size": 4, + // Git gutter behavior configuration. Remove this item to disable git gutters entirely. + "git_gutter": { + "files_included": "all" + }, // Settings specific to the terminal "terminal": { // What shell to use when opening a terminal. May take 3 values: diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 53bfe4a10c..6ecfbc7e62 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -46,10 +46,16 @@ pub use {tree_sitter_rust, tree_sitter_typescript}; pub use lsp::DiagnosticSeverity; +struct GitDiffStatus { + diff: git::BufferDiff, + update_in_progress: bool, + update_requested: bool, +} + pub struct Buffer { text: TextBuffer, head_text: Option>, - git_diff: git::BufferDiff, + git_diff_status: GitDiffStatus, file: Option>, saved_version: clock::Global, saved_version_fingerprint: String, @@ -77,7 +83,7 @@ pub struct Buffer { pub struct BufferSnapshot { text: text::BufferSnapshot, - pub git_diff_snapshot: git::BufferDiff, + pub git_diff: git::BufferDiff, pub(crate) syntax: SyntaxSnapshot, file: Option>, diagnostics: DiagnosticSet, @@ -433,7 +439,11 @@ impl Buffer { was_dirty_before_starting_transaction: None, text: buffer, head_text, - git_diff: git::BufferDiff::new(), + git_diff_status: GitDiffStatus { + diff: git::BufferDiff::new(), + update_in_progress: false, + update_requested: false, + }, file, syntax_map: Mutex::new(SyntaxMap::new()), parsing_in_background: false, @@ -464,7 +474,7 @@ impl Buffer { BufferSnapshot { text, syntax, - git_diff_snapshot: self.git_diff.clone(), + git_diff: self.git_diff_status.diff.clone(), file: self.file.clone(), remote_selections: self.remote_selections.clone(), diagnostics: self.diagnostics.clone(), @@ -653,15 +663,20 @@ impl Buffer { } pub fn needs_git_update(&self) -> bool { - self.git_diff.needs_update(self) + self.git_diff_status.diff.needs_update(self) } pub fn update_git(&mut self, cx: &mut ModelContext) { + if self.git_diff_status.update_in_progress { + self.git_diff_status.update_requested = true; + return; + } + if let Some(head_text) = &self.head_text { let snapshot = self.snapshot(); let head_text = head_text.clone(); - let mut diff = self.git_diff.clone(); + let mut diff = self.git_diff_status.diff.clone(); let diff = cx.background().spawn(async move { diff.update(&head_text, &snapshot).await; diff @@ -671,9 +686,14 @@ impl Buffer { let buffer_diff = diff.await; if let Some(this) = this.upgrade(&cx) { this.update(&mut cx, |this, cx| { - this.git_diff = buffer_diff; + this.git_diff_status.diff = buffer_diff; this.git_diff_update_count += 1; cx.notify(); + + this.git_diff_status.update_in_progress = false; + if this.git_diff_status.update_requested { + this.update_git(cx); + } }) } }) @@ -2195,7 +2215,7 @@ impl BufferSnapshot { &'a self, query_row_range: Range, ) -> impl 'a + Iterator> { - self.git_diff_snapshot.hunks_in_range(query_row_range, self) + self.git_diff.hunks_in_range(query_row_range, self) } pub fn diagnostics_in_range<'a, T, O>( @@ -2275,7 +2295,7 @@ impl Clone for BufferSnapshot { fn clone(&self) -> Self { Self { text: self.text.clone(), - git_diff_snapshot: self.git_diff_snapshot.clone(), + git_diff: self.git_diff.clone(), syntax: self.syntax.clone(), file: self.file.clone(), remote_selections: self.remote_selections.clone(), diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index e346ff60e6..adb2892b36 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -61,6 +61,22 @@ pub struct EditorSettings { pub format_on_save: Option, pub formatter: Option, pub enable_language_server: Option, + pub git_gutter: Option, +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema)] +pub struct GitGutterConfig { + pub files_included: GitGutterLevel, + pub debounce_delay_millis: Option, +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum GitGutterLevel { + #[default] + All, + OnlyTracked, + None, } #[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)] @@ -250,6 +266,7 @@ impl Settings { format_on_save: required(defaults.editor.format_on_save), formatter: required(defaults.editor.formatter), enable_language_server: required(defaults.editor.enable_language_server), + git_gutter: defaults.editor.git_gutter, }, editor_overrides: Default::default(), terminal_defaults: Default::default(), @@ -378,6 +395,7 @@ impl Settings { format_on_save: Some(FormatOnSave::On), formatter: Some(Formatter::LanguageServer), enable_language_server: Some(true), + git_gutter: Default::default(), }, editor_overrides: Default::default(), terminal_defaults: Default::default(), diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index e28e4d66d1..9e8338d289 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -734,18 +734,41 @@ impl ItemHandle for ViewHandle { ); } - const GIT_DELAY: Duration = Duration::from_millis(10); + let debounce_delay = cx + .global::() + .editor_overrides + .git_gutter + .unwrap_or_default() + .debounce_delay_millis; let item = item.clone(); - pending_git_update.fire_new( - GIT_DELAY, - workspace, - cx, - |project, mut cx| async move { - cx.update(|cx| item.update_git(project, cx)) - .await - .log_err(); - }, - ); + + if let Some(delay) = debounce_delay { + const MIN_GIT_DELAY: u64 = 50; + + let delay = delay.max(MIN_GIT_DELAY); + let duration = Duration::from_millis(delay); + + pending_git_update.fire_new( + duration, + workspace, + cx, + |project, mut cx| async move { + cx.update(|cx| item.update_git(project, cx)) + .await + .log_err(); + }, + ); + } else { + let project = workspace.project().downgrade(); + cx.spawn_weak(|_, mut cx| async move { + if let Some(project) = project.upgrade(&cx) { + cx.update(|cx| item.update_git(project, cx)) + .await + .log_err(); + } + }) + .detach(); + } } _ => {} From 632f47930f30e175c81e109c448431b156906600 Mon Sep 17 00:00:00 2001 From: Julia Date: Mon, 19 Sep 2022 18:44:47 -0400 Subject: [PATCH 069/314] Utilize initial file contents as head text by default Co-Authored-By: Mikayla Maki --- crates/project/src/worktree.rs | 24 ++++++++++++++++++++++-- crates/settings/src/settings.rs | 6 +++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 79e2ed9da9..4d2b509738 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -32,6 +32,7 @@ use postage::{ prelude::{Sink as _, Stream as _}, watch, }; +use settings::Settings; use smol::channel::{self, Sender}; use std::{ any::Any, @@ -571,14 +572,33 @@ impl LocalWorktree { let path = Arc::from(path); let abs_path = self.absolutize(&path); let fs = self.fs.clone(); + + let files_included = cx + .global::() + .editor_overrides + .git_gutter + .unwrap_or_default() + .files_included; + cx.spawn(|this, mut cx| async move { let text = fs.load(&abs_path).await?; - let head_text = { + let head_text = if matches!( + files_included, + settings::GitFilesIncluded::All | settings::GitFilesIncluded::OnlyTracked + ) { let fs = fs.clone(); let abs_path = abs_path.clone(); let task = async move { fs.load_head_text(&abs_path).await }; - cx.background().spawn(task).await + let results = cx.background().spawn(task).await; + + if files_included == settings::GitFilesIncluded::All { + results.or_else(|| Some(text.clone())) + } else { + results + } + } else { + None }; // Eagerly populate the snapshot with an updated entry for the loaded file diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index adb2892b36..3f4a764c79 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -66,13 +66,13 @@ pub struct EditorSettings { #[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema)] pub struct GitGutterConfig { - pub files_included: GitGutterLevel, + pub files_included: GitFilesIncluded, pub debounce_delay_millis: Option, } -#[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema)] +#[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "snake_case")] -pub enum GitGutterLevel { +pub enum GitFilesIncluded { #[default] All, OnlyTracked, From 8d2de1074b1b7583c5ed10cf401ec3a92de291bb Mon Sep 17 00:00:00 2001 From: Julia Date: Mon, 19 Sep 2022 19:25:59 -0400 Subject: [PATCH 070/314] Pull git indicator colors out of theme Co-Authored-By: Kay Simmons Co-Authored-By: Mikayla Maki --- crates/editor/src/element.rs | 15 ++++++++++++--- crates/theme/src/theme.rs | 1 + styles/src/styleTree/editor.ts | 6 ++++-- styles/src/themes/common/base16.ts | 5 +++++ styles/src/themes/common/theme.ts | 1 + 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index a293514559..2e767d72e6 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -545,10 +545,19 @@ impl EditorElement { } } + let (inserted_color, modified_color, deleted_color) = { + let editor = &cx.global::().theme.editor; + ( + editor.diff_background_inserted, + editor.diff_background_modified, + editor.diff_background_deleted, + ) + }; + for hunk in &layout.diff_hunks { let color = match hunk.status() { - DiffHunkStatus::Added => Color::green(), - DiffHunkStatus::Modified => Color::blue(), + DiffHunkStatus::Added => inserted_color, + DiffHunkStatus::Modified => modified_color, //TODO: This rendering is entirely a horrible hack DiffHunkStatus::Removed => { @@ -565,7 +574,7 @@ impl EditorElement { cx.scene.push_quad(Quad { bounds: highlight_bounds, - background: Some(Color::red()), + background: Some(deleted_color), border: Border::new(0., Color::transparent_black()), corner_radius: 1. * line_height, }); diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 739a4c7686..1fd586efee 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -490,6 +490,7 @@ pub struct Editor { pub document_highlight_write_background: Color, pub diff_background_deleted: Color, pub diff_background_inserted: Color, + pub diff_background_modified: Color, pub line_number: Color, pub line_number_active: Color, pub guest_selections: Vec, diff --git a/styles/src/styleTree/editor.ts b/styles/src/styleTree/editor.ts index 62f7a0efdf..29d6857964 100644 --- a/styles/src/styleTree/editor.ts +++ b/styles/src/styleTree/editor.ts @@ -7,6 +7,7 @@ import { player, popoverShadow, text, + textColor, TextColor, } from "./components"; import hoverPopover from "./hoverPopover"; @@ -59,8 +60,9 @@ export default function editor(theme: Theme) { indicator: iconColor(theme, "secondary"), verticalScale: 0.618 }, - diffBackgroundDeleted: backgroundColor(theme, "error"), - diffBackgroundInserted: backgroundColor(theme, "ok"), + diffBackgroundDeleted: theme.ramps.red(0.3).hex(), + diffBackgroundInserted: theme.ramps.green(0.3).hex(), + diffBackgroundModified: theme.ramps.blue(0.3).hex(), documentHighlightReadBackground: theme.editor.highlight.occurrence, documentHighlightWriteBackground: theme.editor.highlight.activeOccurrence, errorColor: theme.textColor.error, diff --git a/styles/src/themes/common/base16.ts b/styles/src/themes/common/base16.ts index 7aa72ef137..326928252e 100644 --- a/styles/src/themes/common/base16.ts +++ b/styles/src/themes/common/base16.ts @@ -113,6 +113,11 @@ export function createTheme( hovered: sample(ramps.blue, 0.1), active: sample(ramps.blue, 0.15), }, + on500Ok: { + base: sample(ramps.green, 0.05), + hovered: sample(ramps.green, 0.1), + active: sample(ramps.green, 0.15) + } }; const borderColor = { diff --git a/styles/src/themes/common/theme.ts b/styles/src/themes/common/theme.ts index e01435b846..b93148ae2c 100644 --- a/styles/src/themes/common/theme.ts +++ b/styles/src/themes/common/theme.ts @@ -78,6 +78,7 @@ export default interface Theme { // Hacks for elements on top of the editor on500: BackgroundColorSet; ok: BackgroundColorSet; + on500Ok: BackgroundColorSet; error: BackgroundColorSet; on500Error: BackgroundColorSet; warning: BackgroundColorSet; From bb8798a8444eb96af94236ba41ea0652b91baf59 Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 20 Sep 2022 17:50:29 -0400 Subject: [PATCH 071/314] WIP pls amend me Co-Authored-By: Max Brunsfeld Co-Authored-By: Mikayla Maki --- Cargo.lock | 1 + crates/project/Cargo.toml | 1 + crates/project/src/project.rs | 2 +- crates/project/src/worktree.rs | 76 +++++++++++++++++++++++++++++++++- 4 files changed, 78 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1f60f1d36c..040db0fd41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3996,6 +3996,7 @@ dependencies = [ "fsevent", "futures", "fuzzy", + "git2", "gpui", "ignore", "language", diff --git a/crates/project/Cargo.toml b/crates/project/Cargo.toml index a4ea6f2286..76eef0efa7 100644 --- a/crates/project/Cargo.toml +++ b/crates/project/Cargo.toml @@ -52,6 +52,7 @@ smol = "1.2.5" thiserror = "1.0.29" toml = "0.5" rocksdb = "0.18" +git2 = { version = "0.15", default-features = false } [dev-dependencies] client = { path = "../client", features = ["test-support"] } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 8fa1fe9622..4d16c6ad1f 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4538,7 +4538,7 @@ impl Project { }) .detach(); } - + let push_strong_handle = { let worktree = worktree.read(cx); self.is_shared() || worktree.is_visible() || worktree.is_remote() diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 4d2b509738..b054f93328 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -27,7 +27,7 @@ use language::{ Buffer, DiagnosticEntry, LineEnding, PointUtf16, Rope, }; use lazy_static::lazy_static; -use parking_lot::Mutex; +use parking_lot::{Mutex, RwLock}; use postage::{ prelude::{Sink as _, Stream as _}, watch, @@ -105,12 +105,20 @@ pub struct Snapshot { pub struct LocalSnapshot { abs_path: Arc, ignores_by_parent_abs_path: HashMap, (Arc, usize)>, + git_repositories: Vec, removed_entry_ids: HashMap, next_entry_id: Arc, snapshot: Snapshot, extension_counts: HashMap, } +#[derive(Clone)] +pub(crate) struct GitRepositoryState { + content_path: Arc, + git_dir_path: Arc, + repository: Arc>, +} + impl Deref for LocalSnapshot { type Target = Snapshot; @@ -143,6 +151,7 @@ struct ShareState { pub enum Event { UpdatedEntries, + UpdatedGitRepositories(Vec), } impl Entity for Worktree { @@ -373,6 +382,7 @@ impl LocalWorktree { let mut snapshot = LocalSnapshot { abs_path, ignores_by_parent_abs_path: Default::default(), + git_repositories: Default::default(), removed_entry_ids: Default::default(), next_entry_id, snapshot: Snapshot { @@ -504,6 +514,7 @@ impl LocalWorktree { fn poll_snapshot(&mut self, force: bool, cx: &mut ModelContext) { self.poll_task.take(); + match self.scan_state() { ScanState::Idle => { self.snapshot = self.background_snapshot.lock().clone(); @@ -512,6 +523,7 @@ impl LocalWorktree { } cx.emit(Event::UpdatedEntries); } + ScanState::Initializing => { let is_fake_fs = self.fs.is_fake(); self.snapshot = self.background_snapshot.lock().clone(); @@ -528,12 +540,14 @@ impl LocalWorktree { })); cx.emit(Event::UpdatedEntries); } + _ => { if force { self.snapshot = self.background_snapshot.lock().clone(); } } } + cx.notify(); } @@ -1285,6 +1299,10 @@ impl LocalSnapshot { pub fn extension_counts(&self) -> &HashMap { &self.extension_counts } + + pub(crate) fn git_repository_for_file_path(&self, path: &Path) -> Option { + None + } #[cfg(test)] pub(crate) fn build_initial_update(&self, project_id: u64) -> proto::UpdateWorktree { @@ -3042,6 +3060,61 @@ mod tests { assert!(tree.entry_for_path(".git").unwrap().is_ignored); }); } + + #[gpui::test] + async fn test_git_repository_for_path(cx: &mut TestAppContext) { + let fs = FakeFs::new(cx.background()); + fs.insert_tree( + "/root", + json!({ + "dir1": { + ".git": {}, + "deps": { + "dep1": { + ".git": {}, + "src": { + "a.txt": "" + } + } + }, + "src": { + "b.txt": "" + } + }, + "c.txt": "" + }), + ) + .await; + + let http_client = FakeHttpClient::with_404_response(); + let client = Client::new(http_client); + let tree = Worktree::local( + client, + Arc::from(Path::new("/root")), + true, + fs.clone(), + Default::default(), + &mut cx.to_async(), + ) + .await + .unwrap(); + + cx.foreground().run_until_parked(); + + tree.read_with(cx, |tree, cx| { + let tree = tree.as_local().unwrap(); + + assert!(tree.git_repository_for_file_path("c.txt".as_ref()).is_none()); + + let repo1 = tree.git_repository_for_file_path("dir1/src/b.txt".as_ref()).unwrap().lock(); + assert_eq!(repo1.content_path.as_ref(), Path::new("dir1")); + assert_eq!(repo1.git_dir_path.as_ref(), Path::new("dir1/.git")); + + let repo2 = tree.git_repository_for_file_path("dir1/deps/dep1/src/a.txt".as_ref()).unwrap().lock(); + assert_eq!(repo2.content_path.as_ref(), Path::new("dir1/deps/dep1")); + assert_eq!(repo2.git_dir_path.as_ref(), Path::new("dir1/deps/dep1/.git")); + }); + } #[gpui::test] async fn test_write_file(cx: &mut TestAppContext) { @@ -3161,6 +3234,7 @@ mod tests { abs_path: root_dir.path().into(), removed_entry_ids: Default::default(), ignores_by_parent_abs_path: Default::default(), + git_repositories: Default::default(), next_entry_id: next_entry_id.clone(), snapshot: Snapshot { id: WorktreeId::from_usize(0), From 0d1b2a7e4693f38464bbdfe41fb9b10a03d501e8 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 22 Sep 2022 12:50:35 -0700 Subject: [PATCH 072/314] WIP - max & mikayla working on tests --- crates/project/src/project.rs | 3 +- crates/project/src/worktree.rs | 134 +++++++++++++++++++++++++++++---- 2 files changed, 120 insertions(+), 17 deletions(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 4d16c6ad1f..36c7c6cf81 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4535,10 +4535,11 @@ impl Project { if worktree.read(cx).is_local() { cx.subscribe(worktree, |this, worktree, event, cx| match event { worktree::Event::UpdatedEntries => this.update_local_worktree_buffers(worktree, cx), + worktree::Event::UpdatedGitRepositories(_) => todo!(), }) .detach(); } - + let push_strong_handle = { let worktree = worktree.read(cx); self.is_shared() || worktree.is_visible() || worktree.is_remote() diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index b054f93328..49dbe06117 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -18,6 +18,7 @@ use futures::{ Stream, StreamExt, }; use fuzzy::CharBag; +use git2::Repository; use gpui::{ executor, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task, @@ -27,7 +28,7 @@ use language::{ Buffer, DiagnosticEntry, LineEnding, PointUtf16, Rope, }; use lazy_static::lazy_static; -use parking_lot::{Mutex, RwLock}; +use parking_lot::Mutex; use postage::{ prelude::{Sink as _, Stream as _}, watch, @@ -41,6 +42,7 @@ use std::{ ffi::{OsStr, OsString}, fmt, future::Future, + mem, ops::{Deref, DerefMut}, os::unix::prelude::{OsStrExt, OsStringExt}, path::{Path, PathBuf}, @@ -52,6 +54,7 @@ use sum_tree::{Bias, Edit, SeekTarget, SumTree, TreeMap, TreeSet}; use util::{ResultExt, TryFutureExt}; lazy_static! { + static ref DOT_GIT: &'static OsStr = OsStr::new(".git"); static ref GITIGNORE: &'static OsStr = OsStr::new(".gitignore"); } @@ -101,6 +104,24 @@ pub struct Snapshot { is_complete: bool, } +// + +// 'GitResolver' +// File paths <-> Repository Paths -> git_repository_path() -> First .git in an ancestor in a path +// Repository Paths <-> Repository Pointers -> git_repository_open() +// fs.watch() ^ +// +// Folder: where all the git magic happens +// .git IT +// OR it can be a file that points somewhere else + +// 1. Walk through the file tree, looking for .git files or folders +// 2. When we discover them, open and save a libgit2 pointer to the repository +// 2a. Use git_repository_path() to start a watch on the repository (if not already watched) +// +// File paths -> Git repository == Ancestor check (is there a .git in an ancestor folder) +// Git repository -> Files == Descendent check (subtracting out any intersecting .git folders) + #[derive(Clone)] pub struct LocalSnapshot { abs_path: Arc, @@ -113,9 +134,10 @@ pub struct LocalSnapshot { } #[derive(Clone)] -pub(crate) struct GitRepositoryState { +pub struct GitRepositoryState { content_path: Arc, git_dir_path: Arc, + scan_id: usize, repository: Arc>, } @@ -1299,11 +1321,34 @@ impl LocalSnapshot { pub fn extension_counts(&self) -> &HashMap { &self.extension_counts } - + pub(crate) fn git_repository_for_file_path(&self, path: &Path) -> Option { + for repository in self.git_repositories.iter().rev() { + if path.starts_with(&repository.content_path) { + return Some(repository.clone()); + } + } None } + pub(crate) fn git_repository_for_git_data(&self, path: &Path) -> Option { + for repository in self.git_repositories.iter() { + if path.starts_with(&repository.git_dir_path) { + return Some(repository.clone()); + } + } + None + } + + pub(crate) fn does_git_repository_track_file_path( + &self, + repo: &GitRepositoryState, + file_path: &Path, + ) -> bool { + self.git_repository_for_file_path(file_path) + .map_or(false, |r| r.content_path == repo.content_path) + } + #[cfg(test)] pub(crate) fn build_initial_update(&self, project_id: u64) -> proto::UpdateWorktree { let root_name = self.root_name.clone(); @@ -1403,6 +1448,25 @@ impl LocalSnapshot { ); } } + } else if entry.path.file_name() == Some(&DOT_GIT) { + let abs_path = self.abs_path.join(&entry.path); + let content_path: Arc = entry.path.parent().unwrap().into(); + if let Err(ix) = self + .git_repositories + .binary_search_by_key(&&content_path, |repo| &repo.content_path) + { + if let Some(repository) = Repository::open(&abs_path).log_err() { + self.git_repositories.insert( + ix, + GitRepositoryState { + content_path, + git_dir_path: repository.path().into(), + scan_id: self.scan_id, + repository: Arc::new(Mutex::new(repository)), + }, + ); + } + } } self.reuse_entry_id(&mut entry); @@ -1549,6 +1613,14 @@ impl LocalSnapshot { { *scan_id = self.snapshot.scan_id; } + } else if path.file_name() == Some(&DOT_GIT) { + let parent_path = path.parent().unwrap(); + if let Ok(ix) = self + .git_repositories + .binary_search_by_key(&parent_path, |repo| repo.content_path.as_ref()) + { + self.git_repositories[ix].scan_id = self.snapshot.scan_id; + } } } @@ -2423,6 +2495,7 @@ impl BackgroundScanner { self.snapshot.lock().removed_entry_ids.clear(); self.update_ignore_statuses().await; + self.update_git_repositories().await; true } @@ -2488,6 +2561,16 @@ impl BackgroundScanner { .await; } + async fn update_git_repositories(&self) { + let mut snapshot = self.snapshot(); + let mut git_repositories = mem::take(&mut snapshot.git_repositories); + git_repositories.retain(|git_repository| { + let dot_git_path = git_repository.content_path.join(&*DOT_GIT); + snapshot.entry_for_path(dot_git_path).is_some() + }); + snapshot.git_repositories = git_repositories; + } + async fn update_ignore_status(&self, job: UpdateIgnoreStatusJob, snapshot: &LocalSnapshot) { let mut ignore_stack = job.ignore_stack; if let Some((ignore, _)) = snapshot.ignores_by_parent_abs_path.get(&job.abs_path) { @@ -3060,7 +3143,7 @@ mod tests { assert!(tree.entry_for_path(".git").unwrap().is_ignored); }); } - + #[gpui::test] async fn test_git_repository_for_path(cx: &mut TestAppContext) { let fs = FakeFs::new(cx.background()); @@ -3068,7 +3151,9 @@ mod tests { "/root", json!({ "dir1": { - ".git": {}, + ".git": { + "HEAD": "abc" + }, "deps": { "dep1": { ".git": {}, @@ -3097,22 +3182,39 @@ mod tests { &mut cx.to_async(), ) .await - .unwrap(); - + .unwrap(); + cx.foreground().run_until_parked(); - + tree.read_with(cx, |tree, cx| { let tree = tree.as_local().unwrap(); - - assert!(tree.git_repository_for_file_path("c.txt".as_ref()).is_none()); - let repo1 = tree.git_repository_for_file_path("dir1/src/b.txt".as_ref()).unwrap().lock(); - assert_eq!(repo1.content_path.as_ref(), Path::new("dir1")); - assert_eq!(repo1.git_dir_path.as_ref(), Path::new("dir1/.git")); + assert!(tree + .git_repository_for_file_path("c.txt".as_ref()) + .is_none()); - let repo2 = tree.git_repository_for_file_path("dir1/deps/dep1/src/a.txt".as_ref()).unwrap().lock(); - assert_eq!(repo2.content_path.as_ref(), Path::new("dir1/deps/dep1")); - assert_eq!(repo2.git_dir_path.as_ref(), Path::new("dir1/deps/dep1/.git")); + let repo = tree + .git_repository_for_file_path("dir1/src/b.txt".as_ref()) + .unwrap(); + + // Need to update the file system for anything involving git + // Goal: Make this test pass + // Up Next: Invalidating git repos! + assert_eq!(repo.content_path.as_ref(), Path::new("dir1")); + assert_eq!(repo.git_dir_path.as_ref(), Path::new("dir1/.git")); + + let repo = tree + .git_repository_for_file_path("dir1/deps/dep1/src/a.txt".as_ref()) + .unwrap(); + + assert_eq!(repo.content_path.as_ref(), Path::new("dir1/deps/dep1")); + assert_eq!( repo = tree .git_repository_for_git_data("dir/.git/HEAD".as_ref()) + .unwrap(); + assert_eq!(repo.content_path.as_ref(), Path::new("dir1/deps/dep1")); + + assert!(tree.does_git_repository_track_file_path(&repo, "dir1/src/b.txt".as_ref())); + assert!(!tree + .does_git_repository_track_file_path(&repo, "dir1/deps/dep1/src/a.txt".as_ref())); }); } From 6ac9308a034cd480357355c2566c44464aaf9058 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 22 Sep 2022 16:55:24 -0700 Subject: [PATCH 073/314] Added git repository type infrastructure and moved git file system stuff into fs abstraction so we can test without touching the file system. Co-Authored-By: kay@zed.dev --- crates/project/src/fs.rs | 27 ++++++ crates/project/src/git_repository.rs | 132 ++++++++++++++++++++++++++ crates/project/src/project.rs | 1 + crates/project/src/worktree.rs | 135 ++++++++++++++------------- 4 files changed, 230 insertions(+), 65 deletions(-) create mode 100644 crates/project/src/git_repository.rs diff --git a/crates/project/src/fs.rs b/crates/project/src/fs.rs index a983df0f4b..70d1879886 100644 --- a/crates/project/src/fs.rs +++ b/crates/project/src/fs.rs @@ -12,6 +12,7 @@ use std::{ pin::Pin, time::{Duration, SystemTime}, }; + use text::Rope; #[cfg(any(test, feature = "test-support"))] @@ -21,6 +22,8 @@ use futures::lock::Mutex; #[cfg(any(test, feature = "test-support"))] use std::sync::{Arc, Weak}; +use crate::git_repository::{FakeGitRepository, GitRepository, RealGitRepository}; + #[async_trait::async_trait] pub trait Fs: Send + Sync { async fn create_dir(&self, path: &Path) -> Result<()>; @@ -45,6 +48,11 @@ pub trait Fs: Send + Sync { path: &Path, latency: Duration, ) -> Pin>>>; + fn open_git_repository( + &self, + abs_dotgit_path: &Path, + content_path: &Arc, + ) -> Option>; fn is_fake(&self) -> bool; #[cfg(any(test, feature = "test-support"))] fn as_fake(&self) -> &FakeFs; @@ -270,6 +278,14 @@ impl Fs for RealFs { }))) } + fn open_git_repository( + &self, + abs_dotgit_path: &Path, + content_path: &Arc, + ) -> Option> { + RealGitRepository::open(abs_dotgit_path, content_path) + } + fn is_fake(&self) -> bool { false } @@ -885,6 +901,17 @@ impl Fs for FakeFs { })) } + fn open_git_repository( + &self, + abs_dotgit_path: &Path, + content_path: &Arc, + ) -> Option> { + Some(Box::new(FakeGitRepository::new( + abs_dotgit_path, + content_path, + ))) + } + fn is_fake(&self) -> bool { true } diff --git a/crates/project/src/git_repository.rs b/crates/project/src/git_repository.rs new file mode 100644 index 0000000000..fe7747be9b --- /dev/null +++ b/crates/project/src/git_repository.rs @@ -0,0 +1,132 @@ +use git2::Repository; +use parking_lot::Mutex; +use std::{path::Path, sync::Arc}; +use util::ResultExt; + +pub trait GitRepository: Send + Sync { + fn boxed_clone(&self) -> Box; + fn is_path_managed_by(&self, path: &Path) -> bool; + fn is_path_in_git_folder(&self, path: &Path) -> bool; + fn content_path(&self) -> &Path; + fn git_dir_path(&self) -> &Path; + fn last_scan_id(&self) -> usize; + fn set_scan_id(&mut self, scan_id: usize); +} + +#[derive(Clone)] +pub struct RealGitRepository { + // Path to folder containing the .git file or directory + content_path: Arc, + // Path to the actual .git folder. + // Note: if .git is a file, this points to the folder indicated by the .git file + git_dir_path: Arc, + last_scan_id: usize, + libgit_repository: Arc>, +} + +impl RealGitRepository { + pub fn open( + abs_dotgit_path: &Path, + content_path: &Arc, + ) -> Option> { + Repository::open(&abs_dotgit_path) + .log_err() + .map::, _>(|libgit_repository| { + Box::new(Self { + content_path: content_path.clone(), + git_dir_path: libgit_repository.path().into(), + last_scan_id: 0, + libgit_repository: Arc::new(parking_lot::Mutex::new(libgit_repository)), + }) + }) + } +} + +impl GitRepository for RealGitRepository { + fn boxed_clone(&self) -> Box { + Box::new(self.clone()) + } + + fn is_path_managed_by(&self, path: &Path) -> bool { + path.starts_with(&self.content_path) + } + + fn is_path_in_git_folder(&self, path: &Path) -> bool { + path.starts_with(&self.git_dir_path) + } + + fn content_path(&self) -> &Path { + self.content_path.as_ref() + } + + fn git_dir_path(&self) -> &Path { + self.git_dir_path.as_ref() + } + + fn last_scan_id(&self) -> usize { + self.last_scan_id + } + + fn set_scan_id(&mut self, scan_id: usize) { + self.last_scan_id = scan_id; + } +} + +impl PartialEq for &Box { + fn eq(&self, other: &Self) -> bool { + self.content_path() == other.content_path() + } +} +impl Eq for &Box {} + +#[cfg(any(test, feature = "test-support"))] +#[derive(Clone)] +pub struct FakeGitRepository { + // Path to folder containing the .git file or directory + content_path: Arc, + // Path to the actual .git folder. + // Note: if .git is a file, this points to the folder indicated by the .git file + git_dir_path: Arc, + last_scan_id: usize, +} + +impl FakeGitRepository { + pub fn new(abs_dotgit_path: &Path, content_path: &Arc) -> FakeGitRepository { + Self { + content_path: content_path.clone(), + git_dir_path: abs_dotgit_path.into(), + last_scan_id: 0, + } + } +} + +#[cfg(any(test, feature = "test-support"))] +impl GitRepository for FakeGitRepository { + fn boxed_clone(&self) -> Box { + Box::new(self.clone()) + } + + fn is_path_managed_by(&self, path: &Path) -> bool { + path.starts_with(&self.content_path) + } + + fn is_path_in_git_folder(&self, path: &Path) -> bool { + path.starts_with(&self.git_dir_path) + } + + fn content_path(&self) -> &Path { + self.content_path.as_ref() + } + + fn git_dir_path(&self) -> &Path { + self.git_dir_path.as_ref() + } + + fn last_scan_id(&self) -> usize { + self.last_scan_id + } + + fn set_scan_id(&mut self, scan_id: usize) { + self.last_scan_id = scan_id; + } +} diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 36c7c6cf81..78a500585a 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1,4 +1,5 @@ pub mod fs; +mod git_repository; mod ignore; mod lsp_command; pub mod search; diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 49dbe06117..5ae8bf542c 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -1,4 +1,4 @@ -use crate::{copy_recursive, ProjectEntryId, RemoveOptions}; +use crate::{copy_recursive, git_repository::GitRepository, ProjectEntryId, RemoveOptions}; use super::{ fs::{self, Fs}, @@ -18,7 +18,6 @@ use futures::{ Stream, StreamExt, }; use fuzzy::CharBag; -use git2::Repository; use gpui::{ executor, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task, @@ -104,41 +103,32 @@ pub struct Snapshot { is_complete: bool, } -// - -// 'GitResolver' -// File paths <-> Repository Paths -> git_repository_path() -> First .git in an ancestor in a path -// Repository Paths <-> Repository Pointers -> git_repository_open() -// fs.watch() ^ -// -// Folder: where all the git magic happens -// .git IT -// OR it can be a file that points somewhere else - -// 1. Walk through the file tree, looking for .git files or folders -// 2. When we discover them, open and save a libgit2 pointer to the repository -// 2a. Use git_repository_path() to start a watch on the repository (if not already watched) -// -// File paths -> Git repository == Ancestor check (is there a .git in an ancestor folder) -// Git repository -> Files == Descendent check (subtracting out any intersecting .git folders) - -#[derive(Clone)] pub struct LocalSnapshot { abs_path: Arc, ignores_by_parent_abs_path: HashMap, (Arc, usize)>, - git_repositories: Vec, + git_repositories: Vec>, removed_entry_ids: HashMap, next_entry_id: Arc, snapshot: Snapshot, extension_counts: HashMap, } -#[derive(Clone)] -pub struct GitRepositoryState { - content_path: Arc, - git_dir_path: Arc, - scan_id: usize, - repository: Arc>, +impl Clone for LocalSnapshot { + fn clone(&self) -> Self { + Self { + abs_path: self.abs_path.clone(), + ignores_by_parent_abs_path: self.ignores_by_parent_abs_path.clone(), + git_repositories: self + .git_repositories + .iter() + .map(|repo| repo.boxed_clone()) + .collect(), + removed_entry_ids: self.removed_entry_ids.clone(), + next_entry_id: self.next_entry_id.clone(), + snapshot: self.snapshot.clone(), + extension_counts: self.extension_counts.clone(), + } + } } impl Deref for LocalSnapshot { @@ -173,7 +163,7 @@ struct ShareState { pub enum Event { UpdatedEntries, - UpdatedGitRepositories(Vec), + UpdatedGitRepositories(Vec>), } impl Entity for Worktree { @@ -1322,31 +1312,47 @@ impl LocalSnapshot { &self.extension_counts } - pub(crate) fn git_repository_for_file_path(&self, path: &Path) -> Option { - for repository in self.git_repositories.iter().rev() { - if path.starts_with(&repository.content_path) { - return Some(repository.clone()); - } - } - None + // Gives the most specific git repository for a given path + pub(crate) fn git_repository_for_file_path( + &self, + path: &Path, + ) -> Option> { + self.git_repositories + .iter() + .rev() //git_repository is ordered lexicographically + .find(|repo| repo.is_path_managed_by(path)) + .map(|repo| repo.boxed_clone()) } - pub(crate) fn git_repository_for_git_data(&self, path: &Path) -> Option { - for repository in self.git_repositories.iter() { - if path.starts_with(&repository.git_dir_path) { - return Some(repository.clone()); - } - } - None + // ~/zed: + // - src + // - crates + // - .git -> /usr/.git + pub(crate) fn git_repository_for_git_data( + &self, + path: &Path, + ) -> Option> { + self.git_repositories + .iter() + .find(|repo| repo.is_path_in_git_folder(path)) + .map(|repo| repo.boxed_clone()) } pub(crate) fn does_git_repository_track_file_path( &self, - repo: &GitRepositoryState, + repo: &Box, file_path: &Path, ) -> bool { + // /zed + // - .git + // - a.txt + // - /dep + // - b.txt + // - .git + + // Depends on git_repository_for_file_path returning the most specific git repository for a given path self.git_repository_for_file_path(file_path) - .map_or(false, |r| r.content_path == repo.content_path) + .map_or(false, |r| &r == repo) } #[cfg(test)] @@ -1431,7 +1437,7 @@ impl LocalSnapshot { } fn insert_entry(&mut self, mut entry: Entry, fs: &dyn Fs) -> Entry { - if !entry.is_dir() && entry.path.file_name() == Some(&GITIGNORE) { + if entry.is_file() && entry.path.file_name() == Some(&GITIGNORE) { let abs_path = self.abs_path.join(&entry.path); match smol::block_on(build_gitignore(&abs_path, fs)) { Ok(ignore) => { @@ -1453,18 +1459,10 @@ impl LocalSnapshot { let content_path: Arc = entry.path.parent().unwrap().into(); if let Err(ix) = self .git_repositories - .binary_search_by_key(&&content_path, |repo| &repo.content_path) + .binary_search_by_key(&content_path.as_ref(), |repo| repo.content_path()) { - if let Some(repository) = Repository::open(&abs_path).log_err() { - self.git_repositories.insert( - ix, - GitRepositoryState { - content_path, - git_dir_path: repository.path().into(), - scan_id: self.scan_id, - repository: Arc::new(Mutex::new(repository)), - }, - ); + if let Some(repository) = fs.open_git_repository(&abs_path, &content_path) { + self.git_repositories.insert(ix, repository); } } } @@ -1617,9 +1615,9 @@ impl LocalSnapshot { let parent_path = path.parent().unwrap(); if let Ok(ix) = self .git_repositories - .binary_search_by_key(&parent_path, |repo| repo.content_path.as_ref()) + .binary_search_by_key(&parent_path, |repo| repo.content_path().as_ref()) { - self.git_repositories[ix].scan_id = self.snapshot.scan_id; + self.git_repositories[ix].set_scan_id(self.snapshot.scan_id); } } } @@ -2565,7 +2563,7 @@ impl BackgroundScanner { let mut snapshot = self.snapshot(); let mut git_repositories = mem::take(&mut snapshot.git_repositories); git_repositories.retain(|git_repository| { - let dot_git_path = git_repository.content_path.join(&*DOT_GIT); + let dot_git_path = git_repository.content_path().join(&*DOT_GIT); snapshot.entry_for_path(dot_git_path).is_some() }); snapshot.git_repositories = git_repositories; @@ -2925,6 +2923,7 @@ mod tests { fmt::Write, time::{SystemTime, UNIX_EPOCH}, }; + use util::test::temp_tree; #[gpui::test] @@ -3147,6 +3146,7 @@ mod tests { #[gpui::test] async fn test_git_repository_for_path(cx: &mut TestAppContext) { let fs = FakeFs::new(cx.background()); + fs.insert_tree( "/root", json!({ @@ -3200,17 +3200,22 @@ mod tests { // Need to update the file system for anything involving git // Goal: Make this test pass // Up Next: Invalidating git repos! - assert_eq!(repo.content_path.as_ref(), Path::new("dir1")); - assert_eq!(repo.git_dir_path.as_ref(), Path::new("dir1/.git")); + assert_eq!(repo.content_path(), Path::new("dir1")); + assert_eq!(repo.git_dir_path(), Path::new("dir1/.git")); let repo = tree .git_repository_for_file_path("dir1/deps/dep1/src/a.txt".as_ref()) .unwrap(); - assert_eq!(repo.content_path.as_ref(), Path::new("dir1/deps/dep1")); - assert_eq!( repo = tree .git_repository_for_git_data("dir/.git/HEAD".as_ref()) + assert_eq!(repo.content_path(), Path::new("dir1/deps/dep1")); + assert_eq!(repo.git_dir_path(), Path::new("dir1/deps/dep1")); + + let repo = tree + .git_repository_for_git_data("dir1/.git/HEAD".as_ref()) .unwrap(); - assert_eq!(repo.content_path.as_ref(), Path::new("dir1/deps/dep1")); + + assert_eq!(repo.content_path(), Path::new("dir1")); + assert_eq!(repo.git_dir_path(), Path::new("dir1/.git")); assert!(tree.does_git_repository_track_file_path(&repo, "dir1/src/b.txt".as_ref())); assert!(!tree From c8e63d76a41596a56cd15d0804d0e4cba631b509 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 26 Sep 2022 07:59:51 -0700 Subject: [PATCH 074/314] Get the test to failing,,, correctly --- Cargo.lock | 2 + crates/project/Cargo.toml | 1 + crates/project/src/git_repository.rs | 10 ++++ crates/project/src/worktree.rs | 68 ++++++++++++++-------------- crates/util/Cargo.toml | 6 ++- crates/util/src/lib.rs | 7 +++ crates/util/src/test.rs | 8 ++++ 7 files changed, 68 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 040db0fd41..8157327cf2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6359,6 +6359,8 @@ version = "0.1.0" dependencies = [ "anyhow", "futures", + "git2", + "lazy_static", "log", "rand 0.8.5", "serde_json", diff --git a/crates/project/Cargo.toml b/crates/project/Cargo.toml index 76eef0efa7..8ca01eac2c 100644 --- a/crates/project/Cargo.toml +++ b/crates/project/Cargo.toml @@ -54,6 +54,7 @@ toml = "0.5" rocksdb = "0.18" git2 = { version = "0.15", default-features = false } + [dev-dependencies] client = { path = "../client", features = ["test-support"] } collections = { path = "../collections", features = ["test-support"] } diff --git a/crates/project/src/git_repository.rs b/crates/project/src/git_repository.rs index fe7747be9b..47849bf644 100644 --- a/crates/project/src/git_repository.rs +++ b/crates/project/src/git_repository.rs @@ -11,6 +11,7 @@ pub trait GitRepository: Send + Sync { fn git_dir_path(&self) -> &Path; fn last_scan_id(&self) -> usize; fn set_scan_id(&mut self, scan_id: usize); + fn with_repo(&mut self, f: Box); } #[derive(Clone)] @@ -70,6 +71,11 @@ impl GitRepository for RealGitRepository { fn set_scan_id(&mut self, scan_id: usize) { self.last_scan_id = scan_id; } + + fn with_repo(&mut self, f: Box) { + let mut git2 = self.libgit_repository.lock(); + f(&mut git2) + } } impl PartialEq for &Box { @@ -129,4 +135,8 @@ impl GitRepository for FakeGitRepository { fn set_scan_id(&mut self, scan_id: usize) { self.last_scan_id = scan_id; } + + fn with_repo(&mut self, _: Box) { + unimplemented!(); + } } diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 5ae8bf542c..6fd00aabbd 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -26,7 +26,6 @@ use language::{ proto::{deserialize_version, serialize_line_ending, serialize_version}, Buffer, DiagnosticEntry, LineEnding, PointUtf16, Rope, }; -use lazy_static::lazy_static; use parking_lot::Mutex; use postage::{ prelude::{Sink as _, Stream as _}, @@ -50,12 +49,7 @@ use std::{ time::{Duration, SystemTime}, }; use sum_tree::{Bias, Edit, SeekTarget, SumTree, TreeMap, TreeSet}; -use util::{ResultExt, TryFutureExt}; - -lazy_static! { - static ref DOT_GIT: &'static OsStr = OsStr::new(".git"); - static ref GITIGNORE: &'static OsStr = OsStr::new(".gitignore"); -} +use util::{ResultExt, TryFutureExt, DOT_GIT, GITIGNORE}; #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)] pub struct WorktreeId(usize); @@ -1317,6 +1311,13 @@ impl LocalSnapshot { &self, path: &Path, ) -> Option> { + let repos = self + .git_repositories + .iter() + .map(|repo| repo.content_path().to_str().unwrap().to_string()) + .collect::>(); + dbg!(repos); + self.git_repositories .iter() .rev() //git_repository is ordered lexicographically @@ -1437,6 +1438,7 @@ impl LocalSnapshot { } fn insert_entry(&mut self, mut entry: Entry, fs: &dyn Fs) -> Entry { + dbg!(&entry.path); if entry.is_file() && entry.path.file_name() == Some(&GITIGNORE) { let abs_path = self.abs_path.join(&entry.path); match smol::block_on(build_gitignore(&abs_path, fs)) { @@ -1455,6 +1457,8 @@ impl LocalSnapshot { } } } else if entry.path.file_name() == Some(&DOT_GIT) { + dbg!(&entry.path); + let abs_path = self.abs_path.join(&entry.path); let content_path: Arc = entry.path.parent().unwrap().into(); if let Err(ix) = self @@ -2223,6 +2227,7 @@ impl BackgroundScanner { if ignore_stack.is_all() { if let Some(mut root_entry) = snapshot.root_entry().cloned() { root_entry.is_ignored = true; + dbg!("scan dirs entry"); snapshot.insert_entry(root_entry, self.fs.as_ref()); } } @@ -2445,6 +2450,7 @@ impl BackgroundScanner { snapshot.root_char_bag, ); fs_entry.is_ignored = ignore_stack.is_all(); + dbg!("process_events entry"); snapshot.insert_entry(fs_entry, self.fs.as_ref()); let mut ancestor_inodes = snapshot.ancestor_inodes_for_path(&path); @@ -3145,50 +3151,46 @@ mod tests { #[gpui::test] async fn test_git_repository_for_path(cx: &mut TestAppContext) { - let fs = FakeFs::new(cx.background()); - - fs.insert_tree( - "/root", - json!({ - "dir1": { - ".git": { - "HEAD": "abc" - }, - "deps": { - "dep1": { - ".git": {}, - "src": { - "a.txt": "" - } + let root = temp_tree(json!({ + "dir1": { + ".git": {}, + "deps": { + "dep1": { + ".git": {}, + "src": { + "a.txt": "" } - }, - "src": { - "b.txt": "" } }, - "c.txt": "" - }), - ) - .await; + "src": { + "b.txt": "" + } + }, + "c.txt": "" + })); let http_client = FakeHttpClient::with_404_response(); let client = Client::new(http_client); let tree = Worktree::local( client, - Arc::from(Path::new("/root")), + root.path(), true, - fs.clone(), + Arc::new(RealFs), Default::default(), &mut cx.to_async(), ) .await .unwrap(); - cx.foreground().run_until_parked(); + cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete()) + .await; + tree.flush_fs_events(cx).await; - tree.read_with(cx, |tree, cx| { + tree.read_with(cx, |tree, _cx| { let tree = tree.as_local().unwrap(); + dbg!(tree); + assert!(tree .git_repository_for_file_path("c.txt".as_ref()) .is_none()); diff --git a/crates/util/Cargo.toml b/crates/util/Cargo.toml index 4ec214fef1..78416aa5b5 100644 --- a/crates/util/Cargo.toml +++ b/crates/util/Cargo.toml @@ -7,17 +7,21 @@ edition = "2021" doctest = false [features] -test-support = ["rand", "serde_json", "tempdir"] +test-support = ["rand", "serde_json", "tempdir", "git2"] [dependencies] anyhow = "1.0.38" futures = "0.3" log = { version = "0.4.16", features = ["kv_unstable_serde"] } +lazy_static = "1.4.0" rand = { version = "0.8", optional = true } tempdir = { version = "0.3.7", optional = true } serde_json = { version = "1.0", features = ["preserve_order"], optional = true } +git2 = { version = "0.15", default-features = false, optional = true } + [dev-dependencies] rand = { version = "0.8" } tempdir = { version = "0.3.7" } serde_json = { version = "1.0", features = ["preserve_order"] } +git2 = { version = "0.15", default-features = false } diff --git a/crates/util/src/lib.rs b/crates/util/src/lib.rs index 97f409f410..52bf70e3a7 100644 --- a/crates/util/src/lib.rs +++ b/crates/util/src/lib.rs @@ -2,13 +2,20 @@ pub mod test; use futures::Future; +use lazy_static::lazy_static; use std::{ cmp::Ordering, + ffi::OsStr, ops::AddAssign, pin::Pin, task::{Context, Poll}, }; +lazy_static! { + pub static ref DOT_GIT: &'static OsStr = OsStr::new(".git"); + pub static ref GITIGNORE: &'static OsStr = OsStr::new(".gitignore"); +} + pub fn truncate(s: &str, max_chars: usize) -> &str { match s.char_indices().nth(max_chars) { None => s, diff --git a/crates/util/src/test.rs b/crates/util/src/test.rs index 7b2e00d57b..4e4716434e 100644 --- a/crates/util/src/test.rs +++ b/crates/util/src/test.rs @@ -1,12 +1,15 @@ mod assertions; mod marked_text; +use git2; use std::path::{Path, PathBuf}; use tempdir::TempDir; pub use assertions::*; pub use marked_text::*; +use crate::DOT_GIT; + pub fn temp_tree(tree: serde_json::Value) -> TempDir { let dir = TempDir::new("").unwrap(); write_tree(dir.path(), tree); @@ -24,6 +27,11 @@ fn write_tree(path: &Path, tree: serde_json::Value) { match contents { Value::Object(_) => { fs::create_dir(&path).unwrap(); + + if path.file_name() == Some(&DOT_GIT) { + git2::Repository::init(&path.parent().unwrap()).unwrap(); + } + write_tree(&path, contents); } Value::Null => { From 4251e0f5f13978e6d6a779f30797948e2ac37382 Mon Sep 17 00:00:00 2001 From: Julia Date: Mon, 26 Sep 2022 16:57:31 -0400 Subject: [PATCH 075/314] Find repos under worktree & return correct results for repo queries Co-Authored-By: Mikayla Maki --- crates/project/src/fs.rs | 27 ++---- crates/project/src/git_repository.rs | 121 +++++---------------------- crates/project/src/worktree.rs | 98 +++++++++------------- 3 files changed, 66 insertions(+), 180 deletions(-) diff --git a/crates/project/src/fs.rs b/crates/project/src/fs.rs index 70d1879886..e675ddf8e5 100644 --- a/crates/project/src/fs.rs +++ b/crates/project/src/fs.rs @@ -22,7 +22,7 @@ use futures::lock::Mutex; #[cfg(any(test, feature = "test-support"))] use std::sync::{Arc, Weak}; -use crate::git_repository::{FakeGitRepository, GitRepository, RealGitRepository}; +use crate::git_repository::GitRepository; #[async_trait::async_trait] pub trait Fs: Send + Sync { @@ -48,11 +48,7 @@ pub trait Fs: Send + Sync { path: &Path, latency: Duration, ) -> Pin>>>; - fn open_git_repository( - &self, - abs_dotgit_path: &Path, - content_path: &Arc, - ) -> Option>; + fn open_git_repository(&self, abs_dotgit_path: &Path) -> Option; fn is_fake(&self) -> bool; #[cfg(any(test, feature = "test-support"))] fn as_fake(&self) -> &FakeFs; @@ -278,12 +274,8 @@ impl Fs for RealFs { }))) } - fn open_git_repository( - &self, - abs_dotgit_path: &Path, - content_path: &Arc, - ) -> Option> { - RealGitRepository::open(abs_dotgit_path, content_path) + fn open_git_repository(&self, abs_dotgit_path: &Path) -> Option { + GitRepository::open(abs_dotgit_path) } fn is_fake(&self) -> bool { @@ -901,15 +893,8 @@ impl Fs for FakeFs { })) } - fn open_git_repository( - &self, - abs_dotgit_path: &Path, - content_path: &Arc, - ) -> Option> { - Some(Box::new(FakeGitRepository::new( - abs_dotgit_path, - content_path, - ))) + fn open_git_repository(&self, _: &Path) -> Option { + None } fn is_fake(&self) -> bool { diff --git a/crates/project/src/git_repository.rs b/crates/project/src/git_repository.rs index 47849bf644..d1df841fe7 100644 --- a/crates/project/src/git_repository.rs +++ b/crates/project/src/git_repository.rs @@ -3,19 +3,8 @@ use parking_lot::Mutex; use std::{path::Path, sync::Arc}; use util::ResultExt; -pub trait GitRepository: Send + Sync { - fn boxed_clone(&self) -> Box; - fn is_path_managed_by(&self, path: &Path) -> bool; - fn is_path_in_git_folder(&self, path: &Path) -> bool; - fn content_path(&self) -> &Path; - fn git_dir_path(&self) -> &Path; - fn last_scan_id(&self) -> usize; - fn set_scan_id(&mut self, scan_id: usize); - fn with_repo(&mut self, f: Box); -} - #[derive(Clone)] -pub struct RealGitRepository { +pub struct GitRepository { // Path to folder containing the .git file or directory content_path: Arc, // Path to the actual .git folder. @@ -25,118 +14,50 @@ pub struct RealGitRepository { libgit_repository: Arc>, } -impl RealGitRepository { - pub fn open( - abs_dotgit_path: &Path, - content_path: &Arc, - ) -> Option> { - Repository::open(&abs_dotgit_path) +impl GitRepository { + pub fn open(dotgit_path: &Path) -> Option { + Repository::open(&dotgit_path) .log_err() - .map::, _>(|libgit_repository| { - Box::new(Self { - content_path: content_path.clone(), - git_dir_path: libgit_repository.path().into(), + .and_then(|libgit_repository| { + Some(Self { + content_path: libgit_repository.workdir()?.into(), + git_dir_path: dotgit_path.canonicalize().log_err()?.into(), last_scan_id: 0, libgit_repository: Arc::new(parking_lot::Mutex::new(libgit_repository)), }) }) } -} -impl GitRepository for RealGitRepository { - fn boxed_clone(&self) -> Box { - Box::new(self.clone()) + pub fn is_path_managed_by(&self, path: &Path) -> bool { + path.canonicalize() + .map(|path| path.starts_with(&self.content_path)) + .unwrap_or(false) } - fn is_path_managed_by(&self, path: &Path) -> bool { - path.starts_with(&self.content_path) + pub fn is_path_in_git_folder(&self, path: &Path) -> bool { + path.canonicalize() + .map(|path| path.starts_with(&self.git_dir_path)) + .unwrap_or(false) } - fn is_path_in_git_folder(&self, path: &Path) -> bool { - path.starts_with(&self.git_dir_path) - } - - fn content_path(&self) -> &Path { + pub fn content_path(&self) -> &Path { self.content_path.as_ref() } - fn git_dir_path(&self) -> &Path { + pub fn git_dir_path(&self) -> &Path { self.git_dir_path.as_ref() } - fn last_scan_id(&self) -> usize { + pub fn last_scan_id(&self) -> usize { self.last_scan_id } - fn set_scan_id(&mut self, scan_id: usize) { + pub fn set_scan_id(&mut self, scan_id: usize) { self.last_scan_id = scan_id; } - fn with_repo(&mut self, f: Box) { + pub fn with_repo(&mut self, f: Box) { let mut git2 = self.libgit_repository.lock(); f(&mut git2) } } - -impl PartialEq for &Box { - fn eq(&self, other: &Self) -> bool { - self.content_path() == other.content_path() - } -} -impl Eq for &Box {} - -#[cfg(any(test, feature = "test-support"))] -#[derive(Clone)] -pub struct FakeGitRepository { - // Path to folder containing the .git file or directory - content_path: Arc, - // Path to the actual .git folder. - // Note: if .git is a file, this points to the folder indicated by the .git file - git_dir_path: Arc, - last_scan_id: usize, -} - -impl FakeGitRepository { - pub fn new(abs_dotgit_path: &Path, content_path: &Arc) -> FakeGitRepository { - Self { - content_path: content_path.clone(), - git_dir_path: abs_dotgit_path.into(), - last_scan_id: 0, - } - } -} - -#[cfg(any(test, feature = "test-support"))] -impl GitRepository for FakeGitRepository { - fn boxed_clone(&self) -> Box { - Box::new(self.clone()) - } - - fn is_path_managed_by(&self, path: &Path) -> bool { - path.starts_with(&self.content_path) - } - - fn is_path_in_git_folder(&self, path: &Path) -> bool { - path.starts_with(&self.git_dir_path) - } - - fn content_path(&self) -> &Path { - self.content_path.as_ref() - } - - fn git_dir_path(&self) -> &Path { - self.git_dir_path.as_ref() - } - - fn last_scan_id(&self) -> usize { - self.last_scan_id - } - - fn set_scan_id(&mut self, scan_id: usize) { - self.last_scan_id = scan_id; - } - - fn with_repo(&mut self, _: Box) { - unimplemented!(); - } -} diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 6fd00aabbd..ee54fdb394 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -100,7 +100,7 @@ pub struct Snapshot { pub struct LocalSnapshot { abs_path: Arc, ignores_by_parent_abs_path: HashMap, (Arc, usize)>, - git_repositories: Vec>, + git_repositories: Vec, removed_entry_ids: HashMap, next_entry_id: Arc, snapshot: Snapshot, @@ -115,7 +115,7 @@ impl Clone for LocalSnapshot { git_repositories: self .git_repositories .iter() - .map(|repo| repo.boxed_clone()) + .map(|repo| repo.clone()) .collect(), removed_entry_ids: self.removed_entry_ids.clone(), next_entry_id: self.next_entry_id.clone(), @@ -157,7 +157,7 @@ struct ShareState { pub enum Event { UpdatedEntries, - UpdatedGitRepositories(Vec>), + UpdatedGitRepositories(Vec), } impl Entity for Worktree { @@ -1307,53 +1307,35 @@ impl LocalSnapshot { } // Gives the most specific git repository for a given path - pub(crate) fn git_repository_for_file_path( - &self, - path: &Path, - ) -> Option> { - let repos = self - .git_repositories - .iter() - .map(|repo| repo.content_path().to_str().unwrap().to_string()) - .collect::>(); - dbg!(repos); - + pub(crate) fn git_repository_for_file_path(&self, path: &Path) -> Option { self.git_repositories .iter() .rev() //git_repository is ordered lexicographically - .find(|repo| repo.is_path_managed_by(path)) - .map(|repo| repo.boxed_clone()) + .find(|repo| { + repo.is_path_managed_by(&self.abs_path.join(path)) + }) + .map(|repo| repo.clone()) } // ~/zed: // - src // - crates // - .git -> /usr/.git - pub(crate) fn git_repository_for_git_data( - &self, - path: &Path, - ) -> Option> { + pub(crate) fn git_repository_for_git_data(&self, path: &Path) -> Option { self.git_repositories .iter() - .find(|repo| repo.is_path_in_git_folder(path)) - .map(|repo| repo.boxed_clone()) + .find(|repo| repo.is_path_in_git_folder(&self.abs_path.join(path))) + .map(|repo| repo.clone()) } pub(crate) fn does_git_repository_track_file_path( &self, - repo: &Box, + repo: &GitRepository, file_path: &Path, ) -> bool { - // /zed - // - .git - // - a.txt - // - /dep - // - b.txt - // - .git - // Depends on git_repository_for_file_path returning the most specific git repository for a given path - self.git_repository_for_file_path(file_path) - .map_or(false, |r| &r == repo) + self.git_repository_for_file_path(&self.abs_path.join(file_path)) + .map_or(false, |r| r.git_dir_path() == repo.git_dir_path()) } #[cfg(test)] @@ -1438,7 +1420,6 @@ impl LocalSnapshot { } fn insert_entry(&mut self, mut entry: Entry, fs: &dyn Fs) -> Entry { - dbg!(&entry.path); if entry.is_file() && entry.path.file_name() == Some(&GITIGNORE) { let abs_path = self.abs_path.join(&entry.path); match smol::block_on(build_gitignore(&abs_path, fs)) { @@ -1456,19 +1437,6 @@ impl LocalSnapshot { ); } } - } else if entry.path.file_name() == Some(&DOT_GIT) { - dbg!(&entry.path); - - let abs_path = self.abs_path.join(&entry.path); - let content_path: Arc = entry.path.parent().unwrap().into(); - if let Err(ix) = self - .git_repositories - .binary_search_by_key(&content_path.as_ref(), |repo| repo.content_path()) - { - if let Some(repository) = fs.open_git_repository(&abs_path, &content_path) { - self.git_repositories.insert(ix, repository); - } - } } self.reuse_entry_id(&mut entry); @@ -1506,6 +1474,7 @@ impl LocalSnapshot { parent_path: Arc, entries: impl IntoIterator, ignore: Option>, + fs: &dyn Fs, ) { let mut parent_entry = if let Some(parent_entry) = self.entries_by_path.get(&PathKey(parent_path.clone()), &()) @@ -1531,6 +1500,18 @@ impl LocalSnapshot { unreachable!(); } + if parent_path.file_name() == Some(&DOT_GIT) { + let abs_path = self.abs_path.join(&parent_path); + if let Err(ix) = self + .git_repositories + .binary_search_by_key(&abs_path.as_path(), |repo| repo.git_dir_path()) + { + if let Some(repository) = fs.open_git_repository(&abs_path) { + self.git_repositories.insert(ix, repository); + } + } + } + let mut entries_by_path_edits = vec![Edit::Insert(parent_entry)]; let mut entries_by_id_edits = Vec::new(); @@ -2227,7 +2208,6 @@ impl BackgroundScanner { if ignore_stack.is_all() { if let Some(mut root_entry) = snapshot.root_entry().cloned() { root_entry.is_ignored = true; - dbg!("scan dirs entry"); snapshot.insert_entry(root_entry, self.fs.as_ref()); } } @@ -2375,9 +2355,12 @@ impl BackgroundScanner { new_entries.push(child_entry); } - self.snapshot - .lock() - .populate_dir(job.path.clone(), new_entries, new_ignore); + self.snapshot.lock().populate_dir( + job.path.clone(), + new_entries, + new_ignore, + self.fs.as_ref(), + ); for new_job in new_jobs { job.scan_queue.send(new_job).await.unwrap(); } @@ -2450,7 +2433,6 @@ impl BackgroundScanner { snapshot.root_char_bag, ); fs_entry.is_ignored = ignore_stack.is_all(); - dbg!("process_events entry"); snapshot.insert_entry(fs_entry, self.fs.as_ref()); let mut ancestor_inodes = snapshot.ancestor_inodes_for_path(&path); @@ -3189,8 +3171,6 @@ mod tests { tree.read_with(cx, |tree, _cx| { let tree = tree.as_local().unwrap(); - dbg!(tree); - assert!(tree .git_repository_for_file_path("c.txt".as_ref()) .is_none()); @@ -3202,22 +3182,22 @@ mod tests { // Need to update the file system for anything involving git // Goal: Make this test pass // Up Next: Invalidating git repos! - assert_eq!(repo.content_path(), Path::new("dir1")); - assert_eq!(repo.git_dir_path(), Path::new("dir1/.git")); + assert_eq!(repo.content_path(), root.path().join("dir1").canonicalize().unwrap()); + assert_eq!(repo.git_dir_path(), root.path().join("dir1/.git").canonicalize().unwrap()); let repo = tree .git_repository_for_file_path("dir1/deps/dep1/src/a.txt".as_ref()) .unwrap(); - assert_eq!(repo.content_path(), Path::new("dir1/deps/dep1")); - assert_eq!(repo.git_dir_path(), Path::new("dir1/deps/dep1")); + assert_eq!(repo.content_path(), root.path().join("dir1/deps/dep1").canonicalize().unwrap()); + assert_eq!(repo.git_dir_path(), root.path().join("dir1/deps/dep1/.git").canonicalize().unwrap()); let repo = tree .git_repository_for_git_data("dir1/.git/HEAD".as_ref()) .unwrap(); - assert_eq!(repo.content_path(), Path::new("dir1")); - assert_eq!(repo.git_dir_path(), Path::new("dir1/.git")); + assert_eq!(repo.content_path(), root.path().join("dir1").canonicalize().unwrap()); + assert_eq!(repo.git_dir_path(), root.path().join("dir1/.git").canonicalize().unwrap()); assert!(tree.does_git_repository_track_file_path(&repo, "dir1/src/b.txt".as_ref())); assert!(!tree From d2b18790a0d7b9d597d3015380ea82e66229086c Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 27 Sep 2022 14:07:53 -0400 Subject: [PATCH 076/314] Remove git repos from worktree when deleted on storage Co-Authored-By: Mikayla Maki --- crates/project/src/git_repository.rs | 2 +- crates/project/src/worktree.rs | 68 ++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 20 deletions(-) diff --git a/crates/project/src/git_repository.rs b/crates/project/src/git_repository.rs index d1df841fe7..73f7130e56 100644 --- a/crates/project/src/git_repository.rs +++ b/crates/project/src/git_repository.rs @@ -56,7 +56,7 @@ impl GitRepository { self.last_scan_id = scan_id; } - pub fn with_repo(&mut self, f: Box) { + pub fn with_repo(&mut self, f: F) { let mut git2 = self.libgit_repository.lock(); f(&mut git2) } diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index ee54fdb394..a9ebfd8612 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -1311,9 +1311,7 @@ impl LocalSnapshot { self.git_repositories .iter() .rev() //git_repository is ordered lexicographically - .find(|repo| { - repo.is_path_managed_by(&self.abs_path.join(path)) - }) + .find(|repo| repo.is_path_managed_by(&self.abs_path.join(path))) .map(|repo| repo.clone()) } @@ -2548,13 +2546,16 @@ impl BackgroundScanner { } async fn update_git_repositories(&self) { - let mut snapshot = self.snapshot(); - let mut git_repositories = mem::take(&mut snapshot.git_repositories); - git_repositories.retain(|git_repository| { - let dot_git_path = git_repository.content_path().join(&*DOT_GIT); - snapshot.entry_for_path(dot_git_path).is_some() - }); - snapshot.git_repositories = git_repositories; + let mut snapshot = self.snapshot.lock(); + + let new_repos = snapshot + .git_repositories + .iter() + .cloned() + .filter(|repo| git2::Repository::open(repo.git_dir_path()).is_ok()) + .collect(); + + snapshot.git_repositories = new_repos; } async fn update_ignore_status(&self, job: UpdateIgnoreStatusJob, snapshot: &LocalSnapshot) { @@ -3179,30 +3180,59 @@ mod tests { .git_repository_for_file_path("dir1/src/b.txt".as_ref()) .unwrap(); - // Need to update the file system for anything involving git - // Goal: Make this test pass - // Up Next: Invalidating git repos! - assert_eq!(repo.content_path(), root.path().join("dir1").canonicalize().unwrap()); - assert_eq!(repo.git_dir_path(), root.path().join("dir1/.git").canonicalize().unwrap()); + assert_eq!( + repo.content_path(), + root.path().join("dir1").canonicalize().unwrap() + ); + assert_eq!( + repo.git_dir_path(), + root.path().join("dir1/.git").canonicalize().unwrap() + ); let repo = tree .git_repository_for_file_path("dir1/deps/dep1/src/a.txt".as_ref()) .unwrap(); - assert_eq!(repo.content_path(), root.path().join("dir1/deps/dep1").canonicalize().unwrap()); - assert_eq!(repo.git_dir_path(), root.path().join("dir1/deps/dep1/.git").canonicalize().unwrap()); + assert_eq!( + repo.content_path(), + root.path().join("dir1/deps/dep1").canonicalize().unwrap() + ); + assert_eq!( + repo.git_dir_path(), + root.path() + .join("dir1/deps/dep1/.git") + .canonicalize() + .unwrap() + ); let repo = tree .git_repository_for_git_data("dir1/.git/HEAD".as_ref()) .unwrap(); - assert_eq!(repo.content_path(), root.path().join("dir1").canonicalize().unwrap()); - assert_eq!(repo.git_dir_path(), root.path().join("dir1/.git").canonicalize().unwrap()); + assert_eq!( + repo.content_path(), + root.path().join("dir1").canonicalize().unwrap() + ); + assert_eq!( + repo.git_dir_path(), + root.path().join("dir1/.git").canonicalize().unwrap() + ); assert!(tree.does_git_repository_track_file_path(&repo, "dir1/src/b.txt".as_ref())); assert!(!tree .does_git_repository_track_file_path(&repo, "dir1/deps/dep1/src/a.txt".as_ref())); }); + + std::fs::remove_dir_all(root.path().join("dir1/.git")).unwrap(); + tree.flush_fs_events(cx).await; + + tree.read_with(cx, |tree, _cx| { + let tree = tree.as_local().unwrap(); + + assert!(tree + .git_repository_for_file_path("dir1/src/b.txt".as_ref()) + .is_none()); + }); } #[gpui::test] From 759b7f1e07257b431c048d381e26b858b92933ce Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 27 Sep 2022 14:37:33 -0400 Subject: [PATCH 077/314] Update repo scan id when files under dot git dir events Co-Authored-By: Mikayla Maki --- crates/project/src/git_repository.rs | 16 +++--- crates/project/src/worktree.rs | 74 ++++++++++++---------------- 2 files changed, 39 insertions(+), 51 deletions(-) diff --git a/crates/project/src/git_repository.rs b/crates/project/src/git_repository.rs index 73f7130e56..eab031da17 100644 --- a/crates/project/src/git_repository.rs +++ b/crates/project/src/git_repository.rs @@ -10,7 +10,7 @@ pub struct GitRepository { // Path to the actual .git folder. // Note: if .git is a file, this points to the folder indicated by the .git file git_dir_path: Arc, - last_scan_id: usize, + scan_id: usize, libgit_repository: Arc>, } @@ -22,19 +22,19 @@ impl GitRepository { Some(Self { content_path: libgit_repository.workdir()?.into(), git_dir_path: dotgit_path.canonicalize().log_err()?.into(), - last_scan_id: 0, + scan_id: 0, libgit_repository: Arc::new(parking_lot::Mutex::new(libgit_repository)), }) }) } - pub fn is_path_managed_by(&self, path: &Path) -> bool { + pub fn manages(&self, path: &Path) -> bool { path.canonicalize() .map(|path| path.starts_with(&self.content_path)) .unwrap_or(false) } - pub fn is_path_in_git_folder(&self, path: &Path) -> bool { + pub fn in_dot_git(&self, path: &Path) -> bool { path.canonicalize() .map(|path| path.starts_with(&self.git_dir_path)) .unwrap_or(false) @@ -48,12 +48,12 @@ impl GitRepository { self.git_dir_path.as_ref() } - pub fn last_scan_id(&self) -> usize { - self.last_scan_id + pub fn scan_id(&self) -> usize { + self.scan_id } - pub fn set_scan_id(&mut self, scan_id: usize) { - self.last_scan_id = scan_id; + pub(super) fn set_scan_id(&mut self, scan_id: usize) { + self.scan_id = scan_id; } pub fn with_repo(&mut self, f: F) { diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index a9ebfd8612..aead63102b 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -40,7 +40,6 @@ use std::{ ffi::{OsStr, OsString}, fmt, future::Future, - mem, ops::{Deref, DerefMut}, os::unix::prelude::{OsStrExt, OsStringExt}, path::{Path, PathBuf}, @@ -1307,32 +1306,24 @@ impl LocalSnapshot { } // Gives the most specific git repository for a given path - pub(crate) fn git_repository_for_file_path(&self, path: &Path) -> Option { + pub(crate) fn repo_for(&self, path: &Path) -> Option { self.git_repositories .iter() .rev() //git_repository is ordered lexicographically - .find(|repo| repo.is_path_managed_by(&self.abs_path.join(path))) + .find(|repo| repo.manages(&self.abs_path.join(path))) .map(|repo| repo.clone()) } - // ~/zed: - // - src - // - crates - // - .git -> /usr/.git - pub(crate) fn git_repository_for_git_data(&self, path: &Path) -> Option { + pub(crate) fn in_dot_git(&mut self, path: &Path) -> Option<&mut GitRepository> { self.git_repositories - .iter() - .find(|repo| repo.is_path_in_git_folder(&self.abs_path.join(path))) - .map(|repo| repo.clone()) + .iter_mut() + .rev() //git_repository is ordered lexicographically + .find(|repo| repo.in_dot_git(&self.abs_path.join(path))) } - pub(crate) fn does_git_repository_track_file_path( - &self, - repo: &GitRepository, - file_path: &Path, - ) -> bool { + pub(crate) fn tracks_filepath(&self, repo: &GitRepository, file_path: &Path) -> bool { // Depends on git_repository_for_file_path returning the most specific git repository for a given path - self.git_repository_for_file_path(&self.abs_path.join(file_path)) + self.repo_for(&self.abs_path.join(file_path)) .map_or(false, |r| r.git_dir_path() == repo.git_dir_path()) } @@ -2433,6 +2424,11 @@ impl BackgroundScanner { fs_entry.is_ignored = ignore_stack.is_all(); snapshot.insert_entry(fs_entry, self.fs.as_ref()); + let scan_id = snapshot.scan_id; + if let Some(repo) = snapshot.in_dot_git(&abs_path) { + repo.set_scan_id(scan_id); + } + let mut ancestor_inodes = snapshot.ancestor_inodes_for_path(&path); if metadata.is_dir && !ancestor_inodes.contains(&metadata.inode) { ancestor_inodes.insert(metadata.inode); @@ -3172,13 +3168,9 @@ mod tests { tree.read_with(cx, |tree, _cx| { let tree = tree.as_local().unwrap(); - assert!(tree - .git_repository_for_file_path("c.txt".as_ref()) - .is_none()); + assert!(tree.repo_for("c.txt".as_ref()).is_none()); - let repo = tree - .git_repository_for_file_path("dir1/src/b.txt".as_ref()) - .unwrap(); + let repo = tree.repo_for("dir1/src/b.txt".as_ref()).unwrap(); assert_eq!( repo.content_path(), @@ -3189,9 +3181,7 @@ mod tests { root.path().join("dir1/.git").canonicalize().unwrap() ); - let repo = tree - .git_repository_for_file_path("dir1/deps/dep1/src/a.txt".as_ref()) - .unwrap(); + let repo = tree.repo_for("dir1/deps/dep1/src/a.txt".as_ref()).unwrap(); assert_eq!( repo.content_path(), @@ -3204,23 +3194,23 @@ mod tests { .canonicalize() .unwrap() ); + }); - let repo = tree - .git_repository_for_git_data("dir1/.git/HEAD".as_ref()) - .unwrap(); + let original_scan_id = tree.read_with(cx, |tree, _cx| { + let tree = tree.as_local().unwrap(); + tree.repo_for("dir1/src/b.txt".as_ref()).unwrap().scan_id() + }); - assert_eq!( - repo.content_path(), - root.path().join("dir1").canonicalize().unwrap() + std::fs::write(root.path().join("dir1/.git/random_new_file"), "hello").unwrap(); + tree.flush_fs_events(cx).await; + + tree.read_with(cx, |tree, _cx| { + let tree = tree.as_local().unwrap(); + let new_scan_id = tree.repo_for("dir1/src/b.txt".as_ref()).unwrap().scan_id(); + assert_ne!( + original_scan_id, new_scan_id, + "original {original_scan_id}, new {new_scan_id}" ); - assert_eq!( - repo.git_dir_path(), - root.path().join("dir1/.git").canonicalize().unwrap() - ); - - assert!(tree.does_git_repository_track_file_path(&repo, "dir1/src/b.txt".as_ref())); - assert!(!tree - .does_git_repository_track_file_path(&repo, "dir1/deps/dep1/src/a.txt".as_ref())); }); std::fs::remove_dir_all(root.path().join("dir1/.git")).unwrap(); @@ -3229,9 +3219,7 @@ mod tests { tree.read_with(cx, |tree, _cx| { let tree = tree.as_local().unwrap(); - assert!(tree - .git_repository_for_file_path("dir1/src/b.txt".as_ref()) - .is_none()); + assert!(tree.repo_for("dir1/src/b.txt".as_ref()).is_none()); }); } From 7e5d49487be16511f1246048b221997e9956d8f2 Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 27 Sep 2022 20:06:18 -0400 Subject: [PATCH 078/314] WIP Notifying buffers of head text change Co-Authored-By: Mikayla Maki --- crates/editor/src/items.rs | 4 +- crates/editor/src/multi_buffer.rs | 6 +-- crates/language/src/buffer.rs | 14 +++++-- crates/project/src/fs.rs | 46 ---------------------- crates/project/src/git_repository.rs | 44 +++++++++++++++++++-- crates/project/src/project.rs | 29 +++++++++++++- crates/project/src/worktree.rs | 57 +++++++++++++++++++++++++--- crates/workspace/src/workspace.rs | 12 +++--- 8 files changed, 142 insertions(+), 70 deletions(-) diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 76e1480180..c1082020e5 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -478,13 +478,13 @@ impl Item for Editor { }) } - fn update_git( + fn git_diff_recalc( &mut self, _project: ModelHandle, cx: &mut ViewContext, ) -> Task> { self.buffer().update(cx, |multibuffer, cx| { - multibuffer.update_git(cx); + multibuffer.git_diff_recalc(cx); }); Task::ready(Ok(())) } diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 2f93bc5b09..76093e0496 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -312,13 +312,13 @@ impl MultiBuffer { self.read(cx).symbols_containing(offset, theme) } - pub fn update_git(&mut self, cx: &mut ModelContext) { + pub fn git_diff_recalc(&mut self, cx: &mut ModelContext) { let buffers = self.buffers.borrow(); for buffer_state in buffers.values() { - if buffer_state.buffer.read(cx).needs_git_update() { + if buffer_state.buffer.read(cx).needs_git_diff_recalc() { buffer_state .buffer - .update(cx, |buffer, cx| buffer.update_git(cx)) + .update(cx, |buffer, cx| buffer.git_diff_recalc(cx)) } } } diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 6ecfbc7e62..d1dfb9ec22 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -613,6 +613,7 @@ impl Buffer { cx, ); } + self.update_git(cx); cx.emit(Event::Reloaded); cx.notify(); } @@ -661,12 +662,19 @@ impl Buffer { self.file = Some(new_file); task } + + pub fn update_git(&mut self, cx: &mut ModelContext) { + //Grab head text + - pub fn needs_git_update(&self) -> bool { + self.git_diff_recalc(cx); + } + + pub fn needs_git_diff_recalc(&self) -> bool { self.git_diff_status.diff.needs_update(self) } - pub fn update_git(&mut self, cx: &mut ModelContext) { + pub fn git_diff_recalc(&mut self, cx: &mut ModelContext) { if self.git_diff_status.update_in_progress { self.git_diff_status.update_requested = true; return; @@ -692,7 +700,7 @@ impl Buffer { this.git_diff_status.update_in_progress = false; if this.git_diff_status.update_requested { - this.update_git(cx); + this.git_diff_recalc(cx); } }) } diff --git a/crates/project/src/fs.rs b/crates/project/src/fs.rs index e675ddf8e5..8542030cb7 100644 --- a/crates/project/src/fs.rs +++ b/crates/project/src/fs.rs @@ -34,7 +34,6 @@ pub trait Fs: Send + Sync { async fn remove_file(&self, path: &Path, options: RemoveOptions) -> Result<()>; async fn open_sync(&self, path: &Path) -> Result>; async fn load(&self, path: &Path) -> Result; - async fn load_head_text(&self, path: &Path) -> Option; async fn save(&self, path: &Path, text: &Rope, line_ending: LineEnding) -> Result<()>; async fn canonicalize(&self, path: &Path) -> Result; async fn is_file(&self, path: &Path) -> bool; @@ -48,7 +47,6 @@ pub trait Fs: Send + Sync { path: &Path, latency: Duration, ) -> Pin>>>; - fn open_git_repository(&self, abs_dotgit_path: &Path) -> Option; fn is_fake(&self) -> bool; #[cfg(any(test, feature = "test-support"))] fn as_fake(&self) -> &FakeFs; @@ -168,38 +166,6 @@ impl Fs for RealFs { Ok(text) } - async fn load_head_text(&self, path: &Path) -> Option { - fn logic(path: &Path) -> Result> { - let repo = Repository::open_ext(path, RepositoryOpenFlags::empty(), &[OsStr::new("")])?; - assert!(repo.path().ends_with(".git")); - let repo_root_path = match repo.path().parent() { - Some(root) => root, - None => return Ok(None), - }; - - let relative_path = path.strip_prefix(repo_root_path)?; - let object = repo - .head()? - .peel_to_tree()? - .get_path(relative_path)? - .to_object(&repo)?; - - let content = match object.as_blob() { - Some(blob) => blob.content().to_owned(), - None => return Ok(None), - }; - - let head_text = String::from_utf8(content.to_owned())?; - Ok(Some(head_text)) - } - - match logic(path) { - Ok(value) => return value, - Err(err) => log::error!("Error loading head text: {:?}", err), - } - None - } - async fn save(&self, path: &Path, text: &Rope, line_ending: LineEnding) -> Result<()> { let buffer_size = text.summary().len.min(10 * 1024); let file = smol::fs::File::create(path).await?; @@ -274,10 +240,6 @@ impl Fs for RealFs { }))) } - fn open_git_repository(&self, abs_dotgit_path: &Path) -> Option { - GitRepository::open(abs_dotgit_path) - } - fn is_fake(&self) -> bool { false } @@ -791,10 +753,6 @@ impl Fs for FakeFs { entry.file_content(&path).cloned() } - async fn load_head_text(&self, _: &Path) -> Option { - None - } - async fn save(&self, path: &Path, text: &Rope, line_ending: LineEnding) -> Result<()> { self.simulate_random_delay().await; let path = normalize_path(path); @@ -893,10 +851,6 @@ impl Fs for FakeFs { })) } - fn open_git_repository(&self, _: &Path) -> Option { - None - } - fn is_fake(&self) -> bool { true } diff --git a/crates/project/src/git_repository.rs b/crates/project/src/git_repository.rs index eab031da17..c27b1ba385 100644 --- a/crates/project/src/git_repository.rs +++ b/crates/project/src/git_repository.rs @@ -1,6 +1,7 @@ -use git2::Repository; +use anyhow::Result; +use git2::{Repository as LibGitRepository, RepositoryOpenFlags as LibGitRepositoryOpenFlags}; use parking_lot::Mutex; -use std::{path::Path, sync::Arc}; +use std::{path::Path, sync::Arc, ffi::OsStr}; use util::ResultExt; #[derive(Clone)] @@ -11,12 +12,12 @@ pub struct GitRepository { // Note: if .git is a file, this points to the folder indicated by the .git file git_dir_path: Arc, scan_id: usize, - libgit_repository: Arc>, + libgit_repository: Arc>, } impl GitRepository { pub fn open(dotgit_path: &Path) -> Option { - Repository::open(&dotgit_path) + LibGitRepository::open(&dotgit_path) .log_err() .and_then(|libgit_repository| { Some(Self { @@ -60,4 +61,39 @@ impl GitRepository { let mut git2 = self.libgit_repository.lock(); f(&mut git2) } + + pub async fn load_head_text(&self, file_path: &Path) -> Option { + fn logic(repo: &LibGitRepository, file_path: &Path) -> Result> { + let object = repo + .head()? + .peel_to_tree()? + .get_path(file_path)? + .to_object(&repo)?; + + let content = match object.as_blob() { + Some(blob) => blob.content().to_owned(), + None => return Ok(None), + }; + + let head_text = String::from_utf8(content.to_owned())?; + Ok(Some(head_text)) + } + + match logic(&self.libgit_repository.lock(), file_path) { + Ok(value) => return value, + Err(err) => log::error!("Error loading head text: {:?}", err), + } + None + } +} + +impl std::fmt::Debug for GitRepository { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("GitRepository") + .field("content_path", &self.content_path) + .field("git_dir_path", &self.git_dir_path) + .field("scan_id", &self.scan_id) + .field("libgit_repository", &"LibGitRepository") + .finish() + } } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 78a500585a..4aa3a89d86 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -13,6 +13,7 @@ use client::{proto, Client, PeerId, TypedEnvelope, User, UserStore}; use clock::ReplicaId; use collections::{hash_map, BTreeMap, HashMap, HashSet}; use futures::{future::Shared, AsyncWriteExt, Future, FutureExt, StreamExt, TryFutureExt}; +use git_repository::GitRepository; use gpui::{ AnyModelHandle, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task, UpgradeModelHandle, WeakModelHandle, @@ -4536,7 +4537,9 @@ impl Project { if worktree.read(cx).is_local() { cx.subscribe(worktree, |this, worktree, event, cx| match event { worktree::Event::UpdatedEntries => this.update_local_worktree_buffers(worktree, cx), - worktree::Event::UpdatedGitRepositories(_) => todo!(), + worktree::Event::UpdatedGitRepositories(updated_repos) => { + this.update_local_worktree_buffers_git_repos(updated_repos, cx) + } }) .detach(); } @@ -4644,6 +4647,30 @@ impl Project { } } + fn update_local_worktree_buffers_git_repos( + &mut self, + updated_repos: &[GitRepository], + cx: &mut ModelContext, + ) { + for (buffer_id, buffer) in &self.opened_buffers { + if let Some(buffer) = buffer.upgrade(cx) { + buffer.update(cx, |buffer, cx| { + let updated = updated_repos.iter().any(|repo| { + buffer + .file() + .and_then(|file| file.as_local()) + .map(|file| repo.manages(&file.abs_path(cx))) + .unwrap_or(false) + }); + + if updated { + buffer.update_git(cx); + } + }); + } + } + } + pub fn set_active_path(&mut self, entry: Option, cx: &mut ModelContext) { let new_active_entry = entry.and_then(|project_path| { let worktree = self.worktree_for_id(project_path.worktree_id, cx)?; diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index aead63102b..beef854470 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -467,7 +467,7 @@ impl LocalWorktree { .await?; Ok(cx.add_model(|cx| { let mut buffer = Buffer::from_file(0, contents, head_text, Arc::new(file), cx); - buffer.update_git(cx); + buffer.git_diff_recalc(cx); buffer })) }) @@ -522,16 +522,28 @@ impl LocalWorktree { match self.scan_state() { ScanState::Idle => { - self.snapshot = self.background_snapshot.lock().clone(); + let new_snapshot = self.background_snapshot.lock().clone(); + let updated_repos = self.list_updated_repos(&new_snapshot); + self.snapshot = new_snapshot; + if let Some(share) = self.share.as_mut() { *share.snapshots_tx.borrow_mut() = self.snapshot.clone(); } + cx.emit(Event::UpdatedEntries); + + if !updated_repos.is_empty() { + cx.emit(Event::UpdatedGitRepositories(updated_repos)); + } } ScanState::Initializing => { let is_fake_fs = self.fs.is_fake(); - self.snapshot = self.background_snapshot.lock().clone(); + + let new_snapshot = self.background_snapshot.lock().clone(); + let updated_repos = self.list_updated_repos(&new_snapshot); + self.snapshot = new_snapshot; + self.poll_task = Some(cx.spawn_weak(|this, mut cx| async move { if is_fake_fs { #[cfg(any(test, feature = "test-support"))] @@ -543,7 +555,12 @@ impl LocalWorktree { this.update(&mut cx, |this, cx| this.poll_snapshot(cx)); } })); + cx.emit(Event::UpdatedEntries); + + if !updated_repos.is_empty() { + cx.emit(Event::UpdatedGitRepositories(updated_repos)); + } } _ => { @@ -556,6 +573,34 @@ impl LocalWorktree { cx.notify(); } + fn list_updated_repos(&self, new_snapshot: &LocalSnapshot) -> Vec { + let old_snapshot = &self.snapshot; + + fn diff<'a>( + a: &'a LocalSnapshot, + b: &'a LocalSnapshot, + updated: &mut HashMap<&'a Path, GitRepository>, + ) { + for a_repo in &a.git_repositories { + let matched = b.git_repositories.iter().find(|b_repo| { + a_repo.git_dir_path() == b_repo.git_dir_path() + && a_repo.scan_id() == b_repo.scan_id() + }); + + if matched.is_some() { + updated.insert(a_repo.git_dir_path(), a_repo.clone()); + } + } + } + + let mut updated = HashMap::<&Path, GitRepository>::default(); + + diff(old_snapshot, new_snapshot, &mut updated); + diff(new_snapshot, old_snapshot, &mut updated); + + updated.into_values().collect() + } + pub fn scan_complete(&self) -> impl Future { let mut scan_state_rx = self.last_scan_state_rx.clone(); async move { @@ -606,9 +651,11 @@ impl LocalWorktree { files_included, settings::GitFilesIncluded::All | settings::GitFilesIncluded::OnlyTracked ) { + + let fs = fs.clone(); let abs_path = abs_path.clone(); - let task = async move { fs.load_head_text(&abs_path).await }; + let opt_future = async move { fs.load_head_text(&abs_path).await }; let results = cx.background().spawn(task).await; if files_included == settings::GitFilesIncluded::All { @@ -1495,7 +1542,7 @@ impl LocalSnapshot { .git_repositories .binary_search_by_key(&abs_path.as_path(), |repo| repo.git_dir_path()) { - if let Some(repository) = fs.open_git_repository(&abs_path) { + if let Some(repository) = GitRepository::open(&abs_path) { self.git_repositories.insert(ix, repository); } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 9e8338d289..921fb2de20 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -317,7 +317,7 @@ pub trait Item: View { project: ModelHandle, cx: &mut ViewContext, ) -> Task>; - fn update_git( + fn git_diff_recalc( &mut self, _project: ModelHandle, _cx: &mut ViewContext, @@ -539,7 +539,7 @@ pub trait ItemHandle: 'static + fmt::Debug { ) -> Task>; fn reload(&self, project: ModelHandle, cx: &mut MutableAppContext) -> Task>; - fn update_git( + fn git_diff_recalc( &self, project: ModelHandle, cx: &mut MutableAppContext, @@ -753,7 +753,7 @@ impl ItemHandle for ViewHandle { workspace, cx, |project, mut cx| async move { - cx.update(|cx| item.update_git(project, cx)) + cx.update(|cx| item.git_diff_recalc(project, cx)) .await .log_err(); }, @@ -762,7 +762,7 @@ impl ItemHandle for ViewHandle { let project = workspace.project().downgrade(); cx.spawn_weak(|_, mut cx| async move { if let Some(project) = project.upgrade(&cx) { - cx.update(|cx| item.update_git(project, cx)) + cx.update(|cx| item.git_diff_recalc(project, cx)) .await .log_err(); } @@ -850,12 +850,12 @@ impl ItemHandle for ViewHandle { self.update(cx, |item, cx| item.reload(project, cx)) } - fn update_git( + fn git_diff_recalc( &self, project: ModelHandle, cx: &mut MutableAppContext, ) -> Task> { - self.update(cx, |item, cx| item.update_git(project, cx)) + self.update(cx, |item, cx| item.git_diff_recalc(project, cx)) } fn act_as_type(&self, type_id: TypeId, cx: &AppContext) -> Option { From bf3b3da6edbf654dbda2e0d4f553aa8460da92a2 Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 28 Sep 2022 10:26:30 -0400 Subject: [PATCH 079/314] Build again --- crates/language/src/buffer.rs | 3 +-- crates/project/src/git_repository.rs | 2 +- crates/project/src/worktree.rs | 12 ++++++------ 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index d1dfb9ec22..9d386c14ad 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -662,10 +662,9 @@ impl Buffer { self.file = Some(new_file); task } - + pub fn update_git(&mut self, cx: &mut ModelContext) { //Grab head text - self.git_diff_recalc(cx); } diff --git a/crates/project/src/git_repository.rs b/crates/project/src/git_repository.rs index c27b1ba385..4b46b18391 100644 --- a/crates/project/src/git_repository.rs +++ b/crates/project/src/git_repository.rs @@ -1,7 +1,7 @@ use anyhow::Result; use git2::{Repository as LibGitRepository, RepositoryOpenFlags as LibGitRepositoryOpenFlags}; use parking_lot::Mutex; -use std::{path::Path, sync::Arc, ffi::OsStr}; +use std::{ffi::OsStr, path::Path, sync::Arc}; use util::ResultExt; #[derive(Clone)] diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index beef854470..4885ce104a 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -636,6 +636,7 @@ impl LocalWorktree { let path = Arc::from(path); let abs_path = self.absolutize(&path); let fs = self.fs.clone(); + let snapshot = self.snapshot(); let files_included = cx .global::() @@ -651,12 +652,11 @@ impl LocalWorktree { files_included, settings::GitFilesIncluded::All | settings::GitFilesIncluded::OnlyTracked ) { - - - let fs = fs.clone(); - let abs_path = abs_path.clone(); - let opt_future = async move { fs.load_head_text(&abs_path).await }; - let results = cx.background().spawn(task).await; + let results = if let Some(repo) = snapshot.repo_for(&abs_path) { + repo.load_head_text(&abs_path).await + } else { + None + }; if files_included == settings::GitFilesIncluded::All { results.or_else(|| Some(text.clone())) From d5fd531743680c568e64faa75e1059f20b215453 Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 28 Sep 2022 11:43:33 -0400 Subject: [PATCH 080/314] Move git related things into specialized git crate Co-Authored-By: Mikayla Maki --- Cargo.lock | 22 +++++++- crates/editor/Cargo.toml | 1 + crates/editor/src/element.rs | 2 +- crates/editor/src/multi_buffer.rs | 8 +-- crates/git/Cargo.toml | 22 ++++++++ .../{language/src/git.rs => git/src/diff.rs} | 6 +-- crates/git/src/git.rs | 12 +++++ .../src/repository.rs} | 20 +++---- crates/language/Cargo.toml | 2 +- crates/language/src/buffer.rs | 33 ++++++------ crates/language/src/language.rs | 1 - crates/project/Cargo.toml | 3 +- crates/project/src/fs.rs | 4 -- crates/project/src/project.rs | 45 ++++++++++------ crates/project/src/worktree.rs | 54 +++++++++++-------- crates/util/src/lib.rs | 7 --- crates/util/src/test.rs | 9 ++-- 17 files changed, 151 insertions(+), 100 deletions(-) create mode 100644 crates/git/Cargo.toml rename crates/{language/src/git.rs => git/src/diff.rs} (98%) create mode 100644 crates/git/src/git.rs rename crates/{project/src/git_repository.rs => git/src/repository.rs} (80%) diff --git a/Cargo.lock b/Cargo.lock index 8157327cf2..c8918158be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1697,6 +1697,7 @@ dependencies = [ "env_logger", "futures", "fuzzy", + "git", "gpui", "indoc", "itertools", @@ -2224,6 +2225,23 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "git" +version = "0.1.0" +dependencies = [ + "anyhow", + "clock", + "git2", + "lazy_static", + "log", + "parking_lot 0.11.2", + "smol", + "sum_tree", + "text", + "unindent", + "util", +] + [[package]] name = "git2" version = "0.15.0" @@ -2853,7 +2871,7 @@ dependencies = [ "env_logger", "futures", "fuzzy", - "git2", + "git", "gpui", "lazy_static", "log", @@ -3996,7 +4014,7 @@ dependencies = [ "fsevent", "futures", "fuzzy", - "git2", + "git", "gpui", "ignore", "language", diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index dfd4938742..2ea7473b59 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -25,6 +25,7 @@ clock = { path = "../clock" } collections = { path = "../collections" } context_menu = { path = "../context_menu" } fuzzy = { path = "../fuzzy" } +git = { path = "../git" } gpui = { path = "../gpui" } language = { path = "../language" } lsp = { path = "../lsp" } diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 2e767d72e6..4bc9f9a10b 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -16,6 +16,7 @@ use crate::{ }; use clock::ReplicaId; use collections::{BTreeMap, HashMap}; +use git::diff::{DiffHunk, DiffHunkStatus}; use gpui::{ color::Color, elements::*, @@ -34,7 +35,6 @@ use gpui::{ WeakViewHandle, }; use json::json; -use language::git::{DiffHunk, DiffHunkStatus}; use language::{Bias, DiagnosticSeverity, OffsetUtf16, Selection}; use project::ProjectPath; use settings::Settings; diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 76093e0496..b4e302e3c3 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -4,13 +4,13 @@ pub use anchor::{Anchor, AnchorRangeExt}; use anyhow::Result; use clock::ReplicaId; use collections::{BTreeMap, Bound, HashMap, HashSet}; +use git::diff::DiffHunk; use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task}; pub use language::Completion; use language::{ - char_kind, git::DiffHunk, AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, - Chunk, DiagnosticEntry, Event, File, IndentSize, Language, OffsetRangeExt, Outline, - OutlineItem, Selection, ToOffset as _, ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, - TransactionId, + char_kind, AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, + DiagnosticEntry, Event, File, IndentSize, Language, OffsetRangeExt, Outline, OutlineItem, + Selection, ToOffset as _, ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, }; use smallvec::SmallVec; use std::{ diff --git a/crates/git/Cargo.toml b/crates/git/Cargo.toml new file mode 100644 index 0000000000..79ac56d098 --- /dev/null +++ b/crates/git/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "git" +version = "0.1.0" +edition = "2021" + +[lib] +path = "src/git.rs" + +[dependencies] +anyhow = "1.0.38" +clock = { path = "../clock" } +git2 = { version = "0.15", default-features = false } +lazy_static = "1.4.0" +sum_tree = { path = "../sum_tree" } +text = { path = "../text" } +util = { path = "../util" } +log = { version = "0.4.16", features = ["kv_unstable_serde"] } +smol = "1.2" +parking_lot = "0.11.1" + +[dev-dependencies] +unindent = "0.1.7" diff --git a/crates/language/src/git.rs b/crates/git/src/diff.rs similarity index 98% rename from crates/language/src/git.rs rename to crates/git/src/diff.rs index d713dcbc14..ddaddb7289 100644 --- a/crates/language/src/git.rs +++ b/crates/git/src/diff.rs @@ -259,7 +259,7 @@ mod tests { use text::Buffer; use unindent::Unindent as _; - #[gpui::test] + #[test] fn test_buffer_diff_simple() { let head_text = " one @@ -308,8 +308,4 @@ mod tests { ); } } - - // use rand::rngs::StdRng; - // #[gpui::test(iterations = 100)] - // fn test_buffer_diff_random(mut rng: StdRng) {} } diff --git a/crates/git/src/git.rs b/crates/git/src/git.rs new file mode 100644 index 0000000000..36f54e706a --- /dev/null +++ b/crates/git/src/git.rs @@ -0,0 +1,12 @@ +use std::ffi::OsStr; + +pub use git2 as libgit; +pub use lazy_static::lazy_static; + +pub mod diff; +pub mod repository; + +lazy_static! { + pub static ref DOT_GIT: &'static OsStr = OsStr::new(".git"); + pub static ref GITIGNORE: &'static OsStr = OsStr::new(".gitignore"); +} diff --git a/crates/project/src/git_repository.rs b/crates/git/src/repository.rs similarity index 80% rename from crates/project/src/git_repository.rs rename to crates/git/src/repository.rs index 4b46b18391..a38d13ef0d 100644 --- a/crates/project/src/git_repository.rs +++ b/crates/git/src/repository.rs @@ -1,7 +1,7 @@ use anyhow::Result; -use git2::{Repository as LibGitRepository, RepositoryOpenFlags as LibGitRepositoryOpenFlags}; +use git2::Repository as LibGitRepository; use parking_lot::Mutex; -use std::{ffi::OsStr, path::Path, sync::Arc}; +use std::{path::Path, sync::Arc}; use util::ResultExt; #[derive(Clone)] @@ -53,21 +53,17 @@ impl GitRepository { self.scan_id } - pub(super) fn set_scan_id(&mut self, scan_id: usize) { + pub fn set_scan_id(&mut self, scan_id: usize) { + println!("setting scan id"); self.scan_id = scan_id; } - pub fn with_repo(&mut self, f: F) { - let mut git2 = self.libgit_repository.lock(); - f(&mut git2) - } - - pub async fn load_head_text(&self, file_path: &Path) -> Option { - fn logic(repo: &LibGitRepository, file_path: &Path) -> Result> { + pub async fn load_head_text(&self, relative_file_path: &Path) -> Option { + fn logic(repo: &LibGitRepository, relative_file_path: &Path) -> Result> { let object = repo .head()? .peel_to_tree()? - .get_path(file_path)? + .get_path(relative_file_path)? .to_object(&repo)?; let content = match object.as_blob() { @@ -79,7 +75,7 @@ impl GitRepository { Ok(Some(head_text)) } - match logic(&self.libgit_repository.lock(), file_path) { + match logic(&self.libgit_repository.as_ref().lock(), relative_file_path) { Ok(value) => return value, Err(err) => log::error!("Error loading head text: {:?}", err), } diff --git a/crates/language/Cargo.toml b/crates/language/Cargo.toml index 034b10e89c..7a218acc8e 100644 --- a/crates/language/Cargo.toml +++ b/crates/language/Cargo.toml @@ -25,6 +25,7 @@ client = { path = "../client" } clock = { path = "../clock" } collections = { path = "../collections" } fuzzy = { path = "../fuzzy" } +git = { path = "../git" } gpui = { path = "../gpui" } lsp = { path = "../lsp" } rpc = { path = "../rpc" } @@ -51,7 +52,6 @@ smol = "1.2" tree-sitter = "0.20" tree-sitter-rust = { version = "*", optional = true } tree-sitter-typescript = { version = "*", optional = true } -git2 = { version = "0.15", default-features = false } [dev-dependencies] client = { path = "../client", features = ["test-support"] } diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 9d386c14ad..13fe6daed5 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1,4 +1,3 @@ -use crate::git; pub use crate::{ diagnostic_set::DiagnosticSet, highlight_map::{HighlightId, HighlightMap}, @@ -47,14 +46,14 @@ pub use {tree_sitter_rust, tree_sitter_typescript}; pub use lsp::DiagnosticSeverity; struct GitDiffStatus { - diff: git::BufferDiff, + diff: git::diff::BufferDiff, update_in_progress: bool, update_requested: bool, } pub struct Buffer { text: TextBuffer, - head_text: Option>, + head_text: Option, git_diff_status: GitDiffStatus, file: Option>, saved_version: clock::Global, @@ -83,7 +82,7 @@ pub struct Buffer { pub struct BufferSnapshot { text: text::BufferSnapshot, - pub git_diff: git::BufferDiff, + pub git_diff: git::diff::BufferDiff, pub(crate) syntax: SyntaxSnapshot, file: Option>, diagnostics: DiagnosticSet, @@ -353,7 +352,7 @@ impl Buffer { ) -> Self { Self::build( TextBuffer::new(replica_id, cx.model_id() as u64, base_text.into()), - head_text.map(|h| Arc::new(h.into())), + head_text.map(|h| h.into().into_boxed_str().into()), Some(file), ) } @@ -364,7 +363,11 @@ impl Buffer { file: Option>, ) -> Result { let buffer = TextBuffer::new(replica_id, message.id, message.base_text); - let mut this = Self::build(buffer, message.head_text.map(|text| Arc::new(text)), file); + let mut this = Self::build( + buffer, + message.head_text.map(|text| text.into_boxed_str().into()), + file, + ); this.text.set_line_ending(proto::deserialize_line_ending( proto::LineEnding::from_i32(message.line_ending) .ok_or_else(|| anyhow!("missing line_ending"))?, @@ -420,11 +423,7 @@ impl Buffer { self } - fn build( - buffer: TextBuffer, - head_text: Option>, - file: Option>, - ) -> Self { + fn build(buffer: TextBuffer, head_text: Option, file: Option>) -> Self { let saved_mtime = if let Some(file) = file.as_ref() { file.mtime() } else { @@ -440,7 +439,7 @@ impl Buffer { text: buffer, head_text, git_diff_status: GitDiffStatus { - diff: git::BufferDiff::new(), + diff: git::diff::BufferDiff::new(), update_in_progress: false, update_requested: false, }, @@ -613,7 +612,7 @@ impl Buffer { cx, ); } - self.update_git(cx); + self.git_diff_recalc(cx); cx.emit(Event::Reloaded); cx.notify(); } @@ -663,9 +662,8 @@ impl Buffer { task } - pub fn update_git(&mut self, cx: &mut ModelContext) { - //Grab head text - + pub fn update_head_text(&mut self, head_text: Option, cx: &mut ModelContext) { + self.head_text = head_text; self.git_diff_recalc(cx); } @@ -674,6 +672,7 @@ impl Buffer { } pub fn git_diff_recalc(&mut self, cx: &mut ModelContext) { + println!("recalc"); if self.git_diff_status.update_in_progress { self.git_diff_status.update_requested = true; return; @@ -2221,7 +2220,7 @@ impl BufferSnapshot { pub fn git_diff_hunks_in_range<'a>( &'a self, query_row_range: Range, - ) -> impl 'a + Iterator> { + ) -> impl 'a + Iterator> { self.git_diff.hunks_in_range(query_row_range, self) } diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 8e2fe601e7..780f6e75b5 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -1,6 +1,5 @@ mod buffer; mod diagnostic_set; -pub mod git; mod highlight_map; mod outline; pub mod proto; diff --git a/crates/project/Cargo.toml b/crates/project/Cargo.toml index 8ca01eac2c..1e45e3c6ed 100644 --- a/crates/project/Cargo.toml +++ b/crates/project/Cargo.toml @@ -24,6 +24,7 @@ collections = { path = "../collections" } db = { path = "../db" } fsevent = { path = "../fsevent" } fuzzy = { path = "../fuzzy" } +git = { path = "../git" } gpui = { path = "../gpui" } language = { path = "../language" } lsp = { path = "../lsp" } @@ -52,8 +53,6 @@ smol = "1.2.5" thiserror = "1.0.29" toml = "0.5" rocksdb = "0.18" -git2 = { version = "0.15", default-features = false } - [dev-dependencies] client = { path = "../client", features = ["test-support"] } diff --git a/crates/project/src/fs.rs b/crates/project/src/fs.rs index 8542030cb7..6a496910a0 100644 --- a/crates/project/src/fs.rs +++ b/crates/project/src/fs.rs @@ -1,11 +1,9 @@ use anyhow::{anyhow, Result}; use fsevent::EventStream; use futures::{future::BoxFuture, Stream, StreamExt}; -use language::git::libgit::{Repository, RepositoryOpenFlags}; use language::LineEnding; use smol::io::{AsyncReadExt, AsyncWriteExt}; use std::{ - ffi::OsStr, io, os::unix::fs::MetadataExt, path::{Component, Path, PathBuf}, @@ -22,8 +20,6 @@ use futures::lock::Mutex; #[cfg(any(test, feature = "test-support"))] use std::sync::{Arc, Weak}; -use crate::git_repository::GitRepository; - #[async_trait::async_trait] pub trait Fs: Send + Sync { async fn create_dir(&self, path: &Path) -> Result<()>; diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 4aa3a89d86..57af588c68 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1,5 +1,4 @@ pub mod fs; -mod git_repository; mod ignore; mod lsp_command; pub mod search; @@ -13,7 +12,7 @@ use client::{proto, Client, PeerId, TypedEnvelope, User, UserStore}; use clock::ReplicaId; use collections::{hash_map, BTreeMap, HashMap, HashSet}; use futures::{future::Shared, AsyncWriteExt, Future, FutureExt, StreamExt, TryFutureExt}; -use git_repository::GitRepository; +use git::repository::GitRepository; use gpui::{ AnyModelHandle, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task, UpgradeModelHandle, WeakModelHandle, @@ -4538,6 +4537,7 @@ impl Project { cx.subscribe(worktree, |this, worktree, event, cx| match event { worktree::Event::UpdatedEntries => this.update_local_worktree_buffers(worktree, cx), worktree::Event::UpdatedGitRepositories(updated_repos) => { + println!("{updated_repos:#?}"); this.update_local_worktree_buffers_git_repos(updated_repos, cx) } }) @@ -4649,24 +4649,35 @@ impl Project { fn update_local_worktree_buffers_git_repos( &mut self, - updated_repos: &[GitRepository], + repos: &[GitRepository], cx: &mut ModelContext, ) { - for (buffer_id, buffer) in &self.opened_buffers { - if let Some(buffer) = buffer.upgrade(cx) { - buffer.update(cx, |buffer, cx| { - let updated = updated_repos.iter().any(|repo| { - buffer - .file() - .and_then(|file| file.as_local()) - .map(|file| repo.manages(&file.abs_path(cx))) - .unwrap_or(false) - }); + //TODO: Produce protos - if updated { - buffer.update_git(cx); - } - }); + for (_, buffer) in &self.opened_buffers { + if let Some(buffer) = buffer.upgrade(cx) { + let file = match buffer.read(cx).file().and_then(|file| file.as_local()) { + Some(file) => file, + None => return, + }; + let path = file.path().clone(); + let abs_path = file.abs_path(cx); + println!("got file"); + + let repo = match repos.iter().find(|repo| repo.manages(&abs_path)) { + Some(repo) => repo.clone(), + None => return, + }; + println!("got repo"); + + cx.spawn(|_, mut cx| async move { + let head_text = repo.load_head_text(&path).await; + buffer.update(&mut cx, |buffer, cx| { + println!("got calling update"); + buffer.update_head_text(head_text, cx); + }); + }) + .detach(); } } } diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 4885ce104a..7fd37dc016 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -1,10 +1,9 @@ -use crate::{copy_recursive, git_repository::GitRepository, ProjectEntryId, RemoveOptions}; - use super::{ fs::{self, Fs}, ignore::IgnoreStack, DiagnosticSummary, }; +use crate::{copy_recursive, ProjectEntryId, RemoveOptions}; use ::ignore::gitignore::{Gitignore, GitignoreBuilder}; use anyhow::{anyhow, Context, Result}; use client::{proto, Client}; @@ -18,6 +17,8 @@ use futures::{ Stream, StreamExt, }; use fuzzy::CharBag; +use git::repository::GitRepository; +use git::{DOT_GIT, GITIGNORE}; use gpui::{ executor, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task, @@ -48,7 +49,7 @@ use std::{ time::{Duration, SystemTime}, }; use sum_tree::{Bias, Edit, SeekTarget, SumTree, TreeMap, TreeSet}; -use util::{ResultExt, TryFutureExt, DOT_GIT, GITIGNORE}; +use util::{ResultExt, TryFutureExt}; #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)] pub struct WorktreeId(usize); @@ -523,7 +524,10 @@ impl LocalWorktree { match self.scan_state() { ScanState::Idle => { let new_snapshot = self.background_snapshot.lock().clone(); - let updated_repos = self.list_updated_repos(&new_snapshot); + let updated_repos = Self::list_updated_repos( + &self.snapshot.git_repositories, + &new_snapshot.git_repositories, + ); self.snapshot = new_snapshot; if let Some(share) = self.share.as_mut() { @@ -541,7 +545,10 @@ impl LocalWorktree { let is_fake_fs = self.fs.is_fake(); let new_snapshot = self.background_snapshot.lock().clone(); - let updated_repos = self.list_updated_repos(&new_snapshot); + let updated_repos = Self::list_updated_repos( + &self.snapshot.git_repositories, + &new_snapshot.git_repositories, + ); self.snapshot = new_snapshot; self.poll_task = Some(cx.spawn_weak(|this, mut cx| async move { @@ -573,16 +580,20 @@ impl LocalWorktree { cx.notify(); } - fn list_updated_repos(&self, new_snapshot: &LocalSnapshot) -> Vec { - let old_snapshot = &self.snapshot; + fn list_updated_repos( + old_repos: &[GitRepository], + new_repos: &[GitRepository], + ) -> Vec { + println!("old repos: {:#?}", old_repos); + println!("new repos: {:#?}", new_repos); fn diff<'a>( - a: &'a LocalSnapshot, - b: &'a LocalSnapshot, + a: &'a [GitRepository], + b: &'a [GitRepository], updated: &mut HashMap<&'a Path, GitRepository>, ) { - for a_repo in &a.git_repositories { - let matched = b.git_repositories.iter().find(|b_repo| { + for a_repo in a { + let matched = b.iter().find(|b_repo| { a_repo.git_dir_path() == b_repo.git_dir_path() && a_repo.scan_id() == b_repo.scan_id() }); @@ -595,10 +606,10 @@ impl LocalWorktree { let mut updated = HashMap::<&Path, GitRepository>::default(); - diff(old_snapshot, new_snapshot, &mut updated); - diff(new_snapshot, old_snapshot, &mut updated); + diff(old_repos, new_repos, &mut updated); + diff(new_repos, old_repos, &mut updated); - updated.into_values().collect() + dbg!(updated.into_values().collect()) } pub fn scan_complete(&self) -> impl Future { @@ -653,7 +664,7 @@ impl LocalWorktree { settings::GitFilesIncluded::All | settings::GitFilesIncluded::OnlyTracked ) { let results = if let Some(repo) = snapshot.repo_for(&abs_path) { - repo.load_head_text(&abs_path).await + repo.load_head_text(&path).await } else { None }; @@ -1362,6 +1373,7 @@ impl LocalSnapshot { } pub(crate) fn in_dot_git(&mut self, path: &Path) -> Option<&mut GitRepository> { + println!("chechking {path:?}"); self.git_repositories .iter_mut() .rev() //git_repository is ordered lexicographically @@ -1510,7 +1522,6 @@ impl LocalSnapshot { parent_path: Arc, entries: impl IntoIterator, ignore: Option>, - fs: &dyn Fs, ) { let mut parent_entry = if let Some(parent_entry) = self.entries_by_path.get(&PathKey(parent_path.clone()), &()) @@ -2391,12 +2402,9 @@ impl BackgroundScanner { new_entries.push(child_entry); } - self.snapshot.lock().populate_dir( - job.path.clone(), - new_entries, - new_ignore, - self.fs.as_ref(), - ); + self.snapshot + .lock() + .populate_dir(job.path.clone(), new_entries, new_ignore); for new_job in new_jobs { job.scan_queue.send(new_job).await.unwrap(); } @@ -2595,7 +2603,7 @@ impl BackgroundScanner { .git_repositories .iter() .cloned() - .filter(|repo| git2::Repository::open(repo.git_dir_path()).is_ok()) + .filter(|repo| git::libgit::Repository::open(repo.git_dir_path()).is_ok()) .collect(); snapshot.git_repositories = new_repos; diff --git a/crates/util/src/lib.rs b/crates/util/src/lib.rs index 52bf70e3a7..97f409f410 100644 --- a/crates/util/src/lib.rs +++ b/crates/util/src/lib.rs @@ -2,20 +2,13 @@ pub mod test; use futures::Future; -use lazy_static::lazy_static; use std::{ cmp::Ordering, - ffi::OsStr, ops::AddAssign, pin::Pin, task::{Context, Poll}, }; -lazy_static! { - pub static ref DOT_GIT: &'static OsStr = OsStr::new(".git"); - pub static ref GITIGNORE: &'static OsStr = OsStr::new(".gitignore"); -} - pub fn truncate(s: &str, max_chars: usize) -> &str { match s.char_indices().nth(max_chars) { None => s, diff --git a/crates/util/src/test.rs b/crates/util/src/test.rs index 4e4716434e..96d13f4c81 100644 --- a/crates/util/src/test.rs +++ b/crates/util/src/test.rs @@ -2,14 +2,15 @@ mod assertions; mod marked_text; use git2; -use std::path::{Path, PathBuf}; +use std::{ + ffi::OsStr, + path::{Path, PathBuf}, +}; use tempdir::TempDir; pub use assertions::*; pub use marked_text::*; -use crate::DOT_GIT; - pub fn temp_tree(tree: serde_json::Value) -> TempDir { let dir = TempDir::new("").unwrap(); write_tree(dir.path(), tree); @@ -28,7 +29,7 @@ fn write_tree(path: &Path, tree: serde_json::Value) { Value::Object(_) => { fs::create_dir(&path).unwrap(); - if path.file_name() == Some(&DOT_GIT) { + if path.file_name() == Some(&OsStr::new(".git")) { git2::Repository::init(&path.parent().unwrap()).unwrap(); } From 71b2126eca51cd3a5c9796a86aad6800a33e9184 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 28 Sep 2022 11:42:22 -0700 Subject: [PATCH 081/314] WIP, re-doing fs and fake git repos --- Cargo.lock | 1 + crates/git/Cargo.toml | 2 + crates/git/src/repository.rs | 123 ++++++++++++++++++++++++++++----- crates/language/src/buffer.rs | 1 - crates/project/src/fs.rs | 10 +++ crates/project/src/project.rs | 8 +-- crates/project/src/worktree.rs | 50 +++++++------- 7 files changed, 145 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c8918158be..3c87f336de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2230,6 +2230,7 @@ name = "git" version = "0.1.0" dependencies = [ "anyhow", + "async-trait", "clock", "git2", "lazy_static", diff --git a/crates/git/Cargo.toml b/crates/git/Cargo.toml index 79ac56d098..7ef9a953ba 100644 --- a/crates/git/Cargo.toml +++ b/crates/git/Cargo.toml @@ -17,6 +17,8 @@ util = { path = "../util" } log = { version = "0.4.16", features = ["kv_unstable_serde"] } smol = "1.2" parking_lot = "0.11.1" +async-trait = "0.1" + [dev-dependencies] unindent = "0.1.7" diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs index a38d13ef0d..19ba0d1238 100644 --- a/crates/git/src/repository.rs +++ b/crates/git/src/repository.rs @@ -4,8 +4,29 @@ use parking_lot::Mutex; use std::{path::Path, sync::Arc}; use util::ResultExt; +#[async_trait::async_trait] +pub trait GitRepository: Send + Sync + std::fmt::Debug { + fn manages(&self, path: &Path) -> bool; + + fn in_dot_git(&self, path: &Path) -> bool; + + fn content_path(&self) -> &Path; + + fn git_dir_path(&self) -> &Path; + + fn scan_id(&self) -> usize; + + fn set_scan_id(&mut self, scan_id: usize); + + fn git_repo(&self) -> Arc>; + + fn boxed_clone(&self) -> Box; + + async fn load_head_text(&self, relative_file_path: &Path) -> Option; +} + #[derive(Clone)] -pub struct GitRepository { +pub struct RealGitRepository { // Path to folder containing the .git file or directory content_path: Arc, // Path to the actual .git folder. @@ -15,50 +36,48 @@ pub struct GitRepository { libgit_repository: Arc>, } -impl GitRepository { - pub fn open(dotgit_path: &Path) -> Option { +impl RealGitRepository { + pub fn open(dotgit_path: &Path) -> Option> { LibGitRepository::open(&dotgit_path) .log_err() - .and_then(|libgit_repository| { - Some(Self { + .and_then::, _>(|libgit_repository| { + Some(Box::new(Self { content_path: libgit_repository.workdir()?.into(), git_dir_path: dotgit_path.canonicalize().log_err()?.into(), scan_id: 0, libgit_repository: Arc::new(parking_lot::Mutex::new(libgit_repository)), - }) + })) }) } +} - pub fn manages(&self, path: &Path) -> bool { +#[async_trait::async_trait] +impl GitRepository for RealGitRepository { + fn manages(&self, path: &Path) -> bool { path.canonicalize() .map(|path| path.starts_with(&self.content_path)) .unwrap_or(false) } - pub fn in_dot_git(&self, path: &Path) -> bool { + fn in_dot_git(&self, path: &Path) -> bool { path.canonicalize() .map(|path| path.starts_with(&self.git_dir_path)) .unwrap_or(false) } - pub fn content_path(&self) -> &Path { + fn content_path(&self) -> &Path { self.content_path.as_ref() } - pub fn git_dir_path(&self) -> &Path { + fn git_dir_path(&self) -> &Path { self.git_dir_path.as_ref() } - pub fn scan_id(&self) -> usize { + fn scan_id(&self) -> usize { self.scan_id } - pub fn set_scan_id(&mut self, scan_id: usize) { - println!("setting scan id"); - self.scan_id = scan_id; - } - - pub async fn load_head_text(&self, relative_file_path: &Path) -> Option { + async fn load_head_text(&self, relative_file_path: &Path) -> Option { fn logic(repo: &LibGitRepository, relative_file_path: &Path) -> Result> { let object = repo .head()? @@ -81,9 +100,21 @@ impl GitRepository { } None } + + fn git_repo(&self) -> Arc> { + self.libgit_repository.clone() + } + + fn set_scan_id(&mut self, scan_id: usize) { + self.scan_id = scan_id; + } + + fn boxed_clone(&self) -> Box { + Box::new(self.clone()) + } } -impl std::fmt::Debug for GitRepository { +impl std::fmt::Debug for RealGitRepository { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("GitRepository") .field("content_path", &self.content_path) @@ -93,3 +124,59 @@ impl std::fmt::Debug for GitRepository { .finish() } } + +#[derive(Debug, Clone)] +pub struct FakeGitRepository { + content_path: Arc, + git_dir_path: Arc, + scan_id: usize, +} + +impl FakeGitRepository { + pub fn open(dotgit_path: &Path, scan_id: usize) -> Box { + Box::new(FakeGitRepository { + content_path: dotgit_path.parent().unwrap().into(), + git_dir_path: dotgit_path.into(), + scan_id, + }) + } +} + +#[async_trait::async_trait] +impl GitRepository for FakeGitRepository { + fn manages(&self, path: &Path) -> bool { + path.starts_with(self.content_path()) + } + + fn in_dot_git(&self, path: &Path) -> bool { + path.starts_with(self.git_dir_path()) + } + + fn content_path(&self) -> &Path { + &self.content_path + } + + fn git_dir_path(&self) -> &Path { + &self.git_dir_path + } + + fn scan_id(&self) -> usize { + self.scan_id + } + + async fn load_head_text(&self, _: &Path) -> Option { + unimplemented!() + } + + fn git_repo(&self) -> Arc> { + unimplemented!() + } + + fn set_scan_id(&mut self, scan_id: usize) { + self.scan_id = scan_id; + } + + fn boxed_clone(&self) -> Box { + Box::new(self.clone()) + } +} diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 13fe6daed5..0268f1cc68 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -672,7 +672,6 @@ impl Buffer { } pub fn git_diff_recalc(&mut self, cx: &mut ModelContext) { - println!("recalc"); if self.git_diff_status.update_in_progress { self.git_diff_status.update_requested = true; return; diff --git a/crates/project/src/fs.rs b/crates/project/src/fs.rs index 6a496910a0..4b27a23856 100644 --- a/crates/project/src/fs.rs +++ b/crates/project/src/fs.rs @@ -1,6 +1,7 @@ use anyhow::{anyhow, Result}; use fsevent::EventStream; use futures::{future::BoxFuture, Stream, StreamExt}; +use git::repository::{FakeGitRepository, GitRepository, RealGitRepository}; use language::LineEnding; use smol::io::{AsyncReadExt, AsyncWriteExt}; use std::{ @@ -43,6 +44,7 @@ pub trait Fs: Send + Sync { path: &Path, latency: Duration, ) -> Pin>>>; + async fn open_repo(&self, abs_dot_git: &Path) -> Option>; fn is_fake(&self) -> bool; #[cfg(any(test, feature = "test-support"))] fn as_fake(&self) -> &FakeFs; @@ -236,6 +238,10 @@ impl Fs for RealFs { }))) } + fn open_repo(&self, abs_dot_git: &Path) -> Option> { + RealGitRepository::open(&abs_dot_git) + } + fn is_fake(&self) -> bool { false } @@ -847,6 +853,10 @@ impl Fs for FakeFs { })) } + fn open_repo(&self, abs_dot_git: &Path) -> Option> { + Some(FakeGitRepository::open(abs_dot_git.into(), 0)) + } + fn is_fake(&self) -> bool { true } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 57af588c68..a2a49c9c93 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4537,7 +4537,6 @@ impl Project { cx.subscribe(worktree, |this, worktree, event, cx| match event { worktree::Event::UpdatedEntries => this.update_local_worktree_buffers(worktree, cx), worktree::Event::UpdatedGitRepositories(updated_repos) => { - println!("{updated_repos:#?}"); this.update_local_worktree_buffers_git_repos(updated_repos, cx) } }) @@ -4649,7 +4648,7 @@ impl Project { fn update_local_worktree_buffers_git_repos( &mut self, - repos: &[GitRepository], + repos: &[Box], cx: &mut ModelContext, ) { //TODO: Produce protos @@ -4662,18 +4661,15 @@ impl Project { }; let path = file.path().clone(); let abs_path = file.abs_path(cx); - println!("got file"); let repo = match repos.iter().find(|repo| repo.manages(&abs_path)) { - Some(repo) => repo.clone(), + Some(repo) => repo.boxed_clone(), None => return, }; - println!("got repo"); cx.spawn(|_, mut cx| async move { let head_text = repo.load_head_text(&path).await; buffer.update(&mut cx, |buffer, cx| { - println!("got calling update"); buffer.update_head_text(head_text, cx); }); }) diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 7fd37dc016..ae55659f98 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -100,7 +100,7 @@ pub struct Snapshot { pub struct LocalSnapshot { abs_path: Arc, ignores_by_parent_abs_path: HashMap, (Arc, usize)>, - git_repositories: Vec, + git_repositories: Vec>, removed_entry_ids: HashMap, next_entry_id: Arc, snapshot: Snapshot, @@ -115,7 +115,7 @@ impl Clone for LocalSnapshot { git_repositories: self .git_repositories .iter() - .map(|repo| repo.clone()) + .map(|repo| repo.boxed_clone()) .collect(), removed_entry_ids: self.removed_entry_ids.clone(), next_entry_id: self.next_entry_id.clone(), @@ -157,7 +157,7 @@ struct ShareState { pub enum Event { UpdatedEntries, - UpdatedGitRepositories(Vec), + UpdatedGitRepositories(Vec>), } impl Entity for Worktree { @@ -581,16 +581,13 @@ impl LocalWorktree { } fn list_updated_repos( - old_repos: &[GitRepository], - new_repos: &[GitRepository], - ) -> Vec { - println!("old repos: {:#?}", old_repos); - println!("new repos: {:#?}", new_repos); - + old_repos: &[Box], + new_repos: &[Box], + ) -> Vec> { fn diff<'a>( - a: &'a [GitRepository], - b: &'a [GitRepository], - updated: &mut HashMap<&'a Path, GitRepository>, + a: &'a [Box], + b: &'a [Box], + updated: &mut HashMap<&'a Path, Box>, ) { for a_repo in a { let matched = b.iter().find(|b_repo| { @@ -599,17 +596,17 @@ impl LocalWorktree { }); if matched.is_some() { - updated.insert(a_repo.git_dir_path(), a_repo.clone()); + updated.insert(a_repo.git_dir_path(), a_repo.boxed_clone()); } } } - let mut updated = HashMap::<&Path, GitRepository>::default(); + let mut updated = HashMap::<&Path, Box>::default(); diff(old_repos, new_repos, &mut updated); diff(new_repos, old_repos, &mut updated); - dbg!(updated.into_values().collect()) + updated.into_values().collect() } pub fn scan_complete(&self) -> impl Future { @@ -1364,23 +1361,22 @@ impl LocalSnapshot { } // Gives the most specific git repository for a given path - pub(crate) fn repo_for(&self, path: &Path) -> Option { + pub(crate) fn repo_for(&self, path: &Path) -> Option> { self.git_repositories .iter() .rev() //git_repository is ordered lexicographically .find(|repo| repo.manages(&self.abs_path.join(path))) - .map(|repo| repo.clone()) + .map(|repo| repo.boxed_clone()) } - pub(crate) fn in_dot_git(&mut self, path: &Path) -> Option<&mut GitRepository> { - println!("chechking {path:?}"); + pub(crate) fn in_dot_git(&mut self, path: &Path) -> Option<&mut Box> { self.git_repositories .iter_mut() .rev() //git_repository is ordered lexicographically .find(|repo| repo.in_dot_git(&self.abs_path.join(path))) } - pub(crate) fn tracks_filepath(&self, repo: &GitRepository, file_path: &Path) -> bool { + pub(crate) fn _tracks_filepath(&self, repo: &dyn GitRepository, file_path: &Path) -> bool { // Depends on git_repository_for_file_path returning the most specific git repository for a given path self.repo_for(&self.abs_path.join(file_path)) .map_or(false, |r| r.git_dir_path() == repo.git_dir_path()) @@ -1522,6 +1518,7 @@ impl LocalSnapshot { parent_path: Arc, entries: impl IntoIterator, ignore: Option>, + fs: &dyn Fs, ) { let mut parent_entry = if let Some(parent_entry) = self.entries_by_path.get(&PathKey(parent_path.clone()), &()) @@ -1553,7 +1550,7 @@ impl LocalSnapshot { .git_repositories .binary_search_by_key(&abs_path.as_path(), |repo| repo.git_dir_path()) { - if let Some(repository) = GitRepository::open(&abs_path) { + if let Some(repository) = fs.open_repo(abs_path.as_path()) { self.git_repositories.insert(ix, repository); } } @@ -2402,9 +2399,12 @@ impl BackgroundScanner { new_entries.push(child_entry); } - self.snapshot - .lock() - .populate_dir(job.path.clone(), new_entries, new_ignore); + self.snapshot.lock().populate_dir( + job.path.clone(), + new_entries, + new_ignore, + self.fs.as_ref(), + ); for new_job in new_jobs { job.scan_queue.send(new_job).await.unwrap(); } @@ -2602,7 +2602,7 @@ impl BackgroundScanner { let new_repos = snapshot .git_repositories .iter() - .cloned() + .map(|repo| repo.boxed_clone()) .filter(|repo| git::libgit::Repository::open(repo.git_dir_path()).is_ok()) .collect(); From f7714a25d1556fe3ac3efa49cc4abbd883fb24b7 Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 28 Sep 2022 16:52:24 -0400 Subject: [PATCH 082/314] Don't pretend this is async --- crates/project/src/fs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/project/src/fs.rs b/crates/project/src/fs.rs index 4b27a23856..cc1f6101f4 100644 --- a/crates/project/src/fs.rs +++ b/crates/project/src/fs.rs @@ -44,7 +44,7 @@ pub trait Fs: Send + Sync { path: &Path, latency: Duration, ) -> Pin>>>; - async fn open_repo(&self, abs_dot_git: &Path) -> Option>; + fn open_repo(&self, abs_dot_git: &Path) -> Option>; fn is_fake(&self) -> bool; #[cfg(any(test, feature = "test-support"))] fn as_fake(&self) -> &FakeFs; From 113d3b88d0e1aeb31d8488fe1296bc563c4d842e Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 28 Sep 2022 14:16:21 -0700 Subject: [PATCH 083/314] Added test, and fix, for changed_repos method on LocalWorktree --- crates/project/src/worktree.rs | 52 +++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index ae55659f98..81eec4987f 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -524,7 +524,7 @@ impl LocalWorktree { match self.scan_state() { ScanState::Idle => { let new_snapshot = self.background_snapshot.lock().clone(); - let updated_repos = Self::list_updated_repos( + let updated_repos = Self::changed_repos( &self.snapshot.git_repositories, &new_snapshot.git_repositories, ); @@ -545,7 +545,7 @@ impl LocalWorktree { let is_fake_fs = self.fs.is_fake(); let new_snapshot = self.background_snapshot.lock().clone(); - let updated_repos = Self::list_updated_repos( + let updated_repos = Self::changed_repos( &self.snapshot.git_repositories, &new_snapshot.git_repositories, ); @@ -580,7 +580,7 @@ impl LocalWorktree { cx.notify(); } - fn list_updated_repos( + fn changed_repos( old_repos: &[Box], new_repos: &[Box], ) -> Vec> { @@ -595,7 +595,7 @@ impl LocalWorktree { && a_repo.scan_id() == b_repo.scan_id() }); - if matched.is_some() { + if matched.is_none() { updated.insert(a_repo.git_dir_path(), a_repo.boxed_clone()); } } @@ -2955,6 +2955,7 @@ mod tests { use anyhow::Result; use client::test::FakeHttpClient; use fs::RealFs; + use git::repository::FakeGitRepository; use gpui::{executor::Deterministic, TestAppContext}; use rand::prelude::*; use serde_json::json; @@ -3278,6 +3279,49 @@ mod tests { }); } + #[test] + fn test_changed_repos() { + let prev_repos: Vec> = vec![ + FakeGitRepository::open(Path::new("/.git"), 0), + FakeGitRepository::open(Path::new("/a/.git"), 0), + FakeGitRepository::open(Path::new("/a/b/.git"), 0), + ]; + + let new_repos: Vec> = vec![ + FakeGitRepository::open(Path::new("/a/.git"), 1), + FakeGitRepository::open(Path::new("/a/b/.git"), 0), + FakeGitRepository::open(Path::new("/a/c/.git"), 0), + ]; + + let res = LocalWorktree::changed_repos(&prev_repos, &new_repos); + + dbg!(&res); + + // Deletion retained + assert!(res + .iter() + .find(|repo| repo.git_dir_path() == Path::new("/.git") && repo.scan_id() == 0) + .is_some()); + + // Update retained + assert!(res + .iter() + .find(|repo| repo.git_dir_path() == Path::new("/a/.git") && repo.scan_id() == 1) + .is_some()); + + // Addition retained + assert!(res + .iter() + .find(|repo| repo.git_dir_path() == Path::new("/a/c/.git") && repo.scan_id() == 0) + .is_some()); + + // Nochange, not retained + assert!(res + .iter() + .find(|repo| repo.git_dir_path() == Path::new("/a/b/.git") && repo.scan_id() == 0) + .is_none()); + } + #[gpui::test] async fn test_write_file(cx: &mut TestAppContext) { let dir = temp_tree(json!({ From 8a2430090b11cb01d6c94d78a9def3f96d8a9a2d Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 21 Sep 2022 10:39:03 -0400 Subject: [PATCH 084/314] WIP Git gutter styling --- crates/editor/src/element.rs | 6 +++--- styles/src/styleTree/editor.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 4bc9f9a10b..82bd260819 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -567,7 +567,7 @@ impl EditorElement { let start_y = row as f32 * line_height + offset - scroll_top; let end_y = start_y + line_height; - let width = 0.4 * line_height; + let width = 0.275 * line_height; let highlight_origin = bounds.origin() + vec2f(-width, start_y); let highlight_size = vec2f(width * 2., end_y - start_y); let highlight_bounds = RectF::new(highlight_origin, highlight_size); @@ -589,7 +589,7 @@ impl EditorElement { let start_y = start_row as f32 * line_height - scroll_top; let end_y = end_row as f32 * line_height - scroll_top; - let width = 0.22 * line_height; + let width = 0.12 * line_height; let highlight_origin = bounds.origin() + vec2f(-width, start_y); let highlight_size = vec2f(width * 2., end_y - start_y); let highlight_bounds = RectF::new(highlight_origin, highlight_size); @@ -598,7 +598,7 @@ impl EditorElement { bounds: highlight_bounds, background: Some(color), border: Border::new(0., Color::transparent_black()), - corner_radius: 0.2 * line_height, + corner_radius: 0.05 * line_height, }); } diff --git a/styles/src/styleTree/editor.ts b/styles/src/styleTree/editor.ts index 29d6857964..bd01c3b845 100644 --- a/styles/src/styleTree/editor.ts +++ b/styles/src/styleTree/editor.ts @@ -60,9 +60,9 @@ export default function editor(theme: Theme) { indicator: iconColor(theme, "secondary"), verticalScale: 0.618 }, - diffBackgroundDeleted: theme.ramps.red(0.3).hex(), - diffBackgroundInserted: theme.ramps.green(0.3).hex(), - diffBackgroundModified: theme.ramps.blue(0.3).hex(), + diffBackgroundDeleted: theme.iconColor.error, + diffBackgroundInserted: theme.iconColor.ok, + diffBackgroundModified: theme.iconColor.warning, documentHighlightReadBackground: theme.editor.highlight.occurrence, documentHighlightWriteBackground: theme.editor.highlight.activeOccurrence, errorColor: theme.textColor.error, From b395fbb3f2214564b1f4e5cf8afcadbbe43748b2 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 21 Sep 2022 15:39:51 -0400 Subject: [PATCH 085/314] wip --- crates/editor/src/element.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 82bd260819..b8731f8707 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -589,7 +589,7 @@ impl EditorElement { let start_y = start_row as f32 * line_height - scroll_top; let end_y = end_row as f32 * line_height - scroll_top; - let width = 0.12 * line_height; + let width = 0.16 * line_height; let highlight_origin = bounds.origin() + vec2f(-width, start_y); let highlight_size = vec2f(width * 2., end_y - start_y); let highlight_bounds = RectF::new(highlight_origin, highlight_size); From 9fe6a5e83e1c6a8cdfbe3669a25576fed0a4dbbe Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 28 Sep 2022 14:58:52 -0700 Subject: [PATCH 086/314] made git stuff slightly more themable --- crates/editor/src/element.rs | 18 ++++++++++++++---- crates/theme/src/theme.rs | 3 +++ styles/src/styleTree/editor.ts | 3 +++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index b8731f8707..57ee919288 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -545,12 +545,22 @@ impl EditorElement { } } - let (inserted_color, modified_color, deleted_color) = { + let ( + inserted_color, + modified_color, + deleted_color, + width_multiplier, + corner_radius, + removed_width_mult, + ) = { let editor = &cx.global::().theme.editor; ( editor.diff_background_inserted, editor.diff_background_modified, editor.diff_background_deleted, + editor.diff_indicator_width_multiplier, + editor.diff_indicator_corner_radius, + editor.removed_diff_width_multiplier, ) }; @@ -567,7 +577,7 @@ impl EditorElement { let start_y = row as f32 * line_height + offset - scroll_top; let end_y = start_y + line_height; - let width = 0.275 * line_height; + let width = removed_width_mult * line_height; let highlight_origin = bounds.origin() + vec2f(-width, start_y); let highlight_size = vec2f(width * 2., end_y - start_y); let highlight_bounds = RectF::new(highlight_origin, highlight_size); @@ -589,7 +599,7 @@ impl EditorElement { let start_y = start_row as f32 * line_height - scroll_top; let end_y = end_row as f32 * line_height - scroll_top; - let width = 0.16 * line_height; + let width = width_multiplier * line_height; let highlight_origin = bounds.origin() + vec2f(-width, start_y); let highlight_size = vec2f(width * 2., end_y - start_y); let highlight_bounds = RectF::new(highlight_origin, highlight_size); @@ -598,7 +608,7 @@ impl EditorElement { bounds: highlight_bounds, background: Some(color), border: Border::new(0., Color::transparent_black()), - corner_radius: 0.05 * line_height, + corner_radius: corner_radius * line_height, }); } diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 1fd586efee..0d0c94ea8d 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -491,6 +491,9 @@ pub struct Editor { pub diff_background_deleted: Color, pub diff_background_inserted: Color, pub diff_background_modified: Color, + pub removed_diff_width_multiplier: f32, + pub diff_indicator_width_multiplier: f32, + pub diff_indicator_corner_radius: f32, pub line_number: Color, pub line_number_active: Color, pub guest_selections: Vec, diff --git a/styles/src/styleTree/editor.ts b/styles/src/styleTree/editor.ts index bd01c3b845..6e52c620ee 100644 --- a/styles/src/styleTree/editor.ts +++ b/styles/src/styleTree/editor.ts @@ -63,6 +63,9 @@ export default function editor(theme: Theme) { diffBackgroundDeleted: theme.iconColor.error, diffBackgroundInserted: theme.iconColor.ok, diffBackgroundModified: theme.iconColor.warning, + removedDiffWidthMultiplier: 0.275, + diffIndicatorWidthMultiplier: 0.16, + diffIndicatorCornerRadius: 0.05, documentHighlightReadBackground: theme.editor.highlight.occurrence, documentHighlightWriteBackground: theme.editor.highlight.activeOccurrence, errorColor: theme.textColor.error, From e865b85d9cd88001624d3e869e16fc8600067f07 Mon Sep 17 00:00:00 2001 From: Julia Date: Thu, 29 Sep 2022 13:10:39 -0400 Subject: [PATCH 087/314] Track index instead of head for diffs --- crates/git/src/repository.rs | 32 +++++++++++++++++++++++--------- crates/project/src/fs.rs | 7 +++++-- crates/project/src/worktree.rs | 8 +++++++- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs index 19ba0d1238..37b79fa10d 100644 --- a/crates/git/src/repository.rs +++ b/crates/git/src/repository.rs @@ -18,6 +18,8 @@ pub trait GitRepository: Send + Sync + std::fmt::Debug { fn set_scan_id(&mut self, scan_id: usize); + fn reopen_git_repo(&mut self) -> bool; + fn git_repo(&self) -> Arc>; fn boxed_clone(&self) -> Box; @@ -79,18 +81,15 @@ impl GitRepository for RealGitRepository { async fn load_head_text(&self, relative_file_path: &Path) -> Option { fn logic(repo: &LibGitRepository, relative_file_path: &Path) -> Result> { - let object = repo - .head()? - .peel_to_tree()? - .get_path(relative_file_path)? - .to_object(&repo)?; - - let content = match object.as_blob() { - Some(blob) => blob.content().to_owned(), + const STAGE_NORMAL: i32 = 0; + let index = repo.index()?; + let oid = match index.get_path(relative_file_path, STAGE_NORMAL) { + Some(entry) => entry.id, None => return Ok(None), }; - let head_text = String::from_utf8(content.to_owned())?; + let content = repo.find_blob(oid)?.content().to_owned(); + let head_text = String::from_utf8(content)?; Ok(Some(head_text)) } @@ -101,6 +100,17 @@ impl GitRepository for RealGitRepository { None } + fn reopen_git_repo(&mut self) -> bool { + match LibGitRepository::open(&self.git_dir_path) { + Ok(repo) => { + self.libgit_repository = Arc::new(Mutex::new(repo)); + true + } + + Err(_) => false, + } + } + fn git_repo(&self) -> Arc> { self.libgit_repository.clone() } @@ -168,6 +178,10 @@ impl GitRepository for FakeGitRepository { unimplemented!() } + fn reopen_git_repo(&mut self) -> bool { + unimplemented!() + } + fn git_repo(&self) -> Arc> { unimplemented!() } diff --git a/crates/project/src/fs.rs b/crates/project/src/fs.rs index cc1f6101f4..c14edcd5e4 100644 --- a/crates/project/src/fs.rs +++ b/crates/project/src/fs.rs @@ -1,7 +1,7 @@ use anyhow::{anyhow, Result}; use fsevent::EventStream; use futures::{future::BoxFuture, Stream, StreamExt}; -use git::repository::{FakeGitRepository, GitRepository, RealGitRepository}; +use git::repository::{GitRepository, RealGitRepository}; use language::LineEnding; use smol::io::{AsyncReadExt, AsyncWriteExt}; use std::{ @@ -854,7 +854,10 @@ impl Fs for FakeFs { } fn open_repo(&self, abs_dot_git: &Path) -> Option> { - Some(FakeGitRepository::open(abs_dot_git.into(), 0)) + Some(git::repository::FakeGitRepository::open( + abs_dot_git.into(), + 0, + )) } fn is_fake(&self) -> bool { diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 81eec4987f..1d47c843c5 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -2603,7 +2603,13 @@ impl BackgroundScanner { .git_repositories .iter() .map(|repo| repo.boxed_clone()) - .filter(|repo| git::libgit::Repository::open(repo.git_dir_path()).is_ok()) + .filter_map(|mut repo| { + if repo.reopen_git_repo() { + Some(repo) + } else { + None + } + }) .collect(); snapshot.git_repositories = new_repos; From fcf11b118109a90bcb2167ec309e0afa6d9878f0 Mon Sep 17 00:00:00 2001 From: Julia Date: Thu, 29 Sep 2022 13:12:49 -0400 Subject: [PATCH 088/314] Bump protocol version to be ahead of main --- crates/rpc/src/rpc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/rpc/src/rpc.rs b/crates/rpc/src/rpc.rs index b9f6e6a739..2c28462ee3 100644 --- a/crates/rpc/src/rpc.rs +++ b/crates/rpc/src/rpc.rs @@ -6,4 +6,4 @@ pub use conn::Connection; pub use peer::*; mod macros; -pub const PROTOCOL_VERSION: u32 = 32; +pub const PROTOCOL_VERSION: u32 = 33; From b35e8f0164e75eec56f47281d86da18b7bc025c3 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 29 Sep 2022 19:40:36 +0200 Subject: [PATCH 089/314] Remove projects from contact updates Co-Authored-By: Nathan Sobo --- crates/client/src/user.rs | 34 +- crates/collab/src/integration_tests.rs | 449 +------------- crates/collab/src/rpc/store.rs | 36 -- crates/contacts_panel/src/contacts_panel.rs | 637 +------------------- crates/rpc/proto/zed.proto | 11 +- crates/workspace/src/waiting_room.rs | 185 ------ crates/workspace/src/workspace.rs | 32 - 7 files changed, 45 insertions(+), 1339 deletions(-) delete mode 100644 crates/workspace/src/waiting_room.rs diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index ff5f03d5ef..9f86020044 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -1,7 +1,7 @@ use super::{http::HttpClient, proto, Client, Status, TypedEnvelope}; use crate::incoming_call::IncomingCall; use anyhow::{anyhow, Context, Result}; -use collections::{hash_map::Entry, BTreeSet, HashMap, HashSet}; +use collections::{hash_map::Entry, HashMap, HashSet}; use futures::{channel::mpsc, future, AsyncReadExt, Future, StreamExt}; use gpui::{AsyncAppContext, Entity, ImageData, ModelContext, ModelHandle, Task}; use postage::{sink::Sink, watch}; @@ -40,14 +40,6 @@ impl Eq for User {} pub struct Contact { pub user: Arc, pub online: bool, - pub projects: Vec, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct ProjectMetadata { - pub id: u64, - pub visible_worktree_root_names: Vec, - pub guests: BTreeSet>, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -290,7 +282,6 @@ impl UserStore { let mut user_ids = HashSet::default(); for contact in &message.contacts { user_ids.insert(contact.user_id); - user_ids.extend(contact.projects.iter().flat_map(|w| &w.guests).copied()); } user_ids.extend(message.incoming_requests.iter().map(|req| req.requester_id)); user_ids.extend(message.outgoing_requests.iter()); @@ -688,34 +679,11 @@ impl Contact { user_store.get_user(contact.user_id, cx) }) .await?; - let mut projects = Vec::new(); - for project in contact.projects { - let mut guests = BTreeSet::new(); - for participant_id in project.guests { - guests.insert( - user_store - .update(cx, |user_store, cx| user_store.get_user(participant_id, cx)) - .await?, - ); - } - projects.push(ProjectMetadata { - id: project.id, - visible_worktree_root_names: project.visible_worktree_root_names.clone(), - guests, - }); - } Ok(Self { user, online: contact.online, - projects, }) } - - pub fn non_empty_projects(&self) -> impl Iterator { - self.projects - .iter() - .filter(|project| !project.visible_worktree_root_names.is_empty()) - } } async fn fetch_avatar(http: &dyn HttpClient, url: &str) -> Result> { diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index d16bff2f37..d000ebb309 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -8,7 +8,7 @@ use anyhow::anyhow; use call::Room; use client::{ self, proto, test::FakeHttpClient, Channel, ChannelDetails, ChannelList, Client, Connection, - Credentials, EstablishConnectionError, ProjectMetadata, UserStore, RECEIVE_TIMEOUT, + Credentials, EstablishConnectionError, UserStore, RECEIVE_TIMEOUT, }; use collections::{BTreeMap, HashMap, HashSet}; use editor::{ @@ -731,294 +731,6 @@ async fn test_cancel_join_request( ); } -#[gpui::test(iterations = 10)] -async fn test_offline_projects( - deterministic: Arc, - cx_a: &mut TestAppContext, - cx_b: &mut TestAppContext, - cx_c: &mut TestAppContext, -) { - cx_a.foreground().forbid_parking(); - let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; - let client_a = server.create_client(cx_a, "user_a").await; - let client_b = server.create_client(cx_b, "user_b").await; - let client_c = server.create_client(cx_c, "user_c").await; - let user_a = UserId::from_proto(client_a.user_id().unwrap()); - server - .make_contacts(vec![ - (&client_a, cx_a), - (&client_b, cx_b), - (&client_c, cx_c), - ]) - .await; - - // Set up observers of the project and user stores. Any time either of - // these models update, they should be in a consistent state with each - // other. There should not be an observable moment where the current - // user's contact entry contains a project that does not match one of - // the current open projects. That would cause a duplicate entry to be - // shown in the contacts panel. - let mut subscriptions = vec![]; - let (window_id, view) = cx_a.add_window(|cx| { - subscriptions.push(cx.observe(&client_a.user_store, { - let project_store = client_a.project_store.clone(); - let user_store = client_a.user_store.clone(); - move |_, _, cx| check_project_list(project_store.clone(), user_store.clone(), cx) - })); - - subscriptions.push(cx.observe(&client_a.project_store, { - let project_store = client_a.project_store.clone(); - let user_store = client_a.user_store.clone(); - move |_, _, cx| check_project_list(project_store.clone(), user_store.clone(), cx) - })); - - fn check_project_list( - project_store: ModelHandle, - user_store: ModelHandle, - cx: &mut gpui::MutableAppContext, - ) { - let user_store = user_store.read(cx); - for contact in user_store.contacts() { - if contact.user.id == user_store.current_user().unwrap().id { - for project in &contact.projects { - let store_contains_project = project_store - .read(cx) - .projects(cx) - .filter_map(|project| project.read(cx).remote_id()) - .any(|x| x == project.id); - - if !store_contains_project { - panic!( - concat!( - "current user's contact data has a project", - "that doesn't match any open project {:?}", - ), - project - ); - } - } - } - } - } - - EmptyView - }); - - // Build an offline project with two worktrees. - client_a - .fs - .insert_tree( - "/code", - json!({ - "crate1": { "a.rs": "" }, - "crate2": { "b.rs": "" }, - }), - ) - .await; - let project = cx_a.update(|cx| { - Project::local( - false, - client_a.client.clone(), - client_a.user_store.clone(), - client_a.project_store.clone(), - client_a.language_registry.clone(), - client_a.fs.clone(), - cx, - ) - }); - project - .update(cx_a, |p, cx| { - p.find_or_create_local_worktree("/code/crate1", true, cx) - }) - .await - .unwrap(); - project - .update(cx_a, |p, cx| { - p.find_or_create_local_worktree("/code/crate2", true, cx) - }) - .await - .unwrap(); - project - .update(cx_a, |p, cx| p.restore_state(cx)) - .await - .unwrap(); - - // When a project is offline, we still create it on the server but is invisible - // to other users. - deterministic.run_until_parked(); - assert!(server - .store - .lock() - .await - .project_metadata_for_user(user_a) - .is_empty()); - project.read_with(cx_a, |project, _| { - assert!(project.remote_id().is_some()); - assert!(!project.is_online()); - }); - assert!(client_b - .user_store - .read_with(cx_b, |store, _| { store.contacts()[0].projects.is_empty() })); - - // When the project is taken online, its metadata is sent to the server - // and broadcasted to other users. - project.update(cx_a, |p, cx| p.set_online(true, cx)); - deterministic.run_until_parked(); - let project_id = project.read_with(cx_a, |p, _| p.remote_id()).unwrap(); - client_b.user_store.read_with(cx_b, |store, _| { - assert_eq!( - store.contacts()[0].projects, - &[ProjectMetadata { - id: project_id, - visible_worktree_root_names: vec!["crate1".into(), "crate2".into()], - guests: Default::default(), - }] - ); - }); - - // The project is registered again when the host loses and regains connection. - server.disconnect_client(user_a); - server.forbid_connections(); - cx_a.foreground().advance_clock(rpc::RECEIVE_TIMEOUT); - assert!(server - .store - .lock() - .await - .project_metadata_for_user(user_a) - .is_empty()); - assert!(project.read_with(cx_a, |p, _| p.remote_id().is_none())); - assert!(client_b - .user_store - .read_with(cx_b, |store, _| { store.contacts()[0].projects.is_empty() })); - - server.allow_connections(); - cx_b.foreground().advance_clock(Duration::from_secs(10)); - let project_id = project.read_with(cx_a, |p, _| p.remote_id()).unwrap(); - client_b.user_store.read_with(cx_b, |store, _| { - assert_eq!( - store.contacts()[0].projects, - &[ProjectMetadata { - id: project_id, - visible_worktree_root_names: vec!["crate1".into(), "crate2".into()], - guests: Default::default(), - }] - ); - }); - - project - .update(cx_a, |p, cx| { - p.find_or_create_local_worktree("/code/crate3", true, cx) - }) - .await - .unwrap(); - deterministic.run_until_parked(); - client_b.user_store.read_with(cx_b, |store, _| { - assert_eq!( - store.contacts()[0].projects, - &[ProjectMetadata { - id: project_id, - visible_worktree_root_names: vec![ - "crate1".into(), - "crate2".into(), - "crate3".into() - ], - guests: Default::default(), - }] - ); - }); - - // Build another project using a directory which was previously part of - // an online project. Restore the project's state from the host's database. - let project2_a = cx_a.update(|cx| { - Project::local( - false, - client_a.client.clone(), - client_a.user_store.clone(), - client_a.project_store.clone(), - client_a.language_registry.clone(), - client_a.fs.clone(), - cx, - ) - }); - project2_a - .update(cx_a, |p, cx| { - p.find_or_create_local_worktree("/code/crate3", true, cx) - }) - .await - .unwrap(); - project2_a - .update(cx_a, |project, cx| project.restore_state(cx)) - .await - .unwrap(); - - // This project is now online, because its directory was previously online. - project2_a.read_with(cx_a, |project, _| assert!(project.is_online())); - deterministic.run_until_parked(); - let project2_id = project2_a.read_with(cx_a, |p, _| p.remote_id()).unwrap(); - client_b.user_store.read_with(cx_b, |store, _| { - assert_eq!( - store.contacts()[0].projects, - &[ - ProjectMetadata { - id: project_id, - visible_worktree_root_names: vec![ - "crate1".into(), - "crate2".into(), - "crate3".into() - ], - guests: Default::default(), - }, - ProjectMetadata { - id: project2_id, - visible_worktree_root_names: vec!["crate3".into()], - guests: Default::default(), - } - ] - ); - }); - - let project2_b = client_b.build_remote_project(&project2_a, cx_a, cx_b).await; - let project2_c = cx_c.foreground().spawn(Project::remote( - project2_id, - client_c.client.clone(), - client_c.user_store.clone(), - client_c.project_store.clone(), - client_c.language_registry.clone(), - FakeFs::new(cx_c.background()), - cx_c.to_async(), - )); - deterministic.run_until_parked(); - - // Taking a project offline unshares the project, rejects any pending join request and - // disconnects existing guests. - project2_a.update(cx_a, |project, cx| project.set_online(false, cx)); - deterministic.run_until_parked(); - project2_a.read_with(cx_a, |project, _| assert!(!project.is_shared())); - project2_b.read_with(cx_b, |project, _| assert!(project.is_read_only())); - project2_c.await.unwrap_err(); - - client_b.user_store.read_with(cx_b, |store, _| { - assert_eq!( - store.contacts()[0].projects, - &[ProjectMetadata { - id: project_id, - visible_worktree_root_names: vec![ - "crate1".into(), - "crate2".into(), - "crate3".into() - ], - guests: Default::default(), - },] - ); - }); - - cx_a.update(|cx| { - drop(subscriptions); - drop(view); - cx.remove_window(window_id); - }); -} - #[gpui::test(iterations = 10)] async fn test_propagate_saves_and_fs_changes( cx_a: &mut TestAppContext, @@ -3911,24 +3623,15 @@ async fn test_contacts( deterministic.run_until_parked(); assert_eq!( contacts(&client_a, cx_a), - [ - ("user_b".to_string(), true, vec![]), - ("user_c".to_string(), true, vec![]) - ] + [("user_b".to_string(), true), ("user_c".to_string(), true)] ); assert_eq!( contacts(&client_b, cx_b), - [ - ("user_a".to_string(), true, vec![]), - ("user_c".to_string(), true, vec![]) - ] + [("user_a".to_string(), true), ("user_c".to_string(), true)] ); assert_eq!( contacts(&client_c, cx_c), - [ - ("user_a".to_string(), true, vec![]), - ("user_b".to_string(), true, vec![]) - ] + [("user_a".to_string(), true), ("user_b".to_string(), true)] ); // Share a project as client A. @@ -3938,24 +3641,15 @@ async fn test_contacts( deterministic.run_until_parked(); assert_eq!( contacts(&client_a, cx_a), - [ - ("user_b".to_string(), true, vec![]), - ("user_c".to_string(), true, vec![]) - ] + [("user_b".to_string(), true), ("user_c".to_string(), true)] ); assert_eq!( contacts(&client_b, cx_b), - [ - ("user_a".to_string(), true, vec![("a".to_string(), vec![])]), - ("user_c".to_string(), true, vec![]) - ] + [("user_a".to_string(), true), ("user_c".to_string(), true)] ); assert_eq!( contacts(&client_c, cx_c), - [ - ("user_a".to_string(), true, vec![("a".to_string(), vec![])]), - ("user_b".to_string(), true, vec![]) - ] + [("user_a".to_string(), true), ("user_b".to_string(), true)] ); let _project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; @@ -3963,32 +3657,15 @@ async fn test_contacts( deterministic.run_until_parked(); assert_eq!( contacts(&client_a, cx_a), - [ - ("user_b".to_string(), true, vec![]), - ("user_c".to_string(), true, vec![]) - ] + [("user_b".to_string(), true), ("user_c".to_string(), true)] ); assert_eq!( contacts(&client_b, cx_b), - [ - ( - "user_a".to_string(), - true, - vec![("a".to_string(), vec!["user_b".to_string()])] - ), - ("user_c".to_string(), true, vec![]) - ] + [("user_a".to_string(), true,), ("user_c".to_string(), true)] ); assert_eq!( contacts(&client_c, cx_c), - [ - ( - "user_a".to_string(), - true, - vec![("a".to_string(), vec!["user_b".to_string()])] - ), - ("user_b".to_string(), true, vec![]) - ] + [("user_a".to_string(), true,), ("user_b".to_string(), true)] ); // Add a local project as client B @@ -3998,32 +3675,15 @@ async fn test_contacts( deterministic.run_until_parked(); assert_eq!( contacts(&client_a, cx_a), - [ - ("user_b".to_string(), true, vec![("b".to_string(), vec![])]), - ("user_c".to_string(), true, vec![]) - ] + [("user_b".to_string(), true), ("user_c".to_string(), true)] ); assert_eq!( contacts(&client_b, cx_b), - [ - ( - "user_a".to_string(), - true, - vec![("a".to_string(), vec!["user_b".to_string()])] - ), - ("user_c".to_string(), true, vec![]) - ] + [("user_a".to_string(), true), ("user_c".to_string(), true)] ); assert_eq!( contacts(&client_c, cx_c), - [ - ( - "user_a".to_string(), - true, - vec![("a".to_string(), vec!["user_b".to_string()])] - ), - ("user_b".to_string(), true, vec![("b".to_string(), vec![])]) - ] + [("user_a".to_string(), true,), ("user_b".to_string(), true)] ); project_a @@ -4036,24 +3696,15 @@ async fn test_contacts( deterministic.run_until_parked(); assert_eq!( contacts(&client_a, cx_a), - [ - ("user_b".to_string(), true, vec![("b".to_string(), vec![])]), - ("user_c".to_string(), true, vec![]) - ] + [("user_b".to_string(), true), ("user_c".to_string(), true)] ); assert_eq!( contacts(&client_b, cx_b), - [ - ("user_a".to_string(), true, vec![]), - ("user_c".to_string(), true, vec![]) - ] + [("user_a".to_string(), true), ("user_c".to_string(), true)] ); assert_eq!( contacts(&client_c, cx_c), - [ - ("user_a".to_string(), true, vec![]), - ("user_b".to_string(), true, vec![("b".to_string(), vec![])]) - ] + [("user_a".to_string(), true), ("user_b".to_string(), true)] ); server.disconnect_client(client_c.current_user_id(cx_c)); @@ -4061,17 +3712,11 @@ async fn test_contacts( deterministic.advance_clock(rpc::RECEIVE_TIMEOUT); assert_eq!( contacts(&client_a, cx_a), - [ - ("user_b".to_string(), true, vec![("b".to_string(), vec![])]), - ("user_c".to_string(), false, vec![]) - ] + [("user_b".to_string(), true), ("user_c".to_string(), false)] ); assert_eq!( contacts(&client_b, cx_b), - [ - ("user_a".to_string(), true, vec![]), - ("user_c".to_string(), false, vec![]) - ] + [("user_a".to_string(), true), ("user_c".to_string(), false)] ); assert_eq!(contacts(&client_c, cx_c), []); @@ -4084,48 +3729,24 @@ async fn test_contacts( deterministic.run_until_parked(); assert_eq!( contacts(&client_a, cx_a), - [ - ("user_b".to_string(), true, vec![("b".to_string(), vec![])]), - ("user_c".to_string(), true, vec![]) - ] + [("user_b".to_string(), true), ("user_c".to_string(), true)] ); assert_eq!( contacts(&client_b, cx_b), - [ - ("user_a".to_string(), true, vec![]), - ("user_c".to_string(), true, vec![]) - ] + [("user_a".to_string(), true), ("user_c".to_string(), true)] ); assert_eq!( contacts(&client_c, cx_c), - [ - ("user_a".to_string(), true, vec![]), - ("user_b".to_string(), true, vec![("b".to_string(), vec![])]) - ] + [("user_a".to_string(), true), ("user_b".to_string(), true)] ); #[allow(clippy::type_complexity)] - fn contacts( - client: &TestClient, - cx: &TestAppContext, - ) -> Vec<(String, bool, Vec<(String, Vec)>)> { + fn contacts(client: &TestClient, cx: &TestAppContext) -> Vec<(String, bool)> { client.user_store.read_with(cx, |store, _| { store .contacts() .iter() - .map(|contact| { - let projects = contact - .projects - .iter() - .map(|p| { - ( - p.visible_worktree_root_names[0].clone(), - p.guests.iter().map(|p| p.github_login.clone()).collect(), - ) - }) - .collect(); - (contact.user.github_login.clone(), contact.online, projects) - }) + .map(|contact| (contact.user.github_login.clone(), contact.online)) .collect() }) } @@ -5155,22 +4776,6 @@ async fn test_random_collaboration( log::error!("{} error - {:?}", guest.username, guest_err); } - let contacts = server - .app_state - .db - .get_contacts(guest.current_user_id(&guest_cx)) - .await - .unwrap(); - let contacts = server - .store - .lock() - .await - .build_initial_contacts_update(contacts) - .contacts; - assert!(!contacts - .iter() - .flat_map(|contact| &contact.projects) - .any(|project| project.id == host_project_id)); guest_project.read_with(&guest_cx, |project, _| assert!(project.is_read_only())); guest_cx.update(|_| drop((guest, guest_project))); } @@ -5259,14 +4864,6 @@ async fn test_random_collaboration( "removed guest is still a contact of another peer" ); } - for project in contact.projects { - for project_guest_id in project.guests { - assert_ne!( - project_guest_id, removed_guest_id.0 as u64, - "removed guest appears as still participating on a project" - ); - } - } } } diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index 1c69a7c2f8..54c3a25e27 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -345,47 +345,11 @@ impl Store { pub fn contact_for_user(&self, user_id: UserId, should_notify: bool) -> proto::Contact { proto::Contact { user_id: user_id.to_proto(), - projects: self.project_metadata_for_user(user_id), online: self.is_user_online(user_id), should_notify, } } - pub fn project_metadata_for_user(&self, user_id: UserId) -> Vec { - let user_connection_state = self.connected_users.get(&user_id); - let project_ids = user_connection_state.iter().flat_map(|state| { - state - .connection_ids - .iter() - .filter_map(|connection_id| self.connections.get(connection_id)) - .flat_map(|connection| connection.projects.iter().copied()) - }); - - let mut metadata = Vec::new(); - for project_id in project_ids { - if let Some(project) = self.projects.get(&project_id) { - if project.host.user_id == user_id && project.online { - metadata.push(proto::ProjectMetadata { - id: project_id.to_proto(), - visible_worktree_root_names: project - .worktrees - .values() - .filter(|worktree| worktree.visible) - .map(|worktree| worktree.root_name.clone()) - .collect(), - guests: project - .guests - .values() - .map(|guest| guest.user_id.to_proto()) - .collect(), - }); - } - } - } - - metadata - } - pub fn create_room(&mut self, creator_connection_id: ConnectionId) -> Result { let connection = self .connections diff --git a/crates/contacts_panel/src/contacts_panel.rs b/crates/contacts_panel/src/contacts_panel.rs index b5460f4d06..a0259ab8c5 100644 --- a/crates/contacts_panel/src/contacts_panel.rs +++ b/crates/contacts_panel/src/contacts_panel.rs @@ -8,23 +8,19 @@ use contact_notification::ContactNotification; use editor::{Cancel, Editor}; use fuzzy::{match_strings, StringMatchCandidate}; use gpui::{ - actions, - elements::*, - geometry::{rect::RectF, vector::vec2f}, - impl_actions, impl_internal_actions, - platform::CursorStyle, + actions, elements::*, impl_actions, impl_internal_actions, platform::CursorStyle, AnyViewHandle, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, View, ViewContext, ViewHandle, - WeakModelHandle, WeakViewHandle, + WeakViewHandle, }; use join_project_notification::JoinProjectNotification; use menu::{Confirm, SelectNext, SelectPrev}; -use project::{Project, ProjectStore}; +use project::ProjectStore; use serde::Deserialize; use settings::Settings; -use std::{ops::DerefMut, sync::Arc}; +use std::sync::Arc; use theme::IconButton; -use workspace::{sidebar::SidebarItem, JoinProject, ToggleProjectOnline, Workspace}; +use workspace::{sidebar::SidebarItem, Workspace}; actions!(contacts_panel, [ToggleFocus]); @@ -48,8 +44,6 @@ enum ContactEntry { IncomingRequest(Arc), OutgoingRequest(Arc), Contact(Arc), - ContactProject(Arc, usize, Option>), - OfflineProject(WeakModelHandle), } #[derive(Clone, PartialEq)] @@ -181,7 +175,6 @@ impl ContactsPanel { let list_state = ListState::new(0, Orientation::Top, 1000., cx, move |this, ix, cx| { let theme = cx.global::().theme.clone(); - let current_user_id = this.user_store.read(cx).current_user().map(|user| user.id); let is_selected = this.selection == Some(ix); match &this.entries[ix] { @@ -214,34 +207,6 @@ impl ContactsPanel { ContactEntry::Contact(contact) => { Self::render_contact(&contact.user, &theme.contacts_panel, is_selected) } - ContactEntry::ContactProject(contact, project_ix, open_project) => { - let is_last_project_for_contact = - this.entries.get(ix + 1).map_or(true, |next| { - if let ContactEntry::ContactProject(next_contact, _, _) = next { - next_contact.user.id != contact.user.id - } else { - true - } - }); - Self::render_project( - contact.clone(), - current_user_id, - *project_ix, - *open_project, - &theme.contacts_panel, - &theme.tooltip, - is_last_project_for_contact, - is_selected, - cx, - ) - } - ContactEntry::OfflineProject(project) => Self::render_offline_project( - *project, - &theme.contacts_panel, - &theme.tooltip, - is_selected, - cx, - ), } }); @@ -343,260 +308,6 @@ impl ContactsPanel { .boxed() } - #[allow(clippy::too_many_arguments)] - fn render_project( - contact: Arc, - current_user_id: Option, - project_index: usize, - open_project: Option>, - theme: &theme::ContactsPanel, - tooltip_style: &TooltipStyle, - is_last_project: bool, - is_selected: bool, - cx: &mut RenderContext, - ) -> ElementBox { - enum ToggleOnline {} - - let project = &contact.projects[project_index]; - let project_id = project.id; - let is_host = Some(contact.user.id) == current_user_id; - let open_project = open_project.and_then(|p| p.upgrade(cx.deref_mut())); - - let font_cache = cx.font_cache(); - let host_avatar_height = theme - .contact_avatar - .width - .or(theme.contact_avatar.height) - .unwrap_or(0.); - let row = &theme.project_row.default; - let tree_branch = theme.tree_branch; - let line_height = row.name.text.line_height(font_cache); - let cap_height = row.name.text.cap_height(font_cache); - let baseline_offset = - row.name.text.baseline_offset(font_cache) + (theme.row_height - line_height) / 2.; - - MouseEventHandler::::new(project_id as usize, cx, |mouse_state, cx| { - let tree_branch = *tree_branch.style_for(mouse_state, is_selected); - let row = theme.project_row.style_for(mouse_state, is_selected); - - Flex::row() - .with_child( - Stack::new() - .with_child( - Canvas::new(move |bounds, _, cx| { - let start_x = bounds.min_x() + (bounds.width() / 2.) - - (tree_branch.width / 2.); - let end_x = bounds.max_x(); - let start_y = bounds.min_y(); - let end_y = bounds.min_y() + baseline_offset - (cap_height / 2.); - - cx.scene.push_quad(gpui::Quad { - bounds: RectF::from_points( - vec2f(start_x, start_y), - vec2f( - start_x + tree_branch.width, - if is_last_project { - end_y - } else { - bounds.max_y() - }, - ), - ), - background: Some(tree_branch.color), - border: gpui::Border::default(), - corner_radius: 0., - }); - cx.scene.push_quad(gpui::Quad { - bounds: RectF::from_points( - vec2f(start_x, end_y), - vec2f(end_x, end_y + tree_branch.width), - ), - background: Some(tree_branch.color), - border: gpui::Border::default(), - corner_radius: 0., - }); - }) - .boxed(), - ) - .with_children(open_project.and_then(|open_project| { - let is_going_offline = !open_project.read(cx).is_online(); - if !mouse_state.hovered && !is_going_offline { - return None; - } - - let button = MouseEventHandler::::new( - project_id as usize, - cx, - |state, _| { - let mut icon_style = - *theme.private_button.style_for(state, false); - icon_style.container.background_color = - row.container.background_color; - if is_going_offline { - icon_style.color = theme.disabled_button.color; - } - render_icon_button(&icon_style, "icons/lock_8.svg") - .aligned() - .boxed() - }, - ); - - if is_going_offline { - Some(button.boxed()) - } else { - Some( - button - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, cx| { - cx.dispatch_action(ToggleProjectOnline { - project: Some(open_project.clone()), - }) - }) - .with_tooltip::( - project_id as usize, - "Take project offline".to_string(), - None, - tooltip_style.clone(), - cx, - ) - .boxed(), - ) - } - })) - .constrained() - .with_width(host_avatar_height) - .boxed(), - ) - .with_child( - Label::new( - project.visible_worktree_root_names.join(", "), - row.name.text.clone(), - ) - .aligned() - .left() - .contained() - .with_style(row.name.container) - .flex(1., false) - .boxed(), - ) - .with_children(project.guests.iter().filter_map(|participant| { - participant.avatar.clone().map(|avatar| { - Image::new(avatar) - .with_style(row.guest_avatar) - .aligned() - .left() - .contained() - .with_margin_right(row.guest_avatar_spacing) - .boxed() - }) - })) - .constrained() - .with_height(theme.row_height) - .contained() - .with_style(row.container) - .boxed() - }) - .with_cursor_style(if !is_host { - CursorStyle::PointingHand - } else { - CursorStyle::Arrow - }) - .on_click(MouseButton::Left, move |_, cx| { - if !is_host { - cx.dispatch_global_action(JoinProject { - contact: contact.clone(), - project_index, - }); - } - }) - .boxed() - } - - fn render_offline_project( - project_handle: WeakModelHandle, - theme: &theme::ContactsPanel, - tooltip_style: &TooltipStyle, - is_selected: bool, - cx: &mut RenderContext, - ) -> ElementBox { - let host_avatar_height = theme - .contact_avatar - .width - .or(theme.contact_avatar.height) - .unwrap_or(0.); - - enum LocalProject {} - enum ToggleOnline {} - - let project_id = project_handle.id(); - MouseEventHandler::::new(project_id, cx, |state, cx| { - let row = theme.project_row.style_for(state, is_selected); - let mut worktree_root_names = String::new(); - let project = if let Some(project) = project_handle.upgrade(cx.deref_mut()) { - project.read(cx) - } else { - return Empty::new().boxed(); - }; - let is_going_online = project.is_online(); - for tree in project.visible_worktrees(cx) { - if !worktree_root_names.is_empty() { - worktree_root_names.push_str(", "); - } - worktree_root_names.push_str(tree.read(cx).root_name()); - } - - Flex::row() - .with_child({ - let button = - MouseEventHandler::::new(project_id, cx, |state, _| { - let mut style = *theme.private_button.style_for(state, false); - if is_going_online { - style.color = theme.disabled_button.color; - } - render_icon_button(&style, "icons/lock_8.svg") - .aligned() - .constrained() - .with_width(host_avatar_height) - .boxed() - }); - - if is_going_online { - button.boxed() - } else { - button - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, cx| { - let project = project_handle.upgrade(cx.app); - cx.dispatch_action(ToggleProjectOnline { project }) - }) - .with_tooltip::( - project_id, - "Take project online".to_string(), - None, - tooltip_style.clone(), - cx, - ) - .boxed() - } - }) - .with_child( - Label::new(worktree_root_names, row.name.text.clone()) - .aligned() - .left() - .contained() - .with_style(row.name.container) - .flex(1., false) - .boxed(), - ) - .constrained() - .with_height(theme.row_height) - .contained() - .with_style(row.container) - .boxed() - }) - .boxed() - } - fn render_contact_request( user: Arc, user_store: ModelHandle, @@ -710,7 +421,6 @@ impl ContactsPanel { fn update_entries(&mut self, cx: &mut ViewContext) { let user_store = self.user_store.read(cx); - let project_store = self.project_store.read(cx); let query = self.filter_editor.read(cx).text(cx); let executor = cx.background().clone(); @@ -837,60 +547,6 @@ impl ContactsPanel { for mat in matches { let contact = &contacts[mat.candidate_id]; self.entries.push(ContactEntry::Contact(contact.clone())); - - let is_current_user = current_user - .as_ref() - .map_or(false, |user| user.id == contact.user.id); - if is_current_user { - let mut open_projects = - project_store.projects(cx).collect::>(); - self.entries.extend( - contact.projects.iter().enumerate().filter_map( - |(ix, project)| { - let open_project = open_projects - .iter() - .position(|p| { - p.read(cx).remote_id() == Some(project.id) - }) - .map(|ix| open_projects.remove(ix).downgrade()); - if project.visible_worktree_root_names.is_empty() { - None - } else { - Some(ContactEntry::ContactProject( - contact.clone(), - ix, - open_project, - )) - } - }, - ), - ); - self.entries.extend(open_projects.into_iter().filter_map( - |project| { - if project.read(cx).visible_worktrees(cx).next().is_none() { - None - } else { - Some(ContactEntry::OfflineProject(project.downgrade())) - } - }, - )); - } else { - self.entries.extend( - contact.projects.iter().enumerate().filter_map( - |(ix, project)| { - if project.visible_worktree_root_names.is_empty() { - None - } else { - Some(ContactEntry::ContactProject( - contact.clone(), - ix, - None, - )) - } - }, - ), - ); - } } } } @@ -981,18 +637,6 @@ impl ContactsPanel { let section = *section; self.toggle_expanded(&ToggleExpanded(section), cx); } - ContactEntry::ContactProject(contact, project_index, open_project) => { - if let Some(open_project) = open_project { - workspace::activate_workspace_for_project(cx, |_, cx| { - cx.model_id() == open_project.id() - }); - } else { - cx.dispatch_global_action(JoinProject { - contact: contact.clone(), - project_index: *project_index, - }) - } - } _ => {} } } @@ -1181,16 +825,6 @@ impl PartialEq for ContactEntry { return contact_1.user.id == contact_2.user.id; } } - ContactEntry::ContactProject(contact_1, ix_1, _) => { - if let ContactEntry::ContactProject(contact_2, ix_2, _) = other { - return contact_1.user.id == contact_2.user.id && ix_1 == ix_2; - } - } - ContactEntry::OfflineProject(project_1) => { - if let ContactEntry::OfflineProject(project_2) = other { - return project_1.id() == project_2.id(); - } - } } false } @@ -1205,7 +839,7 @@ mod tests { Client, }; use collections::HashSet; - use gpui::{serde_json::json, TestAppContext}; + use gpui::TestAppContext; use language::LanguageRegistry; use project::{FakeFs, Project}; @@ -1221,8 +855,6 @@ mod tests { let project_store = cx.add_model(|_| ProjectStore::new(project::Db::open_fake())); let server = FakeServer::for_client(current_user_id, &client, cx).await; let fs = FakeFs::new(cx.background()); - fs.insert_tree("/private_dir", json!({ "one.rs": "" })) - .await; let project = cx.update(|cx| { Project::local( false, @@ -1234,14 +866,6 @@ mod tests { cx, ) }); - let worktree_id = project - .update(cx, |project, cx| { - project.find_or_create_local_worktree("/private_dir", true, cx) - }) - .await - .unwrap() - .0 - .read_with(cx, |worktree, _| worktree.id().to_proto()); let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx)); @@ -1315,55 +939,26 @@ mod tests { user_id: 3, online: true, should_notify: false, - projects: vec![proto::ProjectMetadata { - id: 101, - visible_worktree_root_names: vec!["dir1".to_string()], - guests: vec![2], - }], }, proto::Contact { user_id: 4, online: true, should_notify: false, - projects: vec![proto::ProjectMetadata { - id: 102, - visible_worktree_root_names: vec!["dir2".to_string()], - guests: vec![2], - }], }, proto::Contact { user_id: 5, online: false, should_notify: false, - projects: vec![], }, proto::Contact { user_id: current_user_id, online: true, should_notify: false, - projects: vec![proto::ProjectMetadata { - id: 103, - visible_worktree_root_names: vec!["dir3".to_string()], - guests: vec![3], - }], }, ], ..Default::default() }); - assert_eq!( - server - .receive::() - .await - .unwrap() - .payload, - proto::UpdateProject { - project_id: 200, - online: false, - worktrees: vec![] - }, - ); - cx.foreground().run_until_parked(); assert_eq!( cx.read(|cx| render_to_strings(&panel, cx)), @@ -1373,168 +968,8 @@ mod tests { " outgoing user_two", "v Online", " the_current_user", - " dir3", - " 🔒 private_dir", " user_four", - " dir2", " user_three", - " dir1", - "v Offline", - " user_five", - ] - ); - - // Take a project online. It appears as loading, since the project - // isn't yet visible to other contacts. - project.update(cx, |project, cx| project.set_online(true, cx)); - cx.foreground().run_until_parked(); - assert_eq!( - cx.read(|cx| render_to_strings(&panel, cx)), - &[ - "v Requests", - " incoming user_one", - " outgoing user_two", - "v Online", - " the_current_user", - " dir3", - " 🔒 private_dir (going online...)", - " user_four", - " dir2", - " user_three", - " dir1", - "v Offline", - " user_five", - ] - ); - - // The server receives the project's metadata and updates the contact metadata - // for the current user. Now the project appears as online. - assert_eq!( - server - .receive::() - .await - .unwrap() - .payload, - proto::UpdateProject { - project_id: 200, - online: true, - worktrees: vec![proto::WorktreeMetadata { - id: worktree_id, - root_name: "private_dir".to_string(), - visible: true, - }] - }, - ); - server - .receive::() - .await - .unwrap(); - - server.send(proto::UpdateContacts { - contacts: vec![proto::Contact { - user_id: current_user_id, - online: true, - should_notify: false, - projects: vec![ - proto::ProjectMetadata { - id: 103, - visible_worktree_root_names: vec!["dir3".to_string()], - guests: vec![3], - }, - proto::ProjectMetadata { - id: 200, - visible_worktree_root_names: vec!["private_dir".to_string()], - guests: vec![3], - }, - ], - }], - ..Default::default() - }); - cx.foreground().run_until_parked(); - assert_eq!( - cx.read(|cx| render_to_strings(&panel, cx)), - &[ - "v Requests", - " incoming user_one", - " outgoing user_two", - "v Online", - " the_current_user", - " dir3", - " private_dir", - " user_four", - " dir2", - " user_three", - " dir1", - "v Offline", - " user_five", - ] - ); - - // Take the project offline. It appears as loading. - project.update(cx, |project, cx| project.set_online(false, cx)); - cx.foreground().run_until_parked(); - assert_eq!( - cx.read(|cx| render_to_strings(&panel, cx)), - &[ - "v Requests", - " incoming user_one", - " outgoing user_two", - "v Online", - " the_current_user", - " dir3", - " private_dir (going offline...)", - " user_four", - " dir2", - " user_three", - " dir1", - "v Offline", - " user_five", - ] - ); - - // The server receives the unregister request and updates the contact - // metadata for the current user. The project is now offline. - assert_eq!( - server - .receive::() - .await - .unwrap() - .payload, - proto::UpdateProject { - project_id: 200, - online: false, - worktrees: vec![] - }, - ); - - server.send(proto::UpdateContacts { - contacts: vec![proto::Contact { - user_id: current_user_id, - online: true, - should_notify: false, - projects: vec![proto::ProjectMetadata { - id: 103, - visible_worktree_root_names: vec!["dir3".to_string()], - guests: vec![3], - }], - }], - ..Default::default() - }); - cx.foreground().run_until_parked(); - assert_eq!( - cx.read(|cx| render_to_strings(&panel, cx)), - &[ - "v Requests", - " incoming user_one", - " outgoing user_two", - "v Online", - " the_current_user", - " dir3", - " 🔒 private_dir", - " user_four", - " dir2", - " user_three", - " dir1", "v Offline", " user_five", ] @@ -1551,7 +986,6 @@ mod tests { &[ "v Online", " user_four <=== selected", - " dir2", "v Offline", " user_five", ] @@ -1565,25 +999,23 @@ mod tests { &[ "v Online", " user_four", - " dir2 <=== selected", - "v Offline", - " user_five", - ] - ); - - panel.update(cx, |panel, cx| { - panel.select_next(&Default::default(), cx); - }); - assert_eq!( - cx.read(|cx| render_to_strings(&panel, cx)), - &[ - "v Online", - " user_four", - " dir2", "v Offline <=== selected", " user_five", ] ); + + panel.update(cx, |panel, cx| { + panel.select_next(&Default::default(), cx); + }); + assert_eq!( + cx.read(|cx| render_to_strings(&panel, cx)), + &[ + "v Online", + " user_four", + "v Offline", + " user_five <=== selected", + ] + ); } fn render_to_strings(panel: &ViewHandle, cx: &AppContext) -> Vec { @@ -1608,37 +1040,6 @@ mod tests { ContactEntry::Contact(contact) => { format!(" {}", contact.user.github_login) } - ContactEntry::ContactProject(contact, project_ix, project) => { - let project = project - .and_then(|p| p.upgrade(cx)) - .map(|project| project.read(cx)); - format!( - " {}{}", - contact.projects[*project_ix] - .visible_worktree_root_names - .join(", "), - if project.map_or(true, |project| project.is_online()) { - "" - } else { - " (going offline...)" - }, - ) - } - ContactEntry::OfflineProject(project) => { - let project = project.upgrade(cx).unwrap().read(cx); - format!( - " 🔒 {}{}", - project - .worktree_root_names(cx) - .collect::>() - .join(", "), - if project.is_online() { - " (going online...)" - } else { - "" - }, - ) - } }; if panel.selection == Some(ix) { diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 1125c2b3ad..751c41b209 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -1048,15 +1048,8 @@ message ChannelMessage { message Contact { uint64 user_id = 1; - repeated ProjectMetadata projects = 2; - bool online = 3; - bool should_notify = 4; -} - -message ProjectMetadata { - uint64 id = 1; - repeated string visible_worktree_root_names = 3; - repeated uint64 guests = 4; + bool online = 2; + bool should_notify = 3; } message WorktreeMetadata { diff --git a/crates/workspace/src/waiting_room.rs b/crates/workspace/src/waiting_room.rs deleted file mode 100644 index bdced26c8b..0000000000 --- a/crates/workspace/src/waiting_room.rs +++ /dev/null @@ -1,185 +0,0 @@ -use crate::{sidebar::SidebarSide, AppState, ToggleFollow, Workspace}; -use anyhow::Result; -use client::{proto, Client, Contact}; -use gpui::{ - elements::*, ElementBox, Entity, ImageData, MutableAppContext, RenderContext, Task, View, - ViewContext, -}; -use project::Project; -use settings::Settings; -use std::sync::Arc; -use util::ResultExt; - -pub struct WaitingRoom { - project_id: u64, - avatar: Option>, - message: String, - waiting: bool, - client: Arc, - _join_task: Task>, -} - -impl Entity for WaitingRoom { - type Event = (); - - fn release(&mut self, _: &mut MutableAppContext) { - if self.waiting { - self.client - .send(proto::LeaveProject { - project_id: self.project_id, - }) - .log_err(); - } - } -} - -impl View for WaitingRoom { - fn ui_name() -> &'static str { - "WaitingRoom" - } - - fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - let theme = &cx.global::().theme.workspace; - - Flex::column() - .with_children(self.avatar.clone().map(|avatar| { - Image::new(avatar) - .with_style(theme.joining_project_avatar) - .aligned() - .boxed() - })) - .with_child( - Text::new( - self.message.clone(), - theme.joining_project_message.text.clone(), - ) - .contained() - .with_style(theme.joining_project_message.container) - .aligned() - .boxed(), - ) - .aligned() - .contained() - .with_background_color(theme.background) - .boxed() - } -} - -impl WaitingRoom { - pub fn new( - contact: Arc, - project_index: usize, - app_state: Arc, - cx: &mut ViewContext, - ) -> Self { - let project_id = contact.projects[project_index].id; - let client = app_state.client.clone(); - let _join_task = cx.spawn_weak({ - let contact = contact.clone(); - |this, mut cx| async move { - let project = Project::remote( - project_id, - app_state.client.clone(), - app_state.user_store.clone(), - app_state.project_store.clone(), - app_state.languages.clone(), - app_state.fs.clone(), - cx.clone(), - ) - .await; - - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, cx| { - this.waiting = false; - match project { - Ok(project) => { - cx.replace_root_view(|cx| { - let mut workspace = - Workspace::new(project, app_state.default_item_factory, cx); - (app_state.initialize_workspace)( - &mut workspace, - &app_state, - cx, - ); - workspace.toggle_sidebar(SidebarSide::Left, cx); - if let Some((host_peer_id, _)) = workspace - .project - .read(cx) - .collaborators() - .iter() - .find(|(_, collaborator)| collaborator.replica_id == 0) - { - if let Some(follow) = workspace - .toggle_follow(&ToggleFollow(*host_peer_id), cx) - { - follow.detach_and_log_err(cx); - } - } - workspace - }); - } - Err(error) => { - let login = &contact.user.github_login; - let message = match error { - project::JoinProjectError::HostDeclined => { - format!("@{} declined your request.", login) - } - project::JoinProjectError::HostClosedProject => { - format!( - "@{} closed their copy of {}.", - login, - humanize_list( - &contact.projects[project_index] - .visible_worktree_root_names - ) - ) - } - project::JoinProjectError::HostWentOffline => { - format!("@{} went offline.", login) - } - project::JoinProjectError::Other(error) => { - log::error!("error joining project: {}", error); - "An error occurred.".to_string() - } - }; - this.message = message; - cx.notify(); - } - } - }) - } - - Ok(()) - } - }); - - Self { - project_id, - avatar: contact.user.avatar.clone(), - message: format!( - "Asking to join @{}'s copy of {}...", - contact.user.github_login, - humanize_list(&contact.projects[project_index].visible_worktree_root_names) - ), - waiting: true, - client, - _join_task, - } - } -} - -fn humanize_list<'a>(items: impl IntoIterator) -> String { - let mut list = String::new(); - let mut items = items.into_iter().enumerate().peekable(); - while let Some((ix, item)) = items.next() { - if ix > 0 { - list.push_str(", "); - if items.peek().is_none() { - list.push_str("and "); - } - } - - list.push_str(item); - } - list -} diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 04bbc094b2..90f01a3a5f 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -10,7 +10,6 @@ pub mod searchable; pub mod sidebar; mod status_bar; mod toolbar; -mod waiting_room; use anyhow::{anyhow, Context, Result}; use client::{proto, Client, Contact, PeerId, Subscription, TypedEnvelope, UserStore}; @@ -58,7 +57,6 @@ use std::{ use theme::{Theme, ThemeRegistry}; pub use toolbar::{ToolbarItemLocation, ToolbarItemView}; use util::ResultExt; -use waiting_room::WaitingRoom; type ProjectItemBuilders = HashMap< TypeId, @@ -167,14 +165,6 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { } } }); - cx.add_global_action({ - let app_state = Arc::downgrade(&app_state); - move |action: &JoinProject, cx: &mut MutableAppContext| { - if let Some(app_state) = app_state.upgrade() { - join_project(action.contact.clone(), action.project_index, &app_state, cx); - } - } - }); cx.add_async_action(Workspace::toggle_follow); cx.add_async_action(Workspace::follow_next_collaborator); @@ -2663,28 +2653,6 @@ pub fn open_paths( }) } -pub fn join_project( - contact: Arc, - project_index: usize, - app_state: &Arc, - cx: &mut MutableAppContext, -) { - let project_id = contact.projects[project_index].id; - - for window_id in cx.window_ids().collect::>() { - if let Some(workspace) = cx.root_view::(window_id) { - if workspace.read(cx).project().read(cx).remote_id() == Some(project_id) { - cx.activate_window(window_id); - return; - } - } - } - - cx.add_window((app_state.build_window_options)(), |cx| { - WaitingRoom::new(contact, project_index, app_state.clone(), cx) - }); -} - fn open_new(app_state: &Arc, cx: &mut MutableAppContext) { let (window_id, workspace) = cx.add_window((app_state.build_window_options)(), |cx| { let mut workspace = Workspace::new( From 5d09083a7d59129b350af583d731fd5039406086 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 29 Sep 2022 12:32:25 -0700 Subject: [PATCH 090/314] Identify users in amplitude via a separate 'metrics_id' UUID --- crates/client/src/client.rs | 4 +- crates/client/src/telemetry.rs | 10 +- crates/client/src/user.rs | 10 +- .../20220913211150_create_signups.down.sql | 6 - ....sql => 20220913211150_create_signups.sql} | 0 .../20220929182110_add_metrics_id.sql | 2 + crates/collab/src/api.rs | 82 ++-- crates/collab/src/db.rs | 56 ++- crates/collab/src/db_tests.rs | 349 +++++++++--------- crates/collab/src/integration_tests.rs | 7 +- crates/collab/src/rpc.rs | 17 +- crates/rpc/proto/zed.proto | 9 + crates/rpc/src/proto.rs | 3 + 13 files changed, 316 insertions(+), 239 deletions(-) delete mode 100644 crates/collab/migrations/20220913211150_create_signups.down.sql rename crates/collab/migrations/{20220913211150_create_signups.up.sql => 20220913211150_create_signups.sql} (100%) create mode 100644 crates/collab/migrations/20220929182110_add_metrics_id.sql diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index b75be62308..9ec24abae5 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -320,11 +320,9 @@ impl Client { log::info!("set status on client {}: {:?}", self.id, status); let mut state = self.state.write(); *state.status.0.borrow_mut() = status; - let user_id = state.credentials.as_ref().map(|c| c.user_id); match status { Status::Connected { .. } => { - self.telemetry.set_user_id(user_id); state._reconnect_task = None; } Status::ConnectionLost => { @@ -353,7 +351,7 @@ impl Client { })); } Status::SignedOut | Status::UpgradeRequired => { - self.telemetry.set_user_id(user_id); + self.telemetry.set_metrics_id(None); state._reconnect_task.take(); } _ => {} diff --git a/crates/client/src/telemetry.rs b/crates/client/src/telemetry.rs index 8b7be5ba80..c9b5665e9e 100644 --- a/crates/client/src/telemetry.rs +++ b/crates/client/src/telemetry.rs @@ -29,7 +29,7 @@ pub struct Telemetry { #[derive(Default)] struct TelemetryState { - user_id: Option>, + metrics_id: Option>, device_id: Option>, app_version: Option>, os_version: Option>, @@ -115,7 +115,7 @@ impl Telemetry { flush_task: Default::default(), next_event_id: 0, log_file: None, - user_id: None, + metrics_id: None, }), }); @@ -176,8 +176,8 @@ impl Telemetry { .detach(); } - pub fn set_user_id(&self, user_id: Option) { - self.state.lock().user_id = user_id.map(|id| id.to_string().into()); + pub fn set_metrics_id(&self, metrics_id: Option) { + self.state.lock().metrics_id = metrics_id.map(|s| s.into()); } pub fn report_event(self: &Arc, kind: &str, properties: Value) { @@ -199,7 +199,7 @@ impl Telemetry { None }, user_properties: None, - user_id: state.user_id.clone(), + user_id: state.metrics_id.clone(), device_id: state.device_id.clone(), os_name: state.os_name, os_version: state.os_version.clone(), diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 149d22e77a..b31cda94b3 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -142,10 +142,14 @@ impl UserStore { match status { Status::Connected { .. } => { if let Some((this, user_id)) = this.upgrade(&cx).zip(client.user_id()) { - let user = this + let fetch_user = this .update(&mut cx, |this, cx| this.fetch_user(user_id, cx)) - .log_err() - .await; + .log_err(); + let fetch_metrics_id = + client.request(proto::GetPrivateUserInfo {}).log_err(); + let (user, info) = futures::join!(fetch_user, fetch_metrics_id); + client.telemetry.set_metrics_id(info.map(|i| i.metrics_id)); + client.telemetry.report_event("sign in", Default::default()); current_user_tx.send(user).await.ok(); } } diff --git a/crates/collab/migrations/20220913211150_create_signups.down.sql b/crates/collab/migrations/20220913211150_create_signups.down.sql deleted file mode 100644 index 5504bbb8dc..0000000000 --- a/crates/collab/migrations/20220913211150_create_signups.down.sql +++ /dev/null @@ -1,6 +0,0 @@ -DROP TABLE signups; - -ALTER TABLE users - DROP COLUMN github_user_id; - -DROP INDEX index_users_on_email_address; diff --git a/crates/collab/migrations/20220913211150_create_signups.up.sql b/crates/collab/migrations/20220913211150_create_signups.sql similarity index 100% rename from crates/collab/migrations/20220913211150_create_signups.up.sql rename to crates/collab/migrations/20220913211150_create_signups.sql diff --git a/crates/collab/migrations/20220929182110_add_metrics_id.sql b/crates/collab/migrations/20220929182110_add_metrics_id.sql new file mode 100644 index 0000000000..665d6323bf --- /dev/null +++ b/crates/collab/migrations/20220929182110_add_metrics_id.sql @@ -0,0 +1,2 @@ +ALTER TABLE "users" + ADD "metrics_id" uuid NOT NULL DEFAULT gen_random_uuid(); diff --git a/crates/collab/src/api.rs b/crates/collab/src/api.rs index 0a9d8106ce..08dfa91ba9 100644 --- a/crates/collab/src/api.rs +++ b/crates/collab/src/api.rs @@ -24,6 +24,7 @@ use tracing::instrument; pub fn routes(rpc_server: &Arc, state: Arc) -> Router { Router::new() + .route("/user", get(get_authenticated_user)) .route("/users", get(get_users).post(create_user)) .route("/users/:id", put(update_user).delete(destroy_user)) .route("/users/:id/access_tokens", post(create_access_token)) @@ -85,10 +86,33 @@ pub async fn validate_api_token(req: Request, next: Next) -> impl IntoR Ok::<_, Error>(next.run(req).await) } +#[derive(Debug, Deserialize)] +struct AuthenticatedUserParams { + github_user_id: i32, + github_login: String, +} + +#[derive(Debug, Serialize)] +struct AuthenticatedUserResponse { + user: User, + metrics_id: String, +} + +async fn get_authenticated_user( + Query(params): Query, + Extension(app): Extension>, +) -> Result> { + let user = app + .db + .get_user_by_github_account(¶ms.github_login, Some(params.github_user_id)) + .await? + .ok_or_else(|| Error::Http(StatusCode::NOT_FOUND, "user not found".into()))?; + let metrics_id = app.db.get_user_metrics_id(user.id).await?; + return Ok(Json(AuthenticatedUserResponse { user, metrics_id })); +} + #[derive(Debug, Deserialize)] struct GetUsersQueryParams { - github_user_id: Option, - github_login: Option, query: Option, page: Option, limit: Option, @@ -98,14 +122,6 @@ async fn get_users( Query(params): Query, Extension(app): Extension>, ) -> Result>> { - if let Some(github_login) = ¶ms.github_login { - let user = app - .db - .get_user_by_github_account(github_login, params.github_user_id) - .await?; - return Ok(Json(Vec::from_iter(user))); - } - let limit = params.limit.unwrap_or(100); let users = if let Some(query) = params.query { app.db.fuzzy_search_users(&query, limit).await? @@ -124,6 +140,8 @@ struct CreateUserParams { email_address: String, email_confirmation_code: Option, #[serde(default)] + admin: bool, + #[serde(default)] invite_count: i32, } @@ -131,6 +149,7 @@ struct CreateUserParams { struct CreateUserResponse { user: User, signup_device_id: Option, + metrics_id: String, } async fn create_user( @@ -143,12 +162,10 @@ async fn create_user( github_user_id: params.github_user_id, invite_count: params.invite_count, }; - let user_id; - let signup_device_id; + // Creating a user via the normal signup process - if let Some(email_confirmation_code) = params.email_confirmation_code { - let result = app - .db + let result = if let Some(email_confirmation_code) = params.email_confirmation_code { + app.db .create_user_from_invite( &Invite { email_address: params.email_address, @@ -156,34 +173,37 @@ async fn create_user( }, user, ) - .await?; - user_id = result.user_id; - signup_device_id = result.signup_device_id; - if let Some(inviter_id) = result.inviting_user_id { - rpc_server - .invite_code_redeemed(inviter_id, user_id) - .await - .trace_err(); - } + .await? } // Creating a user as an admin - else { - user_id = app - .db + else if params.admin { + app.db .create_user(¶ms.email_address, false, user) - .await?; - signup_device_id = None; + .await? + } else { + Err(Error::Http( + StatusCode::UNPROCESSABLE_ENTITY, + "email confirmation code is required".into(), + ))? + }; + + if let Some(inviter_id) = result.inviting_user_id { + rpc_server + .invite_code_redeemed(inviter_id, result.user_id) + .await + .trace_err(); } let user = app .db - .get_user_by_id(user_id) + .get_user_by_id(result.user_id) .await? .ok_or_else(|| anyhow!("couldn't find the user we just created"))?; Ok(Json(CreateUserResponse { user, - signup_device_id, + metrics_id: result.metrics_id, + signup_device_id: result.signup_device_id, })) } diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs index 8b01cdf971..a12f6a4f89 100644 --- a/crates/collab/src/db.rs +++ b/crates/collab/src/db.rs @@ -17,10 +17,11 @@ pub trait Db: Send + Sync { email_address: &str, admin: bool, params: NewUserParams, - ) -> Result; + ) -> Result; async fn get_all_users(&self, page: u32, limit: u32) -> Result>; async fn fuzzy_search_users(&self, query: &str, limit: u32) -> Result>; async fn get_user_by_id(&self, id: UserId) -> Result>; + async fn get_user_metrics_id(&self, id: UserId) -> Result; async fn get_users_by_ids(&self, ids: Vec) -> Result>; async fn get_users_with_no_invites(&self, invited_by_another_user: bool) -> Result>; async fn get_user_by_github_account( @@ -208,21 +209,26 @@ impl Db for PostgresDb { email_address: &str, admin: bool, params: NewUserParams, - ) -> Result { + ) -> Result { let query = " INSERT INTO users (email_address, github_login, github_user_id, admin) VALUES ($1, $2, $3, $4) ON CONFLICT (github_login) DO UPDATE SET github_login = excluded.github_login - RETURNING id + RETURNING id, metrics_id::text "; - Ok(sqlx::query_scalar(query) + let (user_id, metrics_id): (UserId, String) = sqlx::query_as(query) .bind(email_address) .bind(params.github_login) .bind(params.github_user_id) .bind(admin) .fetch_one(&self.pool) - .await - .map(UserId)?) + .await?; + Ok(NewUserResult { + user_id, + metrics_id, + signup_device_id: None, + inviting_user_id: None, + }) } async fn get_all_users(&self, page: u32, limit: u32) -> Result> { @@ -256,6 +262,18 @@ impl Db for PostgresDb { Ok(users.into_iter().next()) } + async fn get_user_metrics_id(&self, id: UserId) -> Result { + let query = " + SELECT metrics_id::text + FROM users + WHERE id = $1 + "; + Ok(sqlx::query_scalar(query) + .bind(id) + .fetch_one(&self.pool) + .await?) + } + async fn get_users_by_ids(&self, ids: Vec) -> Result> { let ids = ids.into_iter().map(|id| id.0).collect::>(); let query = " @@ -493,13 +511,13 @@ impl Db for PostgresDb { ))?; } - let user_id: UserId = sqlx::query_scalar( + let (user_id, metrics_id): (UserId, String) = sqlx::query_as( " INSERT INTO users (email_address, github_login, github_user_id, admin, invite_count, invite_code) VALUES ($1, $2, $3, 'f', $4, $5) - RETURNING id + RETURNING id, metrics_id::text ", ) .bind(&invite.email_address) @@ -559,6 +577,7 @@ impl Db for PostgresDb { tx.commit().await?; Ok(NewUserResult { user_id, + metrics_id, inviting_user_id, signup_device_id, }) @@ -1722,6 +1741,7 @@ pub struct NewUserParams { #[derive(Debug)] pub struct NewUserResult { pub user_id: UserId, + pub metrics_id: String, pub inviting_user_id: Option, pub signup_device_id: Option, } @@ -1808,15 +1828,15 @@ mod test { email_address: &str, admin: bool, params: NewUserParams, - ) -> Result { + ) -> Result { self.background.simulate_random_delay().await; let mut users = self.users.lock(); - if let Some(user) = users + let user_id = if let Some(user) = users .values() .find(|user| user.github_login == params.github_login) { - Ok(user.id) + user.id } else { let id = post_inc(&mut *self.next_user_id.lock()); let user_id = UserId(id); @@ -1833,8 +1853,14 @@ mod test { connected_once: false, }, ); - Ok(user_id) - } + user_id + }; + Ok(NewUserResult { + user_id, + metrics_id: "the-metrics-id".to_string(), + inviting_user_id: None, + signup_device_id: None, + }) } async fn get_all_users(&self, _page: u32, _limit: u32) -> Result> { @@ -1850,6 +1876,10 @@ mod test { Ok(self.get_users_by_ids(vec![id]).await?.into_iter().next()) } + async fn get_user_metrics_id(&self, _id: UserId) -> Result { + Ok("the-metrics-id".to_string()) + } + async fn get_users_by_ids(&self, ids: Vec) -> Result> { self.background.simulate_random_delay().await; let users = self.users.lock(); diff --git a/crates/collab/src/db_tests.rs b/crates/collab/src/db_tests.rs index 1e48b4b754..e063b97eb6 100644 --- a/crates/collab/src/db_tests.rs +++ b/crates/collab/src/db_tests.rs @@ -12,89 +12,56 @@ async fn test_get_users_by_ids() { ] { let db = test_db.db(); - let user1 = db - .create_user( - "u1@example.com", - false, - NewUserParams { - github_login: "u1".into(), - github_user_id: 1, - invite_count: 0, - }, - ) - .await - .unwrap(); - let user2 = db - .create_user( - "u2@example.com", - false, - NewUserParams { - github_login: "u2".into(), - github_user_id: 2, - invite_count: 0, - }, - ) - .await - .unwrap(); - let user3 = db - .create_user( - "u3@example.com", - false, - NewUserParams { - github_login: "u3".into(), - github_user_id: 3, - invite_count: 0, - }, - ) - .await - .unwrap(); - let user4 = db - .create_user( - "u4@example.com", - false, - NewUserParams { - github_login: "u4".into(), - github_user_id: 4, - invite_count: 0, - }, - ) - .await - .unwrap(); + let mut user_ids = Vec::new(); + for i in 1..=4 { + user_ids.push( + db.create_user( + &format!("user{i}@example.com"), + false, + NewUserParams { + github_login: format!("user{i}"), + github_user_id: i, + invite_count: 0, + }, + ) + .await + .unwrap() + .user_id, + ); + } assert_eq!( - db.get_users_by_ids(vec![user1, user2, user3, user4]) - .await - .unwrap(), + db.get_users_by_ids(user_ids.clone()).await.unwrap(), vec![ User { - id: user1, - github_login: "u1".to_string(), + id: user_ids[0], + github_login: "user1".to_string(), github_user_id: Some(1), - email_address: Some("u1@example.com".to_string()), + email_address: Some("user1@example.com".to_string()), admin: false, ..Default::default() }, User { - id: user2, - github_login: "u2".to_string(), + id: user_ids[1], + github_login: "user2".to_string(), github_user_id: Some(2), - email_address: Some("u2@example.com".to_string()), + email_address: Some("user2@example.com".to_string()), admin: false, ..Default::default() }, User { - id: user3, - github_login: "u3".to_string(), + id: user_ids[2], + github_login: "user3".to_string(), github_user_id: Some(3), - email_address: Some("u3@example.com".to_string()), + email_address: Some("user3@example.com".to_string()), admin: false, ..Default::default() }, User { - id: user4, - github_login: "u4".to_string(), + id: user_ids[3], + github_login: "user4".to_string(), github_user_id: Some(4), - email_address: Some("u4@example.com".to_string()), + email_address: Some("user4@example.com".to_string()), admin: false, ..Default::default() } @@ -121,7 +88,8 @@ async fn test_get_user_by_github_account() { }, ) .await - .unwrap(); + .unwrap() + .user_id; let user_id2 = db .create_user( "user2@example.com", @@ -133,7 +101,8 @@ async fn test_get_user_by_github_account() { }, ) .await - .unwrap(); + .unwrap() + .user_id; let user = db .get_user_by_github_account("login1", None) @@ -177,7 +146,8 @@ async fn test_worktree_extensions() { }, ) .await - .unwrap(); + .unwrap() + .user_id; let project = db.register_project(user).await.unwrap(); db.update_worktree_extensions(project, 100, Default::default()) @@ -237,43 +207,25 @@ async fn test_user_activity() { let test_db = TestDb::postgres().await; let db = test_db.db(); - let user_1 = db - .create_user( - "u1@example.com", - false, - NewUserParams { - github_login: "u1".into(), - github_user_id: 0, - invite_count: 0, - }, - ) - .await - .unwrap(); - let user_2 = db - .create_user( - "u2@example.com", - false, - NewUserParams { - github_login: "u2".into(), - github_user_id: 0, - invite_count: 0, - }, - ) - .await - .unwrap(); - let user_3 = db - .create_user( - "u3@example.com", - false, - NewUserParams { - github_login: "u3".into(), - github_user_id: 0, - invite_count: 0, - }, - ) - .await - .unwrap(); - let project_1 = db.register_project(user_1).await.unwrap(); + let mut user_ids = Vec::new(); + for i in 0..=2 { + user_ids.push( + db.create_user( + &format!("user{i}@example.com"), + false, + NewUserParams { + github_login: format!("user{i}"), + github_user_id: i, + invite_count: 0, + }, + ) + .await + .unwrap() + .user_id, + ); + } + + let project_1 = db.register_project(user_ids[0]).await.unwrap(); db.update_worktree_extensions( project_1, 1, @@ -281,34 +233,37 @@ async fn test_user_activity() { ) .await .unwrap(); - let project_2 = db.register_project(user_2).await.unwrap(); + let project_2 = db.register_project(user_ids[1]).await.unwrap(); let t0 = OffsetDateTime::now_utc() - Duration::from_secs(60 * 60); // User 2 opens a project let t1 = t0 + Duration::from_secs(10); - db.record_user_activity(t0..t1, &[(user_2, project_2)]) + db.record_user_activity(t0..t1, &[(user_ids[1], project_2)]) .await .unwrap(); let t2 = t1 + Duration::from_secs(10); - db.record_user_activity(t1..t2, &[(user_2, project_2)]) + db.record_user_activity(t1..t2, &[(user_ids[1], project_2)]) .await .unwrap(); // User 1 joins the project let t3 = t2 + Duration::from_secs(10); - db.record_user_activity(t2..t3, &[(user_2, project_2), (user_1, project_2)]) - .await - .unwrap(); + db.record_user_activity( + t2..t3, + &[(user_ids[1], project_2), (user_ids[0], project_2)], + ) + .await + .unwrap(); // User 1 opens another project let t4 = t3 + Duration::from_secs(10); db.record_user_activity( t3..t4, &[ - (user_2, project_2), - (user_1, project_2), - (user_1, project_1), + (user_ids[1], project_2), + (user_ids[0], project_2), + (user_ids[0], project_1), ], ) .await @@ -319,10 +274,10 @@ async fn test_user_activity() { db.record_user_activity( t4..t5, &[ - (user_2, project_2), - (user_1, project_2), - (user_1, project_1), - (user_3, project_1), + (user_ids[1], project_2), + (user_ids[0], project_2), + (user_ids[0], project_1), + (user_ids[2], project_1), ], ) .await @@ -330,13 +285,16 @@ async fn test_user_activity() { // User 2 leaves let t6 = t5 + Duration::from_secs(5); - db.record_user_activity(t5..t6, &[(user_1, project_1), (user_3, project_1)]) - .await - .unwrap(); + db.record_user_activity( + t5..t6, + &[(user_ids[0], project_1), (user_ids[2], project_1)], + ) + .await + .unwrap(); let t7 = t6 + Duration::from_secs(60); let t8 = t7 + Duration::from_secs(10); - db.record_user_activity(t7..t8, &[(user_1, project_1)]) + db.record_user_activity(t7..t8, &[(user_ids[0], project_1)]) .await .unwrap(); @@ -344,8 +302,8 @@ async fn test_user_activity() { db.get_top_users_activity_summary(t0..t6, 10).await.unwrap(), &[ UserActivitySummary { - id: user_1, - github_login: "u1".to_string(), + id: user_ids[0], + github_login: "user0".to_string(), project_activity: vec![ ProjectActivitySummary { id: project_1, @@ -360,8 +318,8 @@ async fn test_user_activity() { ] }, UserActivitySummary { - id: user_2, - github_login: "u2".to_string(), + id: user_ids[1], + github_login: "user1".to_string(), project_activity: vec![ProjectActivitySummary { id: project_2, duration: Duration::from_secs(50), @@ -369,8 +327,8 @@ async fn test_user_activity() { }] }, UserActivitySummary { - id: user_3, - github_login: "u3".to_string(), + id: user_ids[2], + github_login: "user2".to_string(), project_activity: vec![ProjectActivitySummary { id: project_1, duration: Duration::from_secs(15), @@ -442,7 +400,9 @@ async fn test_user_activity() { ); assert_eq!( - db.get_user_activity_timeline(t3..t6, user_1).await.unwrap(), + db.get_user_activity_timeline(t3..t6, user_ids[0]) + .await + .unwrap(), &[ UserActivityPeriod { project_id: project_1, @@ -459,7 +419,9 @@ async fn test_user_activity() { ] ); assert_eq!( - db.get_user_activity_timeline(t0..t8, user_1).await.unwrap(), + db.get_user_activity_timeline(t0..t8, user_ids[0]) + .await + .unwrap(), &[ UserActivityPeriod { project_id: project_2, @@ -501,7 +463,8 @@ async fn test_recent_channel_messages() { }, ) .await - .unwrap(); + .unwrap() + .user_id; let org = db.create_org("org", "org").await.unwrap(); let channel = db.create_org_channel(org, "channel").await.unwrap(); for i in 0..10 { @@ -545,7 +508,8 @@ async fn test_channel_message_nonces() { }, ) .await - .unwrap(); + .unwrap() + .user_id; let org = db.create_org("org", "org").await.unwrap(); let channel = db.create_org_channel(org, "channel").await.unwrap(); @@ -587,7 +551,8 @@ async fn test_create_access_tokens() { }, ) .await - .unwrap(); + .unwrap() + .user_id; db.create_access_token_hash(user, "h1", 3).await.unwrap(); db.create_access_token_hash(user, "h2", 3).await.unwrap(); @@ -678,42 +643,27 @@ async fn test_add_contacts() { ] { let db = test_db.db(); - let user_1 = db - .create_user( - "u1@example.com", - false, - NewUserParams { - github_login: "u1".into(), - github_user_id: 0, - invite_count: 0, - }, - ) - .await - .unwrap(); - let user_2 = db - .create_user( - "u2@example.com", - false, - NewUserParams { - github_login: "u2".into(), - github_user_id: 1, - invite_count: 0, - }, - ) - .await - .unwrap(); - let user_3 = db - .create_user( - "u3@example.com", - false, - NewUserParams { - github_login: "u3".into(), - github_user_id: 2, - invite_count: 0, - }, - ) - .await - .unwrap(); + let mut user_ids = Vec::new(); + for i in 0..3 { + user_ids.push( + db.create_user( + &format!("user{i}@example.com"), + false, + NewUserParams { + github_login: format!("user{i}"), + github_user_id: i, + invite_count: 0, + }, + ) + .await + .unwrap() + .user_id, + ); + } + + let user_1 = user_ids[0]; + let user_2 = user_ids[1]; + let user_3 = user_ids[2]; // User starts with no contacts assert_eq!( @@ -927,12 +877,12 @@ async fn test_add_contacts() { async fn test_invite_codes() { let postgres = TestDb::postgres().await; let db = postgres.db(); - let user1 = db + let NewUserResult { user_id: user1, .. } = db .create_user( - "u1@example.com", + "user1@example.com", false, NewUserParams { - github_login: "u1".into(), + github_login: "user1".into(), github_user_id: 0, invite_count: 0, }, @@ -954,13 +904,14 @@ async fn test_invite_codes() { // User 2 redeems the invite code and becomes a contact of user 1. let user2_invite = db - .create_invite_from_code(&invite_code, "u2@example.com", Some("user-2-device-id")) + .create_invite_from_code(&invite_code, "user2@example.com", Some("user-2-device-id")) .await .unwrap(); let NewUserResult { user_id: user2, inviting_user_id, signup_device_id, + metrics_id, } = db .create_user_from_invite( &user2_invite, @@ -976,6 +927,7 @@ async fn test_invite_codes() { assert_eq!(invite_count, 1); assert_eq!(inviting_user_id, Some(user1)); assert_eq!(signup_device_id.unwrap(), "user-2-device-id"); + assert_eq!(db.get_user_metrics_id(user2).await.unwrap(), metrics_id); assert_eq!( db.get_contacts(user1).await.unwrap(), [ @@ -1009,13 +961,14 @@ async fn test_invite_codes() { // User 3 redeems the invite code and becomes a contact of user 1. let user3_invite = db - .create_invite_from_code(&invite_code, "u3@example.com", None) + .create_invite_from_code(&invite_code, "user3@example.com", None) .await .unwrap(); let NewUserResult { user_id: user3, inviting_user_id, signup_device_id, + .. } = db .create_user_from_invite( &user3_invite, @@ -1067,7 +1020,7 @@ async fn test_invite_codes() { ); // Trying to reedem the code for the third time results in an error. - db.create_invite_from_code(&invite_code, "u4@example.com", Some("user-4-device-id")) + db.create_invite_from_code(&invite_code, "user4@example.com", Some("user-4-device-id")) .await .unwrap_err(); @@ -1079,7 +1032,7 @@ async fn test_invite_codes() { // User 4 can now redeem the invite code and becomes a contact of user 1. let user4_invite = db - .create_invite_from_code(&invite_code, "u4@example.com", Some("user-4-device-id")) + .create_invite_from_code(&invite_code, "user4@example.com", Some("user-4-device-id")) .await .unwrap(); let user4 = db @@ -1137,7 +1090,7 @@ async fn test_invite_codes() { ); // An existing user cannot redeem invite codes. - db.create_invite_from_code(&invite_code, "u2@example.com", Some("user-2-device-id")) + db.create_invite_from_code(&invite_code, "user2@example.com", Some("user-2-device-id")) .await .unwrap_err(); let (_, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap(); @@ -1232,6 +1185,7 @@ async fn test_signups() { user_id, inviting_user_id, signup_device_id, + .. } = db .create_user_from_invite( &Invite { @@ -1284,6 +1238,51 @@ async fn test_signups() { .unwrap_err(); } +#[tokio::test(flavor = "multi_thread")] +async fn test_metrics_id() { + let postgres = TestDb::postgres().await; + let db = postgres.db(); + + let NewUserResult { + user_id: user1, + metrics_id: metrics_id1, + .. + } = db + .create_user( + "person1@example.com", + false, + NewUserParams { + github_login: "person1".into(), + github_user_id: 101, + invite_count: 5, + }, + ) + .await + .unwrap(); + let NewUserResult { + user_id: user2, + metrics_id: metrics_id2, + .. + } = db + .create_user( + "person2@example.com", + false, + NewUserParams { + github_login: "person2".into(), + github_user_id: 102, + invite_count: 5, + }, + ) + .await + .unwrap(); + + assert_eq!(db.get_user_metrics_id(user1).await.unwrap(), metrics_id1); + assert_eq!(db.get_user_metrics_id(user2).await.unwrap(), metrics_id2); + assert_eq!(metrics_id1.len(), 36); + assert_eq!(metrics_id2.len(), 36); + assert_ne!(metrics_id1, metrics_id2); +} + fn build_background_executor() -> Arc { Deterministic::new(0).build_background() } diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 3c9886dc16..e9643d3deb 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -4663,7 +4663,8 @@ async fn test_random_collaboration( }, ) .await - .unwrap(); + .unwrap() + .user_id; let mut available_guests = vec![ "guest-1".to_string(), "guest-2".to_string(), @@ -4683,7 +4684,8 @@ async fn test_random_collaboration( }, ) .await - .unwrap(); + .unwrap() + .user_id; assert_eq!(*username, format!("guest-{}", guest_user_id)); server .app_state @@ -5206,6 +5208,7 @@ impl TestServer { ) .await .unwrap() + .user_id }; let client_name = name.to_string(); let mut client = cx.read(|cx| Client::new(http.clone(), cx)); diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 5f27352c5a..467ec174ab 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -205,7 +205,8 @@ impl Server { .add_request_handler(Server::follow) .add_message_handler(Server::unfollow) .add_message_handler(Server::update_followers) - .add_request_handler(Server::get_channel_messages); + .add_request_handler(Server::get_channel_messages) + .add_request_handler(Server::get_private_user_info); Arc::new(server) } @@ -1727,6 +1728,20 @@ impl Server { Ok(()) } + async fn get_private_user_info( + self: Arc, + request: TypedEnvelope, + response: Response, + ) -> Result<()> { + let user_id = self + .store() + .await + .user_id_for_connection(request.sender_id)?; + let metrics_id = self.app_state.db.get_user_metrics_id(user_id).await?; + response.send(proto::GetPrivateUserInfoResponse { metrics_id })?; + Ok(()) + } + pub(crate) async fn store(&self) -> StoreGuard<'_> { #[cfg(test)] tokio::task::yield_now().await; diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 7840829b44..6a48ad1b97 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -108,6 +108,9 @@ message Envelope { FollowResponse follow_response = 93; UpdateFollowers update_followers = 94; Unfollow unfollow = 95; + + GetPrivateUserInfo get_private_user_info = 96; + GetPrivateUserInfoResponse get_private_user_info_response = 97; } } @@ -748,6 +751,12 @@ message Unfollow { uint32 leader_id = 2; } +message GetPrivateUserInfo {} + +message GetPrivateUserInfoResponse { + string metrics_id = 1; +} + // Entities message UpdateActiveView { diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 2ba3fa18ba..001753c709 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -167,6 +167,8 @@ messages!( (UpdateProject, Foreground), (UpdateWorktree, Foreground), (UpdateWorktreeExtensions, Background), + (GetPrivateUserInfo, Foreground), + (GetPrivateUserInfoResponse, Foreground), ); request_messages!( @@ -189,6 +191,7 @@ request_messages!( (GetTypeDefinition, GetTypeDefinitionResponse), (GetDocumentHighlights, GetDocumentHighlightsResponse), (GetReferences, GetReferencesResponse), + (GetPrivateUserInfo, GetPrivateUserInfoResponse), (GetProjectSymbols, GetProjectSymbolsResponse), (FuzzySearchUsers, UsersResponse), (GetUsers, UsersResponse), From 35a537dae012a73c085c24d4a554e7b199de2d86 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 29 Sep 2022 13:51:17 -0700 Subject: [PATCH 091/314] Fix FakeServer to expect new GetPrivateUserInfo request --- crates/client/src/test.rs | 64 ++++++++++++++------- crates/contacts_panel/src/contacts_panel.rs | 11 ++++ 2 files changed, 54 insertions(+), 21 deletions(-) diff --git a/crates/client/src/test.rs b/crates/client/src/test.rs index c634978a57..56d3d80b63 100644 --- a/crates/client/src/test.rs +++ b/crates/client/src/test.rs @@ -6,7 +6,10 @@ use anyhow::{anyhow, Result}; use futures::{future::BoxFuture, stream::BoxStream, Future, StreamExt}; use gpui::{executor, ModelHandle, TestAppContext}; use parking_lot::Mutex; -use rpc::{proto, ConnectionId, Peer, Receipt, TypedEnvelope}; +use rpc::{ + proto::{self, GetPrivateUserInfo, GetPrivateUserInfoResponse}, + ConnectionId, Peer, Receipt, TypedEnvelope, +}; use std::{fmt, rc::Rc, sync::Arc}; pub struct FakeServer { @@ -93,6 +96,7 @@ impl FakeServer { .authenticate_and_connect(false, &cx.to_async()) .await .unwrap(); + server } @@ -126,26 +130,44 @@ impl FakeServer { #[allow(clippy::await_holding_lock)] pub async fn receive(&self) -> Result> { self.executor.start_waiting(); - let message = self - .state - .lock() - .incoming - .as_mut() - .expect("not connected") - .next() - .await - .ok_or_else(|| anyhow!("other half hung up"))?; - self.executor.finish_waiting(); - let type_name = message.payload_type_name(); - Ok(*message - .into_any() - .downcast::>() - .unwrap_or_else(|_| { - panic!( - "fake server received unexpected message type: {:?}", - type_name - ); - })) + + loop { + let message = self + .state + .lock() + .incoming + .as_mut() + .expect("not connected") + .next() + .await + .ok_or_else(|| anyhow!("other half hung up"))?; + self.executor.finish_waiting(); + let type_name = message.payload_type_name(); + let message = message.into_any(); + + if message.is::>() { + return Ok(*message.downcast().unwrap()); + } + + if message.is::>() { + self.respond( + message + .downcast::>() + .unwrap() + .receipt(), + GetPrivateUserInfoResponse { + metrics_id: "the-metrics-id".into(), + }, + ) + .await; + continue; + } + + panic!( + "fake server received unexpected message type: {:?}", + type_name + ); + } } pub async fn respond( diff --git a/crates/contacts_panel/src/contacts_panel.rs b/crates/contacts_panel/src/contacts_panel.rs index 7dcfb8cea4..91b86aaf0e 100644 --- a/crates/contacts_panel/src/contacts_panel.rs +++ b/crates/contacts_panel/src/contacts_panel.rs @@ -1220,6 +1220,17 @@ mod tests { let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx)); let project_store = cx.add_model(|_| ProjectStore::new(project::Db::open_fake())); let server = FakeServer::for_client(current_user_id, &client, cx).await; + + let request = server.receive::().await.unwrap(); + server + .respond( + request.receipt(), + proto::GetPrivateUserInfoResponse { + metrics_id: "the-metrics-id".into(), + }, + ) + .await; + let fs = FakeFs::new(cx.background()); fs.insert_tree("/private_dir", json!({ "one.rs": "" })) .await; From a977593f3d58bad7c90c8bbc662ebfb4f309a5b5 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 29 Sep 2022 16:47:20 -0700 Subject: [PATCH 092/314] 0.57.0 --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b32b6a47a2..e99fa91008 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7151,7 +7151,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.56.0" +version = "0.57.0" dependencies = [ "activity_indicator", "anyhow", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index c96163d99e..48a84a5831 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] description = "The fast, collaborative code editor." edition = "2021" name = "zed" -version = "0.56.0" +version = "0.57.0" [lib] name = "zed" From 25bba396efa28139fc05ae867a10f9ca40206129 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 30 Sep 2022 09:51:03 +0200 Subject: [PATCH 093/314] Cache `CGEventSource` and avoid leaking `CGEvent` when handling events --- crates/gpui/src/platform/mac/event.rs | 83 +++++++++++++++------------ 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/crates/gpui/src/platform/mac/event.rs b/crates/gpui/src/platform/mac/event.rs index 51524f4b15..ea2b492b27 100644 --- a/crates/gpui/src/platform/mac/event.rs +++ b/crates/gpui/src/platform/mac/event.rs @@ -14,8 +14,10 @@ use core_graphics::{ event::{CGEvent, CGEventFlags, CGKeyCode}, event_source::{CGEventSource, CGEventSourceStateID}, }; +use ctor::ctor; +use foreign_types::ForeignType; use objc::{class, msg_send, sel, sel_impl}; -use std::{borrow::Cow, ffi::CStr, os::raw::c_char}; +use std::{borrow::Cow, ffi::CStr, mem, os::raw::c_char, ptr}; const BACKSPACE_KEY: u16 = 0x7f; const SPACE_KEY: u16 = b' ' as u16; @@ -25,6 +27,15 @@ const ESCAPE_KEY: u16 = 0x1b; const TAB_KEY: u16 = 0x09; const SHIFT_TAB_KEY: u16 = 0x19; +static mut EVENT_SOURCE: core_graphics::sys::CGEventSourceRef = ptr::null_mut(); + +#[ctor] +unsafe fn build_event_source() { + let source = CGEventSource::new(CGEventSourceStateID::Private).unwrap(); + EVENT_SOURCE = source.as_ptr(); + mem::forget(source); +} + pub fn key_to_native(key: &str) -> Cow { use cocoa::appkit::*; let code = match key { @@ -228,7 +239,8 @@ unsafe fn parse_keystroke(native_event: id) -> Keystroke { let mut chars_ignoring_modifiers = CStr::from_ptr(native_event.charactersIgnoringModifiers().UTF8String() as *mut c_char) .to_str() - .unwrap(); + .unwrap() + .to_string(); let first_char = chars_ignoring_modifiers.chars().next().map(|ch| ch as u16); let modifiers = native_event.modifierFlags(); @@ -243,31 +255,31 @@ unsafe fn parse_keystroke(native_event: id) -> Keystroke { #[allow(non_upper_case_globals)] let key = match first_char { - Some(SPACE_KEY) => "space", - Some(BACKSPACE_KEY) => "backspace", - Some(ENTER_KEY) | Some(NUMPAD_ENTER_KEY) => "enter", - Some(ESCAPE_KEY) => "escape", - Some(TAB_KEY) => "tab", - Some(SHIFT_TAB_KEY) => "tab", - Some(NSUpArrowFunctionKey) => "up", - Some(NSDownArrowFunctionKey) => "down", - Some(NSLeftArrowFunctionKey) => "left", - Some(NSRightArrowFunctionKey) => "right", - Some(NSPageUpFunctionKey) => "pageup", - Some(NSPageDownFunctionKey) => "pagedown", - Some(NSDeleteFunctionKey) => "delete", - Some(NSF1FunctionKey) => "f1", - Some(NSF2FunctionKey) => "f2", - Some(NSF3FunctionKey) => "f3", - Some(NSF4FunctionKey) => "f4", - Some(NSF5FunctionKey) => "f5", - Some(NSF6FunctionKey) => "f6", - Some(NSF7FunctionKey) => "f7", - Some(NSF8FunctionKey) => "f8", - Some(NSF9FunctionKey) => "f9", - Some(NSF10FunctionKey) => "f10", - Some(NSF11FunctionKey) => "f11", - Some(NSF12FunctionKey) => "f12", + Some(SPACE_KEY) => "space".to_string(), + Some(BACKSPACE_KEY) => "backspace".to_string(), + Some(ENTER_KEY) | Some(NUMPAD_ENTER_KEY) => "enter".to_string(), + Some(ESCAPE_KEY) => "escape".to_string(), + Some(TAB_KEY) => "tab".to_string(), + Some(SHIFT_TAB_KEY) => "tab".to_string(), + Some(NSUpArrowFunctionKey) => "up".to_string(), + Some(NSDownArrowFunctionKey) => "down".to_string(), + Some(NSLeftArrowFunctionKey) => "left".to_string(), + Some(NSRightArrowFunctionKey) => "right".to_string(), + Some(NSPageUpFunctionKey) => "pageup".to_string(), + Some(NSPageDownFunctionKey) => "pagedown".to_string(), + Some(NSDeleteFunctionKey) => "delete".to_string(), + Some(NSF1FunctionKey) => "f1".to_string(), + Some(NSF2FunctionKey) => "f2".to_string(), + Some(NSF3FunctionKey) => "f3".to_string(), + Some(NSF4FunctionKey) => "f4".to_string(), + Some(NSF5FunctionKey) => "f5".to_string(), + Some(NSF6FunctionKey) => "f6".to_string(), + Some(NSF7FunctionKey) => "f7".to_string(), + Some(NSF8FunctionKey) => "f8".to_string(), + Some(NSF9FunctionKey) => "f9".to_string(), + Some(NSF10FunctionKey) => "f10".to_string(), + Some(NSF11FunctionKey) => "f11".to_string(), + Some(NSF12FunctionKey) => "f12".to_string(), _ => { let mut chars_ignoring_modifiers_and_shift = chars_for_modified_key(native_event.keyCode(), false, false); @@ -303,21 +315,19 @@ unsafe fn parse_keystroke(native_event: id) -> Keystroke { shift, cmd, function, - key: key.into(), + key, } } -fn chars_for_modified_key<'a>(code: CGKeyCode, cmd: bool, shift: bool) -> &'a str { +fn chars_for_modified_key(code: CGKeyCode, cmd: bool, shift: bool) -> String { // Ideally, we would use `[NSEvent charactersByApplyingModifiers]` but that // always returns an empty string with certain keyboards, e.g. Japanese. Synthesizing // an event with the given flags instead lets us access `characters`, which always // returns a valid string. - let event = CGEvent::new_keyboard_event( - CGEventSource::new(CGEventSourceStateID::Private).unwrap(), - code, - true, - ) - .unwrap(); + let source = unsafe { core_graphics::event_source::CGEventSource::from_ptr(EVENT_SOURCE) }; + let event = CGEvent::new_keyboard_event(source.clone(), code, true).unwrap(); + mem::forget(source); + let mut flags = CGEventFlags::empty(); if cmd { flags |= CGEventFlags::CGEventFlagCommand; @@ -327,10 +337,11 @@ fn chars_for_modified_key<'a>(code: CGKeyCode, cmd: bool, shift: bool) -> &'a st } event.set_flags(flags); - let event: id = unsafe { msg_send![class!(NSEvent), eventWithCGEvent: event] }; unsafe { + let event: id = msg_send![class!(NSEvent), eventWithCGEvent: &*event]; CStr::from_ptr(event.characters().UTF8String()) .to_str() .unwrap() + .to_string() } } From be8990ea789cc4f6f45b920bf2e853008aada159 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 30 Sep 2022 11:35:50 +0200 Subject: [PATCH 094/314] Remove project join requests --- crates/collab/src/integration_tests.rs | 196 +------- crates/collab/src/rpc.rs | 431 +++++------------- crates/collab/src/rpc/store.rs | 222 +++------ crates/contacts_panel/src/contacts_panel.rs | 67 +-- .../src/join_project_notification.rs | 80 ---- crates/project/src/project.rs | 341 +++----------- crates/rpc/proto/zed.proto | 53 +-- crates/rpc/src/proto.rs | 9 +- crates/workspace/src/workspace.rs | 32 +- crates/zed/src/main.rs | 2 +- crates/zed/src/zed.rs | 7 +- 11 files changed, 284 insertions(+), 1156 deletions(-) delete mode 100644 crates/contacts_panel/src/join_project_notification.rs diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index d000ebb309..ebdc952a0f 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -7,7 +7,7 @@ use ::rpc::Peer; use anyhow::anyhow; use call::Room; use client::{ - self, proto, test::FakeHttpClient, Channel, ChannelDetails, ChannelList, Client, Connection, + self, test::FakeHttpClient, Channel, ChannelDetails, ChannelList, Client, Connection, Credentials, EstablishConnectionError, UserStore, RECEIVE_TIMEOUT, }; use collections::{BTreeMap, HashMap, HashSet}; @@ -40,7 +40,6 @@ use serde_json::json; use settings::{Formatter, Settings}; use sqlx::types::time::OffsetDateTime; use std::{ - cell::RefCell, env, ops::Deref, path::{Path, PathBuf}, @@ -459,12 +458,15 @@ async fn test_unshare_project( .await .unwrap(); - // When client B leaves the project, it gets automatically unshared. - cx_b.update(|_| drop(project_b)); + // When client A unshares the project, client B's project becomes read-only. + project_a + .update(cx_a, |project, cx| project.unshare(cx)) + .unwrap(); deterministic.run_until_parked(); assert!(worktree_a.read_with(cx_a, |tree, _| !tree.as_local().unwrap().is_shared())); + assert!(project_b.read_with(cx_b, |project, _| project.is_read_only())); - // When client B joins again, the project gets re-shared. + // Client B can join again after client A re-shares. let project_b2 = client_b.build_remote_project(&project_a, cx_a, cx_b).await; assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared())); project_b2 @@ -515,7 +517,7 @@ async fn test_host_disconnect( let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap()); - let project_id = project_a.read_with(cx_a, |project, _| project.remote_id().unwrap()); + project_a.read_with(cx_a, |project, _| project.remote_id().unwrap()); let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared())); @@ -539,20 +541,6 @@ async fn test_host_disconnect( editor_b.update(cx_b, |editor, cx| editor.insert("X", cx)); assert!(cx_b.is_window_edited(workspace_b.window_id())); - // Request to join that project as client C - let project_c = cx_c.spawn(|cx| { - Project::remote( - project_id, - client_c.client.clone(), - client_c.user_store.clone(), - client_c.project_store.clone(), - client_c.language_registry.clone(), - FakeFs::new(cx.background()), - cx, - ) - }); - deterministic.run_until_parked(); - // Drop client A's connection. Collaborators should disappear and the project should not be shown as shared. server.disconnect_client(client_a.current_user_id(cx_a)); cx_a.foreground().advance_clock(rpc::RECEIVE_TIMEOUT); @@ -564,10 +552,6 @@ async fn test_host_disconnect( .condition(cx_b, |project, _| project.is_read_only()) .await; assert!(worktree_a.read_with(cx_a, |tree, _| !tree.as_local().unwrap().is_shared())); - assert!(matches!( - project_c.await.unwrap_err(), - project::JoinProjectError::HostWentOffline - )); // Ensure client B's edited state is reset and that the whole window is blurred. cx_b.read(|cx| { @@ -598,139 +582,6 @@ async fn test_host_disconnect( .unwrap(); } -#[gpui::test(iterations = 10)] -async fn test_decline_join_request( - deterministic: Arc, - cx_a: &mut TestAppContext, - cx_b: &mut TestAppContext, -) { - cx_a.foreground().forbid_parking(); - let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; - let client_a = server.create_client(cx_a, "user_a").await; - let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) - .await; - - client_a.fs.insert_tree("/a", json!({})).await; - - let (project_a, _) = client_a.build_local_project("/a", cx_a).await; - let project_id = project_a.read_with(cx_a, |project, _| project.remote_id().unwrap()); - - // Request to join that project as client B - let project_b = cx_b.spawn(|cx| { - Project::remote( - project_id, - client_b.client.clone(), - client_b.user_store.clone(), - client_b.project_store.clone(), - client_b.language_registry.clone(), - FakeFs::new(cx.background()), - cx, - ) - }); - deterministic.run_until_parked(); - project_a.update(cx_a, |project, cx| { - project.respond_to_join_request(client_b.user_id().unwrap(), false, cx) - }); - assert!(matches!( - project_b.await.unwrap_err(), - project::JoinProjectError::HostDeclined - )); - - // Request to join the project again as client B - let project_b = cx_b.spawn(|cx| { - Project::remote( - project_id, - client_b.client.clone(), - client_b.user_store.clone(), - client_b.project_store.clone(), - client_b.language_registry.clone(), - FakeFs::new(cx.background()), - cx, - ) - }); - - // Close the project on the host - deterministic.run_until_parked(); - cx_a.update(|_| drop(project_a)); - deterministic.run_until_parked(); - assert!(matches!( - project_b.await.unwrap_err(), - project::JoinProjectError::HostClosedProject - )); -} - -#[gpui::test(iterations = 10)] -async fn test_cancel_join_request( - deterministic: Arc, - cx_a: &mut TestAppContext, - cx_b: &mut TestAppContext, -) { - cx_a.foreground().forbid_parking(); - let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; - let client_a = server.create_client(cx_a, "user_a").await; - let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) - .await; - - client_a.fs.insert_tree("/a", json!({})).await; - let (project_a, _) = client_a.build_local_project("/a", cx_a).await; - let project_id = project_a.read_with(cx_a, |project, _| project.remote_id().unwrap()); - - let user_b = client_a - .user_store - .update(cx_a, |store, cx| { - store.get_user(client_b.user_id().unwrap(), cx) - }) - .await - .unwrap(); - - let project_a_events = Rc::new(RefCell::new(Vec::new())); - project_a.update(cx_a, { - let project_a_events = project_a_events.clone(); - move |_, cx| { - cx.subscribe(&cx.handle(), move |_, _, event, _| { - project_a_events.borrow_mut().push(event.clone()); - }) - .detach(); - } - }); - - // Request to join that project as client B - let project_b = cx_b.spawn(|cx| { - Project::remote( - project_id, - client_b.client.clone(), - client_b.user_store.clone(), - client_b.project_store.clone(), - client_b.language_registry.clone(), - FakeFs::new(cx.background()), - cx, - ) - }); - deterministic.run_until_parked(); - assert_eq!( - &*project_a_events.borrow(), - &[project::Event::ContactRequestedJoin(user_b.clone())] - ); - project_a_events.borrow_mut().clear(); - - // Cancel the join request by leaving the project - client_b - .client - .send(proto::LeaveProject { project_id }) - .unwrap(); - drop(project_b); - - deterministic.run_until_parked(); - assert_eq!( - &*project_a_events.borrow(), - &[project::Event::ContactCancelledJoinRequest(user_b)] - ); -} - #[gpui::test(iterations = 10)] async fn test_propagate_saves_and_fs_changes( cx_a: &mut TestAppContext, @@ -4586,7 +4437,6 @@ async fn test_random_collaboration( let host = server.create_client(&mut host_cx, "host").await; let host_project = host_cx.update(|cx| { Project::local( - true, host.client.clone(), host.user_store.clone(), host.project_store.clone(), @@ -4738,6 +4588,11 @@ async fn test_random_collaboration( .await; host_language_registry.add(Arc::new(language)); + host_project + .update(&mut host_cx, |project, cx| project.share(cx)) + .await + .unwrap(); + let op_start_signal = futures::channel::mpsc::unbounded(); user_ids.push(host.current_user_id(&host_cx)); op_start_signals.push(op_start_signal.0); @@ -5097,7 +4952,7 @@ impl TestServer { let fs = FakeFs::new(cx.background()); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx)); - let project_store = cx.add_model(|_| ProjectStore::new(project::Db::open_fake())); + let project_store = cx.add_model(|_| ProjectStore::new()); let app_state = Arc::new(workspace::AppState { client: client.clone(), user_store: user_store.clone(), @@ -5283,7 +5138,6 @@ impl TestClient { ) -> (ModelHandle, WorktreeId) { let project = cx.update(|cx| { Project::local( - true, self.client.clone(), self.user_store.clone(), self.project_store.clone(), @@ -5316,7 +5170,10 @@ impl TestClient { let host_project_id = host_project .read_with(host_cx, |project, _| project.next_remote_id()) .await; - let guest_user_id = self.user_id().unwrap(); + host_project + .update(host_cx, |project, cx| project.share(cx)) + .await + .unwrap(); let languages = host_project.read_with(host_cx, |project, _| project.languages().clone()); let project_b = guest_cx.spawn(|cx| { Project::remote( @@ -5329,10 +5186,7 @@ impl TestClient { cx, ) }); - host_cx.foreground().run_until_parked(); - host_project.update(host_cx, |project, cx| { - project.respond_to_join_request(guest_user_id, true, cx) - }); + let project = project_b.await.unwrap(); project } @@ -5369,18 +5223,6 @@ impl TestClient { ) -> anyhow::Result<()> { let fs = project.read_with(cx, |project, _| project.fs().clone()); - cx.update(|cx| { - cx.subscribe(&project, move |project, event, cx| { - if let project::Event::ContactRequestedJoin(user) = event { - log::info!("Host: accepting join request from {}", user.github_login); - project.update(cx, |project, cx| { - project.respond_to_join_request(user.id, true, cx) - }); - } - }) - .detach(); - }); - while op_start_signal.next().await.is_some() { let distribution = rng.lock().gen_range::(0..100); let files = fs.as_fake().files().await; diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 55c3414d85..192adb701c 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -88,11 +88,6 @@ impl Response { self.server.peer.respond(self.receipt, payload)?; Ok(()) } - - fn into_receipt(self) -> Receipt { - self.responded.store(true, SeqCst); - self.receipt - } } pub struct Server { @@ -160,7 +155,7 @@ impl Server { .add_request_handler(Server::unregister_project) .add_request_handler(Server::join_project) .add_message_handler(Server::leave_project) - .add_message_handler(Server::respond_to_join_project_request) + .add_message_handler(Server::unshare_project) .add_message_handler(Server::update_project) .add_message_handler(Server::register_project_activity) .add_request_handler(Server::update_worktree) @@ -491,21 +486,6 @@ impl Server { }, ) }); - - for (_, receipts) in project.join_requests { - for receipt in receipts { - self.peer.respond( - receipt, - proto::JoinProjectResponse { - variant: Some(proto::join_project_response::Variant::Decline( - proto::join_project_response::Decline { - reason: proto::join_project_response::decline::Reason::WentOffline as i32 - }, - )), - }, - )?; - } - } } for project_id in removed_connection.guest_project_ids { @@ -519,16 +499,6 @@ impl Server { }, ) }); - if project.guests.is_empty() { - self.peer - .send( - project.host_connection_id, - proto::ProjectUnshared { - project_id: project_id.to_proto(), - }, - ) - .trace_err(); - } } } @@ -727,11 +697,9 @@ impl Server { .await .user_id_for_connection(request.sender_id)?; let project_id = self.app_state.db.register_project(user_id).await?; - self.store().await.register_project( - request.sender_id, - project_id, - request.payload.online, - )?; + self.store() + .await + .register_project(request.sender_id, project_id)?; response.send(proto::RegisterProjectResponse { project_id: project_id.to_proto(), @@ -746,11 +714,10 @@ impl Server { response: Response, ) -> Result<()> { let project_id = ProjectId::from_proto(request.payload.project_id); - let (user_id, project) = { - let mut state = self.store().await; - let project = state.unregister_project(project_id, request.sender_id)?; - (state.user_id_for_connection(request.sender_id)?, project) - }; + let project = self + .store() + .await + .unregister_project(project_id, request.sender_id)?; self.app_state.db.unregister_project(project_id).await?; broadcast( @@ -765,32 +732,27 @@ impl Server { ) }, ); - for (_, receipts) in project.join_requests { - for receipt in receipts { - self.peer.respond( - receipt, - proto::JoinProjectResponse { - variant: Some(proto::join_project_response::Variant::Decline( - proto::join_project_response::Decline { - reason: proto::join_project_response::decline::Reason::Closed - as i32, - }, - )), - }, - )?; - } - } - - // Send out the `UpdateContacts` message before responding to the unregister - // request. This way, when the project's host can keep track of the project's - // remote id until after they've received the `UpdateContacts` message for - // themself. - self.update_user_contacts(user_id).await?; response.send(proto::Ack {})?; Ok(()) } + async fn unshare_project( + self: Arc, + message: TypedEnvelope, + ) -> Result<()> { + let project_id = ProjectId::from_proto(message.payload.project_id); + let project = self + .store() + .await + .unshare_project(project_id, message.sender_id)?; + broadcast(message.sender_id, project.guest_connection_ids, |conn_id| { + self.peer.send(conn_id, message.payload.clone()) + }); + + Ok(()) + } + async fn update_user_contacts(self: &Arc, user_id: UserId) -> Result<()> { let contacts = self.app_state.db.get_contacts(user_id).await?; let store = self.store().await; @@ -849,167 +811,93 @@ impl Server { return Err(anyhow!("no such project"))?; } - self.store().await.request_join_project( - guest_user_id, - project_id, - response.into_receipt(), - )?; - self.peer.send( - host_connection_id, - proto::RequestJoinProject { - project_id: project_id.to_proto(), - requester_id: guest_user_id.to_proto(), - }, - )?; - Ok(()) - } + let mut store = self.store().await; + let (project, replica_id) = store.join_project(request.sender_id, project_id)?; + let peer_count = project.guests.len(); + let mut collaborators = Vec::with_capacity(peer_count); + collaborators.push(proto::Collaborator { + peer_id: project.host_connection_id.0, + replica_id: 0, + user_id: project.host.user_id.to_proto(), + }); + let worktrees = project + .worktrees + .iter() + .map(|(id, worktree)| proto::WorktreeMetadata { + id: *id, + root_name: worktree.root_name.clone(), + visible: worktree.visible, + }) + .collect::>(); - async fn respond_to_join_project_request( - self: Arc, - request: TypedEnvelope, - ) -> Result<()> { - let host_user_id; - - { - let mut state = self.store().await; - let project_id = ProjectId::from_proto(request.payload.project_id); - let project = state.project(project_id)?; - if project.host_connection_id != request.sender_id { - Err(anyhow!("no such connection"))?; - } - - host_user_id = project.host.user_id; - let guest_user_id = UserId::from_proto(request.payload.requester_id); - - if !request.payload.allow { - let receipts = state - .deny_join_project_request(request.sender_id, guest_user_id, project_id) - .ok_or_else(|| anyhow!("no such request"))?; - for receipt in receipts { - self.peer.respond( - receipt, - proto::JoinProjectResponse { - variant: Some(proto::join_project_response::Variant::Decline( - proto::join_project_response::Decline { - reason: proto::join_project_response::decline::Reason::Declined - as i32, - }, - )), - }, - )?; - } - return Ok(()); - } - - let (receipts_with_replica_ids, project) = state - .accept_join_project_request(request.sender_id, guest_user_id, project_id) - .ok_or_else(|| anyhow!("no such request"))?; - - let peer_count = project.guests.len(); - let mut collaborators = Vec::with_capacity(peer_count); - collaborators.push(proto::Collaborator { - peer_id: project.host_connection_id.0, - replica_id: 0, - user_id: project.host.user_id.to_proto(), - }); - let worktrees = project - .worktrees - .iter() - .map(|(id, worktree)| proto::WorktreeMetadata { - id: *id, - root_name: worktree.root_name.clone(), - visible: worktree.visible, - }) - .collect::>(); - - // Add all guests other than the requesting user's own connections as collaborators - for (guest_conn_id, guest) in &project.guests { - if receipts_with_replica_ids - .iter() - .all(|(receipt, _)| receipt.sender_id != *guest_conn_id) - { - collaborators.push(proto::Collaborator { - peer_id: guest_conn_id.0, - replica_id: guest.replica_id as u32, - user_id: guest.user_id.to_proto(), - }); - } - } - - for conn_id in project.connection_ids() { - for (receipt, replica_id) in &receipts_with_replica_ids { - if conn_id != receipt.sender_id { - self.peer.send( - conn_id, - proto::AddProjectCollaborator { - project_id: project_id.to_proto(), - collaborator: Some(proto::Collaborator { - peer_id: receipt.sender_id.0, - replica_id: *replica_id as u32, - user_id: guest_user_id.to_proto(), - }), - }, - )?; - } - } - } - - // First, we send the metadata associated with each worktree. - for (receipt, replica_id) in &receipts_with_replica_ids { - self.peer.respond( - *receipt, - proto::JoinProjectResponse { - variant: Some(proto::join_project_response::Variant::Accept( - proto::join_project_response::Accept { - worktrees: worktrees.clone(), - replica_id: *replica_id as u32, - collaborators: collaborators.clone(), - language_servers: project.language_servers.clone(), - }, - )), - }, - )?; - } - - for (worktree_id, worktree) in &project.worktrees { - #[cfg(any(test, feature = "test-support"))] - const MAX_CHUNK_SIZE: usize = 2; - #[cfg(not(any(test, feature = "test-support")))] - const MAX_CHUNK_SIZE: usize = 256; - - // Stream this worktree's entries. - let message = proto::UpdateWorktree { - project_id: project_id.to_proto(), - worktree_id: *worktree_id, - root_name: worktree.root_name.clone(), - updated_entries: worktree.entries.values().cloned().collect(), - removed_entries: Default::default(), - scan_id: worktree.scan_id, - is_last_update: worktree.is_complete, - }; - for update in proto::split_worktree_update(message, MAX_CHUNK_SIZE) { - for (receipt, _) in &receipts_with_replica_ids { - self.peer.send(receipt.sender_id, update.clone())?; - } - } - - // Stream this worktree's diagnostics. - for summary in worktree.diagnostic_summaries.values() { - for (receipt, _) in &receipts_with_replica_ids { - self.peer.send( - receipt.sender_id, - proto::UpdateDiagnosticSummary { - project_id: project_id.to_proto(), - worktree_id: *worktree_id, - summary: Some(summary.clone()), - }, - )?; - } - } + // Add all guests other than the requesting user's own connections as collaborators + for (guest_conn_id, guest) in &project.guests { + if request.sender_id != *guest_conn_id { + collaborators.push(proto::Collaborator { + peer_id: guest_conn_id.0, + replica_id: guest.replica_id as u32, + user_id: guest.user_id.to_proto(), + }); + } + } + + for conn_id in project.connection_ids() { + if conn_id != request.sender_id { + self.peer.send( + conn_id, + proto::AddProjectCollaborator { + project_id: project_id.to_proto(), + collaborator: Some(proto::Collaborator { + peer_id: request.sender_id.0, + replica_id: replica_id as u32, + user_id: guest_user_id.to_proto(), + }), + }, + )?; + } + } + + // First, we send the metadata associated with each worktree. + response.send(proto::JoinProjectResponse { + worktrees: worktrees.clone(), + replica_id: replica_id as u32, + collaborators: collaborators.clone(), + language_servers: project.language_servers.clone(), + })?; + + for (worktree_id, worktree) in &project.worktrees { + #[cfg(any(test, feature = "test-support"))] + const MAX_CHUNK_SIZE: usize = 2; + #[cfg(not(any(test, feature = "test-support")))] + const MAX_CHUNK_SIZE: usize = 256; + + // Stream this worktree's entries. + let message = proto::UpdateWorktree { + project_id: project_id.to_proto(), + worktree_id: *worktree_id, + root_name: worktree.root_name.clone(), + updated_entries: worktree.entries.values().cloned().collect(), + removed_entries: Default::default(), + scan_id: worktree.scan_id, + is_last_update: worktree.is_complete, + }; + for update in proto::split_worktree_update(message, MAX_CHUNK_SIZE) { + self.peer.send(request.sender_id, update.clone())?; + } + + // Stream this worktree's diagnostics. + for summary in worktree.diagnostic_summaries.values() { + self.peer.send( + request.sender_id, + proto::UpdateDiagnosticSummary { + project_id: project_id.to_proto(), + worktree_id: *worktree_id, + summary: Some(summary.clone()), + }, + )?; } } - self.update_user_contacts(host_user_id).await?; Ok(()) } @@ -1041,27 +929,8 @@ impl Server { ) }); } - - if let Some(requester_id) = project.cancel_request { - self.peer.send( - project.host_connection_id, - proto::JoinProjectRequestCancelled { - project_id: project_id.to_proto(), - requester_id: requester_id.to_proto(), - }, - )?; - } - - if project.unshare { - self.peer.send( - project.host_connection_id, - proto::ProjectUnshared { - project_id: project_id.to_proto(), - }, - )?; - } } - self.update_user_contacts(project.host_user_id).await?; + Ok(()) } @@ -1070,61 +939,18 @@ impl Server { request: TypedEnvelope, ) -> Result<()> { let project_id = ProjectId::from_proto(request.payload.project_id); - let user_id; { let mut state = self.store().await; - user_id = state.user_id_for_connection(request.sender_id)?; let guest_connection_ids = state .read_project(project_id, request.sender_id)? .guest_connection_ids(); - let unshared_project = state.update_project( - project_id, - &request.payload.worktrees, - request.payload.online, - request.sender_id, - )?; - - if let Some(unshared_project) = unshared_project { - broadcast( - request.sender_id, - unshared_project.guests.keys().copied(), - |conn_id| { - self.peer.send( - conn_id, - proto::UnregisterProject { - project_id: project_id.to_proto(), - }, - ) - }, - ); - for (_, receipts) in unshared_project.pending_join_requests { - for receipt in receipts { - self.peer.respond( - receipt, - proto::JoinProjectResponse { - variant: Some(proto::join_project_response::Variant::Decline( - proto::join_project_response::Decline { - reason: - proto::join_project_response::decline::Reason::Closed - as i32, - }, - )), - }, - )?; - } - } - } else { - broadcast(request.sender_id, guest_connection_ids, |connection_id| { - self.peer.forward_send( - request.sender_id, - connection_id, - request.payload.clone(), - ) - }); - } + state.update_project(project_id, &request.payload.worktrees, request.sender_id)?; + broadcast(request.sender_id, guest_connection_ids, |connection_id| { + self.peer + .forward_send(request.sender_id, connection_id, request.payload.clone()) + }); }; - self.update_user_contacts(user_id).await?; Ok(()) } @@ -1146,32 +972,21 @@ impl Server { ) -> Result<()> { let project_id = ProjectId::from_proto(request.payload.project_id); let worktree_id = request.payload.worktree_id; - let (connection_ids, metadata_changed) = { - let mut store = self.store().await; - let (connection_ids, metadata_changed) = store.update_worktree( - request.sender_id, - project_id, - worktree_id, - &request.payload.root_name, - &request.payload.removed_entries, - &request.payload.updated_entries, - request.payload.scan_id, - request.payload.is_last_update, - )?; - (connection_ids, metadata_changed) - }; + let connection_ids = self.store().await.update_worktree( + request.sender_id, + project_id, + worktree_id, + &request.payload.root_name, + &request.payload.removed_entries, + &request.payload.updated_entries, + request.payload.scan_id, + request.payload.is_last_update, + )?; broadcast(request.sender_id, connection_ids, |connection_id| { self.peer .forward_send(request.sender_id, connection_id, request.payload.clone()) }); - if metadata_changed { - let user_id = self - .store() - .await - .user_id_for_connection(request.sender_id)?; - self.update_user_contacts(user_id).await?; - } response.send(proto::Ack {})?; Ok(()) } diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index 54c3a25e27..deb2230147 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -1,7 +1,7 @@ use crate::db::{self, ChannelId, ProjectId, UserId}; use anyhow::{anyhow, Result}; -use collections::{btree_map, hash_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet}; -use rpc::{proto, ConnectionId, Receipt}; +use collections::{btree_map, BTreeMap, BTreeSet, HashMap, HashSet}; +use rpc::{proto, ConnectionId}; use serde::Serialize; use std::{mem, path::PathBuf, str, time::Duration}; use time::OffsetDateTime; @@ -32,7 +32,6 @@ struct ConnectionState { user_id: UserId, admin: bool, projects: BTreeSet, - requested_projects: HashSet, channels: HashSet, } @@ -45,12 +44,9 @@ pub struct Call { #[derive(Serialize)] pub struct Project { - pub online: bool, pub host_connection_id: ConnectionId, pub host: Collaborator, pub guests: HashMap, - #[serde(skip)] - pub join_requests: HashMap>>, pub active_replica_ids: HashSet, pub worktrees: BTreeMap, pub language_servers: Vec, @@ -98,13 +94,10 @@ pub struct LeftProject { pub host_connection_id: ConnectionId, pub connection_ids: Vec, pub remove_collaborator: bool, - pub cancel_request: Option, - pub unshare: bool, } pub struct UnsharedProject { - pub guests: HashMap, - pub pending_join_requests: HashMap>>, + pub guest_connection_ids: Vec, } #[derive(Copy, Clone)] @@ -159,7 +152,6 @@ impl Store { user_id, admin, projects: Default::default(), - requested_projects: Default::default(), channels: Default::default(), }, ); @@ -578,7 +570,6 @@ impl Store { &mut self, host_connection_id: ConnectionId, project_id: ProjectId, - online: bool, ) -> Result<()> { let connection = self .connections @@ -588,7 +579,6 @@ impl Store { self.projects.insert( project_id, Project { - online, host_connection_id, host: Collaborator { user_id: connection.user_id, @@ -597,7 +587,6 @@ impl Store { admin: connection.admin, }, guests: Default::default(), - join_requests: Default::default(), active_replica_ids: Default::default(), worktrees: Default::default(), language_servers: Default::default(), @@ -610,9 +599,8 @@ impl Store { &mut self, project_id: ProjectId, worktrees: &[proto::WorktreeMetadata], - online: bool, connection_id: ConnectionId, - ) -> Result> { + ) -> Result<()> { let project = self .projects .get_mut(&project_id) @@ -634,32 +622,7 @@ impl Store { } } - if online != project.online { - project.online = online; - if project.online { - Ok(None) - } else { - for connection_id in project.guest_connection_ids() { - if let Some(connection) = self.connections.get_mut(&connection_id) { - connection.projects.remove(&project_id); - } - } - - project.active_replica_ids.clear(); - project.language_servers.clear(); - for worktree in project.worktrees.values_mut() { - worktree.diagnostic_summaries.clear(); - worktree.entries.clear(); - } - - Ok(Some(UnsharedProject { - guests: mem::take(&mut project.guests), - pending_join_requests: mem::take(&mut project.join_requests), - })) - } - } else { - Ok(None) - } + Ok(()) } else { Err(anyhow!("no such project"))? } @@ -685,22 +648,6 @@ impl Store { } } - for requester_user_id in project.join_requests.keys() { - if let Some(requester_user_connection_state) = - self.connected_users.get_mut(requester_user_id) - { - for requester_connection_id in - &requester_user_connection_state.connection_ids - { - if let Some(requester_connection) = - self.connections.get_mut(requester_connection_id) - { - requester_connection.requested_projects.remove(&project_id); - } - } - } - } - Ok(project) } else { Err(anyhow!("no such project"))? @@ -710,6 +657,37 @@ impl Store { } } + pub fn unshare_project( + &mut self, + project_id: ProjectId, + connection_id: ConnectionId, + ) -> Result { + let project = self + .projects + .get_mut(&project_id) + .ok_or_else(|| anyhow!("no such project"))?; + anyhow::ensure!( + project.host_connection_id == connection_id, + "no such project" + ); + + let guest_connection_ids = project.guest_connection_ids(); + project.active_replica_ids.clear(); + project.guests.clear(); + project.language_servers.clear(); + project.worktrees.clear(); + + for connection_id in &guest_connection_ids { + if let Some(connection) = self.connections.get_mut(connection_id) { + connection.projects.remove(&project_id); + } + } + + Ok(UnsharedProject { + guest_connection_ids, + }) + } + pub fn update_diagnostic_summary( &mut self, project_id: ProjectId, @@ -753,91 +731,37 @@ impl Store { Err(anyhow!("no such project"))? } - pub fn request_join_project( + pub fn join_project( &mut self, - requester_id: UserId, + requester_connection_id: ConnectionId, project_id: ProjectId, - receipt: Receipt, - ) -> Result<()> { + ) -> Result<(&Project, ReplicaId)> { let connection = self .connections - .get_mut(&receipt.sender_id) + .get_mut(&requester_connection_id) .ok_or_else(|| anyhow!("no such connection"))?; let project = self .projects .get_mut(&project_id) .ok_or_else(|| anyhow!("no such project"))?; - if project.online { - connection.requested_projects.insert(project_id); - project - .join_requests - .entry(requester_id) - .or_default() - .push(receipt); - Ok(()) - } else { - Err(anyhow!("no such project")) - } - } - - pub fn deny_join_project_request( - &mut self, - responder_connection_id: ConnectionId, - requester_id: UserId, - project_id: ProjectId, - ) -> Option>> { - let project = self.projects.get_mut(&project_id)?; - if responder_connection_id != project.host_connection_id { - return None; - } - - let receipts = project.join_requests.remove(&requester_id)?; - for receipt in &receipts { - let requester_connection = self.connections.get_mut(&receipt.sender_id)?; - requester_connection.requested_projects.remove(&project_id); - } - project.host.last_activity = Some(OffsetDateTime::now_utc()); - - Some(receipts) - } - - #[allow(clippy::type_complexity)] - pub fn accept_join_project_request( - &mut self, - responder_connection_id: ConnectionId, - requester_id: UserId, - project_id: ProjectId, - ) -> Option<(Vec<(Receipt, ReplicaId)>, &Project)> { - let project = self.projects.get_mut(&project_id)?; - if responder_connection_id != project.host_connection_id { - return None; - } - - let receipts = project.join_requests.remove(&requester_id)?; - let mut receipts_with_replica_ids = Vec::new(); - for receipt in receipts { - let requester_connection = self.connections.get_mut(&receipt.sender_id)?; - requester_connection.requested_projects.remove(&project_id); - requester_connection.projects.insert(project_id); - let mut replica_id = 1; - while project.active_replica_ids.contains(&replica_id) { - replica_id += 1; - } - project.active_replica_ids.insert(replica_id); - project.guests.insert( - receipt.sender_id, - Collaborator { - replica_id, - user_id: requester_id, - last_activity: Some(OffsetDateTime::now_utc()), - admin: requester_connection.admin, - }, - ); - receipts_with_replica_ids.push((receipt, replica_id)); + connection.projects.insert(project_id); + let mut replica_id = 1; + while project.active_replica_ids.contains(&replica_id) { + replica_id += 1; } + project.active_replica_ids.insert(replica_id); + project.guests.insert( + requester_connection_id, + Collaborator { + replica_id, + user_id: connection.user_id, + last_activity: Some(OffsetDateTime::now_utc()), + admin: connection.admin, + }, + ); project.host.last_activity = Some(OffsetDateTime::now_utc()); - Some((receipts_with_replica_ids, project)) + Ok((project, replica_id)) } pub fn leave_project( @@ -845,7 +769,6 @@ impl Store { connection_id: ConnectionId, project_id: ProjectId, ) -> Result { - let user_id = self.user_id_for_connection(connection_id)?; let project = self .projects .get_mut(&project_id) @@ -859,39 +782,14 @@ impl Store { false }; - // If the connection leaving the project has a pending request, remove it. - // If that user has no other pending requests on other connections, indicate that the request should be cancelled. - let mut cancel_request = None; - if let Entry::Occupied(mut entry) = project.join_requests.entry(user_id) { - entry - .get_mut() - .retain(|receipt| receipt.sender_id != connection_id); - if entry.get().is_empty() { - entry.remove(); - cancel_request = Some(user_id); - } - } - if let Some(connection) = self.connections.get_mut(&connection_id) { connection.projects.remove(&project_id); } - let connection_ids = project.connection_ids(); - let unshare = connection_ids.len() <= 1 && project.join_requests.is_empty(); - if unshare { - project.language_servers.clear(); - for worktree in project.worktrees.values_mut() { - worktree.diagnostic_summaries.clear(); - worktree.entries.clear(); - } - } - Ok(LeftProject { host_connection_id: project.host_connection_id, host_user_id: project.host.user_id, - connection_ids, - cancel_request, - unshare, + connection_ids: project.connection_ids(), remove_collaborator, }) } @@ -907,15 +805,11 @@ impl Store { updated_entries: &[proto::Entry], scan_id: u64, is_last_update: bool, - ) -> Result<(Vec, bool)> { + ) -> Result> { let project = self.write_project(project_id, connection_id)?; - if !project.online { - return Err(anyhow!("project is not online")); - } let connection_ids = project.connection_ids(); let mut worktree = project.worktrees.entry(worktree_id).or_default(); - let metadata_changed = worktree_root_name != worktree.root_name; worktree.root_name = worktree_root_name.to_string(); for entry_id in removed_entries { @@ -928,7 +822,7 @@ impl Store { worktree.scan_id = scan_id; worktree.is_complete = is_last_update; - Ok((connection_ids, metadata_changed)) + Ok(connection_ids) } pub fn project_connection_ids( diff --git a/crates/contacts_panel/src/contacts_panel.rs b/crates/contacts_panel/src/contacts_panel.rs index a0259ab8c5..eb1afc3810 100644 --- a/crates/contacts_panel/src/contacts_panel.rs +++ b/crates/contacts_panel/src/contacts_panel.rs @@ -1,6 +1,5 @@ mod contact_finder; mod contact_notification; -mod join_project_notification; mod notifications; use client::{Contact, ContactEventKind, User, UserStore}; @@ -13,9 +12,7 @@ use gpui::{ MouseButton, MutableAppContext, RenderContext, Subscription, View, ViewContext, ViewHandle, WeakViewHandle, }; -use join_project_notification::JoinProjectNotification; use menu::{Confirm, SelectNext, SelectPrev}; -use project::ProjectStore; use serde::Deserialize; use settings::Settings; use std::sync::Arc; @@ -54,7 +51,6 @@ pub struct ContactsPanel { match_candidates: Vec, list_state: ListState, user_store: ModelHandle, - project_store: ModelHandle, filter_editor: ViewHandle, collapsed_sections: Vec
, selection: Option, @@ -76,7 +72,6 @@ pub struct RespondToContactRequest { pub fn init(cx: &mut MutableAppContext) { contact_finder::init(cx); contact_notification::init(cx); - join_project_notification::init(cx); cx.add_action(ContactsPanel::request_contact); cx.add_action(ContactsPanel::remove_contact); cx.add_action(ContactsPanel::respond_to_contact_request); @@ -90,7 +85,6 @@ pub fn init(cx: &mut MutableAppContext) { impl ContactsPanel { pub fn new( user_store: ModelHandle, - project_store: ModelHandle, workspace: WeakViewHandle, cx: &mut ViewContext, ) -> Self { @@ -120,38 +114,6 @@ impl ContactsPanel { }) .detach(); - cx.defer({ - let workspace = workspace.clone(); - move |_, cx| { - if let Some(workspace_handle) = workspace.upgrade(cx) { - cx.subscribe(&workspace_handle.read(cx).project().clone(), { - let workspace = workspace; - move |_, project, event, cx| { - if let project::Event::ContactRequestedJoin(user) = event { - if let Some(workspace) = workspace.upgrade(cx) { - workspace.update(cx, |workspace, cx| { - workspace.show_notification(user.id as usize, cx, |cx| { - cx.add_view(|cx| { - JoinProjectNotification::new( - project, - user.clone(), - cx, - ) - }) - }) - }); - } - } - } - }) - .detach(); - } - } - }); - - cx.observe(&project_store, |this, _, cx| this.update_entries(cx)) - .detach(); - cx.subscribe(&user_store, move |_, user_store, event, cx| { if let Some(workspace) = workspace.upgrade(cx) { workspace.update(cx, |workspace, cx| { @@ -219,7 +181,6 @@ impl ContactsPanel { filter_editor, _maintain_contacts: cx.observe(&user_store, |this, _, cx| this.update_entries(cx)), user_store, - project_store, }; this.update_entries(cx); this @@ -841,7 +802,7 @@ mod tests { use collections::HashSet; use gpui::TestAppContext; use language::LanguageRegistry; - use project::{FakeFs, Project}; + use project::{FakeFs, Project, ProjectStore}; #[gpui::test] async fn test_contact_panel(cx: &mut TestAppContext) { @@ -852,12 +813,11 @@ mod tests { let http_client = FakeHttpClient::with_404_response(); let client = Client::new(http_client.clone()); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx)); - let project_store = cx.add_model(|_| ProjectStore::new(project::Db::open_fake())); + let project_store = cx.add_model(|_| ProjectStore::new()); let server = FakeServer::for_client(current_user_id, &client, cx).await; let fs = FakeFs::new(cx.background()); let project = cx.update(|cx| { Project::local( - false, client.clone(), user_store.clone(), project_store.clone(), @@ -870,12 +830,7 @@ mod tests { let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx)); let panel = cx.add_view(&workspace, |cx| { - ContactsPanel::new( - user_store.clone(), - project_store.clone(), - workspace.downgrade(), - cx, - ) + ContactsPanel::new(user_store.clone(), workspace.downgrade(), cx) }); workspace.update(cx, |_, cx| { @@ -890,6 +845,14 @@ mod tests { .detach(); }); + let request = server.receive::().await.unwrap(); + server + .respond( + request.receipt(), + proto::RegisterProjectResponse { project_id: 200 }, + ) + .await; + let get_users_request = server.receive::().await.unwrap(); server .respond( @@ -920,14 +883,6 @@ mod tests { ) .await; - let request = server.receive::().await.unwrap(); - server - .respond( - request.receipt(), - proto::RegisterProjectResponse { project_id: 200 }, - ) - .await; - server.send(proto::UpdateContacts { incoming_requests: vec![proto::IncomingContactRequest { requester_id: 1, diff --git a/crates/contacts_panel/src/join_project_notification.rs b/crates/contacts_panel/src/join_project_notification.rs deleted file mode 100644 index d8e8e670cf..0000000000 --- a/crates/contacts_panel/src/join_project_notification.rs +++ /dev/null @@ -1,80 +0,0 @@ -use client::User; -use gpui::{ - actions, ElementBox, Entity, ModelHandle, MutableAppContext, RenderContext, View, ViewContext, -}; -use project::Project; -use std::sync::Arc; -use workspace::Notification; - -use crate::notifications::render_user_notification; - -pub fn init(cx: &mut MutableAppContext) { - cx.add_action(JoinProjectNotification::decline); - cx.add_action(JoinProjectNotification::accept); -} - -pub enum Event { - Dismiss, -} - -actions!(contacts_panel, [Accept, Decline]); - -pub struct JoinProjectNotification { - project: ModelHandle, - user: Arc, -} - -impl JoinProjectNotification { - pub fn new(project: ModelHandle, user: Arc, cx: &mut ViewContext) -> Self { - cx.subscribe(&project, |this, _, event, cx| { - if let project::Event::ContactCancelledJoinRequest(user) = event { - if *user == this.user { - cx.emit(Event::Dismiss); - } - } - }) - .detach(); - Self { project, user } - } - - fn decline(&mut self, _: &Decline, cx: &mut ViewContext) { - self.project.update(cx, |project, cx| { - project.respond_to_join_request(self.user.id, false, cx) - }); - cx.emit(Event::Dismiss) - } - - fn accept(&mut self, _: &Accept, cx: &mut ViewContext) { - self.project.update(cx, |project, cx| { - project.respond_to_join_request(self.user.id, true, cx) - }); - cx.emit(Event::Dismiss) - } -} - -impl Entity for JoinProjectNotification { - type Event = Event; -} - -impl View for JoinProjectNotification { - fn ui_name() -> &'static str { - "JoinProjectNotification" - } - - fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - render_user_notification( - self.user.clone(), - "wants to join your project", - None, - Decline, - vec![("Decline", Box::new(Decline)), ("Accept", Box::new(Accept))], - cx, - ) - } -} - -impl Notification for JoinProjectNotification { - fn should_dismiss_notification_on_event(&self, event: &::Event) -> bool { - matches!(event, Event::Dismiss) - } -} diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index f6c20ff837..903e103d41 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -74,7 +74,6 @@ pub trait Item: Entity { } pub struct ProjectStore { - db: Arc, projects: Vec>, } @@ -126,7 +125,6 @@ pub struct Project { incomplete_buffers: HashMap>, buffer_snapshots: HashMap>, nonce: u128, - initialized_persistent_state: bool, _maintain_buffer_languages: Task<()>, } @@ -158,10 +156,7 @@ enum ProjectClientState { is_shared: bool, remote_id_tx: watch::Sender>, remote_id_rx: watch::Receiver>, - online_tx: watch::Sender, - online_rx: watch::Receiver, _maintain_remote_id: Task>, - _maintain_online_status: Task>, }, Remote { sharing_has_stopped: bool, @@ -196,8 +191,6 @@ pub enum Event { RemoteIdChanged(Option), DisconnectedFromHost, CollaboratorLeft(PeerId), - ContactRequestedJoin(Arc), - ContactCancelledJoinRequest(Arc), } pub enum LanguageServerState { @@ -382,17 +375,15 @@ impl FormatTrigger { impl Project { pub fn init(client: &Arc) { - client.add_model_message_handler(Self::handle_request_join_project); client.add_model_message_handler(Self::handle_add_collaborator); client.add_model_message_handler(Self::handle_buffer_reloaded); client.add_model_message_handler(Self::handle_buffer_saved); client.add_model_message_handler(Self::handle_start_language_server); client.add_model_message_handler(Self::handle_update_language_server); client.add_model_message_handler(Self::handle_remove_collaborator); - client.add_model_message_handler(Self::handle_join_project_request_cancelled); client.add_model_message_handler(Self::handle_update_project); client.add_model_message_handler(Self::handle_unregister_project); - client.add_model_message_handler(Self::handle_project_unshared); + client.add_model_message_handler(Self::handle_unshare_project); client.add_model_message_handler(Self::handle_create_buffer_for_peer); client.add_model_message_handler(Self::handle_update_buffer_file); client.add_model_message_handler(Self::handle_update_buffer); @@ -424,7 +415,6 @@ impl Project { } pub fn local( - online: bool, client: Arc, user_store: ModelHandle, project_store: ModelHandle, @@ -453,23 +443,6 @@ impl Project { } }); - let (online_tx, online_rx) = watch::channel_with(online); - let _maintain_online_status = cx.spawn_weak({ - let mut online_rx = online_rx.clone(); - move |this, mut cx| async move { - while let Some(online) = online_rx.recv().await { - let this = this.upgrade(&cx)?; - this.update(&mut cx, |this, cx| { - if !online { - this.unshared(cx); - } - this.metadata_changed(false, cx) - }); - } - None - } - }); - let handle = cx.weak_handle(); project_store.update(cx, |store, cx| store.add_project(handle, cx)); @@ -486,10 +459,7 @@ impl Project { is_shared: false, remote_id_tx, remote_id_rx, - online_tx, - online_rx, _maintain_remote_id, - _maintain_online_status, }, opened_buffer: watch::channel(), client_subscriptions: Vec::new(), @@ -510,7 +480,6 @@ impl Project { language_server_settings: Default::default(), next_language_server_id: 0, nonce: StdRng::from_entropy().gen(), - initialized_persistent_state: false, } }) } @@ -532,24 +501,6 @@ impl Project { }) .await?; - let response = match response.variant.ok_or_else(|| anyhow!("missing variant"))? { - proto::join_project_response::Variant::Accept(response) => response, - proto::join_project_response::Variant::Decline(decline) => { - match proto::join_project_response::decline::Reason::from_i32(decline.reason) { - Some(proto::join_project_response::decline::Reason::Declined) => { - Err(JoinProjectError::HostDeclined)? - } - Some(proto::join_project_response::decline::Reason::Closed) => { - Err(JoinProjectError::HostClosedProject)? - } - Some(proto::join_project_response::decline::Reason::WentOffline) => { - Err(JoinProjectError::HostWentOffline)? - } - None => Err(anyhow!("missing decline reason"))?, - } - } - }; - let replica_id = response.replica_id as ReplicaId; let mut worktrees = Vec::new(); @@ -625,7 +576,6 @@ impl Project { opened_buffers: Default::default(), buffer_snapshots: Default::default(), nonce: StdRng::from_entropy().gen(), - initialized_persistent_state: false, }; for worktree in worktrees { this.add_worktree(&worktree, cx); @@ -668,10 +618,9 @@ impl Project { let http_client = client::test::FakeHttpClient::with_404_response(); let client = client::Client::new(http_client.clone()); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx)); - let project_store = cx.add_model(|_| ProjectStore::new(Db::open_fake())); - let project = cx.update(|cx| { - Project::local(true, client, user_store, project_store, languages, fs, cx) - }); + let project_store = cx.add_model(|_| ProjectStore::new()); + let project = + cx.update(|cx| Project::local(client, user_store, project_store, languages, fs, cx)); for path in root_paths { let (tree, _) = project .update(cx, |project, cx| { @@ -685,53 +634,6 @@ impl Project { project } - pub fn restore_state(&mut self, cx: &mut ModelContext) -> Task> { - if self.is_remote() { - return Task::ready(Ok(())); - } - - let db = self.project_store.read(cx).db.clone(); - let keys = self.db_keys_for_online_state(cx); - let online_by_default = cx.global::().projects_online_by_default; - let read_online = cx.background().spawn(async move { - let values = db.read(keys)?; - anyhow::Ok( - values - .into_iter() - .all(|e| e.map_or(online_by_default, |e| e == [true as u8])), - ) - }); - cx.spawn(|this, mut cx| async move { - let online = read_online.await.log_err().unwrap_or(false); - this.update(&mut cx, |this, cx| { - this.initialized_persistent_state = true; - if let ProjectClientState::Local { online_tx, .. } = &mut this.client_state { - let mut online_tx = online_tx.borrow_mut(); - if *online_tx != online { - *online_tx = online; - drop(online_tx); - this.metadata_changed(false, cx); - } - } - }); - Ok(()) - }) - } - - fn persist_state(&mut self, cx: &mut ModelContext) -> Task> { - if self.is_remote() || !self.initialized_persistent_state { - return Task::ready(Ok(())); - } - - let db = self.project_store.read(cx).db.clone(); - let keys = self.db_keys_for_online_state(cx); - let is_online = self.is_online(); - cx.background().spawn(async move { - let value = &[is_online as u8]; - db.write(keys.into_iter().map(|key| (key, value))) - }) - } - fn on_settings_changed(&mut self, cx: &mut ModelContext) { let settings = cx.global::(); @@ -860,24 +762,8 @@ impl Project { &self.fs } - pub fn set_online(&mut self, online: bool, _: &mut ModelContext) { - if let ProjectClientState::Local { online_tx, .. } = &mut self.client_state { - let mut online_tx = online_tx.borrow_mut(); - if *online_tx != online { - *online_tx = online; - } - } - } - - pub fn is_online(&self) -> bool { - match &self.client_state { - ProjectClientState::Local { online_rx, .. } => *online_rx.borrow(), - ProjectClientState::Remote { .. } => true, - } - } - fn unregister(&mut self, cx: &mut ModelContext) -> Task> { - self.unshared(cx); + self.unshare(cx).log_err(); if let ProjectClientState::Local { remote_id_rx, .. } = &mut self.client_state { if let Some(remote_id) = *remote_id_rx.borrow() { let request = self.client.request(proto::UnregisterProject { @@ -905,7 +791,7 @@ impl Project { *remote_id_tx.borrow_mut() = None; } this.client_subscriptions.clear(); - this.metadata_changed(false, cx); + this.metadata_changed(cx); }); response.map(drop) }); @@ -915,19 +801,12 @@ impl Project { } fn register(&mut self, cx: &mut ModelContext) -> Task> { - if let ProjectClientState::Local { - remote_id_rx, - online_rx, - .. - } = &self.client_state - { + if let ProjectClientState::Local { remote_id_rx, .. } = &self.client_state { if remote_id_rx.borrow().is_some() { return Task::ready(Ok(())); } - let response = self.client.request(proto::RegisterProject { - online: *online_rx.borrow(), - }); + let response = self.client.request(proto::RegisterProject {}); cx.spawn(|this, mut cx| async move { let remote_id = response.await?.project_id; this.update(&mut cx, |this, cx| { @@ -935,7 +814,7 @@ impl Project { *remote_id_tx.borrow_mut() = Some(remote_id); } - this.metadata_changed(false, cx); + this.metadata_changed(cx); cx.emit(Event::RemoteIdChanged(Some(remote_id))); this.client_subscriptions .push(this.client.add_model_for_remote_entity(remote_id, cx)); @@ -1001,65 +880,50 @@ impl Project { } } - fn metadata_changed(&mut self, persist: bool, cx: &mut ModelContext) { - if let ProjectClientState::Local { - remote_id_rx, - online_rx, - .. - } = &self.client_state - { + fn metadata_changed(&mut self, cx: &mut ModelContext) { + if let ProjectClientState::Local { remote_id_rx, .. } = &self.client_state { // Broadcast worktrees only if the project is online. - let worktrees = if *online_rx.borrow() { - self.worktrees - .iter() - .filter_map(|worktree| { - worktree - .upgrade(cx) - .map(|worktree| worktree.read(cx).as_local().unwrap().metadata_proto()) - }) - .collect() - } else { - Default::default() - }; + let worktrees = self + .worktrees + .iter() + .filter_map(|worktree| { + worktree + .upgrade(cx) + .map(|worktree| worktree.read(cx).as_local().unwrap().metadata_proto()) + }) + .collect(); if let Some(project_id) = *remote_id_rx.borrow() { - let online = *online_rx.borrow(); self.client .send(proto::UpdateProject { project_id, worktrees, - online, }) .log_err(); - if online { - let worktrees = self.visible_worktrees(cx).collect::>(); - let scans_complete = - futures::future::join_all(worktrees.iter().filter_map(|worktree| { - Some(worktree.read(cx).as_local()?.scan_complete()) - })); + let worktrees = self.visible_worktrees(cx).collect::>(); + let scans_complete = + futures::future::join_all(worktrees.iter().filter_map(|worktree| { + Some(worktree.read(cx).as_local()?.scan_complete()) + })); - let worktrees = worktrees.into_iter().map(|handle| handle.downgrade()); - cx.spawn_weak(move |_, cx| async move { - scans_complete.await; - cx.read(|cx| { - for worktree in worktrees { - if let Some(worktree) = worktree - .upgrade(cx) - .and_then(|worktree| worktree.read(cx).as_local()) - { - worktree.send_extension_counts(project_id); - } + let worktrees = worktrees.into_iter().map(|handle| handle.downgrade()); + cx.spawn_weak(move |_, cx| async move { + scans_complete.await; + cx.read(|cx| { + for worktree in worktrees { + if let Some(worktree) = worktree + .upgrade(cx) + .and_then(|worktree| worktree.read(cx).as_local()) + { + worktree.send_extension_counts(project_id); } - }) + } }) - .detach(); - } + }) + .detach(); } self.project_store.update(cx, |_, cx| cx.notify()); - if persist { - self.persist_state(cx).detach_and_log_err(cx); - } cx.notify(); } } @@ -1097,23 +961,6 @@ impl Project { .map(|tree| tree.read(cx).root_name()) } - fn db_keys_for_online_state(&self, cx: &AppContext) -> Vec { - self.worktrees - .iter() - .filter_map(|worktree| { - let worktree = worktree.upgrade(cx)?.read(cx); - if worktree.is_visible() { - Some(format!( - "project-path-online:{}", - worktree.as_local().unwrap().abs_path().to_string_lossy() - )) - } else { - None - } - }) - .collect::>() - } - pub fn worktree_for_id( &self, id: WorktreeId, @@ -1317,11 +1164,7 @@ impl Project { } } - fn share(&mut self, cx: &mut ModelContext) -> Task> { - if !self.is_online() { - return Task::ready(Err(anyhow!("can't share an offline project"))); - } - + pub fn share(&mut self, cx: &mut ModelContext) -> Task> { let project_id; if let ProjectClientState::Local { remote_id_rx, @@ -1394,10 +1237,15 @@ impl Project { }) } - fn unshared(&mut self, cx: &mut ModelContext) { - if let ProjectClientState::Local { is_shared, .. } = &mut self.client_state { + pub fn unshare(&mut self, cx: &mut ModelContext) -> Result<()> { + if let ProjectClientState::Local { + is_shared, + remote_id_rx, + .. + } = &mut self.client_state + { if !*is_shared { - return; + return Ok(()); } *is_shared = false; @@ -1422,37 +1270,13 @@ impl Project { } cx.notify(); - } else { - log::error!("attempted to unshare a remote project"); - } - } + if let Some(project_id) = *remote_id_rx.borrow() { + self.client.send(proto::UnshareProject { project_id })?; + } - pub fn respond_to_join_request( - &mut self, - requester_id: u64, - allow: bool, - cx: &mut ModelContext, - ) { - if let Some(project_id) = self.remote_id() { - let share = if self.is_online() && allow { - Some(self.share(cx)) - } else { - None - }; - let client = self.client.clone(); - cx.foreground() - .spawn(async move { - client.send(proto::RespondToJoinProjectRequest { - requester_id, - project_id, - allow, - })?; - if let Some(share) = share { - share.await?; - } - anyhow::Ok(()) - }) - .detach_and_log_err(cx); + Ok(()) + } else { + Err(anyhow!("attempted to unshare a remote project")) } } @@ -4527,7 +4351,7 @@ impl Project { false } }); - self.metadata_changed(true, cx); + self.metadata_changed(cx); cx.notify(); } @@ -4552,7 +4376,7 @@ impl Project { .push(WorktreeHandle::Weak(worktree.downgrade())); } - self.metadata_changed(true, cx); + self.metadata_changed(cx); cx.observe_release(worktree, |this, worktree, cx| { this.remove_worktree(worktree.id(), cx); cx.notify(); @@ -4728,29 +4552,6 @@ impl Project { // RPC message handlers - async fn handle_request_join_project( - this: ModelHandle, - message: TypedEnvelope, - _: Arc, - mut cx: AsyncAppContext, - ) -> Result<()> { - let user_id = message.payload.requester_id; - if this.read_with(&cx, |project, _| { - project.collaborators.values().any(|c| c.user.id == user_id) - }) { - this.update(&mut cx, |this, cx| { - this.respond_to_join_request(user_id, true, cx) - }); - } else { - let user_store = this.read_with(&cx, |this, _| this.user_store.clone()); - let user = user_store - .update(&mut cx, |store, cx| store.get_user(user_id, cx)) - .await?; - this.update(&mut cx, |_, cx| cx.emit(Event::ContactRequestedJoin(user))); - } - Ok(()) - } - async fn handle_unregister_project( this: ModelHandle, _: TypedEnvelope, @@ -4761,13 +4562,13 @@ impl Project { Ok(()) } - async fn handle_project_unshared( + async fn handle_unshare_project( this: ModelHandle, - _: TypedEnvelope, + _: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, ) -> Result<()> { - this.update(&mut cx, |this, cx| this.unshared(cx)); + this.update(&mut cx, |this, cx| this.disconnected_from_host(cx)); Ok(()) } @@ -4819,27 +4620,6 @@ impl Project { }) } - async fn handle_join_project_request_cancelled( - this: ModelHandle, - envelope: TypedEnvelope, - _: Arc, - mut cx: AsyncAppContext, - ) -> Result<()> { - let user = this - .update(&mut cx, |this, cx| { - this.user_store.update(cx, |user_store, cx| { - user_store.get_user(envelope.payload.requester_id, cx) - }) - }) - .await?; - - this.update(&mut cx, |_, cx| { - cx.emit(Event::ContactCancelledJoinRequest(user)); - }); - - Ok(()) - } - async fn handle_update_project( this: ModelHandle, envelope: TypedEnvelope, @@ -4871,7 +4651,7 @@ impl Project { } } - this.metadata_changed(true, cx); + this.metadata_changed(cx); for (id, _) in old_worktrees_by_id { cx.emit(Event::WorktreeRemoved(id)); } @@ -6077,9 +5857,8 @@ impl Project { } impl ProjectStore { - pub fn new(db: Arc) -> Self { + pub fn new() -> Self { Self { - db, projects: Default::default(), } } diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 751c41b209..2659ddb86d 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -25,15 +25,12 @@ message Envelope { RegisterProject register_project = 15; RegisterProjectResponse register_project_response = 16; UnregisterProject unregister_project = 17; - RequestJoinProject request_join_project = 18; - RespondToJoinProjectRequest respond_to_join_project_request = 19; - JoinProjectRequestCancelled join_project_request_cancelled = 20; JoinProject join_project = 21; JoinProjectResponse join_project_response = 22; LeaveProject leave_project = 23; AddProjectCollaborator add_project_collaborator = 24; RemoveProjectCollaborator remove_project_collaborator = 25; - ProjectUnshared project_unshared = 26; + UnshareProject unshare_project = 26; GetDefinition get_definition = 27; GetDefinitionResponse get_definition_response = 28; @@ -198,9 +195,7 @@ message RoomUpdated { Room room = 1; } -message RegisterProject { - bool online = 1; -} +message RegisterProject {} message RegisterProjectResponse { uint64 project_id = 1; @@ -213,55 +208,21 @@ message UnregisterProject { message UpdateProject { uint64 project_id = 1; repeated WorktreeMetadata worktrees = 2; - bool online = 3; } message RegisterProjectActivity { uint64 project_id = 1; } -message RequestJoinProject { - uint64 requester_id = 1; - uint64 project_id = 2; -} - -message RespondToJoinProjectRequest { - uint64 requester_id = 1; - uint64 project_id = 2; - bool allow = 3; -} - -message JoinProjectRequestCancelled { - uint64 requester_id = 1; - uint64 project_id = 2; -} - message JoinProject { uint64 project_id = 1; } message JoinProjectResponse { - oneof variant { - Accept accept = 1; - Decline decline = 2; - } - - message Accept { - uint32 replica_id = 1; - repeated WorktreeMetadata worktrees = 2; - repeated Collaborator collaborators = 3; - repeated LanguageServer language_servers = 4; - } - - message Decline { - Reason reason = 1; - - enum Reason { - Declined = 0; - Closed = 1; - WentOffline = 2; - } - } + uint32 replica_id = 1; + repeated WorktreeMetadata worktrees = 2; + repeated Collaborator collaborators = 3; + repeated LanguageServer language_servers = 4; } message LeaveProject { @@ -324,7 +285,7 @@ message RemoveProjectCollaborator { uint32 peer_id = 2; } -message ProjectUnshared { +message UnshareProject { uint64 project_id = 1; } diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 814983938c..c2d2d2b321 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -126,7 +126,6 @@ messages!( (JoinChannelResponse, Foreground), (JoinProject, Foreground), (JoinProjectResponse, Foreground), - (JoinProjectRequestCancelled, Foreground), (JoinRoom, Foreground), (JoinRoomResponse, Foreground), (LeaveChannel, Foreground), @@ -142,7 +141,6 @@ messages!( (PrepareRename, Background), (PrepareRenameResponse, Background), (ProjectEntryResponse, Foreground), - (ProjectUnshared, Foreground), (RegisterProjectResponse, Foreground), (RemoveContact, Foreground), (Ping, Foreground), @@ -153,9 +151,7 @@ messages!( (RemoveProjectCollaborator, Foreground), (RenameProjectEntry, Foreground), (RequestContact, Foreground), - (RequestJoinProject, Foreground), (RespondToContactRequest, Foreground), - (RespondToJoinProjectRequest, Foreground), (RoomUpdated, Foreground), (SaveBuffer, Foreground), (SearchProject, Background), @@ -167,6 +163,7 @@ messages!( (Test, Foreground), (Unfollow, Foreground), (UnregisterProject, Foreground), + (UnshareProject, Foreground), (UpdateBuffer, Foreground), (UpdateBufferFile, Foreground), (UpdateContacts, Foreground), @@ -252,24 +249,22 @@ entity_messages!( GetReferences, GetProjectSymbols, JoinProject, - JoinProjectRequestCancelled, LeaveProject, OpenBufferById, OpenBufferByPath, OpenBufferForSymbol, PerformRename, PrepareRename, - ProjectUnshared, RegisterProjectActivity, ReloadBuffers, RemoveProjectCollaborator, RenameProjectEntry, - RequestJoinProject, SaveBuffer, SearchProject, StartLanguageServer, Unfollow, UnregisterProject, + UnshareProject, UpdateBuffer, UpdateBufferFile, UpdateDiagnosticSummary, diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 90f01a3a5f..7aa93f47d9 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -107,12 +107,6 @@ pub struct OpenPaths { pub paths: Vec, } -#[derive(Clone, Deserialize, PartialEq)] -pub struct ToggleProjectOnline { - #[serde(skip_deserializing)] - pub project: Option>, -} - #[derive(Clone, Deserialize, PartialEq)] pub struct ActivatePane(pub usize); @@ -134,7 +128,7 @@ impl_internal_actions!( RemoveWorktreeFromProject ] ); -impl_actions!(workspace, [ToggleProjectOnline, ActivatePane]); +impl_actions!(workspace, [ActivatePane]); pub fn init(app_state: Arc, cx: &mut MutableAppContext) { pane::init(cx); @@ -172,7 +166,6 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { cx.add_async_action(Workspace::save_all); cx.add_action(Workspace::add_folder_to_project); cx.add_action(Workspace::remove_folder_from_project); - cx.add_action(Workspace::toggle_project_online); cx.add_action( |workspace: &mut Workspace, _: &Unfollow, cx: &mut ViewContext| { let pane = workspace.active_pane().clone(); @@ -840,7 +833,7 @@ impl AppState { let languages = Arc::new(LanguageRegistry::test()); let http_client = client::test::FakeHttpClient::with_404_response(); let client = Client::new(http_client.clone()); - let project_store = cx.add_model(|_| ProjectStore::new(project::Db::open_fake())); + let project_store = cx.add_model(|_| ProjectStore::new()); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx)); let themes = ThemeRegistry::new((), cx.font_cache().clone()); Arc::new(Self { @@ -1086,7 +1079,6 @@ impl Workspace { let (_, workspace) = cx.add_window((app_state.build_window_options)(), |cx| { let mut workspace = Workspace::new( Project::local( - false, app_state.client.clone(), app_state.user_store.clone(), app_state.project_store.clone(), @@ -1291,17 +1283,6 @@ impl Workspace { .update(cx, |project, cx| project.remove_worktree(*worktree_id, cx)); } - fn toggle_project_online(&mut self, action: &ToggleProjectOnline, cx: &mut ViewContext) { - let project = action - .project - .clone() - .unwrap_or_else(|| self.project.clone()); - project.update(cx, |project, cx| { - let public = !project.is_online(); - project.set_online(public, cx); - }); - } - fn project_path_for_path( &self, abs_path: &Path, @@ -2617,7 +2598,6 @@ pub fn open_paths( cx.add_window((app_state.build_window_options)(), |cx| { let project = Project::local( - false, app_state.client.clone(), app_state.user_store.clone(), app_state.project_store.clone(), @@ -2642,13 +2622,6 @@ pub fn open_paths( }) .await; - if let Some(project) = new_project { - project - .update(&mut cx, |project, cx| project.restore_state(cx)) - .await - .log_err(); - } - (workspace, items) }) } @@ -2657,7 +2630,6 @@ fn open_new(app_state: &Arc, cx: &mut MutableAppContext) { let (window_id, workspace) = cx.add_window((app_state.build_window_options)(), |cx| { let mut workspace = Workspace::new( Project::local( - false, app_state.client.clone(), app_state.user_store.clone(), app_state.project_store.clone(), diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index de769a6e5e..ea42c61dfb 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -140,7 +140,7 @@ fn main() { }) .detach(); - let project_store = cx.add_model(|_| ProjectStore::new(db.clone())); + let project_store = cx.add_model(|_| ProjectStore::new()); let app_state = Arc::new(AppState { languages, themes, diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index fcb6f8f74e..d41b9284c4 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -286,12 +286,7 @@ pub fn initialize_workspace( let project_panel = ProjectPanel::new(workspace.project().clone(), cx); let contact_panel = cx.add_view(|cx| { - ContactsPanel::new( - app_state.user_store.clone(), - app_state.project_store.clone(), - workspace.weak_handle(), - cx, - ) + ContactsPanel::new(app_state.user_store.clone(), workspace.weak_handle(), cx) }); workspace.left_sidebar().update(cx, |sidebar, cx| { From 074b8f18d1922bdecf89afdef7934b44f2370395 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 30 Sep 2022 12:23:57 +0200 Subject: [PATCH 095/314] Rip out project registration and use sharing/unsharing instead --- crates/collab/src/integration_tests.rs | 306 +++++++++-------- crates/collab/src/rpc.rs | 60 +--- crates/collab/src/rpc/store.rs | 99 ++---- crates/contacts_panel/src/contacts_panel.rs | 8 - crates/project/src/project.rs | 359 ++++++-------------- crates/rpc/proto/zed.proto | 17 +- crates/rpc/src/proto.rs | 9 +- 7 files changed, 333 insertions(+), 525 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index ebdc952a0f..8753ddee35 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -314,11 +314,14 @@ async fn test_share_project( .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; - let project_id = project_a.read_with(cx_a, |project, _| project.remote_id().unwrap()); + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); // Join that project as client B let client_b_peer_id = client_b.peer_id; - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_b = client_b.build_remote_project(project_id, cx_b).await; let replica_id_b = project_b.read_with(cx_b, |project, _| { assert_eq!( project @@ -390,17 +393,7 @@ async fn test_share_project( // Client B can join again on a different window because they are already a participant. let client_b2 = server.create_client(cx_b2, "user_b").await; - let project_b2 = Project::remote( - project_id, - client_b2.client.clone(), - client_b2.user_store.clone(), - client_b2.project_store.clone(), - client_b2.language_registry.clone(), - FakeFs::new(cx_b2.background()), - cx_b2.to_async(), - ) - .await - .unwrap(); + let project_b2 = client_b2.build_remote_project(project_id, cx_b2).await; deterministic.run_until_parked(); project_a.read_with(cx_a, |project, _| { assert_eq!(project.collaborators().len(), 2); @@ -449,8 +442,12 @@ async fn test_unshare_project( .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap()); - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_b = client_b.build_remote_project(project_id, cx_b).await; assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared())); project_b @@ -467,7 +464,11 @@ async fn test_unshare_project( assert!(project_b.read_with(cx_b, |project, _| project.is_read_only())); // Client B can join again after client A re-shares. - let project_b2 = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); + let project_b2 = client_b.build_remote_project(project_id, cx_b).await; assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared())); project_b2 .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)) @@ -517,9 +518,12 @@ async fn test_host_disconnect( let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap()); - project_a.read_with(cx_a, |project, _| project.remote_id().unwrap()); + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_b = client_b.build_remote_project(project_id, cx_b).await; assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared())); let (_, workspace_b) = @@ -574,7 +578,11 @@ async fn test_host_disconnect( }); // Ensure guests can still join. - let project_b2 = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); + let project_b2 = client_b.build_remote_project(project_id, cx_b).await; assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared())); project_b2 .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)) @@ -613,10 +621,14 @@ async fn test_propagate_saves_and_fs_changes( .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; let worktree_a = project_a.read_with(cx_a, |p, cx| p.worktrees(cx).next().unwrap()); + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); // Join that worktree as clients B and C. - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; - let project_c = client_c.build_remote_project(&project_a, cx_a, cx_c).await; + let project_b = client_b.build_remote_project(project_id, cx_b).await; + let project_c = client_c.build_remote_project(project_id, cx_c).await; let worktree_b = project_b.read_with(cx_b, |p, cx| p.worktrees(cx).next().unwrap()); let worktree_c = project_c.read_with(cx_c, |p, cx| p.worktrees(cx).next().unwrap()); @@ -756,7 +768,11 @@ async fn test_fs_operations( ) .await; let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); + let project_b = client_b.build_remote_project(project_id, cx_b).await; let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap()); let worktree_b = project_b.read_with(cx_b, |project, cx| project.worktrees(cx).next().unwrap()); @@ -1016,7 +1032,11 @@ async fn test_buffer_conflict_after_save(cx_a: &mut TestAppContext, cx_b: &mut T ) .await; let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); + let project_b = client_b.build_remote_project(project_id, cx_b).await; // Open a buffer as client B let buffer_b = project_b @@ -1065,7 +1085,11 @@ async fn test_buffer_reloading(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont ) .await; let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); + let project_b = client_b.build_remote_project(project_id, cx_b).await; // Open a buffer as client B let buffer_b = project_b @@ -1114,7 +1138,11 @@ async fn test_editing_while_guest_opens_buffer( .insert_tree("/dir", json!({ "a.txt": "a-contents" })) .await; let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); + let project_b = client_b.build_remote_project(project_id, cx_b).await; // Open a buffer as client A let buffer_a = project_a @@ -1156,7 +1184,11 @@ async fn test_leaving_worktree_while_opening_buffer( .insert_tree("/dir", json!({ "a.txt": "a-contents" })) .await; let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); + let project_b = client_b.build_remote_project(project_id, cx_b).await; // See that a guest has joined as client A. project_a @@ -1201,7 +1233,11 @@ async fn test_canceling_buffer_opening( ) .await; let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); + let project_b = client_b.build_remote_project(project_id, cx_b).await; let buffer_a = project_a .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)) @@ -1243,7 +1279,11 @@ async fn test_leaving_project(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte ) .await; let (project_a, _) = client_a.build_local_project("/a", cx_a).await; - let _project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); + let _project_b = client_b.build_remote_project(project_id, cx_b).await; // Client A sees that a guest has joined. project_a @@ -1257,7 +1297,7 @@ async fn test_leaving_project(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte .await; // Rejoin the project as client B - let _project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let _project_b = client_b.build_remote_project(project_id, cx_b).await; // Client A sees that a guest has re-joined. project_a @@ -1317,7 +1357,10 @@ async fn test_collaborating_with_diagnostics( ) .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; - let project_id = project_a.update(cx_a, |p, _| p.next_remote_id()).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); // Cause the language server to start. let _buffer = cx_a @@ -1335,7 +1378,7 @@ async fn test_collaborating_with_diagnostics( .unwrap(); // Join the worktree as client B. - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_b = client_b.build_remote_project(project_id, cx_b).await; // Simulate a language server reporting errors for a file. let mut fake_language_server = fake_language_servers.next().await.unwrap(); @@ -1383,7 +1426,7 @@ async fn test_collaborating_with_diagnostics( }); // Join project as client C and observe the diagnostics. - let project_c = client_c.build_remote_project(&project_a, cx_a, cx_c).await; + let project_c = client_c.build_remote_project(project_id, cx_c).await; deterministic.run_until_parked(); project_c.read_with(cx_c, |project, cx| { assert_eq!( @@ -1561,7 +1604,11 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu ) .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); + let project_b = client_b.build_remote_project(project_id, cx_b).await; // Open a file in an editor as the guest. let buffer_b = project_b @@ -1705,8 +1752,12 @@ async fn test_reloading_buffer_manually(cx_a: &mut TestAppContext, cx_b: &mut Te .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)) .await .unwrap(); + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_b = client_b.build_remote_project(project_id, cx_b).await; let buffer_b = cx_b .background() @@ -1805,7 +1856,11 @@ async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppCon .insert_tree(&directory, json!({ "a.rs": "let one = \"two\"" })) .await; let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await; - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); + let project_b = client_b.build_remote_project(project_id, cx_b).await; let buffer_b = cx_b .background() @@ -1908,7 +1963,11 @@ async fn test_definition(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { ) .await; let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await; - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); + let project_b = client_b.build_remote_project(project_id, cx_b).await; // Open the file on client B. let buffer_b = cx_b @@ -2047,7 +2106,11 @@ async fn test_references(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { ) .await; let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await; - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); + let project_b = client_b.build_remote_project(project_id, cx_b).await; // Open the file on client B. let buffer_b = cx_b @@ -2142,8 +2205,12 @@ async fn test_project_search(cx_a: &mut TestAppContext, cx_b: &mut TestAppContex worktree_2 .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete()) .await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_b = client_b.build_remote_project(project_id, cx_b).await; // Perform a search as the guest. let results = project_b @@ -2212,7 +2279,11 @@ async fn test_document_highlights(cx_a: &mut TestAppContext, cx_b: &mut TestAppC client_a.language_registry.add(Arc::new(language)); let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await; - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); + let project_b = client_b.build_remote_project(project_id, cx_b).await; // Open the file on client B. let buffer_b = cx_b @@ -2309,7 +2380,11 @@ async fn test_lsp_hover(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { client_a.language_registry.add(Arc::new(language)); let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await; - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); + let project_b = client_b.build_remote_project(project_id, cx_b).await; // Open the file as the guest let buffer_b = cx_b @@ -2414,7 +2489,11 @@ async fn test_project_symbols(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte ) .await; let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await; - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); + let project_b = client_b.build_remote_project(project_id, cx_b).await; // Cause the language server to start. let _buffer = cx_b @@ -2510,7 +2589,11 @@ async fn test_open_buffer_while_getting_definition_pointing_to_it( ) .await; let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await; - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); + let project_b = client_b.build_remote_project(project_id, cx_b).await; let buffer_b1 = cx_b .background() @@ -2581,9 +2664,13 @@ async fn test_collaborating_with_code_actions( ) .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); // Join the project as client B. - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_b = client_b.build_remote_project(project_id, cx_b).await; let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(project_b.clone(), |_, _| unimplemented!(), cx)); let editor_b = workspace_b @@ -2798,7 +2885,11 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T ) .await; let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); + let project_b = client_b.build_remote_project(project_id, cx_b).await; let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(project_b.clone(), |_, _| unimplemented!(), cx)); @@ -3006,7 +3097,11 @@ async fn test_language_server_statuses( ); }); - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); + let project_b = client_b.build_remote_project(project_id, cx_b).await; project_b.read_with(cx_b, |project, _| { let status = project.language_server_statuses().next().unwrap(); assert_eq!(status.name, "the-language-server"); @@ -3485,79 +3580,6 @@ async fn test_contacts( [("user_a".to_string(), true), ("user_b".to_string(), true)] ); - // Share a project as client A. - client_a.fs.create_dir(Path::new("/a")).await.unwrap(); - let (project_a, _) = client_a.build_local_project("/a", cx_a).await; - - deterministic.run_until_parked(); - assert_eq!( - contacts(&client_a, cx_a), - [("user_b".to_string(), true), ("user_c".to_string(), true)] - ); - assert_eq!( - contacts(&client_b, cx_b), - [("user_a".to_string(), true), ("user_c".to_string(), true)] - ); - assert_eq!( - contacts(&client_c, cx_c), - [("user_a".to_string(), true), ("user_b".to_string(), true)] - ); - - let _project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; - - deterministic.run_until_parked(); - assert_eq!( - contacts(&client_a, cx_a), - [("user_b".to_string(), true), ("user_c".to_string(), true)] - ); - assert_eq!( - contacts(&client_b, cx_b), - [("user_a".to_string(), true,), ("user_c".to_string(), true)] - ); - assert_eq!( - contacts(&client_c, cx_c), - [("user_a".to_string(), true,), ("user_b".to_string(), true)] - ); - - // Add a local project as client B - client_a.fs.create_dir("/b".as_ref()).await.unwrap(); - let (_project_b, _) = client_b.build_local_project("/b", cx_b).await; - - deterministic.run_until_parked(); - assert_eq!( - contacts(&client_a, cx_a), - [("user_b".to_string(), true), ("user_c".to_string(), true)] - ); - assert_eq!( - contacts(&client_b, cx_b), - [("user_a".to_string(), true), ("user_c".to_string(), true)] - ); - assert_eq!( - contacts(&client_c, cx_c), - [("user_a".to_string(), true,), ("user_b".to_string(), true)] - ); - - project_a - .condition(cx_a, |project, _| { - project.collaborators().contains_key(&client_b.peer_id) - }) - .await; - - cx_a.update(move |_| drop(project_a)); - deterministic.run_until_parked(); - assert_eq!( - contacts(&client_a, cx_a), - [("user_b".to_string(), true), ("user_c".to_string(), true)] - ); - assert_eq!( - contacts(&client_b, cx_b), - [("user_a".to_string(), true), ("user_c".to_string(), true)] - ); - assert_eq!( - contacts(&client_c, cx_c), - [("user_a".to_string(), true), ("user_b".to_string(), true)] - ); - server.disconnect_client(client_c.current_user_id(cx_c)); server.forbid_connections(); deterministic.advance_clock(rpc::RECEIVE_TIMEOUT); @@ -3811,8 +3833,11 @@ async fn test_following(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { ) .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; - - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); + let project_b = client_b.build_remote_project(project_id, cx_b).await; // Client A opens some editors. let workspace_a = client_a.build_workspace(&project_a, cx_a); @@ -4019,9 +4044,13 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T ) .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); // Client B joins the project. - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_b = client_b.build_remote_project(project_id, cx_b).await; // Client A opens some editors. let workspace_a = client_a.build_workspace(&project_a, cx_a); @@ -4182,7 +4211,11 @@ async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont ) .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); + let project_b = client_b.build_remote_project(project_id, cx_b).await; // Client A opens some editors. let workspace_a = client_a.build_workspace(&project_a, cx_a); @@ -4331,8 +4364,12 @@ async fn test_peers_simultaneously_following_each_other( client_a.fs.insert_tree("/a", json!({})).await; let (project_a, _) = client_a.build_local_project("/a", cx_a).await; let workspace_a = client_a.build_workspace(&project_a, cx_a); + let project_id = project_a + .update(cx_a, |project, cx| project.share(cx)) + .await + .unwrap(); - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + let project_b = client_b.build_remote_project(project_id, cx_b).await; let workspace_b = client_b.build_workspace(&project_b, cx_b); deterministic.run_until_parked(); @@ -4445,9 +4482,6 @@ async fn test_random_collaboration( cx, ) }); - let host_project_id = host_project - .update(&mut host_cx, |p, _| p.next_remote_id()) - .await; let (collab_worktree, _) = host_project .update(&mut host_cx, |project, cx| { @@ -4588,7 +4622,7 @@ async fn test_random_collaboration( .await; host_language_registry.add(Arc::new(language)); - host_project + let host_project_id = host_project .update(&mut host_cx, |project, cx| project.share(cx)) .await .unwrap(); @@ -5155,40 +5189,26 @@ impl TestClient { worktree .read_with(cx, |tree, _| tree.as_local().unwrap().scan_complete()) .await; - project - .update(cx, |project, _| project.next_remote_id()) - .await; (project, worktree.read_with(cx, |tree, _| tree.id())) } async fn build_remote_project( &self, - host_project: &ModelHandle, - host_cx: &mut TestAppContext, + host_project_id: u64, guest_cx: &mut TestAppContext, ) -> ModelHandle { - let host_project_id = host_project - .read_with(host_cx, |project, _| project.next_remote_id()) - .await; - host_project - .update(host_cx, |project, cx| project.share(cx)) - .await - .unwrap(); - let languages = host_project.read_with(host_cx, |project, _| project.languages().clone()); let project_b = guest_cx.spawn(|cx| { Project::remote( host_project_id, self.client.clone(), self.user_store.clone(), self.project_store.clone(), - languages, + self.language_registry.clone(), FakeFs::new(cx.background()), cx, ) }); - - let project = project_b.await.unwrap(); - project + project_b.await.unwrap() } fn build_workspace( diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 192adb701c..f675ff2931 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -151,11 +151,10 @@ impl Server { .add_message_handler(Server::leave_room) .add_request_handler(Server::call) .add_message_handler(Server::decline_call) - .add_request_handler(Server::register_project) - .add_request_handler(Server::unregister_project) + .add_request_handler(Server::share_project) + .add_message_handler(Server::unshare_project) .add_request_handler(Server::join_project) .add_message_handler(Server::leave_project) - .add_message_handler(Server::unshare_project) .add_message_handler(Server::update_project) .add_message_handler(Server::register_project_activity) .add_request_handler(Server::update_worktree) @@ -470,18 +469,18 @@ impl Server { async fn sign_out(self: &mut Arc, connection_id: ConnectionId) -> Result<()> { self.peer.disconnect(connection_id); - let mut projects_to_unregister = Vec::new(); + let mut projects_to_unshare = Vec::new(); let removed_user_id; { let mut store = self.store().await; let removed_connection = store.remove_connection(connection_id)?; for (project_id, project) in removed_connection.hosted_projects { - projects_to_unregister.push(project_id); + projects_to_unshare.push(project_id); broadcast(connection_id, project.guests.keys().copied(), |conn_id| { self.peer.send( conn_id, - proto::UnregisterProject { + proto::UnshareProject { project_id: project_id.to_proto(), }, ) @@ -514,7 +513,7 @@ impl Server { self.update_user_contacts(removed_user_id).await.trace_err(); - for project_id in projects_to_unregister { + for project_id in projects_to_unshare { self.app_state .db .unregister_project(project_id) @@ -687,10 +686,10 @@ impl Server { } } - async fn register_project( + async fn share_project( self: Arc, - request: TypedEnvelope, - response: Response, + request: TypedEnvelope, + response: Response, ) -> Result<()> { let user_id = self .store() @@ -699,44 +698,15 @@ impl Server { let project_id = self.app_state.db.register_project(user_id).await?; self.store() .await - .register_project(request.sender_id, project_id)?; + .share_project(request.sender_id, project_id)?; - response.send(proto::RegisterProjectResponse { + response.send(proto::ShareProjectResponse { project_id: project_id.to_proto(), })?; Ok(()) } - async fn unregister_project( - self: Arc, - request: TypedEnvelope, - response: Response, - ) -> Result<()> { - let project_id = ProjectId::from_proto(request.payload.project_id); - let project = self - .store() - .await - .unregister_project(project_id, request.sender_id)?; - self.app_state.db.unregister_project(project_id).await?; - - broadcast( - request.sender_id, - project.guests.keys().copied(), - |conn_id| { - self.peer.send( - conn_id, - proto::UnregisterProject { - project_id: project_id.to_proto(), - }, - ) - }, - ); - response.send(proto::Ack {})?; - - Ok(()) - } - async fn unshare_project( self: Arc, message: TypedEnvelope, @@ -746,9 +716,11 @@ impl Server { .store() .await .unshare_project(project_id, message.sender_id)?; - broadcast(message.sender_id, project.guest_connection_ids, |conn_id| { - self.peer.send(conn_id, message.payload.clone()) - }); + broadcast( + message.sender_id, + project.guest_connection_ids(), + |conn_id| self.peer.send(conn_id, message.payload.clone()), + ); Ok(()) } diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index deb2230147..e73b2130c2 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -96,10 +96,6 @@ pub struct LeftProject { pub remove_collaborator: bool, } -pub struct UnsharedProject { - pub guest_connection_ids: Vec, -} - #[derive(Copy, Clone)] pub struct Metrics { pub connections: usize, @@ -201,9 +197,9 @@ impl Store { self.leave_channel(connection_id, channel_id); } - // Unregister and leave all projects. + // Unshare and leave all projects. for project_id in connection_projects { - if let Ok(project) = self.unregister_project(project_id, connection_id) { + if let Ok(project) = self.unshare_project(project_id, connection_id) { result.hosted_projects.insert(project_id, project); } else if self.leave_project(connection_id, project_id).is_ok() { result.guest_project_ids.insert(project_id); @@ -566,7 +562,7 @@ impl Store { } } - pub fn register_project( + pub fn share_project( &mut self, host_connection_id: ConnectionId, project_id: ProjectId, @@ -595,6 +591,35 @@ impl Store { Ok(()) } + pub fn unshare_project( + &mut self, + project_id: ProjectId, + connection_id: ConnectionId, + ) -> Result { + match self.projects.entry(project_id) { + btree_map::Entry::Occupied(e) => { + if e.get().host_connection_id == connection_id { + let project = e.remove(); + + if let Some(host_connection) = self.connections.get_mut(&connection_id) { + host_connection.projects.remove(&project_id); + } + + for guest_connection in project.guests.keys() { + if let Some(connection) = self.connections.get_mut(guest_connection) { + connection.projects.remove(&project_id); + } + } + + Ok(project) + } else { + Err(anyhow!("no such project"))? + } + } + btree_map::Entry::Vacant(_) => Err(anyhow!("no such project"))?, + } + } + pub fn update_project( &mut self, project_id: ProjectId, @@ -628,66 +653,6 @@ impl Store { } } - pub fn unregister_project( - &mut self, - project_id: ProjectId, - connection_id: ConnectionId, - ) -> Result { - match self.projects.entry(project_id) { - btree_map::Entry::Occupied(e) => { - if e.get().host_connection_id == connection_id { - let project = e.remove(); - - if let Some(host_connection) = self.connections.get_mut(&connection_id) { - host_connection.projects.remove(&project_id); - } - - for guest_connection in project.guests.keys() { - if let Some(connection) = self.connections.get_mut(guest_connection) { - connection.projects.remove(&project_id); - } - } - - Ok(project) - } else { - Err(anyhow!("no such project"))? - } - } - btree_map::Entry::Vacant(_) => Err(anyhow!("no such project"))?, - } - } - - pub fn unshare_project( - &mut self, - project_id: ProjectId, - connection_id: ConnectionId, - ) -> Result { - let project = self - .projects - .get_mut(&project_id) - .ok_or_else(|| anyhow!("no such project"))?; - anyhow::ensure!( - project.host_connection_id == connection_id, - "no such project" - ); - - let guest_connection_ids = project.guest_connection_ids(); - project.active_replica_ids.clear(); - project.guests.clear(); - project.language_servers.clear(); - project.worktrees.clear(); - - for connection_id in &guest_connection_ids { - if let Some(connection) = self.connections.get_mut(connection_id) { - connection.projects.remove(&project_id); - } - } - - Ok(UnsharedProject { - guest_connection_ids, - }) - } - pub fn update_diagnostic_summary( &mut self, project_id: ProjectId, diff --git a/crates/contacts_panel/src/contacts_panel.rs b/crates/contacts_panel/src/contacts_panel.rs index eb1afc3810..db6d3bd3b0 100644 --- a/crates/contacts_panel/src/contacts_panel.rs +++ b/crates/contacts_panel/src/contacts_panel.rs @@ -845,14 +845,6 @@ mod tests { .detach(); }); - let request = server.receive::().await.unwrap(); - server - .respond( - request.receipt(), - proto::RegisterProjectResponse { project_id: 200 }, - ) - .await; - let get_users_request = server.receive::().await.unwrap(); server .respond( diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 903e103d41..279e2caaa3 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -35,7 +35,6 @@ use lsp::{ }; use lsp_command::*; use parking_lot::Mutex; -use postage::stream::Stream; use postage::watch; use rand::prelude::*; use search::SearchQuery; @@ -153,10 +152,8 @@ enum WorktreeHandle { enum ProjectClientState { Local { - is_shared: bool, - remote_id_tx: watch::Sender>, - remote_id_rx: watch::Receiver>, - _maintain_remote_id: Task>, + remote_id: Option, + _detect_unshare: Task>, }, Remote { sharing_has_stopped: bool, @@ -382,7 +379,6 @@ impl Project { client.add_model_message_handler(Self::handle_update_language_server); client.add_model_message_handler(Self::handle_remove_collaborator); client.add_model_message_handler(Self::handle_update_project); - client.add_model_message_handler(Self::handle_unregister_project); client.add_model_message_handler(Self::handle_unshare_project); client.add_model_message_handler(Self::handle_create_buffer_for_peer); client.add_model_message_handler(Self::handle_update_buffer_file); @@ -423,24 +419,19 @@ impl Project { cx: &mut MutableAppContext, ) -> ModelHandle { cx.add_model(|cx: &mut ModelContext| { - let (remote_id_tx, remote_id_rx) = watch::channel(); - let _maintain_remote_id = cx.spawn_weak({ - let mut status_rx = client.clone().status(); - move |this, mut cx| async move { - while let Some(status) = status_rx.recv().await { - let this = this.upgrade(&cx)?; - if status.is_connected() { - this.update(&mut cx, |this, cx| this.register(cx)) - .await - .log_err()?; - } else { - this.update(&mut cx, |this, cx| this.unregister(cx)) - .await - .log_err(); + let mut status = client.status(); + let _detect_unshare = cx.spawn_weak(move |this, mut cx| { + async move { + let is_connected = status.next().await.map_or(false, |s| s.is_connected()); + // Even if we're initially connected, any future change of the status means we momentarily disconnected. + if !is_connected || status.next().await.is_some() { + if let Some(this) = this.upgrade(&cx) { + let _ = this.update(&mut cx, |this, cx| this.unshare(cx)); } } - None + Ok(()) } + .log_err() }); let handle = cx.weak_handle(); @@ -456,10 +447,8 @@ impl Project { loading_local_worktrees: Default::default(), buffer_snapshots: Default::default(), client_state: ProjectClientState::Local { - is_shared: false, - remote_id_tx, - remote_id_rx, - _maintain_remote_id, + remote_id: None, + _detect_unshare, }, opened_buffer: watch::channel(), client_subscriptions: Vec::new(), @@ -762,113 +751,9 @@ impl Project { &self.fs } - fn unregister(&mut self, cx: &mut ModelContext) -> Task> { - self.unshare(cx).log_err(); - if let ProjectClientState::Local { remote_id_rx, .. } = &mut self.client_state { - if let Some(remote_id) = *remote_id_rx.borrow() { - let request = self.client.request(proto::UnregisterProject { - project_id: remote_id, - }); - return cx.spawn(|this, mut cx| async move { - let response = request.await; - - // Unregistering the project causes the server to send out a - // contact update removing this project from the host's list - // of online projects. Wait until this contact update has been - // processed before clearing out this project's remote id, so - // that there is no moment where this project appears in the - // contact metadata and *also* has no remote id. - this.update(&mut cx, |this, cx| { - this.user_store() - .update(cx, |store, _| store.contact_updates_done()) - }) - .await; - - this.update(&mut cx, |this, cx| { - if let ProjectClientState::Local { remote_id_tx, .. } = - &mut this.client_state - { - *remote_id_tx.borrow_mut() = None; - } - this.client_subscriptions.clear(); - this.metadata_changed(cx); - }); - response.map(drop) - }); - } - } - Task::ready(Ok(())) - } - - fn register(&mut self, cx: &mut ModelContext) -> Task> { - if let ProjectClientState::Local { remote_id_rx, .. } = &self.client_state { - if remote_id_rx.borrow().is_some() { - return Task::ready(Ok(())); - } - - let response = self.client.request(proto::RegisterProject {}); - cx.spawn(|this, mut cx| async move { - let remote_id = response.await?.project_id; - this.update(&mut cx, |this, cx| { - if let ProjectClientState::Local { remote_id_tx, .. } = &mut this.client_state { - *remote_id_tx.borrow_mut() = Some(remote_id); - } - - this.metadata_changed(cx); - cx.emit(Event::RemoteIdChanged(Some(remote_id))); - this.client_subscriptions - .push(this.client.add_model_for_remote_entity(remote_id, cx)); - Ok(()) - }) - }) - } else { - Task::ready(Err(anyhow!("can't register a remote project"))) - } - } - pub fn remote_id(&self) -> Option { match &self.client_state { - ProjectClientState::Local { remote_id_rx, .. } => *remote_id_rx.borrow(), - ProjectClientState::Remote { remote_id, .. } => Some(*remote_id), - } - } - - pub fn next_remote_id(&self) -> impl Future { - let mut id = None; - let mut watch = None; - match &self.client_state { - ProjectClientState::Local { remote_id_rx, .. } => watch = Some(remote_id_rx.clone()), - ProjectClientState::Remote { remote_id, .. } => id = Some(*remote_id), - } - - async move { - if let Some(id) = id { - return id; - } - let mut watch = watch.unwrap(); - loop { - let id = *watch.borrow(); - if let Some(id) = id { - return id; - } - watch.next().await; - } - } - } - - pub fn shared_remote_id(&self) -> Option { - match &self.client_state { - ProjectClientState::Local { - remote_id_rx, - is_shared, - .. - } => { - if *is_shared { - *remote_id_rx.borrow() - } else { - None - } - } + ProjectClientState::Local { remote_id, .. } => *remote_id, ProjectClientState::Remote { remote_id, .. } => Some(*remote_id), } } @@ -881,7 +766,7 @@ impl Project { } fn metadata_changed(&mut self, cx: &mut ModelContext) { - if let ProjectClientState::Local { remote_id_rx, .. } = &self.client_state { + if let ProjectClientState::Local { remote_id, .. } = &self.client_state { // Broadcast worktrees only if the project is online. let worktrees = self .worktrees @@ -892,7 +777,7 @@ impl Project { .map(|worktree| worktree.read(cx).as_local().unwrap().metadata_proto()) }) .collect(); - if let Some(project_id) = *remote_id_rx.borrow() { + if let Some(project_id) = *remote_id { self.client .send(proto::UpdateProject { project_id, @@ -1164,113 +1049,105 @@ impl Project { } } - pub fn share(&mut self, cx: &mut ModelContext) -> Task> { - let project_id; - if let ProjectClientState::Local { - remote_id_rx, - is_shared, - .. - } = &mut self.client_state - { - if *is_shared { - return Task::ready(Ok(())); - } - *is_shared = true; - if let Some(id) = *remote_id_rx.borrow() { - project_id = id; - } else { - return Task::ready(Err(anyhow!("project hasn't been registered"))); + pub fn share(&mut self, cx: &mut ModelContext) -> Task> { + if let ProjectClientState::Local { remote_id, .. } = &mut self.client_state { + if let Some(remote_id) = remote_id { + return Task::ready(Ok(*remote_id)); } + + let response = self.client.request(proto::ShareProject {}); + cx.spawn(|this, mut cx| async move { + let project_id = response.await?.project_id; + let mut worktree_share_tasks = Vec::new(); + this.update(&mut cx, |this, cx| { + if let ProjectClientState::Local { remote_id, .. } = &mut this.client_state { + *remote_id = Some(project_id); + } + + for open_buffer in this.opened_buffers.values_mut() { + match open_buffer { + OpenBuffer::Strong(_) => {} + OpenBuffer::Weak(buffer) => { + if let Some(buffer) = buffer.upgrade(cx) { + *open_buffer = OpenBuffer::Strong(buffer); + } + } + OpenBuffer::Operations(_) => unreachable!(), + } + } + + for worktree_handle in this.worktrees.iter_mut() { + match worktree_handle { + WorktreeHandle::Strong(_) => {} + WorktreeHandle::Weak(worktree) => { + if let Some(worktree) = worktree.upgrade(cx) { + *worktree_handle = WorktreeHandle::Strong(worktree); + } + } + } + } + + for worktree in this.worktrees(cx).collect::>() { + worktree.update(cx, |worktree, cx| { + let worktree = worktree.as_local_mut().unwrap(); + worktree_share_tasks.push(worktree.share(project_id, cx)); + }); + } + + for (server_id, status) in &this.language_server_statuses { + this.client + .send(proto::StartLanguageServer { + project_id, + server: Some(proto::LanguageServer { + id: *server_id as u64, + name: status.name.clone(), + }), + }) + .log_err(); + } + + this.client_subscriptions + .push(this.client.add_model_for_remote_entity(project_id, cx)); + this.metadata_changed(cx); + cx.emit(Event::RemoteIdChanged(Some(project_id))); + cx.notify(); + }); + + futures::future::try_join_all(worktree_share_tasks).await?; + Ok(project_id) + }) } else { - return Task::ready(Err(anyhow!("can't share a remote project"))); - }; - - for open_buffer in self.opened_buffers.values_mut() { - match open_buffer { - OpenBuffer::Strong(_) => {} - OpenBuffer::Weak(buffer) => { - if let Some(buffer) = buffer.upgrade(cx) { - *open_buffer = OpenBuffer::Strong(buffer); - } - } - OpenBuffer::Operations(_) => unreachable!(), - } + Task::ready(Err(anyhow!("can't share a remote project"))) } - - for worktree_handle in self.worktrees.iter_mut() { - match worktree_handle { - WorktreeHandle::Strong(_) => {} - WorktreeHandle::Weak(worktree) => { - if let Some(worktree) = worktree.upgrade(cx) { - *worktree_handle = WorktreeHandle::Strong(worktree); - } - } - } - } - - let mut tasks = Vec::new(); - for worktree in self.worktrees(cx).collect::>() { - worktree.update(cx, |worktree, cx| { - let worktree = worktree.as_local_mut().unwrap(); - tasks.push(worktree.share(project_id, cx)); - }); - } - - for (server_id, status) in &self.language_server_statuses { - self.client - .send(proto::StartLanguageServer { - project_id, - server: Some(proto::LanguageServer { - id: *server_id as u64, - name: status.name.clone(), - }), - }) - .log_err(); - } - - cx.spawn(|this, mut cx| async move { - for task in tasks { - task.await?; - } - this.update(&mut cx, |_, cx| cx.notify()); - Ok(()) - }) } pub fn unshare(&mut self, cx: &mut ModelContext) -> Result<()> { - if let ProjectClientState::Local { - is_shared, - remote_id_rx, - .. - } = &mut self.client_state - { - if !*is_shared { - return Ok(()); - } + if let ProjectClientState::Local { remote_id, .. } = &mut self.client_state { + if let Some(project_id) = remote_id.take() { + self.collaborators.clear(); + self.shared_buffers.clear(); + self.client_subscriptions.clear(); - *is_shared = false; - self.collaborators.clear(); - self.shared_buffers.clear(); - for worktree_handle in self.worktrees.iter_mut() { - if let WorktreeHandle::Strong(worktree) = worktree_handle { - let is_visible = worktree.update(cx, |worktree, _| { - worktree.as_local_mut().unwrap().unshare(); - worktree.is_visible() - }); - if !is_visible { - *worktree_handle = WorktreeHandle::Weak(worktree.downgrade()); + for worktree_handle in self.worktrees.iter_mut() { + if let WorktreeHandle::Strong(worktree) = worktree_handle { + let is_visible = worktree.update(cx, |worktree, _| { + worktree.as_local_mut().unwrap().unshare(); + worktree.is_visible() + }); + if !is_visible { + *worktree_handle = WorktreeHandle::Weak(worktree.downgrade()); + } } } - } - for open_buffer in self.opened_buffers.values_mut() { - if let OpenBuffer::Strong(buffer) = open_buffer { - *open_buffer = OpenBuffer::Weak(buffer.downgrade()); + for open_buffer in self.opened_buffers.values_mut() { + if let OpenBuffer::Strong(buffer) = open_buffer { + *open_buffer = OpenBuffer::Weak(buffer.downgrade()); + } } - } - cx.notify(); - if let Some(project_id) = *remote_id_rx.borrow() { + self.metadata_changed(cx); + cx.notify(); self.client.send(proto::UnshareProject { project_id })?; } @@ -1750,7 +1627,7 @@ impl Project { ) -> Option<()> { match event { BufferEvent::Operation(operation) => { - if let Some(project_id) = self.shared_remote_id() { + if let Some(project_id) = self.remote_id() { let request = self.client.request(proto::UpdateBuffer { project_id, buffer_id: buffer.read(cx).remote_id(), @@ -2155,7 +2032,7 @@ impl Project { ) .ok(); - if let Some(project_id) = this.shared_remote_id() { + if let Some(project_id) = this.remote_id() { this.client .send(proto::StartLanguageServer { project_id, @@ -2562,7 +2439,7 @@ impl Project { language_server_id: usize, event: proto::update_language_server::Variant, ) { - if let Some(project_id) = self.shared_remote_id() { + if let Some(project_id) = self.remote_id() { self.client .send(proto::UpdateLanguageServer { project_id, @@ -4273,7 +4150,7 @@ impl Project { pub fn is_shared(&self) -> bool { match &self.client_state { - ProjectClientState::Local { is_shared, .. } => *is_shared, + ProjectClientState::Local { remote_id, .. } => remote_id.is_some(), ProjectClientState::Remote { .. } => false, } } @@ -4310,7 +4187,7 @@ impl Project { let project_id = project.update(&mut cx, |project, cx| { project.add_worktree(&worktree, cx); - project.shared_remote_id() + project.remote_id() }); if let Some(project_id) = project_id { @@ -4439,7 +4316,7 @@ impl Project { renamed_buffers.push((cx.handle(), old_path)); } - if let Some(project_id) = self.shared_remote_id() { + if let Some(project_id) = self.remote_id() { self.client .send(proto::UpdateBufferFile { project_id, @@ -4552,16 +4429,6 @@ impl Project { // RPC message handlers - async fn handle_unregister_project( - this: ModelHandle, - _: TypedEnvelope, - _: Arc, - mut cx: AsyncAppContext, - ) -> Result<()> { - this.update(&mut cx, |this, cx| this.disconnected_from_host(cx)); - Ok(()) - } - async fn handle_unshare_project( this: ModelHandle, _: TypedEnvelope, @@ -5987,10 +5854,10 @@ impl Entity for Project { self.project_store.update(cx, ProjectStore::prune_projects); match &self.client_state { - ProjectClientState::Local { remote_id_rx, .. } => { - if let Some(project_id) = *remote_id_rx.borrow() { + ProjectClientState::Local { remote_id, .. } => { + if let Some(project_id) = *remote_id { self.client - .send(proto::UnregisterProject { project_id }) + .send(proto::UnshareProject { project_id }) .log_err(); } } diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 2659ddb86d..acb18878d9 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -22,15 +22,14 @@ message Envelope { DeclineCall decline_call = 13; RoomUpdated room_updated = 14; - RegisterProject register_project = 15; - RegisterProjectResponse register_project_response = 16; - UnregisterProject unregister_project = 17; + ShareProject share_project = 15; + ShareProjectResponse share_project_response = 16; + UnshareProject unshare_project = 17; JoinProject join_project = 21; JoinProjectResponse join_project_response = 22; LeaveProject leave_project = 23; AddProjectCollaborator add_project_collaborator = 24; RemoveProjectCollaborator remove_project_collaborator = 25; - UnshareProject unshare_project = 26; GetDefinition get_definition = 27; GetDefinitionResponse get_definition_response = 28; @@ -195,13 +194,13 @@ message RoomUpdated { Room room = 1; } -message RegisterProject {} +message ShareProject {} -message RegisterProjectResponse { +message ShareProjectResponse { uint64 project_id = 1; } -message UnregisterProject { +message UnshareProject { uint64 project_id = 1; } @@ -285,10 +284,6 @@ message RemoveProjectCollaborator { uint32 peer_id = 2; } -message UnshareProject { - uint64 project_id = 1; -} - message GetDefinition { uint64 project_id = 1; uint64 buffer_id = 2; diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index c2d2d2b321..822a50c3e4 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -141,10 +141,8 @@ messages!( (PrepareRename, Background), (PrepareRenameResponse, Background), (ProjectEntryResponse, Foreground), - (RegisterProjectResponse, Foreground), (RemoveContact, Foreground), (Ping, Foreground), - (RegisterProject, Foreground), (RegisterProjectActivity, Foreground), (ReloadBuffers, Foreground), (ReloadBuffersResponse, Foreground), @@ -158,11 +156,12 @@ messages!( (SearchProjectResponse, Background), (SendChannelMessage, Foreground), (SendChannelMessageResponse, Foreground), + (ShareProject, Foreground), + (ShareProjectResponse, Foreground), (ShowContacts, Foreground), (StartLanguageServer, Foreground), (Test, Foreground), (Unfollow, Foreground), - (UnregisterProject, Foreground), (UnshareProject, Foreground), (UpdateBuffer, Foreground), (UpdateBufferFile, Foreground), @@ -212,7 +211,6 @@ request_messages!( (Ping, Ack), (PerformRename, PerformRenameResponse), (PrepareRename, PrepareRenameResponse), - (RegisterProject, RegisterProjectResponse), (ReloadBuffers, ReloadBuffersResponse), (RequestContact, Ack), (RemoveContact, Ack), @@ -221,8 +219,8 @@ request_messages!( (SaveBuffer, BufferSaved), (SearchProject, SearchProjectResponse), (SendChannelMessage, SendChannelMessageResponse), + (ShareProject, ShareProjectResponse), (Test, Test), - (UnregisterProject, Ack), (UpdateBuffer, Ack), (UpdateWorktree, Ack), ); @@ -263,7 +261,6 @@ entity_messages!( SearchProject, StartLanguageServer, Unfollow, - UnregisterProject, UnshareProject, UpdateBuffer, UpdateBufferFile, From bce25918a039fe1e7706258b5c60e23a5e6368a2 Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 30 Sep 2022 11:13:22 -0400 Subject: [PATCH 096/314] Fix test build --- crates/project/src/worktree.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 1d47c843c5..0d2594475c 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -3211,7 +3211,7 @@ mod tests { })); let http_client = FakeHttpClient::with_404_response(); - let client = Client::new(http_client); + let client = cx.read(|cx| Client::new(http_client, cx)); let tree = Worktree::local( client, root.path(), From 964a5d2db7cf84b76441adf8756724f03a94e4bc Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 30 Sep 2022 18:21:47 +0200 Subject: [PATCH 097/314] WIP: require sharing projects on a given `Room` --- crates/call/src/participant.rs | 4 -- crates/call/src/room.rs | 34 +-------------- crates/collab/src/rpc.rs | 46 +++++++++++++++----- crates/collab/src/rpc/store.rs | 78 ++++++++++++++++++++++++++++------ crates/project/src/project.rs | 4 +- crates/rpc/proto/zed.proto | 4 +- 6 files changed, 105 insertions(+), 65 deletions(-) diff --git a/crates/call/src/participant.rs b/crates/call/src/participant.rs index cde17b45c2..c6257b2895 100644 --- a/crates/call/src/participant.rs +++ b/crates/call/src/participant.rs @@ -20,10 +20,6 @@ impl ParticipantLocation { } } -pub struct LocalParticipant { - pub projects: Vec>, -} - pub struct RemoteParticipant { pub user_id: u64, pub projects: Vec>, diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index adf3a676aa..ca8d5ea95f 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -1,10 +1,9 @@ -use crate::participant::{LocalParticipant, ParticipantLocation, RemoteParticipant}; +use crate::participant::{ParticipantLocation, RemoteParticipant}; use anyhow::{anyhow, Result}; use client::{incoming_call::IncomingCall, proto, Client, PeerId, TypedEnvelope, User, UserStore}; use collections::HashMap; use futures::StreamExt; use gpui::{AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task}; -use project::Project; use std::sync::Arc; use util::ResultExt; @@ -15,7 +14,6 @@ pub enum Event { pub struct Room { id: u64, status: RoomStatus, - local_participant: LocalParticipant, remote_participants: HashMap, pending_users: Vec>, client: Arc, @@ -53,9 +51,6 @@ impl Room { Self { id, status: RoomStatus::Online, - local_participant: LocalParticipant { - projects: Default::default(), - }, remote_participants: Default::default(), pending_users: Default::default(), _subscriptions: vec![client.add_message_handler(cx.handle(), Self::handle_room_updated)], @@ -179,33 +174,6 @@ impl Room { Ok(()) }) } - - pub fn publish_project(&mut self, project: ModelHandle) -> Task> { - if self.status.is_offline() { - return Task::ready(Err(anyhow!("room is offline"))); - } - - todo!() - } - - pub fn unpublish_project(&mut self, project: ModelHandle) -> Task> { - if self.status.is_offline() { - return Task::ready(Err(anyhow!("room is offline"))); - } - - todo!() - } - - pub fn set_active_project( - &mut self, - project: Option<&ModelHandle>, - ) -> Task> { - if self.status.is_offline() { - return Task::ready(Err(anyhow!("room is offline"))); - } - - todo!() - } } #[derive(Copy, Clone, PartialEq, Eq)] diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index f675ff2931..5b89dc6bf6 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -611,8 +611,34 @@ impl Server { async fn leave_room(self: Arc, message: TypedEnvelope) -> Result<()> { let room_id = message.payload.id; let mut store = self.store().await; - let room = store.leave_room(room_id, message.sender_id)?; - self.room_updated(room); + let left_room = store.leave_room(room_id, message.sender_id)?; + + for project in left_room.unshared_projects { + for connection_id in project.connection_ids() { + self.peer.send( + connection_id, + proto::UnshareProject { + project_id: project.id.to_proto(), + }, + )?; + } + } + + for project in left_room.left_projects { + if project.remove_collaborator { + for connection_id in project.connection_ids { + self.peer.send( + connection_id, + proto::RemoveProjectCollaborator { + project_id: project.id.to_proto(), + peer_id: message.sender_id.0, + }, + )?; + } + } + } + + self.room_updated(left_room.room); Ok(()) } @@ -696,13 +722,12 @@ impl Server { .await .user_id_for_connection(request.sender_id)?; let project_id = self.app_state.db.register_project(user_id).await?; - self.store() - .await - .share_project(request.sender_id, project_id)?; - + let mut store = self.store().await; + let room = store.share_project(request.payload.room_id, project_id, request.sender_id)?; response.send(proto::ShareProjectResponse { project_id: project_id.to_proto(), })?; + self.room_updated(room); Ok(()) } @@ -712,15 +737,14 @@ impl Server { message: TypedEnvelope, ) -> Result<()> { let project_id = ProjectId::from_proto(message.payload.project_id); - let project = self - .store() - .await - .unshare_project(project_id, message.sender_id)?; + let mut store = self.store().await; + let (room, project) = store.unshare_project(project_id, message.sender_id)?; broadcast( message.sender_id, project.guest_connection_ids(), |conn_id| self.peer.send(conn_id, message.payload.clone()), ); + self.room_updated(room); Ok(()) } @@ -882,7 +906,7 @@ impl Server { let project; { let mut store = self.store().await; - project = store.leave_project(sender_id, project_id)?; + project = store.leave_project(project_id, sender_id)?; tracing::info!( %project_id, host_user_id = %project.host_user_id, diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index e73b2130c2..2ae52f7c2b 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -44,6 +44,8 @@ pub struct Call { #[derive(Serialize)] pub struct Project { + pub id: ProjectId, + pub room_id: RoomId, pub host_connection_id: ConnectionId, pub host: Collaborator, pub guests: HashMap, @@ -90,12 +92,19 @@ pub struct RemovedConnectionState { } pub struct LeftProject { + pub id: ProjectId, pub host_user_id: UserId, pub host_connection_id: ConnectionId, pub connection_ids: Vec, pub remove_collaborator: bool, } +pub struct LeftRoom<'a> { + pub room: &'a proto::Room, + pub unshared_projects: Vec, + pub left_projects: Vec, +} + #[derive(Copy, Clone)] pub struct Metrics { pub connections: usize, @@ -199,9 +208,9 @@ impl Store { // Unshare and leave all projects. for project_id in connection_projects { - if let Ok(project) = self.unshare_project(project_id, connection_id) { + if let Ok((_, project)) = self.unshare_project(project_id, connection_id) { result.hosted_projects.insert(project_id, project); - } else if self.leave_project(connection_id, project_id).is_ok() { + } else if self.leave_project(project_id, connection_id).is_ok() { result.guest_project_ids.insert(project_id); } } @@ -424,11 +433,7 @@ impl Store { Ok((room, recipient_connection_ids)) } - pub fn leave_room( - &mut self, - room_id: RoomId, - connection_id: ConnectionId, - ) -> Result<&proto::Room> { + pub fn leave_room(&mut self, room_id: RoomId, connection_id: ConnectionId) -> Result { let connection = self .connections .get_mut(&connection_id) @@ -454,7 +459,22 @@ impl Store { .retain(|participant| participant.peer_id != connection_id.0); connected_user.active_call = None; - Ok(room) + let mut unshared_projects = Vec::new(); + let mut left_projects = Vec::new(); + for project_id in connection.projects.clone() { + if let Ok((_, project)) = self.unshare_project(project_id, connection_id) { + unshared_projects.push(project); + } else if let Ok(project) = self.leave_project(project_id, connection_id) { + left_projects.push(project); + } + } + + let room = self.rooms.get(&room_id).unwrap(); + Ok(LeftRoom { + room, + unshared_projects, + left_projects, + }) } pub fn room(&self, room_id: RoomId) -> Option<&proto::Room> { @@ -564,17 +584,32 @@ impl Store { pub fn share_project( &mut self, - host_connection_id: ConnectionId, + room_id: RoomId, project_id: ProjectId, - ) -> Result<()> { + host_connection_id: ConnectionId, + ) -> Result<&proto::Room> { let connection = self .connections .get_mut(&host_connection_id) .ok_or_else(|| anyhow!("no such connection"))?; + + let room = self + .rooms + .get_mut(&room_id) + .ok_or_else(|| anyhow!("no such room"))?; + let participant = room + .participants + .iter_mut() + .find(|participant| participant.peer_id == host_connection_id.0) + .ok_or_else(|| anyhow!("no such room"))?; + participant.project_ids.push(project_id.to_proto()); + connection.projects.insert(project_id); self.projects.insert( project_id, Project { + id: project_id, + room_id, host_connection_id, host: Collaborator { user_id: connection.user_id, @@ -588,14 +623,15 @@ impl Store { language_servers: Default::default(), }, ); - Ok(()) + + Ok(room) } pub fn unshare_project( &mut self, project_id: ProjectId, connection_id: ConnectionId, - ) -> Result { + ) -> Result<(&proto::Room, Project)> { match self.projects.entry(project_id) { btree_map::Entry::Occupied(e) => { if e.get().host_connection_id == connection_id { @@ -611,7 +647,20 @@ impl Store { } } - Ok(project) + let room = self + .rooms + .get_mut(&project.room_id) + .ok_or_else(|| anyhow!("no such room"))?; + let participant = room + .participants + .iter_mut() + .find(|participant| participant.peer_id == connection_id.0) + .ok_or_else(|| anyhow!("no such room"))?; + participant + .project_ids + .retain(|id| *id != project_id.to_proto()); + + Ok((room, project)) } else { Err(anyhow!("no such project"))? } @@ -731,8 +780,8 @@ impl Store { pub fn leave_project( &mut self, - connection_id: ConnectionId, project_id: ProjectId, + connection_id: ConnectionId, ) -> Result { let project = self .projects @@ -752,6 +801,7 @@ impl Store { } Ok(LeftProject { + id: project.id, host_connection_id: project.host_connection_id, host_user_id: project.host.user_id, connection_ids: project.connection_ids(), diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 279e2caaa3..901e3b7d85 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1049,13 +1049,13 @@ impl Project { } } - pub fn share(&mut self, cx: &mut ModelContext) -> Task> { + pub fn share(&mut self, room_id: u64, cx: &mut ModelContext) -> Task> { if let ProjectClientState::Local { remote_id, .. } = &mut self.client_state { if let Some(remote_id) = remote_id { return Task::ready(Ok(*remote_id)); } - let response = self.client.request(proto::ShareProject {}); + let response = self.client.request(proto::ShareProject { room_id }); cx.spawn(|this, mut cx| async move { let project_id = response.await?.project_id; let mut worktree_share_tasks = Vec::new(); diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index acb18878d9..cff10278b4 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -194,7 +194,9 @@ message RoomUpdated { Room room = 1; } -message ShareProject {} +message ShareProject { + uint64 room_id = 1; +} message ShareProjectResponse { uint64 project_id = 1; From 1c5d15b85e785f0f87a50df14160295e3109185f Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 30 Sep 2022 13:32:54 -0400 Subject: [PATCH 098/314] Use sumtree instead of iterator linear search for diff hunks in range Co-Authored-By: Max Brunsfeld Co-Authored-By: Mikayla Maki --- crates/git/src/diff.rs | 200 +++++++++++++++++++++++--------------- crates/text/src/anchor.rs | 2 +- 2 files changed, 123 insertions(+), 79 deletions(-) diff --git a/crates/git/src/diff.rs b/crates/git/src/diff.rs index ddaddb7289..4d12ca90d1 100644 --- a/crates/git/src/diff.rs +++ b/crates/git/src/diff.rs @@ -1,7 +1,7 @@ use std::ops::Range; use sum_tree::SumTree; -use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point, ToPoint}; +use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point}; pub use git2 as libgit; use libgit::{DiffLineType as GitDiffLineType, DiffOptions as GitOptions, Patch as GitPatch}; @@ -37,7 +37,6 @@ impl sum_tree::Item for DiffHunk { fn summary(&self) -> Self::Summary { DiffHunkSummary { buffer_range: self.buffer_range.clone(), - head_range: self.head_byte_range.clone(), } } } @@ -45,54 +44,17 @@ impl sum_tree::Item for DiffHunk { #[derive(Debug, Default, Clone)] pub struct DiffHunkSummary { buffer_range: Range, - head_range: Range, } impl sum_tree::Summary for DiffHunkSummary { type Context = text::BufferSnapshot; - fn add_summary(&mut self, other: &Self, _: &Self::Context) { - self.head_range.start = self.head_range.start.min(other.head_range.start); - self.head_range.end = self.head_range.end.max(other.head_range.end); - } -} - -#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)] -struct HunkHeadEnd(usize); - -impl<'a> sum_tree::Dimension<'a, DiffHunkSummary> for HunkHeadEnd { - fn add_summary(&mut self, summary: &'a DiffHunkSummary, _: &text::BufferSnapshot) { - self.0 = summary.head_range.end; - } - - fn from_summary(summary: &'a DiffHunkSummary, _: &text::BufferSnapshot) -> Self { - HunkHeadEnd(summary.head_range.end) - } -} - -#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)] -struct HunkBufferStart(u32); - -impl<'a> sum_tree::Dimension<'a, DiffHunkSummary> for HunkBufferStart { - fn add_summary(&mut self, summary: &'a DiffHunkSummary, buffer: &text::BufferSnapshot) { - self.0 = summary.buffer_range.start.to_point(buffer).row; - } - - fn from_summary(summary: &'a DiffHunkSummary, buffer: &text::BufferSnapshot) -> Self { - HunkBufferStart(summary.buffer_range.start.to_point(buffer).row) - } -} - -#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)] -struct HunkBufferEnd(u32); - -impl<'a> sum_tree::Dimension<'a, DiffHunkSummary> for HunkBufferEnd { - fn add_summary(&mut self, summary: &'a DiffHunkSummary, buffer: &text::BufferSnapshot) { - self.0 = summary.buffer_range.end.to_point(buffer).row; - } - - fn from_summary(summary: &'a DiffHunkSummary, buffer: &text::BufferSnapshot) -> Self { - HunkBufferEnd(summary.buffer_range.end.to_point(buffer).row) + fn add_summary(&mut self, other: &Self, buffer: &Self::Context) { + self.buffer_range.start = self + .buffer_range + .start + .min(&other.buffer_range.start, buffer); + self.buffer_range.end = self.buffer_range.end.max(&other.buffer_range.end, buffer); } } @@ -115,23 +77,30 @@ impl BufferDiff { query_row_range: Range, buffer: &'a BufferSnapshot, ) -> impl 'a + Iterator> { - self.tree.iter().filter_map(move |hunk| { - let range = hunk.buffer_range.to_point(&buffer); + let start = buffer.anchor_before(Point::new(query_row_range.start, 0)); + let end = buffer.anchor_after(Point::new(query_row_range.end, 0)); - if range.start.row <= query_row_range.end && query_row_range.start <= range.end.row { - let end_row = if range.end.column > 0 { - range.end.row + 1 - } else { - range.end.row - }; + let mut cursor = self.tree.filter::<_, DiffHunkSummary>(move |summary| { + let before_start = summary.buffer_range.end.cmp(&start, buffer).is_lt(); + let after_end = summary.buffer_range.start.cmp(&end, buffer).is_gt(); + !before_start && !after_end + }); - Some(DiffHunk { - buffer_range: range.start.row..end_row, - head_byte_range: hunk.head_byte_range.clone(), - }) + std::iter::from_fn(move || { + cursor.next(buffer); + let hunk = cursor.item()?; + + let range = hunk.buffer_range.to_point(buffer); + let end_row = if range.end.column > 0 { + range.end.row + 1 } else { - None - } + range.end.row + }; + + Some(DiffHunk { + buffer_range: range.start.row..end_row, + head_byte_range: hunk.head_byte_range.clone(), + }) }) } @@ -270,7 +239,7 @@ mod tests { let buffer_text = " one - hello + HELLO three " .unindent(); @@ -278,10 +247,78 @@ mod tests { let mut buffer = Buffer::new(0, 0, buffer_text); let mut diff = BufferDiff::new(); smol::block_on(diff.update(&head_text, &buffer)); - assert_hunks(&diff, &buffer, &head_text, &[(1..2, "two\n")]); + assert_hunks( + &diff, + &buffer, + &head_text, + &[(1..2, "two\n", "HELLO\n")], + None, + ); buffer.edit([(0..0, "point five\n")]); - assert_hunks(&diff, &buffer, &head_text, &[(2..3, "two\n")]); + smol::block_on(diff.update(&head_text, &buffer)); + assert_hunks( + &diff, + &buffer, + &head_text, + &[(0..1, "", "point five\n"), (2..3, "two\n", "HELLO\n")], + None, + ); + } + + #[test] + fn test_buffer_diff_range() { + let head_text = " + one + two + three + four + five + six + seven + eight + nine + ten + " + .unindent(); + + let buffer_text = " + A + one + B + two + C + three + HELLO + four + five + SIXTEEN + seven + eight + WORLD + nine + + ten + + " + .unindent(); + + let buffer = Buffer::new(0, 0, buffer_text); + let mut diff = BufferDiff::new(); + smol::block_on(diff.update(&head_text, &buffer)); + assert_eq!(diff.hunks(&buffer).count(), 8); + + assert_hunks( + &diff, + &buffer, + &head_text, + &[ + (6..7, "", "HELLO\n"), + (9..10, "six\n", "SIXTEEN\n"), + (12..13, "", "WORLD\n"), + ], + Some(7..12), + ); } #[track_caller] @@ -289,23 +326,30 @@ mod tests { diff: &BufferDiff, buffer: &BufferSnapshot, head_text: &str, - expected_hunks: &[(Range, &str)], + expected_hunks: &[(Range, &str, &str)], + range: Option>, ) { - let hunks = diff.hunks(buffer).collect::>(); - assert_eq!( - hunks.len(), - expected_hunks.len(), - "actual hunks are {hunks:#?}" - ); + let actual_hunks = diff + .hunks_in_range(range.unwrap_or(0..u32::MAX), buffer) + .map(|hunk| { + ( + hunk.buffer_range.clone(), + &head_text[hunk.head_byte_range], + buffer + .text_for_range( + Point::new(hunk.buffer_range.start, 0) + ..Point::new(hunk.buffer_range.end, 0), + ) + .collect::(), + ) + }) + .collect::>(); - let diff_iter = hunks.iter().enumerate(); - for ((index, hunk), (expected_range, expected_str)) in diff_iter.zip(expected_hunks) { - assert_eq!(&hunk.buffer_range, expected_range, "for hunk {index}"); - assert_eq!( - &head_text[hunk.head_byte_range.clone()], - *expected_str, - "for hunk {index}" - ); - } + let expected_hunks: Vec<_> = expected_hunks + .iter() + .map(|(r, s, h)| (r.clone(), *s, h.to_string())) + .collect(); + + assert_eq!(actual_hunks, expected_hunks); } } diff --git a/crates/text/src/anchor.rs b/crates/text/src/anchor.rs index 9f70ae1cc7..ab0e1eeabc 100644 --- a/crates/text/src/anchor.rs +++ b/crates/text/src/anchor.rs @@ -26,7 +26,7 @@ impl Anchor { bias: Bias::Right, buffer_id: None, }; - + pub fn cmp(&self, other: &Anchor, buffer: &BufferSnapshot) -> Ordering { let fragment_id_comparison = if self.timestamp == other.timestamp { Ordering::Equal From 6540936970916c98d67e540f692e17615f902f80 Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 30 Sep 2022 13:51:54 -0400 Subject: [PATCH 099/314] Fix some panics in tests --- crates/git/src/repository.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs index 37b79fa10d..f834ebc219 100644 --- a/crates/git/src/repository.rs +++ b/crates/git/src/repository.rs @@ -175,11 +175,11 @@ impl GitRepository for FakeGitRepository { } async fn load_head_text(&self, _: &Path) -> Option { - unimplemented!() + None } fn reopen_git_repo(&mut self) -> bool { - unimplemented!() + false } fn git_repo(&self) -> Arc> { From ce7f6dd0829fb183e05708f15f93977d9e9c650c Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 30 Sep 2022 15:50:55 -0400 Subject: [PATCH 100/314] Start a test for remote git data updating Co-Authored-By: Mikayla Maki Co-Authored-By: Max Brunsfeld --- Cargo.lock | 3 + crates/collab/Cargo.toml | 5 +- crates/collab/src/integration_tests.rs | 138 +++++++++++++++++++++++++ crates/git/Cargo.toml | 5 +- crates/git/src/diff.rs | 75 +++++++------- crates/git/src/repository.rs | 26 ++++- crates/language/src/buffer.rs | 9 ++ crates/project/src/fs.rs | 54 +++++++++- crates/project/src/worktree.rs | 12 +-- crates/text/src/anchor.rs | 2 +- 10 files changed, 272 insertions(+), 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c87f336de..75dd5530c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1031,6 +1031,7 @@ dependencies = [ "env_logger", "envy", "futures", + "git", "gpui", "hyper", "language", @@ -1061,6 +1062,7 @@ dependencies = [ "tracing", "tracing-log", "tracing-subscriber", + "unindent", "util", "workspace", ] @@ -2232,6 +2234,7 @@ dependencies = [ "anyhow", "async-trait", "clock", + "collections", "git2", "lazy_static", "log", diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index 9b3603e6e4..47c86e0fe7 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -1,5 +1,5 @@ [package] -authors = ["Nathan Sobo "] +authors = ["Nathan Sobo "] default-run = "collab" edition = "2021" name = "collab" @@ -26,6 +26,7 @@ base64 = "0.13" clap = { version = "3.1", features = ["derive"], optional = true } envy = "0.4.2" futures = "0.3" +git = { path = "../git" } hyper = "0.14" lazy_static = "1.4" lipsum = { version = "0.8", optional = true } @@ -65,11 +66,13 @@ project = { path = "../project", features = ["test-support"] } settings = { path = "../settings", features = ["test-support"] } theme = { path = "../theme" } workspace = { path = "../workspace", features = ["test-support"] } +git = { path = "../git", features = ["test-support"] } ctor = "0.1" env_logger = "0.9" util = { path = "../util" } lazy_static = "1.4" serde_json = { version = "1.0", features = ["preserve_order"] } +unindent = "0.1" [features] seed-support = ["clap", "lipsum", "reqwest"] diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 3c9886dc16..586d988ef1 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -51,6 +51,7 @@ use std::{ time::Duration, }; use theme::ThemeRegistry; +use unindent::Unindent as _; use workspace::{Item, SplitDirection, ToggleFollow, Workspace}; #[ctor::ctor] @@ -946,6 +947,143 @@ async fn test_propagate_saves_and_fs_changes( .await; } +#[gpui::test(iterations = 10)] +async fn test_git_head_text( + executor: Arc, + cx_a: &mut TestAppContext, + cx_b: &mut TestAppContext, +) { + executor.forbid_parking(); + let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; + let client_a = server.create_client(cx_a, "user_a").await; + let client_b = server.create_client(cx_b, "user_b").await; + server + .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + .await; + + client_a + .fs + .insert_tree( + "/dir", + json!({ + ".git": {}, + "a.txt": " + one + two + three + ".unindent(), + }), + ) + .await; + + let head_text = " + one + three + " + .unindent(); + + let new_head_text = " + 1 + two + three + " + .unindent(); + + client_a + .fs + .as_fake() + .set_head_state_for_git_repository( + Path::new("/dir/.git"), + &[(Path::new("a.txt"), head_text.clone())], + ) + .await; + + let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; + let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; + + // Create the buffer + let buffer_a = project_a + .update(cx_a, |p, cx| p.open_buffer((worktree_id, "/dir/a.txt"), cx)) + .await + .unwrap(); + + // Wait for it to catch up to the new diff + buffer_a + .condition(cx_a, |buffer, _| !buffer.is_recalculating_git_diff()) + .await; + + // Smoke test diffing + buffer_a.read_with(cx_a, |buffer, _| { + assert_eq!(buffer.head_text(), Some(head_text.as_ref())); + git::diff::assert_hunks( + buffer.snapshot().git_diff_hunks_in_range(0..4), + &buffer, + &head_text, + &[(1..2, "", "two\n")], + ); + }); + + // Create remote buffer + let buffer_b = project_b + .update(cx_b, |p, cx| p.open_buffer((worktree_id, "/dir/a.txt"), cx)) + .await + .unwrap(); + + //TODO: WAIT FOR REMOTE UPDATES TO FINISH + + // Smoke test diffing + buffer_b.read_with(cx_b, |buffer, _| { + assert_eq!(buffer.head_text(), Some(head_text.as_ref())); + git::diff::assert_hunks( + buffer.snapshot().git_diff_hunks_in_range(0..4), + &buffer, + &head_text, + &[(1..2, "", "two\n")], + ); + }); + + // TODO: Create a dummy file event + client_a + .fs + .as_fake() + .set_head_state_for_git_repository( + Path::new("/dir/.git"), + &[(Path::new("a.txt"), new_head_text.clone())], + ) + .await; + + // TODO: Flush this file event + + // Wait for buffer_a to receive it + buffer_a + .condition(cx_a, |buffer, _| !buffer.is_recalculating_git_diff()) + .await; + + // Smoke test new diffing + buffer_a.read_with(cx_a, |buffer, _| { + assert_eq!(buffer.head_text(), Some(new_head_text.as_ref())); + git::diff::assert_hunks( + buffer.snapshot().git_diff_hunks_in_range(0..4), + &buffer, + &head_text, + &[(0..1, "1", "one\n")], + ); + }); + + //TODO: WAIT FOR REMOTE UPDATES TO FINISH on B + + // Smoke test B + buffer_b.read_with(cx_b, |buffer, _| { + assert_eq!(buffer.head_text(), Some(new_head_text.as_ref())); + git::diff::assert_hunks( + buffer.snapshot().git_diff_hunks_in_range(0..4), + &buffer, + &head_text, + &[(0..1, "1", "one\n")], + ); + }); +} + #[gpui::test(iterations = 10)] async fn test_fs_operations( executor: Arc, diff --git a/crates/git/Cargo.toml b/crates/git/Cargo.toml index 7ef9a953ba..744fdc8b99 100644 --- a/crates/git/Cargo.toml +++ b/crates/git/Cargo.toml @@ -13,12 +13,15 @@ git2 = { version = "0.15", default-features = false } lazy_static = "1.4.0" sum_tree = { path = "../sum_tree" } text = { path = "../text" } +collections = { path = "../collections" } util = { path = "../util" } log = { version = "0.4.16", features = ["kv_unstable_serde"] } smol = "1.2" parking_lot = "0.11.1" async-trait = "0.1" - [dev-dependencies] unindent = "0.1.7" + +[features] +test-support = [] diff --git a/crates/git/src/diff.rs b/crates/git/src/diff.rs index 4d12ca90d1..6c904d44d1 100644 --- a/crates/git/src/diff.rs +++ b/crates/git/src/diff.rs @@ -222,6 +222,40 @@ impl BufferDiff { } } +/// Range (crossing new lines), old, new +#[cfg(any(test, feature = "test-support"))] +#[track_caller] +pub fn assert_hunks( + diff_hunks: Iter, + buffer: &BufferSnapshot, + head_text: &str, + expected_hunks: &[(Range, &str, &str)], +) where + Iter: Iterator>, +{ + let actual_hunks = diff_hunks + .map(|hunk| { + ( + hunk.buffer_range.clone(), + &head_text[hunk.head_byte_range], + buffer + .text_for_range( + Point::new(hunk.buffer_range.start, 0) + ..Point::new(hunk.buffer_range.end, 0), + ) + .collect::(), + ) + }) + .collect::>(); + + let expected_hunks: Vec<_> = expected_hunks + .iter() + .map(|(r, s, h)| (r.clone(), *s, h.to_string())) + .collect(); + + assert_eq!(actual_hunks, expected_hunks); +} + #[cfg(test)] mod tests { use super::*; @@ -248,21 +282,19 @@ mod tests { let mut diff = BufferDiff::new(); smol::block_on(diff.update(&head_text, &buffer)); assert_hunks( - &diff, + diff.hunks(&buffer), &buffer, &head_text, &[(1..2, "two\n", "HELLO\n")], - None, ); buffer.edit([(0..0, "point five\n")]); smol::block_on(diff.update(&head_text, &buffer)); assert_hunks( - &diff, + diff.hunks(&buffer), &buffer, &head_text, &[(0..1, "", "point five\n"), (2..3, "two\n", "HELLO\n")], - None, ); } @@ -309,7 +341,7 @@ mod tests { assert_eq!(diff.hunks(&buffer).count(), 8); assert_hunks( - &diff, + diff.hunks_in_range(7..12, &buffer), &buffer, &head_text, &[ @@ -317,39 +349,6 @@ mod tests { (9..10, "six\n", "SIXTEEN\n"), (12..13, "", "WORLD\n"), ], - Some(7..12), ); } - - #[track_caller] - fn assert_hunks( - diff: &BufferDiff, - buffer: &BufferSnapshot, - head_text: &str, - expected_hunks: &[(Range, &str, &str)], - range: Option>, - ) { - let actual_hunks = diff - .hunks_in_range(range.unwrap_or(0..u32::MAX), buffer) - .map(|hunk| { - ( - hunk.buffer_range.clone(), - &head_text[hunk.head_byte_range], - buffer - .text_for_range( - Point::new(hunk.buffer_range.start, 0) - ..Point::new(hunk.buffer_range.end, 0), - ) - .collect::(), - ) - }) - .collect::>(); - - let expected_hunks: Vec<_> = expected_hunks - .iter() - .map(|(r, s, h)| (r.clone(), *s, h.to_string())) - .collect(); - - assert_eq!(actual_hunks, expected_hunks); - } } diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs index f834ebc219..fb43e44561 100644 --- a/crates/git/src/repository.rs +++ b/crates/git/src/repository.rs @@ -1,7 +1,11 @@ use anyhow::Result; +use collections::HashMap; use git2::Repository as LibGitRepository; use parking_lot::Mutex; -use std::{path::Path, sync::Arc}; +use std::{ + path::{Path, PathBuf}, + sync::Arc, +}; use util::ResultExt; #[async_trait::async_trait] @@ -140,14 +144,25 @@ pub struct FakeGitRepository { content_path: Arc, git_dir_path: Arc, scan_id: usize, + state: Arc>, +} + +#[derive(Debug, Clone, Default)] +pub struct FakeGitRepositoryState { + pub index_contents: HashMap, } impl FakeGitRepository { - pub fn open(dotgit_path: &Path, scan_id: usize) -> Box { + pub fn open( + dotgit_path: &Path, + scan_id: usize, + state: Arc>, + ) -> Box { Box::new(FakeGitRepository { content_path: dotgit_path.parent().unwrap().into(), git_dir_path: dotgit_path.into(), scan_id, + state, }) } } @@ -174,12 +189,13 @@ impl GitRepository for FakeGitRepository { self.scan_id } - async fn load_head_text(&self, _: &Path) -> Option { - None + async fn load_head_text(&self, path: &Path) -> Option { + let state = self.state.lock(); + state.index_contents.get(path).cloned() } fn reopen_git_repo(&mut self) -> bool { - false + true } fn git_repo(&self) -> Arc> { diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 0268f1cc68..831236ad5d 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -662,6 +662,11 @@ impl Buffer { task } + #[cfg(any(test, feature = "test-support"))] + pub fn head_text(&self) -> Option<&str> { + self.head_text.as_deref() + } + pub fn update_head_text(&mut self, head_text: Option, cx: &mut ModelContext) { self.head_text = head_text; self.git_diff_recalc(cx); @@ -671,6 +676,10 @@ impl Buffer { self.git_diff_status.diff.needs_update(self) } + pub fn is_recalculating_git_diff(&self) -> bool { + self.git_diff_status.update_in_progress + } + pub fn git_diff_recalc(&mut self, cx: &mut ModelContext) { if self.git_diff_status.update_in_progress { self.git_diff_status.update_requested = true; diff --git a/crates/project/src/fs.rs b/crates/project/src/fs.rs index c14edcd5e4..2b914ae373 100644 --- a/crates/project/src/fs.rs +++ b/crates/project/src/fs.rs @@ -1,7 +1,7 @@ use anyhow::{anyhow, Result}; use fsevent::EventStream; use futures::{future::BoxFuture, Stream, StreamExt}; -use git::repository::{GitRepository, RealGitRepository}; +use git::repository::{FakeGitRepositoryState, GitRepository, RealGitRepository}; use language::LineEnding; use smol::io::{AsyncReadExt, AsyncWriteExt}; use std::{ @@ -277,6 +277,7 @@ enum FakeFsEntry { inode: u64, mtime: SystemTime, entries: BTreeMap>>, + git_repo_state: Option>>, }, Symlink { target: PathBuf, @@ -391,6 +392,7 @@ impl FakeFs { inode: 0, mtime: SystemTime::now(), entries: Default::default(), + git_repo_state: None, })), next_inode: 1, event_txs: Default::default(), @@ -480,6 +482,31 @@ impl FakeFs { .boxed() } + pub async fn set_head_state_for_git_repository( + &self, + dot_git: &Path, + head_state: &[(&Path, String)], + ) { + let content_path = dot_git.parent().unwrap(); + let state = self.state.lock().await; + let entry = state.read_path(dot_git).await.unwrap(); + let mut entry = entry.lock().await; + + if let FakeFsEntry::Dir { git_repo_state, .. } = &mut *entry { + let repo_state = git_repo_state.get_or_insert_with(Default::default); + let mut repo_state = repo_state.lock(); + + repo_state.index_contents.clear(); + repo_state.index_contents.extend( + head_state + .iter() + .map(|(path, content)| (content_path.join(path), content.clone())), + ); + } else { + panic!("not a directory"); + } + } + pub async fn files(&self) -> Vec { let mut result = Vec::new(); let mut queue = collections::VecDeque::new(); @@ -569,6 +596,7 @@ impl Fs for FakeFs { inode, mtime: SystemTime::now(), entries: Default::default(), + git_repo_state: None, })) }); Ok(()) @@ -854,10 +882,26 @@ impl Fs for FakeFs { } fn open_repo(&self, abs_dot_git: &Path) -> Option> { - Some(git::repository::FakeGitRepository::open( - abs_dot_git.into(), - 0, - )) + let executor = self.executor.upgrade().unwrap(); + executor.block(async move { + let state = self.state.lock().await; + let entry = state.read_path(abs_dot_git).await.unwrap(); + let mut entry = entry.lock().await; + if let FakeFsEntry::Dir { git_repo_state, .. } = &mut *entry { + let state = git_repo_state + .get_or_insert_with(|| { + Arc::new(parking_lot::Mutex::new(FakeGitRepositoryState::default())) + }) + .clone(); + Some(git::repository::FakeGitRepository::open( + abs_dot_git.into(), + 0, + state, + )) + } else { + None + } + }) } fn is_fake(&self) -> bool { diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 0d2594475c..d3a5f710e0 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -3288,15 +3288,15 @@ mod tests { #[test] fn test_changed_repos() { let prev_repos: Vec> = vec![ - FakeGitRepository::open(Path::new("/.git"), 0), - FakeGitRepository::open(Path::new("/a/.git"), 0), - FakeGitRepository::open(Path::new("/a/b/.git"), 0), + FakeGitRepository::open(Path::new("/.git"), 0, Default::default()), + FakeGitRepository::open(Path::new("/a/.git"), 0, Default::default()), + FakeGitRepository::open(Path::new("/a/b/.git"), 0, Default::default()), ]; let new_repos: Vec> = vec![ - FakeGitRepository::open(Path::new("/a/.git"), 1), - FakeGitRepository::open(Path::new("/a/b/.git"), 0), - FakeGitRepository::open(Path::new("/a/c/.git"), 0), + FakeGitRepository::open(Path::new("/a/.git"), 1, Default::default()), + FakeGitRepository::open(Path::new("/a/b/.git"), 0, Default::default()), + FakeGitRepository::open(Path::new("/a/c/.git"), 0, Default::default()), ]; let res = LocalWorktree::changed_repos(&prev_repos, &new_repos); diff --git a/crates/text/src/anchor.rs b/crates/text/src/anchor.rs index ab0e1eeabc..9f70ae1cc7 100644 --- a/crates/text/src/anchor.rs +++ b/crates/text/src/anchor.rs @@ -26,7 +26,7 @@ impl Anchor { bias: Bias::Right, buffer_id: None, }; - + pub fn cmp(&self, other: &Anchor, buffer: &BufferSnapshot) -> Ordering { let fragment_id_comparison = if self.timestamp == other.timestamp { Ordering::Equal From 42b7820dbbd1095c4e5f66e6a74d984c7843dbfa Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 30 Sep 2022 18:05:09 -0400 Subject: [PATCH 101/314] Perform git diff on remote buffer open --- crates/project/src/project.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index a2a49c9c93..1e8567d4d4 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -5816,7 +5816,7 @@ impl Project { cx: &mut ModelContext, ) -> Task>> { let mut opened_buffer_rx = self.opened_buffer.1.clone(); - cx.spawn(|this, cx| async move { + cx.spawn(|this, mut cx| async move { let buffer = loop { let buffer = this.read_with(&cx, |this, cx| { this.opened_buffers @@ -5834,6 +5834,7 @@ impl Project { .await .ok_or_else(|| anyhow!("project dropped while waiting for buffer"))?; }; + buffer.update(&mut cx, |buffer, cx| buffer.git_diff_recalc(cx)); Ok(buffer) }) } From c95646a298d718096019a120b6f7e4ed890c63ce Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 30 Sep 2022 18:25:25 -0400 Subject: [PATCH 102/314] WIP Start refactoring separation of concerns for repo metadata Co-Authored-By: Max Brunsfeld Co-Authored-By: Mikayla Maki --- crates/collab/src/integration_tests.rs | 12 +- crates/git/src/repository.rs | 147 +++++-------------------- crates/language/src/buffer.rs | 4 - crates/project/src/fs.rs | 5 +- crates/project/src/worktree.rs | 53 +++++++-- 5 files changed, 73 insertions(+), 148 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 586d988ef1..d5a4c56b7d 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -1008,9 +1008,7 @@ async fn test_git_head_text( .unwrap(); // Wait for it to catch up to the new diff - buffer_a - .condition(cx_a, |buffer, _| !buffer.is_recalculating_git_diff()) - .await; + executor.run_until_parked(); // Smoke test diffing buffer_a.read_with(cx_a, |buffer, _| { @@ -1029,7 +1027,8 @@ async fn test_git_head_text( .await .unwrap(); - //TODO: WAIT FOR REMOTE UPDATES TO FINISH + // Wait remote buffer to catch up to the new diff + executor.run_until_parked(); // Smoke test diffing buffer_b.read_with(cx_b, |buffer, _| { @@ -1055,9 +1054,7 @@ async fn test_git_head_text( // TODO: Flush this file event // Wait for buffer_a to receive it - buffer_a - .condition(cx_a, |buffer, _| !buffer.is_recalculating_git_diff()) - .await; + executor.run_until_parked(); // Smoke test new diffing buffer_a.read_with(cx_a, |buffer, _| { @@ -1071,6 +1068,7 @@ async fn test_git_head_text( }); //TODO: WAIT FOR REMOTE UPDATES TO FINISH on B + executor.run_until_parked(); // Smoke test B buffer_b.read_with(cx_b, |buffer, _| { diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs index fb43e44561..ba8faa4b2b 100644 --- a/crates/git/src/repository.rs +++ b/crates/git/src/repository.rs @@ -2,88 +2,39 @@ use anyhow::Result; use collections::HashMap; use git2::Repository as LibGitRepository; use parking_lot::Mutex; -use std::{ - path::{Path, PathBuf}, - sync::Arc, -}; use util::ResultExt; +use std::{path::{Path, PathBuf}, sync::Arc}; #[async_trait::async_trait] -pub trait GitRepository: Send + Sync + std::fmt::Debug { - fn manages(&self, path: &Path) -> bool; +pub trait GitRepository: Send { + // fn manages(&self, path: &Path) -> bool; + // fn reopen_git_repo(&mut self) -> bool; + // fn git_repo(&self) -> Arc>; + // fn boxed_clone(&self) -> Box; - fn in_dot_git(&self, path: &Path) -> bool; - - fn content_path(&self) -> &Path; - - fn git_dir_path(&self) -> &Path; - - fn scan_id(&self) -> usize; - - fn set_scan_id(&mut self, scan_id: usize); - - fn reopen_git_repo(&mut self) -> bool; - - fn git_repo(&self) -> Arc>; - - fn boxed_clone(&self) -> Box; - - async fn load_head_text(&self, relative_file_path: &Path) -> Option; -} - -#[derive(Clone)] -pub struct RealGitRepository { - // Path to folder containing the .git file or directory - content_path: Arc, - // Path to the actual .git folder. - // Note: if .git is a file, this points to the folder indicated by the .git file - git_dir_path: Arc, - scan_id: usize, - libgit_repository: Arc>, -} - -impl RealGitRepository { - pub fn open(dotgit_path: &Path) -> Option> { + fn load_head_text(&self, relative_file_path: &Path) -> Option; + + fn open_real(dotgit_path: &Path) -> Option>> + where Self: Sized + { LibGitRepository::open(&dotgit_path) .log_err() - .and_then::, _>(|libgit_repository| { - Some(Box::new(Self { - content_path: libgit_repository.workdir()?.into(), - git_dir_path: dotgit_path.canonicalize().log_err()?.into(), - scan_id: 0, - libgit_repository: Arc::new(parking_lot::Mutex::new(libgit_repository)), - })) + .and_then::>, _>(|libgit_repository| { + Some(Arc::new(Mutex::new(libgit_repository))) }) } } #[async_trait::async_trait] -impl GitRepository for RealGitRepository { - fn manages(&self, path: &Path) -> bool { - path.canonicalize() - .map(|path| path.starts_with(&self.content_path)) - .unwrap_or(false) - } +impl GitRepository for LibGitRepository { + // fn manages(&self, path: &Path) -> bool { + // path.canonicalize() + // .map(|path| path.starts_with(&self.content_path)) + // .unwrap_or(false) + // } - fn in_dot_git(&self, path: &Path) -> bool { - path.canonicalize() - .map(|path| path.starts_with(&self.git_dir_path)) - .unwrap_or(false) - } - fn content_path(&self) -> &Path { - self.content_path.as_ref() - } - - fn git_dir_path(&self) -> &Path { - self.git_dir_path.as_ref() - } - - fn scan_id(&self) -> usize { - self.scan_id - } - - async fn load_head_text(&self, relative_file_path: &Path) -> Option { + fn load_head_text(&self, relative_file_path: &Path) -> Option { fn logic(repo: &LibGitRepository, relative_file_path: &Path) -> Result> { const STAGE_NORMAL: i32 = 0; let index = repo.index()?; @@ -97,53 +48,18 @@ impl GitRepository for RealGitRepository { Ok(Some(head_text)) } - match logic(&self.libgit_repository.as_ref().lock(), relative_file_path) { + match logic(&self, relative_file_path) { Ok(value) => return value, Err(err) => log::error!("Error loading head text: {:?}", err), } None } - - fn reopen_git_repo(&mut self) -> bool { - match LibGitRepository::open(&self.git_dir_path) { - Ok(repo) => { - self.libgit_repository = Arc::new(Mutex::new(repo)); - true - } - - Err(_) => false, - } - } - - fn git_repo(&self) -> Arc> { - self.libgit_repository.clone() - } - - fn set_scan_id(&mut self, scan_id: usize) { - self.scan_id = scan_id; - } - - fn boxed_clone(&self) -> Box { - Box::new(self.clone()) - } -} - -impl std::fmt::Debug for RealGitRepository { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("GitRepository") - .field("content_path", &self.content_path) - .field("git_dir_path", &self.git_dir_path) - .field("scan_id", &self.scan_id) - .field("libgit_repository", &"LibGitRepository") - .finish() - } } #[derive(Debug, Clone)] pub struct FakeGitRepository { content_path: Arc, git_dir_path: Arc, - scan_id: usize, state: Arc>, } @@ -153,15 +69,10 @@ pub struct FakeGitRepositoryState { } impl FakeGitRepository { - pub fn open( - dotgit_path: &Path, - scan_id: usize, - state: Arc>, - ) -> Box { + pub fn open(dotgit_path: &Path, state: Arc>) -> Box { Box::new(FakeGitRepository { content_path: dotgit_path.parent().unwrap().into(), git_dir_path: dotgit_path.into(), - scan_id, state, }) } @@ -173,9 +84,9 @@ impl GitRepository for FakeGitRepository { path.starts_with(self.content_path()) } - fn in_dot_git(&self, path: &Path) -> bool { - path.starts_with(self.git_dir_path()) - } + // fn in_dot_git(&self, path: &Path) -> bool { + // path.starts_with(self.git_dir_path()) + // } fn content_path(&self) -> &Path { &self.content_path @@ -185,10 +96,6 @@ impl GitRepository for FakeGitRepository { &self.git_dir_path } - fn scan_id(&self) -> usize { - self.scan_id - } - async fn load_head_text(&self, path: &Path) -> Option { let state = self.state.lock(); state.index_contents.get(path).cloned() @@ -202,10 +109,6 @@ impl GitRepository for FakeGitRepository { unimplemented!() } - fn set_scan_id(&mut self, scan_id: usize) { - self.scan_id = scan_id; - } - fn boxed_clone(&self) -> Box { Box::new(self.clone()) } diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 831236ad5d..22706ab1b5 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -676,10 +676,6 @@ impl Buffer { self.git_diff_status.diff.needs_update(self) } - pub fn is_recalculating_git_diff(&self) -> bool { - self.git_diff_status.update_in_progress - } - pub fn git_diff_recalc(&mut self, cx: &mut ModelContext) { if self.git_diff_status.update_in_progress { self.git_diff_status.update_requested = true; diff --git a/crates/project/src/fs.rs b/crates/project/src/fs.rs index 2b914ae373..1280fcb8bc 100644 --- a/crates/project/src/fs.rs +++ b/crates/project/src/fs.rs @@ -1,7 +1,7 @@ use anyhow::{anyhow, Result}; use fsevent::EventStream; use futures::{future::BoxFuture, Stream, StreamExt}; -use git::repository::{FakeGitRepositoryState, GitRepository, RealGitRepository}; +use git::repository::{FakeGitRepositoryState, GitRepository, Git2Repo}; use language::LineEnding; use smol::io::{AsyncReadExt, AsyncWriteExt}; use std::{ @@ -239,7 +239,7 @@ impl Fs for RealFs { } fn open_repo(&self, abs_dot_git: &Path) -> Option> { - RealGitRepository::open(&abs_dot_git) + Git2Repo::open(&abs_dot_git) } fn is_fake(&self) -> bool { @@ -895,7 +895,6 @@ impl Fs for FakeFs { .clone(); Some(git::repository::FakeGitRepository::open( abs_dot_git.into(), - 0, state, )) } else { diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index d3a5f710e0..4f14ab6ad1 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -97,10 +97,39 @@ pub struct Snapshot { is_complete: bool, } +#[derive(Clone)] +struct GitRepositoryEntry { + repo: Arc>, + + // repo: Box, + scan_id: usize, + // Path to folder containing the .git file or directory + content_path: Arc, + // Path to the actual .git folder. + // Note: if .git is a file, this points to the folder indicated by the .git file + git_dir_path: Arc, +} + +impl std::fmt::Debug for GitRepositoryEntry { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("GitRepositoryEntry") + .field("content_path", &self.content_path) + .field("git_dir_path", &self.git_dir_path) + .field("libgit_repository", &"LibGitRepository") + .finish() + } +} + +// impl Clone for GitRepositoryEntry { +// fn clone(&self) -> Self { +// GitRepositoryEntry { repo: self.repo.boxed_clone(), scan_id: self.scan_id } +// } +// } + pub struct LocalSnapshot { abs_path: Arc, ignores_by_parent_abs_path: HashMap, (Arc, usize)>, - git_repositories: Vec>, + git_repositories: Vec, removed_entry_ids: HashMap, next_entry_id: Arc, snapshot: Snapshot, @@ -115,7 +144,7 @@ impl Clone for LocalSnapshot { git_repositories: self .git_repositories .iter() - .map(|repo| repo.boxed_clone()) + .cloned() .collect(), removed_entry_ids: self.removed_entry_ids.clone(), next_entry_id: self.next_entry_id.clone(), @@ -3287,17 +3316,17 @@ mod tests { #[test] fn test_changed_repos() { - let prev_repos: Vec> = vec![ - FakeGitRepository::open(Path::new("/.git"), 0, Default::default()), - FakeGitRepository::open(Path::new("/a/.git"), 0, Default::default()), - FakeGitRepository::open(Path::new("/a/b/.git"), 0, Default::default()), - ]; + // let prev_repos: Vec> = vec![ + // FakeGitRepository::open(Path::new("/.git"), 0, Default::default()), + // FakeGitRepository::open(Path::new("/a/.git"), 0, Default::default()), + // FakeGitRepository::open(Path::new("/a/b/.git"), 0, Default::default()), + // ]; - let new_repos: Vec> = vec![ - FakeGitRepository::open(Path::new("/a/.git"), 1, Default::default()), - FakeGitRepository::open(Path::new("/a/b/.git"), 0, Default::default()), - FakeGitRepository::open(Path::new("/a/c/.git"), 0, Default::default()), - ]; + // let new_repos: Vec> = vec![ + // FakeGitRepository::open(Path::new("/a/.git"), 1, Default::default()), + // FakeGitRepository::open(Path::new("/a/b/.git"), 0, Default::default()), + // FakeGitRepository::open(Path::new("/a/c/.git"), 0, Default::default()), + // ]; let res = LocalWorktree::changed_repos(&prev_repos, &new_repos); From af0974264cd61f21ae46f8b0cf8eee3025314d5b Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 30 Sep 2022 17:33:34 -0700 Subject: [PATCH 103/314] Refactored git repository code to seperate out repository entry tracking data and git2 mocking code. Co-authored-by: Max Co-authored-by: Julia --- Cargo.lock | 1 + crates/collab/src/integration_tests.rs | 12 +- crates/git/Cargo.toml | 1 + crates/git/src/repository.rs | 73 ++-------- crates/project/src/fs.rs | 29 ++-- crates/project/src/project.rs | 11 +- crates/project/src/worktree.rs | 191 +++++++++++++------------ 7 files changed, 143 insertions(+), 175 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 75dd5530c9..fa8f8acbdc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2235,6 +2235,7 @@ dependencies = [ "async-trait", "clock", "collections", + "futures", "git2", "lazy_static", "log", diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index d5a4c56b7d..168231a6b4 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -966,7 +966,8 @@ async fn test_git_head_text( .insert_tree( "/dir", json!({ - ".git": {}, + ".git": { + }, "a.txt": " one two @@ -983,9 +984,8 @@ async fn test_git_head_text( .unindent(); let new_head_text = " - 1 + one two - three " .unindent(); @@ -1041,7 +1041,6 @@ async fn test_git_head_text( ); }); - // TODO: Create a dummy file event client_a .fs .as_fake() @@ -1051,19 +1050,18 @@ async fn test_git_head_text( ) .await; - // TODO: Flush this file event - // Wait for buffer_a to receive it executor.run_until_parked(); // Smoke test new diffing buffer_a.read_with(cx_a, |buffer, _| { assert_eq!(buffer.head_text(), Some(new_head_text.as_ref())); + git::diff::assert_hunks( buffer.snapshot().git_diff_hunks_in_range(0..4), &buffer, &head_text, - &[(0..1, "1", "one\n")], + &[(2..3, "", "three\n")], ); }); diff --git a/crates/git/Cargo.toml b/crates/git/Cargo.toml index 744fdc8b99..b8f3aac0b9 100644 --- a/crates/git/Cargo.toml +++ b/crates/git/Cargo.toml @@ -19,6 +19,7 @@ log = { version = "0.4.16", features = ["kv_unstable_serde"] } smol = "1.2" parking_lot = "0.11.1" async-trait = "0.1" +futures = "0.3" [dev-dependencies] unindent = "0.1.7" diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs index ba8faa4b2b..a49a1e0b60 100644 --- a/crates/git/src/repository.rs +++ b/crates/git/src/repository.rs @@ -1,39 +1,20 @@ use anyhow::Result; use collections::HashMap; -use git2::Repository as LibGitRepository; use parking_lot::Mutex; -use util::ResultExt; -use std::{path::{Path, PathBuf}, sync::Arc}; +use std::{ + path::{Path, PathBuf}, + sync::Arc, +}; + +pub use git2::Repository as LibGitRepository; #[async_trait::async_trait] pub trait GitRepository: Send { - // fn manages(&self, path: &Path) -> bool; - // fn reopen_git_repo(&mut self) -> bool; - // fn git_repo(&self) -> Arc>; - // fn boxed_clone(&self) -> Box; - fn load_head_text(&self, relative_file_path: &Path) -> Option; - - fn open_real(dotgit_path: &Path) -> Option>> - where Self: Sized - { - LibGitRepository::open(&dotgit_path) - .log_err() - .and_then::>, _>(|libgit_repository| { - Some(Arc::new(Mutex::new(libgit_repository))) - }) - } } #[async_trait::async_trait] impl GitRepository for LibGitRepository { - // fn manages(&self, path: &Path) -> bool { - // path.canonicalize() - // .map(|path| path.starts_with(&self.content_path)) - // .unwrap_or(false) - // } - - fn load_head_text(&self, relative_file_path: &Path) -> Option { fn logic(repo: &LibGitRepository, relative_file_path: &Path) -> Result> { const STAGE_NORMAL: i32 = 0; @@ -56,10 +37,8 @@ impl GitRepository for LibGitRepository { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct FakeGitRepository { - content_path: Arc, - git_dir_path: Arc, state: Arc>, } @@ -69,47 +48,15 @@ pub struct FakeGitRepositoryState { } impl FakeGitRepository { - pub fn open(dotgit_path: &Path, state: Arc>) -> Box { - Box::new(FakeGitRepository { - content_path: dotgit_path.parent().unwrap().into(), - git_dir_path: dotgit_path.into(), - state, - }) + pub fn open(state: Arc>) -> Arc> { + Arc::new(Mutex::new(FakeGitRepository { state })) } } #[async_trait::async_trait] impl GitRepository for FakeGitRepository { - fn manages(&self, path: &Path) -> bool { - path.starts_with(self.content_path()) - } - - // fn in_dot_git(&self, path: &Path) -> bool { - // path.starts_with(self.git_dir_path()) - // } - - fn content_path(&self) -> &Path { - &self.content_path - } - - fn git_dir_path(&self) -> &Path { - &self.git_dir_path - } - - async fn load_head_text(&self, path: &Path) -> Option { + fn load_head_text(&self, path: &Path) -> Option { let state = self.state.lock(); state.index_contents.get(path).cloned() } - - fn reopen_git_repo(&mut self) -> bool { - true - } - - fn git_repo(&self) -> Arc> { - unimplemented!() - } - - fn boxed_clone(&self) -> Box { - Box::new(self.clone()) - } } diff --git a/crates/project/src/fs.rs b/crates/project/src/fs.rs index 1280fcb8bc..d0e549c0b5 100644 --- a/crates/project/src/fs.rs +++ b/crates/project/src/fs.rs @@ -1,8 +1,9 @@ use anyhow::{anyhow, Result}; use fsevent::EventStream; use futures::{future::BoxFuture, Stream, StreamExt}; -use git::repository::{FakeGitRepositoryState, GitRepository, Git2Repo}; +use git::repository::{FakeGitRepositoryState, GitRepository, LibGitRepository}; use language::LineEnding; +use parking_lot::Mutex as SyncMutex; use smol::io::{AsyncReadExt, AsyncWriteExt}; use std::{ io, @@ -11,6 +12,7 @@ use std::{ pin::Pin, time::{Duration, SystemTime}, }; +use util::ResultExt; use text::Rope; @@ -44,7 +46,7 @@ pub trait Fs: Send + Sync { path: &Path, latency: Duration, ) -> Pin>>>; - fn open_repo(&self, abs_dot_git: &Path) -> Option>; + fn open_repo(&self, abs_dot_git: &Path) -> Option>>; fn is_fake(&self) -> bool; #[cfg(any(test, feature = "test-support"))] fn as_fake(&self) -> &FakeFs; @@ -238,8 +240,12 @@ impl Fs for RealFs { }))) } - fn open_repo(&self, abs_dot_git: &Path) -> Option> { - Git2Repo::open(&abs_dot_git) + fn open_repo(&self, dotgit_path: &Path) -> Option>> { + LibGitRepository::open(&dotgit_path) + .log_err() + .and_then::>, _>(|libgit_repository| { + Some(Arc::new(SyncMutex::new(libgit_repository))) + }) } fn is_fake(&self) -> bool { @@ -277,7 +283,7 @@ enum FakeFsEntry { inode: u64, mtime: SystemTime, entries: BTreeMap>>, - git_repo_state: Option>>, + git_repo_state: Option>>, }, Symlink { target: PathBuf, @@ -488,7 +494,7 @@ impl FakeFs { head_state: &[(&Path, String)], ) { let content_path = dot_git.parent().unwrap(); - let state = self.state.lock().await; + let mut state = self.state.lock().await; let entry = state.read_path(dot_git).await.unwrap(); let mut entry = entry.lock().await; @@ -502,6 +508,8 @@ impl FakeFs { .iter() .map(|(path, content)| (content_path.join(path), content.clone())), ); + + state.emit_event([dot_git]); } else { panic!("not a directory"); } @@ -881,7 +889,7 @@ impl Fs for FakeFs { })) } - fn open_repo(&self, abs_dot_git: &Path) -> Option> { + fn open_repo(&self, abs_dot_git: &Path) -> Option>> { let executor = self.executor.upgrade().unwrap(); executor.block(async move { let state = self.state.lock().await; @@ -890,13 +898,10 @@ impl Fs for FakeFs { if let FakeFsEntry::Dir { git_repo_state, .. } = &mut *entry { let state = git_repo_state .get_or_insert_with(|| { - Arc::new(parking_lot::Mutex::new(FakeGitRepositoryState::default())) + Arc::new(SyncMutex::new(FakeGitRepositoryState::default())) }) .clone(); - Some(git::repository::FakeGitRepository::open( - abs_dot_git.into(), - state, - )) + Some(git::repository::FakeGitRepository::open(state)) } else { None } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 1e8567d4d4..f1aa98c4e0 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -12,7 +12,7 @@ use client::{proto, Client, PeerId, TypedEnvelope, User, UserStore}; use clock::ReplicaId; use collections::{hash_map, BTreeMap, HashMap, HashSet}; use futures::{future::Shared, AsyncWriteExt, Future, FutureExt, StreamExt, TryFutureExt}; -use git::repository::GitRepository; + use gpui::{ AnyModelHandle, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task, UpgradeModelHandle, WeakModelHandle, @@ -4648,7 +4648,7 @@ impl Project { fn update_local_worktree_buffers_git_repos( &mut self, - repos: &[Box], + repos: &[GitRepositoryEntry], cx: &mut ModelContext, ) { //TODO: Produce protos @@ -4663,12 +4663,15 @@ impl Project { let abs_path = file.abs_path(cx); let repo = match repos.iter().find(|repo| repo.manages(&abs_path)) { - Some(repo) => repo.boxed_clone(), + Some(repo) => repo.clone(), None => return, }; cx.spawn(|_, mut cx| async move { - let head_text = repo.load_head_text(&path).await; + let head_text = cx + .background() + .spawn(async move { repo.repo.lock().load_head_text(&path) }) + .await; buffer.update(&mut cx, |buffer, cx| { buffer.update_head_text(head_text, cx); }); diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 4f14ab6ad1..560f23d147 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -98,16 +98,15 @@ pub struct Snapshot { } #[derive(Clone)] -struct GitRepositoryEntry { - repo: Arc>, - - // repo: Box, - scan_id: usize, +pub struct GitRepositoryEntry { + pub(crate) repo: Arc>, + + pub(crate) scan_id: usize, // Path to folder containing the .git file or directory - content_path: Arc, + pub(crate) content_path: Arc, // Path to the actual .git folder. // Note: if .git is a file, this points to the folder indicated by the .git file - git_dir_path: Arc, + pub(crate) git_dir_path: Arc, } impl std::fmt::Debug for GitRepositoryEntry { @@ -141,11 +140,7 @@ impl Clone for LocalSnapshot { Self { abs_path: self.abs_path.clone(), ignores_by_parent_abs_path: self.ignores_by_parent_abs_path.clone(), - git_repositories: self - .git_repositories - .iter() - .cloned() - .collect(), + git_repositories: self.git_repositories.iter().cloned().collect(), removed_entry_ids: self.removed_entry_ids.clone(), next_entry_id: self.next_entry_id.clone(), snapshot: self.snapshot.clone(), @@ -186,7 +181,7 @@ struct ShareState { pub enum Event { UpdatedEntries, - UpdatedGitRepositories(Vec>), + UpdatedGitRepositories(Vec), } impl Entity for Worktree { @@ -610,27 +605,26 @@ impl LocalWorktree { } fn changed_repos( - old_repos: &[Box], - new_repos: &[Box], - ) -> Vec> { + old_repos: &[GitRepositoryEntry], + new_repos: &[GitRepositoryEntry], + ) -> Vec { fn diff<'a>( - a: &'a [Box], - b: &'a [Box], - updated: &mut HashMap<&'a Path, Box>, + a: &'a [GitRepositoryEntry], + b: &'a [GitRepositoryEntry], + updated: &mut HashMap<&'a Path, GitRepositoryEntry>, ) { for a_repo in a { let matched = b.iter().find(|b_repo| { - a_repo.git_dir_path() == b_repo.git_dir_path() - && a_repo.scan_id() == b_repo.scan_id() + a_repo.git_dir_path == b_repo.git_dir_path && a_repo.scan_id == b_repo.scan_id }); if matched.is_none() { - updated.insert(a_repo.git_dir_path(), a_repo.boxed_clone()); + updated.insert(a_repo.git_dir_path.as_ref(), a_repo.clone()); } } } - let mut updated = HashMap::<&Path, Box>::default(); + let mut updated = HashMap::<&Path, GitRepositoryEntry>::default(); diff(old_repos, new_repos, &mut updated); diff(new_repos, old_repos, &mut updated); @@ -690,7 +684,12 @@ impl LocalWorktree { settings::GitFilesIncluded::All | settings::GitFilesIncluded::OnlyTracked ) { let results = if let Some(repo) = snapshot.repo_for(&abs_path) { - repo.load_head_text(&path).await + cx.background() + .spawn({ + let path = path.clone(); + async move { repo.repo.lock().load_head_text(&path) } + }) + .await } else { None }; @@ -1390,25 +1389,19 @@ impl LocalSnapshot { } // Gives the most specific git repository for a given path - pub(crate) fn repo_for(&self, path: &Path) -> Option> { + pub(crate) fn repo_for(&self, path: &Path) -> Option { self.git_repositories .iter() .rev() //git_repository is ordered lexicographically - .find(|repo| repo.manages(&self.abs_path.join(path))) - .map(|repo| repo.boxed_clone()) + .find(|repo| repo.manages(path)) + .cloned() } - pub(crate) fn in_dot_git(&mut self, path: &Path) -> Option<&mut Box> { + pub(crate) fn in_dot_git(&mut self, path: &Path) -> Option<&mut GitRepositoryEntry> { + // Git repositories cannot be nested, so we don't need to reverse the order self.git_repositories .iter_mut() - .rev() //git_repository is ordered lexicographically - .find(|repo| repo.in_dot_git(&self.abs_path.join(path))) - } - - pub(crate) fn _tracks_filepath(&self, repo: &dyn GitRepository, file_path: &Path) -> bool { - // Depends on git_repository_for_file_path returning the most specific git repository for a given path - self.repo_for(&self.abs_path.join(file_path)) - .map_or(false, |r| r.git_dir_path() == repo.git_dir_path()) + .find(|repo| repo.in_dot_git(path)) } #[cfg(test)] @@ -1575,12 +1568,21 @@ impl LocalSnapshot { if parent_path.file_name() == Some(&DOT_GIT) { let abs_path = self.abs_path.join(&parent_path); + let content_path: Arc = parent_path.parent().unwrap().into(); if let Err(ix) = self .git_repositories - .binary_search_by_key(&abs_path.as_path(), |repo| repo.git_dir_path()) + .binary_search_by_key(&&content_path, |repo| &repo.content_path) { - if let Some(repository) = fs.open_repo(abs_path.as_path()) { - self.git_repositories.insert(ix, repository); + if let Some(repo) = fs.open_repo(abs_path.as_path()) { + self.git_repositories.insert( + ix, + GitRepositoryEntry { + repo, + scan_id: 0, + content_path, + git_dir_path: parent_path, + }, + ); } } } @@ -1673,9 +1675,9 @@ impl LocalSnapshot { let parent_path = path.parent().unwrap(); if let Ok(ix) = self .git_repositories - .binary_search_by_key(&parent_path, |repo| repo.content_path().as_ref()) + .binary_search_by_key(&parent_path, |repo| repo.git_dir_path.as_ref()) { - self.git_repositories[ix].set_scan_id(self.snapshot.scan_id); + self.git_repositories[ix].scan_id = self.snapshot.scan_id; } } } @@ -1716,6 +1718,25 @@ impl LocalSnapshot { ignore_stack } + + pub fn git_repo_entries(&self) -> &[GitRepositoryEntry] { + &self.git_repositories + } +} +// Worktree root +// | +// git_dir_path: c/d/.git +//in_dot_git Query: c/d/.git/HEAD +// Manages Query: c/d/e/f/a.txt + +impl GitRepositoryEntry { + pub(crate) fn manages(&self, path: &Path) -> bool { + path.starts_with(self.content_path.as_ref()) + } + + pub(crate) fn in_dot_git(&self, path: &Path) -> bool { + path.starts_with(self.git_dir_path.as_ref()) + } } async fn build_gitignore(abs_path: &Path, fs: &dyn Fs) -> Result { @@ -2509,8 +2530,8 @@ impl BackgroundScanner { snapshot.insert_entry(fs_entry, self.fs.as_ref()); let scan_id = snapshot.scan_id; - if let Some(repo) = snapshot.in_dot_git(&abs_path) { - repo.set_scan_id(scan_id); + if let Some(repo) = snapshot.in_dot_git(&path) { + repo.scan_id = scan_id; } let mut ancestor_inodes = snapshot.ancestor_inodes_for_path(&path); @@ -2625,19 +2646,21 @@ impl BackgroundScanner { .await; } + // TODO: Clarify what is going on here because re-loading every git repository + // on every file system event seems wrong async fn update_git_repositories(&self) { let mut snapshot = self.snapshot.lock(); let new_repos = snapshot .git_repositories .iter() - .map(|repo| repo.boxed_clone()) - .filter_map(|mut repo| { - if repo.reopen_git_repo() { - Some(repo) - } else { - None - } + .cloned() + .filter_map(|mut repo_entry| { + let repo = self + .fs + .open_repo(&snapshot.abs_path.join(&repo_entry.git_dir_path))?; + repo_entry.repo = repo; + Some(repo_entry) }) .collect(); @@ -3262,34 +3285,17 @@ mod tests { assert!(tree.repo_for("c.txt".as_ref()).is_none()); let repo = tree.repo_for("dir1/src/b.txt".as_ref()).unwrap(); - - assert_eq!( - repo.content_path(), - root.path().join("dir1").canonicalize().unwrap() - ); - assert_eq!( - repo.git_dir_path(), - root.path().join("dir1/.git").canonicalize().unwrap() - ); + assert_eq!(repo.content_path.as_ref(), Path::new("dir1")); + assert_eq!(repo.git_dir_path.as_ref(), Path::new("dir1/.git")); let repo = tree.repo_for("dir1/deps/dep1/src/a.txt".as_ref()).unwrap(); - - assert_eq!( - repo.content_path(), - root.path().join("dir1/deps/dep1").canonicalize().unwrap() - ); - assert_eq!( - repo.git_dir_path(), - root.path() - .join("dir1/deps/dep1/.git") - .canonicalize() - .unwrap() - ); + assert_eq!(repo.content_path.as_ref(), Path::new("dir1/deps/dep1")); + assert_eq!(repo.git_dir_path.as_ref(), Path::new("dir1/deps/dep1/.git"),); }); let original_scan_id = tree.read_with(cx, |tree, _cx| { let tree = tree.as_local().unwrap(); - tree.repo_for("dir1/src/b.txt".as_ref()).unwrap().scan_id() + tree.repo_for("dir1/src/b.txt".as_ref()).unwrap().scan_id }); std::fs::write(root.path().join("dir1/.git/random_new_file"), "hello").unwrap(); @@ -3297,7 +3303,7 @@ mod tests { tree.read_with(cx, |tree, _cx| { let tree = tree.as_local().unwrap(); - let new_scan_id = tree.repo_for("dir1/src/b.txt".as_ref()).unwrap().scan_id(); + let new_scan_id = tree.repo_for("dir1/src/b.txt".as_ref()).unwrap().scan_id; assert_ne!( original_scan_id, new_scan_id, "original {original_scan_id}, new {new_scan_id}" @@ -3316,44 +3322,51 @@ mod tests { #[test] fn test_changed_repos() { - // let prev_repos: Vec> = vec![ - // FakeGitRepository::open(Path::new("/.git"), 0, Default::default()), - // FakeGitRepository::open(Path::new("/a/.git"), 0, Default::default()), - // FakeGitRepository::open(Path::new("/a/b/.git"), 0, Default::default()), - // ]; + fn fake_entry(git_dir_path: impl AsRef, scan_id: usize) -> GitRepositoryEntry { + GitRepositoryEntry { + repo: Arc::new(Mutex::new(FakeGitRepository::default())), + scan_id, + content_path: git_dir_path.as_ref().parent().unwrap().into(), + git_dir_path: git_dir_path.as_ref().into(), + } + } - // let new_repos: Vec> = vec![ - // FakeGitRepository::open(Path::new("/a/.git"), 1, Default::default()), - // FakeGitRepository::open(Path::new("/a/b/.git"), 0, Default::default()), - // FakeGitRepository::open(Path::new("/a/c/.git"), 0, Default::default()), - // ]; + let prev_repos: Vec = vec![ + fake_entry("/.git", 0), + fake_entry("/a/.git", 0), + fake_entry("/a/b/.git", 0), + ]; + + let new_repos: Vec = vec![ + fake_entry("/a/.git", 1), + fake_entry("/a/b/.git", 0), + fake_entry("/a/c/.git", 0), + ]; let res = LocalWorktree::changed_repos(&prev_repos, &new_repos); - dbg!(&res); - // Deletion retained assert!(res .iter() - .find(|repo| repo.git_dir_path() == Path::new("/.git") && repo.scan_id() == 0) + .find(|repo| repo.git_dir_path.as_ref() == Path::new("/.git") && repo.scan_id == 0) .is_some()); // Update retained assert!(res .iter() - .find(|repo| repo.git_dir_path() == Path::new("/a/.git") && repo.scan_id() == 1) + .find(|repo| repo.git_dir_path.as_ref() == Path::new("/a/.git") && repo.scan_id == 1) .is_some()); // Addition retained assert!(res .iter() - .find(|repo| repo.git_dir_path() == Path::new("/a/c/.git") && repo.scan_id() == 0) + .find(|repo| repo.git_dir_path.as_ref() == Path::new("/a/c/.git") && repo.scan_id == 0) .is_some()); // Nochange, not retained assert!(res .iter() - .find(|repo| repo.git_dir_path() == Path::new("/a/b/.git") && repo.scan_id() == 0) + .find(|repo| repo.git_dir_path.as_ref() == Path::new("/a/b/.git") && repo.scan_id == 0) .is_none()); } From a1299d9b68b2b6bf3ec01b311daabf2f54d6b1e5 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 30 Sep 2022 17:34:14 -0700 Subject: [PATCH 104/314] Fixed 1 test --- crates/collab/src/integration_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 168231a6b4..eb3fbc3dc8 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -1075,7 +1075,7 @@ async fn test_git_head_text( buffer.snapshot().git_diff_hunks_in_range(0..4), &buffer, &head_text, - &[(0..1, "1", "one\n")], + &[(2..3, "", "three\n")], ); }); } From 8c24c858c9efee31e0acb55ef61ceb7fe4f0cd43 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 30 Sep 2022 17:36:22 -0700 Subject: [PATCH 105/314] Touched up comments --- crates/project/src/worktree.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 560f23d147..40efeee1d1 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -119,12 +119,6 @@ impl std::fmt::Debug for GitRepositoryEntry { } } -// impl Clone for GitRepositoryEntry { -// fn clone(&self) -> Self { -// GitRepositoryEntry { repo: self.repo.boxed_clone(), scan_id: self.scan_id } -// } -// } - pub struct LocalSnapshot { abs_path: Arc, ignores_by_parent_abs_path: HashMap, (Arc, usize)>, @@ -1723,17 +1717,14 @@ impl LocalSnapshot { &self.git_repositories } } -// Worktree root -// | -// git_dir_path: c/d/.git -//in_dot_git Query: c/d/.git/HEAD -// Manages Query: c/d/e/f/a.txt impl GitRepositoryEntry { + // Note that these paths should be relative to the worktree root. pub(crate) fn manages(&self, path: &Path) -> bool { path.starts_with(self.content_path.as_ref()) } + // Note that theis path should be relative to the worktree root. pub(crate) fn in_dot_git(&self, path: &Path) -> bool { path.starts_with(self.git_dir_path.as_ref()) } From 512f817e2f50a5917f954971c96ee763ae16b33d Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Sat, 1 Oct 2022 18:18:35 -0700 Subject: [PATCH 106/314] Added proto messages for updating the head text --- crates/collab/src/integration_tests.rs | 3 -- crates/collab/src/rpc.rs | 18 +++++++++- crates/project/src/project.rs | 46 ++++++++++++++++++++++++-- crates/rpc/proto/zed.proto | 7 ++++ crates/rpc/src/proto.rs | 2 ++ 5 files changed, 69 insertions(+), 7 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index eb3fbc3dc8..422c9fd0bb 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -1065,9 +1065,6 @@ async fn test_git_head_text( ); }); - //TODO: WAIT FOR REMOTE UPDATES TO FINISH on B - executor.run_until_parked(); - // Smoke test B buffer_b.read_with(cx_b, |buffer, _| { assert_eq!(buffer.head_text(), Some(new_head_text.as_ref())); diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 5f27352c5a..318555b7ed 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -205,7 +205,8 @@ impl Server { .add_request_handler(Server::follow) .add_message_handler(Server::unfollow) .add_message_handler(Server::update_followers) - .add_request_handler(Server::get_channel_messages); + .add_request_handler(Server::get_channel_messages) + .add_message_handler(Server::update_head_text); Arc::new(server) } @@ -1727,6 +1728,21 @@ impl Server { Ok(()) } + async fn update_head_text( + self: Arc, + request: TypedEnvelope, + ) -> Result<()> { + let receiver_ids = self.store().await.project_connection_ids( + ProjectId::from_proto(request.payload.project_id), + request.sender_id, + )?; + broadcast(request.sender_id, receiver_ids, |connection_id| { + self.peer + .forward_send(request.sender_id, connection_id, request.payload.clone()) + }); + Ok(()) + } + pub(crate) async fn store(&self) -> StoreGuard<'_> { #[cfg(test)] tokio::task::yield_now().await; diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index f1aa98c4e0..1064d05fe9 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -8,7 +8,10 @@ pub mod worktree; mod project_tests; use anyhow::{anyhow, Context, Result}; -use client::{proto, Client, PeerId, TypedEnvelope, User, UserStore}; +use client::{ + proto::{self}, + Client, PeerId, TypedEnvelope, User, UserStore, +}; use clock::ReplicaId; use collections::{hash_map, BTreeMap, HashMap, HashSet}; use futures::{future::Shared, AsyncWriteExt, Future, FutureExt, StreamExt, TryFutureExt}; @@ -421,6 +424,7 @@ impl Project { client.add_model_request_handler(Self::handle_open_buffer_by_id); client.add_model_request_handler(Self::handle_open_buffer_by_path); client.add_model_request_handler(Self::handle_save_buffer); + client.add_model_message_handler(Self::handle_update_head_text); } pub fn local( @@ -4667,14 +4671,29 @@ impl Project { None => return, }; + let shared_remote_id = self.shared_remote_id(); + let client = self.client.clone(); + cx.spawn(|_, mut cx| async move { let head_text = cx .background() .spawn(async move { repo.repo.lock().load_head_text(&path) }) .await; - buffer.update(&mut cx, |buffer, cx| { - buffer.update_head_text(head_text, cx); + + let buffer_id = buffer.update(&mut cx, |buffer, cx| { + buffer.update_head_text(head_text.clone(), cx); + buffer.remote_id() }); + + if let Some(project_id) = shared_remote_id { + client + .send(proto::UpdateHeadText { + project_id, + buffer_id: buffer_id as u64, + head_text, + }) + .log_err(); + } }) .detach(); } @@ -5253,6 +5272,27 @@ impl Project { }) } + async fn handle_update_head_text( + this: ModelHandle, + envelope: TypedEnvelope, + _: Arc, + mut cx: AsyncAppContext, + ) -> Result<()> { + this.update(&mut cx, |this, cx| { + let buffer_id = envelope.payload.buffer_id; + let head_text = envelope.payload.head_text; + let buffer = this + .opened_buffers + .get_mut(&buffer_id) + .and_then(|b| b.upgrade(cx)) + .ok_or_else(|| anyhow!("No such buffer {}", buffer_id))?; + + buffer.update(cx, |buffer, cx| buffer.update_head_text(head_text, cx)); + + Ok(()) + }) + } + async fn handle_update_buffer_file( this: ModelHandle, envelope: TypedEnvelope, diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 818f2cb7e1..d6604383da 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -108,6 +108,7 @@ message Envelope { FollowResponse follow_response = 93; UpdateFollowers update_followers = 94; Unfollow unfollow = 95; + UpdateHeadText update_head_text = 96; } } @@ -992,3 +993,9 @@ message WorktreeMetadata { string root_name = 2; bool visible = 3; } + +message UpdateHeadText { + uint64 project_id = 1; + uint64 buffer_id = 2; + optional string head_text = 3; +} diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 2ba3fa18ba..e91a9fd558 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -167,6 +167,7 @@ messages!( (UpdateProject, Foreground), (UpdateWorktree, Foreground), (UpdateWorktreeExtensions, Background), + (UpdateHeadText, Background), ); request_messages!( @@ -263,6 +264,7 @@ entity_messages!( UpdateProject, UpdateWorktree, UpdateWorktreeExtensions, + UpdateHeadText ); entity_messages!(channel_id, ChannelMessageSent); From 7f84abaf13c1e0720de5929bf242b3a51fb525b2 Mon Sep 17 00:00:00 2001 From: Julia Date: Sun, 2 Oct 2022 14:11:35 -0400 Subject: [PATCH 107/314] Increment protocol version again for previous commit --- crates/rpc/src/rpc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/rpc/src/rpc.rs b/crates/rpc/src/rpc.rs index 2c28462ee3..640271d4a2 100644 --- a/crates/rpc/src/rpc.rs +++ b/crates/rpc/src/rpc.rs @@ -6,4 +6,4 @@ pub use conn::Connection; pub use peer::*; mod macros; -pub const PROTOCOL_VERSION: u32 = 33; +pub const PROTOCOL_VERSION: u32 = 34; From 5769cdc3543f953ed38573d566355e7107e8494e Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Sun, 2 Oct 2022 17:56:09 -0700 Subject: [PATCH 108/314] made git diff rendering respect line wrap --- crates/editor/src/element.rs | 237 ++++++++++++++++++++++----------- crates/git/src/diff.rs | 2 +- crates/project/src/fs.rs | 10 +- crates/theme/src/theme.rs | 17 ++- styles/src/styleTree/editor.ts | 14 +- 5 files changed, 185 insertions(+), 95 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 57ee919288..5d83051567 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -46,6 +46,7 @@ use std::{ ops::Range, sync::Arc, }; +use theme::DiffStyle; struct SelectionLayout { head: DisplayPoint, @@ -525,98 +526,156 @@ impl EditorElement { layout: &mut LayoutState, cx: &mut PaintContext, ) { - let line_height = layout.position_map.line_height; - let scroll_position = layout.position_map.snapshot.scroll_position(); - let scroll_top = scroll_position.y() * line_height; + struct GutterLayout { + line_height: f32, + // scroll_position: Vector2F, + scroll_top: f32, + bounds: RectF, + } + + struct DiffLayout<'a> { + buffer_line: usize, + last_diff: Option<(&'a DiffHunk, usize)>, + } + + fn diff_quad( + status: DiffHunkStatus, + layout_range: Range, + gutter_layout: &GutterLayout, + diff_style: &DiffStyle, + ) -> Quad { + let color = match status { + DiffHunkStatus::Added => diff_style.inserted, + DiffHunkStatus::Modified => diff_style.modified, + + //TODO: This rendering is entirely a horrible hack + DiffHunkStatus::Removed => { + let row = layout_range.start; + + let offset = gutter_layout.line_height / 2.; + let start_y = + row as f32 * gutter_layout.line_height + offset - gutter_layout.scroll_top; + let end_y = start_y + gutter_layout.line_height; + + let width = diff_style.removed_width_em * gutter_layout.line_height; + let highlight_origin = gutter_layout.bounds.origin() + vec2f(-width, start_y); + let highlight_size = vec2f(width * 2., end_y - start_y); + let highlight_bounds = RectF::new(highlight_origin, highlight_size); + + return Quad { + bounds: highlight_bounds, + background: Some(diff_style.deleted), + border: Border::new(0., Color::transparent_black()), + corner_radius: 1. * gutter_layout.line_height, + }; + } + }; + + let start_row = layout_range.start; + let end_row = layout_range.end; + + let start_y = start_row as f32 * gutter_layout.line_height - gutter_layout.scroll_top; + let end_y = end_row as f32 * gutter_layout.line_height - gutter_layout.scroll_top; + + let width = diff_style.width_em * gutter_layout.line_height; + let highlight_origin = gutter_layout.bounds.origin() + vec2f(-width, start_y); + let highlight_size = vec2f(width * 2., end_y - start_y); + let highlight_bounds = RectF::new(highlight_origin, highlight_size); + + Quad { + bounds: highlight_bounds, + background: Some(color), + border: Border::new(0., Color::transparent_black()), + corner_radius: diff_style.corner_radius * gutter_layout.line_height, + } + } + + let gutter_layout = { + let scroll_position = layout.position_map.snapshot.scroll_position(); + let line_height = layout.position_map.line_height; + GutterLayout { + scroll_top: scroll_position.y() * line_height, + // scroll_position, + line_height, + bounds, + } + }; + + let mut diff_layout = DiffLayout { + buffer_line: 0, + last_diff: None, + }; + + let diff_style = &cx.global::().theme.editor.diff.clone(); + // dbg!("***************"); + // dbg!(&layout.diff_hunks); + // dbg!("***************"); + + // line is `None` when there's a line wrap for (ix, line) in layout.line_number_layouts.iter().enumerate() { + // dbg!(ix); if let Some(line) = line { let line_origin = bounds.origin() + vec2f( bounds.width() - line.width() - layout.gutter_padding, - ix as f32 * layout.position_map.line_height - - (scroll_top % layout.position_map.line_height), + ix as f32 * gutter_layout.line_height + - (gutter_layout.scroll_top % gutter_layout.line_height), ); - line.paint( - line_origin, - visible_bounds, - layout.position_map.line_height, - cx, - ); + + line.paint(line_origin, visible_bounds, gutter_layout.line_height, cx); + + //This line starts a buffer line, so let's do the diff calculation + let new_hunk = get_hunk(diff_layout.buffer_line, &layout.diff_hunks); + + // This + the unwraps are annoying, but at least it's legible + let (is_ending, is_starting) = match (diff_layout.last_diff, new_hunk) { + (None, None) => (false, false), + (None, Some(_)) => (false, true), + (Some(_), None) => (true, false), + (Some((old_hunk, _)), Some(new_hunk)) if new_hunk == old_hunk => (false, false), + (Some(_), Some(_)) => (true, true), + }; + + // dbg!(diff_layout.buffer_line, is_starting); + + if is_ending { + let (last_hunk, start_line) = diff_layout.last_diff.take().unwrap(); + // dbg!("ending"); + // dbg!(start_line..ix); + cx.scene.push_quad(diff_quad( + last_hunk.status(), + start_line..ix, + &gutter_layout, + diff_style, + )); + } + + if is_starting { + let new_hunk = new_hunk.unwrap(); + + diff_layout.last_diff = Some((new_hunk, ix)); + }; + + diff_layout.buffer_line += 1; } } - let ( - inserted_color, - modified_color, - deleted_color, - width_multiplier, - corner_radius, - removed_width_mult, - ) = { - let editor = &cx.global::().theme.editor; - ( - editor.diff_background_inserted, - editor.diff_background_modified, - editor.diff_background_deleted, - editor.diff_indicator_width_multiplier, - editor.diff_indicator_corner_radius, - editor.removed_diff_width_multiplier, - ) - }; - - for hunk in &layout.diff_hunks { - let color = match hunk.status() { - DiffHunkStatus::Added => inserted_color, - DiffHunkStatus::Modified => modified_color, - - //TODO: This rendering is entirely a horrible hack - DiffHunkStatus::Removed => { - let row = hunk.buffer_range.start; - - let offset = line_height / 2.; - let start_y = row as f32 * line_height + offset - scroll_top; - let end_y = start_y + line_height; - - let width = removed_width_mult * line_height; - let highlight_origin = bounds.origin() + vec2f(-width, start_y); - let highlight_size = vec2f(width * 2., end_y - start_y); - let highlight_bounds = RectF::new(highlight_origin, highlight_size); - - cx.scene.push_quad(Quad { - bounds: highlight_bounds, - background: Some(deleted_color), - border: Border::new(0., Color::transparent_black()), - corner_radius: 1. * line_height, - }); - - continue; - } - }; - - let start_row = hunk.buffer_range.start; - let end_row = hunk.buffer_range.end; - - let start_y = start_row as f32 * line_height - scroll_top; - let end_y = end_row as f32 * line_height - scroll_top; - - let width = width_multiplier * line_height; - let highlight_origin = bounds.origin() + vec2f(-width, start_y); - let highlight_size = vec2f(width * 2., end_y - start_y); - let highlight_bounds = RectF::new(highlight_origin, highlight_size); - - cx.scene.push_quad(Quad { - bounds: highlight_bounds, - background: Some(color), - border: Border::new(0., Color::transparent_black()), - corner_radius: corner_radius * line_height, - }); + // If we ran out with a diff hunk still being prepped, paint it now + if let Some((last_hunk, start_line)) = diff_layout.last_diff { + let end_line = layout.line_number_layouts.len(); + cx.scene.push_quad(diff_quad( + last_hunk.status(), + start_line..end_line, + &gutter_layout, + diff_style, + )) } if let Some((row, indicator)) = layout.code_actions_indicator.as_mut() { let mut x = bounds.width() - layout.gutter_padding; - let mut y = *row as f32 * line_height - scroll_top; + let mut y = *row as f32 * gutter_layout.line_height - gutter_layout.scroll_top; x += ((layout.gutter_padding + layout.gutter_margin) - indicator.size().x()) / 2.; - y += (line_height - indicator.size().y()) / 2.; + y += (gutter_layout.line_height - indicator.size().y()) / 2.; indicator.paint(bounds.origin() + vec2f(x, y), visible_bounds, cx); } } @@ -1321,6 +1380,28 @@ impl EditorElement { } } +/// Get the hunk that contains buffer_line, starting from start_idx +/// Returns none if there is none found, and +fn get_hunk(buffer_line: usize, hunks: &[DiffHunk]) -> Option<&DiffHunk> { + for i in 0..hunks.len() { + // Safety: Index out of bounds is handled by the check above + let hunk = hunks.get(i).unwrap(); + if hunk.buffer_range.contains(&(buffer_line as u32)) { + return Some(hunk); + } else if hunk.status() == DiffHunkStatus::Removed + && buffer_line == hunk.buffer_range.start as usize + { + return Some(hunk); + } else if hunk.buffer_range.start > buffer_line as u32 { + // If we've passed the buffer_line, just stop + return None; + } + } + + // We reached the end of the array without finding a hunk, just return none. + return None; +} + impl Element for EditorElement { type LayoutState = LayoutState; type PaintState = (); diff --git a/crates/git/src/diff.rs b/crates/git/src/diff.rs index 6c904d44d1..48630fc91c 100644 --- a/crates/git/src/diff.rs +++ b/crates/git/src/diff.rs @@ -6,7 +6,7 @@ use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point}; pub use git2 as libgit; use libgit::{DiffLineType as GitDiffLineType, DiffOptions as GitOptions, Patch as GitPatch}; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum DiffHunkStatus { Added, Modified, diff --git a/crates/project/src/fs.rs b/crates/project/src/fs.rs index d0e549c0b5..2b7aca642d 100644 --- a/crates/project/src/fs.rs +++ b/crates/project/src/fs.rs @@ -1,10 +1,11 @@ use anyhow::{anyhow, Result}; use fsevent::EventStream; use futures::{future::BoxFuture, Stream, StreamExt}; -use git::repository::{FakeGitRepositoryState, GitRepository, LibGitRepository}; +use git::repository::{GitRepository, LibGitRepository}; use language::LineEnding; use parking_lot::Mutex as SyncMutex; use smol::io::{AsyncReadExt, AsyncWriteExt}; +use std::sync::Arc; use std::{ io, os::unix::fs::MetadataExt, @@ -12,16 +13,17 @@ use std::{ pin::Pin, time::{Duration, SystemTime}, }; -use util::ResultExt; - use text::Rope; +use util::ResultExt; #[cfg(any(test, feature = "test-support"))] use collections::{btree_map, BTreeMap}; #[cfg(any(test, feature = "test-support"))] use futures::lock::Mutex; #[cfg(any(test, feature = "test-support"))] -use std::sync::{Arc, Weak}; +use git::repository::FakeGitRepositoryState; +#[cfg(any(test, feature = "test-support"))] +use std::sync::Weak; #[async_trait::async_trait] pub trait Fs: Send + Sync { diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 0d0c94ea8d..d8c8296481 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -488,12 +488,7 @@ pub struct Editor { pub rename_fade: f32, pub document_highlight_read_background: Color, pub document_highlight_write_background: Color, - pub diff_background_deleted: Color, - pub diff_background_inserted: Color, - pub diff_background_modified: Color, - pub removed_diff_width_multiplier: f32, - pub diff_indicator_width_multiplier: f32, - pub diff_indicator_corner_radius: f32, + pub diff: DiffStyle, pub line_number: Color, pub line_number_active: Color, pub guest_selections: Vec, @@ -577,6 +572,16 @@ pub struct CodeActions { pub vertical_scale: f32, } +#[derive(Clone, Deserialize, Default)] +pub struct DiffStyle { + pub inserted: Color, + pub modified: Color, + pub deleted: Color, + pub removed_width_em: f32, + pub width_em: f32, + pub corner_radius: f32, +} + #[derive(Debug, Default, Clone, Copy)] pub struct Interactive { pub default: T, diff --git a/styles/src/styleTree/editor.ts b/styles/src/styleTree/editor.ts index 6e52c620ee..04a5bafbd5 100644 --- a/styles/src/styleTree/editor.ts +++ b/styles/src/styleTree/editor.ts @@ -60,12 +60,14 @@ export default function editor(theme: Theme) { indicator: iconColor(theme, "secondary"), verticalScale: 0.618 }, - diffBackgroundDeleted: theme.iconColor.error, - diffBackgroundInserted: theme.iconColor.ok, - diffBackgroundModified: theme.iconColor.warning, - removedDiffWidthMultiplier: 0.275, - diffIndicatorWidthMultiplier: 0.16, - diffIndicatorCornerRadius: 0.05, + diff: { + deleted: theme.iconColor.error, + inserted: theme.iconColor.ok, + modified: theme.iconColor.warning, + removedWidthEm: 0.275, + widthEm: 0.16, + cornerRadius: 0.05, + }, documentHighlightReadBackground: theme.editor.highlight.occurrence, documentHighlightWriteBackground: theme.editor.highlight.activeOccurrence, errorColor: theme.textColor.error, From 52dbf2f9b8246bb5b1258ab990939acb74efc527 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Sun, 2 Oct 2022 18:01:37 -0700 Subject: [PATCH 109/314] add proto stuff --- crates/client/src/client.rs | 4 +- crates/client/src/telemetry.rs | 10 +- crates/client/src/user.rs | 10 +- .../20220913211150_create_signups.down.sql | 6 - ....sql => 20220913211150_create_signups.sql} | 0 .../20220929182110_add_metrics_id.sql | 2 + crates/collab/src/api.rs | 82 ++-- crates/collab/src/db.rs | 56 ++- crates/collab/src/db_tests.rs | 349 +++++++++--------- crates/collab/src/integration_tests.rs | 7 +- crates/collab/src/rpc.rs | 17 + crates/rpc/proto/zed.proto | 10 +- crates/rpc/src/proto.rs | 3 + 13 files changed, 317 insertions(+), 239 deletions(-) delete mode 100644 crates/collab/migrations/20220913211150_create_signups.down.sql rename crates/collab/migrations/{20220913211150_create_signups.up.sql => 20220913211150_create_signups.sql} (100%) create mode 100644 crates/collab/migrations/20220929182110_add_metrics_id.sql diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index b75be62308..9ec24abae5 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -320,11 +320,9 @@ impl Client { log::info!("set status on client {}: {:?}", self.id, status); let mut state = self.state.write(); *state.status.0.borrow_mut() = status; - let user_id = state.credentials.as_ref().map(|c| c.user_id); match status { Status::Connected { .. } => { - self.telemetry.set_user_id(user_id); state._reconnect_task = None; } Status::ConnectionLost => { @@ -353,7 +351,7 @@ impl Client { })); } Status::SignedOut | Status::UpgradeRequired => { - self.telemetry.set_user_id(user_id); + self.telemetry.set_metrics_id(None); state._reconnect_task.take(); } _ => {} diff --git a/crates/client/src/telemetry.rs b/crates/client/src/telemetry.rs index 8b7be5ba80..c9b5665e9e 100644 --- a/crates/client/src/telemetry.rs +++ b/crates/client/src/telemetry.rs @@ -29,7 +29,7 @@ pub struct Telemetry { #[derive(Default)] struct TelemetryState { - user_id: Option>, + metrics_id: Option>, device_id: Option>, app_version: Option>, os_version: Option>, @@ -115,7 +115,7 @@ impl Telemetry { flush_task: Default::default(), next_event_id: 0, log_file: None, - user_id: None, + metrics_id: None, }), }); @@ -176,8 +176,8 @@ impl Telemetry { .detach(); } - pub fn set_user_id(&self, user_id: Option) { - self.state.lock().user_id = user_id.map(|id| id.to_string().into()); + pub fn set_metrics_id(&self, metrics_id: Option) { + self.state.lock().metrics_id = metrics_id.map(|s| s.into()); } pub fn report_event(self: &Arc, kind: &str, properties: Value) { @@ -199,7 +199,7 @@ impl Telemetry { None }, user_properties: None, - user_id: state.user_id.clone(), + user_id: state.metrics_id.clone(), device_id: state.device_id.clone(), os_name: state.os_name, os_version: state.os_version.clone(), diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 149d22e77a..b31cda94b3 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -142,10 +142,14 @@ impl UserStore { match status { Status::Connected { .. } => { if let Some((this, user_id)) = this.upgrade(&cx).zip(client.user_id()) { - let user = this + let fetch_user = this .update(&mut cx, |this, cx| this.fetch_user(user_id, cx)) - .log_err() - .await; + .log_err(); + let fetch_metrics_id = + client.request(proto::GetPrivateUserInfo {}).log_err(); + let (user, info) = futures::join!(fetch_user, fetch_metrics_id); + client.telemetry.set_metrics_id(info.map(|i| i.metrics_id)); + client.telemetry.report_event("sign in", Default::default()); current_user_tx.send(user).await.ok(); } } diff --git a/crates/collab/migrations/20220913211150_create_signups.down.sql b/crates/collab/migrations/20220913211150_create_signups.down.sql deleted file mode 100644 index 5504bbb8dc..0000000000 --- a/crates/collab/migrations/20220913211150_create_signups.down.sql +++ /dev/null @@ -1,6 +0,0 @@ -DROP TABLE signups; - -ALTER TABLE users - DROP COLUMN github_user_id; - -DROP INDEX index_users_on_email_address; diff --git a/crates/collab/migrations/20220913211150_create_signups.up.sql b/crates/collab/migrations/20220913211150_create_signups.sql similarity index 100% rename from crates/collab/migrations/20220913211150_create_signups.up.sql rename to crates/collab/migrations/20220913211150_create_signups.sql diff --git a/crates/collab/migrations/20220929182110_add_metrics_id.sql b/crates/collab/migrations/20220929182110_add_metrics_id.sql new file mode 100644 index 0000000000..665d6323bf --- /dev/null +++ b/crates/collab/migrations/20220929182110_add_metrics_id.sql @@ -0,0 +1,2 @@ +ALTER TABLE "users" + ADD "metrics_id" uuid NOT NULL DEFAULT gen_random_uuid(); diff --git a/crates/collab/src/api.rs b/crates/collab/src/api.rs index 0a9d8106ce..08dfa91ba9 100644 --- a/crates/collab/src/api.rs +++ b/crates/collab/src/api.rs @@ -24,6 +24,7 @@ use tracing::instrument; pub fn routes(rpc_server: &Arc, state: Arc) -> Router { Router::new() + .route("/user", get(get_authenticated_user)) .route("/users", get(get_users).post(create_user)) .route("/users/:id", put(update_user).delete(destroy_user)) .route("/users/:id/access_tokens", post(create_access_token)) @@ -85,10 +86,33 @@ pub async fn validate_api_token(req: Request, next: Next) -> impl IntoR Ok::<_, Error>(next.run(req).await) } +#[derive(Debug, Deserialize)] +struct AuthenticatedUserParams { + github_user_id: i32, + github_login: String, +} + +#[derive(Debug, Serialize)] +struct AuthenticatedUserResponse { + user: User, + metrics_id: String, +} + +async fn get_authenticated_user( + Query(params): Query, + Extension(app): Extension>, +) -> Result> { + let user = app + .db + .get_user_by_github_account(¶ms.github_login, Some(params.github_user_id)) + .await? + .ok_or_else(|| Error::Http(StatusCode::NOT_FOUND, "user not found".into()))?; + let metrics_id = app.db.get_user_metrics_id(user.id).await?; + return Ok(Json(AuthenticatedUserResponse { user, metrics_id })); +} + #[derive(Debug, Deserialize)] struct GetUsersQueryParams { - github_user_id: Option, - github_login: Option, query: Option, page: Option, limit: Option, @@ -98,14 +122,6 @@ async fn get_users( Query(params): Query, Extension(app): Extension>, ) -> Result>> { - if let Some(github_login) = ¶ms.github_login { - let user = app - .db - .get_user_by_github_account(github_login, params.github_user_id) - .await?; - return Ok(Json(Vec::from_iter(user))); - } - let limit = params.limit.unwrap_or(100); let users = if let Some(query) = params.query { app.db.fuzzy_search_users(&query, limit).await? @@ -124,6 +140,8 @@ struct CreateUserParams { email_address: String, email_confirmation_code: Option, #[serde(default)] + admin: bool, + #[serde(default)] invite_count: i32, } @@ -131,6 +149,7 @@ struct CreateUserParams { struct CreateUserResponse { user: User, signup_device_id: Option, + metrics_id: String, } async fn create_user( @@ -143,12 +162,10 @@ async fn create_user( github_user_id: params.github_user_id, invite_count: params.invite_count, }; - let user_id; - let signup_device_id; + // Creating a user via the normal signup process - if let Some(email_confirmation_code) = params.email_confirmation_code { - let result = app - .db + let result = if let Some(email_confirmation_code) = params.email_confirmation_code { + app.db .create_user_from_invite( &Invite { email_address: params.email_address, @@ -156,34 +173,37 @@ async fn create_user( }, user, ) - .await?; - user_id = result.user_id; - signup_device_id = result.signup_device_id; - if let Some(inviter_id) = result.inviting_user_id { - rpc_server - .invite_code_redeemed(inviter_id, user_id) - .await - .trace_err(); - } + .await? } // Creating a user as an admin - else { - user_id = app - .db + else if params.admin { + app.db .create_user(¶ms.email_address, false, user) - .await?; - signup_device_id = None; + .await? + } else { + Err(Error::Http( + StatusCode::UNPROCESSABLE_ENTITY, + "email confirmation code is required".into(), + ))? + }; + + if let Some(inviter_id) = result.inviting_user_id { + rpc_server + .invite_code_redeemed(inviter_id, result.user_id) + .await + .trace_err(); } let user = app .db - .get_user_by_id(user_id) + .get_user_by_id(result.user_id) .await? .ok_or_else(|| anyhow!("couldn't find the user we just created"))?; Ok(Json(CreateUserResponse { user, - signup_device_id, + metrics_id: result.metrics_id, + signup_device_id: result.signup_device_id, })) } diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs index 8b01cdf971..a12f6a4f89 100644 --- a/crates/collab/src/db.rs +++ b/crates/collab/src/db.rs @@ -17,10 +17,11 @@ pub trait Db: Send + Sync { email_address: &str, admin: bool, params: NewUserParams, - ) -> Result; + ) -> Result; async fn get_all_users(&self, page: u32, limit: u32) -> Result>; async fn fuzzy_search_users(&self, query: &str, limit: u32) -> Result>; async fn get_user_by_id(&self, id: UserId) -> Result>; + async fn get_user_metrics_id(&self, id: UserId) -> Result; async fn get_users_by_ids(&self, ids: Vec) -> Result>; async fn get_users_with_no_invites(&self, invited_by_another_user: bool) -> Result>; async fn get_user_by_github_account( @@ -208,21 +209,26 @@ impl Db for PostgresDb { email_address: &str, admin: bool, params: NewUserParams, - ) -> Result { + ) -> Result { let query = " INSERT INTO users (email_address, github_login, github_user_id, admin) VALUES ($1, $2, $3, $4) ON CONFLICT (github_login) DO UPDATE SET github_login = excluded.github_login - RETURNING id + RETURNING id, metrics_id::text "; - Ok(sqlx::query_scalar(query) + let (user_id, metrics_id): (UserId, String) = sqlx::query_as(query) .bind(email_address) .bind(params.github_login) .bind(params.github_user_id) .bind(admin) .fetch_one(&self.pool) - .await - .map(UserId)?) + .await?; + Ok(NewUserResult { + user_id, + metrics_id, + signup_device_id: None, + inviting_user_id: None, + }) } async fn get_all_users(&self, page: u32, limit: u32) -> Result> { @@ -256,6 +262,18 @@ impl Db for PostgresDb { Ok(users.into_iter().next()) } + async fn get_user_metrics_id(&self, id: UserId) -> Result { + let query = " + SELECT metrics_id::text + FROM users + WHERE id = $1 + "; + Ok(sqlx::query_scalar(query) + .bind(id) + .fetch_one(&self.pool) + .await?) + } + async fn get_users_by_ids(&self, ids: Vec) -> Result> { let ids = ids.into_iter().map(|id| id.0).collect::>(); let query = " @@ -493,13 +511,13 @@ impl Db for PostgresDb { ))?; } - let user_id: UserId = sqlx::query_scalar( + let (user_id, metrics_id): (UserId, String) = sqlx::query_as( " INSERT INTO users (email_address, github_login, github_user_id, admin, invite_count, invite_code) VALUES ($1, $2, $3, 'f', $4, $5) - RETURNING id + RETURNING id, metrics_id::text ", ) .bind(&invite.email_address) @@ -559,6 +577,7 @@ impl Db for PostgresDb { tx.commit().await?; Ok(NewUserResult { user_id, + metrics_id, inviting_user_id, signup_device_id, }) @@ -1722,6 +1741,7 @@ pub struct NewUserParams { #[derive(Debug)] pub struct NewUserResult { pub user_id: UserId, + pub metrics_id: String, pub inviting_user_id: Option, pub signup_device_id: Option, } @@ -1808,15 +1828,15 @@ mod test { email_address: &str, admin: bool, params: NewUserParams, - ) -> Result { + ) -> Result { self.background.simulate_random_delay().await; let mut users = self.users.lock(); - if let Some(user) = users + let user_id = if let Some(user) = users .values() .find(|user| user.github_login == params.github_login) { - Ok(user.id) + user.id } else { let id = post_inc(&mut *self.next_user_id.lock()); let user_id = UserId(id); @@ -1833,8 +1853,14 @@ mod test { connected_once: false, }, ); - Ok(user_id) - } + user_id + }; + Ok(NewUserResult { + user_id, + metrics_id: "the-metrics-id".to_string(), + inviting_user_id: None, + signup_device_id: None, + }) } async fn get_all_users(&self, _page: u32, _limit: u32) -> Result> { @@ -1850,6 +1876,10 @@ mod test { Ok(self.get_users_by_ids(vec![id]).await?.into_iter().next()) } + async fn get_user_metrics_id(&self, _id: UserId) -> Result { + Ok("the-metrics-id".to_string()) + } + async fn get_users_by_ids(&self, ids: Vec) -> Result> { self.background.simulate_random_delay().await; let users = self.users.lock(); diff --git a/crates/collab/src/db_tests.rs b/crates/collab/src/db_tests.rs index 1e48b4b754..e063b97eb6 100644 --- a/crates/collab/src/db_tests.rs +++ b/crates/collab/src/db_tests.rs @@ -12,89 +12,56 @@ async fn test_get_users_by_ids() { ] { let db = test_db.db(); - let user1 = db - .create_user( - "u1@example.com", - false, - NewUserParams { - github_login: "u1".into(), - github_user_id: 1, - invite_count: 0, - }, - ) - .await - .unwrap(); - let user2 = db - .create_user( - "u2@example.com", - false, - NewUserParams { - github_login: "u2".into(), - github_user_id: 2, - invite_count: 0, - }, - ) - .await - .unwrap(); - let user3 = db - .create_user( - "u3@example.com", - false, - NewUserParams { - github_login: "u3".into(), - github_user_id: 3, - invite_count: 0, - }, - ) - .await - .unwrap(); - let user4 = db - .create_user( - "u4@example.com", - false, - NewUserParams { - github_login: "u4".into(), - github_user_id: 4, - invite_count: 0, - }, - ) - .await - .unwrap(); + let mut user_ids = Vec::new(); + for i in 1..=4 { + user_ids.push( + db.create_user( + &format!("user{i}@example.com"), + false, + NewUserParams { + github_login: format!("user{i}"), + github_user_id: i, + invite_count: 0, + }, + ) + .await + .unwrap() + .user_id, + ); + } assert_eq!( - db.get_users_by_ids(vec![user1, user2, user3, user4]) - .await - .unwrap(), + db.get_users_by_ids(user_ids.clone()).await.unwrap(), vec![ User { - id: user1, - github_login: "u1".to_string(), + id: user_ids[0], + github_login: "user1".to_string(), github_user_id: Some(1), - email_address: Some("u1@example.com".to_string()), + email_address: Some("user1@example.com".to_string()), admin: false, ..Default::default() }, User { - id: user2, - github_login: "u2".to_string(), + id: user_ids[1], + github_login: "user2".to_string(), github_user_id: Some(2), - email_address: Some("u2@example.com".to_string()), + email_address: Some("user2@example.com".to_string()), admin: false, ..Default::default() }, User { - id: user3, - github_login: "u3".to_string(), + id: user_ids[2], + github_login: "user3".to_string(), github_user_id: Some(3), - email_address: Some("u3@example.com".to_string()), + email_address: Some("user3@example.com".to_string()), admin: false, ..Default::default() }, User { - id: user4, - github_login: "u4".to_string(), + id: user_ids[3], + github_login: "user4".to_string(), github_user_id: Some(4), - email_address: Some("u4@example.com".to_string()), + email_address: Some("user4@example.com".to_string()), admin: false, ..Default::default() } @@ -121,7 +88,8 @@ async fn test_get_user_by_github_account() { }, ) .await - .unwrap(); + .unwrap() + .user_id; let user_id2 = db .create_user( "user2@example.com", @@ -133,7 +101,8 @@ async fn test_get_user_by_github_account() { }, ) .await - .unwrap(); + .unwrap() + .user_id; let user = db .get_user_by_github_account("login1", None) @@ -177,7 +146,8 @@ async fn test_worktree_extensions() { }, ) .await - .unwrap(); + .unwrap() + .user_id; let project = db.register_project(user).await.unwrap(); db.update_worktree_extensions(project, 100, Default::default()) @@ -237,43 +207,25 @@ async fn test_user_activity() { let test_db = TestDb::postgres().await; let db = test_db.db(); - let user_1 = db - .create_user( - "u1@example.com", - false, - NewUserParams { - github_login: "u1".into(), - github_user_id: 0, - invite_count: 0, - }, - ) - .await - .unwrap(); - let user_2 = db - .create_user( - "u2@example.com", - false, - NewUserParams { - github_login: "u2".into(), - github_user_id: 0, - invite_count: 0, - }, - ) - .await - .unwrap(); - let user_3 = db - .create_user( - "u3@example.com", - false, - NewUserParams { - github_login: "u3".into(), - github_user_id: 0, - invite_count: 0, - }, - ) - .await - .unwrap(); - let project_1 = db.register_project(user_1).await.unwrap(); + let mut user_ids = Vec::new(); + for i in 0..=2 { + user_ids.push( + db.create_user( + &format!("user{i}@example.com"), + false, + NewUserParams { + github_login: format!("user{i}"), + github_user_id: i, + invite_count: 0, + }, + ) + .await + .unwrap() + .user_id, + ); + } + + let project_1 = db.register_project(user_ids[0]).await.unwrap(); db.update_worktree_extensions( project_1, 1, @@ -281,34 +233,37 @@ async fn test_user_activity() { ) .await .unwrap(); - let project_2 = db.register_project(user_2).await.unwrap(); + let project_2 = db.register_project(user_ids[1]).await.unwrap(); let t0 = OffsetDateTime::now_utc() - Duration::from_secs(60 * 60); // User 2 opens a project let t1 = t0 + Duration::from_secs(10); - db.record_user_activity(t0..t1, &[(user_2, project_2)]) + db.record_user_activity(t0..t1, &[(user_ids[1], project_2)]) .await .unwrap(); let t2 = t1 + Duration::from_secs(10); - db.record_user_activity(t1..t2, &[(user_2, project_2)]) + db.record_user_activity(t1..t2, &[(user_ids[1], project_2)]) .await .unwrap(); // User 1 joins the project let t3 = t2 + Duration::from_secs(10); - db.record_user_activity(t2..t3, &[(user_2, project_2), (user_1, project_2)]) - .await - .unwrap(); + db.record_user_activity( + t2..t3, + &[(user_ids[1], project_2), (user_ids[0], project_2)], + ) + .await + .unwrap(); // User 1 opens another project let t4 = t3 + Duration::from_secs(10); db.record_user_activity( t3..t4, &[ - (user_2, project_2), - (user_1, project_2), - (user_1, project_1), + (user_ids[1], project_2), + (user_ids[0], project_2), + (user_ids[0], project_1), ], ) .await @@ -319,10 +274,10 @@ async fn test_user_activity() { db.record_user_activity( t4..t5, &[ - (user_2, project_2), - (user_1, project_2), - (user_1, project_1), - (user_3, project_1), + (user_ids[1], project_2), + (user_ids[0], project_2), + (user_ids[0], project_1), + (user_ids[2], project_1), ], ) .await @@ -330,13 +285,16 @@ async fn test_user_activity() { // User 2 leaves let t6 = t5 + Duration::from_secs(5); - db.record_user_activity(t5..t6, &[(user_1, project_1), (user_3, project_1)]) - .await - .unwrap(); + db.record_user_activity( + t5..t6, + &[(user_ids[0], project_1), (user_ids[2], project_1)], + ) + .await + .unwrap(); let t7 = t6 + Duration::from_secs(60); let t8 = t7 + Duration::from_secs(10); - db.record_user_activity(t7..t8, &[(user_1, project_1)]) + db.record_user_activity(t7..t8, &[(user_ids[0], project_1)]) .await .unwrap(); @@ -344,8 +302,8 @@ async fn test_user_activity() { db.get_top_users_activity_summary(t0..t6, 10).await.unwrap(), &[ UserActivitySummary { - id: user_1, - github_login: "u1".to_string(), + id: user_ids[0], + github_login: "user0".to_string(), project_activity: vec![ ProjectActivitySummary { id: project_1, @@ -360,8 +318,8 @@ async fn test_user_activity() { ] }, UserActivitySummary { - id: user_2, - github_login: "u2".to_string(), + id: user_ids[1], + github_login: "user1".to_string(), project_activity: vec![ProjectActivitySummary { id: project_2, duration: Duration::from_secs(50), @@ -369,8 +327,8 @@ async fn test_user_activity() { }] }, UserActivitySummary { - id: user_3, - github_login: "u3".to_string(), + id: user_ids[2], + github_login: "user2".to_string(), project_activity: vec![ProjectActivitySummary { id: project_1, duration: Duration::from_secs(15), @@ -442,7 +400,9 @@ async fn test_user_activity() { ); assert_eq!( - db.get_user_activity_timeline(t3..t6, user_1).await.unwrap(), + db.get_user_activity_timeline(t3..t6, user_ids[0]) + .await + .unwrap(), &[ UserActivityPeriod { project_id: project_1, @@ -459,7 +419,9 @@ async fn test_user_activity() { ] ); assert_eq!( - db.get_user_activity_timeline(t0..t8, user_1).await.unwrap(), + db.get_user_activity_timeline(t0..t8, user_ids[0]) + .await + .unwrap(), &[ UserActivityPeriod { project_id: project_2, @@ -501,7 +463,8 @@ async fn test_recent_channel_messages() { }, ) .await - .unwrap(); + .unwrap() + .user_id; let org = db.create_org("org", "org").await.unwrap(); let channel = db.create_org_channel(org, "channel").await.unwrap(); for i in 0..10 { @@ -545,7 +508,8 @@ async fn test_channel_message_nonces() { }, ) .await - .unwrap(); + .unwrap() + .user_id; let org = db.create_org("org", "org").await.unwrap(); let channel = db.create_org_channel(org, "channel").await.unwrap(); @@ -587,7 +551,8 @@ async fn test_create_access_tokens() { }, ) .await - .unwrap(); + .unwrap() + .user_id; db.create_access_token_hash(user, "h1", 3).await.unwrap(); db.create_access_token_hash(user, "h2", 3).await.unwrap(); @@ -678,42 +643,27 @@ async fn test_add_contacts() { ] { let db = test_db.db(); - let user_1 = db - .create_user( - "u1@example.com", - false, - NewUserParams { - github_login: "u1".into(), - github_user_id: 0, - invite_count: 0, - }, - ) - .await - .unwrap(); - let user_2 = db - .create_user( - "u2@example.com", - false, - NewUserParams { - github_login: "u2".into(), - github_user_id: 1, - invite_count: 0, - }, - ) - .await - .unwrap(); - let user_3 = db - .create_user( - "u3@example.com", - false, - NewUserParams { - github_login: "u3".into(), - github_user_id: 2, - invite_count: 0, - }, - ) - .await - .unwrap(); + let mut user_ids = Vec::new(); + for i in 0..3 { + user_ids.push( + db.create_user( + &format!("user{i}@example.com"), + false, + NewUserParams { + github_login: format!("user{i}"), + github_user_id: i, + invite_count: 0, + }, + ) + .await + .unwrap() + .user_id, + ); + } + + let user_1 = user_ids[0]; + let user_2 = user_ids[1]; + let user_3 = user_ids[2]; // User starts with no contacts assert_eq!( @@ -927,12 +877,12 @@ async fn test_add_contacts() { async fn test_invite_codes() { let postgres = TestDb::postgres().await; let db = postgres.db(); - let user1 = db + let NewUserResult { user_id: user1, .. } = db .create_user( - "u1@example.com", + "user1@example.com", false, NewUserParams { - github_login: "u1".into(), + github_login: "user1".into(), github_user_id: 0, invite_count: 0, }, @@ -954,13 +904,14 @@ async fn test_invite_codes() { // User 2 redeems the invite code and becomes a contact of user 1. let user2_invite = db - .create_invite_from_code(&invite_code, "u2@example.com", Some("user-2-device-id")) + .create_invite_from_code(&invite_code, "user2@example.com", Some("user-2-device-id")) .await .unwrap(); let NewUserResult { user_id: user2, inviting_user_id, signup_device_id, + metrics_id, } = db .create_user_from_invite( &user2_invite, @@ -976,6 +927,7 @@ async fn test_invite_codes() { assert_eq!(invite_count, 1); assert_eq!(inviting_user_id, Some(user1)); assert_eq!(signup_device_id.unwrap(), "user-2-device-id"); + assert_eq!(db.get_user_metrics_id(user2).await.unwrap(), metrics_id); assert_eq!( db.get_contacts(user1).await.unwrap(), [ @@ -1009,13 +961,14 @@ async fn test_invite_codes() { // User 3 redeems the invite code and becomes a contact of user 1. let user3_invite = db - .create_invite_from_code(&invite_code, "u3@example.com", None) + .create_invite_from_code(&invite_code, "user3@example.com", None) .await .unwrap(); let NewUserResult { user_id: user3, inviting_user_id, signup_device_id, + .. } = db .create_user_from_invite( &user3_invite, @@ -1067,7 +1020,7 @@ async fn test_invite_codes() { ); // Trying to reedem the code for the third time results in an error. - db.create_invite_from_code(&invite_code, "u4@example.com", Some("user-4-device-id")) + db.create_invite_from_code(&invite_code, "user4@example.com", Some("user-4-device-id")) .await .unwrap_err(); @@ -1079,7 +1032,7 @@ async fn test_invite_codes() { // User 4 can now redeem the invite code and becomes a contact of user 1. let user4_invite = db - .create_invite_from_code(&invite_code, "u4@example.com", Some("user-4-device-id")) + .create_invite_from_code(&invite_code, "user4@example.com", Some("user-4-device-id")) .await .unwrap(); let user4 = db @@ -1137,7 +1090,7 @@ async fn test_invite_codes() { ); // An existing user cannot redeem invite codes. - db.create_invite_from_code(&invite_code, "u2@example.com", Some("user-2-device-id")) + db.create_invite_from_code(&invite_code, "user2@example.com", Some("user-2-device-id")) .await .unwrap_err(); let (_, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap(); @@ -1232,6 +1185,7 @@ async fn test_signups() { user_id, inviting_user_id, signup_device_id, + .. } = db .create_user_from_invite( &Invite { @@ -1284,6 +1238,51 @@ async fn test_signups() { .unwrap_err(); } +#[tokio::test(flavor = "multi_thread")] +async fn test_metrics_id() { + let postgres = TestDb::postgres().await; + let db = postgres.db(); + + let NewUserResult { + user_id: user1, + metrics_id: metrics_id1, + .. + } = db + .create_user( + "person1@example.com", + false, + NewUserParams { + github_login: "person1".into(), + github_user_id: 101, + invite_count: 5, + }, + ) + .await + .unwrap(); + let NewUserResult { + user_id: user2, + metrics_id: metrics_id2, + .. + } = db + .create_user( + "person2@example.com", + false, + NewUserParams { + github_login: "person2".into(), + github_user_id: 102, + invite_count: 5, + }, + ) + .await + .unwrap(); + + assert_eq!(db.get_user_metrics_id(user1).await.unwrap(), metrics_id1); + assert_eq!(db.get_user_metrics_id(user2).await.unwrap(), metrics_id2); + assert_eq!(metrics_id1.len(), 36); + assert_eq!(metrics_id2.len(), 36); + assert_ne!(metrics_id1, metrics_id2); +} + fn build_background_executor() -> Arc { Deterministic::new(0).build_background() } diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 422c9fd0bb..a13a013e7a 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -4794,7 +4794,8 @@ async fn test_random_collaboration( }, ) .await - .unwrap(); + .unwrap() + .user_id; let mut available_guests = vec![ "guest-1".to_string(), "guest-2".to_string(), @@ -4814,7 +4815,8 @@ async fn test_random_collaboration( }, ) .await - .unwrap(); + .unwrap() + .user_id; assert_eq!(*username, format!("guest-{}", guest_user_id)); server .app_state @@ -5337,6 +5339,7 @@ impl TestServer { ) .await .unwrap() + .user_id }; let client_name = name.to_string(); let mut client = cx.read(|cx| Client::new(http.clone(), cx)); diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 318555b7ed..15748a52cf 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -206,7 +206,11 @@ impl Server { .add_message_handler(Server::unfollow) .add_message_handler(Server::update_followers) .add_request_handler(Server::get_channel_messages) +<<<<<<< HEAD .add_message_handler(Server::update_head_text); +======= + .add_request_handler(Server::get_private_user_info); +>>>>>>> 5d09083a (Identify users in amplitude via a separate 'metrics_id' UUID) Arc::new(server) } @@ -1742,6 +1746,19 @@ impl Server { }); Ok(()) } + async fn get_private_user_info( + self: Arc, + request: TypedEnvelope, + response: Response, + ) -> Result<()> { + let user_id = self + .store() + .await + .user_id_for_connection(request.sender_id)?; + let metrics_id = self.app_state.db.get_user_metrics_id(user_id).await?; + response.send(proto::GetPrivateUserInfoResponse { metrics_id })?; + Ok(()) + } pub(crate) async fn store(&self) -> StoreGuard<'_> { #[cfg(test)] diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index d6604383da..832c5bb6bd 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -108,7 +108,9 @@ message Envelope { FollowResponse follow_response = 93; UpdateFollowers update_followers = 94; Unfollow unfollow = 95; - UpdateHeadText update_head_text = 96; + GetPrivateUserInfo get_private_user_info = 96; + GetPrivateUserInfoResponse get_private_user_info_response = 97; + UpdateHeadText update_head_text = 98; } } @@ -749,6 +751,12 @@ message Unfollow { uint32 leader_id = 2; } +message GetPrivateUserInfo {} + +message GetPrivateUserInfoResponse { + string metrics_id = 1; +} + // Entities message UpdateActiveView { diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index e91a9fd558..8c5832c15f 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -168,6 +168,8 @@ messages!( (UpdateWorktree, Foreground), (UpdateWorktreeExtensions, Background), (UpdateHeadText, Background), + (GetPrivateUserInfo, Foreground), + (GetPrivateUserInfoResponse, Foreground), ); request_messages!( @@ -190,6 +192,7 @@ request_messages!( (GetTypeDefinition, GetTypeDefinitionResponse), (GetDocumentHighlights, GetDocumentHighlightsResponse), (GetReferences, GetReferencesResponse), + (GetPrivateUserInfo, GetPrivateUserInfoResponse), (GetProjectSymbols, GetProjectSymbolsResponse), (FuzzySearchUsers, UsersResponse), (GetUsers, UsersResponse), From 1aa554f4c98d45b7eec5646b22d65e540b4a751c Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 29 Sep 2022 13:51:17 -0700 Subject: [PATCH 110/314] Fix FakeServer to expect new GetPrivateUserInfo request --- crates/client/src/test.rs | 64 ++++++++++++++------- crates/contacts_panel/src/contacts_panel.rs | 11 ++++ 2 files changed, 54 insertions(+), 21 deletions(-) diff --git a/crates/client/src/test.rs b/crates/client/src/test.rs index c634978a57..56d3d80b63 100644 --- a/crates/client/src/test.rs +++ b/crates/client/src/test.rs @@ -6,7 +6,10 @@ use anyhow::{anyhow, Result}; use futures::{future::BoxFuture, stream::BoxStream, Future, StreamExt}; use gpui::{executor, ModelHandle, TestAppContext}; use parking_lot::Mutex; -use rpc::{proto, ConnectionId, Peer, Receipt, TypedEnvelope}; +use rpc::{ + proto::{self, GetPrivateUserInfo, GetPrivateUserInfoResponse}, + ConnectionId, Peer, Receipt, TypedEnvelope, +}; use std::{fmt, rc::Rc, sync::Arc}; pub struct FakeServer { @@ -93,6 +96,7 @@ impl FakeServer { .authenticate_and_connect(false, &cx.to_async()) .await .unwrap(); + server } @@ -126,26 +130,44 @@ impl FakeServer { #[allow(clippy::await_holding_lock)] pub async fn receive(&self) -> Result> { self.executor.start_waiting(); - let message = self - .state - .lock() - .incoming - .as_mut() - .expect("not connected") - .next() - .await - .ok_or_else(|| anyhow!("other half hung up"))?; - self.executor.finish_waiting(); - let type_name = message.payload_type_name(); - Ok(*message - .into_any() - .downcast::>() - .unwrap_or_else(|_| { - panic!( - "fake server received unexpected message type: {:?}", - type_name - ); - })) + + loop { + let message = self + .state + .lock() + .incoming + .as_mut() + .expect("not connected") + .next() + .await + .ok_or_else(|| anyhow!("other half hung up"))?; + self.executor.finish_waiting(); + let type_name = message.payload_type_name(); + let message = message.into_any(); + + if message.is::>() { + return Ok(*message.downcast().unwrap()); + } + + if message.is::>() { + self.respond( + message + .downcast::>() + .unwrap() + .receipt(), + GetPrivateUserInfoResponse { + metrics_id: "the-metrics-id".into(), + }, + ) + .await; + continue; + } + + panic!( + "fake server received unexpected message type: {:?}", + type_name + ); + } } pub async fn respond( diff --git a/crates/contacts_panel/src/contacts_panel.rs b/crates/contacts_panel/src/contacts_panel.rs index 7dcfb8cea4..91b86aaf0e 100644 --- a/crates/contacts_panel/src/contacts_panel.rs +++ b/crates/contacts_panel/src/contacts_panel.rs @@ -1220,6 +1220,17 @@ mod tests { let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx)); let project_store = cx.add_model(|_| ProjectStore::new(project::Db::open_fake())); let server = FakeServer::for_client(current_user_id, &client, cx).await; + + let request = server.receive::().await.unwrap(); + server + .respond( + request.receipt(), + proto::GetPrivateUserInfoResponse { + metrics_id: "the-metrics-id".into(), + }, + ) + .await; + let fs = FakeFs::new(cx.background()); fs.insert_tree("/private_dir", json!({ "one.rs": "" })) .await; From 34926abe83a181aa547d1a9fbd8ef640d1db63f7 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 29 Sep 2022 16:47:20 -0700 Subject: [PATCH 111/314] 0.57.0 --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fa8f8acbdc..8d359bf728 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7203,7 +7203,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.56.0" +version = "0.57.0" dependencies = [ "activity_indicator", "anyhow", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index c96163d99e..48a84a5831 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] description = "The fast, collaborative code editor." edition = "2021" name = "zed" -version = "0.56.0" +version = "0.57.0" [lib] name = "zed" From fd42811ef1544d56ef4d9eb7887ab7a4ee69efb8 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 30 Sep 2022 09:51:03 +0200 Subject: [PATCH 112/314] Cache `CGEventSource` and avoid leaking `CGEvent` when handling events --- crates/gpui/src/platform/mac/event.rs | 83 +++++++++++++++------------ 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/crates/gpui/src/platform/mac/event.rs b/crates/gpui/src/platform/mac/event.rs index 51524f4b15..ea2b492b27 100644 --- a/crates/gpui/src/platform/mac/event.rs +++ b/crates/gpui/src/platform/mac/event.rs @@ -14,8 +14,10 @@ use core_graphics::{ event::{CGEvent, CGEventFlags, CGKeyCode}, event_source::{CGEventSource, CGEventSourceStateID}, }; +use ctor::ctor; +use foreign_types::ForeignType; use objc::{class, msg_send, sel, sel_impl}; -use std::{borrow::Cow, ffi::CStr, os::raw::c_char}; +use std::{borrow::Cow, ffi::CStr, mem, os::raw::c_char, ptr}; const BACKSPACE_KEY: u16 = 0x7f; const SPACE_KEY: u16 = b' ' as u16; @@ -25,6 +27,15 @@ const ESCAPE_KEY: u16 = 0x1b; const TAB_KEY: u16 = 0x09; const SHIFT_TAB_KEY: u16 = 0x19; +static mut EVENT_SOURCE: core_graphics::sys::CGEventSourceRef = ptr::null_mut(); + +#[ctor] +unsafe fn build_event_source() { + let source = CGEventSource::new(CGEventSourceStateID::Private).unwrap(); + EVENT_SOURCE = source.as_ptr(); + mem::forget(source); +} + pub fn key_to_native(key: &str) -> Cow { use cocoa::appkit::*; let code = match key { @@ -228,7 +239,8 @@ unsafe fn parse_keystroke(native_event: id) -> Keystroke { let mut chars_ignoring_modifiers = CStr::from_ptr(native_event.charactersIgnoringModifiers().UTF8String() as *mut c_char) .to_str() - .unwrap(); + .unwrap() + .to_string(); let first_char = chars_ignoring_modifiers.chars().next().map(|ch| ch as u16); let modifiers = native_event.modifierFlags(); @@ -243,31 +255,31 @@ unsafe fn parse_keystroke(native_event: id) -> Keystroke { #[allow(non_upper_case_globals)] let key = match first_char { - Some(SPACE_KEY) => "space", - Some(BACKSPACE_KEY) => "backspace", - Some(ENTER_KEY) | Some(NUMPAD_ENTER_KEY) => "enter", - Some(ESCAPE_KEY) => "escape", - Some(TAB_KEY) => "tab", - Some(SHIFT_TAB_KEY) => "tab", - Some(NSUpArrowFunctionKey) => "up", - Some(NSDownArrowFunctionKey) => "down", - Some(NSLeftArrowFunctionKey) => "left", - Some(NSRightArrowFunctionKey) => "right", - Some(NSPageUpFunctionKey) => "pageup", - Some(NSPageDownFunctionKey) => "pagedown", - Some(NSDeleteFunctionKey) => "delete", - Some(NSF1FunctionKey) => "f1", - Some(NSF2FunctionKey) => "f2", - Some(NSF3FunctionKey) => "f3", - Some(NSF4FunctionKey) => "f4", - Some(NSF5FunctionKey) => "f5", - Some(NSF6FunctionKey) => "f6", - Some(NSF7FunctionKey) => "f7", - Some(NSF8FunctionKey) => "f8", - Some(NSF9FunctionKey) => "f9", - Some(NSF10FunctionKey) => "f10", - Some(NSF11FunctionKey) => "f11", - Some(NSF12FunctionKey) => "f12", + Some(SPACE_KEY) => "space".to_string(), + Some(BACKSPACE_KEY) => "backspace".to_string(), + Some(ENTER_KEY) | Some(NUMPAD_ENTER_KEY) => "enter".to_string(), + Some(ESCAPE_KEY) => "escape".to_string(), + Some(TAB_KEY) => "tab".to_string(), + Some(SHIFT_TAB_KEY) => "tab".to_string(), + Some(NSUpArrowFunctionKey) => "up".to_string(), + Some(NSDownArrowFunctionKey) => "down".to_string(), + Some(NSLeftArrowFunctionKey) => "left".to_string(), + Some(NSRightArrowFunctionKey) => "right".to_string(), + Some(NSPageUpFunctionKey) => "pageup".to_string(), + Some(NSPageDownFunctionKey) => "pagedown".to_string(), + Some(NSDeleteFunctionKey) => "delete".to_string(), + Some(NSF1FunctionKey) => "f1".to_string(), + Some(NSF2FunctionKey) => "f2".to_string(), + Some(NSF3FunctionKey) => "f3".to_string(), + Some(NSF4FunctionKey) => "f4".to_string(), + Some(NSF5FunctionKey) => "f5".to_string(), + Some(NSF6FunctionKey) => "f6".to_string(), + Some(NSF7FunctionKey) => "f7".to_string(), + Some(NSF8FunctionKey) => "f8".to_string(), + Some(NSF9FunctionKey) => "f9".to_string(), + Some(NSF10FunctionKey) => "f10".to_string(), + Some(NSF11FunctionKey) => "f11".to_string(), + Some(NSF12FunctionKey) => "f12".to_string(), _ => { let mut chars_ignoring_modifiers_and_shift = chars_for_modified_key(native_event.keyCode(), false, false); @@ -303,21 +315,19 @@ unsafe fn parse_keystroke(native_event: id) -> Keystroke { shift, cmd, function, - key: key.into(), + key, } } -fn chars_for_modified_key<'a>(code: CGKeyCode, cmd: bool, shift: bool) -> &'a str { +fn chars_for_modified_key(code: CGKeyCode, cmd: bool, shift: bool) -> String { // Ideally, we would use `[NSEvent charactersByApplyingModifiers]` but that // always returns an empty string with certain keyboards, e.g. Japanese. Synthesizing // an event with the given flags instead lets us access `characters`, which always // returns a valid string. - let event = CGEvent::new_keyboard_event( - CGEventSource::new(CGEventSourceStateID::Private).unwrap(), - code, - true, - ) - .unwrap(); + let source = unsafe { core_graphics::event_source::CGEventSource::from_ptr(EVENT_SOURCE) }; + let event = CGEvent::new_keyboard_event(source.clone(), code, true).unwrap(); + mem::forget(source); + let mut flags = CGEventFlags::empty(); if cmd { flags |= CGEventFlags::CGEventFlagCommand; @@ -327,10 +337,11 @@ fn chars_for_modified_key<'a>(code: CGKeyCode, cmd: bool, shift: bool) -> &'a st } event.set_flags(flags); - let event: id = unsafe { msg_send![class!(NSEvent), eventWithCGEvent: event] }; unsafe { + let event: id = msg_send![class!(NSEvent), eventWithCGEvent: &*event]; CStr::from_ptr(event.characters().UTF8String()) .to_str() .unwrap() + .to_string() } } From 56b416202386c80fa91f3fa159ed32cd7bc3fa32 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Sun, 2 Oct 2022 18:02:25 -0700 Subject: [PATCH 113/314] Fix stray merge failure --- crates/collab/src/rpc.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 15748a52cf..beba653fc6 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -206,11 +206,8 @@ impl Server { .add_message_handler(Server::unfollow) .add_message_handler(Server::update_followers) .add_request_handler(Server::get_channel_messages) -<<<<<<< HEAD - .add_message_handler(Server::update_head_text); -======= + .add_message_handler(Server::update_head_text) .add_request_handler(Server::get_private_user_info); ->>>>>>> 5d09083a (Identify users in amplitude via a separate 'metrics_id' UUID) Arc::new(server) } From c2370751020e52c957c93fa7ddc2cc7506dfd816 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Sun, 2 Oct 2022 18:35:19 -0700 Subject: [PATCH 114/314] Touched up settings text --- assets/settings/default.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/assets/settings/default.json b/assets/settings/default.json index 4ebc1e702f..66cc36c38a 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -74,8 +74,16 @@ "hard_tabs": false, // How many columns a tab should occupy. "tab_size": 4, - // Git gutter behavior configuration. Remove this item to disable git gutters entirely. + // Git gutter behavior configuration. "git_gutter": { + // Which files to show the git gutter on. This setting can take + // three values: + // 1. All files: + // "files_included": "all", + // 2. Only files tracked in git: + // "files_included": "only_tracked", + // 3. Disable git gutters: + // "files_included": "none", "files_included": "all" }, // Settings specific to the terminal From 01176e04b7f0399eb7e6f62bfbe7019cd0f39545 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Sun, 2 Oct 2022 18:42:03 -0700 Subject: [PATCH 115/314] Added clarification for git gutter settings --- assets/settings/default.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/assets/settings/default.json b/assets/settings/default.json index 66cc36c38a..11a4b72a10 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -78,11 +78,12 @@ "git_gutter": { // Which files to show the git gutter on. This setting can take // three values: - // 1. All files: + // 1. All files, files not tracked in git will be diffed against + // their contents when the file was last opened in Zed: // "files_included": "all", - // 2. Only files tracked in git: + // 2. Only show for files tracked in git: // "files_included": "only_tracked", - // 3. Disable git gutters: + // 3. Disable git gutters entirely: // "files_included": "none", "files_included": "all" }, From 64260376539b1929734371ddc12eea44bc6cd9d1 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 3 Oct 2022 15:44:11 +0200 Subject: [PATCH 116/314] Adapt integration tests to always pass a room id to `Project::share` Randomized test is failing, so we'll look into that next. --- crates/call/src/room.rs | 14 +- crates/collab/src/integration_tests.rs | 481 +++++++++++++++---------- crates/collab/src/rpc.rs | 15 +- crates/collab/src/rpc/store.rs | 53 ++- crates/project/src/project.rs | 10 +- 5 files changed, 349 insertions(+), 224 deletions(-) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index ca8d5ea95f..f371384a52 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -24,6 +24,10 @@ pub struct Room { impl Entity for Room { type Event = Event; + + fn release(&mut self, _: &mut MutableAppContext) { + self.client.send(proto::LeaveRoom { id: self.id }).log_err(); + } } impl Room { @@ -99,6 +103,14 @@ impl Room { Ok(()) } + pub fn id(&self) -> u64 { + self.id + } + + pub fn status(&self) -> RoomStatus { + self.status + } + pub fn remote_participants(&self) -> &HashMap { &self.remote_participants } @@ -183,7 +195,7 @@ pub enum RoomStatus { } impl RoomStatus { - fn is_offline(&self) -> bool { + pub fn is_offline(&self) -> bool { matches!(self, RoomStatus::Offline) } } diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 8753ddee35..3bb63c0229 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -74,11 +74,7 @@ async fn test_basic_calls( let client_b = server.create_client(cx_b, "user_b").await; let client_c = server.create_client(cx_c, "user_c").await; server - .make_contacts(vec![ - (&client_a, cx_a), - (&client_b, cx_b), - (&client_c, cx_c), - ]) + .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; let room_a = cx_a @@ -224,7 +220,7 @@ async fn test_leaving_room_on_disconnection( let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; let room_a = cx_a @@ -286,15 +282,14 @@ async fn test_share_project( deterministic: Arc, cx_a: &mut TestAppContext, cx_b: &mut TestAppContext, - cx_b2: &mut TestAppContext, ) { cx_a.foreground().forbid_parking(); let (_, window_b) = cx_b.add_window(|_| EmptyView); let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; client_a @@ -315,7 +310,7 @@ async fn test_share_project( let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); @@ -390,30 +385,6 @@ async fn test_share_project( // buffer_a // .condition(&cx_a, |buffer, _| buffer.selection_sets().count() == 0) // .await; - - // Client B can join again on a different window because they are already a participant. - let client_b2 = server.create_client(cx_b2, "user_b").await; - let project_b2 = client_b2.build_remote_project(project_id, cx_b2).await; - deterministic.run_until_parked(); - project_a.read_with(cx_a, |project, _| { - assert_eq!(project.collaborators().len(), 2); - }); - project_b.read_with(cx_b, |project, _| { - assert_eq!(project.collaborators().len(), 2); - }); - project_b2.read_with(cx_b2, |project, _| { - assert_eq!(project.collaborators().len(), 2); - }); - - // Dropping client B's first project removes only that from client A's collaborators. - cx_b.update(move |_| drop(project_b)); - deterministic.run_until_parked(); - project_a.read_with(cx_a, |project, _| { - assert_eq!(project.collaborators().len(), 1); - }); - project_b2.read_with(cx_b2, |project, _| { - assert_eq!(project.collaborators().len(), 1); - }); } #[gpui::test(iterations = 10)] @@ -421,13 +392,15 @@ async fn test_unshare_project( deterministic: Arc, cx_a: &mut TestAppContext, cx_b: &mut TestAppContext, + cx_c: &mut TestAppContext, ) { - cx_a.foreground().forbid_parking(); + deterministic.forbid_parking(); let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let client_c = server.create_client(cx_c, "user_c").await; + let (room_id, mut rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; client_a @@ -443,7 +416,7 @@ async fn test_unshare_project( let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap()); @@ -455,30 +428,39 @@ async fn test_unshare_project( .await .unwrap(); - // When client A unshares the project, client B's project becomes read-only. + // When client B leaves the room, the project becomes read-only. + cx_b.update(|_| drop(rooms.remove(1))); + deterministic.run_until_parked(); + assert!(project_b.read_with(cx_b, |project, _| project.is_read_only())); + + // Client C opens the project. + let project_c = client_c.build_remote_project(project_id, cx_c).await; + + // When client A unshares the project, client C's project becomes read-only. project_a .update(cx_a, |project, cx| project.unshare(cx)) .unwrap(); deterministic.run_until_parked(); assert!(worktree_a.read_with(cx_a, |tree, _| !tree.as_local().unwrap().is_shared())); - assert!(project_b.read_with(cx_b, |project, _| project.is_read_only())); + assert!(project_c.read_with(cx_c, |project, _| project.is_read_only())); - // Client B can join again after client A re-shares. + // Client C can open the project again after client A re-shares. let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); - let project_b2 = client_b.build_remote_project(project_id, cx_b).await; + let project_c2 = client_c.build_remote_project(project_id, cx_c).await; assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared())); - project_b2 - .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)) + project_c2 + .update(cx_c, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)) .await .unwrap(); - // When client A (the host) leaves, the project gets unshared and guests are notified. - cx_a.update(|_| drop(project_a)); + // When client A (the host) leaves the room, the project gets unshared and guests are notified. + cx_a.update(|_| drop(rooms.remove(0))); deterministic.run_until_parked(); - project_b2.read_with(cx_b, |project, _| { + project_a.read_with(cx_a, |project, _| assert!(!project.is_shared())); + project_c2.read_with(cx_c, |project, _| { assert!(project.is_read_only()); assert!(project.collaborators().is_empty()); }); @@ -497,12 +479,8 @@ async fn test_host_disconnect( let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; let client_c = server.create_client(cx_c, "user_c").await; - server - .make_contacts(vec![ - (&client_a, cx_a), - (&client_b, cx_b), - (&client_c, cx_c), - ]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; client_a @@ -519,7 +497,7 @@ async fn test_host_disconnect( let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap()); let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); @@ -576,18 +554,6 @@ async fn test_host_disconnect( drop(workspace_b); drop(project_b); }); - - // Ensure guests can still join. - let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) - .await - .unwrap(); - let project_b2 = client_b.build_remote_project(project_id, cx_b).await; - assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared())); - project_b2 - .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)) - .await - .unwrap(); } #[gpui::test(iterations = 10)] @@ -601,12 +567,8 @@ async fn test_propagate_saves_and_fs_changes( let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; let client_c = server.create_client(cx_c, "user_c").await; - server - .make_contacts(vec![ - (&client_a, cx_a), - (&client_b, cx_b), - (&client_c, cx_c), - ]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; client_a @@ -622,7 +584,7 @@ async fn test_propagate_saves_and_fs_changes( let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; let worktree_a = project_a.read_with(cx_a, |p, cx| p.worktrees(cx).next().unwrap()); let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); @@ -753,8 +715,8 @@ async fn test_fs_operations( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; client_a @@ -769,7 +731,7 @@ async fn test_fs_operations( .await; let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -1018,8 +980,8 @@ async fn test_buffer_conflict_after_save(cx_a: &mut TestAppContext, cx_b: &mut T let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; client_a @@ -1033,7 +995,7 @@ async fn test_buffer_conflict_after_save(cx_a: &mut TestAppContext, cx_b: &mut T .await; let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -1071,8 +1033,8 @@ async fn test_buffer_reloading(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; client_a @@ -1086,7 +1048,7 @@ async fn test_buffer_reloading(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont .await; let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -1129,8 +1091,8 @@ async fn test_editing_while_guest_opens_buffer( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; client_a @@ -1139,7 +1101,7 @@ async fn test_editing_while_guest_opens_buffer( .await; let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -1175,8 +1137,8 @@ async fn test_leaving_worktree_while_opening_buffer( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; client_a @@ -1185,7 +1147,7 @@ async fn test_leaving_worktree_while_opening_buffer( .await; let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -1219,8 +1181,8 @@ async fn test_canceling_buffer_opening( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; client_a @@ -1234,7 +1196,7 @@ async fn test_canceling_buffer_opening( .await; let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -1259,13 +1221,19 @@ async fn test_canceling_buffer_opening( } #[gpui::test(iterations = 10)] -async fn test_leaving_project(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { - cx_a.foreground().forbid_parking(); +async fn test_leaving_project( + deterministic: Arc, + cx_a: &mut TestAppContext, + cx_b: &mut TestAppContext, + cx_c: &mut TestAppContext, +) { + deterministic.forbid_parking(); let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let client_c = server.create_client(cx_c, "user_c").await; + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; client_a @@ -1280,37 +1248,66 @@ async fn test_leaving_project(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte .await; let (project_a, _) = client_a.build_local_project("/a", cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); - let _project_b = client_b.build_remote_project(project_id, cx_b).await; + let project_b = client_b.build_remote_project(project_id, cx_b).await; + let project_c = client_c.build_remote_project(project_id, cx_c).await; // Client A sees that a guest has joined. - project_a - .condition(cx_a, |p, _| p.collaborators().len() == 1) - .await; + deterministic.run_until_parked(); + project_a.read_with(cx_a, |project, _| { + assert_eq!(project.collaborators().len(), 2); + }); + project_b.read_with(cx_b, |project, _| { + assert_eq!(project.collaborators().len(), 2); + }); + project_c.read_with(cx_c, |project, _| { + assert_eq!(project.collaborators().len(), 2); + }); - // Drop client B's connection and ensure client A observes client B leaving the project. + // Drop client B's connection and ensure client A and client C observe client B leaving the project. client_b.disconnect(&cx_b.to_async()).unwrap(); - project_a - .condition(cx_a, |p, _| p.collaborators().is_empty()) - .await; + deterministic.run_until_parked(); + project_a.read_with(cx_a, |project, _| { + assert_eq!(project.collaborators().len(), 1); + }); + project_b.read_with(cx_b, |project, _| { + assert!(project.is_read_only()); + }); + project_c.read_with(cx_c, |project, _| { + assert_eq!(project.collaborators().len(), 1); + }); - // Rejoin the project as client B - let _project_b = client_b.build_remote_project(project_id, cx_b).await; + // Client B can't join the project, unless they re-join the room. + cx_b.spawn(|cx| { + Project::remote( + project_id, + client_b.client.clone(), + client_b.user_store.clone(), + client_b.project_store.clone(), + client_b.language_registry.clone(), + FakeFs::new(cx.background()), + cx, + ) + }) + .await + .unwrap_err(); - // Client A sees that a guest has re-joined. - project_a - .condition(cx_a, |p, _| p.collaborators().len() == 1) - .await; - - // Simulate connection loss for client B and ensure client A observes client B leaving the project. - client_b.wait_for_current_user(cx_b).await; - server.disconnect_client(client_b.current_user_id(cx_b)); + // Simulate connection loss for client C and ensure client A observes client C leaving the project. + client_c.wait_for_current_user(cx_c).await; + server.disconnect_client(client_c.current_user_id(cx_c)); cx_a.foreground().advance_clock(rpc::RECEIVE_TIMEOUT); - project_a - .condition(cx_a, |p, _| p.collaborators().is_empty()) - .await; + deterministic.run_until_parked(); + project_a.read_with(cx_a, |project, _| { + assert_eq!(project.collaborators().len(), 0); + }); + project_b.read_with(cx_b, |project, _| { + assert!(project.is_read_only()); + }); + project_c.read_with(cx_c, |project, _| { + assert!(project.is_read_only()); + }); } #[gpui::test(iterations = 10)] @@ -1325,12 +1322,8 @@ async fn test_collaborating_with_diagnostics( let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; let client_c = server.create_client(cx_c, "user_c").await; - server - .make_contacts(vec![ - (&client_a, cx_a), - (&client_b, cx_b), - (&client_c, cx_c), - ]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; // Set up a fake language server. @@ -1358,7 +1351,7 @@ async fn test_collaborating_with_diagnostics( .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); @@ -1566,8 +1559,8 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; // Set up a fake language server. @@ -1605,7 +1598,7 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -1739,8 +1732,8 @@ async fn test_reloading_buffer_manually(cx_a: &mut TestAppContext, cx_b: &mut Te let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; client_a @@ -1753,7 +1746,7 @@ async fn test_reloading_buffer_manually(cx_a: &mut TestAppContext, cx_b: &mut Te .await .unwrap(); let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); @@ -1831,8 +1824,8 @@ async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppCon let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; // Set up a fake language server. @@ -1857,7 +1850,7 @@ async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppCon .await; let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -1931,8 +1924,8 @@ async fn test_definition(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; // Set up a fake language server. @@ -1964,7 +1957,7 @@ async fn test_definition(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { .await; let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -2074,8 +2067,8 @@ async fn test_references(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; // Set up a fake language server. @@ -2107,7 +2100,7 @@ async fn test_references(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { .await; let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -2174,8 +2167,8 @@ async fn test_project_search(cx_a: &mut TestAppContext, cx_b: &mut TestAppContex let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; client_a @@ -2206,7 +2199,7 @@ async fn test_project_search(cx_a: &mut TestAppContext, cx_b: &mut TestAppContex .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete()) .await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); @@ -2252,8 +2245,8 @@ async fn test_document_highlights(cx_a: &mut TestAppContext, cx_b: &mut TestAppC let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; client_a @@ -2280,7 +2273,7 @@ async fn test_document_highlights(cx_a: &mut TestAppContext, cx_b: &mut TestAppC let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -2353,8 +2346,8 @@ async fn test_lsp_hover(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; client_a @@ -2381,7 +2374,7 @@ async fn test_lsp_hover(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -2455,8 +2448,8 @@ async fn test_project_symbols(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; // Set up a fake language server. @@ -2490,7 +2483,7 @@ async fn test_project_symbols(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte .await; let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -2562,8 +2555,8 @@ async fn test_open_buffer_while_getting_definition_pointing_to_it( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; // Set up a fake language server. @@ -2590,7 +2583,7 @@ async fn test_open_buffer_while_getting_definition_pointing_to_it( .await; let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -2637,8 +2630,8 @@ async fn test_collaborating_with_code_actions( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; // Set up a fake language server. @@ -2665,7 +2658,7 @@ async fn test_collaborating_with_code_actions( .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); @@ -2847,8 +2840,8 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; // Set up a fake language server. @@ -2886,7 +2879,7 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T .await; let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -3038,8 +3031,8 @@ async fn test_language_server_statuses( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; // Set up a fake language server. @@ -3098,7 +3091,7 @@ async fn test_language_server_statuses( }); let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -3559,11 +3552,7 @@ async fn test_contacts( let client_b = server.create_client(cx_b, "user_b").await; let client_c = server.create_client(cx_c, "user_c").await; server - .make_contacts(vec![ - (&client_a, cx_a), - (&client_b, cx_b), - (&client_c, cx_c), - ]) + .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; deterministic.run_until_parked(); @@ -3815,8 +3804,8 @@ async fn test_following(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; cx_a.update(editor::init); cx_b.update(editor::init); @@ -3834,7 +3823,7 @@ async fn test_following(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -4024,8 +4013,8 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; cx_a.update(editor::init); cx_b.update(editor::init); @@ -4045,7 +4034,7 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); @@ -4192,8 +4181,8 @@ async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; cx_a.update(editor::init); cx_b.update(editor::init); @@ -4212,7 +4201,7 @@ async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -4355,8 +4344,8 @@ async fn test_peers_simultaneously_following_each_other( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - server - .make_contacts(vec![(&client_a, cx_a), (&client_b, cx_b)]) + let (room_id, _rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; cx_a.update(editor::init); cx_b.update(editor::init); @@ -4365,7 +4354,7 @@ async fn test_peers_simultaneously_following_each_other( let (project_a, _) = client_a.build_local_project("/a", cx_a).await; let workspace_a = client_a.build_workspace(&project_a, cx_a); let project_id = project_a - .update(cx_a, |project, cx| project.share(cx)) + .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); @@ -4432,7 +4421,8 @@ async fn test_random_collaboration( let mut server = TestServer::start(cx.foreground(), cx.background()).await; let db = server.app_state.db.clone(); - let host_user_id = db.create_user("host", None, false).await.unwrap(); + + let room_creator_user_id = db.create_user("room-creator", None, false).await.unwrap(); let mut available_guests = vec![ "guest-1".to_string(), "guest-2".to_string(), @@ -4440,23 +4430,32 @@ async fn test_random_collaboration( "guest-4".to_string(), ]; - for username in &available_guests { - let guest_user_id = db.create_user(username, None, false).await.unwrap(); - assert_eq!(*username, format!("guest-{}", guest_user_id)); + for username in Some(&"host".to_string()) + .into_iter() + .chain(&available_guests) + { + let user_id = db.create_user(username, None, false).await.unwrap(); server .app_state .db - .send_contact_request(guest_user_id, host_user_id) + .send_contact_request(user_id, room_creator_user_id) .await .unwrap(); server .app_state .db - .respond_to_contact_request(host_user_id, guest_user_id, true) + .respond_to_contact_request(room_creator_user_id, user_id, true) .await .unwrap(); } + let client = server.create_client(cx, "room-creator").await; + let room = cx + .update(|cx| Room::create(client.client.clone(), client.user_store.clone(), cx)) + .await + .unwrap(); + let room_id = room.read_with(cx, |room, _| room.id()); + let mut clients = Vec::new(); let mut user_ids = Vec::new(); let mut op_start_signals = Vec::new(); @@ -4622,15 +4621,36 @@ async fn test_random_collaboration( .await; host_language_registry.add(Arc::new(language)); + let host_user_id = host.current_user_id(&host_cx); + room.update(cx, |room, cx| room.call(host_user_id.to_proto(), cx)) + .await + .unwrap(); + deterministic.run_until_parked(); + let call = host + .user_store + .read_with(&host_cx, |user_store, _| user_store.incoming_call()); + let host_room = host_cx + .update(|cx| { + Room::join( + call.borrow().as_ref().unwrap(), + host.client.clone(), + host.user_store.clone(), + cx, + ) + }) + .await + .unwrap(); + let host_project_id = host_project - .update(&mut host_cx, |project, cx| project.share(cx)) + .update(&mut host_cx, |project, cx| project.share(room_id, cx)) .await .unwrap(); let op_start_signal = futures::channel::mpsc::unbounded(); - user_ids.push(host.current_user_id(&host_cx)); + user_ids.push(host_user_id); op_start_signals.push(op_start_signal.0); clients.push(host_cx.foreground().spawn(host.simulate_host( + host_room, host_project, op_start_signal.1, rng.clone(), @@ -4692,6 +4712,28 @@ async fn test_random_collaboration( deterministic.start_waiting(); let guest = server.create_client(&mut guest_cx, &guest_username).await; + let guest_user_id = guest.current_user_id(&guest_cx); + + room.update(cx, |room, cx| room.call(guest_user_id.to_proto(), cx)) + .await + .unwrap(); + deterministic.run_until_parked(); + let call = guest + .user_store + .read_with(&guest_cx, |user_store, _| user_store.incoming_call()); + + let guest_room = guest_cx + .update(|cx| { + Room::join( + call.borrow().as_ref().unwrap(), + guest.client.clone(), + guest.user_store.clone(), + cx, + ) + }) + .await + .unwrap(); + let guest_project = Project::remote( host_project_id, guest.client.clone(), @@ -4706,10 +4748,11 @@ async fn test_random_collaboration( deterministic.finish_waiting(); let op_start_signal = futures::channel::mpsc::unbounded(); - user_ids.push(guest.current_user_id(&guest_cx)); + user_ids.push(guest_user_id); op_start_signals.push(op_start_signal.0); clients.push(guest_cx.foreground().spawn(guest.simulate_guest( guest_username.clone(), + guest_room, guest_project, op_start_signal.1, rng.clone(), @@ -5039,12 +5082,14 @@ impl TestServer { self.forbid_connections.store(false, SeqCst); } - async fn make_contacts(&self, mut clients: Vec<(&TestClient, &mut TestAppContext)>) { - while let Some((client_a, cx_a)) = clients.pop() { - for (client_b, cx_b) in &mut clients { + async fn make_contacts(&self, clients: &mut [(&TestClient, &mut TestAppContext)]) { + for ix in 1..clients.len() { + let (left, right) = clients.split_at_mut(ix); + let (client_a, cx_a) = left.last_mut().unwrap(); + for (client_b, cx_b) in right { client_a .user_store - .update(cx_a, |store, cx| { + .update(*cx_a, |store, cx| { store.request_contact(client_b.user_id().unwrap(), cx) }) .await @@ -5061,6 +5106,52 @@ impl TestServer { } } + async fn create_rooms( + &self, + clients: &mut [(&TestClient, &mut TestAppContext)], + ) -> (u64, Vec>) { + self.make_contacts(clients).await; + + let mut rooms = Vec::new(); + + let (left, right) = clients.split_at_mut(1); + let (client_a, cx_a) = &mut left[0]; + + let room_a = cx_a + .update(|cx| Room::create(client_a.client.clone(), client_a.user_store.clone(), cx)) + .await + .unwrap(); + let room_id = room_a.read_with(*cx_a, |room, _| room.id()); + + for (client_b, cx_b) in right { + let user_id_b = client_b.current_user_id(*cx_b).to_proto(); + room_a + .update(*cx_a, |room, cx| room.call(user_id_b, cx)) + .await + .unwrap(); + + cx_b.foreground().run_until_parked(); + let incoming_call = client_b + .user_store + .read_with(*cx_b, |user_store, _| user_store.incoming_call()); + let room_b = cx_b + .update(|cx| { + Room::join( + incoming_call.borrow().as_ref().unwrap(), + client_b.client.clone(), + client_b.user_store.clone(), + cx, + ) + }) + .await + .unwrap(); + rooms.push(room_b); + } + + rooms.insert(0, room_a); + (room_id, rooms) + } + async fn build_app_state(test_db: &TestDb) -> Arc { Arc::new(AppState { db: test_db.db().clone(), @@ -5224,6 +5315,7 @@ impl TestClient { async fn simulate_host( mut self, + _room: ModelHandle, project: ModelHandle, op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>, rng: Arc>, @@ -5361,6 +5453,7 @@ impl TestClient { pub async fn simulate_guest( mut self, guest_username: String, + _room: ModelHandle, project: ModelHandle, op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>, rng: Arc>, diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 5b89dc6bf6..31f99759cd 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -635,6 +635,13 @@ impl Server { }, )?; } + + self.peer.send( + message.sender_id, + proto::UnshareProject { + project_id: project.id.to_proto(), + }, + )?; } } @@ -798,14 +805,6 @@ impl Server { }; tracing::info!(%project_id, %host_user_id, %host_connection_id, "join project"); - let has_contact = self - .app_state - .db - .has_contact(guest_user_id, host_user_id) - .await?; - if !has_contact { - return Err(anyhow!("no such project"))?; - } let mut store = self.store().await; let (project, replica_id) = store.join_project(request.sender_id, project_id)?; diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index 2ae52f7c2b..9b241446b5 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -39,7 +39,7 @@ struct ConnectionState { pub struct Call { pub caller_user_id: UserId, pub room_id: RoomId, - pub joined: bool, + pub connection_id: Option, } #[derive(Serialize)] @@ -163,7 +163,7 @@ impl Store { let connected_user = self.connected_users.entry(user_id).or_default(); connected_user.connection_ids.insert(connection_id); if let Some(active_call) = connected_user.active_call { - if active_call.joined { + if active_call.connection_id.is_some() { None } else { let room = self.room(active_call.room_id)?; @@ -378,7 +378,7 @@ impl Store { connected_user.active_call = Some(Call { caller_user_id: connection.user_id, room_id, - joined: true, + connection_id: Some(creator_connection_id), }); Ok(room_id) } @@ -404,7 +404,7 @@ impl Store { .as_mut() .ok_or_else(|| anyhow!("not being called"))?; anyhow::ensure!( - active_call.room_id == room_id && !active_call.joined, + active_call.room_id == room_id && active_call.connection_id.is_none(), "not being called on this room" ); @@ -428,7 +428,7 @@ impl Store { )), }), }); - active_call.joined = true; + active_call.connection_id = Some(connection_id); Ok((room, recipient_connection_ids)) } @@ -440,25 +440,20 @@ impl Store { .ok_or_else(|| anyhow!("no such connection"))?; let user_id = connection.user_id; - let mut connected_user = self + let connected_user = self .connected_users - .get_mut(&user_id) + .get(&user_id) .ok_or_else(|| anyhow!("no such connection"))?; anyhow::ensure!( connected_user .active_call - .map_or(false, |call| call.room_id == room_id && call.joined), + .map_or(false, |call| call.room_id == room_id + && call.connection_id == Some(connection_id)), "cannot leave a room before joining it" ); - let room = self - .rooms - .get_mut(&room_id) - .ok_or_else(|| anyhow!("no such room"))?; - room.participants - .retain(|participant| participant.peer_id != connection_id.0); - connected_user.active_call = None; - + // Given that users can only join one room at a time, we can safely unshare + // and leave all projects associated with the connection. let mut unshared_projects = Vec::new(); let mut left_projects = Vec::new(); for project_id in connection.projects.clone() { @@ -468,8 +463,15 @@ impl Store { left_projects.push(project); } } + self.connected_users.get_mut(&user_id).unwrap().active_call = None; + + let room = self + .rooms + .get_mut(&room_id) + .ok_or_else(|| anyhow!("no such room"))?; + room.participants + .retain(|participant| participant.peer_id != connection_id.0); - let room = self.rooms.get(&room_id).unwrap(); Ok(LeftRoom { room, unshared_projects, @@ -521,7 +523,7 @@ impl Store { recipient.active_call = Some(Call { caller_user_id, room_id, - joined: false, + connection_id: None, }); Ok(( @@ -546,7 +548,8 @@ impl Store { .ok_or_else(|| anyhow!("no such connection"))?; anyhow::ensure!(recipient .active_call - .map_or(false, |call| call.room_id == room_id && !call.joined)); + .map_or(false, |call| call.room_id == room_id + && call.connection_id.is_none())); recipient.active_call = None; let room = self .rooms @@ -754,10 +757,22 @@ impl Store { .connections .get_mut(&requester_connection_id) .ok_or_else(|| anyhow!("no such connection"))?; + let user = self + .connected_users + .get(&connection.user_id) + .ok_or_else(|| anyhow!("no such connection"))?; + let active_call = user.active_call.ok_or_else(|| anyhow!("no such project"))?; + anyhow::ensure!( + active_call.connection_id == Some(requester_connection_id), + "no such project" + ); + let project = self .projects .get_mut(&project_id) .ok_or_else(|| anyhow!("no such project"))?; + anyhow::ensure!(project.room_id == active_call.room_id, "no such project"); + connection.projects.insert(project_id); let mut replica_id = 1; while project.active_replica_ids.contains(&replica_id) { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 901e3b7d85..c0ed2e5ad4 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4435,8 +4435,14 @@ impl Project { _: Arc, mut cx: AsyncAppContext, ) -> Result<()> { - this.update(&mut cx, |this, cx| this.disconnected_from_host(cx)); - Ok(()) + this.update(&mut cx, |this, cx| { + if this.is_local() { + this.unshare(cx)?; + } else { + this.disconnected_from_host(cx); + } + Ok(()) + }) } async fn handle_add_collaborator( From bec6b41448435ab458536d975421d6a918cbdc67 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 3 Oct 2022 15:50:47 +0200 Subject: [PATCH 117/314] Fix randomized integration test failure --- crates/collab/src/integration_tests.rs | 30 ++++++++++++++------------ 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 3bb63c0229..0a789775be 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -4675,20 +4675,20 @@ async fn test_random_collaboration( deterministic.finish_waiting(); deterministic.run_until_parked(); - let (host, host_project, mut host_cx, host_err) = clients.remove(0); + let (host, host_room, host_project, mut host_cx, host_err) = clients.remove(0); if let Some(host_err) = host_err { log::error!("host error - {:?}", host_err); } host_project.read_with(&host_cx, |project, _| assert!(!project.is_shared())); - for (guest, guest_project, mut guest_cx, guest_err) in clients { + for (guest, guest_room, guest_project, mut guest_cx, guest_err) in clients { if let Some(guest_err) = guest_err { log::error!("{} error - {:?}", guest.username, guest_err); } guest_project.read_with(&guest_cx, |project, _| assert!(project.is_read_only())); - guest_cx.update(|_| drop((guest, guest_project))); + guest_cx.update(|_| drop((guest, guest_room, guest_project))); } - host_cx.update(|_| drop((host, host_project))); + host_cx.update(|_| drop((host, host_room, host_project))); return; } @@ -4773,7 +4773,7 @@ async fn test_random_collaboration( deterministic.advance_clock(RECEIVE_TIMEOUT); deterministic.start_waiting(); log::info!("Waiting for guest {} to exit...", removed_guest_id); - let (guest, guest_project, mut guest_cx, guest_err) = guest.await; + let (guest, guest_room, guest_project, mut guest_cx, guest_err) = guest.await; deterministic.finish_waiting(); server.allow_connections(); @@ -4801,7 +4801,7 @@ async fn test_random_collaboration( log::info!("{} removed", guest.username); available_guests.push(guest.username.clone()); - guest_cx.update(|_| drop((guest, guest_project))); + guest_cx.update(|_| drop((guest, guest_room, guest_project))); operations += 1; } @@ -4828,7 +4828,7 @@ async fn test_random_collaboration( deterministic.finish_waiting(); deterministic.run_until_parked(); - let (host_client, host_project, mut host_cx, host_err) = clients.remove(0); + let (host_client, host_room, host_project, mut host_cx, host_err) = clients.remove(0); if let Some(host_err) = host_err { panic!("host error - {:?}", host_err); } @@ -4844,7 +4844,7 @@ async fn test_random_collaboration( host_project.read_with(&host_cx, |project, cx| project.check_invariants(cx)); - for (guest_client, guest_project, mut guest_cx, guest_err) in clients.into_iter() { + for (guest_client, guest_room, guest_project, mut guest_cx, guest_err) in clients.into_iter() { if let Some(guest_err) = guest_err { panic!("{} error - {:?}", guest_client.username, guest_err); } @@ -4916,10 +4916,10 @@ async fn test_random_collaboration( ); } - guest_cx.update(|_| drop((guest_project, guest_client))); + guest_cx.update(|_| drop((guest_room, guest_project, guest_client))); } - host_cx.update(|_| drop((host_client, host_project))); + host_cx.update(|_| drop((host_client, host_room, host_project))); } struct TestServer { @@ -5315,13 +5315,14 @@ impl TestClient { async fn simulate_host( mut self, - _room: ModelHandle, + room: ModelHandle, project: ModelHandle, op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>, rng: Arc>, mut cx: TestAppContext, ) -> ( Self, + ModelHandle, ModelHandle, TestAppContext, Option, @@ -5447,19 +5448,20 @@ impl TestClient { let result = simulate_host_internal(&mut self, project.clone(), op_start_signal, rng, &mut cx).await; log::info!("Host done"); - (self, project, cx, result.err()) + (self, room, project, cx, result.err()) } pub async fn simulate_guest( mut self, guest_username: String, - _room: ModelHandle, + room: ModelHandle, project: ModelHandle, op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>, rng: Arc>, mut cx: TestAppContext, ) -> ( Self, + ModelHandle, ModelHandle, TestAppContext, Option, @@ -5778,7 +5780,7 @@ impl TestClient { .await; log::info!("{}: done", guest_username); - (self, project, cx, result.err()) + (self, room, project, cx, result.err()) } } From da6106db8e6abae46e172181e68f4713b8b675ae Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 3 Oct 2022 15:54:20 +0200 Subject: [PATCH 118/314] Prevent calls from users who aren't contacts --- crates/collab/src/rpc.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 31f99759cd..64d81b51d7 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -654,7 +654,20 @@ impl Server { request: TypedEnvelope, response: Response, ) -> Result<()> { + let caller_user_id = self + .store() + .await + .user_id_for_connection(request.sender_id)?; let recipient_user_id = UserId::from_proto(request.payload.recipient_user_id); + if !self + .app_state + .db + .has_contact(caller_user_id, recipient_user_id) + .await? + { + return Err(anyhow!("cannot call a user who isn't a contact"))?; + } + let room_id = request.payload.room_id; let mut calls = { let mut store = self.store().await; From ad323d6e3b079c8a4438083d64f140584216fd87 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 3 Oct 2022 16:09:49 +0200 Subject: [PATCH 119/314] Automatically fetch remote participant users in `Room` --- crates/call/src/participant.rs | 9 ++-- crates/call/src/room.rs | 67 +++++++++++++++++--------- crates/collab/src/integration_tests.rs | 67 ++++++++++---------------- 3 files changed, 72 insertions(+), 71 deletions(-) diff --git a/crates/call/src/participant.rs b/crates/call/src/participant.rs index c6257b2895..cf7c965816 100644 --- a/crates/call/src/participant.rs +++ b/crates/call/src/participant.rs @@ -1,7 +1,6 @@ use anyhow::{anyhow, Result}; -use client::proto; -use gpui::ModelHandle; -use project::Project; +use client::{proto, User}; +use std::sync::Arc; pub enum ParticipantLocation { Project { project_id: u64 }, @@ -21,7 +20,7 @@ impl ParticipantLocation { } pub struct RemoteParticipant { - pub user_id: u64, - pub projects: Vec>, + pub user: Arc, + pub project_ids: Vec, pub location: ParticipantLocation, } diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index f371384a52..d6a0012e30 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -19,7 +19,7 @@ pub struct Room { client: Arc, user_store: ModelHandle, _subscriptions: Vec, - _load_pending_users: Option>, + _pending_room_update: Option>, } impl Entity for Room { @@ -58,7 +58,7 @@ impl Room { remote_participants: Default::default(), pending_users: Default::default(), _subscriptions: vec![client.add_message_handler(cx.handle(), Self::handle_room_updated)], - _load_pending_users: None, + _pending_room_update: None, client, user_store, } @@ -133,32 +133,51 @@ impl Room { Ok(()) } - fn apply_room_update(&mut self, room: proto::Room, cx: &mut ModelContext) -> Result<()> { - // TODO: compute diff instead of clearing participants - self.remote_participants.clear(); - for participant in room.participants { - if Some(participant.user_id) != self.client.user_id() { - self.remote_participants.insert( - PeerId(participant.peer_id), - RemoteParticipant { - user_id: participant.user_id, - projects: Default::default(), // TODO: populate projects - location: ParticipantLocation::from_proto(participant.location)?, - }, - ); - } - } + fn apply_room_update( + &mut self, + mut room: proto::Room, + cx: &mut ModelContext, + ) -> Result<()> { + // Filter ourselves out from the room's participants. + room.participants + .retain(|participant| Some(participant.user_id) != self.client.user_id()); - let pending_users = self.user_store.update(cx, move |user_store, cx| { - user_store.get_users(room.pending_user_ids, cx) + let participant_user_ids = room + .participants + .iter() + .map(|p| p.user_id) + .collect::>(); + let (participants, pending_users) = self.user_store.update(cx, move |user_store, cx| { + ( + user_store.get_users(participant_user_ids, cx), + user_store.get_users(room.pending_user_ids, cx), + ) }); - self._load_pending_users = Some(cx.spawn(|this, mut cx| async move { - if let Some(pending_users) = pending_users.await.log_err() { - this.update(&mut cx, |this, cx| { + self._pending_room_update = Some(cx.spawn(|this, mut cx| async move { + let (participants, pending_users) = futures::join!(participants, pending_users); + + this.update(&mut cx, |this, cx| { + if let Some(participants) = participants.log_err() { + // TODO: compute diff instead of clearing participants + this.remote_participants.clear(); + for (participant, user) in room.participants.into_iter().zip(participants) { + this.remote_participants.insert( + PeerId(participant.peer_id), + RemoteParticipant { + user, + project_ids: participant.project_ids, + location: ParticipantLocation::from_proto(participant.location) + .unwrap_or(ParticipantLocation::External), + }, + ); + } + } + + if let Some(pending_users) = pending_users.log_err() { this.pending_users = pending_users; cx.notify(); - }); - } + } + }); })); cx.notify(); diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 0a789775be..0bc848f107 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -82,7 +82,7 @@ async fn test_basic_calls( .await .unwrap(); assert_eq!( - room_participants(&room_a, &client_a, cx_a).await, + room_participants(&room_a, cx_a), RoomParticipants { remote: Default::default(), pending: Default::default() @@ -100,7 +100,7 @@ async fn test_basic_calls( deterministic.run_until_parked(); assert_eq!( - room_participants(&room_a, &client_a, cx_a).await, + room_participants(&room_a, cx_a), RoomParticipants { remote: Default::default(), pending: vec!["user_b".to_string()] @@ -127,14 +127,14 @@ async fn test_basic_calls( deterministic.run_until_parked(); assert_eq!( - room_participants(&room_a, &client_a, cx_a).await, + room_participants(&room_a, cx_a), RoomParticipants { remote: vec!["user_b".to_string()], pending: Default::default() } ); assert_eq!( - room_participants(&room_b, &client_b, cx_b).await, + room_participants(&room_b, cx_b), RoomParticipants { remote: vec!["user_a".to_string()], pending: Default::default() @@ -152,14 +152,14 @@ async fn test_basic_calls( deterministic.run_until_parked(); assert_eq!( - room_participants(&room_a, &client_a, cx_a).await, + room_participants(&room_a, cx_a), RoomParticipants { remote: vec!["user_b".to_string()], pending: vec!["user_c".to_string()] } ); assert_eq!( - room_participants(&room_b, &client_b, cx_b).await, + room_participants(&room_b, cx_b), RoomParticipants { remote: vec!["user_a".to_string()], pending: vec!["user_c".to_string()] @@ -176,14 +176,14 @@ async fn test_basic_calls( deterministic.run_until_parked(); assert_eq!( - room_participants(&room_a, &client_a, cx_a).await, + room_participants(&room_a, cx_a), RoomParticipants { remote: vec!["user_b".to_string()], pending: Default::default() } ); assert_eq!( - room_participants(&room_b, &client_b, cx_b).await, + room_participants(&room_b, cx_b), RoomParticipants { remote: vec!["user_a".to_string()], pending: Default::default() @@ -194,14 +194,14 @@ async fn test_basic_calls( room_a.update(cx_a, |room, cx| room.leave(cx)).unwrap(); deterministic.run_until_parked(); assert_eq!( - room_participants(&room_a, &client_a, cx_a).await, + room_participants(&room_a, cx_a), RoomParticipants { remote: Default::default(), pending: Default::default() } ); assert_eq!( - room_participants(&room_b, &client_b, cx_b).await, + room_participants(&room_b, cx_b), RoomParticipants { remote: Default::default(), pending: Default::default() @@ -245,14 +245,14 @@ async fn test_leaving_room_on_disconnection( .unwrap(); deterministic.run_until_parked(); assert_eq!( - room_participants(&room_a, &client_a, cx_a).await, + room_participants(&room_a, cx_a), RoomParticipants { remote: vec!["user_b".to_string()], pending: Default::default() } ); assert_eq!( - room_participants(&room_b, &client_b, cx_b).await, + room_participants(&room_b, cx_b), RoomParticipants { remote: vec!["user_a".to_string()], pending: Default::default() @@ -262,14 +262,14 @@ async fn test_leaving_room_on_disconnection( server.disconnect_client(client_a.current_user_id(cx_a)); cx_a.foreground().advance_clock(rpc::RECEIVE_TIMEOUT); assert_eq!( - room_participants(&room_a, &client_a, cx_a).await, + room_participants(&room_a, cx_a), RoomParticipants { remote: Default::default(), pending: Default::default() } ); assert_eq!( - room_participants(&room_b, &client_b, cx_b).await, + room_participants(&room_b, cx_b), RoomParticipants { remote: Default::default(), pending: Default::default() @@ -5822,34 +5822,17 @@ struct RoomParticipants { pending: Vec, } -async fn room_participants( - room: &ModelHandle, - client: &TestClient, - cx: &mut TestAppContext, -) -> RoomParticipants { - let remote_users = room.update(cx, |room, cx| { - room.remote_participants() - .values() - .map(|participant| { - client - .user_store - .update(cx, |users, cx| users.get_user(participant.user_id, cx)) - }) - .collect::>() - }); - let remote_users = futures::future::try_join_all(remote_users).await.unwrap(); - let pending_users = room.read_with(cx, |room, _| { - room.pending_users().iter().cloned().collect::>() - }); - - RoomParticipants { - remote: remote_users - .into_iter() +fn room_participants(room: &ModelHandle, cx: &mut TestAppContext) -> RoomParticipants { + room.read_with(cx, |room, _| RoomParticipants { + remote: room + .remote_participants() + .iter() + .map(|(_, participant)| participant.user.github_login.clone()) + .collect(), + pending: room + .pending_users() + .iter() .map(|user| user.github_login.clone()) .collect(), - pending: pending_users - .into_iter() - .map(|user| user.github_login.clone()) - .collect(), - } + }) } From 1e45198b9fbf98739f346bcf29528e55677a430d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 3 Oct 2022 17:12:07 +0200 Subject: [PATCH 120/314] Emit event on `Room` when a user shares a new project --- crates/call/src/call.rs | 2 +- crates/call/src/room.rs | 40 ++++++++++-- crates/client/src/user.rs | 2 +- crates/collab/src/integration_tests.rs | 87 +++++++++++++++++++++++++- 4 files changed, 120 insertions(+), 11 deletions(-) diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 0fcf5d7698..01adf9e39d 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -1,5 +1,5 @@ mod participant; -mod room; +pub mod room; use anyhow::{anyhow, Result}; use client::{incoming_call::IncomingCall, Client, UserStore}; diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index d6a0012e30..0e9ce95c21 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -1,14 +1,15 @@ use crate::participant::{ParticipantLocation, RemoteParticipant}; use anyhow::{anyhow, Result}; use client::{incoming_call::IncomingCall, proto, Client, PeerId, TypedEnvelope, User, UserStore}; -use collections::HashMap; +use collections::{HashMap, HashSet}; use futures::StreamExt; use gpui::{AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task}; use std::sync::Arc; use util::ResultExt; +#[derive(Clone, Debug, PartialEq, Eq)] pub enum Event { - PeerChangedActiveProject, + RemoteProjectShared { owner: Arc, project_id: u64 }, } pub struct Room { @@ -158,19 +159,46 @@ impl Room { this.update(&mut cx, |this, cx| { if let Some(participants) = participants.log_err() { - // TODO: compute diff instead of clearing participants - this.remote_participants.clear(); + let mut seen_participants = HashSet::default(); + for (participant, user) in room.participants.into_iter().zip(participants) { + let peer_id = PeerId(participant.peer_id); + seen_participants.insert(peer_id); + + let existing_project_ids = this + .remote_participants + .get(&peer_id) + .map(|existing| existing.project_ids.clone()) + .unwrap_or_default(); + for project_id in &participant.project_ids { + if !existing_project_ids.contains(project_id) { + cx.emit(Event::RemoteProjectShared { + owner: user.clone(), + project_id: *project_id, + }); + } + } + this.remote_participants.insert( - PeerId(participant.peer_id), + peer_id, RemoteParticipant { - user, + user: user.clone(), project_ids: participant.project_ids, location: ParticipantLocation::from_proto(participant.location) .unwrap_or(ParticipantLocation::External), }, ); } + + for participant_peer_id in + this.remote_participants.keys().copied().collect::>() + { + if !seen_participants.contains(&participant_peer_id) { + this.remote_participants.remove(&participant_peer_id); + } + } + + cx.notify(); } if let Some(pending_users) = pending_users.log_err() { diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 9f86020044..0529045c77 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -9,7 +9,7 @@ use rpc::proto::{RequestMessage, UsersResponse}; use std::sync::{Arc, Weak}; use util::TryFutureExt as _; -#[derive(Debug)] +#[derive(Default, Debug)] pub struct User { pub id: u64, pub github_login: String, diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 0bc848f107..5754513e1e 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -5,10 +5,10 @@ use crate::{ }; use ::rpc::Peer; use anyhow::anyhow; -use call::Room; +use call::{room, Room}; use client::{ self, test::FakeHttpClient, Channel, ChannelDetails, ChannelList, Client, Connection, - Credentials, EstablishConnectionError, UserStore, RECEIVE_TIMEOUT, + Credentials, EstablishConnectionError, User, UserStore, RECEIVE_TIMEOUT, }; use collections::{BTreeMap, HashMap, HashSet}; use editor::{ @@ -40,7 +40,8 @@ use serde_json::json; use settings::{Formatter, Settings}; use sqlx::types::time::OffsetDateTime; use std::{ - env, + cell::RefCell, + env, mem, ops::Deref, path::{Path, PathBuf}, rc::Rc, @@ -556,6 +557,86 @@ async fn test_host_disconnect( }); } +#[gpui::test(iterations = 10)] +async fn test_room_events( + deterministic: Arc, + cx_a: &mut TestAppContext, + cx_b: &mut TestAppContext, +) { + deterministic.forbid_parking(); + let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; + let client_a = server.create_client(cx_a, "user_a").await; + let client_b = server.create_client(cx_b, "user_b").await; + client_a.fs.insert_tree("/a", json!({})).await; + client_b.fs.insert_tree("/b", json!({})).await; + + let (project_a, _) = client_a.build_local_project("/a", cx_a).await; + let (project_b, _) = client_b.build_local_project("/b", cx_b).await; + + let (room_id, mut rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + .await; + + let room_a = rooms.remove(0); + let room_a_events = room_events(&room_a, cx_a); + + let room_b = rooms.remove(0); + let room_b_events = room_events(&room_b, cx_b); + + let project_a_id = project_a + .update(cx_a, |project, cx| project.share(room_id, cx)) + .await + .unwrap(); + deterministic.run_until_parked(); + assert_eq!(mem::take(&mut *room_a_events.borrow_mut()), vec![]); + assert_eq!( + mem::take(&mut *room_b_events.borrow_mut()), + vec![room::Event::RemoteProjectShared { + owner: Arc::new(User { + id: client_a.user_id().unwrap(), + github_login: "user_a".to_string(), + avatar: None, + }), + project_id: project_a_id, + }] + ); + + let project_b_id = project_b + .update(cx_b, |project, cx| project.share(room_id, cx)) + .await + .unwrap(); + deterministic.run_until_parked(); + assert_eq!( + mem::take(&mut *room_a_events.borrow_mut()), + vec![room::Event::RemoteProjectShared { + owner: Arc::new(User { + id: client_b.user_id().unwrap(), + github_login: "user_b".to_string(), + avatar: None, + }), + project_id: project_b_id, + }] + ); + assert_eq!(mem::take(&mut *room_b_events.borrow_mut()), vec![]); + + fn room_events( + room: &ModelHandle, + cx: &mut TestAppContext, + ) -> Rc>> { + let events = Rc::new(RefCell::new(Vec::new())); + cx.update({ + let events = events.clone(); + |cx| { + cx.subscribe(room, move |_, event, _| { + events.borrow_mut().push(event.clone()) + }) + .detach() + } + }); + events + } +} + #[gpui::test(iterations = 10)] async fn test_propagate_saves_and_fs_changes( cx_a: &mut TestAppContext, From 9427bb7553ea5eb7bd51cdc6a94ab570300f624e Mon Sep 17 00:00:00 2001 From: Julia Date: Mon, 3 Oct 2022 11:58:48 -0400 Subject: [PATCH 121/314] Be clearer about using GitFilesIncluded setting --- crates/project/src/worktree.rs | 39 +++++++++++++++++----------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 40efeee1d1..c650111207 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -673,28 +673,27 @@ impl LocalWorktree { cx.spawn(|this, mut cx| async move { let text = fs.load(&abs_path).await?; - let head_text = if matches!( - files_included, - settings::GitFilesIncluded::All | settings::GitFilesIncluded::OnlyTracked - ) { - let results = if let Some(repo) = snapshot.repo_for(&abs_path) { - cx.background() - .spawn({ - let path = path.clone(); - async move { repo.repo.lock().load_head_text(&path) } - }) - .await - } else { - None - }; + let head_text = match files_included { + settings::GitFilesIncluded::All | settings::GitFilesIncluded::OnlyTracked => { + let results = if let Some(repo) = snapshot.repo_for(&abs_path) { + cx.background() + .spawn({ + let path = path.clone(); + async move { repo.repo.lock().load_head_text(&path) } + }) + .await + } else { + None + }; - if files_included == settings::GitFilesIncluded::All { - results.or_else(|| Some(text.clone())) - } else { - results + if files_included == settings::GitFilesIncluded::All { + results.or_else(|| Some(text.clone())) + } else { + results + } } - } else { - None + + settings::GitFilesIncluded::None => None, }; // Eagerly populate the snapshot with an updated entry for the loaded file From 4477f95ee631a2fe456ca8c85f6a6d265a71184a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 3 Oct 2022 10:52:57 -0700 Subject: [PATCH 122/314] Set `staff` user property in telemetry Co-authored-by: Joseph Lyons --- crates/client/src/client.rs | 2 +- crates/client/src/telemetry.rs | 30 +++++++++++++++++++-- crates/client/src/test.rs | 1 + crates/client/src/user.rs | 9 ++++++- crates/collab/src/rpc.rs | 11 +++++++- crates/contacts_panel/src/contacts_panel.rs | 10 ------- crates/rpc/proto/zed.proto | 1 + 7 files changed, 49 insertions(+), 15 deletions(-) diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 9ec24abae5..73ecf16084 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -351,7 +351,7 @@ impl Client { })); } Status::SignedOut | Status::UpgradeRequired => { - self.telemetry.set_metrics_id(None); + self.telemetry.set_authenticated_user_info(None, false); state._reconnect_task.take(); } _ => {} diff --git a/crates/client/src/telemetry.rs b/crates/client/src/telemetry.rs index c9b5665e9e..7b0b2ef324 100644 --- a/crates/client/src/telemetry.rs +++ b/crates/client/src/telemetry.rs @@ -9,6 +9,7 @@ use isahc::Request; use lazy_static::lazy_static; use parking_lot::Mutex; use serde::Serialize; +use serde_json::json; use std::{ io::Write, mem, @@ -176,11 +177,32 @@ impl Telemetry { .detach(); } - pub fn set_metrics_id(&self, metrics_id: Option) { + pub fn set_authenticated_user_info( + self: &Arc, + metrics_id: Option, + is_staff: bool, + ) { + let is_signed_in = metrics_id.is_some(); self.state.lock().metrics_id = metrics_id.map(|s| s.into()); + if is_signed_in { + self.report_event_with_user_properties( + "$identify", + Default::default(), + json!({ "$set": { "staff": is_staff } }), + ) + } } pub fn report_event(self: &Arc, kind: &str, properties: Value) { + self.report_event_with_user_properties(kind, properties, Default::default()); + } + + fn report_event_with_user_properties( + self: &Arc, + kind: &str, + properties: Value, + user_properties: Value, + ) { if AMPLITUDE_API_KEY.is_none() { return; } @@ -198,7 +220,11 @@ impl Telemetry { } else { None }, - user_properties: None, + user_properties: if let Value::Object(user_properties) = user_properties { + Some(user_properties) + } else { + None + }, user_id: state.metrics_id.clone(), device_id: state.device_id.clone(), os_name: state.os_name, diff --git a/crates/client/src/test.rs b/crates/client/src/test.rs index 56d3d80b63..288c9a31fa 100644 --- a/crates/client/src/test.rs +++ b/crates/client/src/test.rs @@ -157,6 +157,7 @@ impl FakeServer { .receipt(), GetPrivateUserInfoResponse { metrics_id: "the-metrics-id".into(), + staff: false, }, ) .await; diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index b31cda94b3..d52d6367b0 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -148,7 +148,14 @@ impl UserStore { let fetch_metrics_id = client.request(proto::GetPrivateUserInfo {}).log_err(); let (user, info) = futures::join!(fetch_user, fetch_metrics_id); - client.telemetry.set_metrics_id(info.map(|i| i.metrics_id)); + if let Some(info) = info { + client.telemetry.set_authenticated_user_info( + Some(info.metrics_id), + info.staff, + ); + } else { + client.telemetry.set_authenticated_user_info(None, false); + } client.telemetry.report_event("sign in", Default::default()); current_user_tx.send(user).await.ok(); } diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 467ec174ab..e42b0812ab 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -1738,7 +1738,16 @@ impl Server { .await .user_id_for_connection(request.sender_id)?; let metrics_id = self.app_state.db.get_user_metrics_id(user_id).await?; - response.send(proto::GetPrivateUserInfoResponse { metrics_id })?; + let user = self + .app_state + .db + .get_user_by_id(user_id) + .await? + .ok_or_else(|| anyhow!("user not found"))?; + response.send(proto::GetPrivateUserInfoResponse { + metrics_id, + staff: user.admin, + })?; Ok(()) } diff --git a/crates/contacts_panel/src/contacts_panel.rs b/crates/contacts_panel/src/contacts_panel.rs index 91b86aaf0e..c06b2e17a1 100644 --- a/crates/contacts_panel/src/contacts_panel.rs +++ b/crates/contacts_panel/src/contacts_panel.rs @@ -1221,16 +1221,6 @@ mod tests { let project_store = cx.add_model(|_| ProjectStore::new(project::Db::open_fake())); let server = FakeServer::for_client(current_user_id, &client, cx).await; - let request = server.receive::().await.unwrap(); - server - .respond( - request.receipt(), - proto::GetPrivateUserInfoResponse { - metrics_id: "the-metrics-id".into(), - }, - ) - .await; - let fs = FakeFs::new(cx.background()); fs.insert_tree("/private_dir", json!({ "one.rs": "" })) .await; diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 6a48ad1b97..37434a6d4e 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -755,6 +755,7 @@ message GetPrivateUserInfo {} message GetPrivateUserInfoResponse { string metrics_id = 1; + bool staff = 2; } // Entities From 8f4b3c34938acce79e360266a914a31b64d47480 Mon Sep 17 00:00:00 2001 From: Julia Date: Mon, 3 Oct 2022 14:00:58 -0400 Subject: [PATCH 123/314] Store repo content path as absolute Co-Authored-By: Mikayla Maki --- crates/project/src/worktree.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index c650111207..e04ff2b516 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -1561,7 +1561,7 @@ impl LocalSnapshot { if parent_path.file_name() == Some(&DOT_GIT) { let abs_path = self.abs_path.join(&parent_path); - let content_path: Arc = parent_path.parent().unwrap().into(); + let content_path: Arc = abs_path.parent().unwrap().into(); if let Err(ix) = self .git_repositories .binary_search_by_key(&&content_path, |repo| &repo.content_path) From 06813be5c81b8b8a95d3c3b5fe439de1a0d26f6c Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 3 Oct 2022 11:05:45 -0700 Subject: [PATCH 124/314] Mark platform as "Zed" for telemetry events from the app Co-authored-by: Joseph Lyons --- crates/client/src/telemetry.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/client/src/telemetry.rs b/crates/client/src/telemetry.rs index 7b0b2ef324..f92bf1592b 100644 --- a/crates/client/src/telemetry.rs +++ b/crates/client/src/telemetry.rs @@ -74,6 +74,7 @@ struct AmplitudeEvent { os_name: &'static str, os_version: Option>, app_version: Option>, + platform: &'static str, event_id: usize, session_id: u128, time: u128, @@ -228,6 +229,7 @@ impl Telemetry { user_id: state.metrics_id.clone(), device_id: state.device_id.clone(), os_name: state.os_name, + platform: "Zed", os_version: state.os_version.clone(), app_version: state.app_version.clone(), event_id: post_inc(&mut state.next_event_id), From a5c2f22bf7339066f7e507c059febe1884a500f5 Mon Sep 17 00:00:00 2001 From: Julia Date: Mon, 3 Oct 2022 14:53:33 -0400 Subject: [PATCH 125/314] Move git gutter settings out of editor settings Co-Authored-By: Mikayla Maki --- assets/settings/default.json | 24 +++++++++++----------- crates/collab/src/rpc.rs | 2 +- crates/project/src/worktree.rs | 4 ++-- crates/settings/src/settings.rs | 33 +++++++++++++++++++------------ crates/workspace/src/workspace.rs | 4 ++-- 5 files changed, 38 insertions(+), 29 deletions(-) diff --git a/assets/settings/default.json b/assets/settings/default.json index 11a4b72a10..fc1b1906fc 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -75,17 +75,19 @@ // How many columns a tab should occupy. "tab_size": 4, // Git gutter behavior configuration. - "git_gutter": { - // Which files to show the git gutter on. This setting can take - // three values: - // 1. All files, files not tracked in git will be diffed against - // their contents when the file was last opened in Zed: - // "files_included": "all", - // 2. Only show for files tracked in git: - // "files_included": "only_tracked", - // 3. Disable git gutters entirely: - // "files_included": "none", - "files_included": "all" + "git": { + "git_gutter": { + // Which files to show the git gutter on. This setting can take + // three values: + // 1. All files, files not tracked in git will be diffed against + // their contents when the file was last opened in Zed: + // "files_included": "all", + // 2. Only show for files tracked in git: + // "files_included": "only_tracked", + // 3. Disable git gutters entirely: + // "files_included": "none", + "files_included": "all" + } }, // Settings specific to the terminal "terminal": { diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 627eaf719e..609ae89625 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -1744,7 +1744,7 @@ impl Server { Ok(()) } - async fn get_private_user_info( + async fn get_private_user_info( self: Arc, request: TypedEnvelope, response: Response, diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index e04ff2b516..b914282e01 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -665,9 +665,9 @@ impl LocalWorktree { let files_included = cx .global::() - .editor_overrides + .git .git_gutter - .unwrap_or_default() + .expect("This should be Some by setting setup") .files_included; cx.spawn(|this, mut cx| async move { diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 3f4a764c79..9de4335ec8 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -32,6 +32,7 @@ pub struct Settings { pub default_dock_anchor: DockAnchor, pub editor_defaults: EditorSettings, pub editor_overrides: EditorSettings, + pub git: GitSettings, pub terminal_defaults: TerminalSettings, pub terminal_overrides: TerminalSettings, pub language_defaults: HashMap, EditorSettings>, @@ -52,20 +53,13 @@ impl FeatureFlags { } } -#[derive(Clone, Debug, Default, Deserialize, JsonSchema)] -pub struct EditorSettings { - pub tab_size: Option, - pub hard_tabs: Option, - pub soft_wrap: Option, - pub preferred_line_length: Option, - pub format_on_save: Option, - pub formatter: Option, - pub enable_language_server: Option, - pub git_gutter: Option, +#[derive(Copy, Clone, Debug, Default, Deserialize, JsonSchema)] +pub struct GitSettings { + pub git_gutter: Option, } #[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema)] -pub struct GitGutterConfig { +pub struct GitGutterSettings { pub files_included: GitFilesIncluded, pub debounce_delay_millis: Option, } @@ -79,6 +73,17 @@ pub enum GitFilesIncluded { None, } +#[derive(Clone, Debug, Default, Deserialize, JsonSchema)] +pub struct EditorSettings { + pub tab_size: Option, + pub hard_tabs: Option, + pub soft_wrap: Option, + pub preferred_line_length: Option, + pub format_on_save: Option, + pub formatter: Option, + pub enable_language_server: Option, +} + #[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum SoftWrap { @@ -212,6 +217,8 @@ pub struct SettingsFileContent { #[serde(default)] pub terminal: TerminalSettings, #[serde(default)] + pub git: Option, + #[serde(default)] #[serde(alias = "language_overrides")] pub languages: HashMap, EditorSettings>, #[serde(default)] @@ -266,9 +273,9 @@ impl Settings { format_on_save: required(defaults.editor.format_on_save), formatter: required(defaults.editor.formatter), enable_language_server: required(defaults.editor.enable_language_server), - git_gutter: defaults.editor.git_gutter, }, editor_overrides: Default::default(), + git: defaults.git.unwrap(), terminal_defaults: Default::default(), terminal_overrides: Default::default(), language_defaults: defaults.languages, @@ -395,11 +402,11 @@ impl Settings { format_on_save: Some(FormatOnSave::On), formatter: Some(Formatter::LanguageServer), enable_language_server: Some(true), - git_gutter: Default::default(), }, editor_overrides: Default::default(), terminal_defaults: Default::default(), terminal_overrides: Default::default(), + git: Default::default(), language_defaults: Default::default(), language_overrides: Default::default(), lsp: Default::default(), diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 921fb2de20..fc1f6432a1 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -736,9 +736,9 @@ impl ItemHandle for ViewHandle { let debounce_delay = cx .global::() - .editor_overrides + .git .git_gutter - .unwrap_or_default() + .expect("This should be Some by setting setup") .debounce_delay_millis; let item = item.clone(); From e6487de0691ffcafa1e727727bddfec4dc2d6377 Mon Sep 17 00:00:00 2001 From: Julia Date: Mon, 3 Oct 2022 15:11:06 -0400 Subject: [PATCH 126/314] Rename head text to indicate that it's not always going to be from head Co-Authored-By: Mikayla Maki --- crates/collab/src/integration_tests.rs | 30 +++++++++++++------------- crates/collab/src/rpc.rs | 6 +++--- crates/git/src/diff.rs | 24 ++++++++++----------- crates/git/src/repository.rs | 9 ++++---- crates/language/src/buffer.rs | 28 ++++++++++++------------ crates/project/src/fs.rs | 6 +----- crates/project/src/project.rs | 20 ++++++++--------- crates/project/src/worktree.rs | 10 ++++----- crates/rpc/proto/zed.proto | 8 +++---- crates/rpc/src/proto.rs | 4 ++-- crates/settings/src/settings.rs | 11 +++++++--- 11 files changed, 78 insertions(+), 78 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index a13a013e7a..58a8efc411 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -948,7 +948,7 @@ async fn test_propagate_saves_and_fs_changes( } #[gpui::test(iterations = 10)] -async fn test_git_head_text( +async fn test_git_diff_base_change( executor: Arc, cx_a: &mut TestAppContext, cx_b: &mut TestAppContext, @@ -977,13 +977,13 @@ async fn test_git_head_text( ) .await; - let head_text = " + let diff_base = " one three " .unindent(); - let new_head_text = " + let new_diff_base = " one two " @@ -992,9 +992,9 @@ async fn test_git_head_text( client_a .fs .as_fake() - .set_head_state_for_git_repository( + .set_index_for_repo( Path::new("/dir/.git"), - &[(Path::new("a.txt"), head_text.clone())], + &[(Path::new("a.txt"), diff_base.clone())], ) .await; @@ -1012,11 +1012,11 @@ async fn test_git_head_text( // Smoke test diffing buffer_a.read_with(cx_a, |buffer, _| { - assert_eq!(buffer.head_text(), Some(head_text.as_ref())); + assert_eq!(buffer.diff_base(), Some(diff_base.as_ref())); git::diff::assert_hunks( buffer.snapshot().git_diff_hunks_in_range(0..4), &buffer, - &head_text, + &diff_base, &[(1..2, "", "two\n")], ); }); @@ -1032,11 +1032,11 @@ async fn test_git_head_text( // Smoke test diffing buffer_b.read_with(cx_b, |buffer, _| { - assert_eq!(buffer.head_text(), Some(head_text.as_ref())); + assert_eq!(buffer.diff_base(), Some(diff_base.as_ref())); git::diff::assert_hunks( buffer.snapshot().git_diff_hunks_in_range(0..4), &buffer, - &head_text, + &diff_base, &[(1..2, "", "two\n")], ); }); @@ -1044,9 +1044,9 @@ async fn test_git_head_text( client_a .fs .as_fake() - .set_head_state_for_git_repository( + .set_index_for_repo( Path::new("/dir/.git"), - &[(Path::new("a.txt"), new_head_text.clone())], + &[(Path::new("a.txt"), new_diff_base.clone())], ) .await; @@ -1055,23 +1055,23 @@ async fn test_git_head_text( // Smoke test new diffing buffer_a.read_with(cx_a, |buffer, _| { - assert_eq!(buffer.head_text(), Some(new_head_text.as_ref())); + assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref())); git::diff::assert_hunks( buffer.snapshot().git_diff_hunks_in_range(0..4), &buffer, - &head_text, + &diff_base, &[(2..3, "", "three\n")], ); }); // Smoke test B buffer_b.read_with(cx_b, |buffer, _| { - assert_eq!(buffer.head_text(), Some(new_head_text.as_ref())); + assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref())); git::diff::assert_hunks( buffer.snapshot().git_diff_hunks_in_range(0..4), &buffer, - &head_text, + &diff_base, &[(2..3, "", "three\n")], ); }); diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 609ae89625..9f3c01ac83 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -206,7 +206,7 @@ impl Server { .add_message_handler(Server::unfollow) .add_message_handler(Server::update_followers) .add_request_handler(Server::get_channel_messages) - .add_message_handler(Server::update_head_text) + .add_message_handler(Server::update_diff_base) .add_request_handler(Server::get_private_user_info); Arc::new(server) @@ -1729,9 +1729,9 @@ impl Server { Ok(()) } - async fn update_head_text( + async fn update_diff_base( self: Arc, - request: TypedEnvelope, + request: TypedEnvelope, ) -> Result<()> { let receiver_ids = self.store().await.project_connection_ids( ProjectId::from_proto(request.payload.project_id), diff --git a/crates/git/src/diff.rs b/crates/git/src/diff.rs index 48630fc91c..abf874e2bb 100644 --- a/crates/git/src/diff.rs +++ b/crates/git/src/diff.rs @@ -111,11 +111,11 @@ impl BufferDiff { } } - pub async fn update(&mut self, head_text: &str, buffer: &text::BufferSnapshot) { + pub async fn update(&mut self, diff_base: &str, buffer: &text::BufferSnapshot) { let mut tree = SumTree::new(); let buffer_text = buffer.as_rope().to_string(); - let patch = Self::diff(&head_text, &buffer_text); + let patch = Self::diff(&diff_base, &buffer_text); if let Some(patch) = patch { let mut divergence = 0; @@ -228,7 +228,7 @@ impl BufferDiff { pub fn assert_hunks( diff_hunks: Iter, buffer: &BufferSnapshot, - head_text: &str, + diff_base: &str, expected_hunks: &[(Range, &str, &str)], ) where Iter: Iterator>, @@ -237,7 +237,7 @@ pub fn assert_hunks( .map(|hunk| { ( hunk.buffer_range.clone(), - &head_text[hunk.head_byte_range], + &diff_base[hunk.head_byte_range], buffer .text_for_range( Point::new(hunk.buffer_range.start, 0) @@ -264,7 +264,7 @@ mod tests { #[test] fn test_buffer_diff_simple() { - let head_text = " + let diff_base = " one two three @@ -280,27 +280,27 @@ mod tests { let mut buffer = Buffer::new(0, 0, buffer_text); let mut diff = BufferDiff::new(); - smol::block_on(diff.update(&head_text, &buffer)); + smol::block_on(diff.update(&diff_base, &buffer)); assert_hunks( diff.hunks(&buffer), &buffer, - &head_text, + &diff_base, &[(1..2, "two\n", "HELLO\n")], ); buffer.edit([(0..0, "point five\n")]); - smol::block_on(diff.update(&head_text, &buffer)); + smol::block_on(diff.update(&diff_base, &buffer)); assert_hunks( diff.hunks(&buffer), &buffer, - &head_text, + &diff_base, &[(0..1, "", "point five\n"), (2..3, "two\n", "HELLO\n")], ); } #[test] fn test_buffer_diff_range() { - let head_text = " + let diff_base = " one two three @@ -337,13 +337,13 @@ mod tests { let buffer = Buffer::new(0, 0, buffer_text); let mut diff = BufferDiff::new(); - smol::block_on(diff.update(&head_text, &buffer)); + smol::block_on(diff.update(&diff_base, &buffer)); assert_eq!(diff.hunks(&buffer).count(), 8); assert_hunks( diff.hunks_in_range(7..12, &buffer), &buffer, - &head_text, + &diff_base, &[ (6..7, "", "HELLO\n"), (9..10, "six\n", "SIXTEEN\n"), diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs index a49a1e0b60..67e93416ae 100644 --- a/crates/git/src/repository.rs +++ b/crates/git/src/repository.rs @@ -10,12 +10,12 @@ pub use git2::Repository as LibGitRepository; #[async_trait::async_trait] pub trait GitRepository: Send { - fn load_head_text(&self, relative_file_path: &Path) -> Option; + fn load_index(&self, relative_file_path: &Path) -> Option; } #[async_trait::async_trait] impl GitRepository for LibGitRepository { - fn load_head_text(&self, relative_file_path: &Path) -> Option { + fn load_index(&self, relative_file_path: &Path) -> Option { fn logic(repo: &LibGitRepository, relative_file_path: &Path) -> Result> { const STAGE_NORMAL: i32 = 0; let index = repo.index()?; @@ -25,8 +25,7 @@ impl GitRepository for LibGitRepository { }; let content = repo.find_blob(oid)?.content().to_owned(); - let head_text = String::from_utf8(content)?; - Ok(Some(head_text)) + Ok(Some(String::from_utf8(content)?)) } match logic(&self, relative_file_path) { @@ -55,7 +54,7 @@ impl FakeGitRepository { #[async_trait::async_trait] impl GitRepository for FakeGitRepository { - fn load_head_text(&self, path: &Path) -> Option { + fn load_index(&self, path: &Path) -> Option { let state = self.state.lock(); state.index_contents.get(path).cloned() } diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 22706ab1b5..11ca4fa52a 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -53,7 +53,7 @@ struct GitDiffStatus { pub struct Buffer { text: TextBuffer, - head_text: Option, + diff_base: Option, git_diff_status: GitDiffStatus, file: Option>, saved_version: clock::Global, @@ -346,13 +346,13 @@ impl Buffer { pub fn from_file>( replica_id: ReplicaId, base_text: T, - head_text: Option, + diff_base: Option, file: Arc, cx: &mut ModelContext, ) -> Self { Self::build( TextBuffer::new(replica_id, cx.model_id() as u64, base_text.into()), - head_text.map(|h| h.into().into_boxed_str().into()), + diff_base.map(|h| h.into().into_boxed_str().into()), Some(file), ) } @@ -365,7 +365,7 @@ impl Buffer { let buffer = TextBuffer::new(replica_id, message.id, message.base_text); let mut this = Self::build( buffer, - message.head_text.map(|text| text.into_boxed_str().into()), + message.diff_base.map(|text| text.into_boxed_str().into()), file, ); this.text.set_line_ending(proto::deserialize_line_ending( @@ -380,7 +380,7 @@ impl Buffer { id: self.remote_id(), file: self.file.as_ref().map(|f| f.to_proto()), base_text: self.base_text().to_string(), - head_text: self.head_text.as_ref().map(|h| h.to_string()), + diff_base: self.diff_base.as_ref().map(|h| h.to_string()), line_ending: proto::serialize_line_ending(self.line_ending()) as i32, } } @@ -423,7 +423,7 @@ impl Buffer { self } - fn build(buffer: TextBuffer, head_text: Option, file: Option>) -> Self { + fn build(buffer: TextBuffer, diff_base: Option, file: Option>) -> Self { let saved_mtime = if let Some(file) = file.as_ref() { file.mtime() } else { @@ -437,7 +437,7 @@ impl Buffer { transaction_depth: 0, was_dirty_before_starting_transaction: None, text: buffer, - head_text, + diff_base, git_diff_status: GitDiffStatus { diff: git::diff::BufferDiff::new(), update_in_progress: false, @@ -663,12 +663,12 @@ impl Buffer { } #[cfg(any(test, feature = "test-support"))] - pub fn head_text(&self) -> Option<&str> { - self.head_text.as_deref() + pub fn diff_base(&self) -> Option<&str> { + self.diff_base.as_deref() } - pub fn update_head_text(&mut self, head_text: Option, cx: &mut ModelContext) { - self.head_text = head_text; + pub fn update_diff_base(&mut self, diff_base: Option, cx: &mut ModelContext) { + self.diff_base = diff_base; self.git_diff_recalc(cx); } @@ -682,13 +682,13 @@ impl Buffer { return; } - if let Some(head_text) = &self.head_text { + if let Some(diff_base) = &self.diff_base { let snapshot = self.snapshot(); - let head_text = head_text.clone(); + let diff_base = diff_base.clone(); let mut diff = self.git_diff_status.diff.clone(); let diff = cx.background().spawn(async move { - diff.update(&head_text, &snapshot).await; + diff.update(&diff_base, &snapshot).await; diff }); diff --git a/crates/project/src/fs.rs b/crates/project/src/fs.rs index 2b7aca642d..a43f18ca64 100644 --- a/crates/project/src/fs.rs +++ b/crates/project/src/fs.rs @@ -490,11 +490,7 @@ impl FakeFs { .boxed() } - pub async fn set_head_state_for_git_repository( - &self, - dot_git: &Path, - head_state: &[(&Path, String)], - ) { + pub async fn set_index_for_repo(&self, dot_git: &Path, head_state: &[(&Path, String)]) { let content_path = dot_git.parent().unwrap(); let mut state = self.state.lock().await; let entry = state.read_path(dot_git).await.unwrap(); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 1064d05fe9..7ce9b46085 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -424,7 +424,7 @@ impl Project { client.add_model_request_handler(Self::handle_open_buffer_by_id); client.add_model_request_handler(Self::handle_open_buffer_by_path); client.add_model_request_handler(Self::handle_save_buffer); - client.add_model_message_handler(Self::handle_update_head_text); + client.add_model_message_handler(Self::handle_update_diff_base); } pub fn local( @@ -4675,22 +4675,22 @@ impl Project { let client = self.client.clone(); cx.spawn(|_, mut cx| async move { - let head_text = cx + let diff_base = cx .background() - .spawn(async move { repo.repo.lock().load_head_text(&path) }) + .spawn(async move { repo.repo.lock().load_index(&path) }) .await; let buffer_id = buffer.update(&mut cx, |buffer, cx| { - buffer.update_head_text(head_text.clone(), cx); + buffer.update_diff_base(diff_base.clone(), cx); buffer.remote_id() }); if let Some(project_id) = shared_remote_id { client - .send(proto::UpdateHeadText { + .send(proto::UpdateDiffBase { project_id, buffer_id: buffer_id as u64, - head_text, + diff_base, }) .log_err(); } @@ -5272,22 +5272,22 @@ impl Project { }) } - async fn handle_update_head_text( + async fn handle_update_diff_base( this: ModelHandle, - envelope: TypedEnvelope, + envelope: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, ) -> Result<()> { this.update(&mut cx, |this, cx| { let buffer_id = envelope.payload.buffer_id; - let head_text = envelope.payload.head_text; + let diff_base = envelope.payload.diff_base; let buffer = this .opened_buffers .get_mut(&buffer_id) .and_then(|b| b.upgrade(cx)) .ok_or_else(|| anyhow!("No such buffer {}", buffer_id))?; - buffer.update(cx, |buffer, cx| buffer.update_head_text(head_text, cx)); + buffer.update(cx, |buffer, cx| buffer.update_diff_base(diff_base, cx)); Ok(()) }) diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index b914282e01..ea02431ab9 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -481,11 +481,11 @@ impl LocalWorktree { ) -> Task>> { let path = Arc::from(path); cx.spawn(move |this, mut cx| async move { - let (file, contents, head_text) = this + let (file, contents, diff_base) = this .update(&mut cx, |t, cx| t.as_local().unwrap().load(&path, cx)) .await?; Ok(cx.add_model(|cx| { - let mut buffer = Buffer::from_file(0, contents, head_text, Arc::new(file), cx); + let mut buffer = Buffer::from_file(0, contents, diff_base, Arc::new(file), cx); buffer.git_diff_recalc(cx); buffer })) @@ -673,13 +673,13 @@ impl LocalWorktree { cx.spawn(|this, mut cx| async move { let text = fs.load(&abs_path).await?; - let head_text = match files_included { + let diff_base = match files_included { settings::GitFilesIncluded::All | settings::GitFilesIncluded::OnlyTracked => { let results = if let Some(repo) = snapshot.repo_for(&abs_path) { cx.background() .spawn({ let path = path.clone(); - async move { repo.repo.lock().load_head_text(&path) } + async move { repo.repo.lock().load_index(&path) } }) .await } else { @@ -714,7 +714,7 @@ impl LocalWorktree { is_local: true, }, text, - head_text, + diff_base, )) }) } diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 832c5bb6bd..3c7fa2ad40 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -110,7 +110,7 @@ message Envelope { Unfollow unfollow = 95; GetPrivateUserInfo get_private_user_info = 96; GetPrivateUserInfoResponse get_private_user_info_response = 97; - UpdateHeadText update_head_text = 98; + UpdateDiffBase update_diff_base = 98; } } @@ -830,7 +830,7 @@ message BufferState { uint64 id = 1; optional File file = 2; string base_text = 3; - optional string head_text = 4; + optional string diff_base = 4; LineEnding line_ending = 5; } @@ -1002,8 +1002,8 @@ message WorktreeMetadata { bool visible = 3; } -message UpdateHeadText { +message UpdateDiffBase { uint64 project_id = 1; uint64 buffer_id = 2; - optional string head_text = 3; + optional string diff_base = 3; } diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 8c5832c15f..8d9d715b6c 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -167,7 +167,7 @@ messages!( (UpdateProject, Foreground), (UpdateWorktree, Foreground), (UpdateWorktreeExtensions, Background), - (UpdateHeadText, Background), + (UpdateDiffBase, Background), (GetPrivateUserInfo, Foreground), (GetPrivateUserInfoResponse, Foreground), ); @@ -267,7 +267,7 @@ entity_messages!( UpdateProject, UpdateWorktree, UpdateWorktreeExtensions, - UpdateHeadText + UpdateDiffBase ); entity_messages!(channel_id, ChannelMessageSent); diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 9de4335ec8..9655529744 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -55,11 +55,11 @@ impl FeatureFlags { #[derive(Copy, Clone, Debug, Default, Deserialize, JsonSchema)] pub struct GitSettings { - pub git_gutter: Option, + pub git_gutter: Option, } #[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema)] -pub struct GitGutterSettings { +pub struct GitGutter { pub files_included: GitFilesIncluded, pub debounce_delay_millis: Option, } @@ -406,7 +406,12 @@ impl Settings { editor_overrides: Default::default(), terminal_defaults: Default::default(), terminal_overrides: Default::default(), - git: Default::default(), + git: GitSettings { + git_gutter: Some(GitGutter { + files_included: GitFilesIncluded::All, + debounce_delay_millis: None, + }), + }, language_defaults: Default::default(), language_overrides: Default::default(), lsp: Default::default(), From f3d83631efe8a685505b4dd1b62fc9efdc351ff8 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 3 Oct 2022 12:13:27 -0700 Subject: [PATCH 127/314] Remove unnecessary min_id_length option from amplitude requests --- crates/client/src/telemetry.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/crates/client/src/telemetry.rs b/crates/client/src/telemetry.rs index f92bf1592b..0c162580d4 100644 --- a/crates/client/src/telemetry.rs +++ b/crates/client/src/telemetry.rs @@ -53,12 +53,6 @@ lazy_static! { struct AmplitudeEventBatch { api_key: &'static str, events: Vec, - options: AmplitudeEventBatchOptions, -} - -#[derive(Serialize)] -struct AmplitudeEventBatchOptions { - min_id_length: usize, } #[derive(Serialize)] @@ -273,11 +267,7 @@ impl Telemetry { } } - let batch = AmplitudeEventBatch { - api_key, - events, - options: AmplitudeEventBatchOptions { min_id_length: 1 }, - }; + let batch = AmplitudeEventBatch { api_key, events }; json_bytes.clear(); serde_json::to_writer(&mut json_bytes, &batch)?; let request = From 6f6d72890a3981140e2258cad67ec9113d358f35 Mon Sep 17 00:00:00 2001 From: Julia Date: Mon, 3 Oct 2022 15:42:30 -0400 Subject: [PATCH 128/314] Once again respect user settings for git gutter Co-Authored-By: Mikayla Maki --- crates/project/src/worktree.rs | 8 ++------ crates/settings/src/settings.rs | 27 +++++++++++++++++++++++++-- crates/workspace/src/workspace.rs | 14 ++++++++++---- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index ea02431ab9..1016e58b73 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -663,12 +663,8 @@ impl LocalWorktree { let fs = self.fs.clone(); let snapshot = self.snapshot(); - let files_included = cx - .global::() - .git - .git_gutter - .expect("This should be Some by setting setup") - .files_included; + let settings = cx.global::(); + let files_included = settings.git_gutter().files_included(settings); cx.spawn(|this, mut cx| async move { let text = fs.load(&abs_path).await?; diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 9655529744..3bf09436ed 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -33,6 +33,7 @@ pub struct Settings { pub editor_defaults: EditorSettings, pub editor_overrides: EditorSettings, pub git: GitSettings, + pub git_overrides: GitSettings, pub terminal_defaults: TerminalSettings, pub terminal_overrides: TerminalSettings, pub language_defaults: HashMap, EditorSettings>, @@ -60,10 +61,21 @@ pub struct GitSettings { #[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema)] pub struct GitGutter { - pub files_included: GitFilesIncluded, + pub files_included: Option, pub debounce_delay_millis: Option, } +impl GitGutter { + pub fn files_included(&self, settings: &Settings) -> GitFilesIncluded { + self.files_included.unwrap_or_else(|| { + settings + .git.git_gutter.expect("git_gutter must be some in defaults.json") + .files_included + .expect("Should be some in defaults.json") + }) + } +} + #[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum GitFilesIncluded { @@ -276,6 +288,7 @@ impl Settings { }, editor_overrides: Default::default(), git: defaults.git.unwrap(), + git_overrides: Default::default(), terminal_defaults: Default::default(), terminal_overrides: Default::default(), language_defaults: defaults.languages, @@ -327,6 +340,7 @@ impl Settings { } self.editor_overrides = data.editor; + self.git_overrides = data.git.unwrap_or_default(); self.terminal_defaults.font_size = data.terminal.font_size; self.terminal_overrides = data.terminal; self.language_overrides = data.languages; @@ -382,6 +396,14 @@ impl Settings { .expect("missing default") } + pub fn git_gutter(&self) -> GitGutter { + self.git_overrides.git_gutter.unwrap_or_else(|| { + self.git + .git_gutter + .expect("git_gutter should be some by setting setup") + }) + } + #[cfg(any(test, feature = "test-support"))] pub fn test(cx: &gpui::AppContext) -> Settings { Settings { @@ -408,10 +430,11 @@ impl Settings { terminal_overrides: Default::default(), git: GitSettings { git_gutter: Some(GitGutter { - files_included: GitFilesIncluded::All, + files_included: Some(GitFilesIncluded::All), debounce_delay_millis: None, }), }, + git_overrides: Default::default(), language_defaults: Default::default(), language_overrides: Default::default(), lsp: Default::default(), diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index fc1f6432a1..44c9b19f1b 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -734,12 +734,18 @@ impl ItemHandle for ViewHandle { ); } - let debounce_delay = cx - .global::() - .git + let settings = cx.global::(); + let debounce_delay = settings + .git_overrides .git_gutter - .expect("This should be Some by setting setup") + .unwrap_or_else(|| { + settings + .git + .git_gutter + .expect("This should be Some by setting setup") + }) .debounce_delay_millis; + let item = item.clone(); if let Some(delay) = debounce_delay { From c354b9b9596a41c40dfb4f573d5b66fe9c78e210 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 3 Oct 2022 13:24:37 -0700 Subject: [PATCH 129/314] Add assertions to test for autoclose with embedded languages --- crates/editor/src/editor.rs | 204 +++++++++++++++++++++++++++----- crates/language/src/language.rs | 2 +- 2 files changed, 173 insertions(+), 33 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 699b442a5d..769c03d6ff 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -6001,6 +6001,10 @@ impl Editor { } impl EditorSnapshot { + pub fn language_at(&self, position: T) -> Option<&Arc> { + self.display_snapshot.buffer_snapshot.language_at(position) + } + pub fn is_focused(&self) -> bool { self.is_focused } @@ -9788,13 +9792,24 @@ mod tests { Language::new( LanguageConfig { name: "HTML".into(), - brackets: vec![BracketPair { - start: "<".to_string(), - end: ">".to_string(), - close: true, - newline: true, - }], - autoclose_before: "})]".to_string(), + brackets: vec![ + BracketPair { + start: "<".into(), + end: ">".into(), + ..Default::default() + }, + BracketPair { + start: "{".into(), + end: "}".into(), + ..Default::default() + }, + BracketPair { + start: "(".into(), + end: ")".into(), + ..Default::default() + }, + ], + autoclose_before: "})]>".into(), ..Default::default() }, Some(tree_sitter_html::language()), @@ -9812,13 +9827,24 @@ mod tests { let javascript_language = Arc::new(Language::new( LanguageConfig { name: "JavaScript".into(), - brackets: vec![BracketPair { - start: "/*".to_string(), - end: "*/".to_string(), - close: true, - newline: true, - }], - autoclose_before: "})]".to_string(), + brackets: vec![ + BracketPair { + start: "/*".into(), + end: " */".into(), + ..Default::default() + }, + BracketPair { + start: "{".into(), + end: "}".into(), + ..Default::default() + }, + BracketPair { + start: "(".into(), + end: ")".into(), + ..Default::default() + }, + ], + autoclose_before: "})]>".into(), ..Default::default() }, Some(tree_sitter_javascript::language()), @@ -9839,31 +9865,145 @@ mod tests { - + ˇ "# .unindent(), ); - let cursors = cx.update_editor(|editor, cx| editor.selections.ranges::(cx)); - cx.update_buffer(|buffer, _| { - let snapshot = buffer.snapshot(); + // Precondition: different languages are active at different locations. + cx.update_editor(|editor, cx| { + let snapshot = editor.snapshot(cx); + let cursors = editor.selections.ranges::(cx); + let languages = cursors + .iter() + .map(|c| snapshot.language_at(c.start).unwrap().name()) + .collect::>(); assert_eq!( - snapshot - .language_at(cursors[0].start) - .unwrap() - .name() - .as_ref(), - "HTML" - ); - assert_eq!( - snapshot - .language_at(cursors[1].start) - .unwrap() - .name() - .as_ref(), - "JavaScript" + 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#" + + + + "# + .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#" + + + + "# + .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#" + + + + "# + .unindent(), + ); + cx.update_editor(|editor, cx| { + editor.handle_input(">", cx); + }); + cx.assert_editor_state( + &r#" + ˇ + + ˇ + "# + .unindent(), + ); + + // Reset + cx.set_state( + &r#" + ˇ + + ˇ + "# + .unindent(), + ); + + cx.update_editor(|editor, cx| { + editor.handle_input("<", cx); + }); + cx.assert_editor_state( + &r#" + <ˇ> + + <ˇ> + "# + .unindent(), + ); + + // When backspacing, the closing angle brackets are removed. + cx.update_editor(|editor, cx| { + editor.backspace(&Backspace, cx); + }); + cx.assert_editor_state( + &r#" + ˇ + + ˇ + "# + .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#" + /*ˇ + + /*ˇ + "# + .unindent(), + ); } #[gpui::test] diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index b8d4ca309f..59da0909c6 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -271,7 +271,7 @@ pub struct FakeLspAdapter { pub disk_based_diagnostics_sources: Vec, } -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Debug, Default, Deserialize)] pub struct BracketPair { pub start: String, pub end: String, From 6f7547d28f131cf1af5ef59b593d9f813c0a6786 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 3 Oct 2022 17:18:38 -0700 Subject: [PATCH 130/314] Fixed a couple bugs in tests and worktree path handling --- crates/git/src/repository.rs | 1 + crates/project/src/fs.rs | 3 +-- crates/project/src/project.rs | 18 +++++++++------- crates/project/src/worktree.rs | 39 ++++++++++++++++------------------ 4 files changed, 30 insertions(+), 31 deletions(-) diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs index 67e93416ae..38393dc8a8 100644 --- a/crates/git/src/repository.rs +++ b/crates/git/src/repository.rs @@ -19,6 +19,7 @@ impl GitRepository for LibGitRepository { fn logic(repo: &LibGitRepository, relative_file_path: &Path) -> Result> { const STAGE_NORMAL: i32 = 0; let index = repo.index()?; + dbg!(relative_file_path); let oid = match index.get_path(relative_file_path, STAGE_NORMAL) { Some(entry) => entry.id, None => return Ok(None), diff --git a/crates/project/src/fs.rs b/crates/project/src/fs.rs index a43f18ca64..812842a354 100644 --- a/crates/project/src/fs.rs +++ b/crates/project/src/fs.rs @@ -888,8 +888,7 @@ impl Fs for FakeFs { } fn open_repo(&self, abs_dot_git: &Path) -> Option>> { - let executor = self.executor.upgrade().unwrap(); - executor.block(async move { + smol::block_on(async move { let state = self.state.lock().await; let entry = state.read_path(abs_dot_git).await.unwrap(); let mut entry = entry.lock().await; diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 7ce9b46085..dc783f1818 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4541,7 +4541,7 @@ impl Project { cx.subscribe(worktree, |this, worktree, event, cx| match event { worktree::Event::UpdatedEntries => this.update_local_worktree_buffers(worktree, cx), worktree::Event::UpdatedGitRepositories(updated_repos) => { - this.update_local_worktree_buffers_git_repos(updated_repos, cx) + this.update_local_worktree_buffers_git_repos(worktree, updated_repos, cx) } }) .detach(); @@ -4652,21 +4652,23 @@ impl Project { fn update_local_worktree_buffers_git_repos( &mut self, + worktree: ModelHandle, repos: &[GitRepositoryEntry], cx: &mut ModelContext, ) { - //TODO: Produce protos - for (_, buffer) in &self.opened_buffers { if let Some(buffer) = buffer.upgrade(cx) { - let file = match buffer.read(cx).file().and_then(|file| file.as_local()) { + let file = match File::from_dyn(buffer.read(cx).file()) { Some(file) => file, - None => return, + None => continue, }; - let path = file.path().clone(); - let abs_path = file.abs_path(cx); + if file.worktree != worktree { + continue; + } - let repo = match repos.iter().find(|repo| repo.manages(&abs_path)) { + let path = file.path().clone(); + + let repo = match repos.iter().find(|repo| repo.manages(&path)) { Some(repo) => repo.clone(), None => return, }; diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 1016e58b73..fb07bd837f 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -41,6 +41,7 @@ use std::{ ffi::{OsStr, OsString}, fmt, future::Future, + mem, ops::{Deref, DerefMut}, os::unix::prelude::{OsStrExt, OsStringExt}, path::{Path, PathBuf}, @@ -664,6 +665,13 @@ impl LocalWorktree { let snapshot = self.snapshot(); let settings = cx.global::(); + + // Cut files included because we want to ship! + // TODO: + // - Rename / etc. setting to be show/hide git gutters + // - Unconditionally load index text for all files, + // - then choose at rendering time based on settings + let files_included = settings.git_gutter().files_included(settings); cx.spawn(|this, mut cx| async move { @@ -1379,6 +1387,7 @@ impl LocalSnapshot { // Gives the most specific git repository for a given path pub(crate) fn repo_for(&self, path: &Path) -> Option { + dbg!(&self.git_repositories); self.git_repositories .iter() .rev() //git_repository is ordered lexicographically @@ -1557,7 +1566,7 @@ impl LocalSnapshot { if parent_path.file_name() == Some(&DOT_GIT) { let abs_path = self.abs_path.join(&parent_path); - let content_path: Arc = abs_path.parent().unwrap().into(); + let content_path: Arc = parent_path.parent().unwrap().into(); if let Err(ix) = self .git_repositories .binary_search_by_key(&&content_path, |repo| &repo.content_path) @@ -1716,6 +1725,7 @@ impl LocalSnapshot { impl GitRepositoryEntry { // Note that these paths should be relative to the worktree root. pub(crate) fn manages(&self, path: &Path) -> bool { + dbg!(path, &self.content_path); path.starts_with(self.content_path.as_ref()) } @@ -2566,7 +2576,7 @@ impl BackgroundScanner { self.snapshot.lock().removed_entry_ids.clear(); self.update_ignore_statuses().await; - self.update_git_repositories().await; + self.update_git_repositories(); true } @@ -2632,25 +2642,11 @@ impl BackgroundScanner { .await; } - // TODO: Clarify what is going on here because re-loading every git repository - // on every file system event seems wrong - async fn update_git_repositories(&self) { + fn update_git_repositories(&self) { let mut snapshot = self.snapshot.lock(); - - let new_repos = snapshot - .git_repositories - .iter() - .cloned() - .filter_map(|mut repo_entry| { - let repo = self - .fs - .open_repo(&snapshot.abs_path.join(&repo_entry.git_dir_path))?; - repo_entry.repo = repo; - Some(repo_entry) - }) - .collect(); - - snapshot.git_repositories = new_repos; + let mut git_repositories = mem::take(&mut snapshot.git_repositories); + git_repositories.retain(|repo| snapshot.entry_for_path(&repo.git_dir_path).is_some()); + snapshot.git_repositories = git_repositories; } async fn update_ignore_status(&self, job: UpdateIgnoreStatusJob, snapshot: &LocalSnapshot) { @@ -3245,7 +3241,8 @@ mod tests { "b.txt": "" } }, - "c.txt": "" + "c.txt": "", + })); let http_client = FakeHttpClient::with_404_response(); From 499e95d16a1213ab89052a98d0b3bd5e9b297f3e Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 3 Oct 2022 17:43:05 -0700 Subject: [PATCH 131/314] Removed debugs, simplified settings --- assets/settings/default.json | 18 +++----- crates/editor/src/element.rs | 71 ++++++++++++++++--------------- crates/git/src/repository.rs | 1 - crates/project/src/worktree.rs | 44 +++++-------------- crates/settings/src/settings.rs | 34 +++------------ crates/workspace/src/workspace.rs | 11 +---- 6 files changed, 61 insertions(+), 118 deletions(-) diff --git a/assets/settings/default.json b/assets/settings/default.json index fc1b1906fc..fddac662a5 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -76,18 +76,12 @@ "tab_size": 4, // Git gutter behavior configuration. "git": { - "git_gutter": { - // Which files to show the git gutter on. This setting can take - // three values: - // 1. All files, files not tracked in git will be diffed against - // their contents when the file was last opened in Zed: - // "files_included": "all", - // 2. Only show for files tracked in git: - // "files_included": "only_tracked", - // 3. Disable git gutters entirely: - // "files_included": "none", - "files_included": "all" - } + // Control whether the git gutter is shown. May take 2 values: + // 1. Show the gutter + // "git_gutter": "tracked_files" + // 2. Hide the gutter + // "git_gutter": "hide" + "git_gutter": "tracked_files" }, // Settings specific to the terminal "terminal": { diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 5d83051567..56887f4b45 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -37,7 +37,7 @@ use gpui::{ use json::json; use language::{Bias, DiagnosticSeverity, OffsetUtf16, Selection}; use project::ProjectPath; -use settings::Settings; +use settings::{GitGutter, Settings}; use smallvec::SmallVec; use std::{ cmp::{self, Ordering}, @@ -607,13 +607,16 @@ impl EditorElement { }; let diff_style = &cx.global::().theme.editor.diff.clone(); - // dbg!("***************"); - // dbg!(&layout.diff_hunks); - // dbg!("***************"); + let show_gutter = matches!( + &cx.global::() + .git_overrides + .git_gutter + .unwrap_or_default(), + GitGutter::TrackedFiles + ); // line is `None` when there's a line wrap for (ix, line) in layout.line_number_layouts.iter().enumerate() { - // dbg!(ix); if let Some(line) = line { let line_origin = bounds.origin() + vec2f( @@ -624,39 +627,39 @@ impl EditorElement { line.paint(line_origin, visible_bounds, gutter_layout.line_height, cx); - //This line starts a buffer line, so let's do the diff calculation - let new_hunk = get_hunk(diff_layout.buffer_line, &layout.diff_hunks); + if show_gutter { + //This line starts a buffer line, so let's do the diff calculation + let new_hunk = get_hunk(diff_layout.buffer_line, &layout.diff_hunks); - // This + the unwraps are annoying, but at least it's legible - let (is_ending, is_starting) = match (diff_layout.last_diff, new_hunk) { - (None, None) => (false, false), - (None, Some(_)) => (false, true), - (Some(_), None) => (true, false), - (Some((old_hunk, _)), Some(new_hunk)) if new_hunk == old_hunk => (false, false), - (Some(_), Some(_)) => (true, true), - }; + // This + the unwraps are annoying, but at least it's legible + let (is_ending, is_starting) = match (diff_layout.last_diff, new_hunk) { + (None, None) => (false, false), + (None, Some(_)) => (false, true), + (Some(_), None) => (true, false), + (Some((old_hunk, _)), Some(new_hunk)) if new_hunk == old_hunk => { + (false, false) + } + (Some(_), Some(_)) => (true, true), + }; - // dbg!(diff_layout.buffer_line, is_starting); + if is_ending { + let (last_hunk, start_line) = diff_layout.last_diff.take().unwrap(); + cx.scene.push_quad(diff_quad( + last_hunk.status(), + start_line..ix, + &gutter_layout, + diff_style, + )); + } - if is_ending { - let (last_hunk, start_line) = diff_layout.last_diff.take().unwrap(); - // dbg!("ending"); - // dbg!(start_line..ix); - cx.scene.push_quad(diff_quad( - last_hunk.status(), - start_line..ix, - &gutter_layout, - diff_style, - )); + if is_starting { + let new_hunk = new_hunk.unwrap(); + + diff_layout.last_diff = Some((new_hunk, ix)); + }; + + diff_layout.buffer_line += 1; } - - if is_starting { - let new_hunk = new_hunk.unwrap(); - - diff_layout.last_diff = Some((new_hunk, ix)); - }; - - diff_layout.buffer_line += 1; } } diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs index 38393dc8a8..67e93416ae 100644 --- a/crates/git/src/repository.rs +++ b/crates/git/src/repository.rs @@ -19,7 +19,6 @@ impl GitRepository for LibGitRepository { fn logic(repo: &LibGitRepository, relative_file_path: &Path) -> Result> { const STAGE_NORMAL: i32 = 0; let index = repo.index()?; - dbg!(relative_file_path); let oid = match index.get_path(relative_file_path, STAGE_NORMAL) { Some(entry) => entry.id, None => return Ok(None), diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index fb07bd837f..6880ec4ff1 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -32,7 +32,7 @@ use postage::{ prelude::{Sink as _, Stream as _}, watch, }; -use settings::Settings; + use smol::channel::{self, Sender}; use std::{ any::Any, @@ -664,40 +664,18 @@ impl LocalWorktree { let fs = self.fs.clone(); let snapshot = self.snapshot(); - let settings = cx.global::(); - - // Cut files included because we want to ship! - // TODO: - // - Rename / etc. setting to be show/hide git gutters - // - Unconditionally load index text for all files, - // - then choose at rendering time based on settings - - let files_included = settings.git_gutter().files_included(settings); - cx.spawn(|this, mut cx| async move { let text = fs.load(&abs_path).await?; - let diff_base = match files_included { - settings::GitFilesIncluded::All | settings::GitFilesIncluded::OnlyTracked => { - let results = if let Some(repo) = snapshot.repo_for(&abs_path) { - cx.background() - .spawn({ - let path = path.clone(); - async move { repo.repo.lock().load_index(&path) } - }) - .await - } else { - None - }; - - if files_included == settings::GitFilesIncluded::All { - results.or_else(|| Some(text.clone())) - } else { - results - } - } - - settings::GitFilesIncluded::None => None, + let diff_base = if let Some(repo) = snapshot.repo_for(&abs_path) { + cx.background() + .spawn({ + let path = path.clone(); + async move { repo.repo.lock().load_index(&path) } + }) + .await + } else { + None }; // Eagerly populate the snapshot with an updated entry for the loaded file @@ -1387,7 +1365,6 @@ impl LocalSnapshot { // Gives the most specific git repository for a given path pub(crate) fn repo_for(&self, path: &Path) -> Option { - dbg!(&self.git_repositories); self.git_repositories .iter() .rev() //git_repository is ordered lexicographically @@ -1725,7 +1702,6 @@ impl LocalSnapshot { impl GitRepositoryEntry { // Note that these paths should be relative to the worktree root. pub(crate) fn manages(&self, path: &Path) -> bool { - dbg!(path, &self.content_path); path.starts_with(self.content_path.as_ref()) } diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 3bf09436ed..fd04fc0aa6 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -57,34 +57,19 @@ impl FeatureFlags { #[derive(Copy, Clone, Debug, Default, Deserialize, JsonSchema)] pub struct GitSettings { pub git_gutter: Option, + pub gutter_debounce: Option, } #[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema)] -pub struct GitGutter { - pub files_included: Option, - pub debounce_delay_millis: Option, -} - -impl GitGutter { - pub fn files_included(&self, settings: &Settings) -> GitFilesIncluded { - self.files_included.unwrap_or_else(|| { - settings - .git.git_gutter.expect("git_gutter must be some in defaults.json") - .files_included - .expect("Should be some in defaults.json") - }) - } -} - -#[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "snake_case")] -pub enum GitFilesIncluded { +pub enum GitGutter { #[default] - All, - OnlyTracked, - None, + TrackedFiles, + Hide, } +pub struct GitGutterConfig {} + #[derive(Clone, Debug, Default, Deserialize, JsonSchema)] pub struct EditorSettings { pub tab_size: Option, @@ -428,12 +413,7 @@ impl Settings { editor_overrides: Default::default(), terminal_defaults: Default::default(), terminal_overrides: Default::default(), - git: GitSettings { - git_gutter: Some(GitGutter { - files_included: Some(GitFilesIncluded::All), - debounce_delay_millis: None, - }), - }, + git: Default::default(), git_overrides: Default::default(), language_defaults: Default::default(), language_overrides: Default::default(), diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 44c9b19f1b..2ae498d701 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -735,16 +735,7 @@ impl ItemHandle for ViewHandle { } let settings = cx.global::(); - let debounce_delay = settings - .git_overrides - .git_gutter - .unwrap_or_else(|| { - settings - .git - .git_gutter - .expect("This should be Some by setting setup") - }) - .debounce_delay_millis; + let debounce_delay = settings.git_overrides.gutter_debounce; let item = item.clone(); From 218ba810133067a9fdd7c9230ebf5f9dbceb04e9 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 3 Oct 2022 17:44:18 -0700 Subject: [PATCH 132/314] Fix autoclose error when cursor was at column 0 --- crates/editor/src/editor.rs | 263 ++++++++++++++---------------------- 1 file changed, 101 insertions(+), 162 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 769c03d6ff..93a47cf621 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -569,6 +569,7 @@ struct SelectNextState { done: bool, } +#[derive(Debug)] struct AutocloseRegion { selection_id: usize, range: Range, @@ -1883,19 +1884,20 @@ impl Editor { // If the inserted text is a suffix of an opening bracket and the // selection is preceded by the rest of the opening bracket, then // insert the closing bracket. - let should_autoclose = selection.start.column > (prefix_len as u32) - && snapshot.contains_str_at( - Point::new( - selection.start.row, - selection.start.column - (prefix_len as u32), - ), - &bracket_pair.start[..prefix_len], - ) - && snapshot - .chars_at(selection.start) - .next() - .map_or(true, |c| language.should_autoclose_before(c)); - if should_autoclose { + let following_text_allows_autoclose = snapshot + .chars_at(selection.start) + .next() + .map_or(true, |c| language.should_autoclose_before(c)); + let preceding_text_matches_prefix = prefix_len == 0 + || (selection.start.column >= (prefix_len as u32) + && snapshot.contains_str_at( + Point::new( + selection.start.row, + selection.start.column - (prefix_len as u32), + ), + &bracket_pair.start[..prefix_len], + )); + if following_text_allows_autoclose && preceding_text_matches_prefix { let anchor = snapshot.anchor_before(selection.end); new_selections .push((selection.map(|_| anchor.clone()), text.len())); @@ -2210,14 +2212,14 @@ impl Editor { buffer: &'a MultiBufferSnapshot, ) -> impl Iterator, Option<&'a AutocloseRegion>)> { let mut i = 0; - let mut pair_states = self.autoclose_regions.as_slice(); + let mut regions = self.autoclose_regions.as_slice(); selections.into_iter().map(move |selection| { let range = selection.start.to_offset(buffer)..selection.end.to_offset(buffer); let mut enclosing = None; - while let Some(pair_state) = pair_states.get(i) { + while let Some(pair_state) = regions.get(i) { if pair_state.range.end.to_offset(buffer) < range.start { - pair_states = &pair_states[i + 1..]; + regions = ®ions[i + 1..]; i = 0; } else if pair_state.range.start.to_offset(buffer) > range.end { break; @@ -9594,7 +9596,8 @@ mod tests { #[gpui::test] async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); + let mut cx = EditorTestContext::new(cx); + let language = Arc::new(Language::new( LanguageConfig { brackets: vec![ @@ -9623,165 +9626,101 @@ mod tests { Some(tree_sitter_rust::language()), )); - let text = r#" - a + 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); + }); - / - - "# - .unindent(); - - let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); - let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let (_, view) = cx.add_window(|cx| build_editor(buffer, cx)); - view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) - .await; - - view.update(cx, |view, cx| { - view.change_selections(None, cx, |s| { - s.select_display_ranges([ - DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1), - DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), - ]) - }); + 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); - assert_eq!( - view.text(cx), - " - {{{}}} - {{{}}} - / - - " - .unindent() - ); + }); + 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); - assert_eq!( - view.text(cx), - " - {{{}}}} - {{{}}}} - / + }); + cx.assert_editor_state( + &" + 🏀{{{}}}}ˇ + ε{{{}}}}ˇ + ❤️{{{}}}}ˇ + " + .unindent(), + ); - " - .unindent() - ); - - view.undo(&Undo, cx); + // autoclose multi-character pairs + cx.set_state( + &" + ˇ + ˇ + " + .unindent(), + ); + cx.update_editor(|view, cx| { view.handle_input("/", cx); view.handle_input("*", cx); - assert_eq!( - view.text(cx), - " - /* */ - /* */ - / - - " - .unindent() - ); - - view.undo(&Undo, cx); - view.change_selections(None, cx, |s| { - s.select_display_ranges([ - DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1), - DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0), - ]) - }); - view.handle_input("*", cx); - assert_eq!( - view.text(cx), - " - a - - /* - * - " - .unindent() - ); - - // Don't autoclose if the next character isn't whitespace and isn't - // listed in the language's "autoclose_before" section. - view.finalize_last_transaction(cx); - view.change_selections(None, cx, |s| { - s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]) - }); - view.handle_input("{", cx); - assert_eq!( - view.text(cx), - " - {a - - /* - * - " - .unindent() - ); - - view.undo(&Undo, cx); - view.change_selections(None, cx, |s| { - s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)]) - }); - view.handle_input("{", cx); - assert_eq!( - view.text(cx), - " - {a} - - /* - * - " - .unindent() - ); - assert_eq!( - view.selections.display_ranges(cx), - [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)] - ); - - view.undo(&Undo, cx); - view.handle_input("[", cx); - assert_eq!( - view.text(cx), - " - [a] - - /* - * - " - .unindent() - ); - assert_eq!( - view.selections.display_ranges(cx), - [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)] - ); - - view.undo(&Undo, cx); - view.change_selections(None, cx, |s| { - s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]) - }); - view.handle_input("[", cx); - assert_eq!( - view.text(cx), - " - a[ - - /* - * - " - .unindent() - ); - assert_eq!( - view.selections.display_ranges(cx), - [DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2)] - ); }); + 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"); + + // 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"); } #[gpui::test] From 456dde200c7083c6bbd6dc6ba1d32c0aa1e86eb7 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 4 Oct 2022 11:46:01 +0200 Subject: [PATCH 133/314] Implement `Room::set_location` --- crates/call/src/call.rs | 1 + crates/call/src/participant.rs | 1 + crates/call/src/room.rs | 37 ++++++ crates/collab/src/integration_tests.rs | 162 ++++++++++++++++++++++++- crates/collab/src/rpc.rs | 18 +++ crates/collab/src/rpc/store.rs | 31 +++++ crates/rpc/proto/zed.proto | 6 + crates/rpc/src/proto.rs | 2 + 8 files changed, 256 insertions(+), 2 deletions(-) diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 01adf9e39d..d800c3ac06 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -4,6 +4,7 @@ pub mod room; use anyhow::{anyhow, Result}; use client::{incoming_call::IncomingCall, Client, UserStore}; use gpui::{Entity, ModelContext, ModelHandle, MutableAppContext, Task}; +pub use participant::ParticipantLocation; pub use room::Room; use std::sync::Arc; diff --git a/crates/call/src/participant.rs b/crates/call/src/participant.rs index cf7c965816..e0e96bc590 100644 --- a/crates/call/src/participant.rs +++ b/crates/call/src/participant.rs @@ -2,6 +2,7 @@ use anyhow::{anyhow, Result}; use client::{proto, User}; use std::sync::Arc; +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum ParticipantLocation { Project { project_id: u64 }, External, diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 0e9ce95c21..4cac210f8b 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -4,6 +4,7 @@ use client::{incoming_call::IncomingCall, proto, Client, PeerId, TypedEnvelope, use collections::{HashMap, HashSet}; use futures::StreamExt; use gpui::{AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task}; +use project::Project; use std::sync::Arc; use util::ResultExt; @@ -233,6 +234,42 @@ impl Room { Ok(()) }) } + + pub fn set_location( + &mut self, + project: Option<&ModelHandle>, + cx: &mut ModelContext, + ) -> Task> { + if self.status.is_offline() { + return Task::ready(Err(anyhow!("room is offline"))); + } + + let client = self.client.clone(); + let room_id = self.id; + let location = if let Some(project) = project { + if let Some(project_id) = project.read(cx).remote_id() { + proto::participant_location::Variant::Project( + proto::participant_location::Project { id: project_id }, + ) + } else { + return Task::ready(Err(anyhow!("project is not shared"))); + } + } else { + proto::participant_location::Variant::External(proto::participant_location::External {}) + }; + + cx.foreground().spawn(async move { + client + .request(proto::UpdateParticipantLocation { + room_id, + location: Some(proto::ParticipantLocation { + variant: Some(location), + }), + }) + .await?; + Ok(()) + }) + } } #[derive(Copy, Clone, PartialEq, Eq)] diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 5754513e1e..40af0a731e 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -5,7 +5,7 @@ use crate::{ }; use ::rpc::Peer; use anyhow::anyhow; -use call::{room, Room}; +use call::{room, ParticipantLocation, Room}; use client::{ self, test::FakeHttpClient, Channel, ChannelDetails, ChannelList, Client, Connection, Credentials, EstablishConnectionError, User, UserStore, RECEIVE_TIMEOUT, @@ -40,7 +40,7 @@ use serde_json::json; use settings::{Formatter, Settings}; use sqlx::types::time::OffsetDateTime; use std::{ - cell::RefCell, + cell::{Cell, RefCell}, env, mem, ops::Deref, path::{Path, PathBuf}, @@ -637,6 +637,164 @@ async fn test_room_events( } } +#[gpui::test(iterations = 10)] +async fn test_room_location( + deterministic: Arc, + cx_a: &mut TestAppContext, + cx_b: &mut TestAppContext, +) { + deterministic.forbid_parking(); + let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; + let client_a = server.create_client(cx_a, "user_a").await; + let client_b = server.create_client(cx_b, "user_b").await; + client_a.fs.insert_tree("/a", json!({})).await; + client_b.fs.insert_tree("/b", json!({})).await; + + let (project_a, _) = client_a.build_local_project("/a", cx_a).await; + let (project_b, _) = client_b.build_local_project("/b", cx_b).await; + + let (room_id, mut rooms) = server + .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + .await; + + let room_a = rooms.remove(0); + let room_a_notified = Rc::new(Cell::new(false)); + cx_a.update({ + let room_a_notified = room_a_notified.clone(); + |cx| { + cx.observe(&room_a, move |_, _| room_a_notified.set(true)) + .detach() + } + }); + + let room_b = rooms.remove(0); + let room_b_notified = Rc::new(Cell::new(false)); + cx_b.update({ + let room_b_notified = room_b_notified.clone(); + |cx| { + cx.observe(&room_b, move |_, _| room_b_notified.set(true)) + .detach() + } + }); + + let project_a_id = project_a + .update(cx_a, |project, cx| project.share(room_id, cx)) + .await + .unwrap(); + deterministic.run_until_parked(); + assert!(room_a_notified.take()); + assert_eq!( + participant_locations(&room_a, cx_a), + vec![("user_b".to_string(), ParticipantLocation::External)] + ); + assert!(room_b_notified.take()); + assert_eq!( + participant_locations(&room_b, cx_b), + vec![("user_a".to_string(), ParticipantLocation::External)] + ); + + let project_b_id = project_b + .update(cx_b, |project, cx| project.share(room_id, cx)) + .await + .unwrap(); + deterministic.run_until_parked(); + assert!(room_a_notified.take()); + assert_eq!( + participant_locations(&room_a, cx_a), + vec![("user_b".to_string(), ParticipantLocation::External)] + ); + assert!(room_b_notified.take()); + assert_eq!( + participant_locations(&room_b, cx_b), + vec![("user_a".to_string(), ParticipantLocation::External)] + ); + + room_a + .update(cx_a, |room, cx| room.set_location(Some(&project_a), cx)) + .await + .unwrap(); + deterministic.run_until_parked(); + assert!(room_a_notified.take()); + assert_eq!( + participant_locations(&room_a, cx_a), + vec![("user_b".to_string(), ParticipantLocation::External)] + ); + assert!(room_b_notified.take()); + assert_eq!( + participant_locations(&room_b, cx_b), + vec![( + "user_a".to_string(), + ParticipantLocation::Project { + project_id: project_a_id + } + )] + ); + + room_b + .update(cx_b, |room, cx| room.set_location(Some(&project_b), cx)) + .await + .unwrap(); + deterministic.run_until_parked(); + assert!(room_a_notified.take()); + assert_eq!( + participant_locations(&room_a, cx_a), + vec![( + "user_b".to_string(), + ParticipantLocation::Project { + project_id: project_b_id + } + )] + ); + assert!(room_b_notified.take()); + assert_eq!( + participant_locations(&room_b, cx_b), + vec![( + "user_a".to_string(), + ParticipantLocation::Project { + project_id: project_a_id + } + )] + ); + + room_b + .update(cx_b, |room, cx| room.set_location(None, cx)) + .await + .unwrap(); + deterministic.run_until_parked(); + assert!(room_a_notified.take()); + assert_eq!( + participant_locations(&room_a, cx_a), + vec![("user_b".to_string(), ParticipantLocation::External)] + ); + assert!(room_b_notified.take()); + assert_eq!( + participant_locations(&room_b, cx_b), + vec![( + "user_a".to_string(), + ParticipantLocation::Project { + project_id: project_a_id + } + )] + ); + + fn participant_locations( + room: &ModelHandle, + cx: &TestAppContext, + ) -> Vec<(String, ParticipantLocation)> { + room.read_with(cx, |room, _| { + room.remote_participants() + .values() + .map(|participant| { + ( + participant.user.github_login.to_string(), + participant.location, + ) + }) + .collect() + }) + } +} + #[gpui::test(iterations = 10)] async fn test_propagate_saves_and_fs_changes( cx_a: &mut TestAppContext, diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 64d81b51d7..c000400568 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -151,6 +151,7 @@ impl Server { .add_message_handler(Server::leave_room) .add_request_handler(Server::call) .add_message_handler(Server::decline_call) + .add_request_handler(Server::update_participant_location) .add_request_handler(Server::share_project) .add_message_handler(Server::unshare_project) .add_request_handler(Server::join_project) @@ -719,6 +720,23 @@ impl Server { Ok(()) } + async fn update_participant_location( + self: Arc, + request: TypedEnvelope, + response: Response, + ) -> Result<()> { + let room_id = request.payload.room_id; + let location = request + .payload + .location + .ok_or_else(|| anyhow!("invalid location"))?; + let mut store = self.store().await; + let room = store.update_participant_location(room_id, location, request.sender_id)?; + self.room_updated(room); + response.send(proto::Ack {})?; + Ok(()) + } + fn room_updated(&self, room: &proto::Room) { for participant in &room.participants { self.peer diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index 9b241446b5..c917309cd2 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -585,6 +585,37 @@ impl Store { } } + pub fn update_participant_location( + &mut self, + room_id: RoomId, + location: proto::ParticipantLocation, + connection_id: ConnectionId, + ) -> Result<&proto::Room> { + let room = self + .rooms + .get_mut(&room_id) + .ok_or_else(|| anyhow!("no such room"))?; + if let Some(proto::participant_location::Variant::Project(project)) = + location.variant.as_ref() + { + anyhow::ensure!( + room.participants + .iter() + .any(|participant| participant.project_ids.contains(&project.id)), + "no such project" + ); + } + + let participant = room + .participants + .iter_mut() + .find(|participant| participant.peer_id == connection_id.0) + .ok_or_else(|| anyhow!("no such room"))?; + participant.location = Some(location); + + Ok(room) + } + pub fn share_project( &mut self, room_id: RoomId, diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index cff10278b4..ffec915269 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -20,6 +20,7 @@ message Envelope { IncomingCall incoming_call = 1000; CancelCall cancel_call = 1001; DeclineCall decline_call = 13; + UpdateParticipantLocation update_participant_location = 1003; RoomUpdated room_updated = 14; ShareProject share_project = 15; @@ -190,6 +191,11 @@ message CancelCall {} message DeclineCall {} +message UpdateParticipantLocation { + uint64 room_id = 1; + ParticipantLocation location = 2; +} + message RoomUpdated { Room room = 1; } diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 822a50c3e4..25e04e6645 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -170,6 +170,7 @@ messages!( (UpdateFollowers, Foreground), (UpdateInviteInfo, Foreground), (UpdateLanguageServer, Foreground), + (UpdateParticipantLocation, Foreground), (UpdateProject, Foreground), (UpdateWorktree, Foreground), (UpdateWorktreeExtensions, Background), @@ -222,6 +223,7 @@ request_messages!( (ShareProject, ShareProjectResponse), (Test, Test), (UpdateBuffer, Ack), + (UpdateParticipantLocation, Ack), (UpdateWorktree, Ack), ); From de917c4678395330e01c5649117bc761f0d55d8d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 4 Oct 2022 14:50:41 +0200 Subject: [PATCH 134/314] Use a different style for inactive participants --- crates/call/src/participant.rs | 1 + crates/collab_ui/src/collab_titlebar_item.rs | 92 ++++++++++++++------ crates/collab_ui/src/contacts_popover.rs | 19 ++-- crates/theme/src/theme.rs | 1 + styles/src/styleTree/workspace.ts | 4 + 5 files changed, 82 insertions(+), 35 deletions(-) diff --git a/crates/call/src/participant.rs b/crates/call/src/participant.rs index e0e96bc590..15aaf2f13b 100644 --- a/crates/call/src/participant.rs +++ b/crates/call/src/participant.rs @@ -20,6 +20,7 @@ impl ParticipantLocation { } } +#[derive(Clone)] pub struct RemoteParticipant { pub user: Arc, pub project_ids: Vec, diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 770b9f29e6..4bbf322205 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -1,4 +1,5 @@ use crate::contacts_popover; +use call::{ActiveCall, ParticipantLocation}; use client::{Authenticate, PeerId}; use clock::ReplicaId; use contacts_popover::ContactsPopover; @@ -25,6 +26,7 @@ pub fn init(cx: &mut MutableAppContext) { pub struct CollabTitlebarItem { workspace: WeakViewHandle, contacts_popover: Option>, + room_subscription: Option, _subscriptions: Vec, } @@ -56,12 +58,27 @@ impl View for CollabTitlebarItem { impl CollabTitlebarItem { pub fn new(workspace: &ViewHandle, cx: &mut ViewContext) -> Self { - let observe_workspace = cx.observe(workspace, |_, _, cx| cx.notify()); - Self { + let active_call = ActiveCall::global(cx); + let mut subscriptions = Vec::new(); + subscriptions.push(cx.observe(workspace, |_, _, cx| cx.notify())); + subscriptions.push(cx.observe(&active_call, |this, _, cx| this.active_call_changed(cx))); + let mut this = Self { workspace: workspace.downgrade(), contacts_popover: None, - _subscriptions: vec![observe_workspace], + room_subscription: None, + _subscriptions: subscriptions, + }; + this.active_call_changed(cx); + this + } + + fn active_call_changed(&mut self, cx: &mut ViewContext) { + if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { + self.room_subscription = Some(cx.observe(&room, |_, _, cx| cx.notify())); + } else { + self.room_subscription = None; } + cx.notify(); } fn toggle_contacts_popover(&mut self, _: &ToggleContactsPopover, cx: &mut ViewContext) { @@ -151,28 +168,40 @@ impl CollabTitlebarItem { theme: &Theme, cx: &mut RenderContext, ) -> Vec { - let mut collaborators = workspace - .read(cx) - .project() - .read(cx) - .collaborators() - .values() - .cloned() - .collect::>(); - collaborators.sort_unstable_by_key(|collaborator| collaborator.replica_id); - collaborators - .into_iter() - .filter_map(|collaborator| { - Some(self.render_avatar( - collaborator.user.avatar.clone()?, - collaborator.replica_id, - Some((collaborator.peer_id, &collaborator.user.github_login)), - workspace, - theme, - cx, - )) - }) - .collect() + let active_call = ActiveCall::global(cx); + if let Some(room) = active_call.read(cx).room().cloned() { + let project = workspace.read(cx).project().read(cx); + let project_id = project.remote_id(); + let mut collaborators = project + .collaborators() + .values() + .cloned() + .collect::>(); + collaborators.sort_by_key(|collaborator| collaborator.replica_id); + collaborators + .into_iter() + .filter_map(|collaborator| { + let participant = room + .read(cx) + .remote_participants() + .get(&collaborator.peer_id)?; + let is_active = project_id.map_or(false, |project_id| { + participant.location == ParticipantLocation::Project { project_id } + }); + Some(self.render_avatar( + collaborator.user.avatar.clone()?, + collaborator.replica_id, + Some((collaborator.peer_id, &collaborator.user.github_login)), + is_active, + workspace, + theme, + cx, + )) + }) + .collect() + } else { + Default::default() + } } fn render_current_user( @@ -185,7 +214,7 @@ impl CollabTitlebarItem { let replica_id = workspace.read(cx).project().read(cx).replica_id(); let status = *workspace.read(cx).client().status().borrow(); if let Some(avatar) = user.and_then(|user| user.avatar.clone()) { - Some(self.render_avatar(avatar, replica_id, None, workspace, theme, cx)) + Some(self.render_avatar(avatar, replica_id, None, true, workspace, theme, cx)) } else if matches!(status, client::Status::UpgradeRequired) { None } else { @@ -214,6 +243,7 @@ impl CollabTitlebarItem { avatar: Arc, replica_id: ReplicaId, peer: Option<(PeerId, &str)>, + is_active: bool, workspace: &ViewHandle, theme: &Theme, cx: &mut RenderContext, @@ -222,10 +252,18 @@ impl CollabTitlebarItem { let is_followed = peer.map_or(false, |(peer_id, _)| { workspace.read(cx).is_following(peer_id) }); - let mut avatar_style = theme.workspace.titlebar.avatar; + let mut avatar_style; + + if is_active { + avatar_style = theme.workspace.titlebar.avatar; + } else { + avatar_style = theme.workspace.titlebar.inactive_avatar; + } + if is_followed { avatar_style.border = Border::all(1.0, replica_color); } + let content = Stack::new() .with_child( Image::new(avatar) diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index aff159127f..7d0473bfc5 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -159,14 +159,7 @@ impl ContactsPopover { let active_call = ActiveCall::global(cx); let mut subscriptions = Vec::new(); subscriptions.push(cx.observe(&user_store, |this, _, cx| this.update_entries(cx))); - subscriptions.push(cx.observe(&active_call, |this, active_call, cx| { - if let Some(room) = active_call.read(cx).room().cloned() { - this.room_subscription = Some(cx.observe(&room, |_, _, cx| cx.notify())); - } else { - this.room_subscription = None; - } - cx.notify(); - })); + subscriptions.push(cx.observe(&active_call, |this, _, cx| this.active_call_changed(cx))); let mut this = Self { room_subscription: None, @@ -180,9 +173,19 @@ impl ContactsPopover { user_store, }; this.update_entries(cx); + this.active_call_changed(cx); this } + fn active_call_changed(&mut self, cx: &mut ViewContext) { + if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { + self.room_subscription = Some(cx.observe(&room, |_, _, cx| cx.notify())); + } else { + self.room_subscription = None; + } + cx.notify(); + } + fn clear_filter(&mut self, _: &Cancel, cx: &mut ViewContext) { let did_clear = self.filter_editor.update(cx, |editor, cx| { if editor.buffer().read(cx).len(cx) > 0 { diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 28c8eb3091..7e78f74c59 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -71,6 +71,7 @@ pub struct Titlebar { pub avatar_ribbon: AvatarRibbon, pub offline_icon: OfflineIcon, pub avatar: ImageStyle, + pub inactive_avatar: ImageStyle, pub sign_in_prompt: Interactive, pub outdated_warning: ContainedText, pub toggle_contacts_button: Interactive, diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 8bd1e3800f..4889ee3f10 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -69,6 +69,10 @@ export default function workspace(theme: Theme) { width: 1, }, }, + inactiveAvatar: { + cornerRadius: 10, + opacity: 0.65, + }, avatarRibbon: { height: 3, width: 12, From 57930cb88ad6a44f656249b199594ca320306b05 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 4 Oct 2022 15:56:20 +0200 Subject: [PATCH 135/314] Show `Share` button for unshared projects when inside of a room --- crates/collab_ui/src/collab_titlebar_item.rs | 146 ++++++++++++------- crates/collab_ui/src/contacts_popover.rs | 65 +++++---- crates/theme/src/theme.rs | 1 + styles/src/styleTree/workspace.ts | 42 +++--- 4 files changed, 154 insertions(+), 100 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 4bbf322205..8b467e6cb4 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -17,10 +17,14 @@ use std::{ops::Range, sync::Arc}; use theme::Theme; use workspace::{FollowNextCollaborator, ToggleFollow, Workspace}; -actions!(contacts_titlebar_item, [ToggleContactsPopover]); +actions!( + contacts_titlebar_item, + [ToggleContactsPopover, ShareProject] +); pub fn init(cx: &mut MutableAppContext) { cx.add_action(CollabTitlebarItem::toggle_contacts_popover); + cx.add_action(CollabTitlebarItem::share_project); } pub struct CollabTitlebarItem { @@ -47,12 +51,20 @@ impl View for CollabTitlebarItem { }; let theme = cx.global::().theme.clone(); - Flex::row() - .with_children(self.render_toggle_contacts_button(&workspace, &theme, cx)) - .with_children(self.render_collaborators(&workspace, &theme, cx)) - .with_children(self.render_current_user(&workspace, &theme, cx)) - .with_children(self.render_connection_status(&workspace, cx)) - .boxed() + let project = workspace.read(cx).project().read(cx); + + let mut container = Flex::row(); + if workspace.read(cx).client().status().borrow().is_connected() { + if project.is_shared() || ActiveCall::global(cx).read(cx).room().is_none() { + container.add_child(self.render_toggle_contacts_button(&theme, cx)); + } else { + container.add_child(self.render_share_button(&theme, cx)); + } + } + container.add_children(self.render_collaborators(&workspace, &theme, cx)); + container.add_children(self.render_current_user(&workspace, &theme, cx)); + container.add_children(self.render_connection_status(&workspace, cx)); + container.boxed() } } @@ -81,6 +93,18 @@ impl CollabTitlebarItem { cx.notify(); } + fn share_project(&mut self, _: &ShareProject, cx: &mut ViewContext) { + if let Some(workspace) = self.workspace.upgrade(cx) { + if let Some(room) = ActiveCall::global(cx).read(cx).room() { + let room_id = room.read(cx).id(); + let project = workspace.read(cx).project().clone(); + project + .update(cx, |project, cx| project.share(room_id, cx)) + .detach_and_log_err(cx); + } + } + } + fn toggle_contacts_popover(&mut self, _: &ToggleContactsPopover, cx: &mut ViewContext) { match self.contacts_popover.take() { Some(_) => {} @@ -108,58 +132,72 @@ impl CollabTitlebarItem { fn render_toggle_contacts_button( &self, - workspace: &ViewHandle, theme: &Theme, cx: &mut RenderContext, - ) -> Option { - if !workspace.read(cx).client().status().borrow().is_connected() { - return None; - } + ) -> ElementBox { + let titlebar = &theme.workspace.titlebar; + Stack::new() + .with_child( + MouseEventHandler::::new(0, cx, |state, _| { + let style = titlebar + .toggle_contacts_button + .style_for(state, self.contacts_popover.is_some()); + Svg::new("icons/plus_8.svg") + .with_color(style.color) + .constrained() + .with_width(style.icon_width) + .aligned() + .constrained() + .with_width(style.button_width) + .with_height(style.button_width) + .contained() + .with_style(style.container) + .boxed() + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, |_, cx| { + cx.dispatch_action(ToggleContactsPopover); + }) + .aligned() + .boxed(), + ) + .with_children(self.contacts_popover.as_ref().map(|popover| { + Overlay::new( + ChildView::new(popover) + .contained() + .with_margin_top(titlebar.height) + .with_margin_right(-titlebar.toggle_contacts_button.default.button_width) + .boxed(), + ) + .with_fit_mode(OverlayFitMode::SwitchAnchor) + .with_anchor_corner(AnchorCorner::BottomLeft) + .boxed() + })) + .boxed() + } + + fn render_share_button(&self, theme: &Theme, cx: &mut RenderContext) -> ElementBox { + enum Share {} let titlebar = &theme.workspace.titlebar; - - Some( - Stack::new() - .with_child( - MouseEventHandler::::new(0, cx, |state, _| { - let style = titlebar - .toggle_contacts_button - .style_for(state, self.contacts_popover.is_some()); - Svg::new("icons/plus_8.svg") - .with_color(style.color) - .constrained() - .with_width(style.icon_width) - .aligned() - .constrained() - .with_width(style.button_width) - .with_height(style.button_width) - .contained() - .with_style(style.container) - .boxed() - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, |_, cx| { - cx.dispatch_action(ToggleContactsPopover); - }) - .aligned() - .boxed(), - ) - .with_children(self.contacts_popover.as_ref().map(|popover| { - Overlay::new( - ChildView::new(popover) - .contained() - .with_margin_top(titlebar.height) - .with_margin_right( - -titlebar.toggle_contacts_button.default.button_width, - ) - .boxed(), - ) - .with_fit_mode(OverlayFitMode::SwitchAnchor) - .with_anchor_corner(AnchorCorner::BottomLeft) - .boxed() - })) - .boxed(), + MouseEventHandler::::new(0, cx, |state, _| { + let style = titlebar.share_button.style_for(state, false); + Label::new("Share".into(), style.text.clone()) + .contained() + .with_style(style.container) + .boxed() + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, |_, cx| cx.dispatch_action(ShareProject)) + .with_tooltip::( + 0, + "Share project with call participants".into(), + None, + theme.tooltip.clone(), + cx, ) + .aligned() + .boxed() } fn render_collaborators( diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index 7d0473bfc5..7420961041 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -479,40 +479,49 @@ impl ContactsPopover { is_selected: bool, cx: &mut RenderContext, ) -> ElementBox { + let online = contact.online; let user_id = contact.user.id; - MouseEventHandler::::new(contact.user.id as usize, cx, |_, _| { - Flex::row() - .with_children(contact.user.avatar.clone().map(|avatar| { - Image::new(avatar) - .with_style(theme.contact_avatar) + let mut element = + MouseEventHandler::::new(contact.user.id as usize, cx, |_, _| { + Flex::row() + .with_children(contact.user.avatar.clone().map(|avatar| { + Image::new(avatar) + .with_style(theme.contact_avatar) + .aligned() + .left() + .boxed() + })) + .with_child( + Label::new( + contact.user.github_login.clone(), + theme.contact_username.text.clone(), + ) + .contained() + .with_style(theme.contact_username.container) .aligned() .left() - .boxed() - })) - .with_child( - Label::new( - contact.user.github_login.clone(), - theme.contact_username.text.clone(), + .flex(1., true) + .boxed(), ) + .constrained() + .with_height(theme.row_height) .contained() - .with_style(theme.contact_username.container) - .aligned() - .left() - .flex(1., true) - .boxed(), - ) - .constrained() - .with_height(theme.row_height) - .contained() - .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) - .boxed() - }) - .on_click(MouseButton::Left, move |_, cx| { - cx.dispatch_action(Call { - recipient_user_id: user_id, + .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) + .boxed() }) - }) - .boxed() + .on_click(MouseButton::Left, move |_, cx| { + if online { + cx.dispatch_action(Call { + recipient_user_id: user_id, + }); + } + }); + + if online { + element = element.with_cursor_style(CursorStyle::PointingHand); + } + + element.boxed() } fn render_contact_request( diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 7e78f74c59..52b34462aa 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -74,6 +74,7 @@ pub struct Titlebar { pub inactive_avatar: ImageStyle, pub sign_in_prompt: Interactive, pub outdated_warning: ContainedText, + pub share_button: Interactive, pub toggle_contacts_button: Interactive, pub contacts_popover: AddParticipantPopover, } diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 4889ee3f10..40ed44e8db 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -17,6 +17,26 @@ export function workspaceBackground(theme: Theme) { export default function workspace(theme: Theme) { const titlebarPadding = 6; + const titlebarButton = { + background: backgroundColor(theme, 100), + border: border(theme, "secondary"), + cornerRadius: 6, + margin: { + top: 1, + }, + padding: { + top: 1, + bottom: 1, + left: 7, + right: 7, + }, + ...text(theme, "sans", "secondary", { size: "xs" }), + hover: { + ...text(theme, "sans", "active", { size: "xs" }), + background: backgroundColor(theme, "on300", "hovered"), + border: border(theme, "primary"), + }, + }; return { background: backgroundColor(theme, 300), @@ -81,24 +101,7 @@ export default function workspace(theme: Theme) { }, border: border(theme, "primary", { bottom: true, overlay: true }), signInPrompt: { - background: backgroundColor(theme, 100), - border: border(theme, "secondary"), - cornerRadius: 6, - margin: { - top: 1, - }, - padding: { - top: 1, - bottom: 1, - left: 7, - right: 7, - }, - ...text(theme, "sans", "secondary", { size: "xs" }), - hover: { - ...text(theme, "sans", "active", { size: "xs" }), - background: backgroundColor(theme, "on300", "hovered"), - border: border(theme, "primary"), - }, + ...titlebarButton }, offlineIcon: { color: iconColor(theme, "secondary"), @@ -137,6 +140,9 @@ export default function workspace(theme: Theme) { color: iconColor(theme, "active"), }, }, + shareButton: { + ...titlebarButton + }, contactsPopover: { background: backgroundColor(theme, 300, "base"), cornerRadius: 6, From debedaf004be2c14af5714afd3a7ef8ef8dc8489 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 4 Oct 2022 16:55:41 +0200 Subject: [PATCH 136/314] Show notification when a new project is shared and allow joining it --- crates/collab_ui/src/collab_ui.rs | 11 +- .../src/project_shared_notification.rs | 174 ++++++++++++++++++ crates/theme/src/theme.rs | 9 + crates/zed/src/main.rs | 2 +- styles/src/styleTree/app.ts | 2 + .../styleTree/projectSharedNotification.ts | 22 +++ 6 files changed, 215 insertions(+), 5 deletions(-) create mode 100644 crates/collab_ui/src/project_shared_notification.rs create mode 100644 styles/src/styleTree/projectSharedNotification.ts diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 4bb0860704..ccf17974a4 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -1,13 +1,16 @@ mod collab_titlebar_item; mod contacts_popover; mod incoming_call_notification; +mod project_shared_notification; -use client::UserStore; pub use collab_titlebar_item::CollabTitlebarItem; -use gpui::{ModelHandle, MutableAppContext}; +use gpui::MutableAppContext; +use std::sync::Arc; +use workspace::AppState; -pub fn init(user_store: ModelHandle, cx: &mut MutableAppContext) { +pub fn init(app_state: Arc, cx: &mut MutableAppContext) { contacts_popover::init(cx); collab_titlebar_item::init(cx); - incoming_call_notification::init(user_store, cx); + incoming_call_notification::init(app_state.user_store.clone(), cx); + project_shared_notification::init(app_state, cx); } diff --git a/crates/collab_ui/src/project_shared_notification.rs b/crates/collab_ui/src/project_shared_notification.rs new file mode 100644 index 0000000000..879c3ca28c --- /dev/null +++ b/crates/collab_ui/src/project_shared_notification.rs @@ -0,0 +1,174 @@ +use call::{room, ActiveCall}; +use client::User; +use gpui::{ + actions, + elements::*, + geometry::{rect::RectF, vector::vec2f}, + Entity, MouseButton, MutableAppContext, RenderContext, View, ViewContext, WindowBounds, + WindowKind, WindowOptions, +}; +use project::Project; +use settings::Settings; +use std::sync::Arc; +use workspace::{AppState, Workspace}; + +actions!(project_shared_notification, [JoinProject, DismissProject]); + +pub fn init(app_state: Arc, cx: &mut MutableAppContext) { + cx.add_action(ProjectSharedNotification::join); + cx.add_action(ProjectSharedNotification::dismiss); + + let active_call = ActiveCall::global(cx); + let mut _room_subscription = None; + cx.observe(&active_call, move |active_call, cx| { + if let Some(room) = active_call.read(cx).room().cloned() { + let app_state = app_state.clone(); + _room_subscription = Some(cx.subscribe(&room, move |_, event, cx| match event { + room::Event::RemoteProjectShared { owner, project_id } => { + cx.add_window( + WindowOptions { + bounds: WindowBounds::Fixed(RectF::new( + vec2f(0., 0.), + vec2f(300., 400.), + )), + titlebar: None, + center: true, + kind: WindowKind::PopUp, + is_movable: false, + }, + |_| { + ProjectSharedNotification::new( + *project_id, + owner.clone(), + app_state.clone(), + ) + }, + ); + } + })); + } else { + _room_subscription = None; + } + }) + .detach(); +} + +pub struct ProjectSharedNotification { + project_id: u64, + owner: Arc, + app_state: Arc, +} + +impl ProjectSharedNotification { + fn new(project_id: u64, owner: Arc, app_state: Arc) -> Self { + Self { + project_id, + owner, + app_state, + } + } + + fn join(&mut self, _: &JoinProject, cx: &mut ViewContext) { + let project_id = self.project_id; + let app_state = self.app_state.clone(); + cx.spawn_weak(|_, mut cx| async move { + let project = Project::remote( + project_id, + app_state.client.clone(), + app_state.user_store.clone(), + app_state.project_store.clone(), + app_state.languages.clone(), + app_state.fs.clone(), + cx.clone(), + ) + .await?; + + cx.add_window((app_state.build_window_options)(), |cx| { + let mut workspace = Workspace::new(project, app_state.default_item_factory, cx); + (app_state.initialize_workspace)(&mut workspace, &app_state, cx); + workspace + }); + + anyhow::Ok(()) + }) + .detach_and_log_err(cx); + + let window_id = cx.window_id(); + cx.remove_window(window_id); + } + + fn dismiss(&mut self, _: &DismissProject, cx: &mut ViewContext) { + let window_id = cx.window_id(); + cx.remove_window(window_id); + } + + fn render_owner(&self, cx: &mut RenderContext) -> ElementBox { + let theme = &cx.global::().theme.project_shared_notification; + Flex::row() + .with_children( + self.owner + .avatar + .clone() + .map(|avatar| Image::new(avatar).with_style(theme.owner_avatar).boxed()), + ) + .with_child( + Label::new( + format!("{} has shared a new project", self.owner.github_login), + theme.message.text.clone(), + ) + .boxed(), + ) + .boxed() + } + + fn render_buttons(&self, cx: &mut RenderContext) -> ElementBox { + enum Join {} + enum Dismiss {} + + Flex::row() + .with_child( + MouseEventHandler::::new(0, cx, |_, cx| { + let theme = &cx.global::().theme.project_shared_notification; + Label::new("Join".to_string(), theme.join_button.text.clone()) + .contained() + .with_style(theme.join_button.container) + .boxed() + }) + .on_click(MouseButton::Left, |_, cx| { + cx.dispatch_action(JoinProject); + }) + .boxed(), + ) + .with_child( + MouseEventHandler::::new(0, cx, |_, cx| { + let theme = &cx.global::().theme.project_shared_notification; + Label::new("Dismiss".to_string(), theme.dismiss_button.text.clone()) + .contained() + .with_style(theme.dismiss_button.container) + .boxed() + }) + .on_click(MouseButton::Left, |_, cx| { + cx.dispatch_action(DismissProject); + }) + .boxed(), + ) + .boxed() + } +} + +impl Entity for ProjectSharedNotification { + type Event = (); +} + +impl View for ProjectSharedNotification { + fn ui_name() -> &'static str { + "ProjectSharedNotification" + } + + fn render(&mut self, cx: &mut RenderContext) -> gpui::ElementBox { + Flex::row() + .with_child(self.render_owner(cx)) + .with_child(self.render_buttons(cx)) + .boxed() + } +} diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 52b34462aa..6253b6dabb 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -30,6 +30,7 @@ pub struct Theme { pub breadcrumbs: ContainedText, pub contact_notification: ContactNotification, pub update_notification: UpdateNotification, + pub project_shared_notification: ProjectSharedNotification, pub tooltip: TooltipStyle, pub terminal: TerminalStyle, } @@ -481,6 +482,14 @@ pub struct UpdateNotification { pub dismiss_button: Interactive, } +#[derive(Deserialize, Default)] +pub struct ProjectSharedNotification { + pub owner_avatar: ImageStyle, + pub message: ContainedText, + pub join_button: ContainedText, + pub dismiss_button: ContainedText, +} + #[derive(Clone, Deserialize, Default)] pub struct Editor { pub text_color: Color, diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index ea42c61dfb..580493f6d0 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -107,7 +107,6 @@ fn main() { project::Project::init(&client); client::Channel::init(&client); client::init(client.clone(), cx); - collab_ui::init(user_store.clone(), cx); command_palette::init(cx); editor::init(cx); go_to_line::init(cx); @@ -157,6 +156,7 @@ fn main() { journal::init(app_state.clone(), cx); theme_selector::init(app_state.clone(), cx); zed::init(&app_state, cx); + collab_ui::init(app_state.clone(), cx); cx.set_menus(menus::menus()); diff --git a/styles/src/styleTree/app.ts b/styles/src/styleTree/app.ts index a3ab4b654c..1c0c81cfde 100644 --- a/styles/src/styleTree/app.ts +++ b/styles/src/styleTree/app.ts @@ -14,6 +14,7 @@ import contextMenu from "./contextMenu"; import projectDiagnostics from "./projectDiagnostics"; import contactNotification from "./contactNotification"; import updateNotification from "./updateNotification"; +import projectSharedNotification from "./projectSharedNotification"; import tooltip from "./tooltip"; import terminal from "./terminal"; @@ -47,6 +48,7 @@ export default function app(theme: Theme): Object { }, contactNotification: contactNotification(theme), updateNotification: updateNotification(theme), + projectSharedNotification: projectSharedNotification(theme), tooltip: tooltip(theme), terminal: terminal(theme), }; diff --git a/styles/src/styleTree/projectSharedNotification.ts b/styles/src/styleTree/projectSharedNotification.ts new file mode 100644 index 0000000000..bc34265135 --- /dev/null +++ b/styles/src/styleTree/projectSharedNotification.ts @@ -0,0 +1,22 @@ +import Theme from "../themes/common/theme"; +import { text } from "./components"; + +export default function projectSharedNotification(theme: Theme): Object { + const avatarSize = 12; + return { + ownerAvatar: { + height: avatarSize, + width: avatarSize, + cornerRadius: 6, + }, + message: { + ...text(theme, "sans", "primary", { size: "xs" }), + }, + joinButton: { + ...text(theme, "sans", "primary", { size: "xs" }) + }, + dismissButton: { + ...text(theme, "sans", "primary", { size: "xs" }) + }, + }; +} From 41240351d351197392562c04b8c59a1336104c0f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 4 Oct 2022 18:00:54 +0200 Subject: [PATCH 137/314] Simplify `Collaborator` to stop including the user It can be retrieved from the `Room` and we're guaranteed to have a room in order to have collaborators in a project. --- crates/collab/src/integration_tests.rs | 14 +--------- crates/collab_ui/src/collab_titlebar_item.rs | 5 ++-- crates/project/src/project.rs | 27 +++++--------------- 3 files changed, 11 insertions(+), 35 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 40af0a731e..6cadadb4c5 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -318,24 +318,12 @@ async fn test_share_project( // Join that project as client B let client_b_peer_id = client_b.peer_id; let project_b = client_b.build_remote_project(project_id, cx_b).await; - let replica_id_b = project_b.read_with(cx_b, |project, _| { - assert_eq!( - project - .collaborators() - .get(&client_a.peer_id) - .unwrap() - .user - .github_login, - "user_a" - ); - project.replica_id() - }); + let replica_id_b = project_b.read_with(cx_b, |project, _| project.replica_id()); deterministic.run_until_parked(); project_a.read_with(cx_a, |project, _| { let client_b_collaborator = project.collaborators().get(&client_b_peer_id).unwrap(); assert_eq!(client_b_collaborator.replica_id, replica_id_b); - assert_eq!(client_b_collaborator.user.github_login, "user_b"); }); project_b.read_with(cx_b, |project, cx| { let worktree = project.worktrees(cx).next().unwrap().read(cx); diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 8b467e6cb4..a30ae68f9f 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -223,13 +223,14 @@ impl CollabTitlebarItem { .read(cx) .remote_participants() .get(&collaborator.peer_id)?; + let user = participant.user.clone(); let is_active = project_id.map_or(false, |project_id| { participant.location == ParticipantLocation::Project { project_id } }); Some(self.render_avatar( - collaborator.user.avatar.clone()?, + user.avatar.clone()?, collaborator.replica_id, - Some((collaborator.peer_id, &collaborator.user.github_login)), + Some((collaborator.peer_id, &user.github_login)), is_active, workspace, theme, diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index c0ed2e5ad4..40503297b3 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -9,7 +9,7 @@ pub mod worktree; mod project_tests; use anyhow::{anyhow, Context, Result}; -use client::{proto, Client, PeerId, TypedEnvelope, User, UserStore}; +use client::{proto, Client, PeerId, TypedEnvelope, UserStore}; use clock::ReplicaId; use collections::{hash_map, BTreeMap, HashMap, HashSet}; use futures::{future::Shared, AsyncWriteExt, Future, FutureExt, StreamExt, TryFutureExt}; @@ -165,7 +165,6 @@ enum ProjectClientState { #[derive(Clone, Debug)] pub struct Collaborator { - pub user: Arc, pub peer_id: PeerId, pub replica_id: ReplicaId, } @@ -582,7 +581,7 @@ impl Project { .await?; let mut collaborators = HashMap::default(); for message in response.collaborators { - let collaborator = Collaborator::from_proto(message, &user_store, &mut cx).await?; + let collaborator = Collaborator::from_proto(message); collaborators.insert(collaborator.peer_id, collaborator); } @@ -4451,14 +4450,13 @@ impl Project { _: Arc, mut cx: AsyncAppContext, ) -> Result<()> { - let user_store = this.read_with(&cx, |this, _| this.user_store.clone()); let collaborator = envelope .payload .collaborator .take() .ok_or_else(|| anyhow!("empty collaborator"))?; - let collaborator = Collaborator::from_proto(collaborator, &user_store, &mut cx).await?; + let collaborator = Collaborator::from_proto(collaborator); this.update(&mut cx, |this, cx| { this.collaborators .insert(collaborator.peer_id, collaborator); @@ -5904,21 +5902,10 @@ impl Entity for Project { } impl Collaborator { - fn from_proto( - message: proto::Collaborator, - user_store: &ModelHandle, - cx: &mut AsyncAppContext, - ) -> impl Future> { - let user = user_store.update(cx, |user_store, cx| { - user_store.get_user(message.user_id, cx) - }); - - async move { - Ok(Self { - peer_id: PeerId(message.peer_id), - user: user.await?, - replica_id: message.replica_id as ReplicaId, - }) + fn from_proto(message: proto::Collaborator) -> Self { + Self { + peer_id: PeerId(message.peer_id), + replica_id: message.replica_id as ReplicaId, } } } From ebee2168fc737a5534779a6f1ddea28563736b31 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 4 Oct 2022 18:15:56 +0200 Subject: [PATCH 138/314] Re-emit notifications and events from `ActiveCall` This lets us only observe and subscribe to the active call without needing to track the underlying `Room` if it changes, which implies writing the same boilerplate over and over. --- crates/call/src/call.rs | 34 ++++++++------- crates/collab_ui/src/collab_titlebar_item.rs | 17 +------- crates/collab_ui/src/contacts_popover.rs | 14 +------ .../src/project_shared_notification.rs | 41 ++++++------------- 4 files changed, 35 insertions(+), 71 deletions(-) diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index d800c3ac06..19de06c1c4 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -3,7 +3,7 @@ pub mod room; use anyhow::{anyhow, Result}; use client::{incoming_call::IncomingCall, Client, UserStore}; -use gpui::{Entity, ModelContext, ModelHandle, MutableAppContext, Task}; +use gpui::{Entity, ModelContext, ModelHandle, MutableAppContext, Subscription, Task}; pub use participant::ParticipantLocation; pub use room::Room; use std::sync::Arc; @@ -14,13 +14,13 @@ pub fn init(client: Arc, user_store: ModelHandle, cx: &mut Mu } pub struct ActiveCall { - room: Option>, + room: Option<(ModelHandle, Vec)>, client: Arc, user_store: ModelHandle, } impl Entity for ActiveCall { - type Event = (); + type Event = room::Event; } impl ActiveCall { @@ -41,8 +41,7 @@ impl ActiveCall { recipient_user_id: u64, cx: &mut ModelContext, ) -> Task> { - let room = self.room.clone(); - + let room = self.room.as_ref().map(|(room, _)| room.clone()); let client = self.client.clone(); let user_store = self.user_store.clone(); cx.spawn(|this, mut cx| async move { @@ -50,10 +49,7 @@ impl ActiveCall { room } else { let room = cx.update(|cx| Room::create(client, user_store, cx)).await?; - this.update(&mut cx, |this, cx| { - this.room = Some(room.clone()); - cx.notify(); - }); + this.update(&mut cx, |this, cx| this.set_room(Some(room.clone()), cx)); room }; room.update(&mut cx, |room, cx| room.call(recipient_user_id, cx)) @@ -71,15 +67,25 @@ impl ActiveCall { let join = Room::join(call, self.client.clone(), self.user_store.clone(), cx); cx.spawn(|this, mut cx| async move { let room = join.await?; - this.update(&mut cx, |this, cx| { - this.room = Some(room); - cx.notify(); - }); + this.update(&mut cx, |this, cx| this.set_room(Some(room.clone()), cx)); Ok(()) }) } + fn set_room(&mut self, room: Option>, cx: &mut ModelContext) { + if let Some(room) = room { + let subscriptions = vec![ + cx.observe(&room, |_, _, cx| cx.notify()), + cx.subscribe(&room, |_, _, event, cx| cx.emit(event.clone())), + ]; + self.room = Some((room, subscriptions)); + } else { + self.room = None; + } + cx.notify(); + } + pub fn room(&self) -> Option<&ModelHandle> { - self.room.as_ref() + self.room.as_ref().map(|(room, _)| room) } } diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index a30ae68f9f..81ec7973a5 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -30,7 +30,6 @@ pub fn init(cx: &mut MutableAppContext) { pub struct CollabTitlebarItem { workspace: WeakViewHandle, contacts_popover: Option>, - room_subscription: Option, _subscriptions: Vec, } @@ -73,24 +72,12 @@ impl CollabTitlebarItem { let active_call = ActiveCall::global(cx); let mut subscriptions = Vec::new(); subscriptions.push(cx.observe(workspace, |_, _, cx| cx.notify())); - subscriptions.push(cx.observe(&active_call, |this, _, cx| this.active_call_changed(cx))); - let mut this = Self { + subscriptions.push(cx.observe(&active_call, |_, _, cx| cx.notify())); + Self { workspace: workspace.downgrade(), contacts_popover: None, - room_subscription: None, _subscriptions: subscriptions, - }; - this.active_call_changed(cx); - this - } - - fn active_call_changed(&mut self, cx: &mut ViewContext) { - if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { - self.room_subscription = Some(cx.observe(&room, |_, _, cx| cx.notify())); - } else { - self.room_subscription = None; } - cx.notify(); } fn share_project(&mut self, _: &ShareProject, cx: &mut ViewContext) { diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index 7420961041..1980d8a14f 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -80,7 +80,6 @@ pub enum Event { } pub struct ContactsPopover { - room_subscription: Option, entries: Vec, match_candidates: Vec, list_state: ListState, @@ -159,10 +158,9 @@ impl ContactsPopover { let active_call = ActiveCall::global(cx); let mut subscriptions = Vec::new(); subscriptions.push(cx.observe(&user_store, |this, _, cx| this.update_entries(cx))); - subscriptions.push(cx.observe(&active_call, |this, _, cx| this.active_call_changed(cx))); + subscriptions.push(cx.observe(&active_call, |_, _, cx| cx.notify())); let mut this = Self { - room_subscription: None, list_state, selection: None, collapsed_sections: Default::default(), @@ -173,19 +171,9 @@ impl ContactsPopover { user_store, }; this.update_entries(cx); - this.active_call_changed(cx); this } - fn active_call_changed(&mut self, cx: &mut ViewContext) { - if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { - self.room_subscription = Some(cx.observe(&room, |_, _, cx| cx.notify())); - } else { - self.room_subscription = None; - } - cx.notify(); - } - fn clear_filter(&mut self, _: &Cancel, cx: &mut ViewContext) { let did_clear = self.filter_editor.update(cx, |editor, cx| { if editor.buffer().read(cx).len(cx) > 0 { diff --git a/crates/collab_ui/src/project_shared_notification.rs b/crates/collab_ui/src/project_shared_notification.rs index 879c3ca28c..53eb17684e 100644 --- a/crates/collab_ui/src/project_shared_notification.rs +++ b/crates/collab_ui/src/project_shared_notification.rs @@ -19,35 +19,18 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { cx.add_action(ProjectSharedNotification::dismiss); let active_call = ActiveCall::global(cx); - let mut _room_subscription = None; - cx.observe(&active_call, move |active_call, cx| { - if let Some(room) = active_call.read(cx).room().cloned() { - let app_state = app_state.clone(); - _room_subscription = Some(cx.subscribe(&room, move |_, event, cx| match event { - room::Event::RemoteProjectShared { owner, project_id } => { - cx.add_window( - WindowOptions { - bounds: WindowBounds::Fixed(RectF::new( - vec2f(0., 0.), - vec2f(300., 400.), - )), - titlebar: None, - center: true, - kind: WindowKind::PopUp, - is_movable: false, - }, - |_| { - ProjectSharedNotification::new( - *project_id, - owner.clone(), - app_state.clone(), - ) - }, - ); - } - })); - } else { - _room_subscription = None; + cx.subscribe(&active_call, move |_, event, cx| match event { + room::Event::RemoteProjectShared { owner, project_id } => { + cx.add_window( + WindowOptions { + bounds: WindowBounds::Fixed(RectF::new(vec2f(0., 0.), vec2f(300., 400.))), + titlebar: None, + center: true, + kind: WindowKind::PopUp, + is_movable: false, + }, + |_| ProjectSharedNotification::new(*project_id, owner.clone(), app_state.clone()), + ); } }) .detach(); From 678b013da6a0c604b362417c273ccda2c225b0c5 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 4 Oct 2022 18:35:54 +0200 Subject: [PATCH 139/314] Don't show share button for remote projects Co-Authored-By: Max Brunsfeld --- crates/collab_ui/src/collab_titlebar_item.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 81ec7973a5..94593d5dc5 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -54,7 +54,10 @@ impl View for CollabTitlebarItem { let mut container = Flex::row(); if workspace.read(cx).client().status().borrow().is_connected() { - if project.is_shared() || ActiveCall::global(cx).read(cx).room().is_none() { + if project.is_shared() + || project.is_remote() + || ActiveCall::global(cx).read(cx).room().is_none() + { container.add_child(self.render_toggle_contacts_button(&theme, cx)); } else { container.add_child(self.render_share_button(&theme, cx)); From fceba6814ffda8e75647ff9957296b13567f5d8a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 4 Oct 2022 19:25:48 +0200 Subject: [PATCH 140/314] Automatically share project when creating the room Co-Authored-By: Max Brunsfeld --- crates/call/src/call.rs | 19 ++++++- crates/call/src/room.rs | 2 + crates/client/src/incoming_call.rs | 1 + crates/client/src/user.rs | 1 + crates/collab/src/integration_tests.rs | 18 ++++-- crates/collab/src/rpc.rs | 12 +++- crates/collab/src/rpc/store.rs | 27 +++++++-- crates/collab_ui/src/collab_titlebar_item.rs | 3 +- crates/collab_ui/src/collab_ui.rs | 2 +- crates/collab_ui/src/contacts_popover.rs | 29 ++++++++-- .../src/incoming_call_notification.rs | 56 ++++++++++++++----- crates/rpc/proto/zed.proto | 2 + 12 files changed, 137 insertions(+), 35 deletions(-) diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 19de06c1c4..a861f94bd0 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -5,6 +5,7 @@ use anyhow::{anyhow, Result}; use client::{incoming_call::IncomingCall, Client, UserStore}; use gpui::{Entity, ModelContext, ModelHandle, MutableAppContext, Subscription, Task}; pub use participant::ParticipantLocation; +use project::Project; pub use room::Room; use std::sync::Arc; @@ -39,6 +40,7 @@ impl ActiveCall { pub fn invite( &mut self, recipient_user_id: u64, + initial_project: Option>, cx: &mut ModelContext, ) -> Task> { let room = self.room.as_ref().map(|(room, _)| room.clone()); @@ -52,8 +54,21 @@ impl ActiveCall { this.update(&mut cx, |this, cx| this.set_room(Some(room.clone()), cx)); room }; - room.update(&mut cx, |room, cx| room.call(recipient_user_id, cx)) - .await?; + + let initial_project_id = if let Some(initial_project) = initial_project { + let room_id = room.read_with(&cx, |room, _| room.id()); + Some( + initial_project + .update(&mut cx, |project, cx| project.share(room_id, cx)) + .await?, + ) + } else { + None + }; + room.update(&mut cx, |room, cx| { + room.call(recipient_user_id, initial_project_id, cx) + }) + .await?; Ok(()) }) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 4cac210f8b..0237972167 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -216,6 +216,7 @@ impl Room { pub fn call( &mut self, recipient_user_id: u64, + initial_project_id: Option, cx: &mut ModelContext, ) -> Task> { if self.status.is_offline() { @@ -229,6 +230,7 @@ impl Room { .request(proto::Call { room_id, recipient_user_id, + initial_project_id, }) .await?; Ok(()) diff --git a/crates/client/src/incoming_call.rs b/crates/client/src/incoming_call.rs index 75d8411ec3..80ba014061 100644 --- a/crates/client/src/incoming_call.rs +++ b/crates/client/src/incoming_call.rs @@ -6,4 +6,5 @@ pub struct IncomingCall { pub room_id: u64, pub caller: Arc, pub participants: Vec>, + pub initial_project_id: Option, } diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 0529045c77..2d79b7be84 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -212,6 +212,7 @@ impl UserStore { this.get_user(envelope.payload.caller_user_id, cx) }) .await?, + initial_project_id: envelope.payload.initial_project_id, }; this.update(&mut cx, |this, _| { *this.incoming_call.0.borrow_mut() = Some(call); diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 6cadadb4c5..92f94b6621 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -95,7 +95,9 @@ async fn test_basic_calls( .user_store .update(cx_b, |user, _| user.incoming_call()); room_a - .update(cx_a, |room, cx| room.call(client_b.user_id().unwrap(), cx)) + .update(cx_a, |room, cx| { + room.call(client_b.user_id().unwrap(), None, cx) + }) .await .unwrap(); @@ -147,7 +149,9 @@ async fn test_basic_calls( .user_store .update(cx_c, |user, _| user.incoming_call()); room_b - .update(cx_b, |room, cx| room.call(client_c.user_id().unwrap(), cx)) + .update(cx_b, |room, cx| { + room.call(client_c.user_id().unwrap(), None, cx) + }) .await .unwrap(); @@ -234,7 +238,9 @@ async fn test_leaving_room_on_disconnection( .user_store .update(cx_b, |user, _| user.incoming_call()); room_a - .update(cx_a, |room, cx| room.call(client_b.user_id().unwrap(), cx)) + .update(cx_a, |room, cx| { + room.call(client_b.user_id().unwrap(), None, cx) + }) .await .unwrap(); @@ -4849,7 +4855,7 @@ async fn test_random_collaboration( host_language_registry.add(Arc::new(language)); let host_user_id = host.current_user_id(&host_cx); - room.update(cx, |room, cx| room.call(host_user_id.to_proto(), cx)) + room.update(cx, |room, cx| room.call(host_user_id.to_proto(), None, cx)) .await .unwrap(); deterministic.run_until_parked(); @@ -4941,7 +4947,7 @@ async fn test_random_collaboration( let guest = server.create_client(&mut guest_cx, &guest_username).await; let guest_user_id = guest.current_user_id(&guest_cx); - room.update(cx, |room, cx| room.call(guest_user_id.to_proto(), cx)) + room.update(cx, |room, cx| room.call(guest_user_id.to_proto(), None, cx)) .await .unwrap(); deterministic.run_until_parked(); @@ -5353,7 +5359,7 @@ impl TestServer { for (client_b, cx_b) in right { let user_id_b = client_b.current_user_id(*cx_b).to_proto(); room_a - .update(*cx_a, |room, cx| room.call(user_id_b, cx)) + .update(*cx_a, |room, cx| room.call(user_id_b, None, cx)) .await .unwrap(); diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index c000400568..4098e6522a 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -660,6 +660,10 @@ impl Server { .await .user_id_for_connection(request.sender_id)?; let recipient_user_id = UserId::from_proto(request.payload.recipient_user_id); + let initial_project_id = request + .payload + .initial_project_id + .map(ProjectId::from_proto); if !self .app_state .db @@ -672,8 +676,12 @@ impl Server { let room_id = request.payload.room_id; let mut calls = { let mut store = self.store().await; - let (room, recipient_connection_ids, incoming_call) = - store.call(room_id, request.sender_id, recipient_user_id)?; + let (room, recipient_connection_ids, incoming_call) = store.call( + room_id, + recipient_user_id, + initial_project_id, + request.sender_id, + )?; self.room_updated(room); recipient_connection_ids .into_iter() diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index c917309cd2..da9f242ac5 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -40,6 +40,7 @@ pub struct Call { pub caller_user_id: UserId, pub room_id: RoomId, pub connection_id: Option, + pub initial_project_id: Option, } #[derive(Serialize)] @@ -175,6 +176,9 @@ impl Store { .iter() .map(|participant| participant.user_id) .collect(), + initial_project_id: active_call + .initial_project_id + .map(|project_id| project_id.to_proto()), }) } } else { @@ -379,6 +383,7 @@ impl Store { caller_user_id: connection.user_id, room_id, connection_id: Some(creator_connection_id), + initial_project_id: None, }); Ok(room_id) } @@ -486,17 +491,18 @@ impl Store { pub fn call( &mut self, room_id: RoomId, + recipient_user_id: UserId, + initial_project_id: Option, from_connection_id: ConnectionId, - recipient_id: UserId, ) -> Result<(&proto::Room, Vec, proto::IncomingCall)> { let caller_user_id = self.user_id_for_connection(from_connection_id)?; let recipient_connection_ids = self - .connection_ids_for_user(recipient_id) + .connection_ids_for_user(recipient_user_id) .collect::>(); let mut recipient = self .connected_users - .get_mut(&recipient_id) + .get_mut(&recipient_user_id) .ok_or_else(|| anyhow!("no such connection"))?; anyhow::ensure!( recipient.active_call.is_none(), @@ -516,14 +522,24 @@ impl Store { anyhow::ensure!( room.pending_user_ids .iter() - .all(|user_id| UserId::from_proto(*user_id) != recipient_id), + .all(|user_id| UserId::from_proto(*user_id) != recipient_user_id), "cannot call the same user more than once" ); - room.pending_user_ids.push(recipient_id.to_proto()); + room.pending_user_ids.push(recipient_user_id.to_proto()); + + if let Some(initial_project_id) = initial_project_id { + let project = self + .projects + .get(&initial_project_id) + .ok_or_else(|| anyhow!("no such project"))?; + anyhow::ensure!(project.room_id == room_id, "no such project"); + } + recipient.active_call = Some(Call { caller_user_id, room_id, connection_id: None, + initial_project_id, }); Ok(( @@ -537,6 +553,7 @@ impl Store { .iter() .map(|participant| participant.user_id) .collect(), + initial_project_id: initial_project_id.map(|project_id| project_id.to_proto()), }, )) } diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 94593d5dc5..8e4b8f9f32 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -100,8 +100,9 @@ impl CollabTitlebarItem { Some(_) => {} None => { if let Some(workspace) = self.workspace.upgrade(cx) { + let project = workspace.read(cx).project().clone(); let user_store = workspace.read(cx).user_store().clone(); - let view = cx.add_view(|cx| ContactsPopover::new(user_store, cx)); + let view = cx.add_view(|cx| ContactsPopover::new(project, user_store, cx)); cx.focus(&view); cx.subscribe(&view, |this, _, event, cx| { match event { diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index ccf17974a4..607c1b5054 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -11,6 +11,6 @@ use workspace::AppState; pub fn init(app_state: Arc, cx: &mut MutableAppContext) { contacts_popover::init(cx); collab_titlebar_item::init(cx); - incoming_call_notification::init(app_state.user_store.clone(), cx); + incoming_call_notification::init(app_state.clone(), cx); project_shared_notification::init(app_state, cx); } diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index 1980d8a14f..c4eef3c9d8 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -10,6 +10,7 @@ use gpui::{ ViewHandle, }; use menu::{Confirm, SelectNext, SelectPrev}; +use project::Project; use settings::Settings; use theme::IconButton; @@ -30,6 +31,7 @@ struct ToggleExpanded(Section); #[derive(Clone, PartialEq)] struct Call { recipient_user_id: u64, + initial_project: Option>, } #[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)] @@ -83,6 +85,7 @@ pub struct ContactsPopover { entries: Vec, match_candidates: Vec, list_state: ListState, + project: ModelHandle, user_store: ModelHandle, filter_editor: ViewHandle, collapsed_sections: Vec
, @@ -91,7 +94,11 @@ pub struct ContactsPopover { } impl ContactsPopover { - pub fn new(user_store: ModelHandle, cx: &mut ViewContext) -> Self { + pub fn new( + project: ModelHandle, + user_store: ModelHandle, + cx: &mut ViewContext, + ) -> Self { let filter_editor = cx.add_view(|cx| { let mut editor = Editor::single_line( Some(|theme| theme.contacts_panel.user_query_editor.clone()), @@ -149,9 +156,13 @@ impl ContactsPopover { is_selected, cx, ), - ContactEntry::Contact(contact) => { - Self::render_contact(contact, &theme.contacts_panel, is_selected, cx) - } + ContactEntry::Contact(contact) => Self::render_contact( + contact, + &this.project, + &theme.contacts_panel, + is_selected, + cx, + ), } }); @@ -168,6 +179,7 @@ impl ContactsPopover { match_candidates: Default::default(), filter_editor, _subscriptions: subscriptions, + project, user_store, }; this.update_entries(cx); @@ -463,12 +475,18 @@ impl ContactsPopover { fn render_contact( contact: &Contact, + project: &ModelHandle, theme: &theme::ContactsPanel, is_selected: bool, cx: &mut RenderContext, ) -> ElementBox { let online = contact.online; let user_id = contact.user.id; + let initial_project = if ActiveCall::global(cx).read(cx).room().is_none() { + Some(project.clone()) + } else { + None + }; let mut element = MouseEventHandler::::new(contact.user.id as usize, cx, |_, _| { Flex::row() @@ -501,6 +519,7 @@ impl ContactsPopover { if online { cx.dispatch_action(Call { recipient_user_id: user_id, + initial_project: initial_project.clone(), }); } }); @@ -629,7 +648,7 @@ impl ContactsPopover { fn call(&mut self, action: &Call, cx: &mut ViewContext) { ActiveCall::global(cx) .update(cx, |active_call, cx| { - active_call.invite(action.recipient_user_id, cx) + active_call.invite(action.recipient_user_id, action.initial_project.clone(), cx) }) .detach_and_log_err(cx); } diff --git a/crates/collab_ui/src/incoming_call_notification.rs b/crates/collab_ui/src/incoming_call_notification.rs index a239acc7e6..4630054c5e 100644 --- a/crates/collab_ui/src/incoming_call_notification.rs +++ b/crates/collab_ui/src/incoming_call_notification.rs @@ -1,21 +1,25 @@ +use std::sync::Arc; + use call::ActiveCall; -use client::{incoming_call::IncomingCall, UserStore}; +use client::incoming_call::IncomingCall; use futures::StreamExt; use gpui::{ elements::*, geometry::{rect::RectF, vector::vec2f}, - impl_internal_actions, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, - View, ViewContext, WindowBounds, WindowKind, WindowOptions, + impl_internal_actions, Entity, MouseButton, MutableAppContext, RenderContext, View, + ViewContext, WindowBounds, WindowKind, WindowOptions, }; +use project::Project; use settings::Settings; use util::ResultExt; +use workspace::{AppState, Workspace}; impl_internal_actions!(incoming_call_notification, [RespondToCall]); -pub fn init(user_store: ModelHandle, cx: &mut MutableAppContext) { +pub fn init(app_state: Arc, cx: &mut MutableAppContext) { cx.add_action(IncomingCallNotification::respond_to_call); - let mut incoming_call = user_store.read(cx).incoming_call(); + let mut incoming_call = app_state.user_store.read(cx).incoming_call(); cx.spawn(|mut cx| async move { let mut notification_window = None; while let Some(incoming_call) = incoming_call.next().await { @@ -32,7 +36,7 @@ pub fn init(user_store: ModelHandle, cx: &mut MutableAppContext) { kind: WindowKind::PopUp, is_movable: false, }, - |_| IncomingCallNotification::new(incoming_call, user_store.clone()), + |_| IncomingCallNotification::new(incoming_call, app_state.clone()), ); notification_window = Some(window_id); } @@ -48,21 +52,47 @@ struct RespondToCall { pub struct IncomingCallNotification { call: IncomingCall, - user_store: ModelHandle, + app_state: Arc, } impl IncomingCallNotification { - pub fn new(call: IncomingCall, user_store: ModelHandle) -> Self { - Self { call, user_store } + pub fn new(call: IncomingCall, app_state: Arc) -> Self { + Self { call, app_state } } fn respond_to_call(&mut self, action: &RespondToCall, cx: &mut ViewContext) { if action.accept { - ActiveCall::global(cx) - .update(cx, |active_call, cx| active_call.join(&self.call, cx)) - .detach_and_log_err(cx); + let app_state = self.app_state.clone(); + let join = ActiveCall::global(cx) + .update(cx, |active_call, cx| active_call.join(&self.call, cx)); + let initial_project_id = self.call.initial_project_id; + cx.spawn_weak(|_, mut cx| async move { + join.await?; + if let Some(initial_project_id) = initial_project_id { + let project = Project::remote( + initial_project_id, + app_state.client.clone(), + app_state.user_store.clone(), + app_state.project_store.clone(), + app_state.languages.clone(), + app_state.fs.clone(), + cx.clone(), + ) + .await?; + + cx.add_window((app_state.build_window_options)(), |cx| { + let mut workspace = + Workspace::new(project, app_state.default_item_factory, cx); + (app_state.initialize_workspace)(&mut workspace, &app_state, cx); + workspace + }); + } + anyhow::Ok(()) + }) + .detach_and_log_err(cx); } else { - self.user_store + self.app_state + .user_store .update(cx, |user_store, _| user_store.decline_call().log_err()); } diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index ffec915269..6c8ec72e86 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -179,12 +179,14 @@ message ParticipantLocation { message Call { uint64 room_id = 1; uint64 recipient_user_id = 2; + optional uint64 initial_project_id = 3; } message IncomingCall { uint64 room_id = 1; uint64 caller_user_id = 2; repeated uint64 participant_user_ids = 3; + optional uint64 initial_project_id = 4; } message CancelCall {} From b5d941b10cf7f1e129aebc9607a4330d902c9253 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 4 Oct 2022 11:43:52 -0700 Subject: [PATCH 141/314] 0.58.0 --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e99fa91008..26675a0596 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7151,7 +7151,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.57.0" +version = "0.58.0" dependencies = [ "activity_indicator", "anyhow", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 48a84a5831..491937cd59 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] description = "The fast, collaborative code editor." edition = "2021" name = "zed" -version = "0.57.0" +version = "0.58.0" [lib] name = "zed" From 2bd947d4d07c7b5679c986301e727174d4d35491 Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 4 Oct 2022 14:58:44 -0400 Subject: [PATCH 142/314] Use correct start row for hunk retrieval & correct paint offset Co-Authored-By: Joseph Lyons --- crates/editor/src/element.rs | 63 +++++++++++++----------------------- 1 file changed, 23 insertions(+), 40 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 56887f4b45..2b93255972 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -534,23 +534,22 @@ impl EditorElement { } struct DiffLayout<'a> { - buffer_line: usize, - last_diff: Option<(&'a DiffHunk, usize)>, + buffer_row: u32, + last_diff: Option<&'a DiffHunk>, } fn diff_quad( - status: DiffHunkStatus, - layout_range: Range, + hunk: &DiffHunk, gutter_layout: &GutterLayout, diff_style: &DiffStyle, ) -> Quad { - let color = match status { + let color = match hunk.status() { DiffHunkStatus::Added => diff_style.inserted, DiffHunkStatus::Modified => diff_style.modified, //TODO: This rendering is entirely a horrible hack DiffHunkStatus::Removed => { - let row = layout_range.start; + let row = hunk.buffer_range.start; let offset = gutter_layout.line_height / 2.; let start_y = @@ -571,8 +570,8 @@ impl EditorElement { } }; - let start_row = layout_range.start; - let end_row = layout_range.end; + let start_row = hunk.buffer_range.start; + let end_row = hunk.buffer_range.end; let start_y = start_row as f32 * gutter_layout.line_height - gutter_layout.scroll_top; let end_y = end_row as f32 * gutter_layout.line_height - gutter_layout.scroll_top; @@ -590,19 +589,18 @@ impl EditorElement { } } + let scroll_position = layout.position_map.snapshot.scroll_position(); let gutter_layout = { - let scroll_position = layout.position_map.snapshot.scroll_position(); let line_height = layout.position_map.line_height; GutterLayout { scroll_top: scroll_position.y() * line_height, - // scroll_position, line_height, bounds, } }; let mut diff_layout = DiffLayout { - buffer_line: 0, + buffer_row: scroll_position.y() as u32, last_diff: None, }; @@ -629,49 +627,35 @@ impl EditorElement { if show_gutter { //This line starts a buffer line, so let's do the diff calculation - let new_hunk = get_hunk(diff_layout.buffer_line, &layout.diff_hunks); + let new_hunk = get_hunk(diff_layout.buffer_row, &layout.diff_hunks); - // This + the unwraps are annoying, but at least it's legible let (is_ending, is_starting) = match (diff_layout.last_diff, new_hunk) { - (None, None) => (false, false), - (None, Some(_)) => (false, true), - (Some(_), None) => (true, false), - (Some((old_hunk, _)), Some(new_hunk)) if new_hunk == old_hunk => { + (Some(old_hunk), Some(new_hunk)) if new_hunk == old_hunk => { (false, false) } - (Some(_), Some(_)) => (true, true), + (a, b) => (a.is_some(), b.is_some()), }; if is_ending { - let (last_hunk, start_line) = diff_layout.last_diff.take().unwrap(); - cx.scene.push_quad(diff_quad( - last_hunk.status(), - start_line..ix, - &gutter_layout, - diff_style, - )); + let last_hunk = diff_layout.last_diff.take().unwrap(); + cx.scene + .push_quad(diff_quad(last_hunk, &gutter_layout, diff_style)); } if is_starting { let new_hunk = new_hunk.unwrap(); - - diff_layout.last_diff = Some((new_hunk, ix)); + diff_layout.last_diff = Some(new_hunk); }; - diff_layout.buffer_line += 1; + diff_layout.buffer_row += 1; } } } - // If we ran out with a diff hunk still being prepped, paint it now - if let Some((last_hunk, start_line)) = diff_layout.last_diff { - let end_line = layout.line_number_layouts.len(); - cx.scene.push_quad(diff_quad( - last_hunk.status(), - start_line..end_line, - &gutter_layout, - diff_style, - )) + // If we ran out with a diff hunk still being prepped, paint it now + if let Some(last_hunk) = diff_layout.last_diff { + cx.scene + .push_quad(diff_quad(last_hunk, &gutter_layout, diff_style)) } if let Some((row, indicator)) = layout.code_actions_indicator.as_mut() { @@ -1385,14 +1369,13 @@ impl EditorElement { /// Get the hunk that contains buffer_line, starting from start_idx /// Returns none if there is none found, and -fn get_hunk(buffer_line: usize, hunks: &[DiffHunk]) -> Option<&DiffHunk> { +fn get_hunk(buffer_line: u32, hunks: &[DiffHunk]) -> Option<&DiffHunk> { for i in 0..hunks.len() { // Safety: Index out of bounds is handled by the check above let hunk = hunks.get(i).unwrap(); if hunk.buffer_range.contains(&(buffer_line as u32)) { return Some(hunk); - } else if hunk.status() == DiffHunkStatus::Removed - && buffer_line == hunk.buffer_range.start as usize + } else if hunk.status() == DiffHunkStatus::Removed && buffer_line == hunk.buffer_range.start { return Some(hunk); } else if hunk.buffer_range.start > buffer_line as u32 { From d9fb8c90d8e3cec81d7915a5db37c00b5d5a6cdb Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 4 Oct 2022 17:27:03 -0700 Subject: [PATCH 143/314] Start work on toggling block comments for HTML --- crates/editor/src/editor.rs | 308 ++++++++++++++++------ crates/language/src/language.rs | 17 +- crates/zed/src/languages/html/config.toml | 2 + 3 files changed, 240 insertions(+), 87 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 93a47cf621..b2420c1c44 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -4487,105 +4487,184 @@ impl Editor { pub fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext) { self.transact(cx, |this, cx| { let mut selections = this.selections.all::(cx); - let mut all_selection_lines_are_comments = true; - let mut edit_ranges = Vec::new(); + let mut edits = Vec::new(); + let mut selection_edit_ranges = Vec::new(); let mut last_toggled_row = None; - this.buffer.update(cx, |buffer, cx| { - // TODO: Handle selections that cross excerpts - for selection in &mut selections { - // Get the line comment prefix. Split its trailing whitespace into a separate string, - // as that portion won't be used for detecting if a line is a comment. - let full_comment_prefix: Arc = if let Some(prefix) = buffer - .language_at(selection.start, cx) - .and_then(|l| l.line_comment_prefix().map(|p| p.into())) - { - prefix + let snapshot = this.buffer.read(cx).read(cx); + let empty_str: Arc = "".into(); + + fn comment_prefix_range( + snapshot: &MultiBufferSnapshot, + row: u32, + comment_prefix: &str, + comment_prefix_whitespace: &str, + ) -> Range { + let start = Point::new(row, snapshot.indent_size_for_line(row).len); + + let mut line_bytes = snapshot + .bytes_in_range(start..snapshot.max_point()) + .flatten() + .copied(); + + // If this line currently begins with the line comment prefix, then record + // the range containing the prefix. + if line_bytes + .by_ref() + .take(comment_prefix.len()) + .eq(comment_prefix.bytes()) + { + // Include any whitespace that matches the comment prefix. + let matching_whitespace_len = line_bytes + .zip(comment_prefix_whitespace.bytes()) + .take_while(|(a, b)| a == b) + .count() as u32; + let end = Point::new( + start.row, + start.column + comment_prefix.len() as u32 + matching_whitespace_len, + ); + start..end + } else { + start..start + } + } + + fn comment_suffix_range( + snapshot: &MultiBufferSnapshot, + row: u32, + comment_suffix: &str, + comment_suffix_has_leading_space: bool, + ) -> Range { + let end = Point::new(row, snapshot.line_len(row)); + let suffix_start_column = end.column.saturating_sub(comment_suffix.len() as u32); + + let mut line_end_bytes = snapshot + .bytes_in_range(Point::new(end.row, suffix_start_column.saturating_sub(1))..end) + .flatten() + .copied(); + + let leading_space_len = if suffix_start_column > 0 + && line_end_bytes.next() == Some(b' ') + && comment_suffix_has_leading_space + { + 1 + } else { + 0 + }; + + // If this line currently begins with the line comment prefix, then record + // the range containing the prefix. + if line_end_bytes.by_ref().eq(comment_suffix.bytes()) { + let start = Point::new(end.row, suffix_start_column - leading_space_len); + start..end + } else { + end..end + } + } + + // TODO: Handle selections that cross excerpts + for selection in &mut selections { + let language = if let Some(language) = snapshot.language_at(selection.start) { + language + } else { + continue; + }; + + let mut all_selection_lines_are_comments = true; + selection_edit_ranges.clear(); + + // If multiple selections contain a given row, avoid processing that + // row more than once. + let mut start_row = selection.start.row; + if last_toggled_row == Some(start_row) { + start_row += 1; + } + let end_row = + if selection.end.row > selection.start.row && selection.end.column == 0 { + selection.end.row - 1 } else { - return; + selection.end.row }; + last_toggled_row = Some(end_row); + + // If the language has line comments, toggle those. + if let Some(full_comment_prefix) = language.line_comment_prefix() { + // Split the comment prefix's trailing whitespace into a separate string, + // as that portion won't be used for detecting if a line is a comment. let comment_prefix = full_comment_prefix.trim_end_matches(' '); let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..]; - edit_ranges.clear(); - let snapshot = buffer.snapshot(cx); - - let end_row = - if selection.end.row > selection.start.row && selection.end.column == 0 { - selection.end.row - } else { - selection.end.row + 1 - }; - - for row in selection.start.row..end_row { - // If multiple selections contain a given row, avoid processing that - // row more than once. - if last_toggled_row == Some(row) { - continue; - } else { - last_toggled_row = Some(row); - } + for row in start_row..=end_row { if snapshot.is_line_blank(row) { continue; } - let start = Point::new(row, snapshot.indent_size_for_line(row).len); - let mut line_bytes = snapshot - .bytes_in_range(start..snapshot.max_point()) - .flatten() - .copied(); - - // If this line currently begins with the line comment prefix, then record - // the range containing the prefix. - if all_selection_lines_are_comments - && line_bytes - .by_ref() - .take(comment_prefix.len()) - .eq(comment_prefix.bytes()) - { - // Include any whitespace that matches the comment prefix. - let matching_whitespace_len = line_bytes - .zip(comment_prefix_whitespace.bytes()) - .take_while(|(a, b)| a == b) - .count() - as u32; - let end = Point::new( - row, - start.column - + comment_prefix.len() as u32 - + matching_whitespace_len, - ); - edit_ranges.push(start..end); - } - // If this line does not begin with the line comment prefix, then record - // the position where the prefix should be inserted. - else { + let prefix_range = comment_prefix_range( + snapshot.deref(), + row, + comment_prefix, + comment_prefix_whitespace, + ); + if prefix_range.is_empty() { all_selection_lines_are_comments = false; - edit_ranges.push(start..start); } + selection_edit_ranges.push(prefix_range); } - if !edit_ranges.is_empty() { - if all_selection_lines_are_comments { - let empty_str: Arc = "".into(); - buffer.edit( - edit_ranges - .iter() - .cloned() - .map(|range| (range, empty_str.clone())), - None, - cx, - ); - } else { - let min_column = - edit_ranges.iter().map(|r| r.start.column).min().unwrap(); - let edits = edit_ranges.iter().map(|range| { - let position = Point::new(range.start.row, min_column); - (position..position, full_comment_prefix.clone()) - }); - buffer.edit(edits, None, cx); - } + if all_selection_lines_are_comments { + edits.extend( + selection_edit_ranges + .iter() + .cloned() + .map(|range| (range, empty_str.clone())), + ); + } else { + let min_column = selection_edit_ranges + .iter() + .map(|r| r.start.column) + .min() + .unwrap_or(0); + edits.extend(selection_edit_ranges.iter().map(|range| { + let position = Point::new(range.start.row, min_column); + (position..position, full_comment_prefix.clone()) + })); } + } else if let Some((full_comment_prefix, comment_suffix)) = + language.block_comment_delimiters() + { + let comment_prefix = full_comment_prefix.trim_end_matches(' '); + let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..]; + + let prefix_range = comment_prefix_range( + snapshot.deref(), + start_row, + comment_prefix, + comment_prefix_whitespace, + ); + let suffix_range = comment_suffix_range( + snapshot.deref(), + end_row, + comment_suffix.trim_start_matches(' '), + comment_suffix.starts_with(' '), + ); + + if prefix_range.is_empty() || suffix_range.is_empty() { + edits.push(( + prefix_range.start..prefix_range.start, + full_comment_prefix.clone(), + )); + edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone())); + } else { + edits.push((prefix_range, empty_str.clone())); + edits.push((suffix_range, empty_str.clone())); + } + } else { + continue; } + } + + drop(snapshot); + this.buffer.update(cx, |buffer, cx| { + buffer.edit(edits, None, cx); }); let selections = this.selections.all::(cx); @@ -10777,7 +10856,7 @@ mod tests { cx.update(|cx| cx.set_global(Settings::test(cx))); let language = Arc::new(Language::new( LanguageConfig { - line_comment: Some("// ".to_string()), + line_comment: Some("// ".into()), ..Default::default() }, Some(tree_sitter_rust::language()), @@ -10855,6 +10934,67 @@ mod tests { }); } + #[gpui::test] + async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) { + let mut cx = EditorTestContext::new(cx); + + let html_language = Arc::new( + Language::new( + LanguageConfig { + name: "HTML".into(), + block_comment: Some(("".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_javascript::language()), + )); + + 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#" +

A

ˇ +

B

ˇ +

C

ˇ + "# + .unindent(), + ); + + cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); + cx.assert_editor_state( + &r#" + + + + "# + .unindent(), + ); + } + #[gpui::test] fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) { cx.set_global(Settings::test(cx)); diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 59da0909c6..c7c5def833 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -231,7 +231,10 @@ pub struct LanguageConfig { pub decrease_indent_pattern: Option, #[serde(default)] pub autoclose_before: String, - pub line_comment: Option, + #[serde(default)] + pub line_comment: Option>, + #[serde(default)] + pub block_comment: Option<(Arc, Arc)>, } impl Default for LanguageConfig { @@ -245,6 +248,7 @@ impl Default for LanguageConfig { decrease_indent_pattern: Default::default(), autoclose_before: Default::default(), line_comment: Default::default(), + block_comment: Default::default(), } } } @@ -768,8 +772,15 @@ impl Language { self.config.name.clone() } - pub fn line_comment_prefix(&self) -> Option<&str> { - self.config.line_comment.as_deref() + pub fn line_comment_prefix(&self) -> Option<&Arc> { + self.config.line_comment.as_ref() + } + + pub fn block_comment_delimiters(&self) -> Option<(&Arc, &Arc)> { + self.config + .block_comment + .as_ref() + .map(|(start, end)| (start, end)) } pub async fn disk_based_diagnostic_sources(&self) -> &[String] { diff --git a/crates/zed/src/languages/html/config.toml b/crates/zed/src/languages/html/config.toml index 80b33b1243..3e618da25e 100644 --- a/crates/zed/src/languages/html/config.toml +++ b/crates/zed/src/languages/html/config.toml @@ -8,3 +8,5 @@ brackets = [ { start = "\"", end = "\"", close = true, newline = false }, { start = "!--", end = " --", close = true, newline = false }, ] + +block_comment = [""] \ No newline at end of file From 087760dba0d9d5962a14dae2f4bcab33d85100bb Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 5 Oct 2022 10:51:51 +0200 Subject: [PATCH 144/314] Use AppContext instead of MutableAppContext for ActiveCall::global --- crates/call/src/call.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index a861f94bd0..4a662e42e0 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -3,7 +3,7 @@ pub mod room; use anyhow::{anyhow, Result}; use client::{incoming_call::IncomingCall, Client, UserStore}; -use gpui::{Entity, ModelContext, ModelHandle, MutableAppContext, Subscription, Task}; +use gpui::{AppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Subscription, Task}; pub use participant::ParticipantLocation; use project::Project; pub use room::Room; @@ -33,7 +33,7 @@ impl ActiveCall { } } - pub fn global(cx: &mut MutableAppContext) -> ModelHandle { + pub fn global(cx: &AppContext) -> ModelHandle { cx.global::>().clone() } From 84eebbe24a415d45bd3e9d2d035a800e2f5d3c57 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 5 Oct 2022 11:01:28 +0200 Subject: [PATCH 145/314] Always open project when added to a call via the `+` button --- crates/collab_ui/src/contacts_popover.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index c4eef3c9d8..389fe9fbd2 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -482,11 +482,7 @@ impl ContactsPopover { ) -> ElementBox { let online = contact.online; let user_id = contact.user.id; - let initial_project = if ActiveCall::global(cx).read(cx).room().is_none() { - Some(project.clone()) - } else { - None - }; + let initial_project = project.clone(); let mut element = MouseEventHandler::::new(contact.user.id as usize, cx, |_, _| { Flex::row() @@ -519,7 +515,7 @@ impl ContactsPopover { if online { cx.dispatch_action(Call { recipient_user_id: user_id, - initial_project: initial_project.clone(), + initial_project: Some(initial_project.clone()), }); } }); From 78e3370c1e701a8acbec8d8a06181002afc3ef6f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 5 Oct 2022 11:19:44 +0200 Subject: [PATCH 146/314] Set room only after project has been shared to avoid flicker --- crates/call/src/call.rs | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 4a662e42e0..2f64115fb5 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -50,9 +50,7 @@ impl ActiveCall { let room = if let Some(room) = room { room } else { - let room = cx.update(|cx| Room::create(client, user_store, cx)).await?; - this.update(&mut cx, |this, cx| this.set_room(Some(room.clone()), cx)); - room + cx.update(|cx| Room::create(client, user_store, cx)).await? }; let initial_project_id = if let Some(initial_project) = initial_project { @@ -65,6 +63,8 @@ impl ActiveCall { } else { None }; + + this.update(&mut cx, |this, cx| this.set_room(Some(room.clone()), cx)); room.update(&mut cx, |room, cx| { room.call(recipient_user_id, initial_project_id, cx) }) @@ -88,16 +88,18 @@ impl ActiveCall { } fn set_room(&mut self, room: Option>, cx: &mut ModelContext) { - if let Some(room) = room { - let subscriptions = vec![ - cx.observe(&room, |_, _, cx| cx.notify()), - cx.subscribe(&room, |_, _, event, cx| cx.emit(event.clone())), - ]; - self.room = Some((room, subscriptions)); - } else { - self.room = None; + if room.as_ref() != self.room.as_ref().map(|room| &room.0) { + if let Some(room) = room { + let subscriptions = vec![ + cx.observe(&room, |_, _, cx| cx.notify()), + cx.subscribe(&room, |_, _, event, cx| cx.emit(event.clone())), + ]; + self.room = Some((room, subscriptions)); + } else { + self.room = None; + } + cx.notify(); } - cx.notify(); } pub fn room(&self) -> Option<&ModelHandle> { From 383c21046f1c1e924cae5db33650b0fbe5b0b24e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 5 Oct 2022 14:27:59 +0200 Subject: [PATCH 147/314] Set room location when active workspace changes --- Cargo.lock | 1 + crates/collab_ui/src/collab_titlebar_item.rs | 41 +++++++++-- crates/theme/src/theme.rs | 1 + crates/workspace/Cargo.toml | 9 ++- crates/workspace/src/pane_group.rs | 74 ++++++++++++++++---- crates/workspace/src/workspace.rs | 9 ++- styles/src/styleTree/workspace.ts | 4 ++ 7 files changed, 119 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0211f4d1e1..2e70000673 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7084,6 +7084,7 @@ name = "workspace" version = "0.1.0" dependencies = [ "anyhow", + "call", "client", "collections", "context_menu", diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 8e4b8f9f32..cea3654856 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -76,6 +76,10 @@ impl CollabTitlebarItem { let mut subscriptions = Vec::new(); subscriptions.push(cx.observe(workspace, |_, _, cx| cx.notify())); subscriptions.push(cx.observe(&active_call, |_, _, cx| cx.notify())); + subscriptions.push(cx.observe_window_activation(|this, active, cx| { + this.window_activation_changed(active, cx) + })); + Self { workspace: workspace.downgrade(), contacts_popover: None, @@ -83,14 +87,43 @@ impl CollabTitlebarItem { } } + fn window_activation_changed(&mut self, active: bool, cx: &mut ViewContext) { + let workspace = self.workspace.upgrade(cx); + let room = ActiveCall::global(cx).read(cx).room().cloned(); + if let Some((workspace, room)) = workspace.zip(room) { + let workspace = workspace.read(cx); + let project = if !active { + None + } else if workspace.project().read(cx).remote_id().is_some() { + Some(workspace.project().clone()) + } else { + None + }; + + room.update(cx, |room, cx| { + room.set_location(project.as_ref(), cx) + .detach_and_log_err(cx); + }); + } + } + fn share_project(&mut self, _: &ShareProject, cx: &mut ViewContext) { if let Some(workspace) = self.workspace.upgrade(cx) { - if let Some(room) = ActiveCall::global(cx).read(cx).room() { + if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { + let window_id = cx.window_id(); let room_id = room.read(cx).id(); let project = workspace.read(cx).project().clone(); - project - .update(cx, |project, cx| project.share(room_id, cx)) - .detach_and_log_err(cx); + let share = project.update(cx, |project, cx| project.share(room_id, cx)); + cx.spawn_weak(|_, mut cx| async move { + share.await?; + if cx.update(|cx| cx.window_is_active(window_id)) { + room.update(&mut cx, |room, cx| { + room.set_location(Some(&project), cx).detach_and_log_err(cx); + }); + } + anyhow::Ok(()) + }) + .detach_and_log_err(cx); } } } diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 6253b6dabb..bc7e6f0995 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -58,6 +58,7 @@ pub struct Workspace { pub notifications: Notifications, pub joining_project_avatar: ImageStyle, pub joining_project_message: ContainedText, + pub external_location_message: ContainedText, pub dock: Dock, } diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index 759bff2cbd..2fd43b7bcb 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -8,9 +8,15 @@ path = "src/workspace.rs" doctest = false [features] -test-support = ["client/test-support", "project/test-support", "settings/test-support"] +test-support = [ + "call/test-support", + "client/test-support", + "project/test-support", + "settings/test-support" +] [dependencies] +call = { path = "../call" } client = { path = "../client" } collections = { path = "../collections" } context_menu = { path = "../context_menu" } @@ -32,6 +38,7 @@ serde_json = { version = "1.0", features = ["preserve_order"] } smallvec = { version = "1.6", features = ["union"] } [dev-dependencies] +call = { path = "../call", features = ["test-support"] } client = { path = "../client", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index 94acf427e4..fb04c4ead6 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -1,8 +1,9 @@ use crate::{FollowerStatesByLeader, Pane}; use anyhow::{anyhow, Result}; +use call::ActiveCall; use client::PeerId; use collections::HashMap; -use gpui::{elements::*, Axis, Border, ViewHandle}; +use gpui::{elements::*, AppContext, Axis, Border, ViewHandle}; use project::Collaborator; use serde::Deserialize; use theme::Theme; @@ -56,11 +57,14 @@ impl PaneGroup { pub(crate) fn render( &self, + project_id: Option, theme: &Theme, follower_states: &FollowerStatesByLeader, collaborators: &HashMap, + cx: &AppContext, ) -> ElementBox { - self.root.render(theme, follower_states, collaborators) + self.root + .render(project_id, theme, follower_states, collaborators, cx) } pub(crate) fn panes(&self) -> Vec<&ViewHandle> { @@ -100,13 +104,14 @@ impl Member { pub fn render( &self, + project_id: Option, theme: &Theme, follower_states: &FollowerStatesByLeader, collaborators: &HashMap, + cx: &AppContext, ) -> ElementBox { match self { Member::Pane(pane) => { - let mut border = Border::default(); let leader = follower_states .iter() .find_map(|(leader_id, follower_states)| { @@ -116,21 +121,61 @@ impl Member { None } }) - .and_then(|leader_id| collaborators.get(leader_id)); - if let Some(leader) = leader { - let leader_color = theme - .editor - .replica_selection_style(leader.replica_id) - .cursor; - border = Border::all(theme.workspace.leader_border_width, leader_color); + .and_then(|leader_id| { + let room = ActiveCall::global(cx).read(cx).room()?.read(cx); + let collaborator = collaborators.get(leader_id)?; + let participant = room.remote_participants().get(&leader_id)?; + Some((collaborator.replica_id, participant)) + }); + + if let Some((replica_id, leader)) = leader { + let view = match leader.location { + call::ParticipantLocation::Project { + project_id: leader_project_id, + } => { + if Some(leader_project_id) == project_id { + ChildView::new(pane).boxed() + } else { + Label::new( + format!( + "Follow {} on their currently active project", + leader.user.github_login, + ), + theme.workspace.external_location_message.text.clone(), + ) + .contained() + .with_style(theme.workspace.external_location_message.container) + .aligned() + .boxed() + } + } + call::ParticipantLocation::External => Label::new( + format!( + "{} is viewing a window outside of Zed", + leader.user.github_login + ), + theme.workspace.external_location_message.text.clone(), + ) + .contained() + .with_style(theme.workspace.external_location_message.container) + .aligned() + .boxed(), + }; + + let leader_color = theme.editor.replica_selection_style(replica_id).cursor; + let mut border = Border::all(theme.workspace.leader_border_width, leader_color); border .color .fade_out(1. - theme.workspace.leader_border_opacity); border.overlay = true; + Container::new(view).with_border(border).boxed() + } else { + ChildView::new(pane).boxed() } - ChildView::new(pane).contained().with_border(border).boxed() } - Member::Axis(axis) => axis.render(theme, follower_states, collaborators), + Member::Axis(axis) => { + axis.render(project_id, theme, follower_states, collaborators, cx) + } } } @@ -232,14 +277,17 @@ impl PaneAxis { fn render( &self, + project_id: Option, theme: &Theme, follower_state: &FollowerStatesByLeader, collaborators: &HashMap, + cx: &AppContext, ) -> ElementBox { let last_member_ix = self.members.len() - 1; Flex::new(self.axis) .with_children(self.members.iter().enumerate().map(|(ix, member)| { - let mut member = member.render(theme, follower_state, collaborators); + let mut member = + member.render(project_id, theme, follower_state, collaborators, cx); if ix < last_member_ix { let mut border = theme.workspace.pane_divider; border.left = false; diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 7aa93f47d9..5bec4b4c6d 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -12,7 +12,8 @@ mod status_bar; mod toolbar; use anyhow::{anyhow, Context, Result}; -use client::{proto, Client, Contact, PeerId, Subscription, TypedEnvelope, UserStore}; +use call::ActiveCall; +use client::{proto, Client, Contact, PeerId, TypedEnvelope, UserStore}; use collections::{hash_map, HashMap, HashSet}; use dock::{DefaultItemFactory, Dock, ToggleDockButton}; use drag_and_drop::DragAndDrop; @@ -860,7 +861,7 @@ pub struct Workspace { weak_self: WeakViewHandle, client: Arc, user_store: ModelHandle, - remote_entity_subscription: Option, + remote_entity_subscription: Option, fs: Arc, modal: Option, center: PaneGroup, @@ -880,6 +881,7 @@ pub struct Workspace { last_leaders_by_pane: HashMap, PeerId>, window_edited: bool, _observe_current_user: Task<()>, + _active_call_observation: gpui::Subscription, } #[derive(Default)] @@ -1015,6 +1017,7 @@ impl Workspace { last_leaders_by_pane: Default::default(), window_edited: false, _observe_current_user, + _active_call_observation: cx.observe(&ActiveCall::global(cx), |_, _, cx| cx.notify()), }; this.project_remote_id_changed(this.project.read(cx).remote_id(), cx); cx.defer(|this, cx| this.update_window_title(cx)); @@ -2430,9 +2433,11 @@ impl View for Workspace { Flex::column() .with_child( FlexItem::new(self.center.render( + self.project.read(cx).remote_id(), &theme, &self.follower_states_by_leader, self.project.read(cx).collaborators(), + cx, )) .flex(1., true) .boxed(), diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 40ed44e8db..0877f131c1 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -48,6 +48,10 @@ export default function workspace(theme: Theme) { padding: 12, ...text(theme, "sans", "primary", { size: "lg" }), }, + externalLocationMessage: { + padding: 12, + ...text(theme, "sans", "primary", { size: "lg" }), + }, leaderBorderOpacity: 0.7, leaderBorderWidth: 2.0, tabBar: tabBar(theme), From 8f8843711ff80bb3afed3f29ed22eff41df4a790 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 5 Oct 2022 15:02:57 +0200 Subject: [PATCH 148/314] Move logic for joining project into a global action in `collab_ui` --- crates/collab_ui/src/collab_ui.rs | 79 ++++++++++++++++++- .../src/incoming_call_notification.rs | 51 +++++------- .../src/project_shared_notification.rs | 52 ++++-------- crates/gpui/src/app.rs | 4 + crates/workspace/src/workspace.rs | 6 +- 5 files changed, 115 insertions(+), 77 deletions(-) diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 607c1b5054..786d344df1 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -3,14 +3,87 @@ mod contacts_popover; mod incoming_call_notification; mod project_shared_notification; +use call::ActiveCall; pub use collab_titlebar_item::CollabTitlebarItem; use gpui::MutableAppContext; +use project::Project; use std::sync::Arc; -use workspace::AppState; +use workspace::{AppState, JoinProject, ToggleFollow, Workspace}; pub fn init(app_state: Arc, cx: &mut MutableAppContext) { contacts_popover::init(cx); collab_titlebar_item::init(cx); - incoming_call_notification::init(app_state.clone(), cx); - project_shared_notification::init(app_state, cx); + incoming_call_notification::init(app_state.user_store.clone(), cx); + project_shared_notification::init(cx); + + cx.add_global_action(move |action: &JoinProject, cx| { + let project_id = action.project_id; + let follow_user_id = action.follow_user_id; + let app_state = app_state.clone(); + cx.spawn(|mut cx| async move { + let existing_workspace = cx.update(|cx| { + cx.window_ids() + .filter_map(|window_id| cx.root_view::(window_id)) + .find(|workspace| { + workspace.read(cx).project().read(cx).remote_id() == Some(project_id) + }) + }); + + let workspace = if let Some(existing_workspace) = existing_workspace { + existing_workspace + } else { + let project = Project::remote( + project_id, + app_state.client.clone(), + app_state.user_store.clone(), + app_state.project_store.clone(), + app_state.languages.clone(), + app_state.fs.clone(), + cx.clone(), + ) + .await?; + + let (_, workspace) = cx.add_window((app_state.build_window_options)(), |cx| { + let mut workspace = Workspace::new(project, app_state.default_item_factory, cx); + (app_state.initialize_workspace)(&mut workspace, &app_state, cx); + workspace + }); + workspace + }; + + cx.activate_window(workspace.window_id()); + + workspace.update(&mut cx, |workspace, cx| { + if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { + let follow_peer_id = room + .read(cx) + .remote_participants() + .iter() + .find(|(_, participant)| participant.user.id == follow_user_id) + .map(|(peer_id, _)| *peer_id) + .or_else(|| { + // If we couldn't follow the given user, follow the host instead. + let collaborator = workspace + .project() + .read(cx) + .collaborators() + .values() + .find(|collaborator| collaborator.replica_id == 0)?; + Some(collaborator.peer_id) + }); + + if let Some(follow_peer_id) = follow_peer_id { + if !workspace.is_following(follow_peer_id) { + workspace + .toggle_follow(&ToggleFollow(follow_peer_id), cx) + .map(|follow| follow.detach_and_log_err(cx)); + } + } + } + }); + + anyhow::Ok(()) + }) + .detach_and_log_err(cx); + }); } diff --git a/crates/collab_ui/src/incoming_call_notification.rs b/crates/collab_ui/src/incoming_call_notification.rs index 4630054c5e..e46e69522f 100644 --- a/crates/collab_ui/src/incoming_call_notification.rs +++ b/crates/collab_ui/src/incoming_call_notification.rs @@ -1,25 +1,22 @@ -use std::sync::Arc; - use call::ActiveCall; -use client::incoming_call::IncomingCall; +use client::{incoming_call::IncomingCall, UserStore}; use futures::StreamExt; use gpui::{ elements::*, geometry::{rect::RectF, vector::vec2f}, - impl_internal_actions, Entity, MouseButton, MutableAppContext, RenderContext, View, - ViewContext, WindowBounds, WindowKind, WindowOptions, + impl_internal_actions, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, + View, ViewContext, WindowBounds, WindowKind, WindowOptions, }; -use project::Project; use settings::Settings; use util::ResultExt; -use workspace::{AppState, Workspace}; +use workspace::JoinProject; impl_internal_actions!(incoming_call_notification, [RespondToCall]); -pub fn init(app_state: Arc, cx: &mut MutableAppContext) { +pub fn init(user_store: ModelHandle, cx: &mut MutableAppContext) { cx.add_action(IncomingCallNotification::respond_to_call); - let mut incoming_call = app_state.user_store.read(cx).incoming_call(); + let mut incoming_call = user_store.read(cx).incoming_call(); cx.spawn(|mut cx| async move { let mut notification_window = None; while let Some(incoming_call) = incoming_call.next().await { @@ -36,7 +33,7 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { kind: WindowKind::PopUp, is_movable: false, }, - |_| IncomingCallNotification::new(incoming_call, app_state.clone()), + |_| IncomingCallNotification::new(incoming_call, user_store.clone()), ); notification_window = Some(window_id); } @@ -52,47 +49,35 @@ struct RespondToCall { pub struct IncomingCallNotification { call: IncomingCall, - app_state: Arc, + user_store: ModelHandle, } impl IncomingCallNotification { - pub fn new(call: IncomingCall, app_state: Arc) -> Self { - Self { call, app_state } + pub fn new(call: IncomingCall, user_store: ModelHandle) -> Self { + Self { call, user_store } } fn respond_to_call(&mut self, action: &RespondToCall, cx: &mut ViewContext) { if action.accept { - let app_state = self.app_state.clone(); let join = ActiveCall::global(cx) .update(cx, |active_call, cx| active_call.join(&self.call, cx)); + let caller_user_id = self.call.caller.id; let initial_project_id = self.call.initial_project_id; cx.spawn_weak(|_, mut cx| async move { join.await?; - if let Some(initial_project_id) = initial_project_id { - let project = Project::remote( - initial_project_id, - app_state.client.clone(), - app_state.user_store.clone(), - app_state.project_store.clone(), - app_state.languages.clone(), - app_state.fs.clone(), - cx.clone(), - ) - .await?; - - cx.add_window((app_state.build_window_options)(), |cx| { - let mut workspace = - Workspace::new(project, app_state.default_item_factory, cx); - (app_state.initialize_workspace)(&mut workspace, &app_state, cx); - workspace + if let Some(project_id) = initial_project_id { + cx.update(|cx| { + cx.dispatch_global_action(JoinProject { + project_id, + follow_user_id: caller_user_id, + }) }); } anyhow::Ok(()) }) .detach_and_log_err(cx); } else { - self.app_state - .user_store + self.user_store .update(cx, |user_store, _| user_store.decline_call().log_err()); } diff --git a/crates/collab_ui/src/project_shared_notification.rs b/crates/collab_ui/src/project_shared_notification.rs index 53eb17684e..e0c1966144 100644 --- a/crates/collab_ui/src/project_shared_notification.rs +++ b/crates/collab_ui/src/project_shared_notification.rs @@ -7,14 +7,13 @@ use gpui::{ Entity, MouseButton, MutableAppContext, RenderContext, View, ViewContext, WindowBounds, WindowKind, WindowOptions, }; -use project::Project; use settings::Settings; use std::sync::Arc; -use workspace::{AppState, Workspace}; +use workspace::JoinProject; -actions!(project_shared_notification, [JoinProject, DismissProject]); +actions!(project_shared_notification, [DismissProject]); -pub fn init(app_state: Arc, cx: &mut MutableAppContext) { +pub fn init(cx: &mut MutableAppContext) { cx.add_action(ProjectSharedNotification::join); cx.add_action(ProjectSharedNotification::dismiss); @@ -29,7 +28,7 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { kind: WindowKind::PopUp, is_movable: false, }, - |_| ProjectSharedNotification::new(*project_id, owner.clone(), app_state.clone()), + |_| ProjectSharedNotification::new(*project_id, owner.clone()), ); } }) @@ -39,45 +38,17 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { pub struct ProjectSharedNotification { project_id: u64, owner: Arc, - app_state: Arc, } impl ProjectSharedNotification { - fn new(project_id: u64, owner: Arc, app_state: Arc) -> Self { - Self { - project_id, - owner, - app_state, - } + fn new(project_id: u64, owner: Arc) -> Self { + Self { project_id, owner } } fn join(&mut self, _: &JoinProject, cx: &mut ViewContext) { - let project_id = self.project_id; - let app_state = self.app_state.clone(); - cx.spawn_weak(|_, mut cx| async move { - let project = Project::remote( - project_id, - app_state.client.clone(), - app_state.user_store.clone(), - app_state.project_store.clone(), - app_state.languages.clone(), - app_state.fs.clone(), - cx.clone(), - ) - .await?; - - cx.add_window((app_state.build_window_options)(), |cx| { - let mut workspace = Workspace::new(project, app_state.default_item_factory, cx); - (app_state.initialize_workspace)(&mut workspace, &app_state, cx); - workspace - }); - - anyhow::Ok(()) - }) - .detach_and_log_err(cx); - let window_id = cx.window_id(); cx.remove_window(window_id); + cx.propagate_action(); } fn dismiss(&mut self, _: &DismissProject, cx: &mut ViewContext) { @@ -108,6 +79,8 @@ impl ProjectSharedNotification { enum Join {} enum Dismiss {} + let project_id = self.project_id; + let owner_user_id = self.owner.id; Flex::row() .with_child( MouseEventHandler::::new(0, cx, |_, cx| { @@ -117,8 +90,11 @@ impl ProjectSharedNotification { .with_style(theme.join_button.container) .boxed() }) - .on_click(MouseButton::Left, |_, cx| { - cx.dispatch_action(JoinProject); + .on_click(MouseButton::Left, move |_, cx| { + cx.dispatch_action(JoinProject { + project_id, + follow_user_id: owner_user_id, + }); }) .boxed(), ) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 04e27a8279..002bd01377 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -790,6 +790,10 @@ impl AsyncAppContext { self.update(|cx| cx.remove_window(window_id)) } + pub fn activate_window(&mut self, window_id: usize) { + self.update(|cx| cx.activate_window(window_id)) + } + pub fn platform(&self) -> Arc { self.0.borrow().platform() } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 5bec4b4c6d..1509c52887 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -13,7 +13,7 @@ mod toolbar; use anyhow::{anyhow, Context, Result}; use call::ActiveCall; -use client::{proto, Client, Contact, PeerId, TypedEnvelope, UserStore}; +use client::{proto, Client, PeerId, TypedEnvelope, UserStore}; use collections::{hash_map, HashMap, HashSet}; use dock::{DefaultItemFactory, Dock, ToggleDockButton}; use drag_and_drop::DragAndDrop; @@ -116,8 +116,8 @@ pub struct ToggleFollow(pub PeerId); #[derive(Clone, PartialEq)] pub struct JoinProject { - pub contact: Arc, - pub project_index: usize, + pub project_id: u64, + pub follow_user_id: u64, } impl_internal_actions!( From 183ca5da6f0931b05b52f213affc7e4221ca9e4e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 5 Oct 2022 15:32:55 +0200 Subject: [PATCH 149/314] Allow following users into external projects --- crates/workspace/src/pane_group.rs | 75 ++++++++++++++++++------------ crates/workspace/src/workspace.rs | 4 +- 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index fb04c4ead6..9fd27f78b5 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -1,10 +1,10 @@ -use crate::{FollowerStatesByLeader, Pane}; +use crate::{FollowerStatesByLeader, JoinProject, Pane, Workspace}; use anyhow::{anyhow, Result}; use call::ActiveCall; -use client::PeerId; -use collections::HashMap; -use gpui::{elements::*, AppContext, Axis, Border, ViewHandle}; -use project::Collaborator; +use gpui::{ + elements::*, Axis, Border, CursorStyle, ModelHandle, MouseButton, RenderContext, ViewHandle, +}; +use project::Project; use serde::Deserialize; use theme::Theme; @@ -57,14 +57,12 @@ impl PaneGroup { pub(crate) fn render( &self, - project_id: Option, + project: &ModelHandle, theme: &Theme, follower_states: &FollowerStatesByLeader, - collaborators: &HashMap, - cx: &AppContext, + cx: &mut RenderContext, ) -> ElementBox { - self.root - .render(project_id, theme, follower_states, collaborators, cx) + self.root.render(project, theme, follower_states, cx) } pub(crate) fn panes(&self) -> Vec<&ViewHandle> { @@ -104,12 +102,13 @@ impl Member { pub fn render( &self, - project_id: Option, + project: &ModelHandle, theme: &Theme, follower_states: &FollowerStatesByLeader, - collaborators: &HashMap, - cx: &AppContext, + cx: &mut RenderContext, ) -> ElementBox { + enum FollowIntoExternalProject {} + match self { Member::Pane(pane) => { let leader = follower_states @@ -123,7 +122,7 @@ impl Member { }) .and_then(|leader_id| { let room = ActiveCall::global(cx).read(cx).room()?.read(cx); - let collaborator = collaborators.get(leader_id)?; + let collaborator = project.read(cx).collaborators().get(leader_id)?; let participant = room.remote_participants().get(&leader_id)?; Some((collaborator.replica_id, participant)) }); @@ -133,18 +132,36 @@ impl Member { call::ParticipantLocation::Project { project_id: leader_project_id, } => { - if Some(leader_project_id) == project_id { + if Some(leader_project_id) == project.read(cx).remote_id() { ChildView::new(pane).boxed() } else { - Label::new( - format!( - "Follow {} on their currently active project", - leader.user.github_login, - ), - theme.workspace.external_location_message.text.clone(), + let leader_user = leader.user.clone(); + let leader_user_id = leader.user.id; + MouseEventHandler::::new( + pane.id(), + cx, + |_, _| { + Label::new( + format!( + "Follow {} on their currently active project", + leader_user.github_login, + ), + theme.workspace.external_location_message.text.clone(), + ) + .contained() + .with_style( + theme.workspace.external_location_message.container, + ) + .boxed() + }, ) - .contained() - .with_style(theme.workspace.external_location_message.container) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, cx| { + cx.dispatch_action(JoinProject { + project_id: leader_project_id, + follow_user_id: leader_user_id, + }) + }) .aligned() .boxed() } @@ -173,9 +190,7 @@ impl Member { ChildView::new(pane).boxed() } } - Member::Axis(axis) => { - axis.render(project_id, theme, follower_states, collaborators, cx) - } + Member::Axis(axis) => axis.render(project, theme, follower_states, cx), } } @@ -277,17 +292,15 @@ impl PaneAxis { fn render( &self, - project_id: Option, + project: &ModelHandle, theme: &Theme, follower_state: &FollowerStatesByLeader, - collaborators: &HashMap, - cx: &AppContext, + cx: &mut RenderContext, ) -> ElementBox { let last_member_ix = self.members.len() - 1; Flex::new(self.axis) .with_children(self.members.iter().enumerate().map(|(ix, member)| { - let mut member = - member.render(project_id, theme, follower_state, collaborators, cx); + let mut member = member.render(project, theme, follower_state, cx); if ix < last_member_ix { let mut border = theme.workspace.pane_divider; border.left = false; diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 1509c52887..00b237d1c4 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -2416,6 +2416,7 @@ impl View for Workspace { .with_child( Stack::new() .with_child({ + let project = self.project.clone(); Flex::row() .with_children( if self.left_sidebar.read(cx).active_item().is_some() { @@ -2433,10 +2434,9 @@ impl View for Workspace { Flex::column() .with_child( FlexItem::new(self.center.render( - self.project.read(cx).remote_id(), + &project, &theme, &self.follower_states_by_leader, - self.project.read(cx).collaborators(), cx, )) .flex(1., true) From 5b811e4304ae8824c2ace2511e6f02a17c492b40 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 5 Oct 2022 16:14:40 +0200 Subject: [PATCH 150/314] Add integration test verifying calls to busy users --- crates/collab/src/integration_tests.rs | 84 ++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 92f94b6621..7ab7126613 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -214,6 +214,90 @@ async fn test_basic_calls( ); } +#[gpui::test(iterations = 10)] +async fn test_calling_busy_user( + deterministic: Arc, + cx_a: &mut TestAppContext, + cx_b: &mut TestAppContext, + cx_c: &mut TestAppContext, +) { + deterministic.forbid_parking(); + let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; + let client_a = server.create_client(cx_a, "user_a").await; + let client_b = server.create_client(cx_b, "user_b").await; + let client_c = server.create_client(cx_c, "user_c").await; + server + .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) + .await; + + // Call user B from client A. + let room_a = cx_a + .update(|cx| Room::create(client_a.clone(), client_a.user_store.clone(), cx)) + .await + .unwrap(); + + let mut incoming_call_b = client_b + .user_store + .update(cx_b, |user, _| user.incoming_call()); + room_a + .update(cx_a, |room, cx| { + room.call(client_b.user_id().unwrap(), None, cx) + }) + .await + .unwrap(); + let call_b1 = incoming_call_b.next().await.unwrap().unwrap(); + assert_eq!(call_b1.caller.github_login, "user_a"); + + // Ensure calling users A and B from client C fails. + let room_c = cx_c + .update(|cx| Room::create(client_c.clone(), client_c.user_store.clone(), cx)) + .await + .unwrap(); + room_c + .update(cx_c, |room, cx| { + room.call(client_a.user_id().unwrap(), None, cx) + }) + .await + .unwrap_err(); + room_c + .update(cx_c, |room, cx| { + room.call(client_b.user_id().unwrap(), None, cx) + }) + .await + .unwrap_err(); + + // User B joins the room and calling them after they've joined still fails. + let room_b = cx_b + .update(|cx| { + Room::join( + &call_b1, + client_b.client.clone(), + client_b.user_store.clone(), + cx, + ) + }) + .await + .unwrap(); + room_c + .update(cx_c, |room, cx| { + room.call(client_b.user_id().unwrap(), None, cx) + }) + .await + .unwrap_err(); + + // Client C can successfully call client B after client B leaves the room. + cx_b.update(|_| drop(room_b)); + deterministic.run_until_parked(); + room_c + .update(cx_c, |room, cx| { + room.call(client_b.user_id().unwrap(), None, cx) + }) + .await + .unwrap(); + let call_b2 = incoming_call_b.next().await.unwrap().unwrap(); + assert_eq!(call_b2.caller.github_login, "user_c"); +} + #[gpui::test(iterations = 10)] async fn test_leaving_room_on_disconnection( deterministic: Arc, From 5ef342f8c442b19f7dc9205c98faef9494f08721 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 5 Oct 2022 16:20:01 +0200 Subject: [PATCH 151/314] Enhance integration test to verify creating rooms while busy --- crates/collab/src/integration_tests.rs | 28 ++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 7ab7126613..79d167d013 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -215,7 +215,7 @@ async fn test_basic_calls( } #[gpui::test(iterations = 10)] -async fn test_calling_busy_user( +async fn test_room_uniqueness( deterministic: Arc, cx_a: &mut TestAppContext, cx_b: &mut TestAppContext, @@ -230,12 +230,16 @@ async fn test_calling_busy_user( .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; - // Call user B from client A. let room_a = cx_a .update(|cx| Room::create(client_a.clone(), client_a.user_store.clone(), cx)) .await .unwrap(); + // Ensure room can't be created given we've just created one. + cx_a.update(|cx| Room::create(client_a.clone(), client_a.user_store.clone(), cx)) + .await + .unwrap_err(); + // Call user B from client A. let mut incoming_call_b = client_b .user_store .update(cx_b, |user, _| user.incoming_call()); @@ -266,6 +270,11 @@ async fn test_calling_busy_user( .await .unwrap_err(); + // Ensure User B can't create a room while they still have an incoming call. + cx_b.update(|cx| Room::create(client_b.clone(), client_b.user_store.clone(), cx)) + .await + .unwrap_err(); + // User B joins the room and calling them after they've joined still fails. let room_b = cx_b .update(|cx| { @@ -285,6 +294,11 @@ async fn test_calling_busy_user( .await .unwrap_err(); + // Ensure User B can't create a room while they belong to another room. + cx_b.update(|cx| Room::create(client_b.clone(), client_b.user_store.clone(), cx)) + .await + .unwrap_err(); + // Client C can successfully call client B after client B leaves the room. cx_b.update(|_| drop(room_b)); deterministic.run_until_parked(); @@ -296,6 +310,16 @@ async fn test_calling_busy_user( .unwrap(); let call_b2 = incoming_call_b.next().await.unwrap().unwrap(); assert_eq!(call_b2.caller.github_login, "user_c"); + + // Client B can successfully create a room after declining the call from client C. + client_b + .user_store + .update(cx_b, |user_store, _| user_store.decline_call()) + .unwrap(); + deterministic.run_until_parked(); + cx_b.update(|cx| Room::create(client_b.clone(), client_b.user_store.clone(), cx)) + .await + .unwrap(); } #[gpui::test(iterations = 10)] From fa31c9659bffd6beb8a8d3875d3a3e034dac2095 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 5 Oct 2022 16:29:22 +0200 Subject: [PATCH 152/314] Check room invariants in `Store::check_invariants` --- crates/collab/src/rpc/store.rs | 52 ++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index da9f242ac5..2f95851bf7 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -1030,6 +1030,45 @@ impl Store { *user_id ); } + + if let Some(active_call) = state.active_call.as_ref() { + if let Some(active_call_connection_id) = active_call.connection_id { + assert!( + state.connection_ids.contains(&active_call_connection_id), + "call is active on a dead connection" + ); + assert!( + state.connection_ids.contains(&active_call_connection_id), + "call is active on a dead connection" + ); + } + } + } + + for (room_id, room) in &self.rooms { + for pending_user_id in &room.pending_user_ids { + assert!( + self.connected_users + .contains_key(&UserId::from_proto(*pending_user_id)), + "call is active on a user that has disconnected" + ); + } + + for participant in &room.participants { + assert!( + self.connections + .contains_key(&ConnectionId(participant.peer_id)), + "room contains participant that has disconnected" + ); + + for project_id in &participant.project_ids { + let project = &self.projects[&ProjectId::from_proto(*project_id)]; + assert_eq!( + project.room_id, *room_id, + "project was shared on a different room" + ); + } + } } for (project_id, project) in &self.projects { @@ -1049,6 +1088,19 @@ impl Store { .map(|guest| guest.replica_id) .collect::>(), ); + + let room = &self.rooms[&project.room_id]; + let room_participant = room + .participants + .iter() + .find(|participant| participant.peer_id == project.host_connection_id.0) + .unwrap(); + assert!( + room_participant + .project_ids + .contains(&project_id.to_proto()), + "project was not shared in room" + ); } for (channel_id, channel) in &self.channels { From aa8680640863d6f4cfb4c82588b92e684af1af03 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 5 Oct 2022 12:25:32 -0700 Subject: [PATCH 153/314] Finish generalizing ToggleComments to support block comments --- crates/editor/src/editor.rs | 102 ++++++++++++++++++++++++++++-- crates/language/src/buffer.rs | 1 + crates/language/src/syntax_map.rs | 47 ++++++++------ 3 files changed, 126 insertions(+), 24 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index b2420c1c44..f7c9f81c0c 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -4492,6 +4492,7 @@ impl Editor { let mut last_toggled_row = None; let snapshot = this.buffer.read(cx).read(cx); let empty_str: Arc = "".into(); + let mut suffixes_inserted = Vec::new(); fn comment_prefix_range( snapshot: &MultiBufferSnapshot, @@ -4569,7 +4570,6 @@ impl Editor { continue; }; - let mut all_selection_lines_are_comments = true; selection_edit_ranges.clear(); // If multiple selections contain a given row, avoid processing that @@ -4586,12 +4586,17 @@ impl Editor { }; last_toggled_row = Some(end_row); + if start_row > end_row { + continue; + } + // If the language has line comments, toggle those. if let Some(full_comment_prefix) = language.line_comment_prefix() { // Split the comment prefix's trailing whitespace into a separate string, // as that portion won't be used for detecting if a line is a comment. let comment_prefix = full_comment_prefix.trim_end_matches(' '); let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..]; + let mut all_selection_lines_are_comments = true; for row in start_row..=end_row { if snapshot.is_line_blank(row) { @@ -4633,7 +4638,6 @@ impl Editor { { let comment_prefix = full_comment_prefix.trim_end_matches(' '); let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..]; - let prefix_range = comment_prefix_range( snapshot.deref(), start_row, @@ -4653,6 +4657,7 @@ impl Editor { full_comment_prefix.clone(), )); edits.push((suffix_range.end..suffix_range.end, comment_suffix.clone())); + suffixes_inserted.push((end_row, comment_suffix.len())); } else { edits.push((prefix_range, empty_str.clone())); edits.push((suffix_range, empty_str.clone())); @@ -4667,7 +4672,33 @@ impl Editor { buffer.edit(edits, None, cx); }); - let selections = this.selections.all::(cx); + // Adjust selections so that they end before any comment suffixes that + // were inserted. + let mut suffixes_inserted = suffixes_inserted.into_iter().peekable(); + let mut selections = this.selections.all::(cx); + let snapshot = this.buffer.read(cx).read(cx); + for selection in &mut selections { + while let Some((row, suffix_len)) = suffixes_inserted.peek().copied() { + match row.cmp(&selection.end.row) { + Ordering::Less => { + suffixes_inserted.next(); + continue; + } + Ordering::Greater => break, + Ordering::Equal => { + if selection.end.column == snapshot.line_len(row) { + if selection.is_empty() { + selection.start.column -= suffix_len as u32; + } + selection.end.column -= suffix_len as u32; + } + break; + } + } + } + } + + drop(snapshot); this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections)); }); } @@ -10975,6 +11006,7 @@ mod tests { buffer.set_language(Some(html_language), cx); }); + // Toggle comments for empty selections cx.set_state( &r#"

A

ˇ @@ -10983,7 +11015,6 @@ mod tests { "# .unindent(), ); - cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); cx.assert_editor_state( &r#" @@ -10993,6 +11024,69 @@ mod tests { "# .unindent(), ); + cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); + cx.assert_editor_state( + &r#" +

A

ˇ +

B

ˇ +

C

ˇ + "# + .unindent(), + ); + + // Toggle comments for mixture of empty and non-empty selections, where + // multiple selections occupy a given line. + cx.set_state( + &r#" +

+

ˇ»B

ˇ +

+

ˇ»D

ˇ + "# + .unindent(), + ); + + cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); + cx.assert_editor_state( + &r#" + + + "# + .unindent(), + ); + cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); + cx.assert_editor_state( + &r#" +

+

ˇ»B

ˇ +

+

ˇ»D

ˇ + "# + .unindent(), + ); + + // Toggle comments when different languages are active for different + // selections. + cx.set_state( + &r#" + ˇ + "# + .unindent(), + ); + cx.foreground().run_until_parked(); + cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); + cx.assert_editor_state( + &r#" + + // ˇvar x = new Y(); + + "# + .unindent(), + ); } #[gpui::test] diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 4ff1b002b0..b53a9e5573 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1840,6 +1840,7 @@ impl BufferSnapshot { let offset = position.to_offset(self); self.syntax .layers_for_range(offset..offset, &self.text) + .filter(|l| l.node.end_byte() > offset) .last() .map(|info| info.language) .or(self.language.as_ref()) diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index 8983406690..64145e535b 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -525,19 +525,19 @@ impl SyntaxSnapshot { } #[cfg(test)] - pub fn layers(&self, buffer: &BufferSnapshot) -> Vec { - self.layers_for_range(0..buffer.len(), buffer) + pub fn layers<'a>(&'a self, buffer: &'a BufferSnapshot) -> Vec { + self.layers_for_range(0..buffer.len(), buffer).collect() } pub fn layers_for_range<'a, T: ToOffset>( - &self, + &'a self, range: Range, - buffer: &BufferSnapshot, - ) -> Vec { + buffer: &'a BufferSnapshot, + ) -> impl 'a + Iterator { let start = buffer.anchor_before(range.start.to_offset(buffer)); let end = buffer.anchor_after(range.end.to_offset(buffer)); - let mut cursor = self.layers.filter::<_, ()>(|summary| { + let mut cursor = self.layers.filter::<_, ()>(move |summary| { if summary.max_depth > summary.min_depth { true } else { @@ -547,21 +547,26 @@ impl SyntaxSnapshot { } }); - let mut result = Vec::new(); + // let mut result = Vec::new(); cursor.next(buffer); - while let Some(layer) = cursor.item() { - result.push(SyntaxLayerInfo { - language: &layer.language, - depth: layer.depth, - node: layer.tree.root_node_with_offset( - layer.range.start.to_offset(buffer), - layer.range.start.to_point(buffer).to_ts_point(), - ), - }); - cursor.next(buffer) - } + std::iter::from_fn(move || { + if let Some(layer) = cursor.item() { + let info = SyntaxLayerInfo { + language: &layer.language, + depth: layer.depth, + node: layer.tree.root_node_with_offset( + layer.range.start.to_offset(buffer), + layer.range.start.to_point(buffer).to_ts_point(), + ), + }; + cursor.next(buffer); + Some(info) + } else { + None + } + }) - result + // result } } @@ -1848,7 +1853,9 @@ mod tests { range: Range, expected_layers: &[&str], ) { - let layers = syntax_map.layers_for_range(range, &buffer); + let layers = syntax_map + .layers_for_range(range, &buffer) + .collect::>(); assert_eq!( layers.len(), expected_layers.len(), From 3f4be5521c19ea51ff1f76fceb55af43fb58af7d Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 4 Oct 2022 20:42:01 -0400 Subject: [PATCH 154/314] Load diff base from correct relative path --- crates/project/src/project.rs | 7 ++++++- crates/project/src/worktree.rs | 16 +++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index dc783f1818..54fdb269be 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4673,13 +4673,18 @@ impl Project { None => return, }; + let relative_repo = match path.strip_prefix(repo.content_path) { + Ok(relative_repo) => relative_repo.to_owned(), + Err(_) => return, + }; + let shared_remote_id = self.shared_remote_id(); let client = self.client.clone(); cx.spawn(|_, mut cx| async move { let diff_base = cx .background() - .spawn(async move { repo.repo.lock().load_index(&path) }) + .spawn(async move { repo.repo.lock().load_index(&relative_repo) }) .await; let buffer_id = buffer.update(&mut cx, |buffer, cx| { diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 6880ec4ff1..5348f9785f 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -667,13 +667,15 @@ impl LocalWorktree { cx.spawn(|this, mut cx| async move { let text = fs.load(&abs_path).await?; - let diff_base = if let Some(repo) = snapshot.repo_for(&abs_path) { - cx.background() - .spawn({ - let path = path.clone(); - async move { repo.repo.lock().load_index(&path) } - }) - .await + let diff_base = if let Some(repo) = snapshot.repo_for(&path) { + if let Ok(repo_relative) = path.strip_prefix(repo.content_path) { + let repo_relative = repo_relative.to_owned(); + cx.background() + .spawn(async move { repo.repo.lock().load_index(&repo_relative) }) + .await + } else { + None + } } else { None }; From 8b86781ad13d3e5eee604fb93c15cf943bee8bf5 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 5 Oct 2022 14:44:34 -0700 Subject: [PATCH 155/314] Remove last usages of MultiBufferSnapshot::language --- crates/editor/src/editor.rs | 6 ++++-- crates/editor/src/multi_buffer.rs | 7 ------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index f7c9f81c0c..4e18d04889 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2021,7 +2021,7 @@ impl Editor { let end = selection.end; let mut insert_extra_newline = false; - if let Some(language) = buffer.language() { + if let Some(language) = buffer.language_at(start) { let leading_whitespace_len = buffer .reversed_chars_at(start) .take_while(|c| c.is_whitespace() && *c != '\n') @@ -2927,7 +2927,9 @@ impl Editor { { let indent_size = buffer.indent_size_for_line(line_buffer_range.start.row); - let language_name = buffer.language().map(|language| language.name()); + let language_name = buffer + .language_at(line_buffer_range.start) + .map(|language| language.name()); let indent_len = match indent_size.kind { IndentKind::Space => { cx.global::().tab_size(language_name.as_deref()) diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 3b43f99ca0..cf9f29d73e 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -2501,13 +2501,6 @@ impl MultiBufferSnapshot { self.trailing_excerpt_update_count } - pub fn language(&self) -> Option<&Arc> { - self.excerpts - .iter() - .next() - .and_then(|excerpt| excerpt.buffer.language()) - } - pub fn language_at<'a, T: ToOffset>(&'a self, point: T) -> Option<&'a Arc> { self.point_to_buffer_offset(point) .and_then(|(buffer, offset)| buffer.language_at(offset)) From 7fb5fe036a70e0018739ec1a251f8c1602c74724 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 5 Oct 2022 17:07:35 -0700 Subject: [PATCH 156/314] Derive indent size from the language at the cursor when auto-indenting --- crates/editor/src/multi_buffer.rs | 16 ++++++++-- crates/language/src/buffer.rs | 52 ++++++++++++++++++++++--------- 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index cf9f29d73e..71cbefb78f 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -1967,8 +1967,10 @@ impl MultiBufferSnapshot { let mut rows_for_excerpt = Vec::new(); let mut cursor = self.excerpts.cursor::(); - 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() { @@ -1976,7 +1978,17 @@ impl MultiBufferSnapshot { _ => continue, }; - let single_indent_size = excerpt.buffer.single_indent_size(cx); + // 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; diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index b53a9e5573..9a1c292319 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -84,14 +84,15 @@ pub struct BufferSnapshot { parse_count: usize, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] pub struct IndentSize { pub len: u32, pub kind: IndentKind, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] pub enum IndentKind { + #[default] Space, Tab, } @@ -236,7 +237,6 @@ pub enum AutoindentMode { struct AutoindentRequest { before_edit: BufferSnapshot, entries: Vec, - indent_size: IndentSize, is_block_mode: bool, } @@ -249,6 +249,7 @@ struct AutoindentRequestEntry { /// only be adjusted if the suggested indentation level has *changed* /// since the edit was made. first_line_is_new: bool, + indent_size: IndentSize, original_indent_column: Option, } @@ -794,10 +795,13 @@ impl Buffer { // buffer before this batch of edits. let mut row_ranges = Vec::new(); let mut old_to_new_rows = BTreeMap::new(); + let mut language_indent_sizes_by_new_row = Vec::new(); for entry in &request.entries { let position = entry.range.start; let new_row = position.to_point(&snapshot).row; let new_end_row = entry.range.end.to_point(&snapshot).row + 1; + language_indent_sizes_by_new_row.push((new_row, entry.indent_size)); + if !entry.first_line_is_new { let old_row = position.to_point(&request.before_edit).row; old_to_new_rows.insert(old_row, new_row); @@ -811,6 +815,8 @@ impl Buffer { let mut old_suggestions = BTreeMap::::default(); let old_edited_ranges = contiguous_ranges(old_to_new_rows.keys().copied(), max_rows_between_yields); + let mut language_indent_sizes = language_indent_sizes_by_new_row.iter().peekable(); + let mut language_indent_size = IndentSize::default(); for old_edited_range in old_edited_ranges { let suggestions = request .before_edit @@ -819,6 +825,17 @@ impl Buffer { .flatten(); for (old_row, suggestion) in old_edited_range.zip(suggestions) { if let Some(suggestion) = suggestion { + let new_row = *old_to_new_rows.get(&old_row).unwrap(); + + // Find the indent size based on the language for this row. + while let Some((row, size)) = language_indent_sizes.peek() { + if *row > new_row { + break; + } + language_indent_size = *size; + language_indent_sizes.next(); + } + let suggested_indent = old_to_new_rows .get(&suggestion.basis_row) .and_then(|from_row| old_suggestions.get(from_row).copied()) @@ -827,9 +844,8 @@ impl Buffer { .before_edit .indent_size_for_line(suggestion.basis_row) }) - .with_delta(suggestion.delta, request.indent_size); - old_suggestions - .insert(*old_to_new_rows.get(&old_row).unwrap(), suggested_indent); + .with_delta(suggestion.delta, language_indent_size); + old_suggestions.insert(new_row, suggested_indent); } } yield_now().await; @@ -850,6 +866,8 @@ impl Buffer { // Compute new suggestions for each line, but only include them in the result // if they differ from the old suggestion for that line. + let mut language_indent_sizes = language_indent_sizes_by_new_row.iter().peekable(); + let mut language_indent_size = IndentSize::default(); for new_edited_row_range in new_edited_row_ranges { let suggestions = snapshot .suggest_autoindents(new_edited_row_range.clone()) @@ -857,13 +875,22 @@ impl Buffer { .flatten(); for (new_row, suggestion) in new_edited_row_range.zip(suggestions) { if let Some(suggestion) = suggestion { + // Find the indent size based on the language for this row. + while let Some((row, size)) = language_indent_sizes.peek() { + if *row > new_row { + break; + } + language_indent_size = *size; + language_indent_sizes.next(); + } + let suggested_indent = indent_sizes .get(&suggestion.basis_row) .copied() .unwrap_or_else(|| { snapshot.indent_size_for_line(suggestion.basis_row) }) - .with_delta(suggestion.delta, request.indent_size); + .with_delta(suggestion.delta, language_indent_size); if old_suggestions .get(&new_row) .map_or(true, |old_indentation| { @@ -1194,7 +1221,6 @@ impl Buffer { let edit_id = edit_operation.local_timestamp(); if let Some((before_edit, mode)) = autoindent_request { - let indent_size = before_edit.single_indent_size(cx); let (start_columns, is_block_mode) = match mode { AutoindentMode::Block { original_indent_columns: start_columns, @@ -1243,6 +1269,7 @@ impl Buffer { AutoindentRequestEntry { first_line_is_new, original_indent_column: start_column, + indent_size: before_edit.language_indent_size_at(range.start, cx), range: self.anchor_before(new_start + range_of_insertion_to_indent.start) ..self.anchor_after(new_start + range_of_insertion_to_indent.end), } @@ -1252,7 +1279,6 @@ impl Buffer { self.autoindent_requests.push(Arc::new(AutoindentRequest { before_edit, entries, - indent_size, is_block_mode, })); } @@ -1570,8 +1596,8 @@ impl BufferSnapshot { indent_size_for_line(self, row) } - pub fn single_indent_size(&self, cx: &AppContext) -> IndentSize { - let language_name = self.language().map(|language| language.name()); + pub fn language_indent_size_at(&self, position: T, cx: &AppContext) -> IndentSize { + let language_name = self.language_at(position).map(|language| language.name()); let settings = cx.global::(); if settings.hard_tabs(language_name.as_deref()) { IndentSize::tab() @@ -1832,10 +1858,6 @@ impl BufferSnapshot { } } - pub fn language(&self) -> Option<&Arc> { - self.language.as_ref() - } - pub fn language_at(&self, position: D) -> Option<&Arc> { let offset = position.to_offset(self); self.syntax From b7e115a6a1baacd08a31c6d52586e0a7de1d64aa Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 5 Oct 2022 17:58:10 -0700 Subject: [PATCH 157/314] Add a test for multi-language auto-indent --- Cargo.lock | 2 + crates/language/Cargo.toml | 2 + crates/language/src/tests.rs | 116 ++++++++++++++++++++++++++++++++++- 3 files changed, 119 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 79eae80258..f7a354ed28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2861,6 +2861,8 @@ dependencies = [ "text", "theme", "tree-sitter", + "tree-sitter-html", + "tree-sitter-javascript", "tree-sitter-json 0.19.0", "tree-sitter-python", "tree-sitter-rust", diff --git a/crates/language/Cargo.toml b/crates/language/Cargo.toml index 6e9f368e77..6d80eee779 100644 --- a/crates/language/Cargo.toml +++ b/crates/language/Cargo.toml @@ -63,6 +63,8 @@ util = { path = "../util", features = ["test-support"] } ctor = "0.1" env_logger = "0.9" rand = "0.8.3" +tree-sitter-html = "*" +tree-sitter-javascript = "*" tree-sitter-json = "*" tree-sitter-rust = "*" tree-sitter-python = "*" diff --git a/crates/language/src/tests.rs b/crates/language/src/tests.rs index 8f56f3287e..3cfddce71f 100644 --- a/crates/language/src/tests.rs +++ b/crates/language/src/tests.rs @@ -14,7 +14,7 @@ use std::{ }; use text::network::Network; use unindent::Unindent as _; -use util::post_inc; +use util::{post_inc, test::marked_text_ranges}; #[cfg(test)] #[ctor::ctor] @@ -1035,6 +1035,120 @@ fn test_autoindent_language_without_indents_query(cx: &mut MutableAppContext) { }); } +#[gpui::test] +fn test_autoindent_with_injected_languages(cx: &mut MutableAppContext) { + cx.set_global({ + let mut settings = Settings::test(cx); + settings.language_overrides.extend([ + ( + "HTML".into(), + settings::EditorSettings { + tab_size: Some(2.try_into().unwrap()), + ..Default::default() + }, + ), + ( + "JavaScript".into(), + settings::EditorSettings { + tab_size: Some(8.try_into().unwrap()), + ..Default::default() + }, + ), + ]); + settings + }); + + let html_language = Arc::new( + Language::new( + LanguageConfig { + name: "HTML".into(), + ..Default::default() + }, + Some(tree_sitter_html::language()), + ) + .with_indents_query( + " + (element + (start_tag) @start + (end_tag)? @end) @indent + ", + ) + .unwrap() + .with_injection_query( + r#" + (script_element + (raw_text) @content + (#set! "language" "javascript")) + "#, + ) + .unwrap(), + ); + + let javascript_language = Arc::new( + Language::new( + LanguageConfig { + name: "JavaScript".into(), + ..Default::default() + }, + Some(tree_sitter_javascript::language()), + ) + .with_indents_query( + r#" + (object "}" @end) @indent + "#, + ) + .unwrap(), + ); + + let language_registry = Arc::new(LanguageRegistry::test()); + language_registry.add(html_language.clone()); + language_registry.add(javascript_language.clone()); + + cx.add_model(|cx| { + let (text, ranges) = marked_text_ranges( + &" +
ˇ +
+ + ˇ + + " + .unindent(), + false, + ); + + let mut buffer = Buffer::new(0, text, cx); + buffer.set_language_registry(language_registry); + buffer.set_language(Some(html_language), cx); + buffer.edit( + ranges.into_iter().map(|range| (range, "\na")), + Some(AutoindentMode::EachLine), + cx, + ); + assert_eq!( + buffer.text(), + " +
+ a +
+ + + a + + " + .unindent() + ); + buffer + }); +} + #[gpui::test] fn test_serialization(cx: &mut gpui::MutableAppContext) { let mut now = Instant::now(); From edf4c3ec00b91029334eff26b02be5927e0eab13 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Wed, 5 Oct 2022 21:22:53 -0400 Subject: [PATCH 158/314] Add Discord webhook for published releases (#1684) --- .github/workflows/discord_webhook.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/discord_webhook.yml diff --git a/.github/workflows/discord_webhook.yml b/.github/workflows/discord_webhook.yml new file mode 100644 index 0000000000..b71d451f5b --- /dev/null +++ b/.github/workflows/discord_webhook.yml @@ -0,0 +1,22 @@ +on: + release: + types: [published] + +jobs: + message: + runs-on: ubuntu-latest + steps: + - name: Discord Webhook Action + uses: tsickert/discord-webhook@v5.3.0 + with: + webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }} + content: | + 📣 Zed ${{ github.event.release.name }} was just released! + + Restart your Zed or head to https://zed.dev/releases to grab it. + + ```md + ### Changelog + + ${{ github.event.release.body }} + ``` \ No newline at end of file From 55cc142319a6c07997e33eccf4b3dd6887772288 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 6 Oct 2022 09:50:26 +0200 Subject: [PATCH 159/314] Move incoming calls into `ActiveCall` --- Cargo.lock | 1 + crates/call/Cargo.toml | 1 + crates/call/src/call.rs | 93 ++- crates/call/src/room.rs | 8 +- crates/client/src/user.rs | 58 -- crates/collab/src/integration_tests.rs | 535 ++++++++---------- crates/collab_ui/src/collab_ui.rs | 2 +- .../src/incoming_call_notification.rs | 29 +- crates/gpui/src/app.rs | 4 + crates/gpui/src/test.rs | 2 +- crates/gpui_macros/src/gpui_macros.rs | 2 +- 11 files changed, 362 insertions(+), 373 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e70000673..a971570e2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -693,6 +693,7 @@ dependencies = [ "collections", "futures", "gpui", + "postage", "project", "util", ] diff --git a/crates/call/Cargo.toml b/crates/call/Cargo.toml index cf5e7d6702..e725c7cfe3 100644 --- a/crates/call/Cargo.toml +++ b/crates/call/Cargo.toml @@ -25,6 +25,7 @@ util = { path = "../util" } anyhow = "1.0.38" futures = "0.3" +postage = { version = "0.4.1", features = ["futures-traits"] } [dev-dependencies] client = { path = "../client", features = ["test-support"] } diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 2f64115fb5..607931fdc4 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -2,22 +2,31 @@ mod participant; pub mod room; use anyhow::{anyhow, Result}; -use client::{incoming_call::IncomingCall, Client, UserStore}; -use gpui::{AppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Subscription, Task}; +use client::{incoming_call::IncomingCall, proto, Client, TypedEnvelope, UserStore}; +use gpui::{ + AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, + Subscription, Task, +}; pub use participant::ParticipantLocation; +use postage::watch; use project::Project; pub use room::Room; use std::sync::Arc; pub fn init(client: Arc, user_store: ModelHandle, cx: &mut MutableAppContext) { - let active_call = cx.add_model(|_| ActiveCall::new(client, user_store)); + let active_call = cx.add_model(|cx| ActiveCall::new(client, user_store, cx)); cx.set_global(active_call); } pub struct ActiveCall { room: Option<(ModelHandle, Vec)>, + incoming_call: ( + watch::Sender>, + watch::Receiver>, + ), client: Arc, user_store: ModelHandle, + _subscriptions: Vec, } impl Entity for ActiveCall { @@ -25,14 +34,63 @@ impl Entity for ActiveCall { } impl ActiveCall { - fn new(client: Arc, user_store: ModelHandle) -> Self { + fn new( + client: Arc, + user_store: ModelHandle, + cx: &mut ModelContext, + ) -> Self { Self { room: None, + incoming_call: watch::channel(), + _subscriptions: vec![ + client.add_request_handler(cx.handle(), Self::handle_incoming_call), + client.add_message_handler(cx.handle(), Self::handle_cancel_call), + ], client, user_store, } } + async fn handle_incoming_call( + this: ModelHandle, + envelope: TypedEnvelope, + _: Arc, + mut cx: AsyncAppContext, + ) -> Result { + let user_store = this.read_with(&cx, |this, _| this.user_store.clone()); + let call = IncomingCall { + room_id: envelope.payload.room_id, + participants: user_store + .update(&mut cx, |user_store, cx| { + user_store.get_users(envelope.payload.participant_user_ids, cx) + }) + .await?, + caller: user_store + .update(&mut cx, |user_store, cx| { + user_store.get_user(envelope.payload.caller_user_id, cx) + }) + .await?, + initial_project_id: envelope.payload.initial_project_id, + }; + this.update(&mut cx, |this, _| { + *this.incoming_call.0.borrow_mut() = Some(call); + }); + + Ok(proto::Ack {}) + } + + async fn handle_cancel_call( + this: ModelHandle, + _: TypedEnvelope, + _: Arc, + mut cx: AsyncAppContext, + ) -> Result<()> { + this.update(&mut cx, |this, _| { + *this.incoming_call.0.borrow_mut() = None; + }); + Ok(()) + } + pub fn global(cx: &AppContext) -> ModelHandle { cx.global::>().clone() } @@ -74,12 +132,22 @@ impl ActiveCall { }) } - pub fn join(&mut self, call: &IncomingCall, cx: &mut ModelContext) -> Task> { + pub fn incoming(&self) -> watch::Receiver> { + self.incoming_call.1.clone() + } + + pub fn accept_incoming(&mut self, cx: &mut ModelContext) -> Task> { if self.room.is_some() { return Task::ready(Err(anyhow!("cannot join while on another call"))); } - let join = Room::join(call, self.client.clone(), self.user_store.clone(), cx); + let call = if let Some(call) = self.incoming_call.1.borrow().clone() { + call + } else { + return Task::ready(Err(anyhow!("no incoming call"))); + }; + + let join = Room::join(&call, self.client.clone(), self.user_store.clone(), cx); cx.spawn(|this, mut cx| async move { let room = join.await?; this.update(&mut cx, |this, cx| this.set_room(Some(room.clone()), cx)); @@ -87,6 +155,19 @@ impl ActiveCall { }) } + pub fn decline_incoming(&mut self) -> Result<()> { + *self.incoming_call.0.borrow_mut() = None; + self.client.send(proto::DeclineCall {})?; + Ok(()) + } + + pub fn hang_up(&mut self, cx: &mut ModelContext) -> Result<()> { + if let Some((room, _)) = self.room.take() { + room.update(cx, |room, cx| room.leave(cx))?; + } + Ok(()) + } + fn set_room(&mut self, room: Option>, cx: &mut ModelContext) { if room.as_ref() != self.room.as_ref().map(|room| &room.0) { if let Some(room) = room { diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 0237972167..52f283dd03 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -66,7 +66,7 @@ impl Room { } } - pub fn create( + pub(crate) fn create( client: Arc, user_store: ModelHandle, cx: &mut MutableAppContext, @@ -77,7 +77,7 @@ impl Room { }) } - pub fn join( + pub(crate) fn join( call: &IncomingCall, client: Arc, user_store: ModelHandle, @@ -93,7 +93,7 @@ impl Room { }) } - pub fn leave(&mut self, cx: &mut ModelContext) -> Result<()> { + pub(crate) fn leave(&mut self, cx: &mut ModelContext) -> Result<()> { if self.status.is_offline() { return Err(anyhow!("room is offline")); } @@ -213,7 +213,7 @@ impl Room { Ok(()) } - pub fn call( + pub(crate) fn call( &mut self, recipient_user_id: u64, initial_project_id: Option, diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 2d79b7be84..252fb4d455 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -1,5 +1,4 @@ use super::{http::HttpClient, proto, Client, Status, TypedEnvelope}; -use crate::incoming_call::IncomingCall; use anyhow::{anyhow, Context, Result}; use collections::{hash_map::Entry, HashMap, HashSet}; use futures::{channel::mpsc, future, AsyncReadExt, Future, StreamExt}; @@ -59,10 +58,6 @@ pub struct UserStore { outgoing_contact_requests: Vec>, pending_contact_requests: HashMap, invite_info: Option, - incoming_call: ( - watch::Sender>, - watch::Receiver>, - ), client: Weak, http: Arc, _maintain_contacts: Task<()>, @@ -112,8 +107,6 @@ impl UserStore { client.add_message_handler(cx.handle(), Self::handle_update_contacts), client.add_message_handler(cx.handle(), Self::handle_update_invite_info), client.add_message_handler(cx.handle(), Self::handle_show_contacts), - client.add_request_handler(cx.handle(), Self::handle_incoming_call), - client.add_message_handler(cx.handle(), Self::handle_cancel_call), ]; Self { users: Default::default(), @@ -122,7 +115,6 @@ impl UserStore { incoming_contact_requests: Default::default(), outgoing_contact_requests: Default::default(), invite_info: None, - incoming_call: watch::channel(), client: Arc::downgrade(&client), update_contacts_tx, http, @@ -194,60 +186,10 @@ impl UserStore { Ok(()) } - async fn handle_incoming_call( - this: ModelHandle, - envelope: TypedEnvelope, - _: Arc, - mut cx: AsyncAppContext, - ) -> Result { - let call = IncomingCall { - room_id: envelope.payload.room_id, - participants: this - .update(&mut cx, |this, cx| { - this.get_users(envelope.payload.participant_user_ids, cx) - }) - .await?, - caller: this - .update(&mut cx, |this, cx| { - this.get_user(envelope.payload.caller_user_id, cx) - }) - .await?, - initial_project_id: envelope.payload.initial_project_id, - }; - this.update(&mut cx, |this, _| { - *this.incoming_call.0.borrow_mut() = Some(call); - }); - - Ok(proto::Ack {}) - } - - async fn handle_cancel_call( - this: ModelHandle, - _: TypedEnvelope, - _: Arc, - mut cx: AsyncAppContext, - ) -> Result<()> { - this.update(&mut cx, |this, _| { - *this.incoming_call.0.borrow_mut() = None; - }); - Ok(()) - } - pub fn invite_info(&self) -> Option<&InviteInfo> { self.invite_info.as_ref() } - pub fn incoming_call(&self) -> watch::Receiver> { - self.incoming_call.1.clone() - } - - pub fn decline_call(&mut self) -> Result<()> { - if let Some(client) = self.client.upgrade() { - client.send(proto::DeclineCall {})?; - } - Ok(()) - } - async fn handle_update_contacts( this: ModelHandle, message: TypedEnvelope, diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 79d167d013..1adac8b28e 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -5,7 +5,7 @@ use crate::{ }; use ::rpc::Peer; use anyhow::anyhow; -use call::{room, ParticipantLocation, Room}; +use call::{room, ActiveCall, ParticipantLocation, Room}; use client::{ self, test::FakeHttpClient, Channel, ChannelDetails, ChannelList, Client, Connection, Credentials, EstablishConnectionError, User, UserStore, RECEIVE_TIMEOUT, @@ -78,29 +78,18 @@ async fn test_basic_calls( .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; - let room_a = cx_a - .update(|cx| Room::create(client_a.clone(), client_a.user_store.clone(), cx)) - .await - .unwrap(); - assert_eq!( - room_participants(&room_a, cx_a), - RoomParticipants { - remote: Default::default(), - pending: Default::default() - } - ); + let active_call_a = cx_a.read(ActiveCall::global); + let active_call_b = cx_b.read(ActiveCall::global); + let active_call_c = cx_c.read(ActiveCall::global); // Call user B from client A. - let mut incoming_call_b = client_b - .user_store - .update(cx_b, |user, _| user.incoming_call()); - room_a - .update(cx_a, |room, cx| { - room.call(client_b.user_id().unwrap(), None, cx) + active_call_a + .update(cx_a, |call, cx| { + call.invite(client_b.user_id().unwrap(), None, cx) }) .await .unwrap(); - + let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone()); deterministic.run_until_parked(); assert_eq!( room_participants(&room_a, cx_a), @@ -111,21 +100,24 @@ async fn test_basic_calls( ); // User B receives the call. + let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming()); let call_b = incoming_call_b.next().await.unwrap().unwrap(); + assert_eq!(call_b.caller.github_login, "user_a"); // User B connects via another client and also receives a ring on the newly-connected client. - let client_b2 = server.create_client(cx_b2, "user_b").await; - let mut incoming_call_b2 = client_b2 - .user_store - .update(cx_b2, |user, _| user.incoming_call()); + let _client_b2 = server.create_client(cx_b2, "user_b").await; + let active_call_b2 = cx_b2.read(ActiveCall::global); + let mut incoming_call_b2 = active_call_b2.read_with(cx_b2, |call, _| call.incoming()); deterministic.run_until_parked(); - let _call_b2 = incoming_call_b2.next().await.unwrap().unwrap(); + let call_b2 = incoming_call_b2.next().await.unwrap().unwrap(); + assert_eq!(call_b2.caller.github_login, "user_a"); // User B joins the room using the first client. - let room_b = cx_b - .update(|cx| Room::join(&call_b, client_b.clone(), client_b.user_store.clone(), cx)) + active_call_b + .update(cx_b, |call, cx| call.accept_incoming(cx)) .await .unwrap(); + let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone()); assert!(incoming_call_b.next().await.unwrap().is_none()); deterministic.run_until_parked(); @@ -145,12 +137,10 @@ async fn test_basic_calls( ); // Call user C from client B. - let mut incoming_call_c = client_c - .user_store - .update(cx_c, |user, _| user.incoming_call()); - room_b - .update(cx_b, |room, cx| { - room.call(client_c.user_id().unwrap(), None, cx) + let mut incoming_call_c = active_call_c.read_with(cx_c, |call, _| call.incoming()); + active_call_b + .update(cx_b, |call, cx| { + call.invite(client_c.user_id().unwrap(), None, cx) }) .await .unwrap(); @@ -172,11 +162,9 @@ async fn test_basic_calls( ); // User C receives the call, but declines it. - let _call_c = incoming_call_c.next().await.unwrap().unwrap(); - client_c - .user_store - .update(cx_c, |user, _| user.decline_call()) - .unwrap(); + let call_c = incoming_call_c.next().await.unwrap().unwrap(); + assert_eq!(call_c.caller.github_login, "user_b"); + active_call_c.update(cx_c, |call, _| call.decline_incoming().unwrap()); assert!(incoming_call_c.next().await.unwrap().is_none()); deterministic.run_until_parked(); @@ -196,7 +184,10 @@ async fn test_basic_calls( ); // User A leaves the room. - room_a.update(cx_a, |room, cx| room.leave(cx)).unwrap(); + active_call_a.update(cx_a, |call, cx| { + call.hang_up(cx).unwrap(); + assert!(call.room().is_none()); + }); deterministic.run_until_parked(); assert_eq!( room_participants(&room_a, cx_a), @@ -218,108 +209,107 @@ async fn test_basic_calls( async fn test_room_uniqueness( deterministic: Arc, cx_a: &mut TestAppContext, + cx_a2: &mut TestAppContext, cx_b: &mut TestAppContext, + cx_b2: &mut TestAppContext, cx_c: &mut TestAppContext, ) { deterministic.forbid_parking(); let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; + let _client_a2 = server.create_client(cx_a2, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; + let _client_b2 = server.create_client(cx_b2, "user_b").await; let client_c = server.create_client(cx_c, "user_c").await; server .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; - let room_a = cx_a - .update(|cx| Room::create(client_a.clone(), client_a.user_store.clone(), cx)) - .await - .unwrap(); - // Ensure room can't be created given we've just created one. - cx_a.update(|cx| Room::create(client_a.clone(), client_a.user_store.clone(), cx)) - .await - .unwrap_err(); + let active_call_a = cx_a.read(ActiveCall::global); + let active_call_a2 = cx_a2.read(ActiveCall::global); + let active_call_b = cx_b.read(ActiveCall::global); + let active_call_b2 = cx_b2.read(ActiveCall::global); + let active_call_c = cx_c.read(ActiveCall::global); // Call user B from client A. - let mut incoming_call_b = client_b - .user_store - .update(cx_b, |user, _| user.incoming_call()); - room_a - .update(cx_a, |room, cx| { - room.call(client_b.user_id().unwrap(), None, cx) + active_call_a + .update(cx_a, |call, cx| { + call.invite(client_b.user_id().unwrap(), None, cx) }) .await .unwrap(); + + // Ensure a new room can't be created given user A just created one. + active_call_a2 + .update(cx_a2, |call, cx| { + call.invite(client_c.user_id().unwrap(), None, cx) + }) + .await + .unwrap_err(); + active_call_a2.read_with(cx_a2, |call, _| assert!(call.room().is_none())); + + // User B receives the call from user A. + let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming()); let call_b1 = incoming_call_b.next().await.unwrap().unwrap(); assert_eq!(call_b1.caller.github_login, "user_a"); // Ensure calling users A and B from client C fails. - let room_c = cx_c - .update(|cx| Room::create(client_c.clone(), client_c.user_store.clone(), cx)) - .await - .unwrap(); - room_c - .update(cx_c, |room, cx| { - room.call(client_a.user_id().unwrap(), None, cx) + active_call_c + .update(cx_c, |call, cx| { + call.invite(client_a.user_id().unwrap(), None, cx) }) .await .unwrap_err(); - room_c - .update(cx_c, |room, cx| { - room.call(client_b.user_id().unwrap(), None, cx) + active_call_c + .update(cx_c, |call, cx| { + call.invite(client_b.user_id().unwrap(), None, cx) }) .await .unwrap_err(); // Ensure User B can't create a room while they still have an incoming call. - cx_b.update(|cx| Room::create(client_b.clone(), client_b.user_store.clone(), cx)) - .await - .unwrap_err(); - - // User B joins the room and calling them after they've joined still fails. - let room_b = cx_b - .update(|cx| { - Room::join( - &call_b1, - client_b.client.clone(), - client_b.user_store.clone(), - cx, - ) + active_call_b2 + .update(cx_b2, |call, cx| { + call.invite(client_c.user_id().unwrap(), None, cx) }) .await + .unwrap_err(); + active_call_b2.read_with(cx_b2, |call, _| assert!(call.room().is_none())); + + // User B joins the room and calling them after they've joined still fails. + active_call_b + .update(cx_b, |call, cx| call.accept_incoming(cx)) + .await .unwrap(); - room_c - .update(cx_c, |room, cx| { - room.call(client_b.user_id().unwrap(), None, cx) + active_call_c + .update(cx_c, |call, cx| { + call.invite(client_b.user_id().unwrap(), None, cx) }) .await .unwrap_err(); // Ensure User B can't create a room while they belong to another room. - cx_b.update(|cx| Room::create(client_b.clone(), client_b.user_store.clone(), cx)) + active_call_b2 + .update(cx_b2, |call, cx| { + call.invite(client_c.user_id().unwrap(), None, cx) + }) .await .unwrap_err(); + active_call_b2.read_with(cx_b2, |call, _| assert!(call.room().is_none())); // Client C can successfully call client B after client B leaves the room. - cx_b.update(|_| drop(room_b)); + active_call_b + .update(cx_b, |call, cx| call.hang_up(cx)) + .unwrap(); deterministic.run_until_parked(); - room_c - .update(cx_c, |room, cx| { - room.call(client_b.user_id().unwrap(), None, cx) + active_call_c + .update(cx_c, |call, cx| { + call.invite(client_b.user_id().unwrap(), None, cx) }) .await .unwrap(); let call_b2 = incoming_call_b.next().await.unwrap().unwrap(); assert_eq!(call_b2.caller.github_login, "user_c"); - - // Client B can successfully create a room after declining the call from client C. - client_b - .user_store - .update(cx_b, |user_store, _| user_store.decline_call()) - .unwrap(); - deterministic.run_until_parked(); - cx_b.update(|cx| Room::create(client_b.clone(), client_b.user_store.clone(), cx)) - .await - .unwrap(); } #[gpui::test(iterations = 10)] @@ -336,28 +326,26 @@ async fn test_leaving_room_on_disconnection( .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; - let room_a = cx_a - .update(|cx| Room::create(client_a.clone(), client_a.user_store.clone(), cx)) - .await - .unwrap(); + let active_call_a = cx_a.read(ActiveCall::global); + let active_call_b = cx_b.read(ActiveCall::global); // Call user B from client A. - let mut incoming_call_b = client_b - .user_store - .update(cx_b, |user, _| user.incoming_call()); - room_a - .update(cx_a, |room, cx| { - room.call(client_b.user_id().unwrap(), None, cx) + active_call_a + .update(cx_a, |call, cx| { + call.invite(client_b.user_id().unwrap(), None, cx) }) .await .unwrap(); + let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone()); // User B receives the call and joins the room. - let call_b = incoming_call_b.next().await.unwrap().unwrap(); - let room_b = cx_b - .update(|cx| Room::join(&call_b, client_b.clone(), client_b.user_store.clone(), cx)) + let mut incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming()); + incoming_call_b.next().await.unwrap().unwrap(); + active_call_b + .update(cx_b, |call, cx| call.accept_incoming(cx)) .await .unwrap(); + let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone()); deterministic.run_until_parked(); assert_eq!( room_participants(&room_a, cx_a), @@ -398,13 +386,13 @@ async fn test_share_project( cx_a: &mut TestAppContext, cx_b: &mut TestAppContext, ) { - cx_a.foreground().forbid_parking(); + deterministic.forbid_parking(); let (_, window_b) = cx_b.add_window(|_| EmptyView); let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; client_a @@ -502,10 +490,13 @@ async fn test_unshare_project( let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; let client_c = server.create_client(cx_c, "user_c").await; - let (room_id, mut rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); + let active_call_b = cx_b.read(ActiveCall::global); + client_a .fs .insert_tree( @@ -532,7 +523,7 @@ async fn test_unshare_project( .unwrap(); // When client B leaves the room, the project becomes read-only. - cx_b.update(|_| drop(rooms.remove(1))); + active_call_b.update(cx_b, |call, cx| call.hang_up(cx).unwrap()); deterministic.run_until_parked(); assert!(project_b.read_with(cx_b, |project, _| project.is_read_only())); @@ -560,7 +551,7 @@ async fn test_unshare_project( .unwrap(); // When client A (the host) leaves the room, the project gets unshared and guests are notified. - cx_a.update(|_| drop(rooms.remove(0))); + active_call_a.update(cx_a, |call, cx| call.hang_up(cx).unwrap()); deterministic.run_until_parked(); project_a.read_with(cx_a, |project, _| assert!(!project.is_shared())); project_c2.read_with(cx_c, |project, _| { @@ -582,8 +573,8 @@ async fn test_host_disconnect( let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; let client_c = server.create_client(cx_c, "user_c").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; client_a @@ -660,7 +651,7 @@ async fn test_host_disconnect( } #[gpui::test(iterations = 10)] -async fn test_room_events( +async fn test_active_call_events( deterministic: Arc, cx_a: &mut TestAppContext, cx_b: &mut TestAppContext, @@ -675,24 +666,21 @@ async fn test_room_events( let (project_a, _) = client_a.build_local_project("/a", cx_a).await; let (project_b, _) = client_b.build_local_project("/b", cx_b).await; - let (room_id, mut rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; - let room_a = rooms.remove(0); - let room_a_events = room_events(&room_a, cx_a); - - let room_b = rooms.remove(0); - let room_b_events = room_events(&room_b, cx_b); + let events_a = active_call_events(cx_a); + let events_b = active_call_events(cx_b); let project_a_id = project_a .update(cx_a, |project, cx| project.share(room_id, cx)) .await .unwrap(); deterministic.run_until_parked(); - assert_eq!(mem::take(&mut *room_a_events.borrow_mut()), vec![]); + assert_eq!(mem::take(&mut *events_a.borrow_mut()), vec![]); assert_eq!( - mem::take(&mut *room_b_events.borrow_mut()), + mem::take(&mut *events_b.borrow_mut()), vec![room::Event::RemoteProjectShared { owner: Arc::new(User { id: client_a.user_id().unwrap(), @@ -709,7 +697,7 @@ async fn test_room_events( .unwrap(); deterministic.run_until_parked(); assert_eq!( - mem::take(&mut *room_a_events.borrow_mut()), + mem::take(&mut *events_a.borrow_mut()), vec![room::Event::RemoteProjectShared { owner: Arc::new(User { id: client_b.user_id().unwrap(), @@ -719,17 +707,15 @@ async fn test_room_events( project_id: project_b_id, }] ); - assert_eq!(mem::take(&mut *room_b_events.borrow_mut()), vec![]); + assert_eq!(mem::take(&mut *events_b.borrow_mut()), vec![]); - fn room_events( - room: &ModelHandle, - cx: &mut TestAppContext, - ) -> Rc>> { + fn active_call_events(cx: &mut TestAppContext) -> Rc>> { let events = Rc::new(RefCell::new(Vec::new())); + let active_call = cx.read(ActiveCall::global); cx.update({ let events = events.clone(); |cx| { - cx.subscribe(room, move |_, event, _| { + cx.subscribe(&active_call, move |_, event, _| { events.borrow_mut().push(event.clone()) }) .detach() @@ -755,26 +741,28 @@ async fn test_room_location( let (project_a, _) = client_a.build_local_project("/a", cx_a).await; let (project_b, _) = client_b.build_local_project("/b", cx_b).await; - let (room_id, mut rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; - let room_a = rooms.remove(0); - let room_a_notified = Rc::new(Cell::new(false)); + let active_call_a = cx_a.read(ActiveCall::global); + let room_a = active_call_a.read_with(cx_a, |call, _| call.room().unwrap().clone()); + let a_notified = Rc::new(Cell::new(false)); cx_a.update({ - let room_a_notified = room_a_notified.clone(); + let notified = a_notified.clone(); |cx| { - cx.observe(&room_a, move |_, _| room_a_notified.set(true)) + cx.observe(&active_call_a, move |_, _| notified.set(true)) .detach() } }); - let room_b = rooms.remove(0); - let room_b_notified = Rc::new(Cell::new(false)); + let active_call_b = cx_b.read(ActiveCall::global); + let room_b = active_call_b.read_with(cx_b, |call, _| call.room().unwrap().clone()); + let b_notified = Rc::new(Cell::new(false)); cx_b.update({ - let room_b_notified = room_b_notified.clone(); + let b_notified = b_notified.clone(); |cx| { - cx.observe(&room_b, move |_, _| room_b_notified.set(true)) + cx.observe(&active_call_b, move |_, _| b_notified.set(true)) .detach() } }); @@ -784,12 +772,12 @@ async fn test_room_location( .await .unwrap(); deterministic.run_until_parked(); - assert!(room_a_notified.take()); + assert!(a_notified.take()); assert_eq!( participant_locations(&room_a, cx_a), vec![("user_b".to_string(), ParticipantLocation::External)] ); - assert!(room_b_notified.take()); + assert!(b_notified.take()); assert_eq!( participant_locations(&room_b, cx_b), vec![("user_a".to_string(), ParticipantLocation::External)] @@ -800,12 +788,12 @@ async fn test_room_location( .await .unwrap(); deterministic.run_until_parked(); - assert!(room_a_notified.take()); + assert!(a_notified.take()); assert_eq!( participant_locations(&room_a, cx_a), vec![("user_b".to_string(), ParticipantLocation::External)] ); - assert!(room_b_notified.take()); + assert!(b_notified.take()); assert_eq!( participant_locations(&room_b, cx_b), vec![("user_a".to_string(), ParticipantLocation::External)] @@ -816,12 +804,12 @@ async fn test_room_location( .await .unwrap(); deterministic.run_until_parked(); - assert!(room_a_notified.take()); + assert!(a_notified.take()); assert_eq!( participant_locations(&room_a, cx_a), vec![("user_b".to_string(), ParticipantLocation::External)] ); - assert!(room_b_notified.take()); + assert!(b_notified.take()); assert_eq!( participant_locations(&room_b, cx_b), vec![( @@ -837,7 +825,7 @@ async fn test_room_location( .await .unwrap(); deterministic.run_until_parked(); - assert!(room_a_notified.take()); + assert!(a_notified.take()); assert_eq!( participant_locations(&room_a, cx_a), vec![( @@ -847,7 +835,7 @@ async fn test_room_location( } )] ); - assert!(room_b_notified.take()); + assert!(b_notified.take()); assert_eq!( participant_locations(&room_b, cx_b), vec![( @@ -863,12 +851,12 @@ async fn test_room_location( .await .unwrap(); deterministic.run_until_parked(); - assert!(room_a_notified.take()); + assert!(a_notified.take()); assert_eq!( participant_locations(&room_a, cx_a), vec![("user_b".to_string(), ParticipantLocation::External)] ); - assert!(room_b_notified.take()); + assert!(b_notified.take()); assert_eq!( participant_locations(&room_b, cx_b), vec![( @@ -908,8 +896,8 @@ async fn test_propagate_saves_and_fs_changes( let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; let client_c = server.create_client(cx_c, "user_c").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; client_a @@ -1056,8 +1044,8 @@ async fn test_fs_operations( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; client_a @@ -1321,8 +1309,8 @@ async fn test_buffer_conflict_after_save(cx_a: &mut TestAppContext, cx_b: &mut T let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; client_a @@ -1374,8 +1362,8 @@ async fn test_buffer_reloading(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; client_a @@ -1432,8 +1420,8 @@ async fn test_editing_while_guest_opens_buffer( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; client_a @@ -1478,8 +1466,8 @@ async fn test_leaving_worktree_while_opening_buffer( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; client_a @@ -1522,8 +1510,8 @@ async fn test_canceling_buffer_opening( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; client_a @@ -1573,8 +1561,8 @@ async fn test_leaving_project( let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; let client_c = server.create_client(cx_c, "user_c").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; client_a @@ -1663,8 +1651,8 @@ async fn test_collaborating_with_diagnostics( let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; let client_c = server.create_client(cx_c, "user_c").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; // Set up a fake language server. @@ -1900,8 +1888,8 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; // Set up a fake language server. @@ -2073,8 +2061,8 @@ async fn test_reloading_buffer_manually(cx_a: &mut TestAppContext, cx_b: &mut Te let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; client_a @@ -2165,8 +2153,8 @@ async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppCon let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; // Set up a fake language server. @@ -2265,8 +2253,8 @@ async fn test_definition(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; // Set up a fake language server. @@ -2408,8 +2396,8 @@ async fn test_references(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; // Set up a fake language server. @@ -2508,8 +2496,8 @@ async fn test_project_search(cx_a: &mut TestAppContext, cx_b: &mut TestAppContex let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; client_a @@ -2586,8 +2574,8 @@ async fn test_document_highlights(cx_a: &mut TestAppContext, cx_b: &mut TestAppC let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; client_a @@ -2687,8 +2675,8 @@ async fn test_lsp_hover(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; client_a @@ -2789,8 +2777,8 @@ async fn test_project_symbols(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; // Set up a fake language server. @@ -2896,8 +2884,8 @@ async fn test_open_buffer_while_getting_definition_pointing_to_it( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; // Set up a fake language server. @@ -2971,8 +2959,8 @@ async fn test_collaborating_with_code_actions( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; // Set up a fake language server. @@ -3181,8 +3169,8 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; // Set up a fake language server. @@ -3372,8 +3360,8 @@ async fn test_language_server_statuses( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; // Set up a fake language server. @@ -4145,8 +4133,8 @@ async fn test_following(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; cx_a.update(editor::init); cx_b.update(editor::init); @@ -4354,8 +4342,8 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; cx_a.update(editor::init); cx_b.update(editor::init); @@ -4522,8 +4510,8 @@ async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; cx_a.update(editor::init); cx_b.update(editor::init); @@ -4685,8 +4673,8 @@ async fn test_peers_simultaneously_following_each_other( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let (room_id, _rooms) = server - .create_rooms(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + let room_id = server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; cx_a.update(editor::init); cx_b.update(editor::init); @@ -4790,12 +4778,8 @@ async fn test_random_collaboration( .unwrap(); } - let client = server.create_client(cx, "room-creator").await; - let room = cx - .update(|cx| Room::create(client.client.clone(), client.user_store.clone(), cx)) - .await - .unwrap(); - let room_id = room.read_with(cx, |room, _| room.id()); + let _room_creator = server.create_client(cx, "room-creator").await; + let active_call = cx.read(ActiveCall::global); let mut clients = Vec::new(); let mut user_ids = Vec::new(); @@ -4963,22 +4947,17 @@ async fn test_random_collaboration( host_language_registry.add(Arc::new(language)); let host_user_id = host.current_user_id(&host_cx); - room.update(cx, |room, cx| room.call(host_user_id.to_proto(), None, cx)) + active_call + .update(cx, |call, cx| { + call.invite(host_user_id.to_proto(), None, cx) + }) .await .unwrap(); + let room_id = active_call.read_with(cx, |call, cx| call.room().unwrap().read(cx).id()); deterministic.run_until_parked(); - let call = host - .user_store - .read_with(&host_cx, |user_store, _| user_store.incoming_call()); - let host_room = host_cx - .update(|cx| { - Room::join( - call.borrow().as_ref().unwrap(), - host.client.clone(), - host.user_store.clone(), - cx, - ) - }) + host_cx + .read(ActiveCall::global) + .update(&mut host_cx, |call, cx| call.accept_incoming(cx)) .await .unwrap(); @@ -4991,7 +4970,6 @@ async fn test_random_collaboration( user_ids.push(host_user_id); op_start_signals.push(op_start_signal.0); clients.push(host_cx.foreground().spawn(host.simulate_host( - host_room, host_project, op_start_signal.1, rng.clone(), @@ -5016,20 +4994,26 @@ async fn test_random_collaboration( deterministic.finish_waiting(); deterministic.run_until_parked(); - let (host, host_room, host_project, mut host_cx, host_err) = clients.remove(0); + let (host, host_project, mut host_cx, host_err) = clients.remove(0); if let Some(host_err) = host_err { log::error!("host error - {:?}", host_err); } host_project.read_with(&host_cx, |project, _| assert!(!project.is_shared())); - for (guest, guest_room, guest_project, mut guest_cx, guest_err) in clients { + for (guest, guest_project, mut guest_cx, guest_err) in clients { if let Some(guest_err) = guest_err { log::error!("{} error - {:?}", guest.username, guest_err); } guest_project.read_with(&guest_cx, |project, _| assert!(project.is_read_only())); - guest_cx.update(|_| drop((guest, guest_room, guest_project))); + guest_cx.update(|cx| { + cx.clear_globals(); + drop((guest, guest_project)); + }); } - host_cx.update(|_| drop((host, host_room, host_project))); + host_cx.update(|cx| { + cx.clear_globals(); + drop((host, host_project)); + }); return; } @@ -5055,23 +5039,16 @@ async fn test_random_collaboration( let guest = server.create_client(&mut guest_cx, &guest_username).await; let guest_user_id = guest.current_user_id(&guest_cx); - room.update(cx, |room, cx| room.call(guest_user_id.to_proto(), None, cx)) + active_call + .update(cx, |call, cx| { + call.invite(guest_user_id.to_proto(), None, cx) + }) .await .unwrap(); deterministic.run_until_parked(); - let call = guest - .user_store - .read_with(&guest_cx, |user_store, _| user_store.incoming_call()); - - let guest_room = guest_cx - .update(|cx| { - Room::join( - call.borrow().as_ref().unwrap(), - guest.client.clone(), - guest.user_store.clone(), - cx, - ) - }) + guest_cx + .read(ActiveCall::global) + .update(&mut guest_cx, |call, cx| call.accept_incoming(cx)) .await .unwrap(); @@ -5093,7 +5070,6 @@ async fn test_random_collaboration( op_start_signals.push(op_start_signal.0); clients.push(guest_cx.foreground().spawn(guest.simulate_guest( guest_username.clone(), - guest_room, guest_project, op_start_signal.1, rng.clone(), @@ -5114,7 +5090,7 @@ async fn test_random_collaboration( deterministic.advance_clock(RECEIVE_TIMEOUT); deterministic.start_waiting(); log::info!("Waiting for guest {} to exit...", removed_guest_id); - let (guest, guest_room, guest_project, mut guest_cx, guest_err) = guest.await; + let (guest, guest_project, mut guest_cx, guest_err) = guest.await; deterministic.finish_waiting(); server.allow_connections(); @@ -5142,7 +5118,10 @@ async fn test_random_collaboration( log::info!("{} removed", guest.username); available_guests.push(guest.username.clone()); - guest_cx.update(|_| drop((guest, guest_room, guest_project))); + guest_cx.update(|cx| { + cx.clear_globals(); + drop((guest, guest_project)); + }); operations += 1; } @@ -5169,7 +5148,7 @@ async fn test_random_collaboration( deterministic.finish_waiting(); deterministic.run_until_parked(); - let (host_client, host_room, host_project, mut host_cx, host_err) = clients.remove(0); + let (host_client, host_project, mut host_cx, host_err) = clients.remove(0); if let Some(host_err) = host_err { panic!("host error - {:?}", host_err); } @@ -5185,7 +5164,7 @@ async fn test_random_collaboration( host_project.read_with(&host_cx, |project, cx| project.check_invariants(cx)); - for (guest_client, guest_room, guest_project, mut guest_cx, guest_err) in clients.into_iter() { + for (guest_client, guest_project, mut guest_cx, guest_err) in clients.into_iter() { if let Some(guest_err) = guest_err { panic!("{} error - {:?}", guest_client.username, guest_err); } @@ -5257,10 +5236,16 @@ async fn test_random_collaboration( ); } - guest_cx.update(|_| drop((guest_room, guest_project, guest_client))); + guest_cx.update(|cx| { + cx.clear_globals(); + drop((guest_project, guest_client)); + }); } - host_cx.update(|_| drop((host_client, host_room, host_project))); + host_cx.update(|cx| { + cx.clear_globals(); + drop((host_client, host_project)) + }); } struct TestServer { @@ -5385,7 +5370,10 @@ impl TestServer { Channel::init(&client); Project::init(&client); - cx.update(|cx| workspace::init(app_state.clone(), cx)); + cx.update(|cx| { + workspace::init(app_state.clone(), cx); + call::init(client.clone(), user_store.clone(), cx); + }); client .authenticate_and_connect(false, &cx.to_async()) @@ -5447,50 +5435,29 @@ impl TestServer { } } - async fn create_rooms( - &self, - clients: &mut [(&TestClient, &mut TestAppContext)], - ) -> (u64, Vec>) { + async fn create_room(&self, clients: &mut [(&TestClient, &mut TestAppContext)]) -> u64 { self.make_contacts(clients).await; - let mut rooms = Vec::new(); - let (left, right) = clients.split_at_mut(1); - let (client_a, cx_a) = &mut left[0]; - - let room_a = cx_a - .update(|cx| Room::create(client_a.client.clone(), client_a.user_store.clone(), cx)) - .await - .unwrap(); - let room_id = room_a.read_with(*cx_a, |room, _| room.id()); + let (_client_a, cx_a) = &mut left[0]; + let active_call_a = cx_a.read(ActiveCall::global); for (client_b, cx_b) in right { let user_id_b = client_b.current_user_id(*cx_b).to_proto(); - room_a - .update(*cx_a, |room, cx| room.call(user_id_b, None, cx)) + active_call_a + .update(*cx_a, |call, cx| call.invite(user_id_b, None, cx)) .await .unwrap(); cx_b.foreground().run_until_parked(); - let incoming_call = client_b - .user_store - .read_with(*cx_b, |user_store, _| user_store.incoming_call()); - let room_b = cx_b - .update(|cx| { - Room::join( - incoming_call.borrow().as_ref().unwrap(), - client_b.client.clone(), - client_b.user_store.clone(), - cx, - ) - }) + let active_call_b = cx_b.read(ActiveCall::global); + active_call_b + .update(*cx_b, |call, cx| call.accept_incoming(cx)) .await .unwrap(); - rooms.push(room_b); } - rooms.insert(0, room_a); - (room_id, rooms) + active_call_a.read_with(*cx_a, |call, cx| call.room().unwrap().read(cx).id()) } async fn build_app_state(test_db: &TestDb) -> Arc { @@ -5656,14 +5623,12 @@ impl TestClient { async fn simulate_host( mut self, - room: ModelHandle, project: ModelHandle, op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>, rng: Arc>, mut cx: TestAppContext, ) -> ( Self, - ModelHandle, ModelHandle, TestAppContext, Option, @@ -5789,20 +5754,18 @@ impl TestClient { let result = simulate_host_internal(&mut self, project.clone(), op_start_signal, rng, &mut cx).await; log::info!("Host done"); - (self, room, project, cx, result.err()) + (self, project, cx, result.err()) } pub async fn simulate_guest( mut self, guest_username: String, - room: ModelHandle, project: ModelHandle, op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>, rng: Arc>, mut cx: TestAppContext, ) -> ( Self, - ModelHandle, ModelHandle, TestAppContext, Option, @@ -6121,7 +6084,7 @@ impl TestClient { .await; log::info!("{}: done", guest_username); - (self, room, project, cx, result.err()) + (self, project, cx, result.err()) } } diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 786d344df1..03d1bf6672 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -13,7 +13,7 @@ use workspace::{AppState, JoinProject, ToggleFollow, Workspace}; pub fn init(app_state: Arc, cx: &mut MutableAppContext) { contacts_popover::init(cx); collab_titlebar_item::init(cx); - incoming_call_notification::init(app_state.user_store.clone(), cx); + incoming_call_notification::init(cx); project_shared_notification::init(cx); cx.add_global_action(move |action: &JoinProject, cx| { diff --git a/crates/collab_ui/src/incoming_call_notification.rs b/crates/collab_ui/src/incoming_call_notification.rs index e46e69522f..ae1240d0d9 100644 --- a/crates/collab_ui/src/incoming_call_notification.rs +++ b/crates/collab_ui/src/incoming_call_notification.rs @@ -1,11 +1,11 @@ use call::ActiveCall; -use client::{incoming_call::IncomingCall, UserStore}; +use client::incoming_call::IncomingCall; use futures::StreamExt; use gpui::{ elements::*, geometry::{rect::RectF, vector::vec2f}, - impl_internal_actions, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, - View, ViewContext, WindowBounds, WindowKind, WindowOptions, + impl_internal_actions, Entity, MouseButton, MutableAppContext, RenderContext, View, + ViewContext, WindowBounds, WindowKind, WindowOptions, }; use settings::Settings; use util::ResultExt; @@ -13,10 +13,10 @@ use workspace::JoinProject; impl_internal_actions!(incoming_call_notification, [RespondToCall]); -pub fn init(user_store: ModelHandle, cx: &mut MutableAppContext) { +pub fn init(cx: &mut MutableAppContext) { cx.add_action(IncomingCallNotification::respond_to_call); - let mut incoming_call = user_store.read(cx).incoming_call(); + let mut incoming_call = ActiveCall::global(cx).read(cx).incoming(); cx.spawn(|mut cx| async move { let mut notification_window = None; while let Some(incoming_call) = incoming_call.next().await { @@ -33,7 +33,7 @@ pub fn init(user_store: ModelHandle, cx: &mut MutableAppContext) { kind: WindowKind::PopUp, is_movable: false, }, - |_| IncomingCallNotification::new(incoming_call, user_store.clone()), + |_| IncomingCallNotification::new(incoming_call), ); notification_window = Some(window_id); } @@ -49,18 +49,17 @@ struct RespondToCall { pub struct IncomingCallNotification { call: IncomingCall, - user_store: ModelHandle, } impl IncomingCallNotification { - pub fn new(call: IncomingCall, user_store: ModelHandle) -> Self { - Self { call, user_store } + pub fn new(call: IncomingCall) -> Self { + Self { call } } fn respond_to_call(&mut self, action: &RespondToCall, cx: &mut ViewContext) { + let active_call = ActiveCall::global(cx); if action.accept { - let join = ActiveCall::global(cx) - .update(cx, |active_call, cx| active_call.join(&self.call, cx)); + let join = active_call.update(cx, |active_call, cx| active_call.accept_incoming(cx)); let caller_user_id = self.call.caller.id; let initial_project_id = self.call.initial_project_id; cx.spawn_weak(|_, mut cx| async move { @@ -77,12 +76,10 @@ impl IncomingCallNotification { }) .detach_and_log_err(cx); } else { - self.user_store - .update(cx, |user_store, _| user_store.decline_call().log_err()); + active_call.update(cx, |active_call, _| { + active_call.decline_incoming().log_err(); + }); } - - let window_id = cx.window_id(); - cx.remove_window(window_id); } fn render_caller(&self, cx: &mut RenderContext) -> ElementBox { diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 002bd01377..668071d046 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1906,6 +1906,10 @@ impl MutableAppContext { }) } + pub fn clear_globals(&mut self) { + self.cx.globals.clear(); + } + pub fn add_model(&mut self, build_model: F) -> ModelHandle where T: Entity, diff --git a/crates/gpui/src/test.rs b/crates/gpui/src/test.rs index 4122ad09b7..6cfb4cf2b6 100644 --- a/crates/gpui/src/test.rs +++ b/crates/gpui/src/test.rs @@ -91,7 +91,7 @@ pub fn run_test( cx.update(|cx| cx.remove_all_windows()); deterministic.run_until_parked(); - cx.update(|_| {}); // flush effects + cx.update(|cx| cx.clear_globals()); leak_detector.lock().detect(); if is_last_iteration { diff --git a/crates/gpui_macros/src/gpui_macros.rs b/crates/gpui_macros/src/gpui_macros.rs index a60d385e8f..32a821f4c8 100644 --- a/crates/gpui_macros/src/gpui_macros.rs +++ b/crates/gpui_macros/src/gpui_macros.rs @@ -122,7 +122,7 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream { cx_teardowns.extend(quote!( #cx_varname.update(|cx| cx.remove_all_windows()); deterministic.run_until_parked(); - #cx_varname.update(|_| {}); // flush effects + #cx_varname.update(|cx| cx.clear_globals()); )); inner_fn_args.extend(quote!(&mut #cx_varname,)); continue; From 7763acbdd5a2a7c55d9459b3231f79694e8e1820 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 6 Oct 2022 09:52:03 +0200 Subject: [PATCH 160/314] Move `IncomingCall` into `call` crate --- crates/call/src/call.rs | 10 +++++++++- crates/call/src/room.rs | 7 +++++-- crates/client/src/client.rs | 1 - crates/client/src/incoming_call.rs | 10 ---------- crates/collab_ui/src/incoming_call_notification.rs | 3 +-- 5 files changed, 15 insertions(+), 16 deletions(-) delete mode 100644 crates/client/src/incoming_call.rs diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 607931fdc4..7cd8896bf6 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -2,7 +2,7 @@ mod participant; pub mod room; use anyhow::{anyhow, Result}; -use client::{incoming_call::IncomingCall, proto, Client, TypedEnvelope, UserStore}; +use client::{proto, Client, TypedEnvelope, User, UserStore}; use gpui::{ AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Subscription, Task, @@ -18,6 +18,14 @@ pub fn init(client: Arc, user_store: ModelHandle, cx: &mut Mu cx.set_global(active_call); } +#[derive(Clone)] +pub struct IncomingCall { + pub room_id: u64, + pub caller: Arc, + pub participants: Vec>, + pub initial_project_id: Option, +} + pub struct ActiveCall { room: Option<(ModelHandle, Vec)>, incoming_call: ( diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 52f283dd03..3d8697f1e0 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -1,6 +1,9 @@ -use crate::participant::{ParticipantLocation, RemoteParticipant}; +use crate::{ + participant::{ParticipantLocation, RemoteParticipant}, + IncomingCall, +}; use anyhow::{anyhow, Result}; -use client::{incoming_call::IncomingCall, proto, Client, PeerId, TypedEnvelope, User, UserStore}; +use client::{proto, Client, PeerId, TypedEnvelope, User, UserStore}; use collections::{HashMap, HashSet}; use futures::StreamExt; use gpui::{AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task}; diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 9c5b8e35c9..a762e263ea 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -3,7 +3,6 @@ pub mod test; pub mod channel; pub mod http; -pub mod incoming_call; pub mod user; use anyhow::{anyhow, Context, Result}; diff --git a/crates/client/src/incoming_call.rs b/crates/client/src/incoming_call.rs deleted file mode 100644 index 80ba014061..0000000000 --- a/crates/client/src/incoming_call.rs +++ /dev/null @@ -1,10 +0,0 @@ -use crate::User; -use std::sync::Arc; - -#[derive(Clone)] -pub struct IncomingCall { - pub room_id: u64, - pub caller: Arc, - pub participants: Vec>, - pub initial_project_id: Option, -} diff --git a/crates/collab_ui/src/incoming_call_notification.rs b/crates/collab_ui/src/incoming_call_notification.rs index ae1240d0d9..8860097a59 100644 --- a/crates/collab_ui/src/incoming_call_notification.rs +++ b/crates/collab_ui/src/incoming_call_notification.rs @@ -1,5 +1,4 @@ -use call::ActiveCall; -use client::incoming_call::IncomingCall; +use call::{ActiveCall, IncomingCall}; use futures::StreamExt; use gpui::{ elements::*, From 40163da679d9d5a5ca1cf6bca7824e91daa3233f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 6 Oct 2022 14:00:14 +0200 Subject: [PATCH 161/314] Move contacts panel features into collab_ui --- Cargo.lock | 26 +- assets/keymaps/default.json | 1 - crates/collab_ui/Cargo.toml | 1 + crates/collab_ui/src/collab_titlebar_item.rs | 60 +- crates/collab_ui/src/collab_ui.rs | 5 + .../src/contact_finder.rs | 26 +- .../src/contact_notification.rs | 5 +- crates/collab_ui/src/contacts_popover.rs | 69 +- .../src/notifications.rs | 23 +- crates/contacts_panel/Cargo.toml | 32 - crates/contacts_panel/src/contacts_panel.rs | 1000 ----------------- crates/theme/src/theme.rs | 1 + crates/zed/Cargo.toml | 1 - crates/zed/src/main.rs | 1 - crates/zed/src/menus.rs | 4 - crates/zed/src/zed.rs | 24 +- styles/src/styleTree/workspace.ts | 7 + 17 files changed, 152 insertions(+), 1134 deletions(-) rename crates/{contacts_panel => collab_ui}/src/contact_finder.rs (88%) rename crates/{contacts_panel => collab_ui}/src/contact_notification.rs (96%) rename crates/{contacts_panel => collab_ui}/src/notifications.rs (83%) delete mode 100644 crates/contacts_panel/Cargo.toml delete mode 100644 crates/contacts_panel/src/contacts_panel.rs diff --git a/Cargo.lock b/Cargo.lock index a971570e2f..2d3ca1f78c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1091,6 +1091,7 @@ dependencies = [ "gpui", "log", "menu", + "picker", "postage", "project", "serde", @@ -1141,30 +1142,6 @@ dependencies = [ "cache-padded", ] -[[package]] -name = "contacts_panel" -version = "0.1.0" -dependencies = [ - "anyhow", - "client", - "collections", - "editor", - "futures", - "fuzzy", - "gpui", - "language", - "log", - "menu", - "picker", - "postage", - "project", - "serde", - "settings", - "theme", - "util", - "workspace", -] - [[package]] name = "context_menu" version = "0.1.0" @@ -7165,7 +7142,6 @@ dependencies = [ "collab_ui", "collections", "command_palette", - "contacts_panel", "context_menu", "ctor", "diagnostics", diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index a0bc8c39e6..e2adfc0f81 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -395,7 +395,6 @@ "context": "Workspace", "bindings": { "shift-escape": "dock::FocusDock", - "cmd-shift-c": "contacts_panel::ToggleFocus", "cmd-shift-b": "workspace::ToggleRightSidebar" } }, diff --git a/crates/collab_ui/Cargo.toml b/crates/collab_ui/Cargo.toml index cf3a78a0b5..20db066ce7 100644 --- a/crates/collab_ui/Cargo.toml +++ b/crates/collab_ui/Cargo.toml @@ -29,6 +29,7 @@ editor = { path = "../editor" } fuzzy = { path = "../fuzzy" } gpui = { path = "../gpui" } menu = { path = "../menu" } +picker = { path = "../picker" } project = { path = "../project" } settings = { path = "../settings" } theme = { path = "../theme" } diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index cea3654856..c982962042 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -1,6 +1,6 @@ -use crate::contacts_popover; +use crate::{contact_notification::ContactNotification, contacts_popover}; use call::{ActiveCall, ParticipantLocation}; -use client::{Authenticate, PeerId}; +use client::{Authenticate, ContactEventKind, PeerId, UserStore}; use clock::ReplicaId; use contacts_popover::ContactsPopover; use gpui::{ @@ -9,8 +9,8 @@ use gpui::{ elements::*, geometry::{rect::RectF, vector::vec2f, PathBuilder}, json::{self, ToJson}, - Border, CursorStyle, Entity, ImageData, MouseButton, MutableAppContext, RenderContext, - Subscription, View, ViewContext, ViewHandle, WeakViewHandle, + Border, CursorStyle, Entity, ImageData, ModelHandle, MouseButton, MutableAppContext, + RenderContext, Subscription, View, ViewContext, ViewHandle, WeakViewHandle, }; use settings::Settings; use std::{ops::Range, sync::Arc}; @@ -29,6 +29,7 @@ pub fn init(cx: &mut MutableAppContext) { pub struct CollabTitlebarItem { workspace: WeakViewHandle, + user_store: ModelHandle, contacts_popover: Option>, _subscriptions: Vec, } @@ -71,7 +72,11 @@ impl View for CollabTitlebarItem { } impl CollabTitlebarItem { - pub fn new(workspace: &ViewHandle, cx: &mut ViewContext) -> Self { + pub fn new( + workspace: &ViewHandle, + user_store: &ModelHandle, + cx: &mut ViewContext, + ) -> Self { let active_call = ActiveCall::global(cx); let mut subscriptions = Vec::new(); subscriptions.push(cx.observe(workspace, |_, _, cx| cx.notify())); @@ -79,9 +84,33 @@ impl CollabTitlebarItem { subscriptions.push(cx.observe_window_activation(|this, active, cx| { this.window_activation_changed(active, cx) })); + subscriptions.push(cx.observe(user_store, |_, _, cx| cx.notify())); + subscriptions.push( + cx.subscribe(user_store, move |this, user_store, event, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + workspace.update(cx, |workspace, cx| { + if let client::Event::Contact { user, kind } = event { + if let ContactEventKind::Requested | ContactEventKind::Accepted = kind { + workspace.show_notification(user.id as usize, cx, |cx| { + cx.add_view(|cx| { + ContactNotification::new( + user.clone(), + *kind, + user_store, + cx, + ) + }) + }) + } + } + }); + } + }), + ); Self { workspace: workspace.downgrade(), + user_store: user_store.clone(), contacts_popover: None, _subscriptions: subscriptions, } @@ -160,6 +189,26 @@ impl CollabTitlebarItem { cx: &mut RenderContext, ) -> ElementBox { let titlebar = &theme.workspace.titlebar; + let badge = if self + .user_store + .read(cx) + .incoming_contact_requests() + .is_empty() + { + None + } else { + Some( + Empty::new() + .collapsed() + .contained() + .with_style(titlebar.toggle_contacts_badge) + .contained() + .with_margin_left(titlebar.toggle_contacts_button.default.icon_width) + .with_margin_top(titlebar.toggle_contacts_button.default.icon_width) + .aligned() + .boxed(), + ) + }; Stack::new() .with_child( MouseEventHandler::::new(0, cx, |state, _| { @@ -185,6 +234,7 @@ impl CollabTitlebarItem { .aligned() .boxed(), ) + .with_children(badge) .with_children(self.contacts_popover.as_ref().map(|popover| { Overlay::new( ChildView::new(popover) diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 03d1bf6672..438a41ae7d 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -1,6 +1,9 @@ mod collab_titlebar_item; +mod contact_finder; +mod contact_notification; mod contacts_popover; mod incoming_call_notification; +mod notifications; mod project_shared_notification; use call::ActiveCall; @@ -11,6 +14,8 @@ use std::sync::Arc; use workspace::{AppState, JoinProject, ToggleFollow, Workspace}; pub fn init(app_state: Arc, cx: &mut MutableAppContext) { + contact_notification::init(cx); + contact_finder::init(cx); contacts_popover::init(cx); collab_titlebar_item::init(cx); incoming_call_notification::init(cx); diff --git a/crates/contacts_panel/src/contact_finder.rs b/crates/collab_ui/src/contact_finder.rs similarity index 88% rename from crates/contacts_panel/src/contact_finder.rs rename to crates/collab_ui/src/contact_finder.rs index 1831c1ba72..6814b7479f 100644 --- a/crates/contacts_panel/src/contact_finder.rs +++ b/crates/collab_ui/src/contact_finder.rs @@ -9,8 +9,6 @@ use std::sync::Arc; use util::TryFutureExt; use workspace::Workspace; -use crate::render_icon_button; - actions!(contact_finder, [Toggle]); pub fn init(cx: &mut MutableAppContext) { @@ -117,11 +115,10 @@ impl PickerDelegate for ContactFinder { let icon_path = match request_status { ContactRequestStatus::None | ContactRequestStatus::RequestReceived => { - "icons/check_8.svg" - } - ContactRequestStatus::RequestSent | ContactRequestStatus::RequestAccepted => { - "icons/x_mark_8.svg" + Some("icons/check_8.svg") } + ContactRequestStatus::RequestSent => Some("icons/x_mark_8.svg"), + ContactRequestStatus::RequestAccepted => None, }; let button_style = if self.user_store.read(cx).is_contact_request_pending(user) { &theme.contact_finder.disabled_contact_button @@ -145,12 +142,21 @@ impl PickerDelegate for ContactFinder { .left() .boxed(), ) - .with_child( - render_icon_button(button_style, icon_path) + .with_children(icon_path.map(|icon_path| { + Svg::new(icon_path) + .with_color(button_style.color) + .constrained() + .with_width(button_style.icon_width) + .aligned() + .contained() + .with_style(button_style.container) + .constrained() + .with_width(button_style.button_width) + .with_height(button_style.button_width) .aligned() .flex_float() - .boxed(), - ) + .boxed() + })) .contained() .with_style(style.container) .constrained() diff --git a/crates/contacts_panel/src/contact_notification.rs b/crates/collab_ui/src/contact_notification.rs similarity index 96% rename from crates/contacts_panel/src/contact_notification.rs rename to crates/collab_ui/src/contact_notification.rs index c608346d79..f543a01446 100644 --- a/crates/contacts_panel/src/contact_notification.rs +++ b/crates/collab_ui/src/contact_notification.rs @@ -49,10 +49,7 @@ impl View for ContactNotification { self.user.clone(), "wants to add you as a contact", Some("They won't know if you decline."), - RespondToContactRequest { - user_id: self.user.id, - accept: false, - }, + Dismiss(self.user.id), vec![ ( "Decline", diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index 389fe9fbd2..f3ebf3abea 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -1,22 +1,27 @@ use std::sync::Arc; +use crate::contact_finder; use call::ActiveCall; use client::{Contact, User, UserStore}; use editor::{Cancel, Editor}; use fuzzy::{match_strings, StringMatchCandidate}; use gpui::{ - elements::*, impl_internal_actions, keymap, AppContext, ClipboardItem, CursorStyle, Entity, - ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, View, ViewContext, - ViewHandle, + elements::*, impl_actions, impl_internal_actions, keymap, AppContext, ClipboardItem, + CursorStyle, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, + View, ViewContext, ViewHandle, }; use menu::{Confirm, SelectNext, SelectPrev}; use project::Project; +use serde::Deserialize; use settings::Settings; use theme::IconButton; -impl_internal_actions!(contacts_panel, [ToggleExpanded, Call]); +impl_actions!(contacts_popover, [RemoveContact, RespondToContactRequest]); +impl_internal_actions!(contacts_popover, [ToggleExpanded, Call]); pub fn init(cx: &mut MutableAppContext) { + cx.add_action(ContactsPopover::remove_contact); + cx.add_action(ContactsPopover::respond_to_contact_request); cx.add_action(ContactsPopover::clear_filter); cx.add_action(ContactsPopover::select_next); cx.add_action(ContactsPopover::select_prev); @@ -77,6 +82,18 @@ impl PartialEq for ContactEntry { } } +#[derive(Clone, Deserialize, PartialEq)] +pub struct RequestContact(pub u64); + +#[derive(Clone, Deserialize, PartialEq)] +pub struct RemoveContact(pub u64); + +#[derive(Clone, Deserialize, PartialEq)] +pub struct RespondToContactRequest { + pub user_id: u64, + pub accept: bool, +} + pub enum Event { Dismissed, } @@ -186,6 +203,24 @@ impl ContactsPopover { this } + fn remove_contact(&mut self, request: &RemoveContact, cx: &mut ViewContext) { + self.user_store + .update(cx, |store, cx| store.remove_contact(request.0, cx)) + .detach(); + } + + fn respond_to_contact_request( + &mut self, + action: &RespondToContactRequest, + cx: &mut ViewContext, + ) { + self.user_store + .update(cx, |store, cx| { + store.respond_to_contact_request(action.user_id, action.accept, cx) + }) + .detach(); + } + fn clear_filter(&mut self, _: &Cancel, cx: &mut ViewContext) { let did_clear = self.filter_editor.update(cx, |editor, cx| { if editor.buffer().read(cx).len(cx) > 0 { @@ -574,18 +609,15 @@ impl ContactsPopover { }; render_icon_button(button_style, "icons/x_mark_8.svg") .aligned() - // .flex_float() .boxed() }) .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, move |_, cx| { - todo!(); - // cx.dispatch_action(RespondToContactRequest { - // user_id, - // accept: false, - // }) + cx.dispatch_action(RespondToContactRequest { + user_id, + accept: false, + }) }) - // .flex_float() .contained() .with_margin_right(button_spacing) .boxed(), @@ -602,11 +634,10 @@ impl ContactsPopover { }) .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, move |_, cx| { - todo!() - // cx.dispatch_action(RespondToContactRequest { - // user_id, - // accept: true, - // }) + cx.dispatch_action(RespondToContactRequest { + user_id, + accept: true, + }) }) .boxed(), ]); @@ -626,8 +657,7 @@ impl ContactsPopover { .with_padding(Padding::uniform(2.)) .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, move |_, cx| { - todo!() - // cx.dispatch_action(RemoveContact(user_id)) + cx.dispatch_action(RemoveContact(user_id)) }) .flex_float() .boxed(), @@ -692,8 +722,7 @@ impl View for ContactsPopover { }) .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, |_, cx| { - todo!() - // cx.dispatch_action(contact_finder::Toggle) + cx.dispatch_action(contact_finder::Toggle) }) .boxed(), ) diff --git a/crates/contacts_panel/src/notifications.rs b/crates/collab_ui/src/notifications.rs similarity index 83% rename from crates/contacts_panel/src/notifications.rs rename to crates/collab_ui/src/notifications.rs index b9a6dba545..dcb9894006 100644 --- a/crates/contacts_panel/src/notifications.rs +++ b/crates/collab_ui/src/notifications.rs @@ -1,9 +1,7 @@ -use crate::render_icon_button; use client::User; use gpui::{ - elements::{Flex, Image, Label, MouseEventHandler, Padding, ParentElement, Text}, - platform::CursorStyle, - Action, Element, ElementBox, MouseButton, RenderContext, View, + elements::*, platform::CursorStyle, Action, Element, ElementBox, MouseButton, RenderContext, + View, }; use settings::Settings; use std::sync::Arc; @@ -53,11 +51,18 @@ pub fn render_user_notification( ) .with_child( MouseEventHandler::::new(user.id as usize, cx, |state, _| { - render_icon_button( - theme.dismiss_button.style_for(state, false), - "icons/x_mark_thin_8.svg", - ) - .boxed() + let style = theme.dismiss_button.style_for(state, false); + Svg::new("icons/x_mark_thin_8.svg") + .with_color(style.color) + .constrained() + .with_width(style.icon_width) + .aligned() + .contained() + .with_style(style.container) + .constrained() + .with_width(style.button_width) + .with_height(style.button_width) + .boxed() }) .with_cursor_style(CursorStyle::PointingHand) .with_padding(Padding::uniform(5.)) diff --git a/crates/contacts_panel/Cargo.toml b/crates/contacts_panel/Cargo.toml deleted file mode 100644 index b68f48bb97..0000000000 --- a/crates/contacts_panel/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -name = "contacts_panel" -version = "0.1.0" -edition = "2021" - -[lib] -path = "src/contacts_panel.rs" -doctest = false - -[dependencies] -client = { path = "../client" } -collections = { path = "../collections" } -editor = { path = "../editor" } -fuzzy = { path = "../fuzzy" } -gpui = { path = "../gpui" } -menu = { path = "../menu" } -picker = { path = "../picker" } -project = { path = "../project" } -settings = { path = "../settings" } -theme = { path = "../theme" } -util = { path = "../util" } -workspace = { path = "../workspace" } -anyhow = "1.0" -futures = "0.3" -log = "0.4" -postage = { version = "0.4.1", features = ["futures-traits"] } -serde = { version = "1.0", features = ["derive", "rc"] } - -[dev-dependencies] -language = { path = "../language", features = ["test-support"] } -project = { path = "../project", features = ["test-support"] } -workspace = { path = "../workspace", features = ["test-support"] } diff --git a/crates/contacts_panel/src/contacts_panel.rs b/crates/contacts_panel/src/contacts_panel.rs deleted file mode 100644 index db6d3bd3b0..0000000000 --- a/crates/contacts_panel/src/contacts_panel.rs +++ /dev/null @@ -1,1000 +0,0 @@ -mod contact_finder; -mod contact_notification; -mod notifications; - -use client::{Contact, ContactEventKind, User, UserStore}; -use contact_notification::ContactNotification; -use editor::{Cancel, Editor}; -use fuzzy::{match_strings, StringMatchCandidate}; -use gpui::{ - actions, elements::*, impl_actions, impl_internal_actions, platform::CursorStyle, - AnyViewHandle, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle, - MouseButton, MutableAppContext, RenderContext, Subscription, View, ViewContext, ViewHandle, - WeakViewHandle, -}; -use menu::{Confirm, SelectNext, SelectPrev}; -use serde::Deserialize; -use settings::Settings; -use std::sync::Arc; -use theme::IconButton; -use workspace::{sidebar::SidebarItem, Workspace}; - -actions!(contacts_panel, [ToggleFocus]); - -impl_actions!( - contacts_panel, - [RequestContact, RemoveContact, RespondToContactRequest] -); - -impl_internal_actions!(contacts_panel, [ToggleExpanded]); - -#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)] -enum Section { - Requests, - Online, - Offline, -} - -#[derive(Clone)] -enum ContactEntry { - Header(Section), - IncomingRequest(Arc), - OutgoingRequest(Arc), - Contact(Arc), -} - -#[derive(Clone, PartialEq)] -struct ToggleExpanded(Section); - -pub struct ContactsPanel { - entries: Vec, - match_candidates: Vec, - list_state: ListState, - user_store: ModelHandle, - filter_editor: ViewHandle, - collapsed_sections: Vec
, - selection: Option, - _maintain_contacts: Subscription, -} - -#[derive(Clone, Deserialize, PartialEq)] -pub struct RequestContact(pub u64); - -#[derive(Clone, Deserialize, PartialEq)] -pub struct RemoveContact(pub u64); - -#[derive(Clone, Deserialize, PartialEq)] -pub struct RespondToContactRequest { - pub user_id: u64, - pub accept: bool, -} - -pub fn init(cx: &mut MutableAppContext) { - contact_finder::init(cx); - contact_notification::init(cx); - cx.add_action(ContactsPanel::request_contact); - cx.add_action(ContactsPanel::remove_contact); - cx.add_action(ContactsPanel::respond_to_contact_request); - cx.add_action(ContactsPanel::clear_filter); - cx.add_action(ContactsPanel::select_next); - cx.add_action(ContactsPanel::select_prev); - cx.add_action(ContactsPanel::confirm); - cx.add_action(ContactsPanel::toggle_expanded); -} - -impl ContactsPanel { - pub fn new( - user_store: ModelHandle, - workspace: WeakViewHandle, - cx: &mut ViewContext, - ) -> Self { - let filter_editor = cx.add_view(|cx| { - let mut editor = Editor::single_line( - Some(|theme| theme.contacts_panel.user_query_editor.clone()), - cx, - ); - editor.set_placeholder_text("Filter contacts", cx); - editor - }); - - cx.subscribe(&filter_editor, |this, _, event, cx| { - if let editor::Event::BufferEdited = event { - let query = this.filter_editor.read(cx).text(cx); - if !query.is_empty() { - this.selection.take(); - } - this.update_entries(cx); - if !query.is_empty() { - this.selection = this - .entries - .iter() - .position(|entry| !matches!(entry, ContactEntry::Header(_))); - } - } - }) - .detach(); - - cx.subscribe(&user_store, move |_, user_store, event, cx| { - if let Some(workspace) = workspace.upgrade(cx) { - workspace.update(cx, |workspace, cx| { - if let client::Event::Contact { user, kind } = event { - if let ContactEventKind::Requested | ContactEventKind::Accepted = kind { - workspace.show_notification(user.id as usize, cx, |cx| { - cx.add_view(|cx| { - ContactNotification::new(user.clone(), *kind, user_store, cx) - }) - }) - } - } - }); - } - - if let client::Event::ShowContacts = event { - cx.emit(Event::Activate); - } - }) - .detach(); - - let list_state = ListState::new(0, Orientation::Top, 1000., cx, move |this, ix, cx| { - let theme = cx.global::().theme.clone(); - let is_selected = this.selection == Some(ix); - - match &this.entries[ix] { - ContactEntry::Header(section) => { - let is_collapsed = this.collapsed_sections.contains(section); - Self::render_header( - *section, - &theme.contacts_panel, - is_selected, - is_collapsed, - cx, - ) - } - ContactEntry::IncomingRequest(user) => Self::render_contact_request( - user.clone(), - this.user_store.clone(), - &theme.contacts_panel, - true, - is_selected, - cx, - ), - ContactEntry::OutgoingRequest(user) => Self::render_contact_request( - user.clone(), - this.user_store.clone(), - &theme.contacts_panel, - false, - is_selected, - cx, - ), - ContactEntry::Contact(contact) => { - Self::render_contact(&contact.user, &theme.contacts_panel, is_selected) - } - } - }); - - let mut this = Self { - list_state, - selection: None, - collapsed_sections: Default::default(), - entries: Default::default(), - match_candidates: Default::default(), - filter_editor, - _maintain_contacts: cx.observe(&user_store, |this, _, cx| this.update_entries(cx)), - user_store, - }; - this.update_entries(cx); - this - } - - fn render_header( - section: Section, - theme: &theme::ContactsPanel, - is_selected: bool, - is_collapsed: bool, - cx: &mut RenderContext, - ) -> ElementBox { - enum Header {} - - let header_style = theme.header_row.style_for(Default::default(), is_selected); - let text = match section { - Section::Requests => "Requests", - Section::Online => "Online", - Section::Offline => "Offline", - }; - let icon_size = theme.section_icon_size; - MouseEventHandler::
::new(section as usize, cx, |_, _| { - Flex::row() - .with_child( - Svg::new(if is_collapsed { - "icons/chevron_right_8.svg" - } else { - "icons/chevron_down_8.svg" - }) - .with_color(header_style.text.color) - .constrained() - .with_max_width(icon_size) - .with_max_height(icon_size) - .aligned() - .constrained() - .with_width(icon_size) - .boxed(), - ) - .with_child( - Label::new(text.to_string(), header_style.text.clone()) - .aligned() - .left() - .contained() - .with_margin_left(theme.contact_username.container.margin.left) - .flex(1., true) - .boxed(), - ) - .constrained() - .with_height(theme.row_height) - .contained() - .with_style(header_style.container) - .boxed() - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, cx| { - cx.dispatch_action(ToggleExpanded(section)) - }) - .boxed() - } - - fn render_contact(user: &User, theme: &theme::ContactsPanel, is_selected: bool) -> ElementBox { - Flex::row() - .with_children(user.avatar.clone().map(|avatar| { - Image::new(avatar) - .with_style(theme.contact_avatar) - .aligned() - .left() - .boxed() - })) - .with_child( - Label::new( - user.github_login.clone(), - theme.contact_username.text.clone(), - ) - .contained() - .with_style(theme.contact_username.container) - .aligned() - .left() - .flex(1., true) - .boxed(), - ) - .constrained() - .with_height(theme.row_height) - .contained() - .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) - .boxed() - } - - fn render_contact_request( - user: Arc, - user_store: ModelHandle, - theme: &theme::ContactsPanel, - is_incoming: bool, - is_selected: bool, - cx: &mut RenderContext, - ) -> ElementBox { - enum Decline {} - enum Accept {} - enum Cancel {} - - let mut row = Flex::row() - .with_children(user.avatar.clone().map(|avatar| { - Image::new(avatar) - .with_style(theme.contact_avatar) - .aligned() - .left() - .boxed() - })) - .with_child( - Label::new( - user.github_login.clone(), - theme.contact_username.text.clone(), - ) - .contained() - .with_style(theme.contact_username.container) - .aligned() - .left() - .flex(1., true) - .boxed(), - ); - - let user_id = user.id; - let is_contact_request_pending = user_store.read(cx).is_contact_request_pending(&user); - let button_spacing = theme.contact_button_spacing; - - if is_incoming { - row.add_children([ - MouseEventHandler::::new(user.id as usize, cx, |mouse_state, _| { - let button_style = if is_contact_request_pending { - &theme.disabled_button - } else { - theme.contact_button.style_for(mouse_state, false) - }; - render_icon_button(button_style, "icons/x_mark_8.svg") - .aligned() - // .flex_float() - .boxed() - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, cx| { - cx.dispatch_action(RespondToContactRequest { - user_id, - accept: false, - }) - }) - // .flex_float() - .contained() - .with_margin_right(button_spacing) - .boxed(), - MouseEventHandler::::new(user.id as usize, cx, |mouse_state, _| { - let button_style = if is_contact_request_pending { - &theme.disabled_button - } else { - theme.contact_button.style_for(mouse_state, false) - }; - render_icon_button(button_style, "icons/check_8.svg") - .aligned() - .flex_float() - .boxed() - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, cx| { - cx.dispatch_action(RespondToContactRequest { - user_id, - accept: true, - }) - }) - .boxed(), - ]); - } else { - row.add_child( - MouseEventHandler::::new(user.id as usize, cx, |mouse_state, _| { - let button_style = if is_contact_request_pending { - &theme.disabled_button - } else { - theme.contact_button.style_for(mouse_state, false) - }; - render_icon_button(button_style, "icons/x_mark_8.svg") - .aligned() - .flex_float() - .boxed() - }) - .with_padding(Padding::uniform(2.)) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, cx| { - cx.dispatch_action(RemoveContact(user_id)) - }) - .flex_float() - .boxed(), - ); - } - - row.constrained() - .with_height(theme.row_height) - .contained() - .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) - .boxed() - } - - fn update_entries(&mut self, cx: &mut ViewContext) { - let user_store = self.user_store.read(cx); - let query = self.filter_editor.read(cx).text(cx); - let executor = cx.background().clone(); - - let prev_selected_entry = self.selection.and_then(|ix| self.entries.get(ix).cloned()); - self.entries.clear(); - - let mut request_entries = Vec::new(); - let incoming = user_store.incoming_contact_requests(); - if !incoming.is_empty() { - self.match_candidates.clear(); - self.match_candidates - .extend( - incoming - .iter() - .enumerate() - .map(|(ix, user)| StringMatchCandidate { - id: ix, - string: user.github_login.clone(), - char_bag: user.github_login.chars().collect(), - }), - ); - let matches = executor.block(match_strings( - &self.match_candidates, - &query, - true, - usize::MAX, - &Default::default(), - executor.clone(), - )); - request_entries.extend( - matches - .iter() - .map(|mat| ContactEntry::IncomingRequest(incoming[mat.candidate_id].clone())), - ); - } - - let outgoing = user_store.outgoing_contact_requests(); - if !outgoing.is_empty() { - self.match_candidates.clear(); - self.match_candidates - .extend( - outgoing - .iter() - .enumerate() - .map(|(ix, user)| StringMatchCandidate { - id: ix, - string: user.github_login.clone(), - char_bag: user.github_login.chars().collect(), - }), - ); - let matches = executor.block(match_strings( - &self.match_candidates, - &query, - true, - usize::MAX, - &Default::default(), - executor.clone(), - )); - request_entries.extend( - matches - .iter() - .map(|mat| ContactEntry::OutgoingRequest(outgoing[mat.candidate_id].clone())), - ); - } - - if !request_entries.is_empty() { - self.entries.push(ContactEntry::Header(Section::Requests)); - if !self.collapsed_sections.contains(&Section::Requests) { - self.entries.append(&mut request_entries); - } - } - - let current_user = user_store.current_user(); - - let contacts = user_store.contacts(); - if !contacts.is_empty() { - // Always put the current user first. - self.match_candidates.clear(); - self.match_candidates.reserve(contacts.len()); - self.match_candidates.push(StringMatchCandidate { - id: 0, - string: Default::default(), - char_bag: Default::default(), - }); - for (ix, contact) in contacts.iter().enumerate() { - let candidate = StringMatchCandidate { - id: ix, - string: contact.user.github_login.clone(), - char_bag: contact.user.github_login.chars().collect(), - }; - if current_user - .as_ref() - .map_or(false, |current_user| current_user.id == contact.user.id) - { - self.match_candidates[0] = candidate; - } else { - self.match_candidates.push(candidate); - } - } - if self.match_candidates[0].string.is_empty() { - self.match_candidates.remove(0); - } - - let matches = executor.block(match_strings( - &self.match_candidates, - &query, - true, - usize::MAX, - &Default::default(), - executor.clone(), - )); - - let (online_contacts, offline_contacts) = matches - .iter() - .partition::, _>(|mat| contacts[mat.candidate_id].online); - - for (matches, section) in [ - (online_contacts, Section::Online), - (offline_contacts, Section::Offline), - ] { - if !matches.is_empty() { - self.entries.push(ContactEntry::Header(section)); - if !self.collapsed_sections.contains(§ion) { - for mat in matches { - let contact = &contacts[mat.candidate_id]; - self.entries.push(ContactEntry::Contact(contact.clone())); - } - } - } - } - } - - if let Some(prev_selected_entry) = prev_selected_entry { - self.selection.take(); - for (ix, entry) in self.entries.iter().enumerate() { - if *entry == prev_selected_entry { - self.selection = Some(ix); - break; - } - } - } - - self.list_state.reset(self.entries.len()); - cx.notify(); - } - - fn request_contact(&mut self, request: &RequestContact, cx: &mut ViewContext) { - self.user_store - .update(cx, |store, cx| store.request_contact(request.0, cx)) - .detach(); - } - - fn remove_contact(&mut self, request: &RemoveContact, cx: &mut ViewContext) { - self.user_store - .update(cx, |store, cx| store.remove_contact(request.0, cx)) - .detach(); - } - - fn respond_to_contact_request( - &mut self, - action: &RespondToContactRequest, - cx: &mut ViewContext, - ) { - self.user_store - .update(cx, |store, cx| { - store.respond_to_contact_request(action.user_id, action.accept, cx) - }) - .detach(); - } - - fn clear_filter(&mut self, _: &Cancel, cx: &mut ViewContext) { - let did_clear = self.filter_editor.update(cx, |editor, cx| { - if editor.buffer().read(cx).len(cx) > 0 { - editor.set_text("", cx); - true - } else { - false - } - }); - if !did_clear { - cx.propagate_action(); - } - } - - fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext) { - if let Some(ix) = self.selection { - if self.entries.len() > ix + 1 { - self.selection = Some(ix + 1); - } - } else if !self.entries.is_empty() { - self.selection = Some(0); - } - cx.notify(); - self.list_state.reset(self.entries.len()); - } - - fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext) { - if let Some(ix) = self.selection { - if ix > 0 { - self.selection = Some(ix - 1); - } else { - self.selection = None; - } - } - cx.notify(); - self.list_state.reset(self.entries.len()); - } - - fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext) { - if let Some(selection) = self.selection { - if let Some(entry) = self.entries.get(selection) { - match entry { - ContactEntry::Header(section) => { - let section = *section; - self.toggle_expanded(&ToggleExpanded(section), cx); - } - _ => {} - } - } - } - } - - fn toggle_expanded(&mut self, action: &ToggleExpanded, cx: &mut ViewContext) { - let section = action.0; - if let Some(ix) = self.collapsed_sections.iter().position(|s| *s == section) { - self.collapsed_sections.remove(ix); - } else { - self.collapsed_sections.push(section); - } - self.update_entries(cx); - } -} - -impl SidebarItem for ContactsPanel { - fn should_show_badge(&self, cx: &AppContext) -> bool { - !self - .user_store - .read(cx) - .incoming_contact_requests() - .is_empty() - } - - fn contains_focused_view(&self, cx: &AppContext) -> bool { - self.filter_editor.is_focused(cx) - } - - fn should_activate_item_on_event(&self, event: &Event, _: &AppContext) -> bool { - matches!(event, Event::Activate) - } -} - -fn render_icon_button(style: &IconButton, svg_path: &'static str) -> impl Element { - Svg::new(svg_path) - .with_color(style.color) - .constrained() - .with_width(style.icon_width) - .aligned() - .contained() - .with_style(style.container) - .constrained() - .with_width(style.button_width) - .with_height(style.button_width) -} - -pub enum Event { - Activate, -} - -impl Entity for ContactsPanel { - type Event = Event; -} - -impl View for ContactsPanel { - fn ui_name() -> &'static str { - "ContactsPanel" - } - - fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - enum AddContact {} - - let theme = cx.global::().theme.clone(); - let theme = &theme.contacts_panel; - Container::new( - Flex::column() - .with_child( - Flex::row() - .with_child( - ChildView::new(self.filter_editor.clone()) - .contained() - .with_style(theme.user_query_editor.container) - .flex(1., true) - .boxed(), - ) - .with_child( - MouseEventHandler::::new(0, cx, |_, _| { - Svg::new("icons/user_plus_16.svg") - .with_color(theme.add_contact_button.color) - .constrained() - .with_height(16.) - .contained() - .with_style(theme.add_contact_button.container) - .aligned() - .boxed() - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, |_, cx| { - cx.dispatch_action(contact_finder::Toggle) - }) - .boxed(), - ) - .constrained() - .with_height(theme.user_query_editor_height) - .boxed(), - ) - .with_child(List::new(self.list_state.clone()).flex(1., false).boxed()) - .with_children( - self.user_store - .read(cx) - .invite_info() - .cloned() - .and_then(|info| { - enum InviteLink {} - - if info.count > 0 { - Some( - MouseEventHandler::::new(0, cx, |state, cx| { - let style = - theme.invite_row.style_for(state, false).clone(); - - let copied = - cx.read_from_clipboard().map_or(false, |item| { - item.text().as_str() == info.url.as_ref() - }); - - Label::new( - format!( - "{} invite link ({} left)", - if copied { "Copied" } else { "Copy" }, - info.count - ), - style.label.clone(), - ) - .aligned() - .left() - .constrained() - .with_height(theme.row_height) - .contained() - .with_style(style.container) - .boxed() - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, cx| { - cx.write_to_clipboard(ClipboardItem::new( - info.url.to_string(), - )); - cx.notify(); - }) - .boxed(), - ) - } else { - None - } - }), - ) - .boxed(), - ) - .with_style(theme.container) - .boxed() - } - - fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { - cx.focus(&self.filter_editor); - } - - fn keymap_context(&self, _: &gpui::AppContext) -> gpui::keymap::Context { - let mut cx = Self::default_keymap_context(); - cx.set.insert("menu".into()); - cx - } -} - -impl PartialEq for ContactEntry { - fn eq(&self, other: &Self) -> bool { - match self { - ContactEntry::Header(section_1) => { - if let ContactEntry::Header(section_2) = other { - return section_1 == section_2; - } - } - ContactEntry::IncomingRequest(user_1) => { - if let ContactEntry::IncomingRequest(user_2) = other { - return user_1.id == user_2.id; - } - } - ContactEntry::OutgoingRequest(user_1) => { - if let ContactEntry::OutgoingRequest(user_2) = other { - return user_1.id == user_2.id; - } - } - ContactEntry::Contact(contact_1) => { - if let ContactEntry::Contact(contact_2) = other { - return contact_1.user.id == contact_2.user.id; - } - } - } - false - } -} - -#[cfg(test)] -mod tests { - use super::*; - use client::{ - proto, - test::{FakeHttpClient, FakeServer}, - Client, - }; - use collections::HashSet; - use gpui::TestAppContext; - use language::LanguageRegistry; - use project::{FakeFs, Project, ProjectStore}; - - #[gpui::test] - async fn test_contact_panel(cx: &mut TestAppContext) { - Settings::test_async(cx); - let current_user_id = 100; - - let languages = Arc::new(LanguageRegistry::test()); - let http_client = FakeHttpClient::with_404_response(); - let client = Client::new(http_client.clone()); - let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx)); - let project_store = cx.add_model(|_| ProjectStore::new()); - let server = FakeServer::for_client(current_user_id, &client, cx).await; - let fs = FakeFs::new(cx.background()); - let project = cx.update(|cx| { - Project::local( - client.clone(), - user_store.clone(), - project_store.clone(), - languages, - fs, - cx, - ) - }); - - let (_, workspace) = - cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx)); - let panel = cx.add_view(&workspace, |cx| { - ContactsPanel::new(user_store.clone(), workspace.downgrade(), cx) - }); - - workspace.update(cx, |_, cx| { - cx.observe(&panel, |_, panel, cx| { - let entries = render_to_strings(&panel, cx); - assert!( - entries.iter().collect::>().len() == entries.len(), - "Duplicate contact panel entries {:?}", - entries - ) - }) - .detach(); - }); - - let get_users_request = server.receive::().await.unwrap(); - server - .respond( - get_users_request.receipt(), - proto::UsersResponse { - users: [ - "user_zero", - "user_one", - "user_two", - "user_three", - "user_four", - "user_five", - ] - .into_iter() - .enumerate() - .map(|(id, name)| proto::User { - id: id as u64, - github_login: name.to_string(), - ..Default::default() - }) - .chain([proto::User { - id: current_user_id, - github_login: "the_current_user".to_string(), - ..Default::default() - }]) - .collect(), - }, - ) - .await; - - server.send(proto::UpdateContacts { - incoming_requests: vec![proto::IncomingContactRequest { - requester_id: 1, - should_notify: false, - }], - outgoing_requests: vec![2], - contacts: vec![ - proto::Contact { - user_id: 3, - online: true, - should_notify: false, - }, - proto::Contact { - user_id: 4, - online: true, - should_notify: false, - }, - proto::Contact { - user_id: 5, - online: false, - should_notify: false, - }, - proto::Contact { - user_id: current_user_id, - online: true, - should_notify: false, - }, - ], - ..Default::default() - }); - - cx.foreground().run_until_parked(); - assert_eq!( - cx.read(|cx| render_to_strings(&panel, cx)), - &[ - "v Requests", - " incoming user_one", - " outgoing user_two", - "v Online", - " the_current_user", - " user_four", - " user_three", - "v Offline", - " user_five", - ] - ); - - panel.update(cx, |panel, cx| { - panel - .filter_editor - .update(cx, |editor, cx| editor.set_text("f", cx)) - }); - cx.foreground().run_until_parked(); - assert_eq!( - cx.read(|cx| render_to_strings(&panel, cx)), - &[ - "v Online", - " user_four <=== selected", - "v Offline", - " user_five", - ] - ); - - panel.update(cx, |panel, cx| { - panel.select_next(&Default::default(), cx); - }); - assert_eq!( - cx.read(|cx| render_to_strings(&panel, cx)), - &[ - "v Online", - " user_four", - "v Offline <=== selected", - " user_five", - ] - ); - - panel.update(cx, |panel, cx| { - panel.select_next(&Default::default(), cx); - }); - assert_eq!( - cx.read(|cx| render_to_strings(&panel, cx)), - &[ - "v Online", - " user_four", - "v Offline", - " user_five <=== selected", - ] - ); - } - - fn render_to_strings(panel: &ViewHandle, cx: &AppContext) -> Vec { - let panel = panel.read(cx); - let mut entries = Vec::new(); - entries.extend(panel.entries.iter().enumerate().map(|(ix, entry)| { - let mut string = match entry { - ContactEntry::Header(name) => { - let icon = if panel.collapsed_sections.contains(name) { - ">" - } else { - "v" - }; - format!("{} {:?}", icon, name) - } - ContactEntry::IncomingRequest(user) => { - format!(" incoming {}", user.github_login) - } - ContactEntry::OutgoingRequest(user) => { - format!(" outgoing {}", user.github_login) - } - ContactEntry::Contact(contact) => { - format!(" {}", contact.user.github_login) - } - }; - - if panel.selection == Some(ix) { - string.push_str(" <=== selected"); - } - - string - })); - entries - } -} diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index bc7e6f0995..175c523e53 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -78,6 +78,7 @@ pub struct Titlebar { pub outdated_warning: ContainedText, pub share_button: Interactive, pub toggle_contacts_button: Interactive, + pub toggle_contacts_badge: ContainerStyle, pub contacts_popover: AddParticipantPopover, } diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index f2eb765353..c0b43dca8e 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -28,7 +28,6 @@ command_palette = { path = "../command_palette" } context_menu = { path = "../context_menu" } client = { path = "../client" } clock = { path = "../clock" } -contacts_panel = { path = "../contacts_panel" } diagnostics = { path = "../diagnostics" } editor = { path = "../editor" } file_finder = { path = "../file_finder" } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 580493f6d0..dc953bae8c 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -112,7 +112,6 @@ fn main() { go_to_line::init(cx); file_finder::init(cx); chat_panel::init(cx); - contacts_panel::init(cx); outline::init(cx); project_symbols::init(cx); project_panel::init(cx); diff --git a/crates/zed/src/menus.rs b/crates/zed/src/menus.rs index 3a34166ba6..835519fb5c 100644 --- a/crates/zed/src/menus.rs +++ b/crates/zed/src/menus.rs @@ -244,10 +244,6 @@ pub fn menus() -> Vec> { name: "Project Panel", action: Box::new(project_panel::ToggleFocus), }, - MenuItem::Action { - name: "Contacts Panel", - action: Box::new(contacts_panel::ToggleFocus), - }, MenuItem::Action { name: "Command Palette", action: Box::new(command_palette::Toggle), diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index d41b9284c4..28a1249c12 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -12,8 +12,6 @@ use breadcrumbs::Breadcrumbs; pub use client; use collab_ui::CollabTitlebarItem; use collections::VecDeque; -pub use contacts_panel; -use contacts_panel::ContactsPanel; pub use editor; use editor::{Editor, MultiBuffer}; use gpui::{ @@ -208,13 +206,6 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { workspace.toggle_sidebar_item_focus(SidebarSide::Left, 0, cx); }, ); - cx.add_action( - |workspace: &mut Workspace, - _: &contacts_panel::ToggleFocus, - cx: &mut ViewContext| { - workspace.toggle_sidebar_item_focus(SidebarSide::Right, 0, cx); - }, - ); activity_indicator::init(cx); call::init(app_state.client.clone(), app_state.user_store.clone(), cx); @@ -281,14 +272,11 @@ pub fn initialize_workspace( })); }); - let collab_titlebar_item = cx.add_view(|cx| CollabTitlebarItem::new(&workspace_handle, cx)); + let collab_titlebar_item = + cx.add_view(|cx| CollabTitlebarItem::new(&workspace_handle, &app_state.user_store, cx)); workspace.set_titlebar_item(collab_titlebar_item, cx); let project_panel = ProjectPanel::new(workspace.project().clone(), cx); - let contact_panel = cx.add_view(|cx| { - ContactsPanel::new(app_state.user_store.clone(), workspace.weak_handle(), cx) - }); - workspace.left_sidebar().update(cx, |sidebar, cx| { sidebar.add_item( "icons/folder_tree_16.svg", @@ -297,14 +285,6 @@ pub fn initialize_workspace( cx, ) }); - workspace.right_sidebar().update(cx, |sidebar, cx| { - sidebar.add_item( - "icons/user_group_16.svg", - "Contacts Panel".to_string(), - contact_panel, - cx, - ) - }); let diagnostic_summary = cx.add_view(|cx| diagnostics::items::DiagnosticIndicator::new(workspace.project(), cx)); diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 0877f131c1..65531e6ec9 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -144,6 +144,13 @@ export default function workspace(theme: Theme) { color: iconColor(theme, "active"), }, }, + toggleContactsBadge: { + cornerRadius: 3, + padding: 2, + margin: { top: 3, left: 3 }, + border: { width: 1, color: workspaceBackground(theme) }, + background: iconColor(theme, "feature"), + }, shareButton: { ...titlebarButton }, From c43956d70a91a3c71345fbc6297aa84f781969b9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 6 Oct 2022 14:07:21 +0200 Subject: [PATCH 162/314] Move contacts panel styles into contacts popover --- crates/collab_ui/src/contacts_popover.rs | 36 ++-- .../src/incoming_call_notification.rs | 6 +- crates/theme/src/theme.rs | 45 ++--- styles/src/styleTree/contactsPanel.ts | 165 ------------------ styles/src/styleTree/contactsPopover.ts | 161 ++++++++++++++++- styles/src/styleTree/workspace.ts | 10 -- 6 files changed, 198 insertions(+), 225 deletions(-) delete mode 100644 styles/src/styleTree/contactsPanel.ts diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index f3ebf3abea..388b344879 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -118,7 +118,7 @@ impl ContactsPopover { ) -> Self { let filter_editor = cx.add_view(|cx| { let mut editor = Editor::single_line( - Some(|theme| theme.contacts_panel.user_query_editor.clone()), + Some(|theme| theme.contacts_popover.user_query_editor.clone()), cx, ); editor.set_placeholder_text("Filter contacts", cx); @@ -151,7 +151,7 @@ impl ContactsPopover { let is_collapsed = this.collapsed_sections.contains(section); Self::render_header( *section, - &theme.contacts_panel, + &theme.contacts_popover, is_selected, is_collapsed, cx, @@ -160,7 +160,7 @@ impl ContactsPopover { ContactEntry::IncomingRequest(user) => Self::render_contact_request( user.clone(), this.user_store.clone(), - &theme.contacts_panel, + &theme.contacts_popover, true, is_selected, cx, @@ -168,7 +168,7 @@ impl ContactsPopover { ContactEntry::OutgoingRequest(user) => Self::render_contact_request( user.clone(), this.user_store.clone(), - &theme.contacts_panel, + &theme.contacts_popover, false, is_selected, cx, @@ -176,7 +176,7 @@ impl ContactsPopover { ContactEntry::Contact(contact) => Self::render_contact( contact, &this.project, - &theme.contacts_panel, + &theme.contacts_popover, is_selected, cx, ), @@ -418,7 +418,7 @@ impl ContactsPopover { fn render_active_call(&self, cx: &mut RenderContext) -> Option { let room = ActiveCall::global(cx).read(cx).room()?; - let theme = &cx.global::().theme.contacts_panel; + let theme = &cx.global::().theme.contacts_popover; Some( Flex::column() @@ -455,7 +455,7 @@ impl ContactsPopover { fn render_header( section: Section, - theme: &theme::ContactsPanel, + theme: &theme::ContactsPopover, is_selected: bool, is_collapsed: bool, cx: &mut RenderContext, @@ -511,7 +511,7 @@ impl ContactsPopover { fn render_contact( contact: &Contact, project: &ModelHandle, - theme: &theme::ContactsPanel, + theme: &theme::ContactsPopover, is_selected: bool, cx: &mut RenderContext, ) -> ElementBox { @@ -565,7 +565,7 @@ impl ContactsPopover { fn render_contact_request( user: Arc, user_store: ModelHandle, - theme: &theme::ContactsPanel, + theme: &theme::ContactsPopover, is_incoming: bool, is_selected: bool, cx: &mut RenderContext, @@ -705,18 +705,18 @@ impl View for ContactsPopover { .with_child( ChildView::new(self.filter_editor.clone()) .contained() - .with_style(theme.contacts_panel.user_query_editor.container) + .with_style(theme.contacts_popover.user_query_editor.container) .flex(1., true) .boxed(), ) .with_child( MouseEventHandler::::new(0, cx, |_, _| { Svg::new("icons/user_plus_16.svg") - .with_color(theme.contacts_panel.add_contact_button.color) + .with_color(theme.contacts_popover.add_contact_button.color) .constrained() .with_height(16.) .contained() - .with_style(theme.contacts_panel.add_contact_button.container) + .with_style(theme.contacts_popover.add_contact_button.container) .aligned() .boxed() }) @@ -727,7 +727,7 @@ impl View for ContactsPopover { .boxed(), ) .constrained() - .with_height(theme.contacts_panel.user_query_editor_height) + .with_height(theme.contacts_popover.user_query_editor_height) .boxed(), ) .with_children(self.render_active_call(cx)) @@ -744,7 +744,7 @@ impl View for ContactsPopover { Some( MouseEventHandler::::new(0, cx, |state, cx| { let style = theme - .contacts_panel + .contacts_popover .invite_row .style_for(state, false) .clone(); @@ -764,7 +764,7 @@ impl View for ContactsPopover { .aligned() .left() .constrained() - .with_height(theme.contacts_panel.row_height) + .with_height(theme.contacts_popover.row_height) .contained() .with_style(style.container) .boxed() @@ -782,10 +782,10 @@ impl View for ContactsPopover { }), ) .contained() - .with_style(theme.workspace.titlebar.contacts_popover.container) + .with_style(theme.contacts_popover.container) .constrained() - .with_width(theme.workspace.titlebar.contacts_popover.width) - .with_height(theme.workspace.titlebar.contacts_popover.height) + .with_width(theme.contacts_popover.width) + .with_height(theme.contacts_popover.height) .boxed() } diff --git a/crates/collab_ui/src/incoming_call_notification.rs b/crates/collab_ui/src/incoming_call_notification.rs index 8860097a59..0581859ea9 100644 --- a/crates/collab_ui/src/incoming_call_notification.rs +++ b/crates/collab_ui/src/incoming_call_notification.rs @@ -82,7 +82,7 @@ impl IncomingCallNotification { } fn render_caller(&self, cx: &mut RenderContext) -> ElementBox { - let theme = &cx.global::().theme.contacts_panel; + let theme = &cx.global::().theme.contacts_popover; Flex::row() .with_children( self.call @@ -108,7 +108,7 @@ impl IncomingCallNotification { Flex::row() .with_child( MouseEventHandler::::new(0, cx, |_, cx| { - let theme = &cx.global::().theme.contacts_panel; + let theme = &cx.global::().theme.contacts_popover; Label::new("Accept".to_string(), theme.contact_username.text.clone()).boxed() }) .on_click(MouseButton::Left, |_, cx| { @@ -118,7 +118,7 @@ impl IncomingCallNotification { ) .with_child( MouseEventHandler::::new(0, cx, |_, cx| { - let theme = &cx.global::().theme.contacts_panel; + let theme = &cx.global::().theme.contacts_popover; Label::new("Decline".to_string(), theme.contact_username.text.clone()).boxed() }) .on_click(MouseButton::Left, |_, cx| { diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 175c523e53..b9de72065a 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -19,7 +19,7 @@ pub struct Theme { pub workspace: Workspace, pub context_menu: ContextMenu, pub chat_panel: ChatPanel, - pub contacts_panel: ContactsPanel, + pub contacts_popover: ContactsPopover, pub contact_finder: ContactFinder, pub project_panel: ProjectPanel, pub command_palette: CommandPalette, @@ -79,15 +79,30 @@ pub struct Titlebar { pub share_button: Interactive, pub toggle_contacts_button: Interactive, pub toggle_contacts_badge: ContainerStyle, - pub contacts_popover: AddParticipantPopover, } -#[derive(Clone, Deserialize, Default)] -pub struct AddParticipantPopover { +#[derive(Deserialize, Default)] +pub struct ContactsPopover { #[serde(flatten)] pub container: ContainerStyle, pub height: f32, pub width: f32, + pub user_query_editor: FieldEditor, + pub user_query_editor_height: f32, + pub add_contact_button: IconButton, + pub header_row: Interactive, + pub contact_row: Interactive, + pub project_row: Interactive, + pub row_height: f32, + pub contact_avatar: ImageStyle, + pub contact_username: ContainedText, + pub contact_button: Interactive, + pub contact_button_spacing: f32, + pub disabled_button: IconButton, + pub tree_branch: Interactive, + pub private_button: Interactive, + pub section_icon_size: f32, + pub invite_row: Interactive, } #[derive(Clone, Deserialize, Default)] @@ -329,28 +344,6 @@ pub struct CommandPalette { pub keystroke_spacing: f32, } -#[derive(Deserialize, Default)] -pub struct ContactsPanel { - #[serde(flatten)] - pub container: ContainerStyle, - pub user_query_editor: FieldEditor, - pub user_query_editor_height: f32, - pub add_contact_button: IconButton, - pub header_row: Interactive, - pub contact_row: Interactive, - pub project_row: Interactive, - pub row_height: f32, - pub contact_avatar: ImageStyle, - pub contact_username: ContainedText, - pub contact_button: Interactive, - pub contact_button_spacing: f32, - pub disabled_button: IconButton, - pub tree_branch: Interactive, - pub private_button: Interactive, - pub section_icon_size: f32, - pub invite_row: Interactive, -} - #[derive(Deserialize, Default)] pub struct InviteLink { #[serde(flatten)] diff --git a/styles/src/styleTree/contactsPanel.ts b/styles/src/styleTree/contactsPanel.ts deleted file mode 100644 index 20fce729e4..0000000000 --- a/styles/src/styleTree/contactsPanel.ts +++ /dev/null @@ -1,165 +0,0 @@ -import Theme from "../themes/common/theme"; -import { panel } from "./app"; -import { - backgroundColor, - border, - borderColor, - iconColor, - player, - text, -} from "./components"; - -export default function contactsPanel(theme: Theme) { - const nameMargin = 8; - const sidePadding = 12; - - const projectRow = { - guestAvatarSpacing: 4, - height: 24, - guestAvatar: { - cornerRadius: 8, - width: 14, - }, - name: { - ...text(theme, "mono", "placeholder", { size: "sm" }), - margin: { - left: nameMargin, - right: 6, - }, - }, - guests: { - margin: { - left: nameMargin, - right: nameMargin, - }, - }, - padding: { - left: sidePadding, - right: sidePadding, - }, - }; - - const contactButton = { - background: backgroundColor(theme, 100), - color: iconColor(theme, "primary"), - iconWidth: 8, - buttonWidth: 16, - cornerRadius: 8, - }; - - return { - ...panel, - padding: { top: panel.padding.top, bottom: 0 }, - userQueryEditor: { - background: backgroundColor(theme, 500), - cornerRadius: 6, - text: text(theme, "mono", "primary"), - placeholderText: text(theme, "mono", "placeholder", { size: "sm" }), - selection: player(theme, 1).selection, - border: border(theme, "secondary"), - padding: { - bottom: 4, - left: 8, - right: 8, - top: 4, - }, - margin: { - left: sidePadding, - right: sidePadding, - }, - }, - userQueryEditorHeight: 32, - addContactButton: { - margin: { left: 6, right: 12 }, - color: iconColor(theme, "primary"), - buttonWidth: 16, - iconWidth: 16, - }, - privateButton: { - iconWidth: 12, - color: iconColor(theme, "primary"), - cornerRadius: 5, - buttonWidth: 12, - }, - rowHeight: 28, - sectionIconSize: 8, - headerRow: { - ...text(theme, "mono", "secondary", { size: "sm" }), - margin: { top: 14 }, - padding: { - left: sidePadding, - right: sidePadding, - }, - active: { - ...text(theme, "mono", "primary", { size: "sm" }), - background: backgroundColor(theme, 100, "active"), - }, - }, - contactRow: { - padding: { - left: sidePadding, - right: sidePadding, - }, - active: { - background: backgroundColor(theme, 100, "active"), - }, - }, - treeBranch: { - color: borderColor(theme, "active"), - width: 1, - hover: { - color: borderColor(theme, "active"), - }, - active: { - color: borderColor(theme, "active"), - }, - }, - contactAvatar: { - cornerRadius: 10, - width: 18, - }, - contactUsername: { - ...text(theme, "mono", "primary", { size: "sm" }), - margin: { - left: nameMargin, - }, - }, - contactButtonSpacing: nameMargin, - contactButton: { - ...contactButton, - hover: { - background: backgroundColor(theme, "on300", "hovered"), - }, - }, - disabledButton: { - ...contactButton, - background: backgroundColor(theme, 100), - color: iconColor(theme, "muted"), - }, - projectRow: { - ...projectRow, - background: backgroundColor(theme, 300), - name: { - ...projectRow.name, - ...text(theme, "mono", "secondary", { size: "sm" }), - }, - hover: { - background: backgroundColor(theme, 300, "hovered"), - }, - active: { - background: backgroundColor(theme, 300, "active"), - }, - }, - inviteRow: { - padding: { - left: sidePadding, - right: sidePadding, - }, - border: { top: true, width: 1, color: borderColor(theme, "primary") }, - text: text(theme, "sans", "secondary", { size: "sm" }), - hover: { - text: text(theme, "sans", "active", { size: "sm" }), - }, - }, - }; -} diff --git a/styles/src/styleTree/contactsPopover.ts b/styles/src/styleTree/contactsPopover.ts index e9de5dddaf..82174fd672 100644 --- a/styles/src/styleTree/contactsPopover.ts +++ b/styles/src/styleTree/contactsPopover.ts @@ -1,8 +1,163 @@ import Theme from "../themes/common/theme"; -import { backgroundColor } from "./components"; +import { backgroundColor, border, borderColor, iconColor, player, popoverShadow, text } from "./components"; + +export default function contactsPopover(theme: Theme) { + const nameMargin = 8; + const sidePadding = 12; + + const projectRow = { + guestAvatarSpacing: 4, + height: 24, + guestAvatar: { + cornerRadius: 8, + width: 14, + }, + name: { + ...text(theme, "mono", "placeholder", { size: "sm" }), + margin: { + left: nameMargin, + right: 6, + }, + }, + guests: { + margin: { + left: nameMargin, + right: nameMargin, + }, + }, + padding: { + left: sidePadding, + right: sidePadding, + }, + }; + + const contactButton = { + background: backgroundColor(theme, 100), + color: iconColor(theme, "primary"), + iconWidth: 8, + buttonWidth: 16, + cornerRadius: 8, + }; -export default function workspace(theme: Theme) { return { - background: backgroundColor(theme, 300), + background: backgroundColor(theme, 300, "base"), + cornerRadius: 6, + padding: { top: 6 }, + shadow: popoverShadow(theme), + border: border(theme, "primary"), + margin: { top: -5 }, + width: 250, + height: 300, + userQueryEditor: { + background: backgroundColor(theme, 500), + cornerRadius: 6, + text: text(theme, "mono", "primary"), + placeholderText: text(theme, "mono", "placeholder", { size: "sm" }), + selection: player(theme, 1).selection, + border: border(theme, "secondary"), + padding: { + bottom: 4, + left: 8, + right: 8, + top: 4, + }, + margin: { + left: sidePadding, + right: sidePadding, + }, + }, + userQueryEditorHeight: 32, + addContactButton: { + margin: { left: 6, right: 12 }, + color: iconColor(theme, "primary"), + buttonWidth: 16, + iconWidth: 16, + }, + privateButton: { + iconWidth: 12, + color: iconColor(theme, "primary"), + cornerRadius: 5, + buttonWidth: 12, + }, + rowHeight: 28, + sectionIconSize: 8, + headerRow: { + ...text(theme, "mono", "secondary", { size: "sm" }), + margin: { top: 14 }, + padding: { + left: sidePadding, + right: sidePadding, + }, + active: { + ...text(theme, "mono", "primary", { size: "sm" }), + background: backgroundColor(theme, 100, "active"), + }, + }, + contactRow: { + padding: { + left: sidePadding, + right: sidePadding, + }, + active: { + background: backgroundColor(theme, 100, "active"), + }, + }, + treeBranch: { + color: borderColor(theme, "active"), + width: 1, + hover: { + color: borderColor(theme, "active"), + }, + active: { + color: borderColor(theme, "active"), + }, + }, + contactAvatar: { + cornerRadius: 10, + width: 18, + }, + contactUsername: { + ...text(theme, "mono", "primary", { size: "sm" }), + margin: { + left: nameMargin, + }, + }, + contactButtonSpacing: nameMargin, + contactButton: { + ...contactButton, + hover: { + background: backgroundColor(theme, "on300", "hovered"), + }, + }, + disabledButton: { + ...contactButton, + background: backgroundColor(theme, 100), + color: iconColor(theme, "muted"), + }, + projectRow: { + ...projectRow, + background: backgroundColor(theme, 300), + name: { + ...projectRow.name, + ...text(theme, "mono", "secondary", { size: "sm" }), + }, + hover: { + background: backgroundColor(theme, 300, "hovered"), + }, + active: { + background: backgroundColor(theme, 300, "active"), + }, + }, + inviteRow: { + padding: { + left: sidePadding, + right: sidePadding, + }, + border: { top: true, width: 1, color: borderColor(theme, "primary") }, + text: text(theme, "sans", "secondary", { size: "sm" }), + hover: { + text: text(theme, "sans", "active", { size: "sm" }), + }, + }, } } diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 65531e6ec9..7ad99ef6ab 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -153,16 +153,6 @@ export default function workspace(theme: Theme) { }, shareButton: { ...titlebarButton - }, - contactsPopover: { - background: backgroundColor(theme, 300, "base"), - cornerRadius: 6, - padding: { top: 6 }, - shadow: popoverShadow(theme), - border: border(theme, "primary"), - margin: { top: -5 }, - width: 250, - height: 300 } }, toolbar: { From 2e84fc673798e5ab1e0ffc19f496598bec7d15e5 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 6 Oct 2022 14:20:40 +0200 Subject: [PATCH 163/314] Delete rooms without pending users or participants --- crates/collab/src/integration_tests.rs | 21 +++++++++++++++++++++ crates/collab/src/rpc.rs | 4 +++- crates/collab/src/rpc/store.rs | 23 ++++++++++++++++++----- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 1adac8b28e..a11d908cdd 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -203,6 +203,27 @@ async fn test_basic_calls( pending: Default::default() } ); + + // User B leaves the room. + active_call_b.update(cx_b, |call, cx| { + call.hang_up(cx).unwrap(); + assert!(call.room().is_none()); + }); + deterministic.run_until_parked(); + assert_eq!( + room_participants(&room_a, cx_a), + RoomParticipants { + remote: Default::default(), + pending: Default::default() + } + ); + assert_eq!( + room_participants(&room_b, cx_b), + RoomParticipants { + remote: Default::default(), + pending: Default::default() + } + ); } #[gpui::test(iterations = 10)] diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 4098e6522a..46bf4bf3dc 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -646,7 +646,9 @@ impl Server { } } - self.room_updated(left_room.room); + if let Some(room) = left_room.room { + self.room_updated(room); + } Ok(()) } diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index 2f95851bf7..a0d272ccc8 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -101,7 +101,7 @@ pub struct LeftProject { } pub struct LeftRoom<'a> { - pub room: &'a proto::Room, + pub room: Option<&'a proto::Room>, pub unshared_projects: Vec, pub left_projects: Vec, } @@ -222,7 +222,8 @@ impl Store { let connected_user = self.connected_users.get_mut(&user_id).unwrap(); connected_user.connection_ids.remove(&connection_id); if let Some(active_call) = connected_user.active_call.as_ref() { - if let Some(room) = self.rooms.get_mut(&active_call.room_id) { + let room_id = active_call.room_id; + if let Some(room) = self.rooms.get_mut(&room_id) { let prev_participant_count = room.participants.len(); room.participants .retain(|participant| participant.peer_id != connection_id.0); @@ -230,13 +231,17 @@ impl Store { if connected_user.connection_ids.is_empty() { room.pending_user_ids .retain(|pending_user_id| *pending_user_id != user_id.to_proto()); - result.room_id = Some(active_call.room_id); + result.room_id = Some(room_id); connected_user.active_call = None; } } else { - result.room_id = Some(active_call.room_id); + result.room_id = Some(room_id); connected_user.active_call = None; } + + if room.participants.is_empty() && room.pending_user_ids.is_empty() { + self.rooms.remove(&room_id); + } } else { tracing::error!("disconnected user claims to be in a room that does not exist"); connected_user.active_call = None; @@ -476,9 +481,12 @@ impl Store { .ok_or_else(|| anyhow!("no such room"))?; room.participants .retain(|participant| participant.peer_id != connection_id.0); + if room.participants.is_empty() && room.pending_user_ids.is_empty() { + self.rooms.remove(&room_id); + } Ok(LeftRoom { - room, + room: self.rooms.get(&room_id), unshared_projects, left_projects, }) @@ -1069,6 +1077,11 @@ impl Store { ); } } + + assert!( + !room.pending_user_ids.is_empty() || !room.participants.is_empty(), + "room can't be empty" + ); } for (project_id, project) in &self.projects { From 4cb306fbf36bf28afd73725712332e69faedcea5 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 6 Oct 2022 15:12:27 +0200 Subject: [PATCH 164/314] Implement call cancellation --- crates/call/src/call.rs | 20 ++++++- crates/collab/src/integration_tests.rs | 82 ++++++++++++++++++++++++++ crates/collab/src/rpc.rs | 25 +++++++- crates/collab/src/rpc/store.rs | 45 ++++++++++++++ crates/rpc/proto/zed.proto | 9 ++- crates/rpc/src/proto.rs | 2 + 6 files changed, 176 insertions(+), 7 deletions(-) diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 7cd8896bf6..1f4f7633e1 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -52,7 +52,7 @@ impl ActiveCall { incoming_call: watch::channel(), _subscriptions: vec![ client.add_request_handler(cx.handle(), Self::handle_incoming_call), - client.add_message_handler(cx.handle(), Self::handle_cancel_call), + client.add_message_handler(cx.handle(), Self::handle_call_canceled), ], client, user_store, @@ -87,9 +87,9 @@ impl ActiveCall { Ok(proto::Ack {}) } - async fn handle_cancel_call( + async fn handle_call_canceled( this: ModelHandle, - _: TypedEnvelope, + _: TypedEnvelope, _: Arc, mut cx: AsyncAppContext, ) -> Result<()> { @@ -140,6 +140,20 @@ impl ActiveCall { }) } + pub fn cancel_invite( + &mut self, + recipient_user_id: u64, + cx: &mut ModelContext, + ) -> Task> { + let client = self.client.clone(); + cx.foreground().spawn(async move { + client + .request(proto::CancelCall { recipient_user_id }) + .await?; + anyhow::Ok(()) + }) + } + pub fn incoming(&self) -> watch::Receiver> { self.incoming_call.1.clone() } diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index a11d908cdd..0767ee5ddb 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -401,6 +401,88 @@ async fn test_leaving_room_on_disconnection( ); } +#[gpui::test(iterations = 10)] +async fn test_calls_on_multiple_connections( + deterministic: Arc, + cx_a: &mut TestAppContext, + cx_b1: &mut TestAppContext, + cx_b2: &mut TestAppContext, +) { + deterministic.forbid_parking(); + let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; + let client_a = server.create_client(cx_a, "user_a").await; + let client_b1 = server.create_client(cx_b1, "user_b").await; + let _client_b2 = server.create_client(cx_b2, "user_b").await; + server + .make_contacts(&mut [(&client_a, cx_a), (&client_b1, cx_b1)]) + .await; + + let active_call_a = cx_a.read(ActiveCall::global); + let active_call_b1 = cx_b1.read(ActiveCall::global); + let active_call_b2 = cx_b2.read(ActiveCall::global); + let mut incoming_call_b1 = active_call_b1.read_with(cx_b1, |call, _| call.incoming()); + let mut incoming_call_b2 = active_call_b2.read_with(cx_b2, |call, _| call.incoming()); + assert!(incoming_call_b1.next().await.unwrap().is_none()); + assert!(incoming_call_b2.next().await.unwrap().is_none()); + + // Call user B from client A, ensuring both clients for user B ring. + active_call_a + .update(cx_a, |call, cx| { + call.invite(client_b1.user_id().unwrap(), None, cx) + }) + .await + .unwrap(); + assert!(incoming_call_b1.next().await.unwrap().is_some()); + assert!(incoming_call_b2.next().await.unwrap().is_some()); + + // User B declines the call on one of the two connections, causing both connections + // to stop ringing. + active_call_b2.update(cx_b2, |call, _| call.decline_incoming().unwrap()); + assert!(incoming_call_b1.next().await.unwrap().is_none()); + assert!(incoming_call_b2.next().await.unwrap().is_none()); + + // Call user B again from client A. + active_call_a + .update(cx_a, |call, cx| { + call.invite(client_b1.user_id().unwrap(), None, cx) + }) + .await + .unwrap(); + assert!(incoming_call_b1.next().await.unwrap().is_some()); + assert!(incoming_call_b2.next().await.unwrap().is_some()); + + // User B accepts the call on one of the two connections, causing both connections + // to stop ringing. + active_call_b2 + .update(cx_b2, |call, cx| call.accept_incoming(cx)) + .await + .unwrap(); + assert!(incoming_call_b1.next().await.unwrap().is_none()); + assert!(incoming_call_b2.next().await.unwrap().is_none()); + + // User B hangs up, and user A calls them again. + active_call_b2.update(cx_b2, |call, cx| call.hang_up(cx).unwrap()); + deterministic.run_until_parked(); + active_call_a + .update(cx_a, |call, cx| { + call.invite(client_b1.user_id().unwrap(), None, cx) + }) + .await + .unwrap(); + assert!(incoming_call_b1.next().await.unwrap().is_some()); + assert!(incoming_call_b2.next().await.unwrap().is_some()); + + // User A cancels the call, causing both connections to stop ringing. + active_call_a + .update(cx_a, |call, cx| { + call.cancel_invite(client_b1.user_id().unwrap(), cx) + }) + .await + .unwrap(); + assert!(incoming_call_b1.next().await.unwrap().is_none()); + assert!(incoming_call_b2.next().await.unwrap().is_none()); +} + #[gpui::test(iterations = 10)] async fn test_share_project( deterministic: Arc, diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 46bf4bf3dc..16bb7cfefd 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -150,6 +150,7 @@ impl Server { .add_request_handler(Server::join_room) .add_message_handler(Server::leave_room) .add_request_handler(Server::call) + .add_request_handler(Server::cancel_call) .add_message_handler(Server::decline_call) .add_request_handler(Server::update_participant_location) .add_request_handler(Server::share_project) @@ -599,7 +600,7 @@ impl Server { let (room, recipient_connection_ids) = store.join_room(room_id, request.sender_id)?; for recipient_id in recipient_connection_ids { self.peer - .send(recipient_id, proto::CancelCall {}) + .send(recipient_id, proto::CallCanceled {}) .trace_err(); } response.send(proto::JoinRoomResponse { @@ -715,6 +716,26 @@ impl Server { Err(anyhow!("failed to ring call recipient"))? } + async fn cancel_call( + self: Arc, + request: TypedEnvelope, + response: Response, + ) -> Result<()> { + let mut store = self.store().await; + let (room, recipient_connection_ids) = store.cancel_call( + UserId::from_proto(request.payload.recipient_user_id), + request.sender_id, + )?; + for recipient_id in recipient_connection_ids { + self.peer + .send(recipient_id, proto::CallCanceled {}) + .trace_err(); + } + self.room_updated(room); + response.send(proto::Ack {})?; + Ok(()) + } + async fn decline_call( self: Arc, message: TypedEnvelope, @@ -723,7 +744,7 @@ impl Server { let (room, recipient_connection_ids) = store.call_declined(message.sender_id)?; for recipient_id in recipient_connection_ids { self.peer - .send(recipient_id, proto::CancelCall {}) + .send(recipient_id, proto::CallCanceled {}) .trace_err(); } self.room_updated(room); diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index a0d272ccc8..cb43c73674 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -585,6 +585,51 @@ impl Store { Ok(room) } + pub fn cancel_call( + &mut self, + recipient_user_id: UserId, + canceller_connection_id: ConnectionId, + ) -> Result<(&proto::Room, HashSet)> { + let canceller_user_id = self.user_id_for_connection(canceller_connection_id)?; + let canceller = self + .connected_users + .get(&canceller_user_id) + .ok_or_else(|| anyhow!("no such connection"))?; + let recipient = self + .connected_users + .get(&recipient_user_id) + .ok_or_else(|| anyhow!("no such connection"))?; + let canceller_active_call = canceller + .active_call + .as_ref() + .ok_or_else(|| anyhow!("no active call"))?; + let recipient_active_call = recipient + .active_call + .as_ref() + .ok_or_else(|| anyhow!("no active call for recipient"))?; + + anyhow::ensure!( + canceller_active_call.room_id == recipient_active_call.room_id, + "users are on different calls" + ); + anyhow::ensure!( + recipient_active_call.connection_id.is_none(), + "recipient has already answered" + ); + let room_id = recipient_active_call.room_id; + let room = self + .rooms + .get_mut(&room_id) + .ok_or_else(|| anyhow!("no such room"))?; + room.pending_user_ids + .retain(|user_id| UserId::from_proto(*user_id) != recipient_user_id); + + let recipient = self.connected_users.get_mut(&recipient_user_id).unwrap(); + recipient.active_call.take(); + + Ok((room, recipient.connection_ids.clone())) + } + pub fn call_declined( &mut self, recipient_connection_id: ConnectionId, diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 6c8ec72e86..f938acb7bc 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -18,7 +18,8 @@ message Envelope { LeaveRoom leave_room = 1002; Call call = 12; IncomingCall incoming_call = 1000; - CancelCall cancel_call = 1001; + CallCanceled call_canceled = 1001; + CancelCall cancel_call = 1004; DeclineCall decline_call = 13; UpdateParticipantLocation update_participant_location = 1003; RoomUpdated room_updated = 14; @@ -189,7 +190,11 @@ message IncomingCall { optional uint64 initial_project_id = 4; } -message CancelCall {} +message CallCanceled {} + +message CancelCall { + uint64 recipient_user_id = 1; +} message DeclineCall {} diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index 25e04e6645..7bfefc496a 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -84,6 +84,7 @@ messages!( (BufferReloaded, Foreground), (BufferSaved, Foreground), (Call, Foreground), + (CallCanceled, Foreground), (CancelCall, Foreground), (ChannelMessageSent, Foreground), (CopyProjectEntry, Foreground), @@ -183,6 +184,7 @@ request_messages!( ApplyCompletionAdditionalEditsResponse ), (Call, Ack), + (CancelCall, Ack), (CopyProjectEntry, ProjectEntryResponse), (CreateProjectEntry, ProjectEntryResponse), (CreateRoom, CreateRoomResponse), From baf6097b4904ab3a05e78b0b61a3f4f7834f2514 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 6 Oct 2022 15:17:02 +0200 Subject: [PATCH 165/314] Remove stale contacts panel reference --- styles/src/styleTree/app.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/styles/src/styleTree/app.ts b/styles/src/styleTree/app.ts index 1c0c81cfde..1b1aa26916 100644 --- a/styles/src/styleTree/app.ts +++ b/styles/src/styleTree/app.ts @@ -2,7 +2,6 @@ import Theme from "../themes/common/theme"; import chatPanel from "./chatPanel"; import { text } from "./components"; import contactFinder from "./contactFinder"; -import contactsPanel from "./contactsPanel"; import contactsPopover from "./contactsPopover"; import commandPalette from "./commandPalette"; import editor from "./editor"; @@ -37,7 +36,6 @@ export default function app(theme: Theme): Object { projectPanel: projectPanel(theme), chatPanel: chatPanel(theme), contactsPopover: contactsPopover(theme), - contactsPanel: contactsPanel(theme), contactFinder: contactFinder(theme), search: search(theme), breadcrumbs: { From 95e08edbb82d82f3b9d08715b5ecd4bcf78efcb1 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 6 Oct 2022 15:20:49 +0200 Subject: [PATCH 166/314] Always include room id in protos This is redundant, but it futures-proof the ability to talk about multiple rooms at any given time and feels safer in terms of race conditions. --- crates/call/src/call.rs | 22 +++++++++++++++++++--- crates/collab/src/rpc.rs | 4 +++- crates/collab/src/rpc/store.rs | 11 +++++++++-- crates/rpc/proto/zed.proto | 7 +++++-- 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 1f4f7633e1..8617b5391a 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -145,10 +145,19 @@ impl ActiveCall { recipient_user_id: u64, cx: &mut ModelContext, ) -> Task> { + let room_id = if let Some(room) = self.room() { + room.read(cx).id() + } else { + return Task::ready(Err(anyhow!("no active call"))); + }; + let client = self.client.clone(); cx.foreground().spawn(async move { client - .request(proto::CancelCall { recipient_user_id }) + .request(proto::CancelCall { + room_id, + recipient_user_id, + }) .await?; anyhow::Ok(()) }) @@ -178,8 +187,15 @@ impl ActiveCall { } pub fn decline_incoming(&mut self) -> Result<()> { - *self.incoming_call.0.borrow_mut() = None; - self.client.send(proto::DeclineCall {})?; + let call = self + .incoming_call + .0 + .borrow_mut() + .take() + .ok_or_else(|| anyhow!("no incoming call"))?; + self.client.send(proto::DeclineCall { + room_id: call.room_id, + })?; Ok(()) } diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 16bb7cfefd..50d1c82fc8 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -723,6 +723,7 @@ impl Server { ) -> Result<()> { let mut store = self.store().await; let (room, recipient_connection_ids) = store.cancel_call( + request.payload.room_id, UserId::from_proto(request.payload.recipient_user_id), request.sender_id, )?; @@ -741,7 +742,8 @@ impl Server { message: TypedEnvelope, ) -> Result<()> { let mut store = self.store().await; - let (room, recipient_connection_ids) = store.call_declined(message.sender_id)?; + let (room, recipient_connection_ids) = + store.decline_call(message.payload.room_id, message.sender_id)?; for recipient_id in recipient_connection_ids { self.peer .send(recipient_id, proto::CallCanceled {}) diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index cb43c73674..a9ae91aba0 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -587,6 +587,7 @@ impl Store { pub fn cancel_call( &mut self, + room_id: RoomId, recipient_user_id: UserId, canceller_connection_id: ConnectionId, ) -> Result<(&proto::Room, HashSet)> { @@ -609,7 +610,11 @@ impl Store { .ok_or_else(|| anyhow!("no active call for recipient"))?; anyhow::ensure!( - canceller_active_call.room_id == recipient_active_call.room_id, + canceller_active_call.room_id == room_id, + "users are on different calls" + ); + anyhow::ensure!( + recipient_active_call.room_id == room_id, "users are on different calls" ); anyhow::ensure!( @@ -630,8 +635,9 @@ impl Store { Ok((room, recipient.connection_ids.clone())) } - pub fn call_declined( + pub fn decline_call( &mut self, + room_id: RoomId, recipient_connection_id: ConnectionId, ) -> Result<(&proto::Room, Vec)> { let recipient_user_id = self.user_id_for_connection(recipient_connection_id)?; @@ -640,6 +646,7 @@ impl Store { .get_mut(&recipient_user_id) .ok_or_else(|| anyhow!("no such connection"))?; if let Some(active_call) = recipient.active_call.take() { + anyhow::ensure!(active_call.room_id == room_id, "no such room"); let recipient_connection_ids = self .connection_ids_for_user(recipient_user_id) .collect::>(); diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index f938acb7bc..334bcfbf90 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -193,10 +193,13 @@ message IncomingCall { message CallCanceled {} message CancelCall { - uint64 recipient_user_id = 1; + uint64 room_id = 1; + uint64 recipient_user_id = 2; } -message DeclineCall {} +message DeclineCall { + uint64 room_id = 1; +} message UpdateParticipantLocation { uint64 room_id = 1; From 9f81699e0114da7a6818d2e3a00eacfa7dc3b48d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 6 Oct 2022 16:10:45 +0200 Subject: [PATCH 167/314] WIP: start on menu bar extra --- crates/collab_ui/src/active_call_popover.rs | 40 +++++++ crates/collab_ui/src/collab_ui.rs | 5 +- crates/collab_ui/src/menu_bar_extra.rs | 110 ++++++++++++++++++++ crates/gpui/src/app.rs | 4 + 4 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 crates/collab_ui/src/active_call_popover.rs create mode 100644 crates/collab_ui/src/menu_bar_extra.rs diff --git a/crates/collab_ui/src/active_call_popover.rs b/crates/collab_ui/src/active_call_popover.rs new file mode 100644 index 0000000000..01a4e4721d --- /dev/null +++ b/crates/collab_ui/src/active_call_popover.rs @@ -0,0 +1,40 @@ +use gpui::{color::Color, elements::*, Entity, RenderContext, View, ViewContext}; + +pub enum Event { + Deactivated, +} + +pub struct ActiveCallPopover { + _subscription: gpui::Subscription, +} + +impl Entity for ActiveCallPopover { + type Event = Event; +} + +impl View for ActiveCallPopover { + fn ui_name() -> &'static str { + "ActiveCallPopover" + } + + fn render(&mut self, _: &mut RenderContext) -> ElementBox { + Empty::new() + .contained() + .with_background_color(Color::red()) + .boxed() + } +} + +impl ActiveCallPopover { + pub fn new(cx: &mut ViewContext) -> Self { + Self { + _subscription: cx.observe_window_activation(Self::window_activation_changed), + } + } + + fn window_activation_changed(&mut self, is_active: bool, cx: &mut ViewContext) { + if !is_active { + cx.emit(Event::Deactivated); + } + } +} diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 438a41ae7d..421258114e 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -1,8 +1,10 @@ +mod active_call_popover; mod collab_titlebar_item; mod contact_finder; mod contact_notification; mod contacts_popover; mod incoming_call_notification; +mod menu_bar_extra; mod notifications; mod project_shared_notification; @@ -14,11 +16,12 @@ use std::sync::Arc; use workspace::{AppState, JoinProject, ToggleFollow, Workspace}; pub fn init(app_state: Arc, cx: &mut MutableAppContext) { + collab_titlebar_item::init(cx); contact_notification::init(cx); contact_finder::init(cx); contacts_popover::init(cx); - collab_titlebar_item::init(cx); incoming_call_notification::init(cx); + menu_bar_extra::init(cx); project_shared_notification::init(cx); cx.add_global_action(move |action: &JoinProject, cx| { diff --git a/crates/collab_ui/src/menu_bar_extra.rs b/crates/collab_ui/src/menu_bar_extra.rs new file mode 100644 index 0000000000..8b2baa53eb --- /dev/null +++ b/crates/collab_ui/src/menu_bar_extra.rs @@ -0,0 +1,110 @@ +use crate::active_call_popover::{self, ActiveCallPopover}; +use call::ActiveCall; +use gpui::{ + actions, + color::Color, + elements::*, + geometry::{rect::RectF, vector::vec2f}, + Appearance, Entity, MouseButton, MutableAppContext, RenderContext, View, ViewContext, + ViewHandle, WindowKind, +}; + +actions!(menu_bar_extra, [ToggleActiveCallPopover]); + +pub fn init(cx: &mut MutableAppContext) { + cx.add_action(MenuBarExtra::toggle_active_call_popover); + + let mut status_bar_item_id = None; + cx.observe(&ActiveCall::global(cx), move |call, cx| { + if let Some(status_bar_item_id) = status_bar_item_id.take() { + cx.remove_status_bar_item(status_bar_item_id); + } + + if call.read(cx).room().is_some() { + let (id, _) = cx.add_status_bar_item(|_| MenuBarExtra::new()); + status_bar_item_id = Some(id); + } + }) + .detach(); +} + +struct MenuBarExtra { + popover: Option>, +} + +impl Entity for MenuBarExtra { + type Event = (); +} + +impl View for MenuBarExtra { + fn ui_name() -> &'static str { + "MenuBarExtra" + } + + fn render(&mut self, cx: &mut RenderContext) -> ElementBox { + let color = match cx.appearance { + Appearance::Light | Appearance::VibrantLight => Color::black(), + Appearance::Dark | Appearance::VibrantDark => Color::white(), + }; + MouseEventHandler::::new(0, cx, |_, _| { + Svg::new("icons/zed_22.svg") + .with_color(color) + .aligned() + .boxed() + }) + .on_click(MouseButton::Left, |_, cx| { + cx.dispatch_action(ToggleActiveCallPopover); + }) + .boxed() + } +} + +impl MenuBarExtra { + fn new() -> Self { + Self { popover: None } + } + + fn toggle_active_call_popover( + &mut self, + _: &ToggleActiveCallPopover, + cx: &mut ViewContext, + ) { + match self.popover.take() { + Some(popover) => { + cx.remove_window(popover.window_id()); + } + None => { + let window_bounds = cx.window_bounds(); + let size = vec2f(360., 460.); + let origin = window_bounds.lower_left() + + vec2f(window_bounds.width() / 2. - size.x() / 2., 0.); + let (_, popover) = cx.add_window( + gpui::WindowOptions { + bounds: gpui::WindowBounds::Fixed(RectF::new(origin, size)), + titlebar: None, + center: false, + kind: WindowKind::PopUp, + is_movable: false, + }, + |cx| ActiveCallPopover::new(cx), + ); + cx.subscribe(&popover, Self::on_popover_event).detach(); + self.popover = Some(popover); + } + } + } + + fn on_popover_event( + &mut self, + popover: ViewHandle, + event: &active_call_popover::Event, + cx: &mut ViewContext, + ) { + match event { + active_call_popover::Event::Deactivated => { + self.popover.take(); + cx.remove_window(popover.window_id()); + } + } + } +} diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 668071d046..f55dd2b464 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1990,6 +1990,10 @@ impl MutableAppContext { }) } + pub fn remove_status_bar_item(&mut self, id: usize) { + self.remove_window(id); + } + fn register_platform_window( &mut self, window_id: usize, From 771215d254c7461fa2cc5d7fc0b6b6e674106814 Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 5 Oct 2022 16:28:01 -0400 Subject: [PATCH 168/314] Reload git index on file events to catch new contents --- crates/editor/src/element.rs | 4 +--- crates/git/src/repository.rs | 16 +++++++++++++--- crates/project/src/project.rs | 2 +- crates/project/src/worktree.rs | 3 ++- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 2b93255972..acf2e5887c 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -630,9 +630,7 @@ impl EditorElement { let new_hunk = get_hunk(diff_layout.buffer_row, &layout.diff_hunks); let (is_ending, is_starting) = match (diff_layout.last_diff, new_hunk) { - (Some(old_hunk), Some(new_hunk)) if new_hunk == old_hunk => { - (false, false) - } + (Some(old_hunk), Some(new_hunk)) if new_hunk == old_hunk => (false, false), (a, b) => (a.is_some(), b.is_some()), }; diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs index 67e93416ae..ce881d2b0f 100644 --- a/crates/git/src/repository.rs +++ b/crates/git/src/repository.rs @@ -10,12 +10,20 @@ pub use git2::Repository as LibGitRepository; #[async_trait::async_trait] pub trait GitRepository: Send { - fn load_index(&self, relative_file_path: &Path) -> Option; + fn reload_index(&self); + + fn load_index_text(&self, relative_file_path: &Path) -> Option; } #[async_trait::async_trait] impl GitRepository for LibGitRepository { - fn load_index(&self, relative_file_path: &Path) -> Option { + fn reload_index(&self) { + if let Ok(mut index) = self.index() { + _ = index.read(false); + } + } + + fn load_index_text(&self, relative_file_path: &Path) -> Option { fn logic(repo: &LibGitRepository, relative_file_path: &Path) -> Result> { const STAGE_NORMAL: i32 = 0; let index = repo.index()?; @@ -54,7 +62,9 @@ impl FakeGitRepository { #[async_trait::async_trait] impl GitRepository for FakeGitRepository { - fn load_index(&self, path: &Path) -> Option { + fn reload_index(&self) {} + + fn load_index_text(&self, path: &Path) -> Option { let state = self.state.lock(); state.index_contents.get(path).cloned() } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 54fdb269be..99d74f67db 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4684,7 +4684,7 @@ impl Project { cx.spawn(|_, mut cx| async move { let diff_base = cx .background() - .spawn(async move { repo.repo.lock().load_index(&relative_repo) }) + .spawn(async move { repo.repo.lock().load_index_text(&relative_repo) }) .await; let buffer_id = buffer.update(&mut cx, |buffer, cx| { diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 5348f9785f..968c2d4bc7 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -671,7 +671,7 @@ impl LocalWorktree { if let Ok(repo_relative) = path.strip_prefix(repo.content_path) { let repo_relative = repo_relative.to_owned(); cx.background() - .spawn(async move { repo.repo.lock().load_index(&repo_relative) }) + .spawn(async move { repo.repo.lock().load_index_text(&repo_relative) }) .await } else { None @@ -2505,6 +2505,7 @@ impl BackgroundScanner { let scan_id = snapshot.scan_id; if let Some(repo) = snapshot.in_dot_git(&path) { + repo.repo.lock().reload_index(); repo.scan_id = scan_id; } From fe7a39ba5c36a3cf6e4bf5f207dd4b1307df15b9 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 6 Oct 2022 11:54:28 -0700 Subject: [PATCH 169/314] Apply buffer diff edits as a single batch --- crates/language/src/buffer.rs | 55 +++++++++++++++-------------------- 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 294ecd5cd2..db9aa029f2 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -300,10 +300,8 @@ pub struct Chunk<'a> { pub struct Diff { base_version: clock::Global, - new_text: Arc, - changes: Vec<(ChangeTag, usize)>, line_ending: LineEnding, - start_offset: usize, + edits: Vec<(Range, Arc)>, } #[derive(Clone, Copy)] @@ -1084,16 +1082,30 @@ impl Buffer { let old_text = old_text.to_string(); let line_ending = LineEnding::detect(&new_text); LineEnding::normalize(&mut new_text); - let changes = TextDiff::from_chars(old_text.as_str(), new_text.as_str()) - .iter_all_changes() - .map(|c| (c.tag(), c.value().len())) - .collect::>(); + let diff = TextDiff::from_chars(old_text.as_str(), new_text.as_str()); + let mut edits = Vec::new(); + let mut offset = 0; + let empty: Arc = "".into(); + for change in diff.iter_all_changes() { + let value = change.value(); + let end_offset = offset + value.len(); + match change.tag() { + ChangeTag::Equal => { + offset = end_offset; + } + ChangeTag::Delete => { + edits.push((offset..end_offset, empty.clone())); + offset = end_offset; + } + ChangeTag::Insert => { + edits.push((offset..offset, value.into())); + } + } + } Diff { base_version, - new_text: new_text.into(), - changes, line_ending, - start_offset: 0, + edits, } }) } @@ -1103,28 +1115,7 @@ impl Buffer { self.finalize_last_transaction(); self.start_transaction(); self.text.set_line_ending(diff.line_ending); - let mut offset = diff.start_offset; - for (tag, len) in diff.changes { - let range = offset..(offset + len); - match tag { - ChangeTag::Equal => offset += len, - ChangeTag::Delete => { - self.edit([(range, "")], None, cx); - } - ChangeTag::Insert => { - self.edit( - [( - offset..offset, - &diff.new_text[range.start - diff.start_offset - ..range.end - diff.start_offset], - )], - None, - cx, - ); - offset += len; - } - } - } + self.edit(diff.edits, None, cx); if self.end_transaction(cx).is_some() { self.finalize_last_transaction() } else { From b6525e916461d6d1a0c45f2f4282f342de97dbd4 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 6 Oct 2022 13:32:34 -0700 Subject: [PATCH 170/314] Extract editor tests to their own file --- crates/editor/src/editor.rs | 4891 +---------------------------- crates/editor/src/editor_tests.rs | 4881 ++++++++++++++++++++++++++++ 2 files changed, 4883 insertions(+), 4889 deletions(-) create mode 100644 crates/editor/src/editor_tests.rs diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 4e18d04889..1abe65d482 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -9,6 +9,8 @@ pub mod movement; mod multi_buffer; pub mod selections_collection; +#[cfg(test)] +mod editor_tests; #[cfg(any(test, feature = "test-support"))] pub mod test; @@ -6805,4895 +6807,6 @@ pub fn styled_runs_for_code_label<'a>( }) } -#[cfg(test)] -mod tests { - use crate::test::{ - assert_text_with_selections, build_editor, select_ranges, EditorLspTestContext, - EditorTestContext, - }; - - use super::*; - use futures::StreamExt; - use gpui::{ - geometry::rect::RectF, - platform::{WindowBounds, WindowOptions}, - }; - use indoc::indoc; - use language::{FakeLspAdapter, LanguageConfig, LanguageRegistry}; - use project::FakeFs; - use settings::EditorSettings; - use std::{cell::RefCell, rc::Rc, time::Instant}; - use text::Point; - use unindent::Unindent; - use util::{ - assert_set_eq, - test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker}, - }; - use workspace::{FollowableItem, ItemHandle, NavigationEntry, Pane}; - - #[gpui::test] - fn test_edit_events(cx: &mut MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx)); - - let events = Rc::new(RefCell::new(Vec::new())); - let (_, editor1) = cx.add_window(Default::default(), { - let events = events.clone(); - |cx| { - cx.subscribe(&cx.handle(), move |_, _, event, _| { - if matches!( - event, - Event::Edited | Event::BufferEdited | Event::DirtyChanged - ) { - events.borrow_mut().push(("editor1", *event)); - } - }) - .detach(); - Editor::for_buffer(buffer.clone(), None, cx) - } - }); - let (_, editor2) = cx.add_window(Default::default(), { - let events = events.clone(); - |cx| { - cx.subscribe(&cx.handle(), move |_, _, event, _| { - if matches!( - event, - Event::Edited | Event::BufferEdited | Event::DirtyChanged - ) { - events.borrow_mut().push(("editor2", *event)); - } - }) - .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", Event::Edited), - ("editor1", Event::BufferEdited), - ("editor2", Event::BufferEdited), - ("editor1", Event::DirtyChanged), - ("editor2", Event::DirtyChanged) - ] - ); - - // Mutating editor 2 will emit an `Edited` event only for that editor. - editor2.update(cx, |editor, cx| editor.delete(&Delete, cx)); - assert_eq!( - mem::take(&mut *events.borrow_mut()), - [ - ("editor2", Event::Edited), - ("editor1", Event::BufferEdited), - ("editor2", Event::BufferEdited), - ] - ); - - // Undoing on editor 1 will emit an `Edited` event only for that editor. - editor1.update(cx, |editor, cx| editor.undo(&Undo, cx)); - assert_eq!( - mem::take(&mut *events.borrow_mut()), - [ - ("editor1", Event::Edited), - ("editor1", Event::BufferEdited), - ("editor2", Event::BufferEdited), - ("editor1", Event::DirtyChanged), - ("editor2", Event::DirtyChanged), - ] - ); - - // Redoing on editor 1 will emit an `Edited` event only for that editor. - editor1.update(cx, |editor, cx| editor.redo(&Redo, cx)); - assert_eq!( - mem::take(&mut *events.borrow_mut()), - [ - ("editor1", Event::Edited), - ("editor1", Event::BufferEdited), - ("editor2", Event::BufferEdited), - ("editor1", Event::DirtyChanged), - ("editor2", Event::DirtyChanged), - ] - ); - - // Undoing on editor 2 will emit an `Edited` event only for that editor. - editor2.update(cx, |editor, cx| editor.undo(&Undo, cx)); - assert_eq!( - mem::take(&mut *events.borrow_mut()), - [ - ("editor2", Event::Edited), - ("editor1", Event::BufferEdited), - ("editor2", Event::BufferEdited), - ("editor1", Event::DirtyChanged), - ("editor2", Event::DirtyChanged), - ] - ); - - // Redoing on editor 2 will emit an `Edited` event only for that editor. - editor2.update(cx, |editor, cx| editor.redo(&Redo, cx)); - assert_eq!( - mem::take(&mut *events.borrow_mut()), - [ - ("editor2", Event::Edited), - ("editor1", Event::BufferEdited), - ("editor2", Event::BufferEdited), - ("editor1", Event::DirtyChanged), - ("editor2", Event::DirtyChanged), - ] - ); - - // No event is emitted when the mutation is a no-op. - editor2.update(cx, |editor, cx| { - editor.change_selections(None, cx, |s| s.select_ranges([0..0])); - - editor.backspace(&Backspace, cx); - }); - assert_eq!(mem::take(&mut *events.borrow_mut()), []); - } - - #[gpui::test] - fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) { - cx.set_global(Settings::test(cx)); - let mut now = Instant::now(); - let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx)); - let group_interval = buffer.read(cx).transaction_group_interval(); - let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let (_, editor) = cx.add_window(Default::default(), |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 MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = cx.add_model(|cx| { - let mut buffer = language::Buffer::new(0, "abcde", cx); - // Ensure automatic grouping doesn't occur. - buffer.set_group_interval(Duration::ZERO); - buffer - }); - - let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - cx.add_window(Default::default(), |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 gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - - let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx); - let (_, editor) = cx.add_window(Default::default(), |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)), - [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)] - ); - - editor.update(cx, |view, cx| { - view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx); - }); - - assert_eq!( - editor.update(cx, |view, cx| view.selections.display_ranges(cx)), - [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] - ); - - editor.update(cx, |view, cx| { - view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx); - }); - - assert_eq!( - editor.update(cx, |view, cx| view.selections.display_ranges(cx)), - [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)] - ); - - editor.update(cx, |view, cx| { - view.end_selection(cx); - view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx); - }); - - assert_eq!( - editor.update(cx, |view, cx| view.selections.display_ranges(cx)), - [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)] - ); - - editor.update(cx, |view, cx| { - view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx); - view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx); - }); - - assert_eq!( - editor.update(cx, |view, cx| view.selections.display_ranges(cx)), - [ - DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1), - DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0) - ] - ); - - editor.update(cx, |view, cx| { - view.end_selection(cx); - }); - - assert_eq!( - editor.update(cx, |view, cx| view.selections.display_ranges(cx)), - [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)] - ); - } - - #[gpui::test] - fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx); - let (_, view) = cx.add_window(Default::default(), |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, Vector2F::zero(), cx); - assert_eq!( - view.selections.display_ranges(cx), - [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] - ); - }); - - view.update(cx, |view, cx| { - view.cancel(&Cancel, cx); - view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx); - assert_eq!( - view.selections.display_ranges(cx), - [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] - ); - }); - } - - #[gpui::test] - fn test_clone(cx: &mut gpui::MutableAppContext) { - let (text, selection_ranges) = marked_text_ranges( - indoc! {" - one - two - threeˇ - four - fiveˇ - "}, - true, - ); - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple(&text, cx); - - let (_, editor) = cx.add_window(Default::default(), |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), - ], - cx, - ); - }); - - let (_, cloned_editor) = editor.update(cx, |editor, cx| { - cx.add_window(Default::default(), |cx| editor.clone(cx)) - }); - - let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)); - let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)); - - assert_eq!( - cloned_editor.update(cx, |e, cx| e.display_text(cx)), - editor.update(cx, |e, cx| e.display_text(cx)) - ); - assert_eq!( - cloned_snapshot - .folds_in_range(0..text.len()) - .collect::>(), - snapshot.folds_in_range(0..text.len()).collect::>(), - ); - assert_set_eq!( - cloned_editor.read(cx).selections.ranges::(cx), - editor.read(cx).selections.ranges(cx) - ); - assert_set_eq!( - cloned_editor.update(cx, |e, cx| e.selections.display_ranges(cx)), - editor.update(cx, |e, cx| e.selections.display_ranges(cx)) - ); - } - - #[gpui::test] - fn test_navigation_history(cx: &mut gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - use workspace::Item; - let (_, pane) = cx.add_window(Default::default(), |cx| Pane::new(None, cx)); - let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx); - - cx.add_view(&pane, |cx| { - let mut editor = build_editor(buffer.clone(), cx); - let handle = cx.handle(); - editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle))); - - fn pop_history( - editor: &mut Editor, - cx: &mut MutableAppContext, - ) -> Option { - editor.nav_history.as_mut().unwrap().pop_backward(cx) - } - - // Move the cursor a small distance. - // Nothing is added to the navigation history. - editor.change_selections(None, cx, |s| { - s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]) - }); - editor.change_selections(None, cx, |s| { - s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]) - }); - assert!(pop_history(&mut editor, cx).is_none()); - - // Move the cursor a large distance. - // The history can jump back to the previous position. - editor.change_selections(None, cx, |s| { - s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)]) - }); - let nav_entry = pop_history(&mut editor, cx).unwrap(); - editor.navigate(nav_entry.data.unwrap(), cx); - assert_eq!(nav_entry.item.id(), cx.view_id()); - assert_eq!( - editor.selections.display_ranges(cx), - &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)] - ); - assert!(pop_history(&mut editor, cx).is_none()); - - // Move the cursor a small distance via the mouse. - // Nothing is added to the navigation history. - editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx); - editor.end_selection(cx); - assert_eq!( - editor.selections.display_ranges(cx), - &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)] - ); - assert!(pop_history(&mut editor, cx).is_none()); - - // Move the cursor a large distance via the mouse. - // The history can jump back to the previous position. - editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx); - editor.end_selection(cx); - assert_eq!( - editor.selections.display_ranges(cx), - &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)] - ); - let nav_entry = pop_history(&mut editor, cx).unwrap(); - editor.navigate(nav_entry.data.unwrap(), cx); - assert_eq!(nav_entry.item.id(), cx.view_id()); - assert_eq!( - editor.selections.display_ranges(cx), - &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)] - ); - assert!(pop_history(&mut editor, cx).is_none()); - - // Set scroll position to check later - editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx); - let original_scroll_position = editor.scroll_position; - let original_scroll_top_anchor = editor.scroll_top_anchor.clone(); - - // Jump to the end of the document and adjust scroll - editor.move_to_end(&MoveToEnd, cx); - editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx); - assert_ne!(editor.scroll_position, original_scroll_position); - assert_ne!(editor.scroll_top_anchor, original_scroll_top_anchor); - - let nav_entry = pop_history(&mut editor, cx).unwrap(); - editor.navigate(nav_entry.data.unwrap(), cx); - assert_eq!(editor.scroll_position, original_scroll_position); - assert_eq!(editor.scroll_top_anchor, original_scroll_top_anchor); - - // Ensure we don't panic when navigation data contains invalid anchors *and* points. - let mut invalid_anchor = editor.scroll_top_anchor.clone(); - invalid_anchor.text_anchor.buffer_id = Some(999); - let invalid_point = Point::new(9999, 0); - editor.navigate( - Box::new(NavigationData { - cursor_anchor: invalid_anchor.clone(), - cursor_position: invalid_point, - scroll_top_anchor: invalid_anchor, - scroll_top_row: invalid_point.row, - scroll_position: Default::default(), - }), - cx, - ); - assert_eq!( - editor.selections.display_ranges(cx), - &[editor.max_point(cx)..editor.max_point(cx)] - ); - assert_eq!( - editor.scroll_position(cx), - vec2f(0., editor.max_point(cx).row() as f32) - ); - - editor - }); - } - - #[gpui::test] - fn test_cancel(cx: &mut gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx); - let (_, view) = cx.add_window(Default::default(), |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, Vector2F::zero(), cx); - view.end_selection(cx); - - view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx); - view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx); - view.end_selection(cx); - assert_eq!( - view.selections.display_ranges(cx), - [ - DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), - DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1), - ] - ); - }); - - view.update(cx, |view, cx| { - view.cancel(&Cancel, cx); - assert_eq!( - view.selections.display_ranges(cx), - [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)] - ); - }); - - view.update(cx, |view, cx| { - view.cancel(&Cancel, cx); - assert_eq!( - view.selections.display_ranges(cx), - [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)] - ); - }); - } - - #[gpui::test] - fn test_fold(cx: &mut gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple( - &" - impl Foo { - // Hello! - - fn a() { - 1 - } - - fn b() { - 2 - } - - fn c() { - 3 - } - } - " - .unindent(), - cx, - ); - let (_, view) = cx.add_window(Default::default(), |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), buffer.read(cx).read(cx).text()); - }); - } - - #[gpui::test] - fn test_move_cursor(cx: &mut gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx); - let (_, view) = cx.add_window(Default::default(), |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 gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx); - let (_, view) = cx.add_window(Default::default(), |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), - ], - cx, - ); - assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\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…".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_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())] - ); - view.move_left(&MoveLeft, cx); - assert_eq!( - view.selections.display_ranges(cx), - &[empty_range(0, "ⓐ".len())] - ); - }); - } - - #[gpui::test] - fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx); - let (_, view) = cx.add_window(Default::default(), |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 gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple("abc\n def", cx); - let (_, view) = cx.add_window(Default::default(), |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 gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx); - let (_, view) = cx.add_window(Default::default(), |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); - }); - } - - #[gpui::test] - fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx); - let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); - - view.update(cx, |view, cx| { - view.set_wrap_width(Some(140.), cx); - assert_eq!( - view.display_text(cx), - "use one::{\n two::three::\n four::five\n};" - ); - - view.change_selections(None, cx, |s| { - s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]); - }); - - view.move_to_next_word_end(&MoveToNextWordEnd, cx); - assert_eq!( - view.selections.display_ranges(cx), - &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)] - ); - - view.move_to_next_word_end(&MoveToNextWordEnd, cx); - assert_eq!( - view.selections.display_ranges(cx), - &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)] - ); - - view.move_to_next_word_end(&MoveToNextWordEnd, cx); - assert_eq!( - view.selections.display_ranges(cx), - &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)] - ); - - view.move_to_next_word_end(&MoveToNextWordEnd, cx); - assert_eq!( - view.selections.display_ranges(cx), - &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)] - ); - - view.move_to_previous_word_start(&MoveToPreviousWordStart, cx); - assert_eq!( - view.selections.display_ranges(cx), - &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)] - ); - - view.move_to_previous_word_start(&MoveToPreviousWordStart, cx); - assert_eq!( - view.selections.display_ranges(cx), - &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)] - ); - }); - } - - #[gpui::test] - async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) { - let mut cx = EditorTestContext::new(cx); - 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 gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple("one two three four", cx); - let (_, view) = cx.add_window(Default::default(), |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!(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!(buffer.read(cx).read(cx).text(), "e t te our"); - } - - #[gpui::test] - fn test_newline(cx: &mut gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx); - let (_, view) = cx.add_window(Default::default(), |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 gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple( - " - a - b( - X - ) - c( - X - ) - " - .unindent() - .as_str(), - cx, - ); - - let (_, editor) = cx.add_window(Default::default(), |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 - }); - - // Edit the buffer directly, deleting ranges surrounding the editor's selections - 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() - ); - }); - - editor.update(cx, |editor, cx| { - 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_below(cx: &mut gpui::TestAppContext) { - let mut cx = EditorTestContext::new(cx); - cx.update(|cx| { - cx.update_global::(|settings, _| { - settings.editor_overrides.tab_size = Some(NonZeroU32::new(4).unwrap()); - }); - }); - - 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)); - - 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] - fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx); - let (_, editor) = cx.add_window(Default::default(), |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 - }); - - // Edit the buffer directly, deleting ranges surrounding the editor's selections - 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()); - }); - - editor.update(cx, |editor, cx| { - 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) { - let mut cx = EditorTestContext::new(cx); - cx.update(|cx| { - cx.update_global::(|settings, _| { - settings.editor_overrides.tab_size = Some(NonZeroU32::new(3).unwrap()); - }); - }); - 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_on_blank_line_auto_indents(cx: &mut gpui::TestAppContext) { - let mut cx = EditorTestContext::new(cx); - 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_indent_outdent(cx: &mut gpui::TestAppContext) { - let mut cx = EditorTestContext::new(cx); - - 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) { - let mut cx = EditorTestContext::new(cx); - cx.update(|cx| { - cx.update_global::(|settings, _| { - settings.editor_overrides.hard_tabs = Some(true); - }); - }); - - // 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 gpui::MutableAppContext) { - cx.set_global( - Settings::test(cx) - .with_language_defaults( - "TOML", - EditorSettings { - tab_size: Some(2.try_into().unwrap()), - ..Default::default() - }, - ) - .with_language_defaults( - "Rust", - EditorSettings { - tab_size: Some(4.try_into().unwrap()), - ..Default::default() - }, - ), - ); - let toml_language = Arc::new(Language::new( - LanguageConfig { - name: "TOML".into(), - ..Default::default() - }, - None, - )); - let rust_language = Arc::new(Language::new( - LanguageConfig { - name: "Rust".into(), - ..Default::default() - }, - None, - )); - - let toml_buffer = cx - .add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx)); - let rust_buffer = cx.add_model(|cx| { - Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx) - }); - let multibuffer = cx.add_model(|cx| { - let mut multibuffer = MultiBuffer::new(0); - multibuffer.push_excerpts( - toml_buffer.clone(), - [ExcerptRange { - context: Point::new(0, 0)..Point::new(2, 0), - primary: None, - }], - cx, - ); - multibuffer.push_excerpts( - rust_buffer.clone(), - [ExcerptRange { - context: Point::new(0, 0)..Point::new(1, 0), - primary: None, - }], - cx, - ); - multibuffer - }); - - cx.add_window(Default::default(), |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) { - let mut cx = EditorTestContext::new(cx); - - // 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) { - let mut cx = EditorTestContext::new(cx); - - 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 gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); - let (_, view) = cx.add_window(Default::default(), |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) - ] - ); - }); - - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); - let (_, view) = cx.add_window(Default::default(), |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)] - ); - }); - } - - #[gpui::test] - fn test_duplicate_line(cx: &mut gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); - let (_, view) = cx.add_window(Default::default(), |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 buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); - let (_, view) = cx.add_window(Default::default(), |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 gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx); - let (_, view) = cx.add_window(Default::default(), |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), - ], - 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 gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx); - let snapshot = buffer.read(cx).snapshot(cx); - let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); - editor.update(cx, |editor, cx| { - editor.insert_blocks( - [BlockProperties { - style: BlockStyle::Fixed, - position: snapshot.anchor_after(Point::new(2, 0)), - disposition: BlockDisposition::Below, - height: 1, - render: Arc::new(|_| Empty::new().boxed()), - }], - cx, - ); - editor.change_selections(None, cx, |s| { - s.select_ranges([Point::new(2, 0)..Point::new(2, 0)]) - }); - editor.move_line_down(&MoveLineDown, cx); - }); - } - - #[gpui::test] - fn test_transpose(cx: &mut gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - - _ = cx - .add_window(Default::default(), |cx| { - let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx); - - editor.change_selections(None, cx, |s| s.select_ranges([1..1])); - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "bac"); - assert_eq!(editor.selections.ranges(cx), [2..2]); - - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "bca"); - assert_eq!(editor.selections.ranges(cx), [3..3]); - - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "bac"); - assert_eq!(editor.selections.ranges(cx), [3..3]); - - editor - }) - .1; - - _ = cx - .add_window(Default::default(), |cx| { - let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx); - - editor.change_selections(None, cx, |s| s.select_ranges([3..3])); - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "acb\nde"); - assert_eq!(editor.selections.ranges(cx), [3..3]); - - editor.change_selections(None, cx, |s| s.select_ranges([4..4])); - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "acbd\ne"); - assert_eq!(editor.selections.ranges(cx), [5..5]); - - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "acbde\n"); - assert_eq!(editor.selections.ranges(cx), [6..6]); - - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "acbd\ne"); - assert_eq!(editor.selections.ranges(cx), [6..6]); - - editor - }) - .1; - - _ = cx - .add_window(Default::default(), |cx| { - let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx); - - editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4])); - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "bacd\ne"); - assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]); - - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "bcade\n"); - assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]); - - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "bcda\ne"); - assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]); - - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "bcade\n"); - assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]); - - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "bcaed\n"); - assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]); - - editor - }) - .1; - - _ = cx - .add_window(Default::default(), |cx| { - let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx); - - editor.change_selections(None, cx, |s| s.select_ranges([4..4])); - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "🏀🍐✋"); - assert_eq!(editor.selections.ranges(cx), [8..8]); - - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "🏀✋🍐"); - assert_eq!(editor.selections.ranges(cx), [11..11]); - - editor.transpose(&Default::default(), cx); - assert_eq!(editor.text(cx), "🏀🍐✋"); - assert_eq!(editor.selections.ranges(cx), [11..11]); - - editor - }) - .1; - } - - #[gpui::test] - async fn test_clipboard(cx: &mut gpui::TestAppContext) { - let mut cx = EditorTestContext::new(cx); - - 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(indoc! {" - ( one✅ - three - five ) ˇtwo one✅ four three six five ( one✅ - three - five ) ˇ"}); - - // Cut with three selections, one of which is full-line. - cx.set_state(indoc! {" - 1«2ˇ»3 - 4ˇ567 - «8ˇ»9"}); - cx.update_editor(|e, cx| e.cut(&Cut, cx)); - cx.assert_editor_state(indoc! {" - 1ˇ3 - ˇ9"}); - - // Paste with three selections, noticing how the copied selection that was full-line - // gets inserted before the second cursor. - cx.set_state(indoc! {" - 1ˇ3 - 9ˇ - «oˇ»ne"}); - cx.update_editor(|e, cx| e.paste(&Paste, cx)); - cx.assert_editor_state(indoc! {" - 12ˇ3 - 4567 - 9ˇ - 8ˇne"}); - - // Copy with a single cursor only, which writes the whole line into the clipboard. - cx.set_state(indoc! {" - The quick brown - fox juˇmps over - the lazy dog"}); - cx.update_editor(|e, cx| e.copy(&Copy, cx)); - cx.cx.assert_clipboard_content(Some("fox jumps over\n")); - - // Paste with three selections, noticing how the copied full-line selection is inserted - // before the empty selections but replaces the selection that is non-empty. - cx.set_state(indoc! {" - Tˇhe quick brown - «foˇ»x jumps over - tˇhe lazy dog"}); - cx.update_editor(|e, cx| e.paste(&Paste, cx)); - cx.assert_editor_state(indoc! {" - fox jumps over - Tˇhe quick brown - fox jumps over - ˇx jumps over - fox jumps over - tˇhe lazy dog"}); - } - - #[gpui::test] - async fn test_paste_multiline(cx: &mut gpui::TestAppContext) { - let mut cx = EditorTestContext::new(cx); - 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 gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx); - let (_, view) = cx.add_window(Default::default(), |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 gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx); - let (_, view) = cx.add_window(Default::default(), |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 gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx); - let (_, view) = cx.add_window(Default::default(), |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), - ], - cx, - ); - view.change_selections(None, cx, |s| { - s.select_display_ranges([ - DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1), - DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), - DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), - DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4), - ]) - }); - assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i"); - }); - - view.update(cx, |view, cx| { - view.split_selection_into_lines(&SplitSelectionIntoLines, cx); - assert_eq!( - view.display_text(cx), - "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i" - ); - assert_eq!( - view.selections.display_ranges(cx), - [ - DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), - DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), - DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0), - DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4) - ] - ); - }); - - view.update(cx, |view, cx| { - view.change_selections(None, cx, |s| { - s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)]) - }); - view.split_selection_into_lines(&SplitSelectionIntoLines, cx); - assert_eq!( - view.display_text(cx), - "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii" - ); - assert_eq!( - view.selections.display_ranges(cx), - [ - DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5), - DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5), - DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5), - DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5), - DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5), - DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5), - DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5), - DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0) - ] - ); - }); - } - - #[gpui::test] - fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx); - let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); - - view.update(cx, |view, cx| { - view.change_selections(None, cx, |s| { - s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]) - }); - }); - view.update(cx, |view, cx| { - view.add_selection_above(&AddSelectionAbove, cx); - assert_eq!( - view.selections.display_ranges(cx), - vec![ - DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), - DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3) - ] - ); - }); - - view.update(cx, |view, cx| { - view.add_selection_above(&AddSelectionAbove, cx); - assert_eq!( - view.selections.display_ranges(cx), - vec![ - DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), - DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3) - ] - ); - }); - - view.update(cx, |view, cx| { - view.add_selection_below(&AddSelectionBelow, cx); - assert_eq!( - view.selections.display_ranges(cx), - vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)] - ); - - view.undo_selection(&UndoSelection, cx); - assert_eq!( - view.selections.display_ranges(cx), - vec![ - DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), - DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3) - ] - ); - - view.redo_selection(&RedoSelection, cx); - assert_eq!( - view.selections.display_ranges(cx), - vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)] - ); - }); - - view.update(cx, |view, cx| { - view.add_selection_below(&AddSelectionBelow, cx); - assert_eq!( - view.selections.display_ranges(cx), - vec![ - DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3), - DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3) - ] - ); - }); - - view.update(cx, |view, cx| { - view.add_selection_below(&AddSelectionBelow, cx); - assert_eq!( - view.selections.display_ranges(cx), - vec![ - DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3), - DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3) - ] - ); - }); - - view.update(cx, |view, cx| { - view.change_selections(None, cx, |s| { - s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]) - }); - }); - view.update(cx, |view, cx| { - view.add_selection_below(&AddSelectionBelow, cx); - assert_eq!( - view.selections.display_ranges(cx), - vec![ - DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3), - DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3) - ] - ); - }); - - view.update(cx, |view, cx| { - view.add_selection_below(&AddSelectionBelow, cx); - assert_eq!( - view.selections.display_ranges(cx), - vec![ - DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3), - DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3) - ] - ); - }); - - view.update(cx, |view, cx| { - view.add_selection_above(&AddSelectionAbove, cx); - assert_eq!( - view.selections.display_ranges(cx), - vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)] - ); - }); - - view.update(cx, |view, cx| { - view.add_selection_above(&AddSelectionAbove, cx); - assert_eq!( - view.selections.display_ranges(cx), - vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)] - ); - }); - - view.update(cx, |view, cx| { - view.change_selections(None, cx, |s| { - s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)]) - }); - view.add_selection_below(&AddSelectionBelow, cx); - assert_eq!( - view.selections.display_ranges(cx), - vec![ - DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), - DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4), - DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2), - ] - ); - }); - - view.update(cx, |view, cx| { - view.add_selection_below(&AddSelectionBelow, cx); - assert_eq!( - view.selections.display_ranges(cx), - vec![ - DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), - DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4), - DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2), - DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4), - ] - ); - }); - - view.update(cx, |view, cx| { - view.add_selection_above(&AddSelectionAbove, cx); - assert_eq!( - view.selections.display_ranges(cx), - vec![ - DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), - DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4), - DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2), - ] - ); - }); - - view.update(cx, |view, cx| { - view.change_selections(None, cx, |s| { - s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)]) - }); - }); - view.update(cx, |view, cx| { - view.add_selection_above(&AddSelectionAbove, cx); - assert_eq!( - view.selections.display_ranges(cx), - vec![ - DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1), - DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1), - DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1), - DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1), - ] - ); - }); - - view.update(cx, |view, cx| { - view.add_selection_below(&AddSelectionBelow, cx); - assert_eq!( - view.selections.display_ranges(cx), - vec![ - DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1), - DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1), - DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1), - ] - ); - }); - } - - #[gpui::test] - async fn test_select_next(cx: &mut gpui::TestAppContext) { - let mut cx = EditorTestContext::new(cx); - cx.set_state("abc\nˇabc abc\ndefabc\nabc"); - - cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx)); - cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc"); - - cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx)); - 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)); - cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»"); - - cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx)); - cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»"); - } - - #[gpui::test] - async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); - let language = Arc::new(Language::new( - LanguageConfig::default(), - Some(tree_sitter_rust::language()), - )); - - let text = r#" - use mod1::mod2::{mod3, mod4}; - - fn fn_1(param1: bool, param2: &str) { - let var1 = "text"; - } - "# - .unindent(); - - let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); - let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let (_, view) = cx.add_window(|cx| build_editor(buffer, cx)); - view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) - .await; - - view.update(cx, |view, cx| { - view.change_selections(None, cx, |s| { - s.select_display_ranges([ - DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25), - DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12), - DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18), - ]); - }); - view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx); - }); - assert_eq!( - view.update(cx, |view, cx| { view.selections.display_ranges(cx) }), - &[ - DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27), - DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7), - DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21), - ] - ); - - view.update(cx, |view, cx| { - view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx); - }); - assert_eq!( - view.update(cx, |view, cx| view.selections.display_ranges(cx)), - &[ - DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28), - DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0), - ] - ); - - view.update(cx, |view, cx| { - view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx); - }); - assert_eq!( - view.update(cx, |view, cx| view.selections.display_ranges(cx)), - &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)] - ); - - // Trying to expand the selected syntax node one more time has no effect. - view.update(cx, |view, cx| { - view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx); - }); - assert_eq!( - view.update(cx, |view, cx| view.selections.display_ranges(cx)), - &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)] - ); - - view.update(cx, |view, cx| { - view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx); - }); - assert_eq!( - view.update(cx, |view, cx| view.selections.display_ranges(cx)), - &[ - DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28), - DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0), - ] - ); - - view.update(cx, |view, cx| { - view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx); - }); - assert_eq!( - view.update(cx, |view, cx| view.selections.display_ranges(cx)), - &[ - DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27), - DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7), - DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21), - ] - ); - - view.update(cx, |view, cx| { - view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx); - }); - assert_eq!( - view.update(cx, |view, cx| view.selections.display_ranges(cx)), - &[ - DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25), - DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12), - DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18), - ] - ); - - // Trying to shrink the selected syntax node one more time has no effect. - view.update(cx, |view, cx| { - view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx); - }); - assert_eq!( - view.update(cx, |view, cx| view.selections.display_ranges(cx)), - &[ - DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25), - DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12), - DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18), - ] - ); - - // Ensure that we keep expanding the selection if the larger selection starts or ends within - // a fold. - view.update(cx, |view, cx| { - view.fold_ranges( - vec![ - Point::new(0, 21)..Point::new(0, 24), - Point::new(3, 20)..Point::new(3, 22), - ], - 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) { - cx.update(|cx| cx.set_global(Settings::test(cx))); - let language = Arc::new( - Language::new( - LanguageConfig { - brackets: 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() - }, - Some(tree_sitter_rust::language()), - ) - .with_indents_query( - r#" - (_ "(" ")" @end) @indent - (_ "{" "}" @end) @indent - "#, - ) - .unwrap(), - ); - - let text = "fn a() {}"; - - let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); - let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx)); - editor - .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx)) - .await; - - editor.update(cx, |editor, cx| { - editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9])); - editor.newline(&Newline, cx); - assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n"); - assert_eq!( - editor.selections.ranges(cx), - &[ - Point::new(1, 4)..Point::new(1, 4), - Point::new(3, 4)..Point::new(3, 4), - Point::new(5, 0)..Point::new(5, 0) - ] - ); - }); - } - - #[gpui::test] - async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) { - let mut cx = EditorTestContext::new(cx); - - let language = Arc::new(Language::new( - LanguageConfig { - brackets: 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: false, - newline: true, - }, - ], - 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(), - ); - - // 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"); - - // 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"); - } - - #[gpui::test] - async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) { - let mut cx = EditorTestContext::new(cx); - - let html_language = Arc::new( - Language::new( - LanguageConfig { - name: "HTML".into(), - brackets: vec![ - BracketPair { - start: "<".into(), - end: ">".into(), - ..Default::default() - }, - BracketPair { - start: "{".into(), - end: "}".into(), - ..Default::default() - }, - BracketPair { - start: "(".into(), - end: ")".into(), - ..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: vec![ - BracketPair { - start: "/*".into(), - end: " */".into(), - ..Default::default() - }, - BracketPair { - start: "{".into(), - end: "}".into(), - ..Default::default() - }, - BracketPair { - start: "(".into(), - end: ")".into(), - ..Default::default() - }, - ], - autoclose_before: "})]>".into(), - ..Default::default() - }, - Some(tree_sitter_javascript::language()), - )); - - 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#" - ˇ - - ˇ - "# - .unindent(), - ); - - // Precondition: different languages are active at different locations. - cx.update_editor(|editor, cx| { - let snapshot = editor.snapshot(cx); - let cursors = editor.selections.ranges::(cx); - let languages = cursors - .iter() - .map(|c| snapshot.language_at(c.start).unwrap().name()) - .collect::>(); - 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#" - - - - "# - .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#" - - - - "# - .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#" - - - - "# - .unindent(), - ); - cx.update_editor(|editor, cx| { - editor.handle_input(">", cx); - }); - cx.assert_editor_state( - &r#" - ˇ - - ˇ - "# - .unindent(), - ); - - // Reset - cx.set_state( - &r#" - ˇ - - ˇ - "# - .unindent(), - ); - - cx.update_editor(|editor, cx| { - editor.handle_input("<", cx); - }); - cx.assert_editor_state( - &r#" - <ˇ> - - <ˇ> - "# - .unindent(), - ); - - // When backspacing, the closing angle brackets are removed. - cx.update_editor(|editor, cx| { - editor.backspace(&Backspace, cx); - }); - cx.assert_editor_state( - &r#" - ˇ - - ˇ - "# - .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#" - /*ˇ - - /*ˇ - "# - .unindent(), - ); - } - - #[gpui::test] - async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); - let language = Arc::new(Language::new( - LanguageConfig { - brackets: vec![BracketPair { - start: "{".to_string(), - end: "}".to_string(), - close: true, - newline: true, - }], - ..Default::default() - }, - Some(tree_sitter_rust::language()), - )); - - let text = r#" - a - b - c - "# - .unindent(); - - let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); - let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let (_, view) = cx.add_window(|cx| build_editor(buffer, cx)); - view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) - .await; - - view.update(cx, |view, cx| { - view.change_selections(None, cx, |s| { - s.select_display_ranges([ - DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1), - DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1), - DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1), - ]) - }); - - view.handle_input("{", cx); - view.handle_input("{", cx); - view.handle_input("{", cx); - assert_eq!( - view.text(cx), - " - {{{a}}} - {{{b}}} - {{{c}}} - " - .unindent() - ); - assert_eq!( - view.selections.display_ranges(cx), - [ - DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4), - DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4), - DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4) - ] - ); - - view.undo(&Undo, cx); - 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) - ] - ); - }); - } - - #[gpui::test] - async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); - let language = Arc::new(Language::new( - LanguageConfig { - brackets: vec![BracketPair { - start: "{".to_string(), - end: "}".to_string(), - close: true, - newline: true, - }], - autoclose_before: "}".to_string(), - ..Default::default() - }, - Some(tree_sitter_rust::language()), - )); - - let text = r#" - a - b - c - "# - .unindent(); - - let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); - let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx)); - editor - .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) - .await; - - editor.update(cx, |editor, cx| { - editor.change_selections(None, cx, |s| { - s.select_ranges([ - Point::new(0, 1)..Point::new(0, 1), - Point::new(1, 1)..Point::new(1, 1), - Point::new(2, 1)..Point::new(2, 1), - ]) - }); - - editor.handle_input("{", cx); - editor.handle_input("{", cx); - editor.handle_input("_", cx); - assert_eq!( - editor.text(cx), - " - a{{_}} - b{{_}} - c{{_}} - " - .unindent() - ); - assert_eq!( - editor.selections.ranges::(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::(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::(cx), - [ - Point::new(0, 1)..Point::new(0, 1), - Point::new(1, 1)..Point::new(1, 1), - Point::new(2, 1)..Point::new(2, 1) - ] - ); - }); - } - - #[gpui::test] - async fn test_snippets(cx: &mut gpui::TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); - - let (text, insertion_ranges) = marked_text_ranges( - indoc! {" - a.ˇ b - a.ˇ b - a.ˇ b - "}, - false, - ); - - let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx)); - let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx)); - - 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, 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::(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) { - cx.foreground().forbid_parking(); - - let mut language = Language::new( - LanguageConfig { - name: "Rust".into(), - path_suffixes: vec!["rs".to_string()], - ..Default::default() - }, - Some(tree_sitter_rust::language()), - ); - let mut fake_servers = language - .set_fake_lsp_adapter(Arc::new(FakeLspAdapter { - capabilities: lsp::ServerCapabilities { - document_formatting_provider: Some(lsp::OneOf::Left(true)), - ..Default::default() - }, - ..Default::default() - })) - .await; - - let fs = FakeFs::new(cx.background()); - fs.insert_file("/file.rs", Default::default()).await; - - let project = Project::test(fs, ["/file.rs".as_ref()], cx).await; - project.update(cx, |project, _| project.languages().add(Arc::new(language))); - let buffer = project - .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx)) - .await - .unwrap(); - - cx.foreground().start_waiting(); - let fake_server = fake_servers.next().await.unwrap(); - - let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx)); - editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx)); - assert!(cx.read(|cx| editor.is_dirty(cx))); - - let save = cx.update(|cx| editor.save(project.clone(), cx)); - fake_server - .handle_request::(move |params, _| async move { - assert_eq!( - params.text_document.uri, - lsp::Url::from_file_path("/file.rs").unwrap() - ); - assert_eq!(params.options.tab_size, 4); - Ok(Some(vec![lsp::TextEdit::new( - lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)), - ", ".to_string(), - )])) - }) - .next() - .await; - cx.foreground().start_waiting(); - save.await.unwrap(); - assert_eq!( - editor.read_with(cx, |editor, cx| editor.text(cx)), - "one, two\nthree\n" - ); - assert!(!cx.read(|cx| editor.is_dirty(cx))); - - editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx)); - assert!(cx.read(|cx| editor.is_dirty(cx))); - - // Ensure we can still save even if formatting hangs. - fake_server.handle_request::(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 = cx.update(|cx| editor.save(project.clone(), cx)); - cx.foreground().advance_clock(super::FORMAT_TIMEOUT); - cx.foreground().start_waiting(); - save.await.unwrap(); - assert_eq!( - editor.read_with(cx, |editor, cx| editor.text(cx)), - "one\ntwo\nthree\n" - ); - assert!(!cx.read(|cx| editor.is_dirty(cx))); - - // Set rust language override and assert overriden tabsize is sent to language server - cx.update(|cx| { - cx.update_global::(|settings, _| { - settings.language_overrides.insert( - "Rust".into(), - EditorSettings { - tab_size: Some(8.try_into().unwrap()), - ..Default::default() - }, - ); - }) - }); - - let save = cx.update(|cx| editor.save(project.clone(), cx)); - fake_server - .handle_request::(move |params, _| async move { - assert_eq!( - params.text_document.uri, - lsp::Url::from_file_path("/file.rs").unwrap() - ); - assert_eq!(params.options.tab_size, 8); - Ok(Some(vec![])) - }) - .next() - .await; - cx.foreground().start_waiting(); - save.await.unwrap(); - } - - #[gpui::test] - async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); - - let mut language = Language::new( - LanguageConfig { - name: "Rust".into(), - path_suffixes: vec!["rs".to_string()], - ..Default::default() - }, - Some(tree_sitter_rust::language()), - ); - let mut fake_servers = language - .set_fake_lsp_adapter(Arc::new(FakeLspAdapter { - capabilities: lsp::ServerCapabilities { - document_range_formatting_provider: Some(lsp::OneOf::Left(true)), - ..Default::default() - }, - ..Default::default() - })) - .await; - - let fs = FakeFs::new(cx.background()); - fs.insert_file("/file.rs", Default::default()).await; - - let project = Project::test(fs, ["/file.rs".as_ref()], cx).await; - project.update(cx, |project, _| project.languages().add(Arc::new(language))); - let buffer = project - .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx)) - .await - .unwrap(); - - cx.foreground().start_waiting(); - let fake_server = fake_servers.next().await.unwrap(); - - let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx)); - editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx)); - assert!(cx.read(|cx| editor.is_dirty(cx))); - - let save = cx.update(|cx| editor.save(project.clone(), cx)); - fake_server - .handle_request::(move |params, _| async move { - assert_eq!( - params.text_document.uri, - lsp::Url::from_file_path("/file.rs").unwrap() - ); - assert_eq!(params.options.tab_size, 4); - Ok(Some(vec![lsp::TextEdit::new( - lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)), - ", ".to_string(), - )])) - }) - .next() - .await; - cx.foreground().start_waiting(); - save.await.unwrap(); - assert_eq!( - editor.read_with(cx, |editor, cx| editor.text(cx)), - "one, two\nthree\n" - ); - assert!(!cx.read(|cx| editor.is_dirty(cx))); - - editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx)); - assert!(cx.read(|cx| editor.is_dirty(cx))); - - // Ensure we can still save even if formatting hangs. - fake_server.handle_request::( - 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 = cx.update(|cx| editor.save(project.clone(), cx)); - cx.foreground().advance_clock(super::FORMAT_TIMEOUT); - cx.foreground().start_waiting(); - save.await.unwrap(); - assert_eq!( - editor.read_with(cx, |editor, cx| editor.text(cx)), - "one\ntwo\nthree\n" - ); - assert!(!cx.read(|cx| editor.is_dirty(cx))); - - // Set rust language override and assert overriden tabsize is sent to language server - cx.update(|cx| { - cx.update_global::(|settings, _| { - settings.language_overrides.insert( - "Rust".into(), - EditorSettings { - tab_size: Some(8.try_into().unwrap()), - ..Default::default() - }, - ); - }) - }); - - let save = cx.update(|cx| editor.save(project.clone(), cx)); - fake_server - .handle_request::(move |params, _| async move { - assert_eq!( - params.text_document.uri, - lsp::Url::from_file_path("/file.rs").unwrap() - ); - assert_eq!(params.options.tab_size, 8); - Ok(Some(vec![])) - }) - .next() - .await; - cx.foreground().start_waiting(); - save.await.unwrap(); - } - - #[gpui::test] - async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) { - cx.foreground().forbid_parking(); - - let mut language = Language::new( - LanguageConfig { - name: "Rust".into(), - path_suffixes: vec!["rs".to_string()], - ..Default::default() - }, - Some(tree_sitter_rust::language()), - ); - let mut fake_servers = language - .set_fake_lsp_adapter(Arc::new(FakeLspAdapter { - capabilities: lsp::ServerCapabilities { - document_formatting_provider: Some(lsp::OneOf::Left(true)), - ..Default::default() - }, - ..Default::default() - })) - .await; - - let fs = FakeFs::new(cx.background()); - fs.insert_file("/file.rs", Default::default()).await; - - let project = Project::test(fs, ["/file.rs".as_ref()], cx).await; - project.update(cx, |project, _| project.languages().add(Arc::new(language))); - let buffer = project - .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx)) - .await - .unwrap(); - - cx.foreground().start_waiting(); - let fake_server = fake_servers.next().await.unwrap(); - - let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx)); - 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(), cx)); - fake_server - .handle_request::(move |params, _| async move { - assert_eq!( - params.text_document.uri, - lsp::Url::from_file_path("/file.rs").unwrap() - ); - assert_eq!(params.options.tab_size, 4); - Ok(Some(vec![lsp::TextEdit::new( - lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)), - ", ".to_string(), - )])) - }) - .next() - .await; - cx.foreground().start_waiting(); - format.await.unwrap(); - assert_eq!( - editor.read_with(cx, |editor, cx| editor.text(cx)), - "one, two\nthree\n" - ); - - editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx)); - // Ensure we don't lock if formatting hangs. - fake_server.handle_request::(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, cx)); - cx.foreground().advance_clock(super::FORMAT_TIMEOUT); - cx.foreground().start_waiting(); - format.await.unwrap(); - assert_eq!( - editor.read_with(cx, |editor, cx| editor.text(cx)), - "one\ntwo\nthree\n" - ); - } - - #[gpui::test] - async fn test_completion(cx: &mut gpui::TestAppContext) { - 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ˇ - 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.move_down(&MoveDown, 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(( - 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.is_none())); - cx.simulate_keystroke("s"); - assert!(cx.editor(|e, _| e.context_menu.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 - 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 - 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::(|settings, _| { - settings.show_completions_on_input = false; - }) - }); - cx.set_state("editorˇ"); - cx.simulate_keystroke("."); - assert!(cx.editor(|e, _| e.context_menu.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.is_none())); - cx.update_editor(|editor, cx| { - editor.show_completions(&ShowCompletions, cx); - }); - handle_completion_request(&mut cx, "editor.", 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(); - - // 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 - async fn handle_completion_request<'a>( - cx: &mut EditorLspTestContext<'a>, - marked_string: &str, - completions: Vec<&'static str>, - ) { - 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()); - - cx.handle_request::(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(), - ))) - } - }) - .next() - .await; - } - - async fn handle_resolve_completion_request<'a>( - cx: &mut EditorLspTestContext<'a>, - edit: Option<(&'static str, &'static str)>, - ) { - let edit = edit.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()); - vec![lsp::TextEdit::new(replace_range, new_text.to_string())] - }); - - cx.handle_request::(move |_, _, _| { - let edit = edit.clone(); - async move { - Ok(lsp::CompletionItem { - additional_text_edits: edit, - ..Default::default() - }) - } - }) - .next() - .await; - } - } - - #[gpui::test] - async fn test_toggle_comment(cx: &mut gpui::TestAppContext) { - cx.update(|cx| cx.set_global(Settings::test(cx))); - let language = Arc::new(Language::new( - LanguageConfig { - line_comment: Some("// ".into()), - ..Default::default() - }, - Some(tree_sitter_rust::language()), - )); - - let text = " - fn a() { - //b(); - // c(); - // d(); - } - " - .unindent(); - - let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); - let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let (_, view) = cx.add_window(|cx| build_editor(buffer, cx)); - - view.update(cx, |editor, cx| { - // If multiple selections intersect a line, the line is only - // toggled once. - editor.change_selections(None, cx, |s| { - s.select_display_ranges([ - DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3), - DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6), - ]) - }); - editor.toggle_comments(&ToggleComments, cx); - assert_eq!( - editor.text(cx), - " - fn a() { - b(); - c(); - d(); - } - " - .unindent() - ); - - // The comment prefix is inserted at the same column for every line - // in a selection. - editor.change_selections(None, cx, |s| { - s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)]) - }); - editor.toggle_comments(&ToggleComments, cx); - assert_eq!( - editor.text(cx), - " - fn a() { - // b(); - // c(); - // d(); - } - " - .unindent() - ); - - // If a selection ends at the beginning of a line, that line is not toggled. - editor.change_selections(None, cx, |s| { - s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)]) - }); - editor.toggle_comments(&ToggleComments, cx); - assert_eq!( - editor.text(cx), - " - fn a() { - // b(); - c(); - // d(); - } - " - .unindent() - ); - }); - } - - #[gpui::test] - async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) { - let mut cx = EditorTestContext::new(cx); - - let html_language = Arc::new( - Language::new( - LanguageConfig { - name: "HTML".into(), - block_comment: Some(("".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_javascript::language()), - )); - - 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#" -

A

ˇ -

B

ˇ -

C

ˇ - "# - .unindent(), - ); - cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); - cx.assert_editor_state( - &r#" - - - - "# - .unindent(), - ); - cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); - cx.assert_editor_state( - &r#" -

A

ˇ -

B

ˇ -

C

ˇ - "# - .unindent(), - ); - - // Toggle comments for mixture of empty and non-empty selections, where - // multiple selections occupy a given line. - cx.set_state( - &r#" -

-

ˇ»B

ˇ -

-

ˇ»D

ˇ - "# - .unindent(), - ); - - cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); - cx.assert_editor_state( - &r#" - - - "# - .unindent(), - ); - cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); - cx.assert_editor_state( - &r#" -

-

ˇ»B

ˇ -

-

ˇ»D

ˇ - "# - .unindent(), - ); - - // Toggle comments when different languages are active for different - // selections. - cx.set_state( - &r#" - ˇ - "# - .unindent(), - ); - cx.foreground().run_until_parked(); - cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); - cx.assert_editor_state( - &r#" - - // ˇvar x = new Y(); - - "# - .unindent(), - ); - } - - #[gpui::test] - fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx)); - let multibuffer = cx.add_model(|cx| { - let mut multibuffer = MultiBuffer::new(0); - multibuffer.push_excerpts( - buffer.clone(), - [ - ExcerptRange { - context: Point::new(0, 0)..Point::new(0, 4), - primary: None, - }, - ExcerptRange { - context: Point::new(1, 0)..Point::new(1, 4), - primary: None, - }, - ], - cx, - ); - multibuffer - }); - - assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb"); - - let (_, view) = cx.add_window(Default::default(), |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), - ] - ) - }); - } - - #[gpui::test] - fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let markers = vec![('[', ']').into(), ('(', ')').into()]; - let (initial_text, mut excerpt_ranges) = marked_text_ranges_by( - indoc! {" - [aaaa - (bbbb] - cccc)", - }, - markers.clone(), - ); - let excerpt_ranges = markers.into_iter().map(|marker| { - let context = excerpt_ranges.remove(&marker).unwrap()[0].clone(); - ExcerptRange { - context, - primary: None, - } - }); - let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx)); - let multibuffer = cx.add_model(|cx| { - let mut multibuffer = MultiBuffer::new(0); - multibuffer.push_excerpts(buffer, excerpt_ranges, cx); - multibuffer - }); - - let (_, view) = cx.add_window(Default::default(), |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 gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx)); - let mut excerpt1_id = None; - let multibuffer = cx.add_model(|cx| { - let mut multibuffer = MultiBuffer::new(0); - excerpt1_id = multibuffer - .push_excerpts( - buffer.clone(), - [ - ExcerptRange { - context: Point::new(0, 0)..Point::new(1, 4), - primary: None, - }, - ExcerptRange { - context: Point::new(1, 0)..Point::new(2, 4), - primary: None, - }, - ], - cx, - ) - .into_iter() - .next(); - multibuffer - }); - assert_eq!( - multibuffer.read(cx).read(cx).text(), - "aaaa\nbbbb\nbbbb\ncccc" - ); - let (_, editor) = cx.add_window(Default::default(), |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 gpui::MutableAppContext) { - cx.set_global(Settings::test(cx)); - let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx)); - let mut excerpt1_id = None; - let multibuffer = cx.add_model(|cx| { - let mut multibuffer = MultiBuffer::new(0); - excerpt1_id = multibuffer - .push_excerpts( - buffer.clone(), - [ - ExcerptRange { - context: Point::new(0, 0)..Point::new(1, 4), - primary: None, - }, - ExcerptRange { - context: Point::new(1, 0)..Point::new(2, 4), - primary: None, - }, - ], - cx, - ) - .into_iter() - .next(); - multibuffer - }); - assert_eq!( - multibuffer.read(cx).read(cx).text(), - "aaaa\nbbbb\nbbbb\ncccc" - ); - let (_, editor) = cx.add_window(Default::default(), |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) { - cx.update(|cx| cx.set_global(Settings::test(cx))); - let language = Arc::new( - Language::new( - LanguageConfig { - brackets: 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() - }, - Some(tree_sitter_rust::language()), - ) - .with_indents_query("") - .unwrap(), - ); - - let text = concat!( - "{ }\n", // Suppress rustfmt - " x\n", // - " /* */\n", // - "x\n", // - "{{} }\n", // - ); - - let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); - let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); - let (_, view) = cx.add_window(|cx| build_editor(buffer, cx)); - view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) - .await; - - view.update(cx, |view, cx| { - view.change_selections(None, cx, |s| { - s.select_display_ranges([ - DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3), - DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5), - DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4), - ]) - }); - view.newline(&Newline, cx); - - assert_eq!( - view.buffer().read(cx).read(cx).text(), - concat!( - "{ \n", // Suppress rustfmt - "\n", // - "}\n", // - " x\n", // - " /* \n", // - " \n", // - " */\n", // - "x\n", // - "{{} \n", // - "}\n", // - ) - ); - }); - } - - #[gpui::test] - fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) { - let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx); - - cx.set_global(Settings::test(cx)); - let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); - - editor.update(cx, |editor, cx| { - struct Type1; - struct Type2; - - let buffer = buffer.read(cx).snapshot(cx); - - let anchor_range = |range: Range| { - buffer.anchor_after(range.start)..buffer.anchor_after(range.end) - }; - - editor.highlight_background::( - vec![ - anchor_range(Point::new(2, 1)..Point::new(2, 3)), - anchor_range(Point::new(4, 2)..Point::new(4, 4)), - anchor_range(Point::new(6, 3)..Point::new(6, 5)), - anchor_range(Point::new(8, 4)..Point::new(8, 6)), - ], - |_| Color::red(), - cx, - ); - editor.highlight_background::( - vec![ - anchor_range(Point::new(3, 2)..Point::new(3, 5)), - anchor_range(Point::new(5, 3)..Point::new(5, 6)), - anchor_range(Point::new(7, 4)..Point::new(7, 7)), - anchor_range(Point::new(9, 5)..Point::new(9, 8)), - ], - |_| Color::green(), - cx, - ); - - let snapshot = editor.snapshot(cx); - let mut highlighted_ranges = editor.background_highlights_in_range( - anchor_range(Point::new(3, 4)..Point::new(7, 4)), - &snapshot, - cx.global::().theme.as_ref(), - ); - // Enforce a consistent ordering based on color without relying on the ordering of the - // highlight's `TypeId` which is non-deterministic. - highlighted_ranges.sort_unstable_by_key(|(_, color)| *color); - assert_eq!( - highlighted_ranges, - &[ - ( - DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5), - Color::green(), - ), - ( - DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6), - Color::green(), - ), - ( - DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4), - Color::red(), - ), - ( - DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5), - Color::red(), - ), - ] - ); - assert_eq!( - editor.background_highlights_in_range( - anchor_range(Point::new(5, 6)..Point::new(6, 4)), - &snapshot, - cx.global::().theme.as_ref(), - ), - &[( - DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5), - Color::red(), - )] - ); - }); - } - - #[gpui::test] - fn test_following(cx: &mut gpui::MutableAppContext) { - let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx); - - cx.set_global(Settings::test(cx)); - - let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); - let (_, follower) = cx.add_window( - WindowOptions { - bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))), - ..Default::default() - }, - |cx| build_editor(buffer.clone(), cx), - ); - - let pending_update = Rc::new(RefCell::new(None)); - follower.update(cx, { - let update = pending_update.clone(); - |_, cx| { - cx.subscribe(&leader, move |_, leader, event, cx| { - leader - .read(cx) - .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx); - }) - .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(pending_update.borrow_mut().take().unwrap(), cx) - .unwrap(); - }); - assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]); - - // Update the scroll position only - leader.update(cx, |leader, cx| { - leader.set_scroll_position(vec2f(1.5, 3.5), cx); - }); - follower.update(cx, |follower, cx| { - follower - .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx) - .unwrap(); - }); - assert_eq!( - follower.update(cx, |follower, cx| follower.scroll_position(cx)), - vec2f(1.5, 3.5) - ); - - // Update the selections and scroll position - leader.update(cx, |leader, cx| { - leader.change_selections(None, cx, |s| s.select_ranges([0..0])); - leader.request_autoscroll(Autoscroll::Newest, cx); - leader.set_scroll_position(vec2f(1.5, 3.5), cx); - }); - follower.update(cx, |follower, cx| { - let initial_scroll_position = follower.scroll_position(cx); - follower - .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx) - .unwrap(); - assert_eq!(follower.scroll_position(cx), initial_scroll_position); - assert!(follower.autoscroll_request.is_some()); - }); - assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]); - - // 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(pending_update.borrow_mut().take().unwrap(), cx) - .unwrap(); - }); - assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]); - - // 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(pending_update.borrow_mut().take().unwrap(), cx) - .unwrap(); - }); - assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]); - } - - #[test] - fn test_combine_syntax_and_fuzzy_match_highlights() { - let string = "abcdefghijklmnop"; - let syntax_ranges = [ - ( - 0..3, - HighlightStyle { - color: Some(Color::red()), - ..Default::default() - }, - ), - ( - 4..8, - HighlightStyle { - color: Some(Color::green()), - ..Default::default() - }, - ), - ]; - let match_indices = [4, 6, 7, 8]; - assert_eq!( - combine_syntax_and_fuzzy_match_highlights( - string, - Default::default(), - syntax_ranges.into_iter(), - &match_indices, - ), - &[ - ( - 0..3, - HighlightStyle { - color: Some(Color::red()), - ..Default::default() - }, - ), - ( - 4..5, - HighlightStyle { - color: Some(Color::green()), - weight: Some(fonts::Weight::BOLD), - ..Default::default() - }, - ), - ( - 5..6, - HighlightStyle { - color: Some(Color::green()), - ..Default::default() - }, - ), - ( - 6..8, - HighlightStyle { - color: Some(Color::green()), - weight: Some(fonts::Weight::BOLD), - ..Default::default() - }, - ), - ( - 8..9, - HighlightStyle { - weight: Some(fonts::Weight::BOLD), - ..Default::default() - }, - ), - ] - ); - } - - fn empty_range(row: usize, column: usize) -> Range { - 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) { - 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 - ); - } -} - trait RangeExt { fn sorted(&self) -> Range; fn to_inclusive(&self) -> RangeInclusive; diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs new file mode 100644 index 0000000000..c2840cc17b --- /dev/null +++ b/crates/editor/src/editor_tests.rs @@ -0,0 +1,4881 @@ +use crate::test::{ + assert_text_with_selections, build_editor, select_ranges, EditorLspTestContext, + EditorTestContext, +}; + +use super::*; +use futures::StreamExt; +use gpui::{ + geometry::rect::RectF, + platform::{WindowBounds, WindowOptions}, +}; +use indoc::indoc; +use language::{FakeLspAdapter, LanguageConfig, LanguageRegistry}; +use project::FakeFs; +use settings::EditorSettings; +use std::{cell::RefCell, rc::Rc, time::Instant}; +use text::Point; +use unindent::Unindent; +use util::{ + assert_set_eq, + test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker}, +}; +use workspace::{FollowableItem, ItemHandle, NavigationEntry, Pane}; + +#[gpui::test] +fn test_edit_events(cx: &mut MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx)); + + let events = Rc::new(RefCell::new(Vec::new())); + let (_, editor1) = cx.add_window(Default::default(), { + let events = events.clone(); + |cx| { + cx.subscribe(&cx.handle(), move |_, _, event, _| { + if matches!( + event, + Event::Edited | Event::BufferEdited | Event::DirtyChanged + ) { + events.borrow_mut().push(("editor1", *event)); + } + }) + .detach(); + Editor::for_buffer(buffer.clone(), None, cx) + } + }); + let (_, editor2) = cx.add_window(Default::default(), { + let events = events.clone(); + |cx| { + cx.subscribe(&cx.handle(), move |_, _, event, _| { + if matches!( + event, + Event::Edited | Event::BufferEdited | Event::DirtyChanged + ) { + events.borrow_mut().push(("editor2", *event)); + } + }) + .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", Event::Edited), + ("editor1", Event::BufferEdited), + ("editor2", Event::BufferEdited), + ("editor1", Event::DirtyChanged), + ("editor2", Event::DirtyChanged) + ] + ); + + // Mutating editor 2 will emit an `Edited` event only for that editor. + editor2.update(cx, |editor, cx| editor.delete(&Delete, cx)); + assert_eq!( + mem::take(&mut *events.borrow_mut()), + [ + ("editor2", Event::Edited), + ("editor1", Event::BufferEdited), + ("editor2", Event::BufferEdited), + ] + ); + + // Undoing on editor 1 will emit an `Edited` event only for that editor. + editor1.update(cx, |editor, cx| editor.undo(&Undo, cx)); + assert_eq!( + mem::take(&mut *events.borrow_mut()), + [ + ("editor1", Event::Edited), + ("editor1", Event::BufferEdited), + ("editor2", Event::BufferEdited), + ("editor1", Event::DirtyChanged), + ("editor2", Event::DirtyChanged), + ] + ); + + // Redoing on editor 1 will emit an `Edited` event only for that editor. + editor1.update(cx, |editor, cx| editor.redo(&Redo, cx)); + assert_eq!( + mem::take(&mut *events.borrow_mut()), + [ + ("editor1", Event::Edited), + ("editor1", Event::BufferEdited), + ("editor2", Event::BufferEdited), + ("editor1", Event::DirtyChanged), + ("editor2", Event::DirtyChanged), + ] + ); + + // Undoing on editor 2 will emit an `Edited` event only for that editor. + editor2.update(cx, |editor, cx| editor.undo(&Undo, cx)); + assert_eq!( + mem::take(&mut *events.borrow_mut()), + [ + ("editor2", Event::Edited), + ("editor1", Event::BufferEdited), + ("editor2", Event::BufferEdited), + ("editor1", Event::DirtyChanged), + ("editor2", Event::DirtyChanged), + ] + ); + + // Redoing on editor 2 will emit an `Edited` event only for that editor. + editor2.update(cx, |editor, cx| editor.redo(&Redo, cx)); + assert_eq!( + mem::take(&mut *events.borrow_mut()), + [ + ("editor2", Event::Edited), + ("editor1", Event::BufferEdited), + ("editor2", Event::BufferEdited), + ("editor1", Event::DirtyChanged), + ("editor2", Event::DirtyChanged), + ] + ); + + // No event is emitted when the mutation is a no-op. + editor2.update(cx, |editor, cx| { + editor.change_selections(None, cx, |s| s.select_ranges([0..0])); + + editor.backspace(&Backspace, cx); + }); + assert_eq!(mem::take(&mut *events.borrow_mut()), []); +} + +#[gpui::test] +fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) { + cx.set_global(Settings::test(cx)); + let mut now = Instant::now(); + let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx)); + let group_interval = buffer.read(cx).transaction_group_interval(); + let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); + let (_, editor) = cx.add_window(Default::default(), |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 MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = cx.add_model(|cx| { + let mut buffer = language::Buffer::new(0, "abcde", cx); + // Ensure automatic grouping doesn't occur. + buffer.set_group_interval(Duration::ZERO); + buffer + }); + + let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); + cx.add_window(Default::default(), |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 gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + + let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx); + let (_, editor) = cx.add_window(Default::default(), |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)), + [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)] + ); + + editor.update(cx, |view, cx| { + view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx); + }); + + assert_eq!( + editor.update(cx, |view, cx| view.selections.display_ranges(cx)), + [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] + ); + + editor.update(cx, |view, cx| { + view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx); + }); + + assert_eq!( + editor.update(cx, |view, cx| view.selections.display_ranges(cx)), + [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)] + ); + + editor.update(cx, |view, cx| { + view.end_selection(cx); + view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx); + }); + + assert_eq!( + editor.update(cx, |view, cx| view.selections.display_ranges(cx)), + [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)] + ); + + editor.update(cx, |view, cx| { + view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx); + view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx); + }); + + assert_eq!( + editor.update(cx, |view, cx| view.selections.display_ranges(cx)), + [ + DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1), + DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0) + ] + ); + + editor.update(cx, |view, cx| { + view.end_selection(cx); + }); + + assert_eq!( + editor.update(cx, |view, cx| view.selections.display_ranges(cx)), + [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)] + ); +} + +#[gpui::test] +fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx); + let (_, view) = cx.add_window(Default::default(), |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, Vector2F::zero(), cx); + assert_eq!( + view.selections.display_ranges(cx), + [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] + ); + }); + + view.update(cx, |view, cx| { + view.cancel(&Cancel, cx); + view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx); + assert_eq!( + view.selections.display_ranges(cx), + [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)] + ); + }); +} + +#[gpui::test] +fn test_clone(cx: &mut gpui::MutableAppContext) { + let (text, selection_ranges) = marked_text_ranges( + indoc! {" + one + two + threeˇ + four + fiveˇ + "}, + true, + ); + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple(&text, cx); + + let (_, editor) = cx.add_window(Default::default(), |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), + ], + cx, + ); + }); + + let (_, cloned_editor) = editor.update(cx, |editor, cx| { + cx.add_window(Default::default(), |cx| editor.clone(cx)) + }); + + let snapshot = editor.update(cx, |e, cx| e.snapshot(cx)); + let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx)); + + assert_eq!( + cloned_editor.update(cx, |e, cx| e.display_text(cx)), + editor.update(cx, |e, cx| e.display_text(cx)) + ); + assert_eq!( + cloned_snapshot + .folds_in_range(0..text.len()) + .collect::>(), + snapshot.folds_in_range(0..text.len()).collect::>(), + ); + assert_set_eq!( + cloned_editor.read(cx).selections.ranges::(cx), + editor.read(cx).selections.ranges(cx) + ); + assert_set_eq!( + cloned_editor.update(cx, |e, cx| e.selections.display_ranges(cx)), + editor.update(cx, |e, cx| e.selections.display_ranges(cx)) + ); +} + +#[gpui::test] +fn test_navigation_history(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + use workspace::Item; + let (_, pane) = cx.add_window(Default::default(), |cx| Pane::new(None, cx)); + let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx); + + cx.add_view(&pane, |cx| { + let mut editor = build_editor(buffer.clone(), cx); + let handle = cx.handle(); + editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle))); + + fn pop_history(editor: &mut Editor, cx: &mut MutableAppContext) -> Option { + editor.nav_history.as_mut().unwrap().pop_backward(cx) + } + + // Move the cursor a small distance. + // Nothing is added to the navigation history. + editor.change_selections(None, cx, |s| { + s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]) + }); + editor.change_selections(None, cx, |s| { + s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]) + }); + assert!(pop_history(&mut editor, cx).is_none()); + + // Move the cursor a large distance. + // The history can jump back to the previous position. + editor.change_selections(None, cx, |s| { + s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)]) + }); + let nav_entry = pop_history(&mut editor, cx).unwrap(); + editor.navigate(nav_entry.data.unwrap(), cx); + assert_eq!(nav_entry.item.id(), cx.view_id()); + assert_eq!( + editor.selections.display_ranges(cx), + &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)] + ); + assert!(pop_history(&mut editor, cx).is_none()); + + // Move the cursor a small distance via the mouse. + // Nothing is added to the navigation history. + editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx); + editor.end_selection(cx); + assert_eq!( + editor.selections.display_ranges(cx), + &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)] + ); + assert!(pop_history(&mut editor, cx).is_none()); + + // Move the cursor a large distance via the mouse. + // The history can jump back to the previous position. + editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx); + editor.end_selection(cx); + assert_eq!( + editor.selections.display_ranges(cx), + &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)] + ); + let nav_entry = pop_history(&mut editor, cx).unwrap(); + editor.navigate(nav_entry.data.unwrap(), cx); + assert_eq!(nav_entry.item.id(), cx.view_id()); + assert_eq!( + editor.selections.display_ranges(cx), + &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)] + ); + assert!(pop_history(&mut editor, cx).is_none()); + + // Set scroll position to check later + editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx); + let original_scroll_position = editor.scroll_position; + let original_scroll_top_anchor = editor.scroll_top_anchor.clone(); + + // Jump to the end of the document and adjust scroll + editor.move_to_end(&MoveToEnd, cx); + editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx); + assert_ne!(editor.scroll_position, original_scroll_position); + assert_ne!(editor.scroll_top_anchor, original_scroll_top_anchor); + + let nav_entry = pop_history(&mut editor, cx).unwrap(); + editor.navigate(nav_entry.data.unwrap(), cx); + assert_eq!(editor.scroll_position, original_scroll_position); + assert_eq!(editor.scroll_top_anchor, original_scroll_top_anchor); + + // Ensure we don't panic when navigation data contains invalid anchors *and* points. + let mut invalid_anchor = editor.scroll_top_anchor.clone(); + invalid_anchor.text_anchor.buffer_id = Some(999); + let invalid_point = Point::new(9999, 0); + editor.navigate( + Box::new(NavigationData { + cursor_anchor: invalid_anchor.clone(), + cursor_position: invalid_point, + scroll_top_anchor: invalid_anchor, + scroll_top_row: invalid_point.row, + scroll_position: Default::default(), + }), + cx, + ); + assert_eq!( + editor.selections.display_ranges(cx), + &[editor.max_point(cx)..editor.max_point(cx)] + ); + assert_eq!( + editor.scroll_position(cx), + vec2f(0., editor.max_point(cx).row() as f32) + ); + + editor + }); +} + +#[gpui::test] +fn test_cancel(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx); + let (_, view) = cx.add_window(Default::default(), |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, Vector2F::zero(), cx); + view.end_selection(cx); + + view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx); + view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx); + view.end_selection(cx); + assert_eq!( + view.selections.display_ranges(cx), + [ + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), + DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1), + ] + ); + }); + + view.update(cx, |view, cx| { + view.cancel(&Cancel, cx); + assert_eq!( + view.selections.display_ranges(cx), + [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)] + ); + }); + + view.update(cx, |view, cx| { + view.cancel(&Cancel, cx); + assert_eq!( + view.selections.display_ranges(cx), + [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)] + ); + }); +} + +#[gpui::test] +fn test_fold(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple( + &" + impl Foo { + // Hello! + + fn a() { + 1 + } + + fn b() { + 2 + } + + fn c() { + 3 + } + } + " + .unindent(), + cx, + ); + let (_, view) = cx.add_window(Default::default(), |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), buffer.read(cx).read(cx).text()); + }); +} + +#[gpui::test] +fn test_move_cursor(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx); + let (_, view) = cx.add_window(Default::default(), |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 gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx); + let (_, view) = cx.add_window(Default::default(), |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), + ], + cx, + ); + assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\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…".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_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())] + ); + view.move_left(&MoveLeft, cx); + assert_eq!( + view.selections.display_ranges(cx), + &[empty_range(0, "ⓐ".len())] + ); + }); +} + +#[gpui::test] +fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx); + let (_, view) = cx.add_window(Default::default(), |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 gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple("abc\n def", cx); + let (_, view) = cx.add_window(Default::default(), |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 gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx); + let (_, view) = cx.add_window(Default::default(), |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); + }); +} + +#[gpui::test] +fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx); + let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); + + view.update(cx, |view, cx| { + view.set_wrap_width(Some(140.), cx); + assert_eq!( + view.display_text(cx), + "use one::{\n two::three::\n four::five\n};" + ); + + view.change_selections(None, cx, |s| { + s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]); + }); + + view.move_to_next_word_end(&MoveToNextWordEnd, cx); + assert_eq!( + view.selections.display_ranges(cx), + &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)] + ); + + view.move_to_next_word_end(&MoveToNextWordEnd, cx); + assert_eq!( + view.selections.display_ranges(cx), + &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)] + ); + + view.move_to_next_word_end(&MoveToNextWordEnd, cx); + assert_eq!( + view.selections.display_ranges(cx), + &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)] + ); + + view.move_to_next_word_end(&MoveToNextWordEnd, cx); + assert_eq!( + view.selections.display_ranges(cx), + &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)] + ); + + view.move_to_previous_word_start(&MoveToPreviousWordStart, cx); + assert_eq!( + view.selections.display_ranges(cx), + &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)] + ); + + view.move_to_previous_word_start(&MoveToPreviousWordStart, cx); + assert_eq!( + view.selections.display_ranges(cx), + &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)] + ); + }); +} + +#[gpui::test] +async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) { + let mut cx = EditorTestContext::new(cx); + 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 gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple("one two three four", cx); + let (_, view) = cx.add_window(Default::default(), |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!(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!(buffer.read(cx).read(cx).text(), "e t te our"); +} + +#[gpui::test] +fn test_newline(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx); + let (_, view) = cx.add_window(Default::default(), |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 gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple( + " + a + b( + X + ) + c( + X + ) + " + .unindent() + .as_str(), + cx, + ); + + let (_, editor) = cx.add_window(Default::default(), |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 + }); + + // Edit the buffer directly, deleting ranges surrounding the editor's selections + 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() + ); + }); + + editor.update(cx, |editor, cx| { + 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_below(cx: &mut gpui::TestAppContext) { + let mut cx = EditorTestContext::new(cx); + cx.update(|cx| { + cx.update_global::(|settings, _| { + settings.editor_overrides.tab_size = Some(NonZeroU32::new(4).unwrap()); + }); + }); + + 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)); + + 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] +fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx); + let (_, editor) = cx.add_window(Default::default(), |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 + }); + + // Edit the buffer directly, deleting ranges surrounding the editor's selections + 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()); + }); + + editor.update(cx, |editor, cx| { + 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) { + let mut cx = EditorTestContext::new(cx); + cx.update(|cx| { + cx.update_global::(|settings, _| { + settings.editor_overrides.tab_size = Some(NonZeroU32::new(3).unwrap()); + }); + }); + 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_on_blank_line_auto_indents(cx: &mut gpui::TestAppContext) { + let mut cx = EditorTestContext::new(cx); + 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_indent_outdent(cx: &mut gpui::TestAppContext) { + let mut cx = EditorTestContext::new(cx); + + 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) { + let mut cx = EditorTestContext::new(cx); + cx.update(|cx| { + cx.update_global::(|settings, _| { + settings.editor_overrides.hard_tabs = Some(true); + }); + }); + + // 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 gpui::MutableAppContext) { + cx.set_global( + Settings::test(cx) + .with_language_defaults( + "TOML", + EditorSettings { + tab_size: Some(2.try_into().unwrap()), + ..Default::default() + }, + ) + .with_language_defaults( + "Rust", + EditorSettings { + tab_size: Some(4.try_into().unwrap()), + ..Default::default() + }, + ), + ); + let toml_language = Arc::new(Language::new( + LanguageConfig { + name: "TOML".into(), + ..Default::default() + }, + None, + )); + let rust_language = Arc::new(Language::new( + LanguageConfig { + name: "Rust".into(), + ..Default::default() + }, + None, + )); + + let toml_buffer = + cx.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx)); + let rust_buffer = cx.add_model(|cx| { + Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx) + }); + let multibuffer = cx.add_model(|cx| { + let mut multibuffer = MultiBuffer::new(0); + multibuffer.push_excerpts( + toml_buffer.clone(), + [ExcerptRange { + context: Point::new(0, 0)..Point::new(2, 0), + primary: None, + }], + cx, + ); + multibuffer.push_excerpts( + rust_buffer.clone(), + [ExcerptRange { + context: Point::new(0, 0)..Point::new(1, 0), + primary: None, + }], + cx, + ); + multibuffer + }); + + cx.add_window(Default::default(), |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) { + let mut cx = EditorTestContext::new(cx); + + // 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) { + let mut cx = EditorTestContext::new(cx); + + 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 gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); + let (_, view) = cx.add_window(Default::default(), |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) + ] + ); + }); + + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); + let (_, view) = cx.add_window(Default::default(), |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)] + ); + }); +} + +#[gpui::test] +fn test_duplicate_line(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); + let (_, view) = cx.add_window(Default::default(), |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 buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx); + let (_, view) = cx.add_window(Default::default(), |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 gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx); + let (_, view) = cx.add_window(Default::default(), |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), + ], + 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 gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx); + let snapshot = buffer.read(cx).snapshot(cx); + let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); + editor.update(cx, |editor, cx| { + editor.insert_blocks( + [BlockProperties { + style: BlockStyle::Fixed, + position: snapshot.anchor_after(Point::new(2, 0)), + disposition: BlockDisposition::Below, + height: 1, + render: Arc::new(|_| Empty::new().boxed()), + }], + cx, + ); + editor.change_selections(None, cx, |s| { + s.select_ranges([Point::new(2, 0)..Point::new(2, 0)]) + }); + editor.move_line_down(&MoveLineDown, cx); + }); +} + +#[gpui::test] +fn test_transpose(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + + _ = cx + .add_window(Default::default(), |cx| { + let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx); + + editor.change_selections(None, cx, |s| s.select_ranges([1..1])); + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "bac"); + assert_eq!(editor.selections.ranges(cx), [2..2]); + + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "bca"); + assert_eq!(editor.selections.ranges(cx), [3..3]); + + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "bac"); + assert_eq!(editor.selections.ranges(cx), [3..3]); + + editor + }) + .1; + + _ = cx + .add_window(Default::default(), |cx| { + let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx); + + editor.change_selections(None, cx, |s| s.select_ranges([3..3])); + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "acb\nde"); + assert_eq!(editor.selections.ranges(cx), [3..3]); + + editor.change_selections(None, cx, |s| s.select_ranges([4..4])); + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "acbd\ne"); + assert_eq!(editor.selections.ranges(cx), [5..5]); + + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "acbde\n"); + assert_eq!(editor.selections.ranges(cx), [6..6]); + + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "acbd\ne"); + assert_eq!(editor.selections.ranges(cx), [6..6]); + + editor + }) + .1; + + _ = cx + .add_window(Default::default(), |cx| { + let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx); + + editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4])); + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "bacd\ne"); + assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]); + + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "bcade\n"); + assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]); + + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "bcda\ne"); + assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]); + + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "bcade\n"); + assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]); + + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "bcaed\n"); + assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]); + + editor + }) + .1; + + _ = cx + .add_window(Default::default(), |cx| { + let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx); + + editor.change_selections(None, cx, |s| s.select_ranges([4..4])); + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "🏀🍐✋"); + assert_eq!(editor.selections.ranges(cx), [8..8]); + + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "🏀✋🍐"); + assert_eq!(editor.selections.ranges(cx), [11..11]); + + editor.transpose(&Default::default(), cx); + assert_eq!(editor.text(cx), "🏀🍐✋"); + assert_eq!(editor.selections.ranges(cx), [11..11]); + + editor + }) + .1; +} + +#[gpui::test] +async fn test_clipboard(cx: &mut gpui::TestAppContext) { + let mut cx = EditorTestContext::new(cx); + + 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(indoc! {" + ( one✅ + three + five ) ˇtwo one✅ four three six five ( one✅ + three + five ) ˇ"}); + + // Cut with three selections, one of which is full-line. + cx.set_state(indoc! {" + 1«2ˇ»3 + 4ˇ567 + «8ˇ»9"}); + cx.update_editor(|e, cx| e.cut(&Cut, cx)); + cx.assert_editor_state(indoc! {" + 1ˇ3 + ˇ9"}); + + // Paste with three selections, noticing how the copied selection that was full-line + // gets inserted before the second cursor. + cx.set_state(indoc! {" + 1ˇ3 + 9ˇ + «oˇ»ne"}); + cx.update_editor(|e, cx| e.paste(&Paste, cx)); + cx.assert_editor_state(indoc! {" + 12ˇ3 + 4567 + 9ˇ + 8ˇne"}); + + // Copy with a single cursor only, which writes the whole line into the clipboard. + cx.set_state(indoc! {" + The quick brown + fox juˇmps over + the lazy dog"}); + cx.update_editor(|e, cx| e.copy(&Copy, cx)); + cx.cx.assert_clipboard_content(Some("fox jumps over\n")); + + // Paste with three selections, noticing how the copied full-line selection is inserted + // before the empty selections but replaces the selection that is non-empty. + cx.set_state(indoc! {" + Tˇhe quick brown + «foˇ»x jumps over + tˇhe lazy dog"}); + cx.update_editor(|e, cx| e.paste(&Paste, cx)); + cx.assert_editor_state(indoc! {" + fox jumps over + Tˇhe quick brown + fox jumps over + ˇx jumps over + fox jumps over + tˇhe lazy dog"}); +} + +#[gpui::test] +async fn test_paste_multiline(cx: &mut gpui::TestAppContext) { + let mut cx = EditorTestContext::new(cx); + 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 gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx); + let (_, view) = cx.add_window(Default::default(), |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 gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx); + let (_, view) = cx.add_window(Default::default(), |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 gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx); + let (_, view) = cx.add_window(Default::default(), |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), + ], + cx, + ); + view.change_selections(None, cx, |s| { + s.select_display_ranges([ + DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1), + DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), + DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0), + DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4), + ]) + }); + assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i"); + }); + + view.update(cx, |view, cx| { + view.split_selection_into_lines(&SplitSelectionIntoLines, cx); + assert_eq!( + view.display_text(cx), + "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i" + ); + assert_eq!( + view.selections.display_ranges(cx), + [ + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1), + DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2), + DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0), + DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4) + ] + ); + }); + + view.update(cx, |view, cx| { + view.change_selections(None, cx, |s| { + s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)]) + }); + view.split_selection_into_lines(&SplitSelectionIntoLines, cx); + assert_eq!( + view.display_text(cx), + "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii" + ); + assert_eq!( + view.selections.display_ranges(cx), + [ + DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5), + DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5), + DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5), + DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5), + DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5), + DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5), + DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5), + DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0) + ] + ); + }); +} + +#[gpui::test] +fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx); + let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx)); + + view.update(cx, |view, cx| { + view.change_selections(None, cx, |s| { + s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]) + }); + }); + view.update(cx, |view, cx| { + view.add_selection_above(&AddSelectionAbove, cx); + assert_eq!( + view.selections.display_ranges(cx), + vec![ + DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), + DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3) + ] + ); + }); + + view.update(cx, |view, cx| { + view.add_selection_above(&AddSelectionAbove, cx); + assert_eq!( + view.selections.display_ranges(cx), + vec![ + DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), + DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3) + ] + ); + }); + + view.update(cx, |view, cx| { + view.add_selection_below(&AddSelectionBelow, cx); + assert_eq!( + view.selections.display_ranges(cx), + vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)] + ); + + view.undo_selection(&UndoSelection, cx); + assert_eq!( + view.selections.display_ranges(cx), + vec![ + DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3), + DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3) + ] + ); + + view.redo_selection(&RedoSelection, cx); + assert_eq!( + view.selections.display_ranges(cx), + vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)] + ); + }); + + view.update(cx, |view, cx| { + view.add_selection_below(&AddSelectionBelow, cx); + assert_eq!( + view.selections.display_ranges(cx), + vec![ + DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3), + DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3) + ] + ); + }); + + view.update(cx, |view, cx| { + view.add_selection_below(&AddSelectionBelow, cx); + assert_eq!( + view.selections.display_ranges(cx), + vec![ + DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3), + DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3) + ] + ); + }); + + view.update(cx, |view, cx| { + view.change_selections(None, cx, |s| { + s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]) + }); + }); + view.update(cx, |view, cx| { + view.add_selection_below(&AddSelectionBelow, cx); + assert_eq!( + view.selections.display_ranges(cx), + vec![ + DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3), + DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3) + ] + ); + }); + + view.update(cx, |view, cx| { + view.add_selection_below(&AddSelectionBelow, cx); + assert_eq!( + view.selections.display_ranges(cx), + vec![ + DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3), + DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3) + ] + ); + }); + + view.update(cx, |view, cx| { + view.add_selection_above(&AddSelectionAbove, cx); + assert_eq!( + view.selections.display_ranges(cx), + vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)] + ); + }); + + view.update(cx, |view, cx| { + view.add_selection_above(&AddSelectionAbove, cx); + assert_eq!( + view.selections.display_ranges(cx), + vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)] + ); + }); + + view.update(cx, |view, cx| { + view.change_selections(None, cx, |s| { + s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)]) + }); + view.add_selection_below(&AddSelectionBelow, cx); + assert_eq!( + view.selections.display_ranges(cx), + vec![ + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), + DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4), + DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2), + ] + ); + }); + + view.update(cx, |view, cx| { + view.add_selection_below(&AddSelectionBelow, cx); + assert_eq!( + view.selections.display_ranges(cx), + vec![ + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), + DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4), + DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2), + DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4), + ] + ); + }); + + view.update(cx, |view, cx| { + view.add_selection_above(&AddSelectionAbove, cx); + assert_eq!( + view.selections.display_ranges(cx), + vec![ + DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3), + DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4), + DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2), + ] + ); + }); + + view.update(cx, |view, cx| { + view.change_selections(None, cx, |s| { + s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)]) + }); + }); + view.update(cx, |view, cx| { + view.add_selection_above(&AddSelectionAbove, cx); + assert_eq!( + view.selections.display_ranges(cx), + vec![ + DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1), + DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1), + DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1), + DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1), + ] + ); + }); + + view.update(cx, |view, cx| { + view.add_selection_below(&AddSelectionBelow, cx); + assert_eq!( + view.selections.display_ranges(cx), + vec![ + DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1), + DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1), + DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1), + ] + ); + }); +} + +#[gpui::test] +async fn test_select_next(cx: &mut gpui::TestAppContext) { + let mut cx = EditorTestContext::new(cx); + cx.set_state("abc\nˇabc abc\ndefabc\nabc"); + + cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx)); + cx.assert_editor_state("abc\n«abcˇ» abc\ndefabc\nabc"); + + cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx)); + 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)); + cx.assert_editor_state("abc\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»"); + + cx.update_editor(|e, cx| e.select_next(&SelectNext::default(), cx)); + cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndefabc\n«abcˇ»"); +} + +#[gpui::test] +async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) { + cx.update(|cx| cx.set_global(Settings::test(cx))); + let language = Arc::new(Language::new( + LanguageConfig::default(), + Some(tree_sitter_rust::language()), + )); + + let text = r#" + use mod1::mod2::{mod3, mod4}; + + fn fn_1(param1: bool, param2: &str) { + let var1 = "text"; + } + "# + .unindent(); + + let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); + let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); + let (_, view) = cx.add_window(|cx| build_editor(buffer, cx)); + view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) + .await; + + view.update(cx, |view, cx| { + view.change_selections(None, cx, |s| { + s.select_display_ranges([ + DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25), + DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12), + DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18), + ]); + }); + view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx); + }); + assert_eq!( + view.update(cx, |view, cx| { view.selections.display_ranges(cx) }), + &[ + DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27), + DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7), + DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21), + ] + ); + + view.update(cx, |view, cx| { + view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx); + }); + assert_eq!( + view.update(cx, |view, cx| view.selections.display_ranges(cx)), + &[ + DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28), + DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0), + ] + ); + + view.update(cx, |view, cx| { + view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx); + }); + assert_eq!( + view.update(cx, |view, cx| view.selections.display_ranges(cx)), + &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)] + ); + + // Trying to expand the selected syntax node one more time has no effect. + view.update(cx, |view, cx| { + view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx); + }); + assert_eq!( + view.update(cx, |view, cx| view.selections.display_ranges(cx)), + &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)] + ); + + view.update(cx, |view, cx| { + view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx); + }); + assert_eq!( + view.update(cx, |view, cx| view.selections.display_ranges(cx)), + &[ + DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28), + DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0), + ] + ); + + view.update(cx, |view, cx| { + view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx); + }); + assert_eq!( + view.update(cx, |view, cx| view.selections.display_ranges(cx)), + &[ + DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27), + DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7), + DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21), + ] + ); + + view.update(cx, |view, cx| { + view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx); + }); + assert_eq!( + view.update(cx, |view, cx| view.selections.display_ranges(cx)), + &[ + DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25), + DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12), + DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18), + ] + ); + + // Trying to shrink the selected syntax node one more time has no effect. + view.update(cx, |view, cx| { + view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx); + }); + assert_eq!( + view.update(cx, |view, cx| view.selections.display_ranges(cx)), + &[ + DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25), + DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12), + DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18), + ] + ); + + // Ensure that we keep expanding the selection if the larger selection starts or ends within + // a fold. + view.update(cx, |view, cx| { + view.fold_ranges( + vec![ + Point::new(0, 21)..Point::new(0, 24), + Point::new(3, 20)..Point::new(3, 22), + ], + 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) { + cx.update(|cx| cx.set_global(Settings::test(cx))); + let language = Arc::new( + Language::new( + LanguageConfig { + brackets: 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() + }, + Some(tree_sitter_rust::language()), + ) + .with_indents_query( + r#" + (_ "(" ")" @end) @indent + (_ "{" "}" @end) @indent + "#, + ) + .unwrap(), + ); + + let text = "fn a() {}"; + + let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); + let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); + let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx)); + editor + .condition(cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx)) + .await; + + editor.update(cx, |editor, cx| { + editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9])); + editor.newline(&Newline, cx); + assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n"); + assert_eq!( + editor.selections.ranges(cx), + &[ + Point::new(1, 4)..Point::new(1, 4), + Point::new(3, 4)..Point::new(3, 4), + Point::new(5, 0)..Point::new(5, 0) + ] + ); + }); +} + +#[gpui::test] +async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) { + let mut cx = EditorTestContext::new(cx); + + let language = Arc::new(Language::new( + LanguageConfig { + brackets: 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: false, + newline: true, + }, + ], + 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(), + ); + + // 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"); + + // 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"); +} + +#[gpui::test] +async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) { + let mut cx = EditorTestContext::new(cx); + + let html_language = Arc::new( + Language::new( + LanguageConfig { + name: "HTML".into(), + brackets: vec![ + BracketPair { + start: "<".into(), + end: ">".into(), + ..Default::default() + }, + BracketPair { + start: "{".into(), + end: "}".into(), + ..Default::default() + }, + BracketPair { + start: "(".into(), + end: ")".into(), + ..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: vec![ + BracketPair { + start: "/*".into(), + end: " */".into(), + ..Default::default() + }, + BracketPair { + start: "{".into(), + end: "}".into(), + ..Default::default() + }, + BracketPair { + start: "(".into(), + end: ")".into(), + ..Default::default() + }, + ], + autoclose_before: "})]>".into(), + ..Default::default() + }, + Some(tree_sitter_javascript::language()), + )); + + 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#" + ˇ + + ˇ + "# + .unindent(), + ); + + // Precondition: different languages are active at different locations. + cx.update_editor(|editor, cx| { + let snapshot = editor.snapshot(cx); + let cursors = editor.selections.ranges::(cx); + let languages = cursors + .iter() + .map(|c| snapshot.language_at(c.start).unwrap().name()) + .collect::>(); + 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#" + + + + "# + .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#" +
+ + + "# + .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#" + + + + "# + .unindent(), + ); + cx.update_editor(|editor, cx| { + editor.handle_input(">", cx); + }); + cx.assert_editor_state( + &r#" + ˇ + + ˇ + "# + .unindent(), + ); + + // Reset + cx.set_state( + &r#" + ˇ + + ˇ + "# + .unindent(), + ); + + cx.update_editor(|editor, cx| { + editor.handle_input("<", cx); + }); + cx.assert_editor_state( + &r#" + <ˇ> + + <ˇ> + "# + .unindent(), + ); + + // When backspacing, the closing angle brackets are removed. + cx.update_editor(|editor, cx| { + editor.backspace(&Backspace, cx); + }); + cx.assert_editor_state( + &r#" + ˇ + + ˇ + "# + .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#" + /*ˇ + + /*ˇ + "# + .unindent(), + ); +} + +#[gpui::test] +async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) { + cx.update(|cx| cx.set_global(Settings::test(cx))); + let language = Arc::new(Language::new( + LanguageConfig { + brackets: vec![BracketPair { + start: "{".to_string(), + end: "}".to_string(), + close: true, + newline: true, + }], + ..Default::default() + }, + Some(tree_sitter_rust::language()), + )); + + let text = r#" + a + b + c + "# + .unindent(); + + let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); + let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); + let (_, view) = cx.add_window(|cx| build_editor(buffer, cx)); + view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) + .await; + + view.update(cx, |view, cx| { + view.change_selections(None, cx, |s| { + s.select_display_ranges([ + DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1), + DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1), + DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1), + ]) + }); + + view.handle_input("{", cx); + view.handle_input("{", cx); + view.handle_input("{", cx); + assert_eq!( + view.text(cx), + " + {{{a}}} + {{{b}}} + {{{c}}} + " + .unindent() + ); + assert_eq!( + view.selections.display_ranges(cx), + [ + DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4), + DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4), + DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4) + ] + ); + + view.undo(&Undo, cx); + 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) + ] + ); + }); +} + +#[gpui::test] +async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) { + cx.update(|cx| cx.set_global(Settings::test(cx))); + let language = Arc::new(Language::new( + LanguageConfig { + brackets: vec![BracketPair { + start: "{".to_string(), + end: "}".to_string(), + close: true, + newline: true, + }], + autoclose_before: "}".to_string(), + ..Default::default() + }, + Some(tree_sitter_rust::language()), + )); + + let text = r#" + a + b + c + "# + .unindent(); + + let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); + let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); + let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx)); + editor + .condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) + .await; + + editor.update(cx, |editor, cx| { + editor.change_selections(None, cx, |s| { + s.select_ranges([ + Point::new(0, 1)..Point::new(0, 1), + Point::new(1, 1)..Point::new(1, 1), + Point::new(2, 1)..Point::new(2, 1), + ]) + }); + + editor.handle_input("{", cx); + editor.handle_input("{", cx); + editor.handle_input("_", cx); + assert_eq!( + editor.text(cx), + " + a{{_}} + b{{_}} + c{{_}} + " + .unindent() + ); + assert_eq!( + editor.selections.ranges::(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::(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::(cx), + [ + Point::new(0, 1)..Point::new(0, 1), + Point::new(1, 1)..Point::new(1, 1), + Point::new(2, 1)..Point::new(2, 1) + ] + ); + }); +} + +#[gpui::test] +async fn test_snippets(cx: &mut gpui::TestAppContext) { + cx.update(|cx| cx.set_global(Settings::test(cx))); + + let (text, insertion_ranges) = marked_text_ranges( + indoc! {" + a.ˇ b + a.ˇ b + a.ˇ b + "}, + false, + ); + + let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx)); + let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx)); + + 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, 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::(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) { + cx.foreground().forbid_parking(); + + let mut language = Language::new( + LanguageConfig { + name: "Rust".into(), + path_suffixes: vec!["rs".to_string()], + ..Default::default() + }, + Some(tree_sitter_rust::language()), + ); + let mut fake_servers = language + .set_fake_lsp_adapter(Arc::new(FakeLspAdapter { + capabilities: lsp::ServerCapabilities { + document_formatting_provider: Some(lsp::OneOf::Left(true)), + ..Default::default() + }, + ..Default::default() + })) + .await; + + let fs = FakeFs::new(cx.background()); + fs.insert_file("/file.rs", Default::default()).await; + + let project = Project::test(fs, ["/file.rs".as_ref()], cx).await; + project.update(cx, |project, _| project.languages().add(Arc::new(language))); + let buffer = project + .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx)) + .await + .unwrap(); + + cx.foreground().start_waiting(); + let fake_server = fake_servers.next().await.unwrap(); + + let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); + let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx)); + editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx)); + assert!(cx.read(|cx| editor.is_dirty(cx))); + + let save = cx.update(|cx| editor.save(project.clone(), cx)); + fake_server + .handle_request::(move |params, _| async move { + assert_eq!( + params.text_document.uri, + lsp::Url::from_file_path("/file.rs").unwrap() + ); + assert_eq!(params.options.tab_size, 4); + Ok(Some(vec![lsp::TextEdit::new( + lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)), + ", ".to_string(), + )])) + }) + .next() + .await; + cx.foreground().start_waiting(); + save.await.unwrap(); + assert_eq!( + editor.read_with(cx, |editor, cx| editor.text(cx)), + "one, two\nthree\n" + ); + assert!(!cx.read(|cx| editor.is_dirty(cx))); + + editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx)); + assert!(cx.read(|cx| editor.is_dirty(cx))); + + // Ensure we can still save even if formatting hangs. + fake_server.handle_request::(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 = cx.update(|cx| editor.save(project.clone(), cx)); + cx.foreground().advance_clock(super::FORMAT_TIMEOUT); + cx.foreground().start_waiting(); + save.await.unwrap(); + assert_eq!( + editor.read_with(cx, |editor, cx| editor.text(cx)), + "one\ntwo\nthree\n" + ); + assert!(!cx.read(|cx| editor.is_dirty(cx))); + + // Set rust language override and assert overriden tabsize is sent to language server + cx.update(|cx| { + cx.update_global::(|settings, _| { + settings.language_overrides.insert( + "Rust".into(), + EditorSettings { + tab_size: Some(8.try_into().unwrap()), + ..Default::default() + }, + ); + }) + }); + + let save = cx.update(|cx| editor.save(project.clone(), cx)); + fake_server + .handle_request::(move |params, _| async move { + assert_eq!( + params.text_document.uri, + lsp::Url::from_file_path("/file.rs").unwrap() + ); + assert_eq!(params.options.tab_size, 8); + Ok(Some(vec![])) + }) + .next() + .await; + cx.foreground().start_waiting(); + save.await.unwrap(); +} + +#[gpui::test] +async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) { + cx.foreground().forbid_parking(); + + let mut language = Language::new( + LanguageConfig { + name: "Rust".into(), + path_suffixes: vec!["rs".to_string()], + ..Default::default() + }, + Some(tree_sitter_rust::language()), + ); + let mut fake_servers = language + .set_fake_lsp_adapter(Arc::new(FakeLspAdapter { + capabilities: lsp::ServerCapabilities { + document_range_formatting_provider: Some(lsp::OneOf::Left(true)), + ..Default::default() + }, + ..Default::default() + })) + .await; + + let fs = FakeFs::new(cx.background()); + fs.insert_file("/file.rs", Default::default()).await; + + let project = Project::test(fs, ["/file.rs".as_ref()], cx).await; + project.update(cx, |project, _| project.languages().add(Arc::new(language))); + let buffer = project + .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx)) + .await + .unwrap(); + + cx.foreground().start_waiting(); + let fake_server = fake_servers.next().await.unwrap(); + + let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); + let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx)); + editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx)); + assert!(cx.read(|cx| editor.is_dirty(cx))); + + let save = cx.update(|cx| editor.save(project.clone(), cx)); + fake_server + .handle_request::(move |params, _| async move { + assert_eq!( + params.text_document.uri, + lsp::Url::from_file_path("/file.rs").unwrap() + ); + assert_eq!(params.options.tab_size, 4); + Ok(Some(vec![lsp::TextEdit::new( + lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)), + ", ".to_string(), + )])) + }) + .next() + .await; + cx.foreground().start_waiting(); + save.await.unwrap(); + assert_eq!( + editor.read_with(cx, |editor, cx| editor.text(cx)), + "one, two\nthree\n" + ); + assert!(!cx.read(|cx| editor.is_dirty(cx))); + + editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx)); + assert!(cx.read(|cx| editor.is_dirty(cx))); + + // Ensure we can still save even if formatting hangs. + fake_server.handle_request::( + 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 = cx.update(|cx| editor.save(project.clone(), cx)); + cx.foreground().advance_clock(super::FORMAT_TIMEOUT); + cx.foreground().start_waiting(); + save.await.unwrap(); + assert_eq!( + editor.read_with(cx, |editor, cx| editor.text(cx)), + "one\ntwo\nthree\n" + ); + assert!(!cx.read(|cx| editor.is_dirty(cx))); + + // Set rust language override and assert overriden tabsize is sent to language server + cx.update(|cx| { + cx.update_global::(|settings, _| { + settings.language_overrides.insert( + "Rust".into(), + EditorSettings { + tab_size: Some(8.try_into().unwrap()), + ..Default::default() + }, + ); + }) + }); + + let save = cx.update(|cx| editor.save(project.clone(), cx)); + fake_server + .handle_request::(move |params, _| async move { + assert_eq!( + params.text_document.uri, + lsp::Url::from_file_path("/file.rs").unwrap() + ); + assert_eq!(params.options.tab_size, 8); + Ok(Some(vec![])) + }) + .next() + .await; + cx.foreground().start_waiting(); + save.await.unwrap(); +} + +#[gpui::test] +async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) { + cx.foreground().forbid_parking(); + + let mut language = Language::new( + LanguageConfig { + name: "Rust".into(), + path_suffixes: vec!["rs".to_string()], + ..Default::default() + }, + Some(tree_sitter_rust::language()), + ); + let mut fake_servers = language + .set_fake_lsp_adapter(Arc::new(FakeLspAdapter { + capabilities: lsp::ServerCapabilities { + document_formatting_provider: Some(lsp::OneOf::Left(true)), + ..Default::default() + }, + ..Default::default() + })) + .await; + + let fs = FakeFs::new(cx.background()); + fs.insert_file("/file.rs", Default::default()).await; + + let project = Project::test(fs, ["/file.rs".as_ref()], cx).await; + project.update(cx, |project, _| project.languages().add(Arc::new(language))); + let buffer = project + .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx)) + .await + .unwrap(); + + cx.foreground().start_waiting(); + let fake_server = fake_servers.next().await.unwrap(); + + let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); + let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx)); + 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(), cx)); + fake_server + .handle_request::(move |params, _| async move { + assert_eq!( + params.text_document.uri, + lsp::Url::from_file_path("/file.rs").unwrap() + ); + assert_eq!(params.options.tab_size, 4); + Ok(Some(vec![lsp::TextEdit::new( + lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)), + ", ".to_string(), + )])) + }) + .next() + .await; + cx.foreground().start_waiting(); + format.await.unwrap(); + assert_eq!( + editor.read_with(cx, |editor, cx| editor.text(cx)), + "one, two\nthree\n" + ); + + editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx)); + // Ensure we don't lock if formatting hangs. + fake_server.handle_request::(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, cx)); + cx.foreground().advance_clock(super::FORMAT_TIMEOUT); + cx.foreground().start_waiting(); + format.await.unwrap(); + assert_eq!( + editor.read_with(cx, |editor, cx| editor.text(cx)), + "one\ntwo\nthree\n" + ); +} + +#[gpui::test] +async fn test_completion(cx: &mut gpui::TestAppContext) { + 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ˇ + 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.move_down(&MoveDown, 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(( + 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.is_none())); + cx.simulate_keystroke("s"); + assert!(cx.editor(|e, _| e.context_menu.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 + 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 + 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::(|settings, _| { + settings.show_completions_on_input = false; + }) + }); + cx.set_state("editorˇ"); + cx.simulate_keystroke("."); + assert!(cx.editor(|e, _| e.context_menu.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.is_none())); + cx.update_editor(|editor, cx| { + editor.show_completions(&ShowCompletions, cx); + }); + handle_completion_request(&mut cx, "editor.", 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(); + + // 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 + async fn handle_completion_request<'a>( + cx: &mut EditorLspTestContext<'a>, + marked_string: &str, + completions: Vec<&'static str>, + ) { + 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()); + + cx.handle_request::(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(), + ))) + } + }) + .next() + .await; + } + + async fn handle_resolve_completion_request<'a>( + cx: &mut EditorLspTestContext<'a>, + edit: Option<(&'static str, &'static str)>, + ) { + let edit = edit.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()); + vec![lsp::TextEdit::new(replace_range, new_text.to_string())] + }); + + cx.handle_request::(move |_, _, _| { + let edit = edit.clone(); + async move { + Ok(lsp::CompletionItem { + additional_text_edits: edit, + ..Default::default() + }) + } + }) + .next() + .await; + } +} + +#[gpui::test] +async fn test_toggle_comment(cx: &mut gpui::TestAppContext) { + cx.update(|cx| cx.set_global(Settings::test(cx))); + let language = Arc::new(Language::new( + LanguageConfig { + line_comment: Some("// ".into()), + ..Default::default() + }, + Some(tree_sitter_rust::language()), + )); + + let text = " + fn a() { + //b(); + // c(); + // d(); + } + " + .unindent(); + + let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); + let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); + let (_, view) = cx.add_window(|cx| build_editor(buffer, cx)); + + view.update(cx, |editor, cx| { + // If multiple selections intersect a line, the line is only + // toggled once. + editor.change_selections(None, cx, |s| { + s.select_display_ranges([ + DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3), + DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6), + ]) + }); + editor.toggle_comments(&ToggleComments, cx); + assert_eq!( + editor.text(cx), + " + fn a() { + b(); + c(); + d(); + } + " + .unindent() + ); + + // The comment prefix is inserted at the same column for every line + // in a selection. + editor.change_selections(None, cx, |s| { + s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)]) + }); + editor.toggle_comments(&ToggleComments, cx); + assert_eq!( + editor.text(cx), + " + fn a() { + // b(); + // c(); + // d(); + } + " + .unindent() + ); + + // If a selection ends at the beginning of a line, that line is not toggled. + editor.change_selections(None, cx, |s| { + s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)]) + }); + editor.toggle_comments(&ToggleComments, cx); + assert_eq!( + editor.text(cx), + " + fn a() { + // b(); + c(); + // d(); + } + " + .unindent() + ); + }); +} + +#[gpui::test] +async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) { + let mut cx = EditorTestContext::new(cx); + + let html_language = Arc::new( + Language::new( + LanguageConfig { + name: "HTML".into(), + block_comment: Some(("".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_javascript::language()), + )); + + 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#" +

A

ˇ +

B

ˇ +

C

ˇ + "# + .unindent(), + ); + cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); + cx.assert_editor_state( + &r#" + + + + "# + .unindent(), + ); + cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); + cx.assert_editor_state( + &r#" +

A

ˇ +

B

ˇ +

C

ˇ + "# + .unindent(), + ); + + // Toggle comments for mixture of empty and non-empty selections, where + // multiple selections occupy a given line. + cx.set_state( + &r#" +

+

ˇ»B

ˇ +

+

ˇ»D

ˇ + "# + .unindent(), + ); + + cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); + cx.assert_editor_state( + &r#" + + + "# + .unindent(), + ); + cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); + cx.assert_editor_state( + &r#" +

+

ˇ»B

ˇ +

+

ˇ»D

ˇ + "# + .unindent(), + ); + + // Toggle comments when different languages are active for different + // selections. + cx.set_state( + &r#" + ˇ + "# + .unindent(), + ); + cx.foreground().run_until_parked(); + cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); + cx.assert_editor_state( + &r#" + + // ˇvar x = new Y(); + + "# + .unindent(), + ); +} + +#[gpui::test] +fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx)); + let multibuffer = cx.add_model(|cx| { + let mut multibuffer = MultiBuffer::new(0); + multibuffer.push_excerpts( + buffer.clone(), + [ + ExcerptRange { + context: Point::new(0, 0)..Point::new(0, 4), + primary: None, + }, + ExcerptRange { + context: Point::new(1, 0)..Point::new(1, 4), + primary: None, + }, + ], + cx, + ); + multibuffer + }); + + assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb"); + + let (_, view) = cx.add_window(Default::default(), |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), + ] + ) + }); +} + +#[gpui::test] +fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let markers = vec![('[', ']').into(), ('(', ')').into()]; + let (initial_text, mut excerpt_ranges) = marked_text_ranges_by( + indoc! {" + [aaaa + (bbbb] + cccc)", + }, + markers.clone(), + ); + let excerpt_ranges = markers.into_iter().map(|marker| { + let context = excerpt_ranges.remove(&marker).unwrap()[0].clone(); + ExcerptRange { + context, + primary: None, + } + }); + let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx)); + let multibuffer = cx.add_model(|cx| { + let mut multibuffer = MultiBuffer::new(0); + multibuffer.push_excerpts(buffer, excerpt_ranges, cx); + multibuffer + }); + + let (_, view) = cx.add_window(Default::default(), |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 gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx)); + let mut excerpt1_id = None; + let multibuffer = cx.add_model(|cx| { + let mut multibuffer = MultiBuffer::new(0); + excerpt1_id = multibuffer + .push_excerpts( + buffer.clone(), + [ + ExcerptRange { + context: Point::new(0, 0)..Point::new(1, 4), + primary: None, + }, + ExcerptRange { + context: Point::new(1, 0)..Point::new(2, 4), + primary: None, + }, + ], + cx, + ) + .into_iter() + .next(); + multibuffer + }); + assert_eq!( + multibuffer.read(cx).read(cx).text(), + "aaaa\nbbbb\nbbbb\ncccc" + ); + let (_, editor) = cx.add_window(Default::default(), |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 gpui::MutableAppContext) { + cx.set_global(Settings::test(cx)); + let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx)); + let mut excerpt1_id = None; + let multibuffer = cx.add_model(|cx| { + let mut multibuffer = MultiBuffer::new(0); + excerpt1_id = multibuffer + .push_excerpts( + buffer.clone(), + [ + ExcerptRange { + context: Point::new(0, 0)..Point::new(1, 4), + primary: None, + }, + ExcerptRange { + context: Point::new(1, 0)..Point::new(2, 4), + primary: None, + }, + ], + cx, + ) + .into_iter() + .next(); + multibuffer + }); + assert_eq!( + multibuffer.read(cx).read(cx).text(), + "aaaa\nbbbb\nbbbb\ncccc" + ); + let (_, editor) = cx.add_window(Default::default(), |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) { + cx.update(|cx| cx.set_global(Settings::test(cx))); + let language = Arc::new( + Language::new( + LanguageConfig { + brackets: 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() + }, + Some(tree_sitter_rust::language()), + ) + .with_indents_query("") + .unwrap(), + ); + + let text = concat!( + "{ }\n", // Suppress rustfmt + " x\n", // + " /* */\n", // + "x\n", // + "{{} }\n", // + ); + + let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); + let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); + let (_, view) = cx.add_window(|cx| build_editor(buffer, cx)); + view.condition(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) + .await; + + view.update(cx, |view, cx| { + view.change_selections(None, cx, |s| { + s.select_display_ranges([ + DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3), + DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5), + DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4), + ]) + }); + view.newline(&Newline, cx); + + assert_eq!( + view.buffer().read(cx).read(cx).text(), + concat!( + "{ \n", // Suppress rustfmt + "\n", // + "}\n", // + " x\n", // + " /* \n", // + " \n", // + " */\n", // + "x\n", // + "{{} \n", // + "}\n", // + ) + ); + }); +} + +#[gpui::test] +fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) { + let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx); + + cx.set_global(Settings::test(cx)); + let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); + + editor.update(cx, |editor, cx| { + struct Type1; + struct Type2; + + let buffer = buffer.read(cx).snapshot(cx); + + let anchor_range = + |range: Range| buffer.anchor_after(range.start)..buffer.anchor_after(range.end); + + editor.highlight_background::( + vec![ + anchor_range(Point::new(2, 1)..Point::new(2, 3)), + anchor_range(Point::new(4, 2)..Point::new(4, 4)), + anchor_range(Point::new(6, 3)..Point::new(6, 5)), + anchor_range(Point::new(8, 4)..Point::new(8, 6)), + ], + |_| Color::red(), + cx, + ); + editor.highlight_background::( + vec![ + anchor_range(Point::new(3, 2)..Point::new(3, 5)), + anchor_range(Point::new(5, 3)..Point::new(5, 6)), + anchor_range(Point::new(7, 4)..Point::new(7, 7)), + anchor_range(Point::new(9, 5)..Point::new(9, 8)), + ], + |_| Color::green(), + cx, + ); + + let snapshot = editor.snapshot(cx); + let mut highlighted_ranges = editor.background_highlights_in_range( + anchor_range(Point::new(3, 4)..Point::new(7, 4)), + &snapshot, + cx.global::().theme.as_ref(), + ); + // Enforce a consistent ordering based on color without relying on the ordering of the + // highlight's `TypeId` which is non-deterministic. + highlighted_ranges.sort_unstable_by_key(|(_, color)| *color); + assert_eq!( + highlighted_ranges, + &[ + ( + DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5), + Color::green(), + ), + ( + DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6), + Color::green(), + ), + ( + DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4), + Color::red(), + ), + ( + DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5), + Color::red(), + ), + ] + ); + assert_eq!( + editor.background_highlights_in_range( + anchor_range(Point::new(5, 6)..Point::new(6, 4)), + &snapshot, + cx.global::().theme.as_ref(), + ), + &[( + DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5), + Color::red(), + )] + ); + }); +} + +#[gpui::test] +fn test_following(cx: &mut gpui::MutableAppContext) { + let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx); + + cx.set_global(Settings::test(cx)); + + let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx)); + let (_, follower) = cx.add_window( + WindowOptions { + bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))), + ..Default::default() + }, + |cx| build_editor(buffer.clone(), cx), + ); + + let pending_update = Rc::new(RefCell::new(None)); + follower.update(cx, { + let update = pending_update.clone(); + |_, cx| { + cx.subscribe(&leader, move |_, leader, event, cx| { + leader + .read(cx) + .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx); + }) + .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(pending_update.borrow_mut().take().unwrap(), cx) + .unwrap(); + }); + assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]); + + // Update the scroll position only + leader.update(cx, |leader, cx| { + leader.set_scroll_position(vec2f(1.5, 3.5), cx); + }); + follower.update(cx, |follower, cx| { + follower + .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx) + .unwrap(); + }); + assert_eq!( + follower.update(cx, |follower, cx| follower.scroll_position(cx)), + vec2f(1.5, 3.5) + ); + + // Update the selections and scroll position + leader.update(cx, |leader, cx| { + leader.change_selections(None, cx, |s| s.select_ranges([0..0])); + leader.request_autoscroll(Autoscroll::Newest, cx); + leader.set_scroll_position(vec2f(1.5, 3.5), cx); + }); + follower.update(cx, |follower, cx| { + let initial_scroll_position = follower.scroll_position(cx); + follower + .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx) + .unwrap(); + assert_eq!(follower.scroll_position(cx), initial_scroll_position); + assert!(follower.autoscroll_request.is_some()); + }); + assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]); + + // 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(pending_update.borrow_mut().take().unwrap(), cx) + .unwrap(); + }); + assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]); + + // 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(pending_update.borrow_mut().take().unwrap(), cx) + .unwrap(); + }); + assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]); +} + +#[test] +fn test_combine_syntax_and_fuzzy_match_highlights() { + let string = "abcdefghijklmnop"; + let syntax_ranges = [ + ( + 0..3, + HighlightStyle { + color: Some(Color::red()), + ..Default::default() + }, + ), + ( + 4..8, + HighlightStyle { + color: Some(Color::green()), + ..Default::default() + }, + ), + ]; + let match_indices = [4, 6, 7, 8]; + assert_eq!( + combine_syntax_and_fuzzy_match_highlights( + string, + Default::default(), + syntax_ranges.into_iter(), + &match_indices, + ), + &[ + ( + 0..3, + HighlightStyle { + color: Some(Color::red()), + ..Default::default() + }, + ), + ( + 4..5, + HighlightStyle { + color: Some(Color::green()), + weight: Some(fonts::Weight::BOLD), + ..Default::default() + }, + ), + ( + 5..6, + HighlightStyle { + color: Some(Color::green()), + ..Default::default() + }, + ), + ( + 6..8, + HighlightStyle { + color: Some(Color::green()), + weight: Some(fonts::Weight::BOLD), + ..Default::default() + }, + ), + ( + 8..9, + HighlightStyle { + weight: Some(fonts::Weight::BOLD), + ..Default::default() + }, + ), + ] + ); +} + +fn empty_range(row: usize, column: usize) -> Range { + 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) { + 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 + ); +} From 63e1c839fefec43b0f797c15286ebef477d13815 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 6 Oct 2022 13:32:49 -0700 Subject: [PATCH 171/314] Rename language::tests -> language::buffer_tests --- crates/language/src/{tests.rs => buffer_tests.rs} | 0 crates/language/src/language.rs | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) rename crates/language/src/{tests.rs => buffer_tests.rs} (100%) diff --git a/crates/language/src/tests.rs b/crates/language/src/buffer_tests.rs similarity index 100% rename from crates/language/src/tests.rs rename to crates/language/src/buffer_tests.rs diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index c7c5def833..bb75edbc32 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -4,8 +4,9 @@ mod highlight_map; mod outline; pub mod proto; mod syntax_map; + #[cfg(test)] -mod tests; +mod buffer_tests; use anyhow::{anyhow, Context, Result}; use async_trait::async_trait; From 8411d886aca5d09bb6e0b913a6689a99adea84a2 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 6 Oct 2022 15:13:29 -0700 Subject: [PATCH 172/314] Fix multi-line string formatting in editor_test.rs --- crates/editor/src/editor_tests.rs | 1300 ++++++++++++++--------------- 1 file changed, 649 insertions(+), 651 deletions(-) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index c2840cc17b..ac84c0ef1a 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -1,9 +1,8 @@ +use super::*; use crate::test::{ assert_text_with_selections, build_editor, select_ranges, EditorLspTestContext, EditorTestContext, }; - -use super::*; use futures::StreamExt; use gpui::{ geometry::rect::RectF, @@ -418,12 +417,12 @@ fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) { fn test_clone(cx: &mut gpui::MutableAppContext) { let (text, selection_ranges) = marked_text_ranges( indoc! {" - one - two - threeˇ - four - fiveˇ - "}, + one + two + threeˇ + four + fiveˇ + "}, true, ); cx.set_global(Settings::test(cx)); @@ -624,22 +623,22 @@ fn test_fold(cx: &mut gpui::MutableAppContext) { cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple( &" - impl Foo { - // Hello! + impl Foo { + // Hello! - fn a() { - 1 - } - - fn b() { - 2 - } - - fn c() { - 3 - } + fn a() { + 1 } - " + + fn b() { + 2 + } + + fn c() { + 3 + } + } + " .unindent(), cx, ); @@ -653,20 +652,20 @@ fn test_fold(cx: &mut gpui::MutableAppContext) { assert_eq!( view.display_text(cx), " - impl Foo { - // Hello! + impl Foo { + // Hello! - fn a() { - 1 - } - - fn b() {… - } - - fn c() {… - } + fn a() { + 1 } - " + + fn b() {… + } + + fn c() {… + } + } + " .unindent(), ); @@ -674,9 +673,9 @@ fn test_fold(cx: &mut gpui::MutableAppContext) { assert_eq!( view.display_text(cx), " - impl Foo {… - } - " + impl Foo {… + } + " .unindent(), ); @@ -684,20 +683,20 @@ fn test_fold(cx: &mut gpui::MutableAppContext) { assert_eq!( view.display_text(cx), " - impl Foo { - // Hello! + impl Foo { + // Hello! - fn a() { - 1 - } - - fn b() {… - } - - fn c() {… - } + fn a() { + 1 } - " + + fn b() {… + } + + fn c() {… + } + } + " .unindent(), ); @@ -1264,14 +1263,14 @@ fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) { cx.set_global(Settings::test(cx)); let buffer = MultiBuffer::build_simple( " - a - b( - X - ) - c( - X - ) - " + a + b( + X + ) + c( + X + ) + " .unindent() .as_str(), cx, @@ -1301,10 +1300,10 @@ fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) { assert_eq!( buffer.read(cx).text(), " - a - b() - c() - " + a + b() + c() + " .unindent() ); }); @@ -1322,12 +1321,12 @@ fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) { assert_eq!( editor.text(cx), " - a - b( - ) - c( - ) - " + a + b( + ) + c( + ) + " .unindent() ); @@ -1362,33 +1361,33 @@ async fn test_newline_below(cx: &mut gpui::TestAppContext) { 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,ˇ - )ˇ - ˇ);ˇ - "}); + 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, - ˇ - ˇ - ˇ - ˇ - ) - ˇ - ); + const a: A = ( ˇ + ( + ˇ + const_function(), + ˇ + ˇ + something_else, + ˇ + ˇ + ˇ + ˇ + ) ˇ - "}); + ); + ˇ + ˇ + "}); } #[gpui::test] @@ -1427,26 +1426,26 @@ async fn test_tab(cx: &mut gpui::TestAppContext) { }); }); cx.set_state(indoc! {" - ˇabˇc - ˇ🏀ˇ🏀ˇefg - dˇ - "}); + ˇabˇc + ˇ🏀ˇ🏀ˇefg + dˇ + "}); cx.update_editor(|e, cx| e.tab(&Tab, cx)); cx.assert_editor_state(indoc! {" - ˇab ˇc - ˇ🏀 ˇ🏀 ˇefg - d ˇ - "}); + ˇab ˇc + ˇ🏀 ˇ🏀 ˇefg + d ˇ + "}); cx.set_state(indoc! {" - a - «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ» - "}); + a + «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ» + "}); cx.update_editor(|e, cx| e.tab(&Tab, cx)); cx.assert_editor_state(indoc! {" - a - «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ» - "}); + a + «🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ» + "}); } #[gpui::test] @@ -1466,45 +1465,45 @@ async fn test_tab_on_blank_line_auto_indents(cx: &mut gpui::TestAppContext) { // 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( - ˇ - ) - ˇ - ˇ ) - ); - "}); + ˇ + const a: B = ( + c( + d( + ˇ + ) + ˇ + ˇ ) + ); + "}); cx.update_editor(|e, cx| e.tab(&Tab, cx)); cx.assert_editor_state(indoc! {" - ˇ - const a: B = ( - c( - d( - ˇ - ) + ˇ + 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( - ˇ ˇ - ˇ ) - ); - "}); + const a: B = ( + c( + ˇ ˇ + ˇ ) + ); + "}); cx.update_editor(|e, cx| e.tab(&Tab, cx)); cx.assert_editor_state(indoc! {" - const a: B = ( - c( - ˇ - ˇ) - ); - "}); + const a: B = ( + c( + ˇ + ˇ) + ); + "}); } #[gpui::test] @@ -1512,68 +1511,68 @@ async fn test_indent_outdent(cx: &mut gpui::TestAppContext) { let mut cx = EditorTestContext::new(cx); cx.set_state(indoc! {" - «oneˇ» «twoˇ» - three - four - "}); + «oneˇ» «twoˇ» + three + four + "}); cx.update_editor(|e, cx| e.tab(&Tab, cx)); cx.assert_editor_state(indoc! {" - «oneˇ» «twoˇ» - three - four - "}); + «oneˇ» «twoˇ» + three + four + "}); cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx)); cx.assert_editor_state(indoc! {" - «oneˇ» «twoˇ» - three - four - "}); + «oneˇ» «twoˇ» + three + four + "}); // select across line ending cx.set_state(indoc! {" - one two - t«hree - ˇ» four - "}); + one two + t«hree + ˇ» four + "}); cx.update_editor(|e, cx| e.tab(&Tab, cx)); cx.assert_editor_state(indoc! {" - one two - t«hree - ˇ» four - "}); + 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 - "}); + 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 - "}); + one two + ˇthree + four + "}); cx.update_editor(|e, cx| e.tab(&Tab, cx)); cx.assert_editor_state(indoc! {" - one two - ˇthree - four - "}); + one two + ˇthree + four + "}); cx.set_state(indoc! {" - one two - ˇ three - four - "}); + one two + ˇ three + four + "}); cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx)); cx.assert_editor_state(indoc! {" - one two - ˇthree - four - "}); + one two + ˇthree + four + "}); } #[gpui::test] @@ -1587,90 +1586,90 @@ async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) { // select two ranges on one line cx.set_state(indoc! {" - «oneˇ» «twoˇ» - three - four - "}); + «oneˇ» «twoˇ» + three + four + "}); cx.update_editor(|e, cx| e.tab(&Tab, cx)); cx.assert_editor_state(indoc! {" - \t«oneˇ» «twoˇ» - three - four - "}); + \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 - "}); + \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 - "}); + \t«oneˇ» «twoˇ» + three + four + "}); cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx)); cx.assert_editor_state(indoc! {" - «oneˇ» «twoˇ» - three - four - "}); + «oneˇ» «twoˇ» + three + four + "}); // select across a line ending cx.set_state(indoc! {" - one two - t«hree - ˇ»four - "}); + one two + t«hree + ˇ»four + "}); cx.update_editor(|e, cx| e.tab(&Tab, cx)); cx.assert_editor_state(indoc! {" - one two - \tt«hree - ˇ»four - "}); + 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 - "}); + 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 - "}); + 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 - "}); + 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 - "}); + one two + ˇthree + four + "}); cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx)); cx.assert_editor_state(indoc! {" - one two - ˇthree - four - "}); + one two + ˇthree + four + "}); cx.update_editor(|e, cx| e.tab(&Tab, cx)); cx.assert_editor_state(indoc! {" - one two - \tˇthree - four - "}); + one two + \tˇthree + four + "}); cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx)); cx.assert_editor_state(indoc! {" - one two - ˇthree - four - "}); + one two + ˇthree + four + "}); } #[gpui::test] @@ -1739,21 +1738,21 @@ fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) { assert_eq!( editor.text(cx), indoc! {" - a = 1 - b = 2 + a = 1 + b = 2 - const c: usize = 3; - "} + const c: usize = 3; + "} ); select_ranges( &mut editor, indoc! {" - «aˇ» = 1 - b = 2 + «aˇ» = 1 + b = 2 - «const c:ˇ» usize = 3; - "}, + «const c:ˇ» usize = 3; + "}, cx, ); @@ -1761,22 +1760,22 @@ fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) { assert_text_with_selections( &mut editor, indoc! {" - «aˇ» = 1 - b = 2 + «aˇ» = 1 + b = 2 - «const c:ˇ» usize = 3; - "}, + «const c:ˇ» usize = 3; + "}, cx, ); editor.tab_prev(&TabPrev, cx); assert_text_with_selections( &mut editor, indoc! {" - «aˇ» = 1 - b = 2 + «aˇ» = 1 + b = 2 - «const c:ˇ» usize = 3; - "}, + «const c:ˇ» usize = 3; + "}, cx, ); @@ -1790,45 +1789,45 @@ async fn test_backspace(cx: &mut gpui::TestAppContext) { // Basic backspace cx.set_state(indoc! {" - onˇe two three - fou«rˇ» five six - seven «ˇeight nine - »ten - "}); + 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 - "}); + 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 + zero ˇone ˇtwo - ˇ threeˇ four - "}); + ˇ ˇ ˇ 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"}); + 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ˇ"}); + ˇfox jumps over + the lazy dogˇ"}); } #[gpui::test] @@ -1836,25 +1835,25 @@ async fn test_delete(cx: &mut gpui::TestAppContext) { let mut cx = EditorTestContext::new(cx); cx.set_state(indoc! {" - onˇe two three - fou«rˇ» five six - seven «ˇeight nine - »ten - "}); + 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 - "}); + 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"}); + 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ˇ"); } @@ -2191,57 +2190,57 @@ async fn test_clipboard(cx: &mut gpui::TestAppContext) { e.handle_input(") ", cx); }); cx.assert_editor_state(indoc! {" - ( one✅ - three - five ) ˇtwo one✅ four three six five ( one✅ - three - five ) ˇ"}); + ( one✅ + three + five ) ˇtwo one✅ four three six five ( one✅ + three + five ) ˇ"}); // Cut with three selections, one of which is full-line. cx.set_state(indoc! {" - 1«2ˇ»3 - 4ˇ567 - «8ˇ»9"}); + 1«2ˇ»3 + 4ˇ567 + «8ˇ»9"}); cx.update_editor(|e, cx| e.cut(&Cut, cx)); cx.assert_editor_state(indoc! {" - 1ˇ3 - ˇ9"}); + 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"}); + 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"}); + 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"}); + The quick brown + fox juˇmps over + the lazy dog"}); cx.update_editor(|e, cx| e.copy(&Copy, cx)); cx.cx.assert_clipboard_content(Some("fox jumps over\n")); // Paste with three selections, noticing how the copied full-line selection is inserted // before the empty selections but replaces the selection that is non-empty. cx.set_state(indoc! {" - Tˇhe quick brown - «foˇ»x jumps over - tˇhe lazy dog"}); + 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"}); + fox jumps over + Tˇhe quick brown + fox jumps over + ˇx jumps over + fox jumps over + tˇhe lazy dog"}); } #[gpui::test] @@ -2255,105 +2254,105 @@ async fn test_paste_multiline(cx: &mut gpui::TestAppContext) { // Cut an indented block, without the leading whitespace. cx.set_state(indoc! {" - const a: B = ( - c(), - «d( - e, - f - )ˇ» - ); - "}); + 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(), - ˇ - ); - "}); + 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! {" + const a: B = ( + c(), d( e, f )ˇ - const a: B = ( - c(), - ); - "}); + ); + "}); + + // 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 - ) - ˇ»); - "}); + 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(), - ˇ); - "}); + 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 - ) - ˇ); - "}); + 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ˇ - ) - ); - "}); + 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( + const a: B = ( + c(), + d( + e, + f d( e, - f d( - e, - f - ) - ˇ + f ) - ); - "}); + ˇ + ) + ); + "}); } #[gpui::test] @@ -2706,12 +2705,12 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) { )); let text = r#" - use mod1::mod2::{mod3, mod4}; + use mod1::mod2::{mod3, mod4}; - fn fn_1(param1: bool, param2: &str) { - let var1 = "text"; - } - "# + fn fn_1(param1: bool, param2: &str) { + let var1 = "text"; + } + "# .unindent(); let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); @@ -2865,7 +2864,7 @@ async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) { r#" (_ "(" ")" @end) @indent (_ "{" "}" @end) @indent - "#, + "#, ) .unwrap(), ); @@ -2935,10 +2934,10 @@ async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) { cx.set_state( &r#" - 🏀ˇ - εˇ - ❤️ˇ - "# + 🏀ˇ + εˇ + ❤️ˇ + "# .unindent(), ); @@ -2950,10 +2949,10 @@ async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) { }); cx.assert_editor_state( &" - 🏀{{{ˇ}}} - ε{{{ˇ}}} - ❤️{{{ˇ}}} - " + 🏀{{{ˇ}}} + ε{{{ˇ}}} + ❤️{{{ˇ}}} + " .unindent(), ); @@ -2966,19 +2965,19 @@ async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) { }); cx.assert_editor_state( &" - 🏀{{{}}}}ˇ - ε{{{}}}}ˇ - ❤️{{{}}}}ˇ - " + 🏀{{{}}}}ˇ + ε{{{}}}}ˇ + ❤️{{{}}}}ˇ + " .unindent(), ); // autoclose multi-character pairs cx.set_state( &" - ˇ - ˇ - " + ˇ + ˇ + " .unindent(), ); cx.update_editor(|view, cx| { @@ -2987,9 +2986,9 @@ async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) { }); cx.assert_editor_state( &" - /*ˇ */ - /*ˇ */ - " + /*ˇ */ + /*ˇ */ + " .unindent(), ); @@ -2997,17 +2996,17 @@ async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) { // does not autoclose. cx.set_state( &" - /ˇ - ˇ - " + /ˇ + ˇ + " .unindent(), ); cx.update_editor(|view, cx| view.handle_input("*", cx)); cx.assert_editor_state( &" - /*ˇ */ - *ˇ - " + /*ˇ */ + *ˇ + " .unindent(), ); @@ -3055,10 +3054,10 @@ async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) { ) .with_injection_query( r#" - (script_element - (raw_text) @content - (#set! "language" "javascript")) - "#, + (script_element + (raw_text) @content + (#set! "language" "javascript")) + "#, ) .unwrap(), ); @@ -3100,12 +3099,12 @@ async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) { cx.set_state( &r#" - ˇ - - ˇ - "# + ˇ + + ˇ + "# .unindent(), ); @@ -3130,12 +3129,12 @@ async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) { }); cx.assert_editor_state( &r#" - - - - "# + + + + "# .unindent(), ); @@ -3148,12 +3147,12 @@ async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) { }); cx.assert_editor_state( &r#" -
- - - "# + + + + "# .unindent(), ); @@ -3165,12 +3164,12 @@ async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) { }); cx.assert_editor_state( &r#" - - - - "# + + + + "# .unindent(), ); cx.update_editor(|editor, cx| { @@ -3178,24 +3177,24 @@ async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) { }); cx.assert_editor_state( &r#" - ˇ - - ˇ - "# + ˇ + + ˇ + "# .unindent(), ); // Reset cx.set_state( &r#" - ˇ - - ˇ - "# + ˇ + + ˇ + "# .unindent(), ); @@ -3204,12 +3203,12 @@ async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) { }); cx.assert_editor_state( &r#" - <ˇ> - - <ˇ> - "# + <ˇ> + + <ˇ> + "# .unindent(), ); @@ -3219,12 +3218,12 @@ async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) { }); cx.assert_editor_state( &r#" - ˇ - - ˇ - "# + ˇ + + ˇ + "# .unindent(), ); @@ -3235,12 +3234,12 @@ async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) { }); cx.assert_editor_state( &r#" - /*ˇ - - /*ˇ - "# + /*ˇ + + /*ˇ + "# .unindent(), ); } @@ -3262,10 +3261,10 @@ async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) { )); let text = r#" - a - b - c - "# + a + b + c + "# .unindent(); let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); @@ -3292,7 +3291,7 @@ async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) { {{{a}}} {{{b}}} {{{c}}} - " + " .unindent() ); assert_eq!( @@ -3311,7 +3310,7 @@ async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) { a b c - " + " .unindent() ); assert_eq!( @@ -3343,10 +3342,10 @@ async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) { )); let text = r#" - a - b - c - "# + a + b + c + "# .unindent(); let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); @@ -3374,7 +3373,7 @@ async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) { a{{_}} b{{_}} c{{_}} - " + " .unindent() ); assert_eq!( @@ -3394,7 +3393,7 @@ async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) { a{} b{} c{} - " + " .unindent() ); assert_eq!( @@ -3413,7 +3412,7 @@ async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) { a b c - " + " .unindent() ); assert_eq!( @@ -3433,10 +3432,10 @@ async fn test_snippets(cx: &mut gpui::TestAppContext) { let (text, insertion_ranges) = marked_text_ranges( indoc! {" - a.ˇ b - a.ˇ b - a.ˇ b - "}, + a.ˇ b + a.ˇ b + a.ˇ b + "}, false, ); @@ -3460,10 +3459,10 @@ async fn test_snippets(cx: &mut gpui::TestAppContext) { editor, cx, indoc! {" - a.f(«one», two, «three») b - a.f(«one», two, «three») b - a.f(«one», two, «three») b - "}, + 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 @@ -3472,10 +3471,10 @@ async fn test_snippets(cx: &mut gpui::TestAppContext) { editor, cx, indoc! {" - a.f(«one», two, «three») b - a.f(«one», two, «three») b - a.f(«one», two, «three») b - "}, + 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)); @@ -3483,10 +3482,10 @@ async fn test_snippets(cx: &mut gpui::TestAppContext) { editor, cx, indoc! {" - a.f(one, «two», three) b - a.f(one, «two», three) b - a.f(one, «two», three) b - "}, + 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); @@ -3494,10 +3493,10 @@ async fn test_snippets(cx: &mut gpui::TestAppContext) { editor, cx, indoc! {" - a.f(«one», two, «three») b - a.f(«one», two, «three») b - a.f(«one», two, «three») b - "}, + 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)); @@ -3505,20 +3504,20 @@ async fn test_snippets(cx: &mut gpui::TestAppContext) { editor, cx, indoc! {" - a.f(one, «two», three) b - a.f(one, «two», three) b - a.f(one, «two», three) b - "}, + 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 - "}, + 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 @@ -3527,10 +3526,10 @@ async fn test_snippets(cx: &mut gpui::TestAppContext) { editor, cx, indoc! {" - a.f(one, two, three)ˇ b - a.f(one, two, three)ˇ b - a.f(one, two, three)ˇ b - "}, + a.f(one, two, three)ˇ b + a.f(one, two, three)ˇ b + a.f(one, two, three)ˇ b + "}, ); }); } @@ -3861,18 +3860,18 @@ async fn test_completion(cx: &mut gpui::TestAppContext) { .await; cx.set_state(indoc! {" - oneˇ - two - three - "}); + oneˇ + two + three + "}); cx.simulate_keystroke("."); handle_completion_request( &mut cx, indoc! {" - one.|<> - two - three - "}, + one.|<> + two + three + "}, vec!["first_completion", "second_completion"], ) .await; @@ -3885,57 +3884,56 @@ async fn test_completion(cx: &mut gpui::TestAppContext) { .unwrap() }); cx.assert_editor_state(indoc! {" - one.second_completionˇ - two - three - "}); + one.second_completionˇ + two + three + "}); handle_resolve_completion_request( &mut cx, Some(( indoc! {" - one.second_completion - two - threeˇ - "}, + 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 - "}); + one.second_completionˇ + two + three + additional edit + "}); cx.set_state(indoc! {" - one.second_completion - twoˇ - threeˇ - additional edit - "}); + one.second_completion + twoˇ + threeˇ + additional edit + "}); cx.simulate_keystroke(" "); assert!(cx.editor(|e, _| e.context_menu.is_none())); cx.simulate_keystroke("s"); assert!(cx.editor(|e, _| e.context_menu.is_none())); cx.assert_editor_state(indoc! {" - one.second_completion - two sˇ - three sˇ - additional edit - "}); - // + one.second_completion + two sˇ + three sˇ + additional edit + "}); handle_completion_request( &mut cx, indoc! {" - one.second_completion - two s - three - additional edit - "}, + one.second_completion + two s + three + additional edit + "}, vec!["fourth_completion", "fifth_completion", "sixth_completion"], ) .await; @@ -3947,11 +3945,11 @@ async fn test_completion(cx: &mut gpui::TestAppContext) { handle_completion_request( &mut cx, indoc! {" - one.second_completion - two si - three - additional edit - "}, + one.second_completion + two si + three + additional edit + "}, vec!["fourth_completion", "fifth_completion", "sixth_completion"], ) .await; @@ -3964,11 +3962,11 @@ async fn test_completion(cx: &mut gpui::TestAppContext) { .unwrap() }); cx.assert_editor_state(indoc! {" - one.second_completion - two sixth_completionˇ - three sixth_completionˇ - additional edit - "}); + one.second_completion + two sixth_completionˇ + three sixth_completionˇ + additional edit + "}); handle_resolve_completion_request(&mut cx, None).await; apply_additional_edits.await.unwrap(); @@ -4084,12 +4082,12 @@ async fn test_toggle_comment(cx: &mut gpui::TestAppContext) { )); let text = " - fn a() { - //b(); - // c(); - // d(); - } - " + fn a() { + //b(); + // c(); + // d(); + } + " .unindent(); let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx)); @@ -4109,12 +4107,12 @@ async fn test_toggle_comment(cx: &mut gpui::TestAppContext) { assert_eq!( editor.text(cx), " - fn a() { - b(); - c(); - d(); - } - " + fn a() { + b(); + c(); + d(); + } + " .unindent() ); @@ -4127,12 +4125,12 @@ async fn test_toggle_comment(cx: &mut gpui::TestAppContext) { assert_eq!( editor.text(cx), " - fn a() { - // b(); - // c(); - // d(); - } - " + fn a() { + // b(); + // c(); + // d(); + } + " .unindent() ); @@ -4144,12 +4142,12 @@ async fn test_toggle_comment(cx: &mut gpui::TestAppContext) { assert_eq!( editor.text(cx), " - fn a() { - // b(); - c(); - // d(); - } - " + fn a() { + // b(); + c(); + // d(); + } + " .unindent() ); }); @@ -4170,10 +4168,10 @@ async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) { ) .with_injection_query( r#" - (script_element - (raw_text) @content - (#set! "language" "javascript")) - "#, + (script_element + (raw_text) @content + (#set! "language" "javascript")) + "#, ) .unwrap(), ); @@ -4199,28 +4197,28 @@ async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) { // Toggle comments for empty selections cx.set_state( &r#" -

A

ˇ -

B

ˇ -

C

ˇ - "# +

A

ˇ +

B

ˇ +

C

ˇ + "# .unindent(), ); cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); cx.assert_editor_state( &r#" - - - - "# + + + + "# .unindent(), ); cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); cx.assert_editor_state( &r#" -

A

ˇ -

B

ˇ -

C

ˇ - "# +

A

ˇ +

B

ˇ +

C

ˇ + "# .unindent(), ); @@ -4228,32 +4226,32 @@ async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) { // multiple selections occupy a given line. cx.set_state( &r#" -

-

ˇ»B

ˇ -

-

ˇ»D

ˇ - "# +

+

ˇ»B

ˇ +

+

ˇ»D

ˇ + "# .unindent(), ); cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); cx.assert_editor_state( &r#" - - - "# + + + "# .unindent(), ); cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); cx.assert_editor_state( &r#" -

-

ˇ»B

ˇ -

-

ˇ»D

ˇ - "# +

+

ˇ»B

ˇ +

+

ˇ»D

ˇ + "# .unindent(), ); @@ -4261,20 +4259,20 @@ async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) { // selections. cx.set_state( &r#" - ˇ - "# + ˇ + "# .unindent(), ); cx.foreground().run_until_parked(); cx.update_editor(|editor, cx| editor.toggle_comments(&ToggleComments, cx)); cx.assert_editor_state( &r#" - - // ˇvar x = new Y(); - - "# + + // ˇvar x = new Y(); + + "# .unindent(), ); } @@ -4332,9 +4330,9 @@ fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) { let markers = vec![('[', ']').into(), ('(', ')').into()]; let (initial_text, mut excerpt_ranges) = marked_text_ranges_by( indoc! {" - [aaaa - (bbbb] - cccc)", + [aaaa + (bbbb] + cccc)", }, markers.clone(), ); @@ -4356,10 +4354,10 @@ fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) { view.update(cx, |view, cx| { let (expected_text, selection_ranges) = marked_text_ranges( indoc! {" - aaaa - bˇbbb - bˇbbˇb - cccc" + aaaa + bˇbbb + bˇbbˇb + cccc" }, true, ); @@ -4370,10 +4368,10 @@ fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) { let (expected_text, expected_selections) = marked_text_ranges( indoc! {" - aaaa - bXˇbbXb - bXˇbbXˇb - cccc" + aaaa + bXˇbbXb + bXˇbbXˇb + cccc" }, false, ); @@ -4383,14 +4381,14 @@ fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) { view.newline(&Newline, cx); let (expected_text, expected_selections) = marked_text_ranges( indoc! {" - aaaa - bX - ˇbbX - b - bX - ˇbbX - ˇb - cccc" + aaaa + bX + ˇbbX + b + bX + ˇbbX + ˇb + cccc" }, false, ); @@ -4580,7 +4578,7 @@ async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) { ); let text = concat!( - "{ }\n", // Suppress rustfmt + "{ }\n", // " x\n", // " /* */\n", // "x\n", // From 4508d94a3ef5d3d873d65e130e1fbfa9dd1bf74b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 6 Oct 2022 17:03:23 -0700 Subject: [PATCH 173/314] In deterministic executor, ensure fake timers are ordered by wake time Previously, advancing the clock would fail to wake a timer that was set *after* another time whose wake time had not yet arrived. --- crates/gpui/src/executor.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/gpui/src/executor.rs b/crates/gpui/src/executor.rs index 980da91167..0639445b0d 100644 --- a/crates/gpui/src/executor.rs +++ b/crates/gpui/src/executor.rs @@ -325,7 +325,12 @@ impl Deterministic { let mut state = self.state.lock(); let wakeup_at = state.now + duration; let id = util::post_inc(&mut state.next_timer_id); - state.pending_timers.push((id, wakeup_at, tx)); + match state + .pending_timers + .binary_search_by_key(&wakeup_at, |e| e.1) + { + Ok(ix) | Err(ix) => state.pending_timers.insert(ix, (id, wakeup_at, tx)), + } let state = self.state.clone(); Timer::Deterministic(DeterministicTimer { rx, id, state }) } From 47a8e4222ac0ee8f2d57f1b83fb1cb17d3c2461d Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 6 Oct 2022 17:03:38 -0700 Subject: [PATCH 174/314] Don't allow multiple concurrent formatting requests for the same buffer Co-authored-by: Nathan Sobo --- crates/editor/src/editor_tests.rs | 57 +++++++++++++++++++++++++++++++ crates/project/src/project.rs | 33 ++++++++++++++---- 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index ac84c0ef1a..430b958407 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -3845,6 +3845,63 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) { ); } +#[gpui::test] +async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) { + cx.foreground().forbid_parking(); + + 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::(move |_, cx| { + let executor = cx.background(); + async move { + executor.timer(Duration::from_millis(100)).await; + Ok(Some(vec![lsp::TextEdit { + range: lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 3)), + new_text: "\n ".into(), + }])) + } + }); + + // Submit a format request. + let format_1 = cx + .update_editor(|editor, cx| editor.format(&Format, cx)) + .unwrap(); + cx.foreground().run_until_parked(); + + // Submit a second format request. + let format_2 = cx + .update_editor(|editor, cx| editor.format(&Format, cx)) + .unwrap(); + cx.foreground().run_until_parked(); + + // Wait for both format requests to complete + cx.foreground().advance_clock(Duration::from_millis(200)); + cx.foreground().start_waiting(); + format_1.await.unwrap(); + cx.foreground().start_waiting(); + format_2.await.unwrap(); + + // The formatting edits only happens once. + cx.assert_editor_state(indoc! {" + one + .twoˇ + "}); +} + #[gpui::test] async fn test_completion(cx: &mut gpui::TestAppContext) { let mut cx = EditorLspTestContext::new_rust( diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index dc783f1818..3aa2f45264 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -8,10 +8,7 @@ pub mod worktree; mod project_tests; use anyhow::{anyhow, Context, Result}; -use client::{ - proto::{self}, - Client, PeerId, TypedEnvelope, User, UserStore, -}; +use client::{proto, Client, PeerId, TypedEnvelope, User, UserStore}; use clock::ReplicaId; use collections::{hash_map, BTreeMap, HashMap, HashSet}; use futures::{future::Shared, AsyncWriteExt, Future, FutureExt, StreamExt, TryFutureExt}; @@ -66,7 +63,7 @@ use std::{ time::Instant, }; use thiserror::Error; -use util::{post_inc, ResultExt, TryFutureExt as _}; +use util::{defer, post_inc, ResultExt, TryFutureExt as _}; pub use db::Db; pub use fs::*; @@ -128,6 +125,7 @@ pub struct Project { opened_buffers: HashMap, incomplete_buffers: HashMap>, buffer_snapshots: HashMap>, + buffers_being_formatted: HashSet, nonce: u128, initialized_persistent_state: bool, _maintain_buffer_languages: Task<()>, @@ -512,6 +510,7 @@ impl Project { language_server_statuses: Default::default(), last_workspace_edits_by_language_server: Default::default(), language_server_settings: Default::default(), + buffers_being_formatted: Default::default(), next_language_server_id: 0, nonce: StdRng::from_entropy().gen(), initialized_persistent_state: false, @@ -627,6 +626,7 @@ impl Project { last_workspace_edits_by_language_server: Default::default(), next_language_server_id: 0, opened_buffers: Default::default(), + buffers_being_formatted: Default::default(), buffer_snapshots: Default::default(), nonce: StdRng::from_entropy().gen(), initialized_persistent_state: false, @@ -3113,7 +3113,26 @@ impl Project { .await?; } - for (buffer, buffer_abs_path, language_server) in local_buffers { + // Do not allow multiple concurrent formatting requests for the + // same buffer. + this.update(&mut cx, |this, _| { + local_buffers + .retain(|(buffer, _, _)| this.buffers_being_formatted.insert(buffer.id())); + }); + let _cleanup = defer({ + let this = this.clone(); + let mut cx = cx.clone(); + let local_buffers = &local_buffers; + move || { + this.update(&mut cx, |this, _| { + for (buffer, _, _) in local_buffers { + this.buffers_being_formatted.remove(&buffer.id()); + } + }); + } + }); + + for (buffer, buffer_abs_path, language_server) in &local_buffers { let (format_on_save, formatter, tab_size) = buffer.read_with(&cx, |buffer, cx| { let settings = cx.global::(); let language_name = buffer.language().map(|language| language.name()); @@ -3165,7 +3184,7 @@ impl Project { buffer.forget_transaction(transaction.id) }); } - project_transaction.0.insert(buffer, transaction); + project_transaction.0.insert(buffer.clone(), transaction); } } From d67fad8dca3b81966ec63cb6e3ef85eff5bbc010 Mon Sep 17 00:00:00 2001 From: Julia Date: Thu, 6 Oct 2022 22:20:10 -0400 Subject: [PATCH 175/314] Extend a test to cover repos not at worktree root --- crates/collab/src/integration_tests.rs | 142 ++++++++++++++++++++++--- crates/project/src/fs.rs | 3 +- 2 files changed, 130 insertions(+), 15 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 58a8efc411..7e84c70601 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -966,7 +966,14 @@ async fn test_git_diff_base_change( .insert_tree( "/dir", json!({ - ".git": { + ".git": {}, + "sub": { + ".git": {}, + "b.txt": " + one + two + three + ".unindent(), }, "a.txt": " one @@ -977,6 +984,11 @@ async fn test_git_diff_base_change( ) .await; + let (project_local, worktree_id) = client_a.build_local_project("/dir", cx_a).await; + let project_remote = client_b + .build_remote_project(&project_local, cx_a, cx_b) + .await; + let diff_base = " one three @@ -998,12 +1010,9 @@ async fn test_git_diff_base_change( ) .await; - let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; - let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await; - // Create the buffer - let buffer_a = project_a - .update(cx_a, |p, cx| p.open_buffer((worktree_id, "/dir/a.txt"), cx)) + let buffer_local_a = project_local + .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)) .await .unwrap(); @@ -1011,7 +1020,7 @@ async fn test_git_diff_base_change( executor.run_until_parked(); // Smoke test diffing - buffer_a.read_with(cx_a, |buffer, _| { + buffer_local_a.read_with(cx_a, |buffer, _| { assert_eq!(buffer.diff_base(), Some(diff_base.as_ref())); git::diff::assert_hunks( buffer.snapshot().git_diff_hunks_in_range(0..4), @@ -1022,8 +1031,8 @@ async fn test_git_diff_base_change( }); // Create remote buffer - let buffer_b = project_b - .update(cx_b, |p, cx| p.open_buffer((worktree_id, "/dir/a.txt"), cx)) + let buffer_remote_a = project_remote + .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx)) .await .unwrap(); @@ -1031,7 +1040,7 @@ async fn test_git_diff_base_change( executor.run_until_parked(); // Smoke test diffing - buffer_b.read_with(cx_b, |buffer, _| { + buffer_remote_a.read_with(cx_b, |buffer, _| { assert_eq!(buffer.diff_base(), Some(diff_base.as_ref())); git::diff::assert_hunks( buffer.snapshot().git_diff_hunks_in_range(0..4), @@ -1050,11 +1059,11 @@ async fn test_git_diff_base_change( ) .await; - // Wait for buffer_a to receive it + // Wait for buffer_local_a to receive it executor.run_until_parked(); // Smoke test new diffing - buffer_a.read_with(cx_a, |buffer, _| { + buffer_local_a.read_with(cx_a, |buffer, _| { assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref())); git::diff::assert_hunks( @@ -1066,7 +1075,114 @@ async fn test_git_diff_base_change( }); // Smoke test B - buffer_b.read_with(cx_b, |buffer, _| { + buffer_remote_a.read_with(cx_b, |buffer, _| { + assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref())); + git::diff::assert_hunks( + buffer.snapshot().git_diff_hunks_in_range(0..4), + &buffer, + &diff_base, + &[(2..3, "", "three\n")], + ); + }); + + //Nested git dir + + let diff_base = " + one + three + " + .unindent(); + + let new_diff_base = " + one + two + " + .unindent(); + + client_a + .fs + .as_fake() + .set_index_for_repo( + Path::new("/dir/sub/.git"), + &[(Path::new("b.txt"), diff_base.clone())], + ) + .await; + + // Create the buffer + let buffer_local_b = project_local + .update(cx_a, |p, cx| p.open_buffer((worktree_id, "sub/b.txt"), cx)) + .await + .unwrap(); + + // Wait for it to catch up to the new diff + executor.run_until_parked(); + + // Smoke test diffing + buffer_local_b.read_with(cx_a, |buffer, _| { + assert_eq!(buffer.diff_base(), Some(diff_base.as_ref())); + git::diff::assert_hunks( + buffer.snapshot().git_diff_hunks_in_range(0..4), + &buffer, + &diff_base, + &[(1..2, "", "two\n")], + ); + }); + + // Create remote buffer + let buffer_remote_b = project_remote + .update(cx_b, |p, cx| p.open_buffer((worktree_id, "sub/b.txt"), cx)) + .await + .unwrap(); + + // Wait remote buffer to catch up to the new diff + executor.run_until_parked(); + + // Smoke test diffing + buffer_remote_b.read_with(cx_b, |buffer, _| { + assert_eq!(buffer.diff_base(), Some(diff_base.as_ref())); + git::diff::assert_hunks( + buffer.snapshot().git_diff_hunks_in_range(0..4), + &buffer, + &diff_base, + &[(1..2, "", "two\n")], + ); + }); + + client_a + .fs + .as_fake() + .set_index_for_repo( + Path::new("/dir/sub/.git"), + &[(Path::new("b.txt"), new_diff_base.clone())], + ) + .await; + + // Wait for buffer_local_b to receive it + executor.run_until_parked(); + + // Smoke test new diffing + buffer_local_b.read_with(cx_a, |buffer, _| { + assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref())); + println!("{:?}", buffer.as_rope().to_string()); + println!("{:?}", buffer.diff_base()); + println!( + "{:?}", + buffer + .snapshot() + .git_diff_hunks_in_range(0..4) + .collect::>() + ); + + git::diff::assert_hunks( + buffer.snapshot().git_diff_hunks_in_range(0..4), + &buffer, + &diff_base, + &[(2..3, "", "three\n")], + ); + }); + + // Smoke test B + buffer_remote_b.read_with(cx_b, |buffer, _| { assert_eq!(buffer.diff_base(), Some(new_diff_base.as_ref())); git::diff::assert_hunks( buffer.snapshot().git_diff_hunks_in_range(0..4), diff --git a/crates/project/src/fs.rs b/crates/project/src/fs.rs index 812842a354..a9a0a1707f 100644 --- a/crates/project/src/fs.rs +++ b/crates/project/src/fs.rs @@ -491,7 +491,6 @@ impl FakeFs { } pub async fn set_index_for_repo(&self, dot_git: &Path, head_state: &[(&Path, String)]) { - let content_path = dot_git.parent().unwrap(); let mut state = self.state.lock().await; let entry = state.read_path(dot_git).await.unwrap(); let mut entry = entry.lock().await; @@ -504,7 +503,7 @@ impl FakeFs { repo_state.index_contents.extend( head_state .iter() - .map(|(path, content)| (content_path.join(path), content.clone())), + .map(|(path, content)| (path.to_path_buf(), content.clone())), ); state.emit_event([dot_git]); From 3d467a949178022f29a9876be23a2bcb42318141 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 7 Oct 2022 09:23:25 +0200 Subject: [PATCH 176/314] Unset room on active call when disconnecting --- crates/call/src/call.rs | 8 +++++++- crates/collab/src/integration_tests.rs | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 8617b5391a..01787023be 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -210,7 +210,13 @@ impl ActiveCall { if room.as_ref() != self.room.as_ref().map(|room| &room.0) { if let Some(room) = room { let subscriptions = vec![ - cx.observe(&room, |_, _, cx| cx.notify()), + cx.observe(&room, |this, room, cx| { + if room.read(cx).status().is_offline() { + this.set_room(None, cx); + } + + cx.notify(); + }), cx.subscribe(&room, |_, _, event, cx| cx.emit(event.clone())), ]; self.room = Some((room, subscriptions)); diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 0767ee5ddb..665a9ca729 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -385,6 +385,7 @@ async fn test_leaving_room_on_disconnection( server.disconnect_client(client_a.current_user_id(cx_a)); cx_a.foreground().advance_clock(rpc::RECEIVE_TIMEOUT); + active_call_a.read_with(cx_a, |call, _| assert!(call.room().is_none())); assert_eq!( room_participants(&room_a, cx_a), RoomParticipants { From b479c8c8ba616b9eece4d4be8bd45c8a2b82b77a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 7 Oct 2022 10:14:17 +0200 Subject: [PATCH 177/314] Move project sharing into `Room` --- crates/call/src/call.rs | 28 +- crates/call/src/room.rs | 19 ++ crates/collab/src/integration_tests.rs | 262 +++++++++++-------- crates/collab_ui/src/collab_titlebar_item.rs | 31 ++- crates/project/src/project.rs | 10 +- 5 files changed, 210 insertions(+), 140 deletions(-) diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 01787023be..91fee3ae87 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -120,10 +120,8 @@ impl ActiveCall { }; let initial_project_id = if let Some(initial_project) = initial_project { - let room_id = room.read_with(&cx, |room, _| room.id()); Some( - initial_project - .update(&mut cx, |project, cx| project.share(room_id, cx)) + room.update(&mut cx, |room, cx| room.share_project(initial_project, cx)) .await?, ) } else { @@ -206,6 +204,30 @@ impl ActiveCall { Ok(()) } + pub fn share_project( + &mut self, + project: ModelHandle, + cx: &mut ModelContext, + ) -> Task> { + if let Some((room, _)) = self.room.as_ref() { + room.update(cx, |room, cx| room.share_project(project, cx)) + } else { + Task::ready(Err(anyhow!("no active call"))) + } + } + + pub fn set_location( + &mut self, + project: Option<&ModelHandle>, + cx: &mut ModelContext, + ) -> Task> { + if let Some((room, _)) = self.room.as_ref() { + room.update(cx, |room, cx| room.set_location(project, cx)) + } else { + Task::ready(Err(anyhow!("no active call"))) + } + } + fn set_room(&mut self, room: Option>, cx: &mut ModelContext) { if room.as_ref() != self.room.as_ref().map(|room| &room.0) { if let Some(room) = room { diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 3d8697f1e0..ba2c51b39e 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -240,6 +240,25 @@ impl Room { }) } + pub(crate) fn share_project( + &mut self, + project: ModelHandle, + cx: &mut ModelContext, + ) -> Task> { + let request = self + .client + .request(proto::ShareProject { room_id: self.id() }); + cx.spawn_weak(|_, mut cx| async move { + let response = request.await?; + project + .update(&mut cx, |project, cx| { + project.shared(response.project_id, cx) + }) + .await?; + Ok(response.project_id) + }) + } + pub fn set_location( &mut self, project: Option<&ModelHandle>, diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 665a9ca729..55f1267ba2 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -495,9 +495,10 @@ async fn test_share_project( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); client_a .fs @@ -516,8 +517,8 @@ async fn test_share_project( .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); @@ -594,7 +595,7 @@ async fn test_unshare_project( let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; let client_c = server.create_client(cx_c, "user_c").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; @@ -613,8 +614,8 @@ async fn test_unshare_project( .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap()); @@ -643,8 +644,8 @@ async fn test_unshare_project( assert!(project_c.read_with(cx_c, |project, _| project.is_read_only())); // Client C can open the project again after client A re-shares. - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); let project_c2 = client_c.build_remote_project(project_id, cx_c).await; @@ -677,7 +678,7 @@ async fn test_host_disconnect( let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; let client_c = server.create_client(cx_c, "user_c").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; @@ -692,10 +693,11 @@ async fn test_host_disconnect( ) .await; + let active_call_a = cx_a.read(ActiveCall::global); let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap()); - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); @@ -770,15 +772,17 @@ async fn test_active_call_events( let (project_a, _) = client_a.build_local_project("/a", cx_a).await; let (project_b, _) = client_b.build_local_project("/b", cx_b).await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); + let active_call_b = cx_b.read(ActiveCall::global); let events_a = active_call_events(cx_a); let events_b = active_call_events(cx_b); - let project_a_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_a_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); deterministic.run_until_parked(); @@ -795,8 +799,8 @@ async fn test_active_call_events( }] ); - let project_b_id = project_b - .update(cx_b, |project, cx| project.share(room_id, cx)) + let project_b_id = active_call_b + .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx)) .await .unwrap(); deterministic.run_until_parked(); @@ -845,7 +849,7 @@ async fn test_room_location( let (project_a, _) = client_a.build_local_project("/a", cx_a).await; let (project_b, _) = client_b.build_local_project("/b", cx_b).await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; @@ -871,8 +875,8 @@ async fn test_room_location( } }); - let project_a_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_a_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); deterministic.run_until_parked(); @@ -887,8 +891,8 @@ async fn test_room_location( vec![("user_a".to_string(), ParticipantLocation::External)] ); - let project_b_id = project_b - .update(cx_b, |project, cx| project.share(room_id, cx)) + let project_b_id = active_call_b + .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx)) .await .unwrap(); deterministic.run_until_parked(); @@ -1000,9 +1004,10 @@ async fn test_propagate_saves_and_fs_changes( let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; let client_c = server.create_client(cx_c, "user_c").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); client_a .fs @@ -1016,8 +1021,8 @@ async fn test_propagate_saves_and_fs_changes( .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; let worktree_a = project_a.read_with(cx_a, |p, cx| p.worktrees(cx).next().unwrap()); - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); @@ -1148,9 +1153,10 @@ async fn test_fs_operations( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); client_a .fs @@ -1163,8 +1169,8 @@ async fn test_fs_operations( ) .await; let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -1413,9 +1419,10 @@ async fn test_buffer_conflict_after_save(cx_a: &mut TestAppContext, cx_b: &mut T let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); client_a .fs @@ -1427,8 +1434,8 @@ async fn test_buffer_conflict_after_save(cx_a: &mut TestAppContext, cx_b: &mut T ) .await; let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -1466,9 +1473,10 @@ async fn test_buffer_reloading(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); client_a .fs @@ -1480,8 +1488,8 @@ async fn test_buffer_reloading(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont ) .await; let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -1524,17 +1532,18 @@ async fn test_editing_while_guest_opens_buffer( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); client_a .fs .insert_tree("/dir", json!({ "a.txt": "a-contents" })) .await; let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -1570,17 +1579,18 @@ async fn test_leaving_worktree_while_opening_buffer( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); client_a .fs .insert_tree("/dir", json!({ "a.txt": "a-contents" })) .await; let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -1614,9 +1624,10 @@ async fn test_canceling_buffer_opening( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); client_a .fs @@ -1628,8 +1639,8 @@ async fn test_canceling_buffer_opening( ) .await; let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -1665,9 +1676,10 @@ async fn test_leaving_project( let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; let client_c = server.create_client(cx_c, "user_c").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); client_a .fs @@ -1680,8 +1692,8 @@ async fn test_leaving_project( ) .await; let (project_a, _) = client_a.build_local_project("/a", cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -1755,9 +1767,10 @@ async fn test_collaborating_with_diagnostics( let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; let client_c = server.create_client(cx_c, "user_c").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); // Set up a fake language server. let mut language = Language::new( @@ -1783,8 +1796,8 @@ async fn test_collaborating_with_diagnostics( ) .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); @@ -1992,9 +2005,10 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); // Set up a fake language server. let mut language = Language::new( @@ -2030,8 +2044,8 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu ) .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -2165,9 +2179,10 @@ async fn test_reloading_buffer_manually(cx_a: &mut TestAppContext, cx_b: &mut Te let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); client_a .fs @@ -2178,8 +2193,8 @@ async fn test_reloading_buffer_manually(cx_a: &mut TestAppContext, cx_b: &mut Te .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx)) .await .unwrap(); - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); @@ -2257,9 +2272,10 @@ async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppCon let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); // Set up a fake language server. let mut language = Language::new( @@ -2282,8 +2298,8 @@ async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppCon .insert_tree(&directory, json!({ "a.rs": "let one = \"two\"" })) .await; let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -2357,9 +2373,10 @@ async fn test_definition(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); // Set up a fake language server. let mut language = Language::new( @@ -2389,8 +2406,8 @@ async fn test_definition(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { ) .await; let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -2500,9 +2517,10 @@ async fn test_references(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); // Set up a fake language server. let mut language = Language::new( @@ -2532,8 +2550,8 @@ async fn test_references(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { ) .await; let (project_a, worktree_id) = client_a.build_local_project("/root/dir-1", cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -2600,9 +2618,10 @@ async fn test_project_search(cx_a: &mut TestAppContext, cx_b: &mut TestAppContex let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); client_a .fs @@ -2631,8 +2650,8 @@ async fn test_project_search(cx_a: &mut TestAppContext, cx_b: &mut TestAppContex worktree_2 .read_with(cx_a, |tree, _| tree.as_local().unwrap().scan_complete()) .await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); @@ -2678,9 +2697,10 @@ async fn test_document_highlights(cx_a: &mut TestAppContext, cx_b: &mut TestAppC let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); client_a .fs @@ -2705,8 +2725,8 @@ async fn test_document_highlights(cx_a: &mut TestAppContext, cx_b: &mut TestAppC client_a.language_registry.add(Arc::new(language)); let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -2779,9 +2799,10 @@ async fn test_lsp_hover(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); client_a .fs @@ -2806,8 +2827,8 @@ async fn test_lsp_hover(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { client_a.language_registry.add(Arc::new(language)); let (project_a, worktree_id) = client_a.build_local_project("/root-1", cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -2881,9 +2902,10 @@ async fn test_project_symbols(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); // Set up a fake language server. let mut language = Language::new( @@ -2915,8 +2937,8 @@ async fn test_project_symbols(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte ) .await; let (project_a, worktree_id) = client_a.build_local_project("/code/crate-1", cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -2988,9 +3010,10 @@ async fn test_open_buffer_while_getting_definition_pointing_to_it( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); // Set up a fake language server. let mut language = Language::new( @@ -3015,8 +3038,8 @@ async fn test_open_buffer_while_getting_definition_pointing_to_it( ) .await; let (project_a, worktree_id) = client_a.build_local_project("/root", cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -3063,9 +3086,10 @@ async fn test_collaborating_with_code_actions( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); // Set up a fake language server. let mut language = Language::new( @@ -3090,8 +3114,8 @@ async fn test_collaborating_with_code_actions( ) .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); @@ -3273,9 +3297,10 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); // Set up a fake language server. let mut language = Language::new( @@ -3311,8 +3336,8 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T ) .await; let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -3464,9 +3489,10 @@ async fn test_language_server_statuses( let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); // Set up a fake language server. let mut language = Language::new( @@ -3523,8 +3549,8 @@ async fn test_language_server_statuses( ); }); - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -4234,14 +4260,16 @@ async fn test_contact_requests( #[gpui::test(iterations = 10)] async fn test_following(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { cx_a.foreground().forbid_parking(); + cx_a.update(editor::init); + cx_b.update(editor::init); + let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; - cx_a.update(editor::init); - cx_b.update(editor::init); + let active_call_a = cx_a.read(ActiveCall::global); client_a .fs @@ -4255,8 +4283,8 @@ async fn test_following(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { ) .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -4443,14 +4471,16 @@ async fn test_following(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { #[gpui::test(iterations = 10)] async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { cx_a.foreground().forbid_parking(); + cx_a.update(editor::init); + cx_b.update(editor::init); + let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; - cx_a.update(editor::init); - cx_b.update(editor::init); + let active_call_a = cx_a.read(ActiveCall::global); // Client A shares a project. client_a @@ -4466,8 +4496,8 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T ) .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); @@ -4609,16 +4639,17 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T #[gpui::test(iterations = 10)] async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) { cx_a.foreground().forbid_parking(); + cx_a.update(editor::init); + cx_b.update(editor::init); // 2 clients connect to a server. let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; - cx_a.update(editor::init); - cx_b.update(editor::init); + let active_call_a = cx_a.read(ActiveCall::global); // Client A shares a project. client_a @@ -4633,8 +4664,8 @@ async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont ) .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; @@ -4773,21 +4804,22 @@ async fn test_peers_simultaneously_following_each_other( cx_b: &mut TestAppContext, ) { deterministic.forbid_parking(); + cx_a.update(editor::init); + cx_b.update(editor::init); let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; - let room_id = server + server .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; - cx_a.update(editor::init); - cx_b.update(editor::init); + let active_call_a = cx_a.read(ActiveCall::global); client_a.fs.insert_tree("/a", json!({})).await; let (project_a, _) = client_a.build_local_project("/a", cx_a).await; let workspace_a = client_a.build_workspace(&project_a, cx_a); - let project_id = project_a - .update(cx_a, |project, cx| project.share(room_id, cx)) + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); @@ -5057,16 +5089,18 @@ async fn test_random_collaboration( }) .await .unwrap(); - let room_id = active_call.read_with(cx, |call, cx| call.room().unwrap().read(cx).id()); + active_call.read_with(cx, |call, cx| call.room().unwrap().read(cx).id()); deterministic.run_until_parked(); - host_cx - .read(ActiveCall::global) + let host_active_call = host_cx.read(ActiveCall::global); + host_active_call .update(&mut host_cx, |call, cx| call.accept_incoming(cx)) .await .unwrap(); - let host_project_id = host_project - .update(&mut host_cx, |project, cx| project.share(room_id, cx)) + let host_project_id = host_active_call + .update(&mut host_cx, |call, cx| { + call.share_project(host_project.clone(), cx) + }) .await .unwrap(); @@ -5539,7 +5573,7 @@ impl TestServer { } } - async fn create_room(&self, clients: &mut [(&TestClient, &mut TestAppContext)]) -> u64 { + async fn create_room(&self, clients: &mut [(&TestClient, &mut TestAppContext)]) { self.make_contacts(clients).await; let (left, right) = clients.split_at_mut(1); @@ -5560,8 +5594,6 @@ impl TestServer { .await .unwrap(); } - - active_call_a.read_with(*cx_a, |call, cx| call.room().unwrap().read(cx).id()) } async fn build_app_state(test_db: &TestDb) -> Arc { diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index c982962042..cc82acac5e 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -138,22 +138,21 @@ impl CollabTitlebarItem { fn share_project(&mut self, _: &ShareProject, cx: &mut ViewContext) { if let Some(workspace) = self.workspace.upgrade(cx) { - if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { - let window_id = cx.window_id(); - let room_id = room.read(cx).id(); - let project = workspace.read(cx).project().clone(); - let share = project.update(cx, |project, cx| project.share(room_id, cx)); - cx.spawn_weak(|_, mut cx| async move { - share.await?; - if cx.update(|cx| cx.window_is_active(window_id)) { - room.update(&mut cx, |room, cx| { - room.set_location(Some(&project), cx).detach_and_log_err(cx); - }); - } - anyhow::Ok(()) - }) - .detach_and_log_err(cx); - } + let active_call = ActiveCall::global(cx); + + let window_id = cx.window_id(); + let project = workspace.read(cx).project().clone(); + let share = active_call.update(cx, |call, cx| call.share_project(project.clone(), cx)); + cx.spawn_weak(|_, mut cx| async move { + share.await?; + if cx.update(|cx| cx.window_is_active(window_id)) { + active_call.update(&mut cx, |call, cx| { + call.set_location(Some(&project), cx).detach_and_log_err(cx); + }); + } + anyhow::Ok(()) + }) + .detach_and_log_err(cx); } } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 40503297b3..f984e16990 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1048,15 +1048,13 @@ impl Project { } } - pub fn share(&mut self, room_id: u64, cx: &mut ModelContext) -> Task> { + pub fn shared(&mut self, project_id: u64, cx: &mut ModelContext) -> Task> { if let ProjectClientState::Local { remote_id, .. } = &mut self.client_state { - if let Some(remote_id) = remote_id { - return Task::ready(Ok(*remote_id)); + if remote_id.is_some() { + return Task::ready(Err(anyhow!("project was already shared"))); } - let response = self.client.request(proto::ShareProject { room_id }); cx.spawn(|this, mut cx| async move { - let project_id = response.await?.project_id; let mut worktree_share_tasks = Vec::new(); this.update(&mut cx, |this, cx| { if let ProjectClientState::Local { remote_id, .. } = &mut this.client_state { @@ -1113,7 +1111,7 @@ impl Project { }); futures::future::try_join_all(worktree_share_tasks).await?; - Ok(project_id) + Ok(()) }) } else { Task::ready(Err(anyhow!("can't share a remote project"))) From 669406d5af5f29011939e5cdf31e7852296d523c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 7 Oct 2022 11:56:50 +0200 Subject: [PATCH 178/314] Leave room when client is the only participant --- crates/call/src/call.rs | 37 ++++++++------- crates/call/src/room.rs | 66 ++++++++++++++++++++++---- crates/collab/src/integration_tests.rs | 2 + 3 files changed, 78 insertions(+), 27 deletions(-) diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 91fee3ae87..2cfb155d11 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -109,31 +109,32 @@ impl ActiveCall { initial_project: Option>, cx: &mut ModelContext, ) -> Task> { - let room = self.room.as_ref().map(|(room, _)| room.clone()); let client = self.client.clone(); let user_store = self.user_store.clone(); cx.spawn(|this, mut cx| async move { - let room = if let Some(room) = room { - room - } else { - cx.update(|cx| Room::create(client, user_store, cx)).await? - }; + if let Some(room) = this.read_with(&cx, |this, _| this.room().cloned()) { + let initial_project_id = if let Some(initial_project) = initial_project { + Some( + room.update(&mut cx, |room, cx| room.share_project(initial_project, cx)) + .await?, + ) + } else { + None + }; - let initial_project_id = if let Some(initial_project) = initial_project { - Some( - room.update(&mut cx, |room, cx| room.share_project(initial_project, cx)) - .await?, - ) + room.update(&mut cx, |room, cx| { + room.call(recipient_user_id, initial_project_id, cx) + }) + .await?; } else { - None + let room = cx + .update(|cx| { + Room::create(recipient_user_id, initial_project, client, user_store, cx) + }) + .await?; + this.update(&mut cx, |this, cx| this.set_room(Some(room), cx)); }; - this.update(&mut cx, |this, cx| this.set_room(Some(room.clone()), cx)); - room.update(&mut cx, |room, cx| { - room.call(recipient_user_id, initial_project_id, cx) - }) - .await?; - Ok(()) }) } diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index ba2c51b39e..29e3c04259 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -21,9 +21,10 @@ pub struct Room { status: RoomStatus, remote_participants: HashMap, pending_users: Vec>, + pending_call_count: usize, client: Arc, user_store: ModelHandle, - _subscriptions: Vec, + subscriptions: Vec, _pending_room_update: Option>, } @@ -62,7 +63,8 @@ impl Room { status: RoomStatus::Online, remote_participants: Default::default(), pending_users: Default::default(), - _subscriptions: vec![client.add_message_handler(cx.handle(), Self::handle_room_updated)], + pending_call_count: 0, + subscriptions: vec![client.add_message_handler(cx.handle(), Self::handle_room_updated)], _pending_room_update: None, client, user_store, @@ -70,13 +72,40 @@ impl Room { } pub(crate) fn create( + recipient_user_id: u64, + initial_project: Option>, client: Arc, user_store: ModelHandle, cx: &mut MutableAppContext, ) -> Task>> { cx.spawn(|mut cx| async move { - let room = client.request(proto::CreateRoom {}).await?; - Ok(cx.add_model(|cx| Self::new(room.id, client, user_store, cx))) + let response = client.request(proto::CreateRoom {}).await?; + let room = cx.add_model(|cx| Self::new(response.id, client, user_store, cx)); + let initial_project_id = if let Some(initial_project) = initial_project { + let initial_project_id = room + .update(&mut cx, |room, cx| { + room.share_project(initial_project.clone(), cx) + }) + .await?; + initial_project + .update(&mut cx, |project, cx| { + project.shared(initial_project_id, cx) + }) + .await?; + Some(initial_project_id) + } else { + None + }; + + match room + .update(&mut cx, |room, cx| { + room.call(recipient_user_id, initial_project_id, cx) + }) + .await + { + Ok(()) => Ok(room), + Err(_) => Err(anyhow!("call failed")), + } }) } @@ -96,6 +125,12 @@ impl Room { }) } + fn should_leave(&self) -> bool { + self.pending_users.is_empty() + && self.remote_participants.is_empty() + && self.pending_call_count == 0 + } + pub(crate) fn leave(&mut self, cx: &mut ModelContext) -> Result<()> { if self.status.is_offline() { return Err(anyhow!("room is offline")); @@ -104,6 +139,7 @@ impl Room { cx.notify(); self.status = RoomStatus::Offline; self.remote_participants.clear(); + self.subscriptions.clear(); self.client.send(proto::LeaveRoom { id: self.id })?; Ok(()) } @@ -134,8 +170,7 @@ impl Room { .payload .room .ok_or_else(|| anyhow!("invalid room"))?; - this.update(&mut cx, |this, cx| this.apply_room_update(room, cx))?; - Ok(()) + this.update(&mut cx, |this, cx| this.apply_room_update(room, cx)) } fn apply_room_update( @@ -209,6 +244,10 @@ impl Room { this.pending_users = pending_users; cx.notify(); } + + if this.should_leave() { + let _ = this.leave(cx); + } }); })); @@ -226,16 +265,25 @@ impl Room { return Task::ready(Err(anyhow!("room is offline"))); } + cx.notify(); let client = self.client.clone(); let room_id = self.id; - cx.foreground().spawn(async move { - client + self.pending_call_count += 1; + cx.spawn(|this, mut cx| async move { + let result = client .request(proto::Call { room_id, recipient_user_id, initial_project_id, }) - .await?; + .await; + this.update(&mut cx, |this, cx| { + this.pending_call_count -= 1; + if this.should_leave() { + this.leave(cx)?; + } + result + })?; Ok(()) }) } diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 55f1267ba2..ba344d0aab 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -383,9 +383,11 @@ async fn test_leaving_room_on_disconnection( } ); + // When user A disconnects, both client A and B clear their room on the active call. server.disconnect_client(client_a.current_user_id(cx_a)); cx_a.foreground().advance_clock(rpc::RECEIVE_TIMEOUT); active_call_a.read_with(cx_a, |call, _| assert!(call.room().is_none())); + active_call_b.read_with(cx_b, |call, _| assert!(call.room().is_none())); assert_eq!( room_participants(&room_a, cx_a), RoomParticipants { From e82320cde8822c2d20fe5920a34d348b39ed911a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 7 Oct 2022 12:00:23 +0200 Subject: [PATCH 179/314] Never set a room on active call if it is offline --- crates/call/src/call.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 2cfb155d11..7015173ce7 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -232,17 +232,21 @@ impl ActiveCall { fn set_room(&mut self, room: Option>, cx: &mut ModelContext) { if room.as_ref() != self.room.as_ref().map(|room| &room.0) { if let Some(room) = room { - let subscriptions = vec![ - cx.observe(&room, |this, room, cx| { - if room.read(cx).status().is_offline() { - this.set_room(None, cx); - } + if room.read(cx).status().is_offline() { + self.room = None; + } else { + let subscriptions = vec![ + cx.observe(&room, |this, room, cx| { + if room.read(cx).status().is_offline() { + this.set_room(None, cx); + } - cx.notify(); - }), - cx.subscribe(&room, |_, _, event, cx| cx.emit(event.clone())), - ]; - self.room = Some((room, subscriptions)); + cx.notify(); + }), + cx.subscribe(&room, |_, _, event, cx| cx.emit(event.clone())), + ]; + self.room = Some((room, subscriptions)); + } } else { self.room = None; } From d7cea646fc66e3fc97086d7f8b025a04fe2522d6 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 7 Oct 2022 12:21:56 +0200 Subject: [PATCH 180/314] Include a `busy` field in `proto::Contact` --- crates/client/src/user.rs | 2 + crates/collab/src/integration_tests.rs | 200 +++++++++++++++++++++++-- crates/collab/src/rpc.rs | 148 ++++++++++-------- crates/collab/src/rpc/store.rs | 9 ++ crates/rpc/proto/zed.proto | 3 +- 5 files changed, 291 insertions(+), 71 deletions(-) diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs index 252fb4d455..e0c5713dab 100644 --- a/crates/client/src/user.rs +++ b/crates/client/src/user.rs @@ -39,6 +39,7 @@ impl Eq for User {} pub struct Contact { pub user: Arc, pub online: bool, + pub busy: bool, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -625,6 +626,7 @@ impl Contact { Ok(Self { user, online: contact.online, + busy: contact.busy, }) } } diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index ba344d0aab..72ebe937ab 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -435,12 +435,14 @@ async fn test_calls_on_multiple_connections( }) .await .unwrap(); + deterministic.run_until_parked(); assert!(incoming_call_b1.next().await.unwrap().is_some()); assert!(incoming_call_b2.next().await.unwrap().is_some()); // User B declines the call on one of the two connections, causing both connections // to stop ringing. active_call_b2.update(cx_b2, |call, _| call.decline_incoming().unwrap()); + deterministic.run_until_parked(); assert!(incoming_call_b1.next().await.unwrap().is_none()); assert!(incoming_call_b2.next().await.unwrap().is_none()); @@ -451,6 +453,7 @@ async fn test_calls_on_multiple_connections( }) .await .unwrap(); + deterministic.run_until_parked(); assert!(incoming_call_b1.next().await.unwrap().is_some()); assert!(incoming_call_b2.next().await.unwrap().is_some()); @@ -460,6 +463,7 @@ async fn test_calls_on_multiple_connections( .update(cx_b2, |call, cx| call.accept_incoming(cx)) .await .unwrap(); + deterministic.run_until_parked(); assert!(incoming_call_b1.next().await.unwrap().is_none()); assert!(incoming_call_b2.next().await.unwrap().is_none()); @@ -472,6 +476,7 @@ async fn test_calls_on_multiple_connections( }) .await .unwrap(); + deterministic.run_until_parked(); assert!(incoming_call_b1.next().await.unwrap().is_some()); assert!(incoming_call_b2.next().await.unwrap().is_some()); @@ -482,6 +487,7 @@ async fn test_calls_on_multiple_connections( }) .await .unwrap(); + deterministic.run_until_parked(); assert!(incoming_call_b1.next().await.unwrap().is_none()); assert!(incoming_call_b2.next().await.unwrap().is_none()); } @@ -4015,19 +4021,31 @@ async fn test_contacts( server .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; + let active_call_a = cx_a.read(ActiveCall::global); + let active_call_b = cx_b.read(ActiveCall::global); + let active_call_c = cx_c.read(ActiveCall::global); deterministic.run_until_parked(); assert_eq!( contacts(&client_a, cx_a), - [("user_b".to_string(), true), ("user_c".to_string(), true)] + [ + ("user_b".to_string(), "online", "free"), + ("user_c".to_string(), "online", "free") + ] ); assert_eq!( contacts(&client_b, cx_b), - [("user_a".to_string(), true), ("user_c".to_string(), true)] + [ + ("user_a".to_string(), "online", "free"), + ("user_c".to_string(), "online", "free") + ] ); assert_eq!( contacts(&client_c, cx_c), - [("user_a".to_string(), true), ("user_b".to_string(), true)] + [ + ("user_a".to_string(), "online", "free"), + ("user_b".to_string(), "online", "free") + ] ); server.disconnect_client(client_c.current_user_id(cx_c)); @@ -4035,11 +4053,17 @@ async fn test_contacts( deterministic.advance_clock(rpc::RECEIVE_TIMEOUT); assert_eq!( contacts(&client_a, cx_a), - [("user_b".to_string(), true), ("user_c".to_string(), false)] + [ + ("user_b".to_string(), "online", "free"), + ("user_c".to_string(), "offline", "free") + ] ); assert_eq!( contacts(&client_b, cx_b), - [("user_a".to_string(), true), ("user_c".to_string(), false)] + [ + ("user_a".to_string(), "online", "free"), + ("user_c".to_string(), "offline", "free") + ] ); assert_eq!(contacts(&client_c, cx_c), []); @@ -4052,24 +4076,180 @@ async fn test_contacts( deterministic.run_until_parked(); assert_eq!( contacts(&client_a, cx_a), - [("user_b".to_string(), true), ("user_c".to_string(), true)] + [ + ("user_b".to_string(), "online", "free"), + ("user_c".to_string(), "online", "free") + ] ); assert_eq!( contacts(&client_b, cx_b), - [("user_a".to_string(), true), ("user_c".to_string(), true)] + [ + ("user_a".to_string(), "online", "free"), + ("user_c".to_string(), "online", "free") + ] ); assert_eq!( contacts(&client_c, cx_c), - [("user_a".to_string(), true), ("user_b".to_string(), true)] + [ + ("user_a".to_string(), "online", "free"), + ("user_b".to_string(), "online", "free") + ] + ); + + active_call_a + .update(cx_a, |call, cx| { + call.invite(client_b.user_id().unwrap(), None, cx) + }) + .await + .unwrap(); + deterministic.run_until_parked(); + assert_eq!( + contacts(&client_a, cx_a), + [ + ("user_b".to_string(), "online", "busy"), + ("user_c".to_string(), "online", "free") + ] + ); + assert_eq!( + contacts(&client_b, cx_b), + [ + ("user_a".to_string(), "online", "busy"), + ("user_c".to_string(), "online", "free") + ] + ); + assert_eq!( + contacts(&client_c, cx_c), + [ + ("user_a".to_string(), "online", "busy"), + ("user_b".to_string(), "online", "busy") + ] + ); + + active_call_b.update(cx_b, |call, _| call.decline_incoming().unwrap()); + deterministic.run_until_parked(); + assert_eq!( + contacts(&client_a, cx_a), + [ + ("user_b".to_string(), "online", "free"), + ("user_c".to_string(), "online", "free") + ] + ); + assert_eq!( + contacts(&client_b, cx_b), + [ + ("user_a".to_string(), "online", "free"), + ("user_c".to_string(), "online", "free") + ] + ); + assert_eq!( + contacts(&client_c, cx_c), + [ + ("user_a".to_string(), "online", "free"), + ("user_b".to_string(), "online", "free") + ] + ); + + active_call_c + .update(cx_c, |call, cx| { + call.invite(client_a.user_id().unwrap(), None, cx) + }) + .await + .unwrap(); + deterministic.run_until_parked(); + assert_eq!( + contacts(&client_a, cx_a), + [ + ("user_b".to_string(), "online", "free"), + ("user_c".to_string(), "online", "busy") + ] + ); + assert_eq!( + contacts(&client_b, cx_b), + [ + ("user_a".to_string(), "online", "busy"), + ("user_c".to_string(), "online", "busy") + ] + ); + assert_eq!( + contacts(&client_c, cx_c), + [ + ("user_a".to_string(), "online", "busy"), + ("user_b".to_string(), "online", "free") + ] + ); + + active_call_a + .update(cx_a, |call, cx| call.accept_incoming(cx)) + .await + .unwrap(); + deterministic.run_until_parked(); + assert_eq!( + contacts(&client_a, cx_a), + [ + ("user_b".to_string(), "online", "free"), + ("user_c".to_string(), "online", "busy") + ] + ); + assert_eq!( + contacts(&client_b, cx_b), + [ + ("user_a".to_string(), "online", "busy"), + ("user_c".to_string(), "online", "busy") + ] + ); + assert_eq!( + contacts(&client_c, cx_c), + [ + ("user_a".to_string(), "online", "busy"), + ("user_b".to_string(), "online", "free") + ] + ); + + active_call_a + .update(cx_a, |call, cx| { + call.invite(client_b.user_id().unwrap(), None, cx) + }) + .await + .unwrap(); + deterministic.run_until_parked(); + assert_eq!( + contacts(&client_a, cx_a), + [ + ("user_b".to_string(), "online", "busy"), + ("user_c".to_string(), "online", "busy") + ] + ); + assert_eq!( + contacts(&client_b, cx_b), + [ + ("user_a".to_string(), "online", "busy"), + ("user_c".to_string(), "online", "busy") + ] + ); + assert_eq!( + contacts(&client_c, cx_c), + [ + ("user_a".to_string(), "online", "busy"), + ("user_b".to_string(), "online", "busy") + ] ); #[allow(clippy::type_complexity)] - fn contacts(client: &TestClient, cx: &TestAppContext) -> Vec<(String, bool)> { + fn contacts( + client: &TestClient, + cx: &TestAppContext, + ) -> Vec<(String, &'static str, &'static str)> { client.user_store.read_with(cx, |store, _| { store .contacts() .iter() - .map(|contact| (contact.user.github_login.clone(), contact.online)) + .map(|contact| { + ( + contact.user.github_login.clone(), + if contact.online { "online" } else { "offline" }, + if contact.busy { "busy" } else { "free" }, + ) + }) .collect() }) } diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 50d1c82fc8..bd7afba775 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -585,8 +585,15 @@ impl Server { request: TypedEnvelope, response: Response, ) -> Result<()> { - let room_id = self.store().await.create_room(request.sender_id)?; + let user_id; + let room_id; + { + let mut store = self.store().await; + user_id = store.user_id_for_connection(request.sender_id)?; + room_id = store.create_room(request.sender_id)?; + } response.send(proto::CreateRoomResponse { id: room_id })?; + self.update_user_contacts(user_id).await?; Ok(()) } @@ -595,61 +602,71 @@ impl Server { request: TypedEnvelope, response: Response, ) -> Result<()> { - let room_id = request.payload.id; - let mut store = self.store().await; - let (room, recipient_connection_ids) = store.join_room(room_id, request.sender_id)?; - for recipient_id in recipient_connection_ids { - self.peer - .send(recipient_id, proto::CallCanceled {}) - .trace_err(); + let user_id; + { + let mut store = self.store().await; + user_id = store.user_id_for_connection(request.sender_id)?; + let (room, recipient_connection_ids) = + store.join_room(request.payload.id, request.sender_id)?; + for recipient_id in recipient_connection_ids { + self.peer + .send(recipient_id, proto::CallCanceled {}) + .trace_err(); + } + response.send(proto::JoinRoomResponse { + room: Some(room.clone()), + })?; + self.room_updated(room); } - response.send(proto::JoinRoomResponse { - room: Some(room.clone()), - })?; - self.room_updated(room); + self.update_user_contacts(user_id).await?; Ok(()) } async fn leave_room(self: Arc, message: TypedEnvelope) -> Result<()> { - let room_id = message.payload.id; - let mut store = self.store().await; - let left_room = store.leave_room(room_id, message.sender_id)?; + let user_id; + { + let mut store = self.store().await; + user_id = store.user_id_for_connection(message.sender_id)?; + let left_room = store.leave_room(message.payload.id, message.sender_id)?; - for project in left_room.unshared_projects { - for connection_id in project.connection_ids() { - self.peer.send( - connection_id, - proto::UnshareProject { - project_id: project.id.to_proto(), - }, - )?; - } - } - - for project in left_room.left_projects { - if project.remove_collaborator { - for connection_id in project.connection_ids { + for project in left_room.unshared_projects { + for connection_id in project.connection_ids() { self.peer.send( connection_id, - proto::RemoveProjectCollaborator { + proto::UnshareProject { project_id: project.id.to_proto(), - peer_id: message.sender_id.0, }, )?; } + } - self.peer.send( - message.sender_id, - proto::UnshareProject { - project_id: project.id.to_proto(), - }, - )?; + for project in left_room.left_projects { + if project.remove_collaborator { + for connection_id in project.connection_ids { + self.peer.send( + connection_id, + proto::RemoveProjectCollaborator { + project_id: project.id.to_proto(), + peer_id: message.sender_id.0, + }, + )?; + } + + self.peer.send( + message.sender_id, + proto::UnshareProject { + project_id: project.id.to_proto(), + }, + )?; + } + } + + if let Some(room) = left_room.room { + self.room_updated(room); } } + self.update_user_contacts(user_id).await?; - if let Some(room) = left_room.room { - self.room_updated(room); - } Ok(()) } @@ -694,6 +711,7 @@ impl Server { }) .collect::>() }; + self.update_user_contacts(recipient_user_id).await?; while let Some(call_response) = calls.next().await { match call_response.as_ref() { @@ -712,6 +730,7 @@ impl Server { let room = store.call_failed(room_id, recipient_user_id)?; self.room_updated(&room); } + self.update_user_contacts(recipient_user_id).await?; Err(anyhow!("failed to ring call recipient"))? } @@ -721,19 +740,23 @@ impl Server { request: TypedEnvelope, response: Response, ) -> Result<()> { - let mut store = self.store().await; - let (room, recipient_connection_ids) = store.cancel_call( - request.payload.room_id, - UserId::from_proto(request.payload.recipient_user_id), - request.sender_id, - )?; - for recipient_id in recipient_connection_ids { - self.peer - .send(recipient_id, proto::CallCanceled {}) - .trace_err(); + let recipient_user_id = UserId::from_proto(request.payload.recipient_user_id); + { + let mut store = self.store().await; + let (room, recipient_connection_ids) = store.cancel_call( + request.payload.room_id, + recipient_user_id, + request.sender_id, + )?; + for recipient_id in recipient_connection_ids { + self.peer + .send(recipient_id, proto::CallCanceled {}) + .trace_err(); + } + self.room_updated(room); + response.send(proto::Ack {})?; } - self.room_updated(room); - response.send(proto::Ack {})?; + self.update_user_contacts(recipient_user_id).await?; Ok(()) } @@ -741,15 +764,20 @@ impl Server { self: Arc, message: TypedEnvelope, ) -> Result<()> { - let mut store = self.store().await; - let (room, recipient_connection_ids) = - store.decline_call(message.payload.room_id, message.sender_id)?; - for recipient_id in recipient_connection_ids { - self.peer - .send(recipient_id, proto::CallCanceled {}) - .trace_err(); + let recipient_user_id; + { + let mut store = self.store().await; + recipient_user_id = store.user_id_for_connection(message.sender_id)?; + let (room, recipient_connection_ids) = + store.decline_call(message.payload.room_id, message.sender_id)?; + for recipient_id in recipient_connection_ids { + self.peer + .send(recipient_id, proto::CallCanceled {}) + .trace_err(); + } + self.room_updated(room); } - self.room_updated(room); + self.update_user_contacts(recipient_user_id).await?; Ok(()) } diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index a9ae91aba0..be7f798685 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -314,6 +314,14 @@ impl Store { .is_empty() } + fn is_user_busy(&self, user_id: UserId) -> bool { + self.connected_users + .get(&user_id) + .unwrap_or(&Default::default()) + .active_call + .is_some() + } + pub fn build_initial_contacts_update( &self, contacts: Vec, @@ -352,6 +360,7 @@ impl Store { proto::Contact { user_id: user_id.to_proto(), online: self.is_user_online(user_id), + busy: self.is_user_busy(user_id), should_notify, } } diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 334bcfbf90..5f62e3585e 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -1023,7 +1023,8 @@ message ChannelMessage { message Contact { uint64 user_id = 1; bool online = 2; - bool should_notify = 3; + bool busy = 3; + bool should_notify = 4; } message WorktreeMetadata { From 4aaf3df8c738ee83845b9de13dc7720b205c6342 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 7 Oct 2022 13:56:28 +0200 Subject: [PATCH 181/314] Show contact status --- crates/collab_ui/src/contacts_popover.rs | 29 ++++++++++++++++++++---- crates/theme/src/theme.rs | 2 ++ styles/src/styleTree/contactsPopover.ts | 12 ++++++++++ styles/src/styleTree/workspace.ts | 1 - styles/src/themes/common/base16.ts | 1 + styles/src/themes/common/theme.ts | 1 + 6 files changed, 41 insertions(+), 5 deletions(-) diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index 388b344879..074e816163 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -522,10 +522,31 @@ impl ContactsPopover { MouseEventHandler::::new(contact.user.id as usize, cx, |_, _| { Flex::row() .with_children(contact.user.avatar.clone().map(|avatar| { - Image::new(avatar) - .with_style(theme.contact_avatar) - .aligned() - .left() + let status_badge = if contact.online { + Some( + Empty::new() + .collapsed() + .contained() + .with_style(if contact.busy { + theme.contact_status_busy + } else { + theme.contact_status_free + }) + .aligned() + .boxed(), + ) + } else { + None + }; + Stack::new() + .with_child( + Image::new(avatar) + .with_style(theme.contact_avatar) + .aligned() + .left() + .boxed(), + ) + .with_children(status_badge) .boxed() })) .with_child( diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index b9de72065a..d70aaed189 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -95,6 +95,8 @@ pub struct ContactsPopover { pub project_row: Interactive, pub row_height: f32, pub contact_avatar: ImageStyle, + pub contact_status_free: ContainerStyle, + pub contact_status_busy: ContainerStyle, pub contact_username: ContainedText, pub contact_button: Interactive, pub contact_button_spacing: f32, diff --git a/styles/src/styleTree/contactsPopover.ts b/styles/src/styleTree/contactsPopover.ts index 82174fd672..16656c3fcd 100644 --- a/styles/src/styleTree/contactsPopover.ts +++ b/styles/src/styleTree/contactsPopover.ts @@ -116,6 +116,18 @@ export default function contactsPopover(theme: Theme) { cornerRadius: 10, width: 18, }, + contactStatusFree: { + cornerRadius: 4, + padding: 4, + margin: { top: 12, left: 12 }, + background: iconColor(theme, "success"), + }, + contactStatusBusy: { + cornerRadius: 3, + padding: 2, + margin: { top: 3, left: 3 }, + background: iconColor(theme, "feature"), + }, contactUsername: { ...text(theme, "mono", "primary", { size: "sm" }), margin: { diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 7ad99ef6ab..c970c38296 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -5,7 +5,6 @@ import { border, iconColor, modalShadow, - popoverShadow, text, } from "./components"; import statusBar from "./statusBar"; diff --git a/styles/src/themes/common/base16.ts b/styles/src/themes/common/base16.ts index 7aa72ef137..3612988075 100644 --- a/styles/src/themes/common/base16.ts +++ b/styles/src/themes/common/base16.ts @@ -137,6 +137,7 @@ export function createTheme( ok: sample(ramps.green, 0.5), error: sample(ramps.red, 0.5), warning: sample(ramps.yellow, 0.5), + success: sample(ramps.green, 0.5), info: sample(ramps.blue, 0.5), onMedia: darkest, }; diff --git a/styles/src/themes/common/theme.ts b/styles/src/themes/common/theme.ts index e01435b846..18ad8122e0 100644 --- a/styles/src/themes/common/theme.ts +++ b/styles/src/themes/common/theme.ts @@ -123,6 +123,7 @@ export default interface Theme { error: string; warning: string; info: string; + success: string; }; editor: { background: string; From 386de03f46a95bac42c97086894ebe684c63cbb2 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 7 Oct 2022 14:39:11 +0200 Subject: [PATCH 182/314] Fix room disconnection problems when creating room and sharing project --- crates/call/src/participant.rs | 2 +- crates/call/src/room.rs | 24 ++++--- crates/collab/src/integration_tests.rs | 19 +++++- crates/project/src/project.rs | 91 +++++++++++++------------- crates/rpc/src/peer.rs | 6 +- 5 files changed, 81 insertions(+), 61 deletions(-) diff --git a/crates/call/src/participant.rs b/crates/call/src/participant.rs index 15aaf2f13b..db7a84e584 100644 --- a/crates/call/src/participant.rs +++ b/crates/call/src/participant.rs @@ -20,7 +20,7 @@ impl ParticipantLocation { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct RemoteParticipant { pub user: Arc, pub project_ids: Vec, diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 29e3c04259..e08d814a3e 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -22,6 +22,7 @@ pub struct Room { remote_participants: HashMap, pending_users: Vec>, pending_call_count: usize, + leave_when_empty: bool, client: Arc, user_store: ModelHandle, subscriptions: Vec, @@ -65,6 +66,7 @@ impl Room { pending_users: Default::default(), pending_call_count: 0, subscriptions: vec![client.add_message_handler(cx.handle(), Self::handle_room_updated)], + leave_when_empty: false, _pending_room_update: None, client, user_store, @@ -81,17 +83,13 @@ impl Room { cx.spawn(|mut cx| async move { let response = client.request(proto::CreateRoom {}).await?; let room = cx.add_model(|cx| Self::new(response.id, client, user_store, cx)); + let initial_project_id = if let Some(initial_project) = initial_project { let initial_project_id = room .update(&mut cx, |room, cx| { room.share_project(initial_project.clone(), cx) }) .await?; - initial_project - .update(&mut cx, |project, cx| { - project.shared(initial_project_id, cx) - }) - .await?; Some(initial_project_id) } else { None @@ -103,8 +101,11 @@ impl Room { }) .await { - Ok(()) => Ok(room), - Err(_) => Err(anyhow!("call failed")), + Ok(()) => { + room.update(&mut cx, |room, _| room.leave_when_empty = true); + Ok(room) + } + Err(error) => Err(anyhow!("room creation failed: {:?}", error)), } }) } @@ -120,13 +121,18 @@ impl Room { let response = client.request(proto::JoinRoom { id: room_id }).await?; let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?; let room = cx.add_model(|cx| Self::new(room_id, client, user_store, cx)); - room.update(&mut cx, |room, cx| room.apply_room_update(room_proto, cx))?; + room.update(&mut cx, |room, cx| { + room.leave_when_empty = true; + room.apply_room_update(room_proto, cx)?; + anyhow::Ok(()) + })?; Ok(room) }) } fn should_leave(&self) -> bool { - self.pending_users.is_empty() + self.leave_when_empty + && self.pending_users.is_empty() && self.remote_participants.is_empty() && self.pending_call_count == 0 } diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 72ebe937ab..36b9d46d7b 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -504,9 +504,10 @@ async fn test_share_project( let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; server - .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)]) .await; let active_call_a = cx_a.read(ActiveCall::global); + let active_call_b = cx_b.read(ActiveCall::global); client_a .fs @@ -524,13 +525,25 @@ async fn test_share_project( ) .await; + // Invite client B to collaborate on a project let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; - let project_id = active_call_a - .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) + active_call_a + .update(cx_a, |call, cx| { + call.invite(client_b.user_id().unwrap(), Some(project_a.clone()), cx) + }) .await .unwrap(); // Join that project as client B + let incoming_call_b = active_call_b.read_with(cx_b, |call, _| call.incoming()); + deterministic.run_until_parked(); + let call = incoming_call_b.borrow().clone().unwrap(); + assert_eq!(call.caller.github_login, "user_a"); + let project_id = call.initial_project_id.unwrap(); + active_call_b + .update(cx_b, |call, cx| call.accept_incoming(cx)) + .await + .unwrap(); let client_b_peer_id = client_b.peer_id; let project_b = client_b.build_remote_project(project_id, cx_b).await; let replica_id_b = project_b.read_with(cx_b, |project, _| project.replica_id()); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index f984e16990..000751d417 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1054,62 +1054,59 @@ impl Project { return Task::ready(Err(anyhow!("project was already shared"))); } - cx.spawn(|this, mut cx| async move { - let mut worktree_share_tasks = Vec::new(); - this.update(&mut cx, |this, cx| { - if let ProjectClientState::Local { remote_id, .. } = &mut this.client_state { - *remote_id = Some(project_id); - } + *remote_id = Some(project_id); - for open_buffer in this.opened_buffers.values_mut() { - match open_buffer { - OpenBuffer::Strong(_) => {} - OpenBuffer::Weak(buffer) => { - if let Some(buffer) = buffer.upgrade(cx) { - *open_buffer = OpenBuffer::Strong(buffer); - } - } - OpenBuffer::Operations(_) => unreachable!(), + let mut worktree_share_tasks = Vec::new(); + + for open_buffer in self.opened_buffers.values_mut() { + match open_buffer { + OpenBuffer::Strong(_) => {} + OpenBuffer::Weak(buffer) => { + if let Some(buffer) = buffer.upgrade(cx) { + *open_buffer = OpenBuffer::Strong(buffer); } } + OpenBuffer::Operations(_) => unreachable!(), + } + } - for worktree_handle in this.worktrees.iter_mut() { - match worktree_handle { - WorktreeHandle::Strong(_) => {} - WorktreeHandle::Weak(worktree) => { - if let Some(worktree) = worktree.upgrade(cx) { - *worktree_handle = WorktreeHandle::Strong(worktree); - } - } + for worktree_handle in self.worktrees.iter_mut() { + match worktree_handle { + WorktreeHandle::Strong(_) => {} + WorktreeHandle::Weak(worktree) => { + if let Some(worktree) = worktree.upgrade(cx) { + *worktree_handle = WorktreeHandle::Strong(worktree); } } + } + } - for worktree in this.worktrees(cx).collect::>() { - worktree.update(cx, |worktree, cx| { - let worktree = worktree.as_local_mut().unwrap(); - worktree_share_tasks.push(worktree.share(project_id, cx)); - }); - } - - for (server_id, status) in &this.language_server_statuses { - this.client - .send(proto::StartLanguageServer { - project_id, - server: Some(proto::LanguageServer { - id: *server_id as u64, - name: status.name.clone(), - }), - }) - .log_err(); - } - - this.client_subscriptions - .push(this.client.add_model_for_remote_entity(project_id, cx)); - this.metadata_changed(cx); - cx.emit(Event::RemoteIdChanged(Some(project_id))); - cx.notify(); + for worktree in self.worktrees(cx).collect::>() { + worktree.update(cx, |worktree, cx| { + let worktree = worktree.as_local_mut().unwrap(); + worktree_share_tasks.push(worktree.share(project_id, cx)); }); + } + for (server_id, status) in &self.language_server_statuses { + self.client + .send(proto::StartLanguageServer { + project_id, + server: Some(proto::LanguageServer { + id: *server_id as u64, + name: status.name.clone(), + }), + }) + .log_err(); + } + + self.client_subscriptions + .push(self.client.add_model_for_remote_entity(project_id, cx)); + self.metadata_changed(cx); + cx.emit(Event::RemoteIdChanged(Some(project_id))); + cx.notify(); + + cx.foreground().spawn(async move { futures::future::try_join_all(worktree_share_tasks).await?; Ok(()) }) diff --git a/crates/rpc/src/peer.rs b/crates/rpc/src/peer.rs index 6c1c4f01da..834acd0afa 100644 --- a/crates/rpc/src/peer.rs +++ b/crates/rpc/src/peer.rs @@ -394,7 +394,11 @@ impl Peer { send?; let (response, _barrier) = rx.await.map_err(|_| anyhow!("connection was closed"))?; if let Some(proto::envelope::Payload::Error(error)) = &response.payload { - Err(anyhow!("RPC request failed - {}", error.message)) + Err(anyhow!( + "RPC request {} failed - {}", + T::NAME, + error.message + )) } else { T::Response::from_envelope(response) .ok_or_else(|| anyhow!("received response of the wrong type")) From d3cddfdced0f5f6695249589de0dc24cf06a453f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 7 Oct 2022 14:42:18 +0200 Subject: [PATCH 183/314] Fix styling for busy contacts --- styles/src/styleTree/contactsPopover.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/styles/src/styleTree/contactsPopover.ts b/styles/src/styleTree/contactsPopover.ts index 16656c3fcd..7dfdb404d6 100644 --- a/styles/src/styleTree/contactsPopover.ts +++ b/styles/src/styleTree/contactsPopover.ts @@ -123,10 +123,10 @@ export default function contactsPopover(theme: Theme) { background: iconColor(theme, "success"), }, contactStatusBusy: { - cornerRadius: 3, - padding: 2, - margin: { top: 3, left: 3 }, - background: iconColor(theme, "feature"), + cornerRadius: 4, + padding: 4, + margin: { top: 12, left: 12 }, + background: iconColor(theme, "warning"), }, contactUsername: { ...text(theme, "mono", "primary", { size: "sm" }), From 6fb5901d69054eadc881c104ff2b4220ff1dd05e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 7 Oct 2022 14:47:06 +0200 Subject: [PATCH 184/314] Ensure sharing the same project twice is idempotent --- crates/call/src/room.rs | 6 ++++++ crates/collab/src/integration_tests.rs | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index e08d814a3e..1630edb300 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -299,6 +299,12 @@ impl Room { project: ModelHandle, cx: &mut ModelContext, ) -> Task> { + if project.read(cx).is_remote() { + return Task::ready(Err(anyhow!("can't share remote project"))); + } else if let Some(project_id) = project.read(cx).remote_id() { + return Task::ready(Ok(project_id)); + } + let request = self .client .request(proto::ShareProject { room_id: self.id() }); diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 36b9d46d7b..9cde2b2206 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -838,6 +838,16 @@ async fn test_active_call_events( ); assert_eq!(mem::take(&mut *events_b.borrow_mut()), vec![]); + // Sharing a project twice is idempotent. + let project_b_id_2 = active_call_b + .update(cx_b, |call, cx| call.share_project(project_b.clone(), cx)) + .await + .unwrap(); + assert_eq!(project_b_id_2, project_b_id); + deterministic.run_until_parked(); + assert_eq!(mem::take(&mut *events_a.borrow_mut()), vec![]); + assert_eq!(mem::take(&mut *events_b.borrow_mut()), vec![]); + fn active_call_events(cx: &mut TestAppContext) -> Rc>> { let events = Rc::new(RefCell::new(Vec::new())); let active_call = cx.read(ActiveCall::global); From 251e06c50f3a7c88e1cd73465872629a5574a5e0 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 7 Oct 2022 14:51:04 +0200 Subject: [PATCH 185/314] :lipstick: --- crates/call/src/room.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 1630edb300..21e7c85c7f 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -97,14 +97,12 @@ impl Room { match room .update(&mut cx, |room, cx| { + room.leave_when_empty = true; room.call(recipient_user_id, initial_project_id, cx) }) .await { - Ok(()) => { - room.update(&mut cx, |room, _| room.leave_when_empty = true); - Ok(room) - } + Ok(()) => Ok(room), Err(error) => Err(anyhow!("room creation failed: {:?}", error)), } }) From 560d8a8004bc8614268d30640887ed486df0be9f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 7 Oct 2022 14:52:39 +0200 Subject: [PATCH 186/314] Don't leave the room if there's a pending room update --- crates/call/src/room.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 21e7c85c7f..41648e6b24 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -26,7 +26,7 @@ pub struct Room { client: Arc, user_store: ModelHandle, subscriptions: Vec, - _pending_room_update: Option>, + pending_room_update: Option>, } impl Entity for Room { @@ -67,7 +67,7 @@ impl Room { pending_call_count: 0, subscriptions: vec![client.add_message_handler(cx.handle(), Self::handle_room_updated)], leave_when_empty: false, - _pending_room_update: None, + pending_room_update: None, client, user_store, } @@ -130,6 +130,7 @@ impl Room { fn should_leave(&self) -> bool { self.leave_when_empty + && self.pending_room_update.is_none() && self.pending_users.is_empty() && self.remote_participants.is_empty() && self.pending_call_count == 0 @@ -197,7 +198,7 @@ impl Room { user_store.get_users(room.pending_user_ids, cx), ) }); - self._pending_room_update = Some(cx.spawn(|this, mut cx| async move { + self.pending_room_update = Some(cx.spawn(|this, mut cx| async move { let (participants, pending_users) = futures::join!(participants, pending_users); this.update(&mut cx, |this, cx| { @@ -249,6 +250,7 @@ impl Room { cx.notify(); } + this.pending_room_update.take(); if this.should_leave() { let _ = this.leave(cx); } From 96c5bb8c396d0ac9a5fd7a9962783502cce2eca9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 7 Oct 2022 15:07:09 +0200 Subject: [PATCH 187/314] Fix flicker due to adding and removing menu bar extra unnecessarily --- crates/collab_ui/src/menu_bar_extra.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/crates/collab_ui/src/menu_bar_extra.rs b/crates/collab_ui/src/menu_bar_extra.rs index 8b2baa53eb..814d51b189 100644 --- a/crates/collab_ui/src/menu_bar_extra.rs +++ b/crates/collab_ui/src/menu_bar_extra.rs @@ -16,13 +16,17 @@ pub fn init(cx: &mut MutableAppContext) { let mut status_bar_item_id = None; cx.observe(&ActiveCall::global(cx), move |call, cx| { - if let Some(status_bar_item_id) = status_bar_item_id.take() { - cx.remove_status_bar_item(status_bar_item_id); - } + let had_room = status_bar_item_id.is_some(); + let has_room = call.read(cx).room().is_some(); + if had_room != has_room { + if let Some(status_bar_item_id) = status_bar_item_id.take() { + cx.remove_status_bar_item(status_bar_item_id); + } - if call.read(cx).room().is_some() { - let (id, _) = cx.add_status_bar_item(|_| MenuBarExtra::new()); - status_bar_item_id = Some(id); + if has_room { + let (id, _) = cx.add_status_bar_item(|_| MenuBarExtra::new()); + status_bar_item_id = Some(id); + } } }) .detach(); @@ -34,6 +38,12 @@ struct MenuBarExtra { impl Entity for MenuBarExtra { type Event = (); + + fn release(&mut self, cx: &mut MutableAppContext) { + if let Some(popover) = self.popover.take() { + cx.remove_window(popover.window_id()); + } + } } impl View for MenuBarExtra { From f9fb3f78b2a63714387f46dd603f5f1194e3f8fb Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 7 Oct 2022 17:01:48 +0200 Subject: [PATCH 188/314] WIP: Render active call in contacts popover Co-Authored-By: Nathan Sobo --- crates/call/src/room.rs | 85 ++++++++---- crates/collab/src/integration_tests.rs | 2 +- crates/collab/src/rpc/store.rs | 26 ++-- crates/collab_ui/src/contacts_popover.rs | 159 +++++++++++++++++------ crates/rpc/proto/zed.proto | 2 +- crates/rpc/src/peer.rs | 2 +- crates/theme/src/theme.rs | 1 + styles/src/styleTree/contactsPopover.ts | 3 + 8 files changed, 198 insertions(+), 82 deletions(-) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 41648e6b24..9cad1ff211 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -4,7 +4,7 @@ use crate::{ }; use anyhow::{anyhow, Result}; use client::{proto, Client, PeerId, TypedEnvelope, User, UserStore}; -use collections::{HashMap, HashSet}; +use collections::{BTreeMap, HashSet}; use futures::StreamExt; use gpui::{AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task}; use project::Project; @@ -19,8 +19,9 @@ pub enum Event { pub struct Room { id: u64, status: RoomStatus, - remote_participants: HashMap, - pending_users: Vec>, + remote_participants: BTreeMap, + pending_participants: Vec>, + participant_user_ids: HashSet, pending_call_count: usize, leave_when_empty: bool, client: Arc, @@ -62,8 +63,9 @@ impl Room { Self { id, status: RoomStatus::Online, + participant_user_ids: Default::default(), remote_participants: Default::default(), - pending_users: Default::default(), + pending_participants: Default::default(), pending_call_count: 0, subscriptions: vec![client.add_message_handler(cx.handle(), Self::handle_room_updated)], leave_when_empty: false, @@ -131,7 +133,7 @@ impl Room { fn should_leave(&self) -> bool { self.leave_when_empty && self.pending_room_update.is_none() - && self.pending_users.is_empty() + && self.pending_participants.is_empty() && self.remote_participants.is_empty() && self.pending_call_count == 0 } @@ -144,6 +146,8 @@ impl Room { cx.notify(); self.status = RoomStatus::Offline; self.remote_participants.clear(); + self.pending_participants.clear(); + self.participant_user_ids.clear(); self.subscriptions.clear(); self.client.send(proto::LeaveRoom { id: self.id })?; Ok(()) @@ -157,12 +161,16 @@ impl Room { self.status } - pub fn remote_participants(&self) -> &HashMap { + pub fn remote_participants(&self) -> &BTreeMap { &self.remote_participants } - pub fn pending_users(&self) -> &[Arc] { - &self.pending_users + pub fn pending_participants(&self) -> &[Arc] { + &self.pending_participants + } + + pub fn contains_participant(&self, user_id: u64) -> bool { + self.participant_user_ids.contains(&user_id) } async fn handle_room_updated( @@ -187,27 +195,29 @@ impl Room { room.participants .retain(|participant| Some(participant.user_id) != self.client.user_id()); - let participant_user_ids = room + let remote_participant_user_ids = room .participants .iter() .map(|p| p.user_id) .collect::>(); - let (participants, pending_users) = self.user_store.update(cx, move |user_store, cx| { - ( - user_store.get_users(participant_user_ids, cx), - user_store.get_users(room.pending_user_ids, cx), - ) - }); + let (remote_participants, pending_participants) = + self.user_store.update(cx, move |user_store, cx| { + ( + user_store.get_users(remote_participant_user_ids, cx), + user_store.get_users(room.pending_participant_user_ids, cx), + ) + }); self.pending_room_update = Some(cx.spawn(|this, mut cx| async move { - let (participants, pending_users) = futures::join!(participants, pending_users); + let (remote_participants, pending_participants) = + futures::join!(remote_participants, pending_participants); this.update(&mut cx, |this, cx| { - if let Some(participants) = participants.log_err() { - let mut seen_participants = HashSet::default(); + this.participant_user_ids.clear(); + if let Some(participants) = remote_participants.log_err() { for (participant, user) in room.participants.into_iter().zip(participants) { let peer_id = PeerId(participant.peer_id); - seen_participants.insert(peer_id); + this.participant_user_ids.insert(participant.user_id); let existing_project_ids = this .remote_participants @@ -234,19 +244,18 @@ impl Room { ); } - for participant_peer_id in - this.remote_participants.keys().copied().collect::>() - { - if !seen_participants.contains(&participant_peer_id) { - this.remote_participants.remove(&participant_peer_id); - } - } + this.remote_participants.retain(|_, participant| { + this.participant_user_ids.contains(&participant.user.id) + }); cx.notify(); } - if let Some(pending_users) = pending_users.log_err() { - this.pending_users = pending_users; + if let Some(pending_participants) = pending_participants.log_err() { + this.pending_participants = pending_participants; + for participant in &this.pending_participants { + this.participant_user_ids.insert(participant.id); + } cx.notify(); } @@ -254,6 +263,8 @@ impl Room { if this.should_leave() { let _ = this.leave(cx); } + + this.check_invariants(); }); })); @@ -261,6 +272,24 @@ impl Room { Ok(()) } + fn check_invariants(&self) { + #[cfg(any(test, feature = "test-support"))] + { + for participant in self.remote_participants.values() { + assert!(self.participant_user_ids.contains(&participant.user.id)); + } + + for participant in &self.pending_participants { + assert!(self.participant_user_ids.contains(&participant.id)); + } + + assert_eq!( + self.participant_user_ids.len(), + self.remote_participants.len() + self.pending_participants.len() + ); + } + } + pub(crate) fn call( &mut self, recipient_user_id: u64, diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 9cde2b2206..c94d766f82 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -6475,7 +6475,7 @@ fn room_participants(room: &ModelHandle, cx: &mut TestAppContext) -> RoomP .map(|(_, participant)| participant.user.github_login.clone()) .collect(), pending: room - .pending_users() + .pending_participants() .iter() .map(|user| user.github_login.clone()) .collect(), diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index be7f798685..5b23ac92d5 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -229,7 +229,7 @@ impl Store { .retain(|participant| participant.peer_id != connection_id.0); if prev_participant_count == room.participants.len() { if connected_user.connection_ids.is_empty() { - room.pending_user_ids + room.pending_participant_user_ids .retain(|pending_user_id| *pending_user_id != user_id.to_proto()); result.room_id = Some(room_id); connected_user.active_call = None; @@ -239,7 +239,7 @@ impl Store { connected_user.active_call = None; } - if room.participants.is_empty() && room.pending_user_ids.is_empty() { + if room.participants.is_empty() && room.pending_participant_user_ids.is_empty() { self.rooms.remove(&room_id); } } else { @@ -432,10 +432,11 @@ impl Store { .get_mut(&room_id) .ok_or_else(|| anyhow!("no such room"))?; anyhow::ensure!( - room.pending_user_ids.contains(&user_id.to_proto()), + room.pending_participant_user_ids + .contains(&user_id.to_proto()), anyhow!("no such room") ); - room.pending_user_ids + room.pending_participant_user_ids .retain(|pending| *pending != user_id.to_proto()); room.participants.push(proto::Participant { user_id: user_id.to_proto(), @@ -490,7 +491,7 @@ impl Store { .ok_or_else(|| anyhow!("no such room"))?; room.participants .retain(|participant| participant.peer_id != connection_id.0); - if room.participants.is_empty() && room.pending_user_ids.is_empty() { + if room.participants.is_empty() && room.pending_participant_user_ids.is_empty() { self.rooms.remove(&room_id); } @@ -537,12 +538,13 @@ impl Store { "no such room" ); anyhow::ensure!( - room.pending_user_ids + room.pending_participant_user_ids .iter() .all(|user_id| UserId::from_proto(*user_id) != recipient_user_id), "cannot call the same user more than once" ); - room.pending_user_ids.push(recipient_user_id.to_proto()); + room.pending_participant_user_ids + .push(recipient_user_id.to_proto()); if let Some(initial_project_id) = initial_project_id { let project = self @@ -589,7 +591,7 @@ impl Store { .rooms .get_mut(&room_id) .ok_or_else(|| anyhow!("no such room"))?; - room.pending_user_ids + room.pending_participant_user_ids .retain(|user_id| UserId::from_proto(*user_id) != to_user_id); Ok(room) } @@ -635,7 +637,7 @@ impl Store { .rooms .get_mut(&room_id) .ok_or_else(|| anyhow!("no such room"))?; - room.pending_user_ids + room.pending_participant_user_ids .retain(|user_id| UserId::from_proto(*user_id) != recipient_user_id); let recipient = self.connected_users.get_mut(&recipient_user_id).unwrap(); @@ -663,7 +665,7 @@ impl Store { .rooms .get_mut(&active_call.room_id) .ok_or_else(|| anyhow!("no such room"))?; - room.pending_user_ids + room.pending_participant_user_ids .retain(|user_id| UserId::from_proto(*user_id) != recipient_user_id); Ok((room, recipient_connection_ids)) } else { @@ -1115,7 +1117,7 @@ impl Store { } for (room_id, room) in &self.rooms { - for pending_user_id in &room.pending_user_ids { + for pending_user_id in &room.pending_participant_user_ids { assert!( self.connected_users .contains_key(&UserId::from_proto(*pending_user_id)), @@ -1140,7 +1142,7 @@ impl Store { } assert!( - !room.pending_user_ids.is_empty() || !room.participants.is_empty(), + !room.pending_participant_user_ids.is_empty() || !room.participants.is_empty(), "room can't be empty" ); } diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index 074e816163..090720c7a6 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use crate::contact_finder; use call::ActiveCall; -use client::{Contact, User, UserStore}; +use client::{Contact, PeerId, User, UserStore}; use editor::{Cancel, Editor}; use fuzzy::{match_strings, StringMatchCandidate}; use gpui::{ @@ -41,6 +41,7 @@ struct Call { #[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)] enum Section { + ActiveCall, Requests, Online, Offline, @@ -49,6 +50,7 @@ enum Section { #[derive(Clone)] enum ContactEntry { Header(Section), + CallParticipant { user: Arc, is_pending: bool }, IncomingRequest(Arc), OutgoingRequest(Arc), Contact(Arc), @@ -62,6 +64,11 @@ impl PartialEq for ContactEntry { return section_1 == section_2; } } + ContactEntry::CallParticipant { user: user_1, .. } => { + if let ContactEntry::CallParticipant { user: user_2, .. } = other { + return user_1.id == user_2.id; + } + } ContactEntry::IncomingRequest(user_1) => { if let ContactEntry::IncomingRequest(user_2) = other { return user_1.id == user_2.id; @@ -157,6 +164,9 @@ impl ContactsPopover { cx, ) } + ContactEntry::CallParticipant { user, is_pending } => { + Self::render_call_participant(user, *is_pending, &theme.contacts_popover) + } ContactEntry::IncomingRequest(user) => Self::render_contact_request( user.clone(), this.user_store.clone(), @@ -186,7 +196,7 @@ impl ContactsPopover { let active_call = ActiveCall::global(cx); let mut subscriptions = Vec::new(); subscriptions.push(cx.observe(&user_store, |this, _, cx| this.update_entries(cx))); - subscriptions.push(cx.observe(&active_call, |_, _, cx| cx.notify())); + subscriptions.push(cx.observe(&active_call, |this, _, cx| this.update_entries(cx))); let mut this = Self { list_state, @@ -291,6 +301,66 @@ impl ContactsPopover { let prev_selected_entry = self.selection.and_then(|ix| self.entries.get(ix).cloned()); self.entries.clear(); + if let Some(room) = ActiveCall::global(cx).read(cx).room() { + let room = room.read(cx); + + self.entries.push(ContactEntry::Header(Section::ActiveCall)); + if !self.collapsed_sections.contains(&Section::ActiveCall) { + // Populate remote participants. + self.match_candidates.clear(); + self.match_candidates + .extend( + room.remote_participants() + .iter() + .map(|(peer_id, participant)| StringMatchCandidate { + id: peer_id.0 as usize, + string: participant.user.github_login.clone(), + char_bag: participant.user.github_login.chars().collect(), + }), + ); + let matches = executor.block(match_strings( + &self.match_candidates, + &query, + true, + usize::MAX, + &Default::default(), + executor.clone(), + )); + self.entries.extend(matches.iter().map(|mat| { + ContactEntry::CallParticipant { + user: room.remote_participants()[&PeerId(mat.candidate_id as u32)] + .user + .clone(), + is_pending: false, + } + })); + + // Populate pending participants. + self.match_candidates.clear(); + self.match_candidates + .extend(room.pending_participants().iter().enumerate().map( + |(id, participant)| StringMatchCandidate { + id, + string: participant.github_login.clone(), + char_bag: participant.github_login.chars().collect(), + }, + )); + let matches = executor.block(match_strings( + &self.match_candidates, + &query, + true, + usize::MAX, + &Default::default(), + executor.clone(), + )); + self.entries + .extend(matches.iter().map(|mat| ContactEntry::CallParticipant { + user: room.pending_participants()[mat.candidate_id].clone(), + is_pending: true, + })); + } + } + let mut request_entries = Vec::new(); let incoming = user_store.incoming_contact_requests(); if !incoming.is_empty() { @@ -359,7 +429,6 @@ impl ContactsPopover { let contacts = user_store.contacts(); if !contacts.is_empty() { - // Always put the current user first. self.match_candidates.clear(); self.match_candidates .extend( @@ -382,9 +451,16 @@ impl ContactsPopover { executor.clone(), )); - let (online_contacts, offline_contacts) = matches + let (mut online_contacts, offline_contacts) = matches .iter() .partition::, _>(|mat| contacts[mat.candidate_id].online); + if let Some(room) = ActiveCall::global(cx).read(cx).room() { + let room = room.read(cx); + online_contacts.retain(|contact| { + let contact = &contacts[contact.candidate_id]; + !room.contains_participant(contact.user.id) + }); + } for (matches, section) in [ (online_contacts, Section::Online), @@ -416,41 +492,46 @@ impl ContactsPopover { cx.notify(); } - fn render_active_call(&self, cx: &mut RenderContext) -> Option { - let room = ActiveCall::global(cx).read(cx).room()?; - let theme = &cx.global::().theme.contacts_popover; - - Some( - Flex::column() - .with_children(room.read(cx).pending_users().iter().map(|user| { - Flex::row() - .with_children(user.avatar.clone().map(|avatar| { - Image::new(avatar) - .with_style(theme.contact_avatar) - .aligned() - .left() - .boxed() - })) - .with_child( - Label::new( - user.github_login.clone(), - theme.contact_username.text.clone(), - ) - .contained() - .with_style(theme.contact_username.container) - .aligned() - .left() - .flex(1., true) - .boxed(), - ) - .constrained() - .with_height(theme.row_height) - .contained() - .with_style(theme.contact_row.default) - .boxed() - })) + fn render_call_participant( + user: &User, + is_pending: bool, + theme: &theme::ContactsPopover, + ) -> ElementBox { + Flex::row() + .with_children(user.avatar.clone().map(|avatar| { + Image::new(avatar) + .with_style(theme.contact_avatar) + .aligned() + .left() + .boxed() + })) + .with_child( + Label::new( + user.github_login.clone(), + theme.contact_username.text.clone(), + ) + .contained() + .with_style(theme.contact_username.container) .boxed(), - ) + ) + .with_children(if is_pending { + Some( + Label::new( + "Calling...".to_string(), + theme.calling_indicator.text.clone(), + ) + .contained() + .with_style(theme.calling_indicator.container) + .aligned() + .flex_float() + .boxed(), + ) + } else { + None + }) + .constrained() + .with_height(theme.row_height) + .boxed() } fn render_header( @@ -464,6 +545,7 @@ impl ContactsPopover { let header_style = theme.header_row.style_for(Default::default(), is_selected); let text = match section { + Section::ActiveCall => "Call", Section::Requests => "Requests", Section::Online => "Online", Section::Offline => "Offline", @@ -751,7 +833,6 @@ impl View for ContactsPopover { .with_height(theme.contacts_popover.user_query_editor_height) .boxed(), ) - .with_children(self.render_active_call(cx)) .with_child(List::new(self.list_state.clone()).flex(1., false).boxed()) .with_children( self.user_store diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 5f62e3585e..67f3afb461 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -154,7 +154,7 @@ message LeaveRoom { message Room { repeated Participant participants = 1; - repeated uint64 pending_user_ids = 2; + repeated uint64 pending_participant_user_ids = 2; } message Participant { diff --git a/crates/rpc/src/peer.rs b/crates/rpc/src/peer.rs index 834acd0afa..5b1ed6c2af 100644 --- a/crates/rpc/src/peer.rs +++ b/crates/rpc/src/peer.rs @@ -33,7 +33,7 @@ impl fmt::Display for ConnectionId { } } -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub struct PeerId(pub u32); impl fmt::Display for PeerId { diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index d70aaed189..d66f30fe95 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -105,6 +105,7 @@ pub struct ContactsPopover { pub private_button: Interactive, pub section_icon_size: f32, pub invite_row: Interactive, + pub calling_indicator: ContainedText, } #[derive(Clone, Deserialize, Default)] diff --git a/styles/src/styleTree/contactsPopover.ts b/styles/src/styleTree/contactsPopover.ts index 7dfdb404d6..3e39746bdf 100644 --- a/styles/src/styleTree/contactsPopover.ts +++ b/styles/src/styleTree/contactsPopover.ts @@ -171,5 +171,8 @@ export default function contactsPopover(theme: Theme) { text: text(theme, "sans", "active", { size: "sm" }), }, }, + callingIndicator: { + ...text(theme, "mono", "primary", { size: "xs" }), + } } } From 188b775fa6a21ec3c91f6e524b1e0990f74b3b56 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 7 Oct 2022 10:03:09 -0700 Subject: [PATCH 189/314] Fixed non-block terminal cursors being displayed incorrectly --- crates/terminal/src/terminal_element.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/terminal/src/terminal_element.rs b/crates/terminal/src/terminal_element.rs index e7fd69fe49..0f037863af 100644 --- a/crates/terminal/src/terminal_element.rs +++ b/crates/terminal/src/terminal_element.rs @@ -680,12 +680,12 @@ impl Element for TerminalElement { let focused = self.focused; TerminalElement::shape_cursor(cursor_point, dimensions, &cursor_text).map( move |(cursor_position, block_width)| { - let shape = match cursor.shape { - AlacCursorShape::Block if !focused => CursorShape::Hollow, - AlacCursorShape::Block => CursorShape::Block, - AlacCursorShape::Underline => CursorShape::Underscore, - AlacCursorShape::Beam => CursorShape::Bar, - AlacCursorShape::HollowBlock => CursorShape::Hollow, + let (shape, text) = match cursor.shape { + AlacCursorShape::Block if !focused => (CursorShape::Hollow, None), + AlacCursorShape::Block => (CursorShape::Block, Some(cursor_text)), + AlacCursorShape::Underline => (CursorShape::Underscore, None), + AlacCursorShape::Beam => (CursorShape::Bar, None), + AlacCursorShape::HollowBlock => (CursorShape::Hollow, None), //This case is handled in the if wrapping the whole cursor layout AlacCursorShape::Hidden => unreachable!(), }; @@ -696,7 +696,7 @@ impl Element for TerminalElement { dimensions.line_height, terminal_theme.colors.cursor, shape, - Some(cursor_text), + text, ) }, ) From bf50a8ad8e6d0e01c713e32908036c3404b46ba4 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 7 Oct 2022 11:37:39 -0700 Subject: [PATCH 190/314] Implemented a simplistic version of correct cmd-k behavior --- crates/terminal/src/terminal.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 473bbd4f52..b86043b122 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -618,8 +618,11 @@ impl Terminal { term.resize(new_size); } InternalEvent::Clear => { - self.write_to_pty("\x0c".to_string()); term.clear_screen(ClearMode::Saved); + + term.clear_screen(ClearMode::All); + + term.grid_mut().cursor.point = Point::new(Line(0), Column(0)); } InternalEvent::Scroll(scroll) => { term.scroll_display(*scroll); From 15595a67faafd1ca68bd01631d9164c1fc6fba5e Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 7 Oct 2022 12:04:26 -0700 Subject: [PATCH 191/314] Added a horrible hacky way of doing cmd-k correctly. --- crates/terminal/src/terminal.rs | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index b86043b122..004815a510 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -618,11 +618,34 @@ impl Terminal { term.resize(new_size); } InternalEvent::Clear => { + // Clear back buffer term.clear_screen(ClearMode::Saved); - term.clear_screen(ClearMode::All); + let cursor = term.grid().cursor.point; - term.grid_mut().cursor.point = Point::new(Line(0), Column(0)); + // Clear the lines above + term.grid_mut().reset_region(..cursor.line); + + // Copy the current line up + let line = term.grid()[cursor.line][..cursor.column] + .iter() + .cloned() + .enumerate() + .collect::>(); + + for (i, cell) in line { + term.grid_mut()[Line(0)][Column(i)] = cell; + } + + // Reset the cursor + term.grid_mut().cursor.point = + Point::new(Line(0), term.grid_mut().cursor.point.column); + let new_cursor = term.grid().cursor.point; + + // Clear the lines below the new cursor + if (new_cursor.line.0 as usize) < term.screen_lines() - 1 { + term.grid_mut().reset_region((new_cursor.line + 1)..); + } } InternalEvent::Scroll(scroll) => { term.scroll_display(*scroll); From e15f27106d713a3a519cc44b02e0c41eccd0a31a Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 7 Oct 2022 12:20:54 -0400 Subject: [PATCH 192/314] Reset buffer git diff when setting diff base to None Co-Authored-By: Joseph Lyons --- crates/git/src/diff.rs | 8 ++++++++ crates/language/src/buffer.rs | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/crates/git/src/diff.rs b/crates/git/src/diff.rs index abf874e2bb..4191e5d260 100644 --- a/crates/git/src/diff.rs +++ b/crates/git/src/diff.rs @@ -104,6 +104,11 @@ impl BufferDiff { }) } + pub fn clear(&mut self, buffer: &text::BufferSnapshot) { + self.last_buffer_version = Some(buffer.version().clone()); + self.tree = SumTree::new(); + } + pub fn needs_update(&self, buffer: &text::BufferSnapshot) -> bool { match &self.last_buffer_version { Some(last) => buffer.version().changed_since(last), @@ -296,6 +301,9 @@ mod tests { &diff_base, &[(0..1, "", "point five\n"), (2..3, "two\n", "HELLO\n")], ); + + diff.clear(&buffer); + assert_hunks(diff.hunks(&buffer), &buffer, &diff_base, &[]); } #[test] diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index db9aa029f2..a3c0c54d01 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -707,6 +707,11 @@ impl Buffer { } }) .detach() + } else { + let snapshot = self.snapshot(); + self.git_diff_status.diff.clear(&snapshot); + self.git_diff_update_count += 1; + cx.notify(); } } From 070c4bc503b752b2632f34f8313bf2e71439eb64 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 7 Oct 2022 12:44:20 -0700 Subject: [PATCH 193/314] Add color for 'variable.special' and use it in highlight queries --- crates/zed/src/languages/cpp/highlights.scm | 2 +- crates/zed/src/languages/css/highlights.scm | 11 +++++++---- crates/zed/src/languages/javascript/highlights.scm | 4 ++-- crates/zed/src/languages/rust/highlights.scm | 2 +- crates/zed/src/languages/typescript/highlights.scm | 4 ++-- styles/src/themes/common/base16.ts | 4 ++++ 6 files changed, 17 insertions(+), 10 deletions(-) diff --git a/crates/zed/src/languages/cpp/highlights.scm b/crates/zed/src/languages/cpp/highlights.scm index 2dd9188308..b832fb4e2d 100644 --- a/crates/zed/src/languages/cpp/highlights.scm +++ b/crates/zed/src/languages/cpp/highlights.scm @@ -41,7 +41,7 @@ (field_identifier) @property (statement_identifier) @label -(this) @variable.builtin +(this) @variable.special [ "break" diff --git a/crates/zed/src/languages/css/highlights.scm b/crates/zed/src/languages/css/highlights.scm index 3638837af7..aba156633a 100644 --- a/crates/zed/src/languages/css/highlights.scm +++ b/crates/zed/src/languages/css/highlights.scm @@ -41,10 +41,13 @@ (function_name) @function -((property_name) @variable - (#match? @variable "^--")) -((plain_value) @variable - (#match? @variable "^--")) +( + [ + (property_name) + (plain_value) + ] @variable.special + (#match? @variable.special "^--") +) [ "@media" diff --git a/crates/zed/src/languages/javascript/highlights.scm b/crates/zed/src/languages/javascript/highlights.scm index d3921cdbc8..773780a8e0 100644 --- a/crates/zed/src/languages/javascript/highlights.scm +++ b/crates/zed/src/languages/javascript/highlights.scm @@ -55,8 +55,8 @@ ; Literals -(this) @variable.builtin -(super) @variable.builtin +(this) @variable.special +(super) @variable.special [ (true) diff --git a/crates/zed/src/languages/rust/highlights.scm b/crates/zed/src/languages/rust/highlights.scm index 72482b4073..f4a451529e 100644 --- a/crates/zed/src/languages/rust/highlights.scm +++ b/crates/zed/src/languages/rust/highlights.scm @@ -1,6 +1,6 @@ (type_identifier) @type (primitive_type) @type.builtin -(self) @variable.builtin +(self) @variable.special (field_identifier) @property (call_expression diff --git a/crates/zed/src/languages/typescript/highlights.scm b/crates/zed/src/languages/typescript/highlights.scm index d3921cdbc8..773780a8e0 100644 --- a/crates/zed/src/languages/typescript/highlights.scm +++ b/crates/zed/src/languages/typescript/highlights.scm @@ -55,8 +55,8 @@ ; Literals -(this) @variable.builtin -(super) @variable.builtin +(this) @variable.special +(super) @variable.special [ (true) diff --git a/styles/src/themes/common/base16.ts b/styles/src/themes/common/base16.ts index 326928252e..1811167719 100644 --- a/styles/src/themes/common/base16.ts +++ b/styles/src/themes/common/base16.ts @@ -185,6 +185,10 @@ export function createTheme( color: sample(ramps.neutral, 7), weight: fontWeights.normal, }, + "variable.special": { + color: sample(ramps.blue, 0.80), + weight: fontWeights.normal, + }, comment: { color: sample(ramps.neutral, 5), weight: fontWeights.normal, From fcf13b44fb0be7e41323fadeb8e9626ce0c26767 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 7 Oct 2022 12:44:39 -0700 Subject: [PATCH 194/314] CSS: color '#' the same as the rest of the color --- crates/zed/src/languages/css/highlights.scm | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/zed/src/languages/css/highlights.scm b/crates/zed/src/languages/css/highlights.scm index aba156633a..e271d8583c 100644 --- a/crates/zed/src/languages/css/highlights.scm +++ b/crates/zed/src/languages/css/highlights.scm @@ -73,7 +73,6 @@ (unit) @type [ - "#" "," ":" ] @punctuation.delimiter From 95cb9ceac9d7e90f0aa2138edff711c0ced17c45 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 7 Oct 2022 12:44:55 -0700 Subject: [PATCH 195/314] Collapse variant and type into the same color --- crates/zed/src/languages/rust/highlights.scm | 13 ++----------- styles/src/themes/common/base16.ts | 8 ++------ styles/src/themes/common/theme.ts | 2 +- 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/crates/zed/src/languages/rust/highlights.scm b/crates/zed/src/languages/rust/highlights.scm index f4a451529e..d717c5d459 100644 --- a/crates/zed/src/languages/rust/highlights.scm +++ b/crates/zed/src/languages/rust/highlights.scm @@ -27,17 +27,8 @@ ; Identifier conventions -; Assume uppercase names are enum constructors -((identifier) @variant - (#match? @variant "^[A-Z]")) - -; Assume that uppercase names in paths are types -((scoped_identifier - path: (identifier) @type) - (#match? @type "^[A-Z]")) -((scoped_identifier - path: (scoped_identifier - name: (identifier) @type)) +; Assume uppercase names are types/enum-constructors +((identifier) @type (#match? @type "^[A-Z]")) ; Assume all-caps names are constants diff --git a/styles/src/themes/common/base16.ts b/styles/src/themes/common/base16.ts index 1811167719..cd6d46a771 100644 --- a/styles/src/themes/common/base16.ts +++ b/styles/src/themes/common/base16.ts @@ -214,15 +214,11 @@ export function createTheme( weight: fontWeights.normal, }, constructor: { - color: sample(ramps.blue, 0.5), - weight: fontWeights.normal, - }, - variant: { - color: sample(ramps.blue, 0.5), + color: sample(ramps.cyan, 0.5), weight: fontWeights.normal, }, property: { - color: sample(ramps.blue, 0.5), + color: sample(ramps.blue, 0.6), weight: fontWeights.normal, }, enum: { diff --git a/styles/src/themes/common/theme.ts b/styles/src/themes/common/theme.ts index b93148ae2c..a787443f31 100644 --- a/styles/src/themes/common/theme.ts +++ b/styles/src/themes/common/theme.ts @@ -43,7 +43,7 @@ export interface Syntax { keyword: SyntaxHighlightStyle; function: SyntaxHighlightStyle; type: SyntaxHighlightStyle; - variant: SyntaxHighlightStyle; + constructor: SyntaxHighlightStyle; property: SyntaxHighlightStyle; enum: SyntaxHighlightStyle; operator: SyntaxHighlightStyle; From 6ecf870c665514ba7c812dad52d6cd629f1c5c84 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 7 Oct 2022 12:46:49 -0700 Subject: [PATCH 196/314] Tweak SCREAMING_SNAKE_CASE regexes in highlight queries --- crates/zed/src/languages/c/highlights.scm | 2 +- crates/zed/src/languages/cpp/highlights.scm | 2 +- crates/zed/src/languages/javascript/highlights.scm | 2 +- crates/zed/src/languages/python/highlights.scm | 2 +- crates/zed/src/languages/rust/highlights.scm | 2 +- crates/zed/src/languages/typescript/highlights.scm | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/zed/src/languages/c/highlights.scm b/crates/zed/src/languages/c/highlights.scm index 007c871ffa..064ec61a37 100644 --- a/crates/zed/src/languages/c/highlights.scm +++ b/crates/zed/src/languages/c/highlights.scm @@ -86,7 +86,7 @@ (identifier) @variable ((identifier) @constant - (#match? @constant "^[A-Z][A-Z\\d_]*$")) + (#match? @constant "^_*[A-Z][A-Z\\d_]*$")) (call_expression function: (identifier) @function) diff --git a/crates/zed/src/languages/cpp/highlights.scm b/crates/zed/src/languages/cpp/highlights.scm index b832fb4e2d..bcfa01ca5c 100644 --- a/crates/zed/src/languages/cpp/highlights.scm +++ b/crates/zed/src/languages/cpp/highlights.scm @@ -37,7 +37,7 @@ (type_identifier) @type ((identifier) @constant - (#match? @constant "^[A-Z][A-Z\\d_]*$")) + (#match? @constant "^_*[A-Z][A-Z\\d_]*$")) (field_identifier) @property (statement_identifier) @label diff --git a/crates/zed/src/languages/javascript/highlights.scm b/crates/zed/src/languages/javascript/highlights.scm index 773780a8e0..bd1986b6b3 100644 --- a/crates/zed/src/languages/javascript/highlights.scm +++ b/crates/zed/src/languages/javascript/highlights.scm @@ -51,7 +51,7 @@ (shorthand_property_identifier) (shorthand_property_identifier_pattern) ] @constant - (#match? @constant "^[A-Z_][A-Z\\d_]+$")) + (#match? @constant "^_*[A-Z_][A-Z\\d_]*$")) ; Literals diff --git a/crates/zed/src/languages/python/highlights.scm b/crates/zed/src/languages/python/highlights.scm index 118af92aaa..71ab963d82 100644 --- a/crates/zed/src/languages/python/highlights.scm +++ b/crates/zed/src/languages/python/highlights.scm @@ -21,7 +21,7 @@ (#match? @type "^[A-Z]")) ((identifier) @constant - (#match? @constant "^[A-Z][A-Z_]*$")) + (#match? @constant "^_*[A-Z][A-Z\\d_]*$")) ; Builtin functions diff --git a/crates/zed/src/languages/rust/highlights.scm b/crates/zed/src/languages/rust/highlights.scm index d717c5d459..98ea1ee40e 100644 --- a/crates/zed/src/languages/rust/highlights.scm +++ b/crates/zed/src/languages/rust/highlights.scm @@ -33,7 +33,7 @@ ; Assume all-caps names are constants ((identifier) @constant - (#match? @constant "^[A-Z][A-Z\\d_]+$")) + (#match? @constant "^_*[A-Z][A-Z\\d_]*$")) [ "(" diff --git a/crates/zed/src/languages/typescript/highlights.scm b/crates/zed/src/languages/typescript/highlights.scm index 773780a8e0..bd1986b6b3 100644 --- a/crates/zed/src/languages/typescript/highlights.scm +++ b/crates/zed/src/languages/typescript/highlights.scm @@ -51,7 +51,7 @@ (shorthand_property_identifier) (shorthand_property_identifier_pattern) ] @constant - (#match? @constant "^[A-Z_][A-Z\\d_]+$")) + (#match? @constant "^_*[A-Z_][A-Z\\d_]*$")) ; Literals From e96abf1429174b80807baa85de97b68f59782288 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 7 Oct 2022 14:51:01 -0700 Subject: [PATCH 197/314] 0.59.0 --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 246dfbbbef..da2362670d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7236,7 +7236,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.58.0" +version = "0.59.0" dependencies = [ "activity_indicator", "anyhow", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index cdf0e36eba..9a65fd0816 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] description = "The fast, collaborative code editor." edition = "2021" name = "zed" -version = "0.58.0" +version = "0.59.0" [lib] name = "zed" From d14744d02f030017b70eff2afb082fb276242ca8 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sat, 8 Oct 2022 14:38:17 +0200 Subject: [PATCH 198/314] Show current user in active call --- crates/collab_ui/src/contacts_popover.rs | 138 +++++++++++++++-------- crates/theme/src/theme.rs | 19 ---- styles/src/styleTree/contactsPopover.ts | 52 +-------- 3 files changed, 89 insertions(+), 120 deletions(-) diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index 090720c7a6..672201590a 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -165,7 +165,12 @@ impl ContactsPopover { ) } ContactEntry::CallParticipant { user, is_pending } => { - Self::render_call_participant(user, *is_pending, &theme.contacts_popover) + Self::render_call_participant( + user, + *is_pending, + is_selected, + &theme.contacts_popover, + ) } ContactEntry::IncomingRequest(user) => Self::render_contact_request( user.clone(), @@ -303,21 +308,16 @@ impl ContactsPopover { if let Some(room) = ActiveCall::global(cx).read(cx).room() { let room = room.read(cx); + let mut call_participants = Vec::new(); - self.entries.push(ContactEntry::Header(Section::ActiveCall)); - if !self.collapsed_sections.contains(&Section::ActiveCall) { - // Populate remote participants. + // Populate the active user. + if let Some(user) = user_store.current_user() { self.match_candidates.clear(); - self.match_candidates - .extend( - room.remote_participants() - .iter() - .map(|(peer_id, participant)| StringMatchCandidate { - id: peer_id.0 as usize, - string: participant.user.github_login.clone(), - char_bag: participant.user.github_login.chars().collect(), - }), - ); + self.match_candidates.push(StringMatchCandidate { + id: 0, + string: user.github_login.clone(), + char_bag: user.github_login.chars().collect(), + }); let matches = executor.block(match_strings( &self.match_candidates, &query, @@ -326,38 +326,74 @@ impl ContactsPopover { &Default::default(), executor.clone(), )); - self.entries.extend(matches.iter().map(|mat| { - ContactEntry::CallParticipant { - user: room.remote_participants()[&PeerId(mat.candidate_id as u32)] - .user - .clone(), + if !matches.is_empty() { + call_participants.push(ContactEntry::CallParticipant { + user, is_pending: false, - } - })); + }); + } + } - // Populate pending participants. - self.match_candidates.clear(); - self.match_candidates - .extend(room.pending_participants().iter().enumerate().map( - |(id, participant)| StringMatchCandidate { + // Populate remote participants. + self.match_candidates.clear(); + self.match_candidates + .extend( + room.remote_participants() + .iter() + .map(|(peer_id, participant)| StringMatchCandidate { + id: peer_id.0 as usize, + string: participant.user.github_login.clone(), + char_bag: participant.user.github_login.chars().collect(), + }), + ); + let matches = executor.block(match_strings( + &self.match_candidates, + &query, + true, + usize::MAX, + &Default::default(), + executor.clone(), + )); + call_participants.extend(matches.iter().map(|mat| { + ContactEntry::CallParticipant { + user: room.remote_participants()[&PeerId(mat.candidate_id as u32)] + .user + .clone(), + is_pending: false, + } + })); + + // Populate pending participants. + self.match_candidates.clear(); + self.match_candidates + .extend( + room.pending_participants() + .iter() + .enumerate() + .map(|(id, participant)| StringMatchCandidate { id, string: participant.github_login.clone(), char_bag: participant.github_login.chars().collect(), - }, - )); - let matches = executor.block(match_strings( - &self.match_candidates, - &query, - true, - usize::MAX, - &Default::default(), - executor.clone(), - )); - self.entries - .extend(matches.iter().map(|mat| ContactEntry::CallParticipant { - user: room.pending_participants()[mat.candidate_id].clone(), - is_pending: true, - })); + }), + ); + let matches = executor.block(match_strings( + &self.match_candidates, + &query, + true, + usize::MAX, + &Default::default(), + executor.clone(), + )); + call_participants.extend(matches.iter().map(|mat| ContactEntry::CallParticipant { + user: room.pending_participants()[mat.candidate_id].clone(), + is_pending: true, + })); + + if !call_participants.is_empty() { + self.entries.push(ContactEntry::Header(Section::ActiveCall)); + if !self.collapsed_sections.contains(&Section::ActiveCall) { + self.entries.extend(call_participants); + } } } @@ -495,6 +531,7 @@ impl ContactsPopover { fn render_call_participant( user: &User, is_pending: bool, + is_selected: bool, theme: &theme::ContactsPopover, ) -> ElementBox { Flex::row() @@ -512,25 +549,26 @@ impl ContactsPopover { ) .contained() .with_style(theme.contact_username.container) + .aligned() + .left() + .flex(1., true) .boxed(), ) .with_children(if is_pending { Some( - Label::new( - "Calling...".to_string(), - theme.calling_indicator.text.clone(), - ) - .contained() - .with_style(theme.calling_indicator.container) - .aligned() - .flex_float() - .boxed(), + Label::new("Calling".to_string(), theme.calling_indicator.text.clone()) + .contained() + .with_style(theme.calling_indicator.container) + .aligned() + .boxed(), ) } else { None }) .constrained() .with_height(theme.row_height) + .contained() + .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) .boxed() } diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index d66f30fe95..96d5b07582 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -92,7 +92,6 @@ pub struct ContactsPopover { pub add_contact_button: IconButton, pub header_row: Interactive, pub contact_row: Interactive, - pub project_row: Interactive, pub row_height: f32, pub contact_avatar: ImageStyle, pub contact_status_free: ContainerStyle, @@ -101,8 +100,6 @@ pub struct ContactsPopover { pub contact_button: Interactive, pub contact_button_spacing: f32, pub disabled_button: IconButton, - pub tree_branch: Interactive, - pub private_button: Interactive, pub section_icon_size: f32, pub invite_row: Interactive, pub calling_indicator: ContainedText, @@ -356,12 +353,6 @@ pub struct InviteLink { pub icon: Icon, } -#[derive(Deserialize, Default, Clone, Copy)] -pub struct TreeBranch { - pub width: f32, - pub color: Color, -} - #[derive(Deserialize, Default)] pub struct ContactFinder { pub row_height: f32, @@ -389,16 +380,6 @@ pub struct IconButton { pub button_width: f32, } -#[derive(Deserialize, Default)] -pub struct ProjectRow { - #[serde(flatten)] - pub container: ContainerStyle, - pub name: ContainedText, - pub guests: ContainerStyle, - pub guest_avatar: ImageStyle, - pub guest_avatar_spacing: f32, -} - #[derive(Deserialize, Default)] pub struct ChatMessage { #[serde(flatten)] diff --git a/styles/src/styleTree/contactsPopover.ts b/styles/src/styleTree/contactsPopover.ts index 3e39746bdf..8786e81f5c 100644 --- a/styles/src/styleTree/contactsPopover.ts +++ b/styles/src/styleTree/contactsPopover.ts @@ -5,32 +5,6 @@ export default function contactsPopover(theme: Theme) { const nameMargin = 8; const sidePadding = 12; - const projectRow = { - guestAvatarSpacing: 4, - height: 24, - guestAvatar: { - cornerRadius: 8, - width: 14, - }, - name: { - ...text(theme, "mono", "placeholder", { size: "sm" }), - margin: { - left: nameMargin, - right: 6, - }, - }, - guests: { - margin: { - left: nameMargin, - right: nameMargin, - }, - }, - padding: { - left: sidePadding, - right: sidePadding, - }, - }; - const contactButton = { background: backgroundColor(theme, 100), color: iconColor(theme, "primary"), @@ -102,16 +76,6 @@ export default function contactsPopover(theme: Theme) { background: backgroundColor(theme, 100, "active"), }, }, - treeBranch: { - color: borderColor(theme, "active"), - width: 1, - hover: { - color: borderColor(theme, "active"), - }, - active: { - color: borderColor(theme, "active"), - }, - }, contactAvatar: { cornerRadius: 10, width: 18, @@ -146,20 +110,6 @@ export default function contactsPopover(theme: Theme) { background: backgroundColor(theme, 100), color: iconColor(theme, "muted"), }, - projectRow: { - ...projectRow, - background: backgroundColor(theme, 300), - name: { - ...projectRow.name, - ...text(theme, "mono", "secondary", { size: "sm" }), - }, - hover: { - background: backgroundColor(theme, 300, "hovered"), - }, - active: { - background: backgroundColor(theme, 300, "active"), - }, - }, inviteRow: { padding: { left: sidePadding, @@ -172,7 +122,7 @@ export default function contactsPopover(theme: Theme) { }, }, callingIndicator: { - ...text(theme, "mono", "primary", { size: "xs" }), + ...text(theme, "mono", "muted", { size: "xs" }) } } } From 59aaf4ce1b69d80734af06e572235e165941c8e6 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sat, 8 Oct 2022 14:43:41 +0200 Subject: [PATCH 199/314] Call contact on enter --- crates/collab_ui/src/contacts_popover.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index 672201590a..700c717962 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -282,6 +282,17 @@ impl ContactsPopover { let section = *section; self.toggle_expanded(&ToggleExpanded(section), cx); } + ContactEntry::Contact(contact) => { + if contact.online && !contact.busy { + self.call( + &Call { + recipient_user_id: contact.user.id, + initial_project: Some(self.project.clone()), + }, + cx, + ); + } + } _ => {} } } @@ -636,6 +647,7 @@ impl ContactsPopover { cx: &mut RenderContext, ) -> ElementBox { let online = contact.online; + let busy = contact.busy; let user_id = contact.user.id; let initial_project = project.clone(); let mut element = @@ -688,7 +700,7 @@ impl ContactsPopover { .boxed() }) .on_click(MouseButton::Left, move |_, cx| { - if online { + if online && !busy { cx.dispatch_action(Call { recipient_user_id: user_id, initial_project: Some(initial_project.clone()), From 34cb742db1cbdd24fdb6ac3e67a294b51e42d4a3 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Sat, 8 Oct 2022 14:47:40 +0200 Subject: [PATCH 200/314] Set current location after calling another user --- crates/collab_ui/src/contacts_popover.rs | 26 +++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index 700c717962..abc8db658b 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -825,11 +825,27 @@ impl ContactsPopover { } fn call(&mut self, action: &Call, cx: &mut ViewContext) { - ActiveCall::global(cx) - .update(cx, |active_call, cx| { - active_call.invite(action.recipient_user_id, action.initial_project.clone(), cx) - }) - .detach_and_log_err(cx); + let recipient_user_id = action.recipient_user_id; + let initial_project = action.initial_project.clone(); + let window_id = cx.window_id(); + + let active_call = ActiveCall::global(cx); + cx.spawn_weak(|_, mut cx| async move { + active_call + .update(&mut cx, |active_call, cx| { + active_call.invite(recipient_user_id, initial_project.clone(), cx) + }) + .await?; + if cx.update(|cx| cx.window_is_active(window_id)) { + active_call + .update(&mut cx, |call, cx| { + call.set_location(initial_project.as_ref(), cx) + }) + .await?; + } + anyhow::Ok(()) + }) + .detach_and_log_err(cx); } } From b82db3a254ad0661892af5dc73b455212f1a5c78 Mon Sep 17 00:00:00 2001 From: K Simmons Date: Wed, 5 Oct 2022 20:19:30 -0700 Subject: [PATCH 201/314] Adds word and sentence text objects along with a new vim testing system which uses cached neovim data to verify our test accuracy --- Cargo.lock | 1197 ++++++++++------- assets/keymaps/vim.json | 32 +- crates/editor/src/display_map.rs | 85 +- crates/editor/src/editor.rs | 4 +- crates/editor/src/element.rs | 2 +- crates/editor/src/movement.rs | 157 ++- crates/vim/Cargo.toml | 17 +- crates/vim/src/insert.rs | 2 +- crates/vim/src/motion.rs | 34 +- crates/vim/src/normal.rs | 46 +- crates/vim/src/normal/change.rs | 27 +- crates/vim/src/normal/delete.rs | 69 +- crates/vim/src/normal/yank.rs | 27 +- crates/vim/src/object.rs | 488 +++++++ crates/vim/src/state.rs | 14 +- crates/vim/src/test_contexts.rs | 9 + .../neovim_backed_binding_test_context.rs | 56 + .../neovim_backed_test_context.rs | 374 +++++ .../test_contexts/vim_binding_test_context.rs | 69 + .../{ => test_contexts}/vim_test_context.rs | 66 +- crates/vim/src/vim.rs | 25 +- crates/vim/src/visual.rs | 6 +- .../test_change_around_sentence/0text.txt | 1 + .../test_change_around_sentence/10head.txt | 1 + .../test_change_around_sentence/1head.txt | 1 + .../test_change_around_sentence/2mode.txt | 1 + .../test_change_around_sentence/3text.txt | 1 + .../test_change_around_sentence/4head.txt | 1 + .../test_change_around_sentence/5mode.txt | 1 + .../test_change_around_sentence/6text.txt | 1 + .../test_change_around_sentence/7head.txt | 1 + .../test_change_around_sentence/8mode.txt | 1 + .../test_change_around_sentence/9text.txt | 1 + .../test_change_around_word/0text.txt | 12 + .../test_change_around_word/100head.txt | 1 + .../test_change_around_word/101mode.txt | 1 + .../test_change_around_word/102text.txt | 12 + .../test_change_around_word/103head.txt | 1 + .../test_change_around_word/104mode.txt | 1 + .../test_change_around_word/105text.txt | 12 + .../test_change_around_word/106head.txt | 1 + .../test_change_around_word/107mode.txt | 1 + .../test_change_around_word/108text.txt | 12 + .../test_change_around_word/109head.txt | 1 + .../test_change_around_word/10head.txt | 1 + .../test_change_around_word/110mode.txt | 1 + .../test_change_around_word/111text.txt | 12 + .../test_change_around_word/112head.txt | 1 + .../test_change_around_word/113mode.txt | 1 + .../test_change_around_word/114text.txt | 12 + .../test_change_around_word/115head.txt | 1 + .../test_change_around_word/116mode.txt | 1 + .../test_change_around_word/117text.txt | 9 + .../test_change_around_word/118head.txt | 1 + .../test_change_around_word/119mode.txt | 1 + .../test_change_around_word/11mode.txt | 1 + .../test_change_around_word/120text.txt | 10 + .../test_change_around_word/121head.txt | 1 + .../test_change_around_word/122mode.txt | 1 + .../test_change_around_word/123text.txt | 11 + .../test_change_around_word/124head.txt | 1 + .../test_change_around_word/125mode.txt | 1 + .../test_change_around_word/126text.txt | 12 + .../test_change_around_word/127head.txt | 1 + .../test_change_around_word/128mode.txt | 1 + .../test_change_around_word/129text.txt | 12 + .../test_change_around_word/12text.txt | 12 + .../test_change_around_word/130head.txt | 1 + .../test_change_around_word/131mode.txt | 1 + .../test_change_around_word/132text.txt | 11 + .../test_change_around_word/133head.txt | 1 + .../test_change_around_word/134mode.txt | 1 + .../test_change_around_word/135text.txt | 11 + .../test_change_around_word/136head.txt | 1 + .../test_change_around_word/137mode.txt | 1 + .../test_change_around_word/13head.txt | 1 + .../test_change_around_word/14mode.txt | 1 + .../test_change_around_word/15text.txt | 12 + .../test_change_around_word/16head.txt | 1 + .../test_change_around_word/17mode.txt | 1 + .../test_change_around_word/18text.txt | 11 + .../test_change_around_word/19head.txt | 1 + .../test_change_around_word/1head.txt | 1 + .../test_change_around_word/20mode.txt | 1 + .../test_change_around_word/21text.txt | 11 + .../test_change_around_word/22head.txt | 1 + .../test_change_around_word/23mode.txt | 1 + .../test_change_around_word/24text.txt | 11 + .../test_change_around_word/25head.txt | 1 + .../test_change_around_word/26mode.txt | 1 + .../test_change_around_word/27text.txt | 11 + .../test_change_around_word/28head.txt | 1 + .../test_change_around_word/29mode.txt | 1 + .../test_change_around_word/2mode.txt | 1 + .../test_change_around_word/30text.txt | 12 + .../test_change_around_word/31head.txt | 1 + .../test_change_around_word/32mode.txt | 1 + .../test_change_around_word/33text.txt | 12 + .../test_change_around_word/34head.txt | 1 + .../test_change_around_word/35mode.txt | 1 + .../test_change_around_word/36text.txt | 12 + .../test_change_around_word/37head.txt | 1 + .../test_change_around_word/38mode.txt | 1 + .../test_change_around_word/39text.txt | 12 + .../test_change_around_word/3text.txt | 12 + .../test_change_around_word/40head.txt | 1 + .../test_change_around_word/41mode.txt | 1 + .../test_change_around_word/42text.txt | 12 + .../test_change_around_word/43head.txt | 1 + .../test_change_around_word/44mode.txt | 1 + .../test_change_around_word/45text.txt | 12 + .../test_change_around_word/46head.txt | 1 + .../test_change_around_word/47mode.txt | 1 + .../test_change_around_word/48text.txt | 9 + .../test_change_around_word/49head.txt | 1 + .../test_change_around_word/4head.txt | 1 + .../test_change_around_word/50mode.txt | 1 + .../test_change_around_word/51text.txt | 10 + .../test_change_around_word/52head.txt | 1 + .../test_change_around_word/53mode.txt | 1 + .../test_change_around_word/54text.txt | 11 + .../test_change_around_word/55head.txt | 1 + .../test_change_around_word/56mode.txt | 1 + .../test_change_around_word/57text.txt | 12 + .../test_change_around_word/58head.txt | 1 + .../test_change_around_word/59mode.txt | 1 + .../test_change_around_word/5mode.txt | 1 + .../test_change_around_word/60text.txt | 12 + .../test_change_around_word/61head.txt | 1 + .../test_change_around_word/62mode.txt | 1 + .../test_change_around_word/63text.txt | 11 + .../test_change_around_word/64head.txt | 1 + .../test_change_around_word/65mode.txt | 1 + .../test_change_around_word/66text.txt | 11 + .../test_change_around_word/67head.txt | 1 + .../test_change_around_word/68mode.txt | 1 + .../test_change_around_word/69text.txt | 12 + .../test_change_around_word/6text.txt | 11 + .../test_change_around_word/70head.txt | 1 + .../test_change_around_word/71mode.txt | 1 + .../test_change_around_word/72text.txt | 12 + .../test_change_around_word/73head.txt | 1 + .../test_change_around_word/74mode.txt | 1 + .../test_change_around_word/75text.txt | 11 + .../test_change_around_word/76head.txt | 1 + .../test_change_around_word/77mode.txt | 1 + .../test_change_around_word/78text.txt | 12 + .../test_change_around_word/79head.txt | 1 + .../test_change_around_word/7head.txt | 1 + .../test_change_around_word/80mode.txt | 1 + .../test_change_around_word/81text.txt | 12 + .../test_change_around_word/82head.txt | 1 + .../test_change_around_word/83mode.txt | 1 + .../test_change_around_word/84text.txt | 12 + .../test_change_around_word/85head.txt | 1 + .../test_change_around_word/86mode.txt | 1 + .../test_change_around_word/87text.txt | 11 + .../test_change_around_word/88head.txt | 1 + .../test_change_around_word/89mode.txt | 1 + .../test_change_around_word/8mode.txt | 1 + .../test_change_around_word/90text.txt | 11 + .../test_change_around_word/91head.txt | 1 + .../test_change_around_word/92mode.txt | 1 + .../test_change_around_word/93text.txt | 11 + .../test_change_around_word/94head.txt | 1 + .../test_change_around_word/95mode.txt | 1 + .../test_change_around_word/96text.txt | 11 + .../test_change_around_word/97head.txt | 1 + .../test_change_around_word/98mode.txt | 1 + .../test_change_around_word/99text.txt | 12 + .../test_change_around_word/9text.txt | 12 + .../test_change_in_sentence/0text.txt | 1 + .../test_change_in_sentence/10head.txt | 1 + .../test_change_in_sentence/11mode.txt | 1 + .../test_change_in_sentence/12text.txt | 1 + .../test_change_in_sentence/13head.txt | 1 + .../test_change_in_sentence/14mode.txt | 1 + .../test_change_in_sentence/15text.txt | 1 + .../test_change_in_sentence/16head.txt | 1 + .../test_change_in_sentence/17mode.txt | 1 + .../test_change_in_sentence/18text.txt | 1 + .../test_change_in_sentence/19head.txt | 1 + .../test_change_in_sentence/1head.txt | 1 + .../test_change_in_sentence/20mode.txt | 1 + .../test_change_in_sentence/21text.txt | 1 + .../test_change_in_sentence/22head.txt | 1 + .../test_change_in_sentence/23mode.txt | 1 + .../test_change_in_sentence/24text.txt | 1 + .../test_change_in_sentence/25head.txt | 1 + .../test_change_in_sentence/26mode.txt | 1 + .../test_change_in_sentence/27text.txt | 1 + .../test_change_in_sentence/28head.txt | 1 + .../test_change_in_sentence/29mode.txt | 1 + .../test_change_in_sentence/2mode.txt | 1 + .../test_change_in_sentence/30text.txt | 1 + .../test_change_in_sentence/31head.txt | 1 + .../test_change_in_sentence/32mode.txt | 1 + .../test_change_in_sentence/33text.txt | 2 + .../test_change_in_sentence/34head.txt | 1 + .../test_change_in_sentence/35mode.txt | 1 + .../test_change_in_sentence/36text.txt | 2 + .../test_change_in_sentence/37head.txt | 1 + .../test_change_in_sentence/38mode.txt | 1 + .../test_change_in_sentence/39text.txt | 2 + .../test_change_in_sentence/3text.txt | 1 + .../test_change_in_sentence/40head.txt | 1 + .../test_change_in_sentence/41mode.txt | 1 + .../test_change_in_sentence/42text.txt | 2 + .../test_change_in_sentence/43head.txt | 1 + .../test_change_in_sentence/44mode.txt | 1 + .../test_change_in_sentence/45text.txt | 2 + .../test_change_in_sentence/46head.txt | 1 + .../test_change_in_sentence/47mode.txt | 1 + .../test_change_in_sentence/48text.txt | 4 + .../test_change_in_sentence/49head.txt | 1 + .../test_change_in_sentence/4head.txt | 1 + .../test_change_in_sentence/50mode.txt | 1 + .../test_change_in_sentence/51text.txt | 3 + .../test_change_in_sentence/52head.txt | 1 + .../test_change_in_sentence/53mode.txt | 1 + .../test_change_in_sentence/54text.txt | 3 + .../test_change_in_sentence/55head.txt | 1 + .../test_change_in_sentence/56mode.txt | 1 + .../test_change_in_sentence/5mode.txt | 1 + .../test_change_in_sentence/6text.txt | 1 + .../test_change_in_sentence/7head.txt | 1 + .../test_change_in_sentence/8mode.txt | 1 + .../test_change_in_sentence/9text.txt | 1 + .../test_data/test_change_in_word/0text.txt | 12 + .../test_data/test_change_in_word/100head.txt | 1 + .../test_data/test_change_in_word/101mode.txt | 1 + .../test_data/test_change_in_word/102text.txt | 12 + .../test_data/test_change_in_word/103head.txt | 1 + .../test_data/test_change_in_word/104mode.txt | 1 + .../test_data/test_change_in_word/105text.txt | 12 + .../test_data/test_change_in_word/106head.txt | 1 + .../test_data/test_change_in_word/107mode.txt | 1 + .../test_data/test_change_in_word/108text.txt | 12 + .../test_data/test_change_in_word/109head.txt | 1 + .../test_data/test_change_in_word/10head.txt | 1 + .../test_data/test_change_in_word/110mode.txt | 1 + .../test_data/test_change_in_word/111text.txt | 12 + .../test_data/test_change_in_word/112head.txt | 1 + .../test_data/test_change_in_word/113mode.txt | 1 + .../test_data/test_change_in_word/114text.txt | 12 + .../test_data/test_change_in_word/115head.txt | 1 + .../test_data/test_change_in_word/116mode.txt | 1 + .../test_data/test_change_in_word/117text.txt | 12 + .../test_data/test_change_in_word/118head.txt | 1 + .../test_data/test_change_in_word/119mode.txt | 1 + .../test_data/test_change_in_word/11mode.txt | 1 + .../test_data/test_change_in_word/120text.txt | 12 + .../test_data/test_change_in_word/121head.txt | 1 + .../test_data/test_change_in_word/122mode.txt | 1 + .../test_data/test_change_in_word/123text.txt | 12 + .../test_data/test_change_in_word/124head.txt | 1 + .../test_data/test_change_in_word/125mode.txt | 1 + .../test_data/test_change_in_word/126text.txt | 12 + .../test_data/test_change_in_word/127head.txt | 1 + .../test_data/test_change_in_word/128mode.txt | 1 + .../test_data/test_change_in_word/129text.txt | 12 + .../test_data/test_change_in_word/12text.txt | 12 + .../test_data/test_change_in_word/130head.txt | 1 + .../test_data/test_change_in_word/131mode.txt | 1 + .../test_data/test_change_in_word/132text.txt | 12 + .../test_data/test_change_in_word/133head.txt | 1 + .../test_data/test_change_in_word/134mode.txt | 1 + .../test_data/test_change_in_word/135text.txt | 12 + .../test_data/test_change_in_word/136head.txt | 1 + .../test_data/test_change_in_word/137mode.txt | 1 + .../test_data/test_change_in_word/13head.txt | 1 + .../test_data/test_change_in_word/14mode.txt | 1 + .../test_data/test_change_in_word/15text.txt | 12 + .../test_data/test_change_in_word/16head.txt | 1 + .../test_data/test_change_in_word/17mode.txt | 1 + .../test_data/test_change_in_word/18text.txt | 12 + .../test_data/test_change_in_word/19head.txt | 1 + .../test_data/test_change_in_word/1head.txt | 1 + .../test_data/test_change_in_word/20mode.txt | 1 + .../test_data/test_change_in_word/21text.txt | 12 + .../test_data/test_change_in_word/22head.txt | 1 + .../test_data/test_change_in_word/23mode.txt | 1 + .../test_data/test_change_in_word/24text.txt | 12 + .../test_data/test_change_in_word/25head.txt | 1 + .../test_data/test_change_in_word/26mode.txt | 1 + .../test_data/test_change_in_word/27text.txt | 12 + .../test_data/test_change_in_word/28head.txt | 1 + .../test_data/test_change_in_word/29mode.txt | 1 + .../test_data/test_change_in_word/2mode.txt | 1 + .../test_data/test_change_in_word/30text.txt | 12 + .../test_data/test_change_in_word/31head.txt | 1 + .../test_data/test_change_in_word/32mode.txt | 1 + .../test_data/test_change_in_word/33text.txt | 12 + .../test_data/test_change_in_word/34head.txt | 1 + .../test_data/test_change_in_word/35mode.txt | 1 + .../test_data/test_change_in_word/36text.txt | 12 + .../test_data/test_change_in_word/37head.txt | 1 + .../test_data/test_change_in_word/38mode.txt | 1 + .../test_data/test_change_in_word/39text.txt | 12 + .../test_data/test_change_in_word/3text.txt | 12 + .../test_data/test_change_in_word/40head.txt | 1 + .../test_data/test_change_in_word/41mode.txt | 1 + .../test_data/test_change_in_word/42text.txt | 12 + .../test_data/test_change_in_word/43head.txt | 1 + .../test_data/test_change_in_word/44mode.txt | 1 + .../test_data/test_change_in_word/45text.txt | 12 + .../test_data/test_change_in_word/46head.txt | 1 + .../test_data/test_change_in_word/47mode.txt | 1 + .../test_data/test_change_in_word/48text.txt | 12 + .../test_data/test_change_in_word/49head.txt | 1 + .../test_data/test_change_in_word/4head.txt | 1 + .../test_data/test_change_in_word/50mode.txt | 1 + .../test_data/test_change_in_word/51text.txt | 12 + .../test_data/test_change_in_word/52head.txt | 1 + .../test_data/test_change_in_word/53mode.txt | 1 + .../test_data/test_change_in_word/54text.txt | 12 + .../test_data/test_change_in_word/55head.txt | 1 + .../test_data/test_change_in_word/56mode.txt | 1 + .../test_data/test_change_in_word/57text.txt | 12 + .../test_data/test_change_in_word/58head.txt | 1 + .../test_data/test_change_in_word/59mode.txt | 1 + .../test_data/test_change_in_word/5mode.txt | 1 + .../test_data/test_change_in_word/60text.txt | 12 + .../test_data/test_change_in_word/61head.txt | 1 + .../test_data/test_change_in_word/62mode.txt | 1 + .../test_data/test_change_in_word/63text.txt | 12 + .../test_data/test_change_in_word/64head.txt | 1 + .../test_data/test_change_in_word/65mode.txt | 1 + .../test_data/test_change_in_word/66text.txt | 12 + .../test_data/test_change_in_word/67head.txt | 1 + .../test_data/test_change_in_word/68mode.txt | 1 + .../test_data/test_change_in_word/69text.txt | 12 + .../test_data/test_change_in_word/6text.txt | 12 + .../test_data/test_change_in_word/70head.txt | 1 + .../test_data/test_change_in_word/71mode.txt | 1 + .../test_data/test_change_in_word/72text.txt | 12 + .../test_data/test_change_in_word/73head.txt | 1 + .../test_data/test_change_in_word/74mode.txt | 1 + .../test_data/test_change_in_word/75text.txt | 12 + .../test_data/test_change_in_word/76head.txt | 1 + .../test_data/test_change_in_word/77mode.txt | 1 + .../test_data/test_change_in_word/78text.txt | 12 + .../test_data/test_change_in_word/79head.txt | 1 + .../test_data/test_change_in_word/7head.txt | 1 + .../test_data/test_change_in_word/80mode.txt | 1 + .../test_data/test_change_in_word/81text.txt | 12 + .../test_data/test_change_in_word/82head.txt | 1 + .../test_data/test_change_in_word/83mode.txt | 1 + .../test_data/test_change_in_word/84text.txt | 12 + .../test_data/test_change_in_word/85head.txt | 1 + .../test_data/test_change_in_word/86mode.txt | 1 + .../test_data/test_change_in_word/87text.txt | 12 + .../test_data/test_change_in_word/88head.txt | 1 + .../test_data/test_change_in_word/89mode.txt | 1 + .../test_data/test_change_in_word/8mode.txt | 1 + .../test_data/test_change_in_word/90text.txt | 12 + .../test_data/test_change_in_word/91head.txt | 1 + .../test_data/test_change_in_word/92mode.txt | 1 + .../test_data/test_change_in_word/93text.txt | 12 + .../test_data/test_change_in_word/94head.txt | 1 + .../test_data/test_change_in_word/95mode.txt | 1 + .../test_data/test_change_in_word/96text.txt | 12 + .../test_data/test_change_in_word/97head.txt | 1 + .../test_data/test_change_in_word/98mode.txt | 1 + .../test_data/test_change_in_word/99text.txt | 12 + .../test_data/test_change_in_word/9text.txt | 12 + .../test_delete_around_sentence/0text.txt | 1 + .../test_delete_around_sentence/10head.txt | 1 + .../test_delete_around_sentence/1head.txt | 1 + .../test_delete_around_sentence/2mode.txt | 1 + .../test_delete_around_sentence/3text.txt | 1 + .../test_delete_around_sentence/4head.txt | 1 + .../test_delete_around_sentence/5mode.txt | 1 + .../test_delete_around_sentence/6text.txt | 1 + .../test_delete_around_sentence/7head.txt | 1 + .../test_delete_around_sentence/8mode.txt | 1 + .../test_delete_around_sentence/9text.txt | 1 + .../test_delete_around_word/0text.txt | 12 + .../test_delete_around_word/100head.txt | 1 + .../test_delete_around_word/101mode.txt | 1 + .../test_delete_around_word/102text.txt | 12 + .../test_delete_around_word/103head.txt | 1 + .../test_delete_around_word/104mode.txt | 1 + .../test_delete_around_word/105text.txt | 12 + .../test_delete_around_word/106head.txt | 1 + .../test_delete_around_word/107mode.txt | 1 + .../test_delete_around_word/108text.txt | 12 + .../test_delete_around_word/109head.txt | 1 + .../test_delete_around_word/10head.txt | 1 + .../test_delete_around_word/110mode.txt | 1 + .../test_delete_around_word/111text.txt | 12 + .../test_delete_around_word/112head.txt | 1 + .../test_delete_around_word/113mode.txt | 1 + .../test_delete_around_word/114text.txt | 12 + .../test_delete_around_word/115head.txt | 1 + .../test_delete_around_word/116mode.txt | 1 + .../test_delete_around_word/117text.txt | 9 + .../test_delete_around_word/118head.txt | 1 + .../test_delete_around_word/119mode.txt | 1 + .../test_delete_around_word/11mode.txt | 1 + .../test_delete_around_word/120text.txt | 10 + .../test_delete_around_word/121head.txt | 1 + .../test_delete_around_word/122mode.txt | 1 + .../test_delete_around_word/123text.txt | 11 + .../test_delete_around_word/124head.txt | 1 + .../test_delete_around_word/125mode.txt | 1 + .../test_delete_around_word/126text.txt | 12 + .../test_delete_around_word/127head.txt | 1 + .../test_delete_around_word/128mode.txt | 1 + .../test_delete_around_word/129text.txt | 12 + .../test_delete_around_word/12text.txt | 12 + .../test_delete_around_word/130head.txt | 1 + .../test_delete_around_word/131mode.txt | 1 + .../test_delete_around_word/132text.txt | 11 + .../test_delete_around_word/133head.txt | 1 + .../test_delete_around_word/134mode.txt | 1 + .../test_delete_around_word/135text.txt | 11 + .../test_delete_around_word/136head.txt | 1 + .../test_delete_around_word/137mode.txt | 1 + .../test_delete_around_word/13head.txt | 1 + .../test_delete_around_word/14mode.txt | 1 + .../test_delete_around_word/15text.txt | 12 + .../test_delete_around_word/16head.txt | 1 + .../test_delete_around_word/17mode.txt | 1 + .../test_delete_around_word/18text.txt | 11 + .../test_delete_around_word/19head.txt | 1 + .../test_delete_around_word/1head.txt | 1 + .../test_delete_around_word/20mode.txt | 1 + .../test_delete_around_word/21text.txt | 10 + .../test_delete_around_word/22head.txt | 1 + .../test_delete_around_word/23mode.txt | 1 + .../test_delete_around_word/24text.txt | 10 + .../test_delete_around_word/25head.txt | 1 + .../test_delete_around_word/26mode.txt | 1 + .../test_delete_around_word/27text.txt | 11 + .../test_delete_around_word/28head.txt | 1 + .../test_delete_around_word/29mode.txt | 1 + .../test_delete_around_word/2mode.txt | 1 + .../test_delete_around_word/30text.txt | 12 + .../test_delete_around_word/31head.txt | 1 + .../test_delete_around_word/32mode.txt | 1 + .../test_delete_around_word/33text.txt | 12 + .../test_delete_around_word/34head.txt | 1 + .../test_delete_around_word/35mode.txt | 1 + .../test_delete_around_word/36text.txt | 12 + .../test_delete_around_word/37head.txt | 1 + .../test_delete_around_word/38mode.txt | 1 + .../test_delete_around_word/39text.txt | 12 + .../test_delete_around_word/3text.txt | 12 + .../test_delete_around_word/40head.txt | 1 + .../test_delete_around_word/41mode.txt | 1 + .../test_delete_around_word/42text.txt | 12 + .../test_delete_around_word/43head.txt | 1 + .../test_delete_around_word/44mode.txt | 1 + .../test_delete_around_word/45text.txt | 12 + .../test_delete_around_word/46head.txt | 1 + .../test_delete_around_word/47mode.txt | 1 + .../test_delete_around_word/48text.txt | 9 + .../test_delete_around_word/49head.txt | 1 + .../test_delete_around_word/4head.txt | 1 + .../test_delete_around_word/50mode.txt | 1 + .../test_delete_around_word/51text.txt | 10 + .../test_delete_around_word/52head.txt | 1 + .../test_delete_around_word/53mode.txt | 1 + .../test_delete_around_word/54text.txt | 11 + .../test_delete_around_word/55head.txt | 1 + .../test_delete_around_word/56mode.txt | 1 + .../test_delete_around_word/57text.txt | 12 + .../test_delete_around_word/58head.txt | 1 + .../test_delete_around_word/59mode.txt | 1 + .../test_delete_around_word/5mode.txt | 1 + .../test_delete_around_word/60text.txt | 12 + .../test_delete_around_word/61head.txt | 1 + .../test_delete_around_word/62mode.txt | 1 + .../test_delete_around_word/63text.txt | 11 + .../test_delete_around_word/64head.txt | 1 + .../test_delete_around_word/65mode.txt | 1 + .../test_delete_around_word/66text.txt | 11 + .../test_delete_around_word/67head.txt | 1 + .../test_delete_around_word/68mode.txt | 1 + .../test_delete_around_word/69text.txt | 12 + .../test_delete_around_word/6text.txt | 11 + .../test_delete_around_word/70head.txt | 1 + .../test_delete_around_word/71mode.txt | 1 + .../test_delete_around_word/72text.txt | 12 + .../test_delete_around_word/73head.txt | 1 + .../test_delete_around_word/74mode.txt | 1 + .../test_delete_around_word/75text.txt | 11 + .../test_delete_around_word/76head.txt | 1 + .../test_delete_around_word/77mode.txt | 1 + .../test_delete_around_word/78text.txt | 12 + .../test_delete_around_word/79head.txt | 1 + .../test_delete_around_word/7head.txt | 1 + .../test_delete_around_word/80mode.txt | 1 + .../test_delete_around_word/81text.txt | 12 + .../test_delete_around_word/82head.txt | 1 + .../test_delete_around_word/83mode.txt | 1 + .../test_delete_around_word/84text.txt | 12 + .../test_delete_around_word/85head.txt | 1 + .../test_delete_around_word/86mode.txt | 1 + .../test_delete_around_word/87text.txt | 11 + .../test_delete_around_word/88head.txt | 1 + .../test_delete_around_word/89mode.txt | 1 + .../test_delete_around_word/8mode.txt | 1 + .../test_delete_around_word/90text.txt | 10 + .../test_delete_around_word/91head.txt | 1 + .../test_delete_around_word/92mode.txt | 1 + .../test_delete_around_word/93text.txt | 10 + .../test_delete_around_word/94head.txt | 1 + .../test_delete_around_word/95mode.txt | 1 + .../test_delete_around_word/96text.txt | 11 + .../test_delete_around_word/97head.txt | 1 + .../test_delete_around_word/98mode.txt | 1 + .../test_delete_around_word/99text.txt | 12 + .../test_delete_around_word/9text.txt | 12 + .../test_delete_in_sentence/0text.txt | 1 + .../test_delete_in_sentence/10head.txt | 1 + .../test_delete_in_sentence/11mode.txt | 1 + .../test_delete_in_sentence/12text.txt | 1 + .../test_delete_in_sentence/13head.txt | 1 + .../test_delete_in_sentence/14mode.txt | 1 + .../test_delete_in_sentence/15text.txt | 1 + .../test_delete_in_sentence/16head.txt | 1 + .../test_delete_in_sentence/17mode.txt | 1 + .../test_delete_in_sentence/18text.txt | 1 + .../test_delete_in_sentence/19head.txt | 1 + .../test_delete_in_sentence/1head.txt | 1 + .../test_delete_in_sentence/20mode.txt | 1 + .../test_delete_in_sentence/21text.txt | 1 + .../test_delete_in_sentence/22head.txt | 1 + .../test_delete_in_sentence/23mode.txt | 1 + .../test_delete_in_sentence/24text.txt | 1 + .../test_delete_in_sentence/25head.txt | 1 + .../test_delete_in_sentence/26mode.txt | 1 + .../test_delete_in_sentence/27text.txt | 1 + .../test_delete_in_sentence/28head.txt | 1 + .../test_delete_in_sentence/29mode.txt | 1 + .../test_delete_in_sentence/2mode.txt | 1 + .../test_delete_in_sentence/30text.txt | 1 + .../test_delete_in_sentence/31head.txt | 1 + .../test_delete_in_sentence/32mode.txt | 1 + .../test_delete_in_sentence/33text.txt | 2 + .../test_delete_in_sentence/34head.txt | 1 + .../test_delete_in_sentence/35mode.txt | 1 + .../test_delete_in_sentence/36text.txt | 2 + .../test_delete_in_sentence/37head.txt | 1 + .../test_delete_in_sentence/38mode.txt | 1 + .../test_delete_in_sentence/39text.txt | 2 + .../test_delete_in_sentence/3text.txt | 1 + .../test_delete_in_sentence/40head.txt | 1 + .../test_delete_in_sentence/41mode.txt | 1 + .../test_delete_in_sentence/42text.txt | 2 + .../test_delete_in_sentence/43head.txt | 1 + .../test_delete_in_sentence/44mode.txt | 1 + .../test_delete_in_sentence/45text.txt | 2 + .../test_delete_in_sentence/46head.txt | 1 + .../test_delete_in_sentence/47mode.txt | 1 + .../test_delete_in_sentence/48text.txt | 4 + .../test_delete_in_sentence/49head.txt | 1 + .../test_delete_in_sentence/4head.txt | 1 + .../test_delete_in_sentence/50mode.txt | 1 + .../test_delete_in_sentence/51text.txt | 3 + .../test_delete_in_sentence/52head.txt | 1 + .../test_delete_in_sentence/53mode.txt | 1 + .../test_delete_in_sentence/54text.txt | 3 + .../test_delete_in_sentence/55head.txt | 1 + .../test_delete_in_sentence/56mode.txt | 1 + .../test_delete_in_sentence/5mode.txt | 1 + .../test_delete_in_sentence/6text.txt | 1 + .../test_delete_in_sentence/7head.txt | 1 + .../test_delete_in_sentence/8mode.txt | 1 + .../test_delete_in_sentence/9text.txt | 1 + .../test_data/test_delete_in_word/0text.txt | 12 + .../test_data/test_delete_in_word/100head.txt | 1 + .../test_data/test_delete_in_word/101mode.txt | 1 + .../test_data/test_delete_in_word/102text.txt | 12 + .../test_data/test_delete_in_word/103head.txt | 1 + .../test_data/test_delete_in_word/104mode.txt | 1 + .../test_data/test_delete_in_word/105text.txt | 12 + .../test_data/test_delete_in_word/106head.txt | 1 + .../test_data/test_delete_in_word/107mode.txt | 1 + .../test_data/test_delete_in_word/108text.txt | 12 + .../test_data/test_delete_in_word/109head.txt | 1 + .../test_data/test_delete_in_word/10head.txt | 1 + .../test_data/test_delete_in_word/110mode.txt | 1 + .../test_data/test_delete_in_word/111text.txt | 12 + .../test_data/test_delete_in_word/112head.txt | 1 + .../test_data/test_delete_in_word/113mode.txt | 1 + .../test_data/test_delete_in_word/114text.txt | 12 + .../test_data/test_delete_in_word/115head.txt | 1 + .../test_data/test_delete_in_word/116mode.txt | 1 + .../test_data/test_delete_in_word/117text.txt | 12 + .../test_data/test_delete_in_word/118head.txt | 1 + .../test_data/test_delete_in_word/119mode.txt | 1 + .../test_data/test_delete_in_word/11mode.txt | 1 + .../test_data/test_delete_in_word/120text.txt | 12 + .../test_data/test_delete_in_word/121head.txt | 1 + .../test_data/test_delete_in_word/122mode.txt | 1 + .../test_data/test_delete_in_word/123text.txt | 12 + .../test_data/test_delete_in_word/124head.txt | 1 + .../test_data/test_delete_in_word/125mode.txt | 1 + .../test_data/test_delete_in_word/126text.txt | 12 + .../test_data/test_delete_in_word/127head.txt | 1 + .../test_data/test_delete_in_word/128mode.txt | 1 + .../test_data/test_delete_in_word/129text.txt | 12 + .../test_data/test_delete_in_word/12text.txt | 12 + .../test_data/test_delete_in_word/130head.txt | 1 + .../test_data/test_delete_in_word/131mode.txt | 1 + .../test_data/test_delete_in_word/132text.txt | 12 + .../test_data/test_delete_in_word/133head.txt | 1 + .../test_data/test_delete_in_word/134mode.txt | 1 + .../test_data/test_delete_in_word/135text.txt | 12 + .../test_data/test_delete_in_word/136head.txt | 1 + .../test_data/test_delete_in_word/137mode.txt | 1 + .../test_data/test_delete_in_word/13head.txt | 1 + .../test_data/test_delete_in_word/14mode.txt | 1 + .../test_data/test_delete_in_word/15text.txt | 12 + .../test_data/test_delete_in_word/16head.txt | 1 + .../test_data/test_delete_in_word/17mode.txt | 1 + .../test_data/test_delete_in_word/18text.txt | 12 + .../test_data/test_delete_in_word/19head.txt | 1 + .../test_data/test_delete_in_word/1head.txt | 1 + .../test_data/test_delete_in_word/20mode.txt | 1 + .../test_data/test_delete_in_word/21text.txt | 12 + .../test_data/test_delete_in_word/22head.txt | 1 + .../test_data/test_delete_in_word/23mode.txt | 1 + .../test_data/test_delete_in_word/24text.txt | 12 + .../test_data/test_delete_in_word/25head.txt | 1 + .../test_data/test_delete_in_word/26mode.txt | 1 + .../test_data/test_delete_in_word/27text.txt | 12 + .../test_data/test_delete_in_word/28head.txt | 1 + .../test_data/test_delete_in_word/29mode.txt | 1 + .../test_data/test_delete_in_word/2mode.txt | 1 + .../test_data/test_delete_in_word/30text.txt | 12 + .../test_data/test_delete_in_word/31head.txt | 1 + .../test_data/test_delete_in_word/32mode.txt | 1 + .../test_data/test_delete_in_word/33text.txt | 12 + .../test_data/test_delete_in_word/34head.txt | 1 + .../test_data/test_delete_in_word/35mode.txt | 1 + .../test_data/test_delete_in_word/36text.txt | 12 + .../test_data/test_delete_in_word/37head.txt | 1 + .../test_data/test_delete_in_word/38mode.txt | 1 + .../test_data/test_delete_in_word/39text.txt | 12 + .../test_data/test_delete_in_word/3text.txt | 12 + .../test_data/test_delete_in_word/40head.txt | 1 + .../test_data/test_delete_in_word/41mode.txt | 1 + .../test_data/test_delete_in_word/42text.txt | 12 + .../test_data/test_delete_in_word/43head.txt | 1 + .../test_data/test_delete_in_word/44mode.txt | 1 + .../test_data/test_delete_in_word/45text.txt | 12 + .../test_data/test_delete_in_word/46head.txt | 1 + .../test_data/test_delete_in_word/47mode.txt | 1 + .../test_data/test_delete_in_word/48text.txt | 12 + .../test_data/test_delete_in_word/49head.txt | 1 + .../test_data/test_delete_in_word/4head.txt | 1 + .../test_data/test_delete_in_word/50mode.txt | 1 + .../test_data/test_delete_in_word/51text.txt | 12 + .../test_data/test_delete_in_word/52head.txt | 1 + .../test_data/test_delete_in_word/53mode.txt | 1 + .../test_data/test_delete_in_word/54text.txt | 12 + .../test_data/test_delete_in_word/55head.txt | 1 + .../test_data/test_delete_in_word/56mode.txt | 1 + .../test_data/test_delete_in_word/57text.txt | 12 + .../test_data/test_delete_in_word/58head.txt | 1 + .../test_data/test_delete_in_word/59mode.txt | 1 + .../test_data/test_delete_in_word/5mode.txt | 1 + .../test_data/test_delete_in_word/60text.txt | 12 + .../test_data/test_delete_in_word/61head.txt | 1 + .../test_data/test_delete_in_word/62mode.txt | 1 + .../test_data/test_delete_in_word/63text.txt | 12 + .../test_data/test_delete_in_word/64head.txt | 1 + .../test_data/test_delete_in_word/65mode.txt | 1 + .../test_data/test_delete_in_word/66text.txt | 12 + .../test_data/test_delete_in_word/67head.txt | 1 + .../test_data/test_delete_in_word/68mode.txt | 1 + .../test_data/test_delete_in_word/69text.txt | 12 + .../test_data/test_delete_in_word/6text.txt | 12 + .../test_data/test_delete_in_word/70head.txt | 1 + .../test_data/test_delete_in_word/71mode.txt | 1 + .../test_data/test_delete_in_word/72text.txt | 12 + .../test_data/test_delete_in_word/73head.txt | 1 + .../test_data/test_delete_in_word/74mode.txt | 1 + .../test_data/test_delete_in_word/75text.txt | 12 + .../test_data/test_delete_in_word/76head.txt | 1 + .../test_data/test_delete_in_word/77mode.txt | 1 + .../test_data/test_delete_in_word/78text.txt | 12 + .../test_data/test_delete_in_word/79head.txt | 1 + .../test_data/test_delete_in_word/7head.txt | 1 + .../test_data/test_delete_in_word/80mode.txt | 1 + .../test_data/test_delete_in_word/81text.txt | 12 + .../test_data/test_delete_in_word/82head.txt | 1 + .../test_data/test_delete_in_word/83mode.txt | 1 + .../test_data/test_delete_in_word/84text.txt | 12 + .../test_data/test_delete_in_word/85head.txt | 1 + .../test_data/test_delete_in_word/86mode.txt | 1 + .../test_data/test_delete_in_word/87text.txt | 12 + .../test_data/test_delete_in_word/88head.txt | 1 + .../test_data/test_delete_in_word/89mode.txt | 1 + .../test_data/test_delete_in_word/8mode.txt | 1 + .../test_data/test_delete_in_word/90text.txt | 12 + .../test_data/test_delete_in_word/91head.txt | 1 + .../test_data/test_delete_in_word/92mode.txt | 1 + .../test_data/test_delete_in_word/93text.txt | 12 + .../test_data/test_delete_in_word/94head.txt | 1 + .../test_data/test_delete_in_word/95mode.txt | 1 + .../test_data/test_delete_in_word/96text.txt | 12 + .../test_data/test_delete_in_word/97head.txt | 1 + .../test_data/test_delete_in_word/98mode.txt | 1 + .../test_data/test_delete_in_word/99text.txt | 12 + .../test_data/test_delete_in_word/9text.txt | 12 + crates/vim/test_data/test_neovim/0text.txt | 1 + crates/vim/test_data/test_neovim/1head.txt | 1 + crates/vim/test_data/test_neovim/2mode.txt | 1 + .../test_data/test_word_text_object/0text.txt | 9 + .../test_word_text_object/100head.txt | 1 + .../test_word_text_object/101mode.txt | 1 + .../test_word_text_object/102text.txt | 9 + .../test_word_text_object/103head.txt | 1 + .../test_word_text_object/104mode.txt | 1 + .../test_word_text_object/105text.txt | 9 + .../test_word_text_object/106head.txt | 1 + .../test_word_text_object/107mode.txt | 1 + .../test_word_text_object/108text.txt | 9 + .../test_word_text_object/109head.txt | 1 + .../test_word_text_object/10head.txt | 1 + .../test_word_text_object/110mode.txt | 1 + .../test_word_text_object/111text.txt | 9 + .../test_word_text_object/112head.txt | 1 + .../test_word_text_object/113mode.txt | 1 + .../test_word_text_object/114text.txt | 9 + .../test_word_text_object/115head.txt | 1 + .../test_word_text_object/116mode.txt | 1 + .../test_word_text_object/117text.txt | 9 + .../test_word_text_object/118head.txt | 1 + .../test_word_text_object/119mode.txt | 1 + .../test_word_text_object/11mode.txt | 1 + .../test_word_text_object/120text.txt | 8 + .../test_word_text_object/121head.txt | 1 + .../test_word_text_object/122mode.txt | 1 + .../test_word_text_object/123text.txt | 9 + .../test_word_text_object/124head.txt | 1 + .../test_word_text_object/125mode.txt | 1 + .../test_word_text_object/126text.txt | 9 + .../test_word_text_object/127head.txt | 1 + .../test_word_text_object/128mode.txt | 1 + .../test_word_text_object/129text.txt | 9 + .../test_word_text_object/12text.txt | 9 + .../test_word_text_object/130head.txt | 1 + .../test_word_text_object/131mode.txt | 1 + .../test_word_text_object/132text.txt | 8 + .../test_word_text_object/133head.txt | 1 + .../test_word_text_object/134mode.txt | 1 + .../test_word_text_object/135text.txt | 8 + .../test_word_text_object/136head.txt | 1 + .../test_word_text_object/137mode.txt | 1 + .../test_word_text_object/138text.txt | 8 + .../test_word_text_object/139head.txt | 1 + .../test_word_text_object/13head.txt | 1 + .../test_word_text_object/140mode.txt | 1 + .../test_word_text_object/141text.txt | 8 + .../test_word_text_object/142head.txt | 1 + .../test_word_text_object/143mode.txt | 1 + .../test_word_text_object/144text.txt | 9 + .../test_word_text_object/145head.txt | 1 + .../test_word_text_object/146mode.txt | 1 + .../test_word_text_object/147text.txt | 9 + .../test_word_text_object/148head.txt | 1 + .../test_word_text_object/149mode.txt | 1 + .../test_word_text_object/14mode.txt | 1 + .../test_word_text_object/150text.txt | 9 + .../test_word_text_object/151head.txt | 1 + .../test_word_text_object/152mode.txt | 1 + .../test_word_text_object/153text.txt | 9 + .../test_word_text_object/154head.txt | 1 + .../test_word_text_object/155mode.txt | 1 + .../test_word_text_object/156text.txt | 9 + .../test_word_text_object/157head.txt | 1 + .../test_word_text_object/158mode.txt | 1 + .../test_word_text_object/159text.txt | 9 + .../test_word_text_object/15text.txt | 9 + .../test_word_text_object/160head.txt | 1 + .../test_word_text_object/161mode.txt | 1 + .../test_word_text_object/162text.txt | 8 + .../test_word_text_object/163head.txt | 1 + .../test_word_text_object/164mode.txt | 1 + .../test_word_text_object/165text.txt | 9 + .../test_word_text_object/166head.txt | 1 + .../test_word_text_object/167mode.txt | 1 + .../test_word_text_object/168text.txt | 9 + .../test_word_text_object/169head.txt | 1 + .../test_word_text_object/16head.txt | 1 + .../test_word_text_object/170mode.txt | 1 + .../test_word_text_object/171text.txt | 9 + .../test_word_text_object/172head.txt | 1 + .../test_word_text_object/173mode.txt | 1 + .../test_word_text_object/174text.txt | 9 + .../test_word_text_object/175head.txt | 1 + .../test_word_text_object/176mode.txt | 1 + .../test_word_text_object/177text.txt | 8 + .../test_word_text_object/178head.txt | 1 + .../test_word_text_object/179mode.txt | 1 + .../test_word_text_object/17mode.txt | 1 + .../test_word_text_object/180text.txt | 9 + .../test_word_text_object/181head.txt | 1 + .../test_word_text_object/182mode.txt | 1 + .../test_word_text_object/183text.txt | 9 + .../test_word_text_object/184head.txt | 1 + .../test_word_text_object/185mode.txt | 1 + .../test_word_text_object/186text.txt | 9 + .../test_word_text_object/187head.txt | 1 + .../test_word_text_object/188mode.txt | 1 + .../test_word_text_object/189text.txt | 8 + .../test_word_text_object/18text.txt | 9 + .../test_word_text_object/190head.txt | 1 + .../test_word_text_object/191mode.txt | 1 + .../test_word_text_object/192text.txt | 8 + .../test_word_text_object/193head.txt | 1 + .../test_word_text_object/194mode.txt | 1 + .../test_word_text_object/195text.txt | 8 + .../test_word_text_object/196head.txt | 1 + .../test_word_text_object/197mode.txt | 1 + .../test_word_text_object/198text.txt | 8 + .../test_word_text_object/199head.txt | 1 + .../test_word_text_object/19head.txt | 1 + .../test_data/test_word_text_object/1head.txt | 1 + .../test_word_text_object/200mode.txt | 1 + .../test_word_text_object/201text.txt | 9 + .../test_word_text_object/202head.txt | 1 + .../test_word_text_object/203mode.txt | 1 + .../test_word_text_object/204text.txt | 9 + .../test_word_text_object/205head.txt | 1 + .../test_word_text_object/206mode.txt | 1 + .../test_word_text_object/207text.txt | 9 + .../test_word_text_object/208head.txt | 1 + .../test_word_text_object/209mode.txt | 1 + .../test_word_text_object/20mode.txt | 1 + .../test_word_text_object/210text.txt | 9 + .../test_word_text_object/211head.txt | 1 + .../test_word_text_object/212mode.txt | 1 + .../test_word_text_object/213text.txt | 9 + .../test_word_text_object/214head.txt | 1 + .../test_word_text_object/215mode.txt | 1 + .../test_word_text_object/216text.txt | 9 + .../test_word_text_object/217head.txt | 1 + .../test_word_text_object/218mode.txt | 1 + .../test_word_text_object/219text.txt | 8 + .../test_word_text_object/21text.txt | 9 + .../test_word_text_object/220head.txt | 1 + .../test_word_text_object/221mode.txt | 1 + .../test_word_text_object/222text.txt | 9 + .../test_word_text_object/223head.txt | 1 + .../test_word_text_object/224mode.txt | 1 + .../test_word_text_object/225text.txt | 9 + .../test_word_text_object/226head.txt | 1 + .../test_word_text_object/227mode.txt | 1 + .../test_word_text_object/228text.txt | 9 + .../test_word_text_object/229head.txt | 1 + .../test_word_text_object/22head.txt | 1 + .../test_word_text_object/230mode.txt | 1 + .../test_word_text_object/231text.txt | 9 + .../test_word_text_object/23mode.txt | 1 + .../test_word_text_object/24text.txt | 9 + .../test_word_text_object/25head.txt | 1 + .../test_word_text_object/26mode.txt | 1 + .../test_word_text_object/27text.txt | 9 + .../test_word_text_object/28head.txt | 1 + .../test_word_text_object/29mode.txt | 1 + .../test_data/test_word_text_object/2mode.txt | 1 + .../test_word_text_object/30text.txt | 9 + .../test_word_text_object/31head.txt | 1 + .../test_word_text_object/32mode.txt | 1 + .../test_word_text_object/33text.txt | 9 + .../test_word_text_object/34head.txt | 1 + .../test_word_text_object/35mode.txt | 1 + .../test_word_text_object/36text.txt | 9 + .../test_word_text_object/37head.txt | 1 + .../test_word_text_object/38mode.txt | 1 + .../test_word_text_object/39text.txt | 9 + .../test_data/test_word_text_object/3text.txt | 9 + .../test_word_text_object/40head.txt | 1 + .../test_word_text_object/41mode.txt | 1 + .../test_word_text_object/42text.txt | 9 + .../test_word_text_object/43head.txt | 1 + .../test_word_text_object/44mode.txt | 1 + .../test_word_text_object/45text.txt | 9 + .../test_word_text_object/46head.txt | 1 + .../test_word_text_object/47mode.txt | 1 + .../test_word_text_object/48text.txt | 9 + .../test_word_text_object/49head.txt | 1 + .../test_data/test_word_text_object/4head.txt | 1 + .../test_word_text_object/50mode.txt | 1 + .../test_word_text_object/51text.txt | 9 + .../test_word_text_object/52head.txt | 1 + .../test_word_text_object/53mode.txt | 1 + .../test_word_text_object/54text.txt | 9 + .../test_word_text_object/55head.txt | 1 + .../test_word_text_object/56mode.txt | 1 + .../test_word_text_object/57text.txt | 9 + .../test_word_text_object/58head.txt | 1 + .../test_word_text_object/59mode.txt | 1 + .../test_data/test_word_text_object/5mode.txt | 1 + .../test_word_text_object/60text.txt | 9 + .../test_word_text_object/61head.txt | 1 + .../test_word_text_object/62mode.txt | 1 + .../test_word_text_object/63text.txt | 9 + .../test_word_text_object/64head.txt | 1 + .../test_word_text_object/65mode.txt | 1 + .../test_word_text_object/66text.txt | 9 + .../test_word_text_object/67head.txt | 1 + .../test_word_text_object/68mode.txt | 1 + .../test_word_text_object/69text.txt | 9 + .../test_data/test_word_text_object/6text.txt | 9 + .../test_word_text_object/70head.txt | 1 + .../test_word_text_object/71mode.txt | 1 + .../test_word_text_object/72text.txt | 9 + .../test_word_text_object/73head.txt | 1 + .../test_word_text_object/74mode.txt | 1 + .../test_word_text_object/75text.txt | 9 + .../test_word_text_object/76head.txt | 1 + .../test_word_text_object/77mode.txt | 1 + .../test_word_text_object/78text.txt | 9 + .../test_word_text_object/79head.txt | 1 + .../test_data/test_word_text_object/7head.txt | 1 + .../test_word_text_object/80mode.txt | 1 + .../test_word_text_object/81text.txt | 9 + .../test_word_text_object/82head.txt | 1 + .../test_word_text_object/83mode.txt | 1 + .../test_word_text_object/84text.txt | 9 + .../test_word_text_object/85head.txt | 1 + .../test_word_text_object/86mode.txt | 1 + .../test_word_text_object/87text.txt | 9 + .../test_word_text_object/88head.txt | 1 + .../test_word_text_object/89mode.txt | 1 + .../test_data/test_word_text_object/8mode.txt | 1 + .../test_word_text_object/90text.txt | 9 + .../test_word_text_object/91head.txt | 1 + .../test_word_text_object/92mode.txt | 1 + .../test_word_text_object/93text.txt | 9 + .../test_word_text_object/94head.txt | 1 + .../test_word_text_object/95mode.txt | 1 + .../test_word_text_object/96text.txt | 9 + .../test_word_text_object/97head.txt | 1 + .../test_word_text_object/98mode.txt | 1 + .../test_word_text_object/99text.txt | 9 + .../test_data/test_word_text_object/9text.txt | 9 + 945 files changed, 5678 insertions(+), 655 deletions(-) create mode 100644 crates/vim/src/object.rs create mode 100644 crates/vim/src/test_contexts.rs create mode 100644 crates/vim/src/test_contexts/neovim_backed_binding_test_context.rs create mode 100644 crates/vim/src/test_contexts/neovim_backed_test_context.rs create mode 100644 crates/vim/src/test_contexts/vim_binding_test_context.rs rename crates/vim/src/{ => test_contexts}/vim_test_context.rs (75%) create mode 100644 crates/vim/test_data/test_change_around_sentence/0text.txt create mode 100644 crates/vim/test_data/test_change_around_sentence/10head.txt create mode 100644 crates/vim/test_data/test_change_around_sentence/1head.txt create mode 100644 crates/vim/test_data/test_change_around_sentence/2mode.txt create mode 100644 crates/vim/test_data/test_change_around_sentence/3text.txt create mode 100644 crates/vim/test_data/test_change_around_sentence/4head.txt create mode 100644 crates/vim/test_data/test_change_around_sentence/5mode.txt create mode 100644 crates/vim/test_data/test_change_around_sentence/6text.txt create mode 100644 crates/vim/test_data/test_change_around_sentence/7head.txt create mode 100644 crates/vim/test_data/test_change_around_sentence/8mode.txt create mode 100644 crates/vim/test_data/test_change_around_sentence/9text.txt create mode 100644 crates/vim/test_data/test_change_around_word/0text.txt create mode 100644 crates/vim/test_data/test_change_around_word/100head.txt create mode 100644 crates/vim/test_data/test_change_around_word/101mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/102text.txt create mode 100644 crates/vim/test_data/test_change_around_word/103head.txt create mode 100644 crates/vim/test_data/test_change_around_word/104mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/105text.txt create mode 100644 crates/vim/test_data/test_change_around_word/106head.txt create mode 100644 crates/vim/test_data/test_change_around_word/107mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/108text.txt create mode 100644 crates/vim/test_data/test_change_around_word/109head.txt create mode 100644 crates/vim/test_data/test_change_around_word/10head.txt create mode 100644 crates/vim/test_data/test_change_around_word/110mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/111text.txt create mode 100644 crates/vim/test_data/test_change_around_word/112head.txt create mode 100644 crates/vim/test_data/test_change_around_word/113mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/114text.txt create mode 100644 crates/vim/test_data/test_change_around_word/115head.txt create mode 100644 crates/vim/test_data/test_change_around_word/116mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/117text.txt create mode 100644 crates/vim/test_data/test_change_around_word/118head.txt create mode 100644 crates/vim/test_data/test_change_around_word/119mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/11mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/120text.txt create mode 100644 crates/vim/test_data/test_change_around_word/121head.txt create mode 100644 crates/vim/test_data/test_change_around_word/122mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/123text.txt create mode 100644 crates/vim/test_data/test_change_around_word/124head.txt create mode 100644 crates/vim/test_data/test_change_around_word/125mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/126text.txt create mode 100644 crates/vim/test_data/test_change_around_word/127head.txt create mode 100644 crates/vim/test_data/test_change_around_word/128mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/129text.txt create mode 100644 crates/vim/test_data/test_change_around_word/12text.txt create mode 100644 crates/vim/test_data/test_change_around_word/130head.txt create mode 100644 crates/vim/test_data/test_change_around_word/131mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/132text.txt create mode 100644 crates/vim/test_data/test_change_around_word/133head.txt create mode 100644 crates/vim/test_data/test_change_around_word/134mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/135text.txt create mode 100644 crates/vim/test_data/test_change_around_word/136head.txt create mode 100644 crates/vim/test_data/test_change_around_word/137mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/13head.txt create mode 100644 crates/vim/test_data/test_change_around_word/14mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/15text.txt create mode 100644 crates/vim/test_data/test_change_around_word/16head.txt create mode 100644 crates/vim/test_data/test_change_around_word/17mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/18text.txt create mode 100644 crates/vim/test_data/test_change_around_word/19head.txt create mode 100644 crates/vim/test_data/test_change_around_word/1head.txt create mode 100644 crates/vim/test_data/test_change_around_word/20mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/21text.txt create mode 100644 crates/vim/test_data/test_change_around_word/22head.txt create mode 100644 crates/vim/test_data/test_change_around_word/23mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/24text.txt create mode 100644 crates/vim/test_data/test_change_around_word/25head.txt create mode 100644 crates/vim/test_data/test_change_around_word/26mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/27text.txt create mode 100644 crates/vim/test_data/test_change_around_word/28head.txt create mode 100644 crates/vim/test_data/test_change_around_word/29mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/2mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/30text.txt create mode 100644 crates/vim/test_data/test_change_around_word/31head.txt create mode 100644 crates/vim/test_data/test_change_around_word/32mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/33text.txt create mode 100644 crates/vim/test_data/test_change_around_word/34head.txt create mode 100644 crates/vim/test_data/test_change_around_word/35mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/36text.txt create mode 100644 crates/vim/test_data/test_change_around_word/37head.txt create mode 100644 crates/vim/test_data/test_change_around_word/38mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/39text.txt create mode 100644 crates/vim/test_data/test_change_around_word/3text.txt create mode 100644 crates/vim/test_data/test_change_around_word/40head.txt create mode 100644 crates/vim/test_data/test_change_around_word/41mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/42text.txt create mode 100644 crates/vim/test_data/test_change_around_word/43head.txt create mode 100644 crates/vim/test_data/test_change_around_word/44mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/45text.txt create mode 100644 crates/vim/test_data/test_change_around_word/46head.txt create mode 100644 crates/vim/test_data/test_change_around_word/47mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/48text.txt create mode 100644 crates/vim/test_data/test_change_around_word/49head.txt create mode 100644 crates/vim/test_data/test_change_around_word/4head.txt create mode 100644 crates/vim/test_data/test_change_around_word/50mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/51text.txt create mode 100644 crates/vim/test_data/test_change_around_word/52head.txt create mode 100644 crates/vim/test_data/test_change_around_word/53mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/54text.txt create mode 100644 crates/vim/test_data/test_change_around_word/55head.txt create mode 100644 crates/vim/test_data/test_change_around_word/56mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/57text.txt create mode 100644 crates/vim/test_data/test_change_around_word/58head.txt create mode 100644 crates/vim/test_data/test_change_around_word/59mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/5mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/60text.txt create mode 100644 crates/vim/test_data/test_change_around_word/61head.txt create mode 100644 crates/vim/test_data/test_change_around_word/62mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/63text.txt create mode 100644 crates/vim/test_data/test_change_around_word/64head.txt create mode 100644 crates/vim/test_data/test_change_around_word/65mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/66text.txt create mode 100644 crates/vim/test_data/test_change_around_word/67head.txt create mode 100644 crates/vim/test_data/test_change_around_word/68mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/69text.txt create mode 100644 crates/vim/test_data/test_change_around_word/6text.txt create mode 100644 crates/vim/test_data/test_change_around_word/70head.txt create mode 100644 crates/vim/test_data/test_change_around_word/71mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/72text.txt create mode 100644 crates/vim/test_data/test_change_around_word/73head.txt create mode 100644 crates/vim/test_data/test_change_around_word/74mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/75text.txt create mode 100644 crates/vim/test_data/test_change_around_word/76head.txt create mode 100644 crates/vim/test_data/test_change_around_word/77mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/78text.txt create mode 100644 crates/vim/test_data/test_change_around_word/79head.txt create mode 100644 crates/vim/test_data/test_change_around_word/7head.txt create mode 100644 crates/vim/test_data/test_change_around_word/80mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/81text.txt create mode 100644 crates/vim/test_data/test_change_around_word/82head.txt create mode 100644 crates/vim/test_data/test_change_around_word/83mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/84text.txt create mode 100644 crates/vim/test_data/test_change_around_word/85head.txt create mode 100644 crates/vim/test_data/test_change_around_word/86mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/87text.txt create mode 100644 crates/vim/test_data/test_change_around_word/88head.txt create mode 100644 crates/vim/test_data/test_change_around_word/89mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/8mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/90text.txt create mode 100644 crates/vim/test_data/test_change_around_word/91head.txt create mode 100644 crates/vim/test_data/test_change_around_word/92mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/93text.txt create mode 100644 crates/vim/test_data/test_change_around_word/94head.txt create mode 100644 crates/vim/test_data/test_change_around_word/95mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/96text.txt create mode 100644 crates/vim/test_data/test_change_around_word/97head.txt create mode 100644 crates/vim/test_data/test_change_around_word/98mode.txt create mode 100644 crates/vim/test_data/test_change_around_word/99text.txt create mode 100644 crates/vim/test_data/test_change_around_word/9text.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/0text.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/10head.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/11mode.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/12text.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/13head.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/14mode.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/15text.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/16head.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/17mode.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/18text.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/19head.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/1head.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/20mode.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/21text.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/22head.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/23mode.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/24text.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/25head.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/26mode.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/27text.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/28head.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/29mode.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/2mode.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/30text.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/31head.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/32mode.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/33text.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/34head.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/35mode.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/36text.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/37head.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/38mode.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/39text.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/3text.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/40head.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/41mode.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/42text.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/43head.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/44mode.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/45text.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/46head.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/47mode.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/48text.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/49head.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/4head.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/50mode.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/51text.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/52head.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/53mode.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/54text.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/55head.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/56mode.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/5mode.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/6text.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/7head.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/8mode.txt create mode 100644 crates/vim/test_data/test_change_in_sentence/9text.txt create mode 100644 crates/vim/test_data/test_change_in_word/0text.txt create mode 100644 crates/vim/test_data/test_change_in_word/100head.txt create mode 100644 crates/vim/test_data/test_change_in_word/101mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/102text.txt create mode 100644 crates/vim/test_data/test_change_in_word/103head.txt create mode 100644 crates/vim/test_data/test_change_in_word/104mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/105text.txt create mode 100644 crates/vim/test_data/test_change_in_word/106head.txt create mode 100644 crates/vim/test_data/test_change_in_word/107mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/108text.txt create mode 100644 crates/vim/test_data/test_change_in_word/109head.txt create mode 100644 crates/vim/test_data/test_change_in_word/10head.txt create mode 100644 crates/vim/test_data/test_change_in_word/110mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/111text.txt create mode 100644 crates/vim/test_data/test_change_in_word/112head.txt create mode 100644 crates/vim/test_data/test_change_in_word/113mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/114text.txt create mode 100644 crates/vim/test_data/test_change_in_word/115head.txt create mode 100644 crates/vim/test_data/test_change_in_word/116mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/117text.txt create mode 100644 crates/vim/test_data/test_change_in_word/118head.txt create mode 100644 crates/vim/test_data/test_change_in_word/119mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/11mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/120text.txt create mode 100644 crates/vim/test_data/test_change_in_word/121head.txt create mode 100644 crates/vim/test_data/test_change_in_word/122mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/123text.txt create mode 100644 crates/vim/test_data/test_change_in_word/124head.txt create mode 100644 crates/vim/test_data/test_change_in_word/125mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/126text.txt create mode 100644 crates/vim/test_data/test_change_in_word/127head.txt create mode 100644 crates/vim/test_data/test_change_in_word/128mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/129text.txt create mode 100644 crates/vim/test_data/test_change_in_word/12text.txt create mode 100644 crates/vim/test_data/test_change_in_word/130head.txt create mode 100644 crates/vim/test_data/test_change_in_word/131mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/132text.txt create mode 100644 crates/vim/test_data/test_change_in_word/133head.txt create mode 100644 crates/vim/test_data/test_change_in_word/134mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/135text.txt create mode 100644 crates/vim/test_data/test_change_in_word/136head.txt create mode 100644 crates/vim/test_data/test_change_in_word/137mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/13head.txt create mode 100644 crates/vim/test_data/test_change_in_word/14mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/15text.txt create mode 100644 crates/vim/test_data/test_change_in_word/16head.txt create mode 100644 crates/vim/test_data/test_change_in_word/17mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/18text.txt create mode 100644 crates/vim/test_data/test_change_in_word/19head.txt create mode 100644 crates/vim/test_data/test_change_in_word/1head.txt create mode 100644 crates/vim/test_data/test_change_in_word/20mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/21text.txt create mode 100644 crates/vim/test_data/test_change_in_word/22head.txt create mode 100644 crates/vim/test_data/test_change_in_word/23mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/24text.txt create mode 100644 crates/vim/test_data/test_change_in_word/25head.txt create mode 100644 crates/vim/test_data/test_change_in_word/26mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/27text.txt create mode 100644 crates/vim/test_data/test_change_in_word/28head.txt create mode 100644 crates/vim/test_data/test_change_in_word/29mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/2mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/30text.txt create mode 100644 crates/vim/test_data/test_change_in_word/31head.txt create mode 100644 crates/vim/test_data/test_change_in_word/32mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/33text.txt create mode 100644 crates/vim/test_data/test_change_in_word/34head.txt create mode 100644 crates/vim/test_data/test_change_in_word/35mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/36text.txt create mode 100644 crates/vim/test_data/test_change_in_word/37head.txt create mode 100644 crates/vim/test_data/test_change_in_word/38mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/39text.txt create mode 100644 crates/vim/test_data/test_change_in_word/3text.txt create mode 100644 crates/vim/test_data/test_change_in_word/40head.txt create mode 100644 crates/vim/test_data/test_change_in_word/41mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/42text.txt create mode 100644 crates/vim/test_data/test_change_in_word/43head.txt create mode 100644 crates/vim/test_data/test_change_in_word/44mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/45text.txt create mode 100644 crates/vim/test_data/test_change_in_word/46head.txt create mode 100644 crates/vim/test_data/test_change_in_word/47mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/48text.txt create mode 100644 crates/vim/test_data/test_change_in_word/49head.txt create mode 100644 crates/vim/test_data/test_change_in_word/4head.txt create mode 100644 crates/vim/test_data/test_change_in_word/50mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/51text.txt create mode 100644 crates/vim/test_data/test_change_in_word/52head.txt create mode 100644 crates/vim/test_data/test_change_in_word/53mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/54text.txt create mode 100644 crates/vim/test_data/test_change_in_word/55head.txt create mode 100644 crates/vim/test_data/test_change_in_word/56mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/57text.txt create mode 100644 crates/vim/test_data/test_change_in_word/58head.txt create mode 100644 crates/vim/test_data/test_change_in_word/59mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/5mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/60text.txt create mode 100644 crates/vim/test_data/test_change_in_word/61head.txt create mode 100644 crates/vim/test_data/test_change_in_word/62mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/63text.txt create mode 100644 crates/vim/test_data/test_change_in_word/64head.txt create mode 100644 crates/vim/test_data/test_change_in_word/65mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/66text.txt create mode 100644 crates/vim/test_data/test_change_in_word/67head.txt create mode 100644 crates/vim/test_data/test_change_in_word/68mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/69text.txt create mode 100644 crates/vim/test_data/test_change_in_word/6text.txt create mode 100644 crates/vim/test_data/test_change_in_word/70head.txt create mode 100644 crates/vim/test_data/test_change_in_word/71mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/72text.txt create mode 100644 crates/vim/test_data/test_change_in_word/73head.txt create mode 100644 crates/vim/test_data/test_change_in_word/74mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/75text.txt create mode 100644 crates/vim/test_data/test_change_in_word/76head.txt create mode 100644 crates/vim/test_data/test_change_in_word/77mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/78text.txt create mode 100644 crates/vim/test_data/test_change_in_word/79head.txt create mode 100644 crates/vim/test_data/test_change_in_word/7head.txt create mode 100644 crates/vim/test_data/test_change_in_word/80mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/81text.txt create mode 100644 crates/vim/test_data/test_change_in_word/82head.txt create mode 100644 crates/vim/test_data/test_change_in_word/83mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/84text.txt create mode 100644 crates/vim/test_data/test_change_in_word/85head.txt create mode 100644 crates/vim/test_data/test_change_in_word/86mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/87text.txt create mode 100644 crates/vim/test_data/test_change_in_word/88head.txt create mode 100644 crates/vim/test_data/test_change_in_word/89mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/8mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/90text.txt create mode 100644 crates/vim/test_data/test_change_in_word/91head.txt create mode 100644 crates/vim/test_data/test_change_in_word/92mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/93text.txt create mode 100644 crates/vim/test_data/test_change_in_word/94head.txt create mode 100644 crates/vim/test_data/test_change_in_word/95mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/96text.txt create mode 100644 crates/vim/test_data/test_change_in_word/97head.txt create mode 100644 crates/vim/test_data/test_change_in_word/98mode.txt create mode 100644 crates/vim/test_data/test_change_in_word/99text.txt create mode 100644 crates/vim/test_data/test_change_in_word/9text.txt create mode 100644 crates/vim/test_data/test_delete_around_sentence/0text.txt create mode 100644 crates/vim/test_data/test_delete_around_sentence/10head.txt create mode 100644 crates/vim/test_data/test_delete_around_sentence/1head.txt create mode 100644 crates/vim/test_data/test_delete_around_sentence/2mode.txt create mode 100644 crates/vim/test_data/test_delete_around_sentence/3text.txt create mode 100644 crates/vim/test_data/test_delete_around_sentence/4head.txt create mode 100644 crates/vim/test_data/test_delete_around_sentence/5mode.txt create mode 100644 crates/vim/test_data/test_delete_around_sentence/6text.txt create mode 100644 crates/vim/test_data/test_delete_around_sentence/7head.txt create mode 100644 crates/vim/test_data/test_delete_around_sentence/8mode.txt create mode 100644 crates/vim/test_data/test_delete_around_sentence/9text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/0text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/100head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/101mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/102text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/103head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/104mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/105text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/106head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/107mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/108text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/109head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/10head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/110mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/111text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/112head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/113mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/114text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/115head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/116mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/117text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/118head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/119mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/11mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/120text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/121head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/122mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/123text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/124head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/125mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/126text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/127head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/128mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/129text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/12text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/130head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/131mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/132text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/133head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/134mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/135text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/136head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/137mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/13head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/14mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/15text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/16head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/17mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/18text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/19head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/1head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/20mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/21text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/22head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/23mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/24text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/25head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/26mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/27text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/28head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/29mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/2mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/30text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/31head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/32mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/33text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/34head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/35mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/36text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/37head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/38mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/39text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/3text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/40head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/41mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/42text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/43head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/44mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/45text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/46head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/47mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/48text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/49head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/4head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/50mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/51text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/52head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/53mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/54text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/55head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/56mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/57text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/58head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/59mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/5mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/60text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/61head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/62mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/63text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/64head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/65mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/66text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/67head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/68mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/69text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/6text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/70head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/71mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/72text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/73head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/74mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/75text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/76head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/77mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/78text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/79head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/7head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/80mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/81text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/82head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/83mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/84text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/85head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/86mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/87text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/88head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/89mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/8mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/90text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/91head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/92mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/93text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/94head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/95mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/96text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/97head.txt create mode 100644 crates/vim/test_data/test_delete_around_word/98mode.txt create mode 100644 crates/vim/test_data/test_delete_around_word/99text.txt create mode 100644 crates/vim/test_data/test_delete_around_word/9text.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/0text.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/10head.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/11mode.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/12text.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/13head.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/14mode.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/15text.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/16head.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/17mode.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/18text.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/19head.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/1head.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/20mode.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/21text.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/22head.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/23mode.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/24text.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/25head.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/26mode.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/27text.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/28head.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/29mode.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/2mode.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/30text.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/31head.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/32mode.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/33text.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/34head.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/35mode.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/36text.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/37head.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/38mode.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/39text.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/3text.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/40head.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/41mode.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/42text.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/43head.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/44mode.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/45text.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/46head.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/47mode.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/48text.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/49head.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/4head.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/50mode.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/51text.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/52head.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/53mode.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/54text.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/55head.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/56mode.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/5mode.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/6text.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/7head.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/8mode.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence/9text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/0text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/100head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/101mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/102text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/103head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/104mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/105text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/106head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/107mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/108text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/109head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/10head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/110mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/111text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/112head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/113mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/114text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/115head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/116mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/117text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/118head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/119mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/11mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/120text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/121head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/122mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/123text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/124head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/125mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/126text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/127head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/128mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/129text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/12text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/130head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/131mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/132text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/133head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/134mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/135text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/136head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/137mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/13head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/14mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/15text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/16head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/17mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/18text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/19head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/1head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/20mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/21text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/22head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/23mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/24text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/25head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/26mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/27text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/28head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/29mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/2mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/30text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/31head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/32mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/33text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/34head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/35mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/36text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/37head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/38mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/39text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/3text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/40head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/41mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/42text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/43head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/44mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/45text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/46head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/47mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/48text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/49head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/4head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/50mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/51text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/52head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/53mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/54text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/55head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/56mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/57text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/58head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/59mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/5mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/60text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/61head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/62mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/63text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/64head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/65mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/66text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/67head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/68mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/69text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/6text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/70head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/71mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/72text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/73head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/74mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/75text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/76head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/77mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/78text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/79head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/7head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/80mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/81text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/82head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/83mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/84text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/85head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/86mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/87text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/88head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/89mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/8mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/90text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/91head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/92mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/93text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/94head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/95mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/96text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/97head.txt create mode 100644 crates/vim/test_data/test_delete_in_word/98mode.txt create mode 100644 crates/vim/test_data/test_delete_in_word/99text.txt create mode 100644 crates/vim/test_data/test_delete_in_word/9text.txt create mode 100644 crates/vim/test_data/test_neovim/0text.txt create mode 100644 crates/vim/test_data/test_neovim/1head.txt create mode 100644 crates/vim/test_data/test_neovim/2mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/0text.txt create mode 100644 crates/vim/test_data/test_word_text_object/100head.txt create mode 100644 crates/vim/test_data/test_word_text_object/101mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/102text.txt create mode 100644 crates/vim/test_data/test_word_text_object/103head.txt create mode 100644 crates/vim/test_data/test_word_text_object/104mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/105text.txt create mode 100644 crates/vim/test_data/test_word_text_object/106head.txt create mode 100644 crates/vim/test_data/test_word_text_object/107mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/108text.txt create mode 100644 crates/vim/test_data/test_word_text_object/109head.txt create mode 100644 crates/vim/test_data/test_word_text_object/10head.txt create mode 100644 crates/vim/test_data/test_word_text_object/110mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/111text.txt create mode 100644 crates/vim/test_data/test_word_text_object/112head.txt create mode 100644 crates/vim/test_data/test_word_text_object/113mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/114text.txt create mode 100644 crates/vim/test_data/test_word_text_object/115head.txt create mode 100644 crates/vim/test_data/test_word_text_object/116mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/117text.txt create mode 100644 crates/vim/test_data/test_word_text_object/118head.txt create mode 100644 crates/vim/test_data/test_word_text_object/119mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/11mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/120text.txt create mode 100644 crates/vim/test_data/test_word_text_object/121head.txt create mode 100644 crates/vim/test_data/test_word_text_object/122mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/123text.txt create mode 100644 crates/vim/test_data/test_word_text_object/124head.txt create mode 100644 crates/vim/test_data/test_word_text_object/125mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/126text.txt create mode 100644 crates/vim/test_data/test_word_text_object/127head.txt create mode 100644 crates/vim/test_data/test_word_text_object/128mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/129text.txt create mode 100644 crates/vim/test_data/test_word_text_object/12text.txt create mode 100644 crates/vim/test_data/test_word_text_object/130head.txt create mode 100644 crates/vim/test_data/test_word_text_object/131mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/132text.txt create mode 100644 crates/vim/test_data/test_word_text_object/133head.txt create mode 100644 crates/vim/test_data/test_word_text_object/134mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/135text.txt create mode 100644 crates/vim/test_data/test_word_text_object/136head.txt create mode 100644 crates/vim/test_data/test_word_text_object/137mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/138text.txt create mode 100644 crates/vim/test_data/test_word_text_object/139head.txt create mode 100644 crates/vim/test_data/test_word_text_object/13head.txt create mode 100644 crates/vim/test_data/test_word_text_object/140mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/141text.txt create mode 100644 crates/vim/test_data/test_word_text_object/142head.txt create mode 100644 crates/vim/test_data/test_word_text_object/143mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/144text.txt create mode 100644 crates/vim/test_data/test_word_text_object/145head.txt create mode 100644 crates/vim/test_data/test_word_text_object/146mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/147text.txt create mode 100644 crates/vim/test_data/test_word_text_object/148head.txt create mode 100644 crates/vim/test_data/test_word_text_object/149mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/14mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/150text.txt create mode 100644 crates/vim/test_data/test_word_text_object/151head.txt create mode 100644 crates/vim/test_data/test_word_text_object/152mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/153text.txt create mode 100644 crates/vim/test_data/test_word_text_object/154head.txt create mode 100644 crates/vim/test_data/test_word_text_object/155mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/156text.txt create mode 100644 crates/vim/test_data/test_word_text_object/157head.txt create mode 100644 crates/vim/test_data/test_word_text_object/158mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/159text.txt create mode 100644 crates/vim/test_data/test_word_text_object/15text.txt create mode 100644 crates/vim/test_data/test_word_text_object/160head.txt create mode 100644 crates/vim/test_data/test_word_text_object/161mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/162text.txt create mode 100644 crates/vim/test_data/test_word_text_object/163head.txt create mode 100644 crates/vim/test_data/test_word_text_object/164mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/165text.txt create mode 100644 crates/vim/test_data/test_word_text_object/166head.txt create mode 100644 crates/vim/test_data/test_word_text_object/167mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/168text.txt create mode 100644 crates/vim/test_data/test_word_text_object/169head.txt create mode 100644 crates/vim/test_data/test_word_text_object/16head.txt create mode 100644 crates/vim/test_data/test_word_text_object/170mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/171text.txt create mode 100644 crates/vim/test_data/test_word_text_object/172head.txt create mode 100644 crates/vim/test_data/test_word_text_object/173mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/174text.txt create mode 100644 crates/vim/test_data/test_word_text_object/175head.txt create mode 100644 crates/vim/test_data/test_word_text_object/176mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/177text.txt create mode 100644 crates/vim/test_data/test_word_text_object/178head.txt create mode 100644 crates/vim/test_data/test_word_text_object/179mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/17mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/180text.txt create mode 100644 crates/vim/test_data/test_word_text_object/181head.txt create mode 100644 crates/vim/test_data/test_word_text_object/182mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/183text.txt create mode 100644 crates/vim/test_data/test_word_text_object/184head.txt create mode 100644 crates/vim/test_data/test_word_text_object/185mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/186text.txt create mode 100644 crates/vim/test_data/test_word_text_object/187head.txt create mode 100644 crates/vim/test_data/test_word_text_object/188mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/189text.txt create mode 100644 crates/vim/test_data/test_word_text_object/18text.txt create mode 100644 crates/vim/test_data/test_word_text_object/190head.txt create mode 100644 crates/vim/test_data/test_word_text_object/191mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/192text.txt create mode 100644 crates/vim/test_data/test_word_text_object/193head.txt create mode 100644 crates/vim/test_data/test_word_text_object/194mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/195text.txt create mode 100644 crates/vim/test_data/test_word_text_object/196head.txt create mode 100644 crates/vim/test_data/test_word_text_object/197mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/198text.txt create mode 100644 crates/vim/test_data/test_word_text_object/199head.txt create mode 100644 crates/vim/test_data/test_word_text_object/19head.txt create mode 100644 crates/vim/test_data/test_word_text_object/1head.txt create mode 100644 crates/vim/test_data/test_word_text_object/200mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/201text.txt create mode 100644 crates/vim/test_data/test_word_text_object/202head.txt create mode 100644 crates/vim/test_data/test_word_text_object/203mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/204text.txt create mode 100644 crates/vim/test_data/test_word_text_object/205head.txt create mode 100644 crates/vim/test_data/test_word_text_object/206mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/207text.txt create mode 100644 crates/vim/test_data/test_word_text_object/208head.txt create mode 100644 crates/vim/test_data/test_word_text_object/209mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/20mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/210text.txt create mode 100644 crates/vim/test_data/test_word_text_object/211head.txt create mode 100644 crates/vim/test_data/test_word_text_object/212mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/213text.txt create mode 100644 crates/vim/test_data/test_word_text_object/214head.txt create mode 100644 crates/vim/test_data/test_word_text_object/215mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/216text.txt create mode 100644 crates/vim/test_data/test_word_text_object/217head.txt create mode 100644 crates/vim/test_data/test_word_text_object/218mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/219text.txt create mode 100644 crates/vim/test_data/test_word_text_object/21text.txt create mode 100644 crates/vim/test_data/test_word_text_object/220head.txt create mode 100644 crates/vim/test_data/test_word_text_object/221mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/222text.txt create mode 100644 crates/vim/test_data/test_word_text_object/223head.txt create mode 100644 crates/vim/test_data/test_word_text_object/224mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/225text.txt create mode 100644 crates/vim/test_data/test_word_text_object/226head.txt create mode 100644 crates/vim/test_data/test_word_text_object/227mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/228text.txt create mode 100644 crates/vim/test_data/test_word_text_object/229head.txt create mode 100644 crates/vim/test_data/test_word_text_object/22head.txt create mode 100644 crates/vim/test_data/test_word_text_object/230mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/231text.txt create mode 100644 crates/vim/test_data/test_word_text_object/23mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/24text.txt create mode 100644 crates/vim/test_data/test_word_text_object/25head.txt create mode 100644 crates/vim/test_data/test_word_text_object/26mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/27text.txt create mode 100644 crates/vim/test_data/test_word_text_object/28head.txt create mode 100644 crates/vim/test_data/test_word_text_object/29mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/2mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/30text.txt create mode 100644 crates/vim/test_data/test_word_text_object/31head.txt create mode 100644 crates/vim/test_data/test_word_text_object/32mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/33text.txt create mode 100644 crates/vim/test_data/test_word_text_object/34head.txt create mode 100644 crates/vim/test_data/test_word_text_object/35mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/36text.txt create mode 100644 crates/vim/test_data/test_word_text_object/37head.txt create mode 100644 crates/vim/test_data/test_word_text_object/38mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/39text.txt create mode 100644 crates/vim/test_data/test_word_text_object/3text.txt create mode 100644 crates/vim/test_data/test_word_text_object/40head.txt create mode 100644 crates/vim/test_data/test_word_text_object/41mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/42text.txt create mode 100644 crates/vim/test_data/test_word_text_object/43head.txt create mode 100644 crates/vim/test_data/test_word_text_object/44mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/45text.txt create mode 100644 crates/vim/test_data/test_word_text_object/46head.txt create mode 100644 crates/vim/test_data/test_word_text_object/47mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/48text.txt create mode 100644 crates/vim/test_data/test_word_text_object/49head.txt create mode 100644 crates/vim/test_data/test_word_text_object/4head.txt create mode 100644 crates/vim/test_data/test_word_text_object/50mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/51text.txt create mode 100644 crates/vim/test_data/test_word_text_object/52head.txt create mode 100644 crates/vim/test_data/test_word_text_object/53mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/54text.txt create mode 100644 crates/vim/test_data/test_word_text_object/55head.txt create mode 100644 crates/vim/test_data/test_word_text_object/56mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/57text.txt create mode 100644 crates/vim/test_data/test_word_text_object/58head.txt create mode 100644 crates/vim/test_data/test_word_text_object/59mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/5mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/60text.txt create mode 100644 crates/vim/test_data/test_word_text_object/61head.txt create mode 100644 crates/vim/test_data/test_word_text_object/62mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/63text.txt create mode 100644 crates/vim/test_data/test_word_text_object/64head.txt create mode 100644 crates/vim/test_data/test_word_text_object/65mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/66text.txt create mode 100644 crates/vim/test_data/test_word_text_object/67head.txt create mode 100644 crates/vim/test_data/test_word_text_object/68mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/69text.txt create mode 100644 crates/vim/test_data/test_word_text_object/6text.txt create mode 100644 crates/vim/test_data/test_word_text_object/70head.txt create mode 100644 crates/vim/test_data/test_word_text_object/71mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/72text.txt create mode 100644 crates/vim/test_data/test_word_text_object/73head.txt create mode 100644 crates/vim/test_data/test_word_text_object/74mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/75text.txt create mode 100644 crates/vim/test_data/test_word_text_object/76head.txt create mode 100644 crates/vim/test_data/test_word_text_object/77mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/78text.txt create mode 100644 crates/vim/test_data/test_word_text_object/79head.txt create mode 100644 crates/vim/test_data/test_word_text_object/7head.txt create mode 100644 crates/vim/test_data/test_word_text_object/80mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/81text.txt create mode 100644 crates/vim/test_data/test_word_text_object/82head.txt create mode 100644 crates/vim/test_data/test_word_text_object/83mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/84text.txt create mode 100644 crates/vim/test_data/test_word_text_object/85head.txt create mode 100644 crates/vim/test_data/test_word_text_object/86mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/87text.txt create mode 100644 crates/vim/test_data/test_word_text_object/88head.txt create mode 100644 crates/vim/test_data/test_word_text_object/89mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/8mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/90text.txt create mode 100644 crates/vim/test_data/test_word_text_object/91head.txt create mode 100644 crates/vim/test_data/test_word_text_object/92mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/93text.txt create mode 100644 crates/vim/test_data/test_word_text_object/94head.txt create mode 100644 crates/vim/test_data/test_word_text_object/95mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/96text.txt create mode 100644 crates/vim/test_data/test_word_text_object/97head.txt create mode 100644 crates/vim/test_data/test_word_text_object/98mode.txt create mode 100644 crates/vim/test_data/test_word_text_object/99text.txt create mode 100644 crates/vim/test_data/test_word_text_object/9text.txt diff --git a/Cargo.lock b/Cargo.lock index da2362670d..e5f183c9ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.1.0" dependencies = [ "auto_update", "editor", - "futures", + "futures 0.3.24", "gpui", "language", "project", @@ -52,9 +52,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" dependencies = [ "memchr", ] @@ -113,6 +113,15 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec8ad6edb4840b78c5c3d88de606b22252d552b55f3a4699fbb10fc070ec3049" +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "ansi_term" version = "0.12.1" @@ -124,9 +133,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.58" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" [[package]] name = "arrayref" @@ -148,9 +157,9 @@ checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] name = "ascii" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf56136a5198c7b01a49e3afcbef6cf84597273d298f54432926024107b0109" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" [[package]] name = "assets" @@ -174,9 +183,9 @@ dependencies = [ [[package]] name = "async-channel" -version = "1.6.1" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" +checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" dependencies = [ "concurrent-queue", "event-listener", @@ -184,10 +193,23 @@ dependencies = [ ] [[package]] -name = "async-compression" -version = "0.3.14" +name = "async-compat" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345fd392ab01f746c717b1357165b76f0b67a60192007b234058c9045fdcf695" +checksum = "9b48b4ff0c2026db683dea961cd8ea874737f56cffca86fa84415eaddc51c00d" +dependencies = [ + "futures-core", + "futures-io", + "once_cell", + "pin-project-lite 0.2.9", + "tokio", +] + +[[package]] +name = "async-compression" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" dependencies = [ "flate2", "futures-core", @@ -212,21 +234,23 @@ dependencies = [ [[package]] name = "async-fs" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b3ca4f8ff117c37c278a2f7415ce9be55560b846b5bc4412aaa5d29c1c3dae2" +checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" dependencies = [ "async-lock", + "autocfg 1.1.0", "blocking", "futures-lite", ] [[package]] name = "async-io" -version = "1.7.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5e18f61464ae81cde0a23e713ae8fd299580c54d697a35820cfd0625b8b0e07" +checksum = "83e21f3a490c72b3b0cf44962180e60045de2925d8dff97918f7ee43c8f637c7" dependencies = [ + "autocfg 1.1.0", "concurrent-queue", "futures-lite", "libc", @@ -251,11 +275,12 @@ dependencies = [ [[package]] name = "async-net" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5373304df79b9b4395068fb080369ec7178608827306ce4d081cba51cac551df" +checksum = "4051e67316bc7eff608fe723df5d32ed639946adcd69e07df41fd42a7b411f1f" dependencies = [ "async-io", + "autocfg 1.1.0", "blocking", "futures-lite", ] @@ -265,17 +290,18 @@ name = "async-pipe" version = "0.1.3" source = "git+https://github.com/zed-industries/async-pipe-rs?rev=82d00a04211cf4e1236029aa03e6b6ce2a74c553#82d00a04211cf4e1236029aa03e6b6ce2a74c553" dependencies = [ - "futures", + "futures 0.3.24", "log", ] [[package]] name = "async-process" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2c06e30a24e8c78a3987d07f0930edf76ef35e027e7bdb063fccafdad1f60c" +checksum = "02111fd8655a613c25069ea89fc8d9bb89331fa77486eb3bc059ee757cfa481c" dependencies = [ "async-io", + "autocfg 1.1.0", "blocking", "cfg-if 1.0.0", "event-listener", @@ -338,9 +364,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" +checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" dependencies = [ "proc-macro2", "quote", @@ -435,15 +461,15 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.5.11" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2cc6e8e8c993cb61a005fab8c1e5093a29199b7253b05a6883999312935c1ff" +checksum = "c9e3356844c4d6a6d6467b8da2cffb4a2820be256f50a3a386c9d152bab31043" dependencies = [ "async-trait", "axum-core", "base64", "bitflags", - "bytes", + "bytes 1.2.1", "futures-util", "headers", "http", @@ -470,26 +496,28 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4d047478b986f14a13edad31a009e2e05cb241f9805d0d75e4cba4e129ad4d" +checksum = "d9f0c0a60006f2a293d82d571f635042a72edf927539b7685bd62d361963839b" dependencies = [ "async-trait", - "bytes", + "bytes 1.2.1", "futures-util", "http", "http-body", "mime", + "tower-layer", + "tower-service", ] [[package]] name = "axum-extra" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "277c75e6c814b061ae4947d02335d9659db9771b9950cca670002ae986372f44" +checksum = "69034b3b0fd97923eee2ce8a47540edb21e07f48f87f67d44bb4271cec622bdb" dependencies = [ "axum", - "bytes", + "bytes 1.2.1", "futures-util", "http", "mime", @@ -505,16 +533,16 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.65" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" dependencies = [ "addr2line", "cc", "cfg-if 1.0.0", "libc", - "miniz_oxide 0.5.3", - "object", + "miniz_oxide 0.5.4", + "object 0.29.0", "rustc-demangle", ] @@ -526,9 +554,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "base64ct" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdca834647821e0b13d9539a8634eb62d3501b6b6c2cec1722786ee6671b851" +checksum = "ea2b2456fd614d856680dcd9fcc660a51a820fa09daef2e49772b56a193c8474" [[package]] name = "bincode" @@ -585,9 +613,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ "generic-array", ] @@ -645,15 +673,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.10.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" +checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" [[package]] name = "bytemuck" -version = "1.10.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c53dfa917ec274df8ed3c572698f381a24eef2efba9492d797301b72b6db408a" +checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da" [[package]] name = "byteorder" @@ -661,6 +689,16 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +dependencies = [ + "byteorder", + "iovec", +] + [[package]] name = "bytes" version = "1.2.1" @@ -758,12 +796,12 @@ dependencies = [ "bindgen", "block", "byteorder", - "bytes", + "bytes 1.2.1", "cocoa", "core-foundation", "core-graphics", "foreign-types", - "futures", + "futures 0.3.24", "gpui", "hmac 0.12.1", "jwt", @@ -774,7 +812,7 @@ dependencies = [ "parking_lot 0.11.2", "postage", "serde", - "sha2 0.10.2", + "sha2 0.10.6", "simplelog", ] @@ -825,21 +863,23 @@ dependencies = [ "postage", "settings", "theme", - "time 0.3.11", + "time 0.3.15", "util", "workspace", ] [[package]] name = "chrono" -version = "0.4.19" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" dependencies = [ - "libc", + "iana-time-zone", + "js-sys", "num-integer", "num-traits", "time 0.1.44", + "wasm-bindgen", "winapi 0.3.9", ] @@ -860,9 +900,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.3.3" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a050e2153c5be08febd6734e29298e844fdb0fa21aeddd63b4eb7baa106c69b" +checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" dependencies = [ "glob", "libc", @@ -886,9 +926,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.8" +version = "3.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83" +checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" dependencies = [ "atty", "bitflags", @@ -898,14 +938,14 @@ dependencies = [ "once_cell", "strsim 0.10.0", "termcolor", - "textwrap 0.15.0", + "textwrap 0.15.1", ] [[package]] name = "clap_derive" -version = "3.2.7" +version = "3.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" dependencies = [ "heck 0.4.0", "proc-macro-error", @@ -928,7 +968,7 @@ name = "cli" version = "0.1.0" dependencies = [ "anyhow", - "clap 3.2.8", + "clap 3.2.22", "core-foundation", "core-services", "dirs 3.0.2", @@ -946,7 +986,7 @@ dependencies = [ "async-tungstenite", "collections", "db", - "futures", + "futures 0.3.24", "gpui", "image", "isahc", @@ -961,11 +1001,11 @@ dependencies = [ "sum_tree", "tempfile", "thiserror", - "time 0.3.11", + "time 0.3.15", "tiny_http", "url", "util", - "uuid 1.1.2", + "uuid 1.2.1", ] [[package]] @@ -1013,6 +1053,16 @@ dependencies = [ "objc", ] +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "collab" version = "0.1.0" @@ -1023,14 +1073,14 @@ dependencies = [ "axum", "axum-extra", "base64", - "clap 3.2.8", + "clap 3.2.22", "client", "collections", "ctor", "editor", "env_logger", "envy", - "futures", + "futures 0.3.24", "git", "gpui", "hyper", @@ -1053,7 +1103,7 @@ dependencies = [ "sha-1 0.9.8", "sqlx", "theme", - "time 0.3.11", + "time 0.3.15", "tokio", "tokio-tungstenite", "toml", @@ -1101,9 +1151,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "1.2.2" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" dependencies = [ "cache-padded", ] @@ -1116,7 +1166,7 @@ dependencies = [ "client", "collections", "editor", - "futures", + "futures 0.3.24", "fuzzy", "gpui", "language", @@ -1140,7 +1190,7 @@ dependencies = [ "client", "collections", "editor", - "futures", + "futures 0.3.24", "fuzzy", "gpui", "language", @@ -1236,27 +1286,27 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" dependencies = [ "libc", ] [[package]] name = "cranelift-bforest" -version = "0.85.1" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7901fbba05decc537080b07cb3f1cadf53be7b7602ca8255786288a8692ae29a" +checksum = "749d0d6022c9038dccf480bdde2a38d435937335bf2bb0f14e815d94517cdce8" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.85.1" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ba1b45d243a4a28e12d26cd5f2507da74e77c45927d40de8b6ffbf088b46b5" +checksum = "e94370cc7b37bf652ccd8bb8f09bd900997f7ccf97520edfc75554bb5c4abbea" dependencies = [ "cranelift-bforest", "cranelift-codegen-meta", @@ -1272,33 +1322,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.85.1" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54cc30032171bf230ce22b99c07c3a1de1221cb5375bd6dbe6dbe77d0eed743c" +checksum = "e0a3cea8fdab90e44018c5b9a1dfd460d8ee265ac354337150222a354628bdb6" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.85.1" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23f2672426d2bb4c9c3ef53e023076cfc4d8922f0eeaebaf372c92fae8b5c69" +checksum = "5ac72f76f2698598951ab26d8c96eaa854810e693e7dd52523958b5909fde6b2" [[package]] name = "cranelift-entity" -version = "0.85.1" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "886c59a5e0de1f06dbb7da80db149c75de10d5e2caca07cdd9fef8a5918a6336" +checksum = "09eaeacfcd2356fe0e66b295e8f9d59fdd1ac3ace53ba50de14d628ec902f72d" dependencies = [ "serde", ] [[package]] name = "cranelift-frontend" -version = "0.85.1" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace74eeca11c439a9d4ed1a5cb9df31a54cd0f7fbddf82c8ce4ea8e9ad2a8fe0" +checksum = "dba69c9980d5ffd62c18a2bde927855fcd7c8dc92f29feaf8636052662cbd99c" dependencies = [ "cranelift-codegen", "log", @@ -1308,15 +1358,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.85.1" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db1ae52a5cc2cad0d86fdd3dcb16b7217d2f1e65ab4f5814aa4f014ad335fa43" +checksum = "d2920dc1e05cac40304456ed3301fde2c09bd6a9b0210bcfa2f101398d628d5b" [[package]] name = "cranelift-native" -version = "0.85.1" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dadcfb7852900780d37102bce5698bcd401736403f07b52e714ff7a180e0e22f" +checksum = "f04dfa45f9b2a6f587c564d6b63388e00cd6589d2df6ea2758cf79e1a13285e6" dependencies = [ "cranelift-codegen", "libc", @@ -1325,9 +1375,9 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.85.1" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c84e3410960389110b88f97776f39f6d2c8becdaa4cd59e390e6b76d9d0e7190" +checksum = "31a46513ae6f26f3f267d8d75b5373d555fbbd1e68681f348d99df43f747ec54" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1375,47 +1425,46 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.10", + "crossbeam-utils 0.8.12", ] [[package]] name = "crossbeam-deque" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" dependencies = [ "cfg-if 1.0.0", "crossbeam-epoch", - "crossbeam-utils 0.8.10", + "crossbeam-utils 0.8.12", ] [[package]] name = "crossbeam-epoch" -version = "0.9.9" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d" +checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" dependencies = [ "autocfg 1.1.0", "cfg-if 1.0.0", - "crossbeam-utils 0.8.10", + "crossbeam-utils 0.8.12", "memoffset", - "once_cell", "scopeguard", ] [[package]] name = "crossbeam-queue" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2" +checksum = "1cd42583b04998a5363558e5f9291ee5a5ff6b49944332103f251e7479a82aa7" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.10", + "crossbeam-utils 0.8.12", ] [[package]] @@ -1431,19 +1480,18 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.10" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83" +checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" dependencies = [ "cfg-if 1.0.0", - "once_cell", ] [[package]] name = "crypto-common" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5999502d32b9c48d492abe66392408144895020ec4709e549e840799f3bb74c0" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", @@ -1461,9 +1509,9 @@ dependencies = [ [[package]] name = "ctor" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c" +checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" dependencies = [ "quote", "syn", @@ -1471,9 +1519,9 @@ dependencies = [ [[package]] name = "curl" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d855aeef205b43f65a5001e0997d81f8efca7badad4fad7d897aa7f0d0651f" +checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22" dependencies = [ "curl-sys", "libc", @@ -1486,9 +1534,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.55+curl-7.83.1" +version = "0.4.56+curl-7.83.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23734ec77368ec583c2e61dd3f0b0e5c98b93abe6d2a004ca06b91dd7e3e2762" +checksum = "6093e169dd4de29e468fa649fbae11cdcd5551c81fe5bf1b0677adad7ef3d26f" dependencies = [ "cc", "libc", @@ -1500,6 +1548,50 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "cxx" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f39818dcfc97d45b03953c1292efc4e80954e1583c4aa770bac1383e2310a4" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e580d70777c116df50c390d1211993f62d40302881e54d4b79727acb83d0199" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56a46460b88d1cec95112c8c363f0e2c39afdb237f60583b0b36343bf627ea9c" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747b608fecf06b0d72d440f27acc99288207324b793be2c17991839f3d4995ea" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "data-url" version = "0.1.1" @@ -1534,13 +1626,13 @@ dependencies = [ [[package]] name = "dhat" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47003dc9f6368a88e85956c3b2573a7e6872746a3e5d762a8885da3a136a0381" +checksum = "0684eaa19a59be283a6f99369917b679bd4d1d06604b2eb2e2f87b4bbd67668d" dependencies = [ "backtrace", "lazy_static", - "parking_lot 0.11.2", + "parking_lot 0.12.1", "rustc-hash", "serde", "serde_json", @@ -1579,11 +1671,11 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" dependencies = [ - "block-buffer 0.10.2", + "block-buffer 0.10.3", "crypto-common", "subtle", ] @@ -1649,10 +1741,13 @@ dependencies = [ ] [[package]] -name = "dotenv" -version = "0.15.0" +name = "dotenvy" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" +checksum = "ed9155c8f4dc55c7470ae9da3f63c6785245093b3f6aeb0f5bf2e968efbba314" +dependencies = [ + "dirs 4.0.0", +] [[package]] name = "drag_and_drop" @@ -1676,9 +1771,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "140206b78fb2bc3edbcfc9b5ccbd0b30699cfe8d348b8b31b330e47df5291a5a" +checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2" [[package]] name = "easy-parallel" @@ -1697,7 +1792,7 @@ dependencies = [ "context_menu", "ctor", "env_logger", - "futures", + "futures 0.3.24", "fuzzy", "git", "gpui", @@ -1732,9 +1827,9 @@ dependencies = [ [[package]] name = "either" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "encoding_rs" @@ -1747,9 +1842,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272" dependencies = [ "atty", "humantime", @@ -1769,9 +1864,9 @@ dependencies = [ [[package]] name = "erased-serde" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81d013529d5574a60caeda29e179e695125448e5de52e3874f7b4c1d7360e18e" +checksum = "54558e0ba96fbe24280072642eceb9d7d442e32c7ec0ea9e7ecd7b4ea2cf4e11" dependencies = [ "serde", ] @@ -1818,9 +1913,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "expat-sys" @@ -1840,9 +1935,9 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fastrand" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" dependencies = [ "instant", ] @@ -1890,7 +1985,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" dependencies = [ "crc32fast", - "miniz_oxide 0.5.3", + "miniz_oxide 0.5.4", ] [[package]] @@ -1963,11 +2058,10 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" dependencies = [ - "matches", "percent-encoding", ] @@ -2046,9 +2140,15 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" -version = "0.3.21" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" + +[[package]] +name = "futures" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" dependencies = [ "futures-channel", "futures-core", @@ -2061,9 +2161,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" dependencies = [ "futures-core", "futures-sink", @@ -2071,15 +2171,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" +checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" [[package]] name = "futures-executor" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" dependencies = [ "futures-core", "futures-task", @@ -2099,9 +2199,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" +checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" [[package]] name = "futures-lite" @@ -2120,9 +2220,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" dependencies = [ "proc-macro2", "quote", @@ -2131,22 +2231,23 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" +checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" [[package]] name = "futures-task" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" +checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" [[package]] name = "futures-util" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" dependencies = [ + "futures 0.1.31", "futures-channel", "futures-core", "futures-io", @@ -2157,6 +2258,7 @@ dependencies = [ "pin-project-lite 0.2.9", "pin-utils", "slab", + "tokio-io", ] [[package]] @@ -2178,9 +2280,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.5" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", "version_check", @@ -2220,9 +2322,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" dependencies = [ "fallible-iterator", "indexmap", @@ -2237,7 +2339,7 @@ dependencies = [ "async-trait", "clock", "collections", - "futures", + "futures 0.3.24", "git2", "lazy_static", "log", @@ -2315,7 +2417,7 @@ dependencies = [ "etagere", "font-kit", "foreign-types", - "futures", + "futures 0.3.24", "gpui_macros", "image", "lazy_static", @@ -2340,7 +2442,7 @@ dependencies = [ "smallvec", "smol", "sum_tree", - "time 0.3.11", + "time 0.3.15", "tiny-skia", "tree-sitter", "usvg", @@ -2359,11 +2461,11 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" dependencies = [ - "bytes", + "bytes 1.2.1", "fnv", "futures-core", "futures-sink", @@ -2372,7 +2474,7 @@ dependencies = [ "indexmap", "slab", "tokio", - "tokio-util 0.7.3", + "tokio-util 0.7.4", "tracing", ] @@ -2387,36 +2489,36 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ "ahash", ] [[package]] name = "hashlink" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d452c155cb93fecdfb02a73dd57b5d8e442c2063bd7aac72f1bc5e4263a43086" +checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa" dependencies = [ - "hashbrown 0.12.1", + "hashbrown 0.12.3", ] [[package]] name = "headers" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d" +checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" dependencies = [ "base64", "bitflags", - "bytes", + "bytes 1.2.1", "headers-core", "http", "httpdate", "mime", - "sha-1 0.10.0", + "sha1", ] [[package]] @@ -2457,9 +2559,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.3" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d37fb7dc756218a0559bfc21e4381f03cbb696cdaf959e7e95e927496f0564cd" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" dependencies = [ "libc", ] @@ -2495,7 +2597,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.3", + "digest 0.10.5", ] [[package]] @@ -2504,7 +2606,7 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ - "bytes", + "bytes 1.2.1", "fnv", "itoa", ] @@ -2515,7 +2617,7 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ - "bytes", + "bytes 1.2.1", "http", "pin-project-lite 0.2.9", ] @@ -2528,9 +2630,9 @@ checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" [[package]] name = "httparse" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" @@ -2546,11 +2648,11 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.19" +version = "0.14.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" dependencies = [ - "bytes", + "bytes 1.2.1", "futures-channel", "futures-core", "futures-util", @@ -2586,7 +2688,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ - "bytes", + "bytes 1.2.1", "hyper", "native-tls", "tokio", @@ -2594,12 +2696,35 @@ dependencies = [ ] [[package]] -name = "idna" -version = "0.2.3" +name = "iana-time-zone" +version = "0.1.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "f5a6ef98976b22b3b7f2f3a806f858cb862044cfa66805aa3ad84cb3d3b785ed" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi 0.3.9", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fde6edd6cef363e9359ed3c98ba64590ba9eecba2293eb5a723ab32aee8926aa" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] @@ -2610,7 +2735,7 @@ version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" dependencies = [ - "crossbeam-utils 0.8.10", + "crossbeam-utils 0.8.12", "globset", "lazy_static", "log", @@ -2648,15 +2773,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg 1.1.0", - "hashbrown 0.12.1", + "hashbrown 0.12.3", "serde", ] [[package]] name = "indoc" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05a0bd019339e5d968b37855180087b7b9d512c5046fbd244cf8c95687927d6e" +checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3" [[package]] name = "instant" @@ -2727,7 +2852,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c89a757e762896bdbdfadf2860d0f8b0cea5e363d8cf3e7bdfeb63d1d976352" dependencies = [ - "hermit-abi 0.2.3", + "hermit-abi 0.2.6", "io-lifetimes", "rustix", "winapi 0.3.9", @@ -2741,7 +2866,7 @@ checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9" dependencies = [ "async-channel", "castaway", - "crossbeam-utils 0.8.10", + "crossbeam-utils 0.8.12", "curl", "curl-sys", "encoding_rs", @@ -2762,18 +2887,18 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" [[package]] name = "ittapi-rs" @@ -2786,9 +2911,9 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" dependencies = [ "libc", ] @@ -2817,9 +2942,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.58" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" dependencies = [ "wasm-bindgen", ] @@ -2838,11 +2963,11 @@ checksum = "6204285f77fe7d9784db3fdc449ecce1a0114927a51d5a41c4c7a292011c015f" dependencies = [ "base64", "crypto-common", - "digest 0.10.3", + "digest 0.10.5", "hmac 0.12.1", "serde", "serde_json", - "sha2 0.10.2", + "sha2 0.10.6", ] [[package]] @@ -2876,7 +3001,7 @@ dependencies = [ "collections", "ctor", "env_logger", - "futures", + "futures 0.3.24", "fuzzy", "git", "gpui", @@ -2931,9 +3056,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" [[package]] name = "libgit2-sys" @@ -2959,9 +3084,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db" +checksum = "292a948cd991e376cf75541fe5b97a1081d713c618b4f1b9500f8844e49eb565" [[package]] name = "libnghttp2-sys" @@ -3008,6 +3133,15 @@ dependencies = [ "safemem", ] +[[package]] +name = "link-cplusplus" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +dependencies = [ + "cc", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -3037,7 +3171,7 @@ dependencies = [ "anyhow", "core-foundation", "core-graphics", - "futures", + "futures 0.3.24", "media", "parking_lot 0.11.2", "serde", @@ -3046,9 +3180,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ "autocfg 1.1.0", "scopeguard", @@ -3074,7 +3208,7 @@ dependencies = [ "collections", "ctor", "env_logger", - "futures", + "futures 0.3.24", "gpui", "log", "lsp-types", @@ -3153,11 +3287,11 @@ checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "md-5" -version = "0.10.1" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" dependencies = [ - "digest 0.10.3", + "digest 0.10.5", ] [[package]] @@ -3167,7 +3301,7 @@ dependencies = [ "anyhow", "bindgen", "block", - "bytes", + "bytes 1.2.1", "core-foundation", "foreign-types", "metal", @@ -3261,9 +3395,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" dependencies = [ "adler", ] @@ -3437,6 +3571,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi 0.3.9", +] + [[package]] name = "num-bigint" version = "0.4.3" @@ -3527,6 +3671,21 @@ dependencies = [ "libc", ] +[[package]] +name = "nvim-rs" +version = "0.5.0" +source = "git+https://github.com/KillTheMule/nvim-rs?branch=master#d701c2790dcb2579f8f4d7003ba30e2100a7d25b" +dependencies = [ + "async-trait", + "futures 0.3.24", + "log", + "parity-tokio-ipc", + "rmp", + "rmpv", + "tokio", + "tokio-util 0.7.4", +] + [[package]] name = "objc" version = "0.2.7" @@ -3559,10 +3718,19 @@ dependencies = [ ] [[package]] -name = "once_cell" -version = "1.13.0" +name = "object" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" [[package]] name = "opaque-debug" @@ -3572,9 +3740,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.40" +version = "0.10.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb81a6430ac911acb25fe5ac8f1d2af1b4ea8a4fdfda0f1ee4292af2e2d8eb0e" +checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -3604,9 +3772,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.74" +version = "0.9.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835363342df5fba8354c5b453325b110ffd54044e588c539cf2f20a8014e4cb1" +checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" dependencies = [ "autocfg 1.1.0", "cc", @@ -3626,9 +3794,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.1.0" +version = "6.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" [[package]] name = "outline" @@ -3647,6 +3815,26 @@ dependencies = [ "workspace", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parity-tokio-ipc" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9981e32fb75e004cc148f5fb70342f393830e0a4aa62e3cc93b50976218d42b6" +dependencies = [ + "futures 0.3.24", + "libc", + "log", + "rand 0.7.3", + "tokio", + "winapi 0.3.9", +] + [[package]] name = "parking" version = "2.0.0" @@ -3708,15 +3896,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77e0b28ace46c5a396546bcf443bf422b57049617433d8854227352a4a9b24e7" dependencies = [ "base64ct", - "rand_core 0.6.3", + "rand_core 0.6.4", "subtle", ] [[package]] name = "paste" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" +checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" [[package]] name = "pathfinder_color" @@ -3774,16 +3962,17 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.1.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +checksum = "dbc7bc69c062e492337d74d59b120c274fd3d261b6bf6d3207d499b4b379c41a" dependencies = [ + "thiserror", "ucd-trie", ] @@ -3821,18 +4010,18 @@ checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" [[package]] name = "pin-project" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78203e83c48cffbe01e4a2d35d566ca4de445d79a85372fc64e378bfc812a260" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "710faf75e1b33345361201d36d04e98ac1ed8909151a017ed384700836104c74" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ "proc-macro2", "quote", @@ -3873,7 +4062,7 @@ dependencies = [ "indexmap", "line-wrap", "serde", - "time 0.3.11", + "time 0.3.15", "xml-rs", ] @@ -3926,10 +4115,11 @@ dependencies = [ [[package]] name = "polling" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" +checksum = "899b00b9c8ab553c743b3e11e87c5c7d423b2a2de229ba95b24a756344748011" dependencies = [ + "autocfg 1.1.0", "cfg-if 1.0.0", "libc", "log", @@ -3951,7 +4141,7 @@ checksum = "a63d25391d04a097954b76aba742b6b5b74f213dfe3dbaeeb36e8ddc1c657f0b" dependencies = [ "atomic", "crossbeam-queue", - "futures", + "futures 0.3.24", "log", "pin-project", "pollster", @@ -3991,9 +4181,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.40" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" dependencies = [ "unicode-ident", ] @@ -4021,7 +4211,7 @@ dependencies = [ "collections", "db", "fsevent", - "futures", + "futures 0.3.24", "fuzzy", "git", "gpui", @@ -4041,7 +4231,7 @@ dependencies = [ "serde", "serde_json", "settings", - "sha2 0.10.2", + "sha2 0.10.6", "similar", "smol", "sum_tree", @@ -4059,7 +4249,7 @@ version = "0.1.0" dependencies = [ "context_menu", "editor", - "futures", + "futures 0.3.24", "gpui", "menu", "postage", @@ -4078,7 +4268,7 @@ version = "0.1.0" dependencies = [ "anyhow", "editor", - "futures", + "futures 0.3.24", "fuzzy", "gpui", "language", @@ -4096,9 +4286,9 @@ dependencies = [ [[package]] name = "prometheus" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cface98dfa6d645ea4c789839f176e4b072265d085bfcc48eaa8d137f58d3c39" +checksum = "45c8babc29389186697fe5a2a4859d697825496b83db5d0b65271cdc0488e88c" dependencies = [ "cfg-if 1.0.0", "fnv", @@ -4115,7 +4305,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020" dependencies = [ - "bytes", + "bytes 1.2.1", "prost-derive 0.8.0", ] @@ -4125,7 +4315,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" dependencies = [ - "bytes", + "bytes 1.2.1", "prost-derive 0.9.0", ] @@ -4135,7 +4325,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62941722fb675d463659e49c4f3fe1fe792ff24fe5bbaa9c08cd3b98a1c354f5" dependencies = [ - "bytes", + "bytes 1.2.1", "heck 0.3.3", "itertools", "lazy_static", @@ -4181,30 +4371,30 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a" dependencies = [ - "bytes", + "bytes 1.2.1", "prost 0.9.0", ] [[package]] name = "protobuf" -version = "2.27.1" +version = "2.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf7e6d18738ecd0902d30d1ad232c9125985a3422929b16c65517b38adc14f96" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" [[package]] name = "psm" -version = "0.1.19" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd89aa18fbf9533a581355a22438101fe9c2ed8c9e2f0dcf520552a3afddf2" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" dependencies = [ "cc", ] [[package]] name = "pulldown-cmark" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34f197a544b0c9ab3ae46c359a7ec9cbbb5c7bf97054266fecb7ead794a181d6" +checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" dependencies = [ "bitflags", "memchr", @@ -4213,9 +4403,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] @@ -4254,7 +4444,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha 0.3.1", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -4274,7 +4464,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -4303,9 +4493,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom 0.2.7", ] @@ -4337,9 +4527,9 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" dependencies = [ - "crossbeam-channel 0.5.5", + "crossbeam-channel 0.5.6", "crossbeam-deque", - "crossbeam-utils 0.8.10", + "crossbeam-utils 0.8.12", "num_cpus", ] @@ -4360,9 +4550,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] @@ -4439,12 +4629,12 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.11" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" +checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" dependencies = [ "base64", - "bytes", + "bytes 1.2.1", "encoding_rs", "futures-core", "futures-util", @@ -4455,10 +4645,10 @@ dependencies = [ "hyper-tls", "ipnet", "js-sys", - "lazy_static", "log", "mime", "native-tls", + "once_cell", "percent-encoding", "pin-project-lite 0.2.9", "serde", @@ -4492,9 +4682,9 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3b221de559e4a29df3b957eec92bc0de6bc8eaf6ca9cfed43e5e1d67ff65a34" +checksum = "3603b7d71ca82644f79b5a06d1220e9a58ede60bd32255f698cb1af8838b8db3" dependencies = [ "bytemuck", ] @@ -4514,6 +4704,27 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "rmp" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44519172358fd6d58656c86ab8e7fbc9e1490c3e8f14d35ed78ca0dd07403c9f" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmpv" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de8813b3a2f95c5138fe5925bfb8784175d88d6bff059ba8ce090aa891319754" +dependencies = [ + "num-traits", + "rmp", +] + [[package]] name = "rocksdb" version = "0.18.0" @@ -4544,7 +4755,7 @@ dependencies = [ "collections", "ctor", "env_logger", - "futures", + "futures 0.3.24", "gpui", "parking_lot 0.11.2", "prost 0.8.0", @@ -4582,9 +4793,9 @@ dependencies = [ [[package]] name = "rust-embed" -version = "6.4.0" +version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a17e5ac65b318f397182ae94e532da0ba56b88dd1200b774715d36c4943b1c3" +checksum = "e26934cd67a1da1165efe61cba4047cc1b4a526019da609fcce13a1000afb5fa" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -4593,9 +4804,9 @@ dependencies = [ [[package]] name = "rust-embed-impl" -version = "6.2.0" +version = "6.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e763e24ba2bf0c72bc6be883f967f794a019fafd1b86ba1daff9c91a7edd30" +checksum = "e35d7b402e273544cc08e0824aa3404333fab8a90ac43589d3d5b72f4b346e12" dependencies = [ "proc-macro2", "quote", @@ -4606,12 +4817,12 @@ dependencies = [ [[package]] name = "rust-embed-utils" -version = "7.2.0" +version = "7.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756feca3afcbb1487a1d01f4ecd94cf8ec98ea074c55a69e7136d29fb6166029" +checksum = "c1669d81dfabd1b5f8e2856b8bbe146c6192b0ba22162edc738ac0a5de18f054" dependencies = [ "globset", - "sha2 0.9.9", + "sha2 0.10.6", "walkdir", ] @@ -4679,9 +4890,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7522c9de787ff061458fe9a829dc790a3f5b22dc571694fc5883f448b94d9a9" +checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" dependencies = [ "base64", ] @@ -4704,9 +4915,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] name = "safe_arch" @@ -4753,9 +4964,9 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1847b767a3d62d95cbf3d8a9f0e421cf57a0d8aa4f411d4b16525afb0284d4ed" +checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307" dependencies = [ "dyn-clone", "schemars_derive", @@ -4765,9 +4976,9 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4d7e1b012cb3d9129567661a63755ea4b8a7386d339dc945ae187e403c6743" +checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9" dependencies = [ "proc-macro2", "quote", @@ -4787,6 +4998,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scratch" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" + [[package]] name = "scrypt" version = "0.7.0" @@ -4852,9 +5069,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.6.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" +checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" dependencies = [ "bitflags", "core-foundation", @@ -4899,18 +5116,18 @@ checksum = "5a9f47faea3cad316faa914d013d24f471cd90bfca1a0c70f05a3f42c6441e99" [[package]] name = "serde" -version = "1.0.138" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.138" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" dependencies = [ "proc-macro2", "quote", @@ -4939,9 +5156,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.82" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" dependencies = [ "indexmap", "itoa", @@ -4951,18 +5168,18 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7868ad3b8196a8a0aea99a8220b124278ee5320a55e4fde97794b6f85b1a377" +checksum = "184c643044780f7ceb59104cef98a5a6f12cb2288a7bc701ab93a362b49fd47d" dependencies = [ "serde", ] [[package]] name = "serde_repr" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ad84e47328a31223de7fed7a4f5087f2d6ddfe586cf3ca25b7a165bc0a5aed" +checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca" dependencies = [ "proc-macro2", "quote", @@ -4983,9 +5200,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.8.24" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707d15895415db6628332b737c838b88c598522e4dc70647e59b72312924aebc" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" dependencies = [ "indexmap", "ryu", @@ -5053,7 +5270,18 @@ checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.3", + "digest 0.10.5", +] + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.5", ] [[package]] @@ -5071,13 +5299,13 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.2" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.3", + "digest 0.10.5", ] [[package]] @@ -5091,11 +5319,11 @@ dependencies = [ [[package]] name = "shellexpand" -version = "2.1.0" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83bdb7831b2d85ddf4a7b148aa19d0587eddbe8671a436b7bd1182eaad0f2829" +checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4" dependencies = [ - "dirs-next", + "dirs 4.0.0", ] [[package]] @@ -5181,9 +5409,12 @@ checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" [[package]] name = "slab" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg 1.1.0", +] [[package]] name = "slice-group-by" @@ -5204,9 +5435,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "smol" @@ -5246,9 +5477,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.4" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" dependencies = [ "libc", "winapi 0.3.9", @@ -5268,9 +5499,9 @@ checksum = "be6c3f39c37a4283ee4b43d1311c828f2e1fb0541e76ea0cb1a2abd9ef2f5b3b" [[package]] name = "sqlformat" -version = "0.1.8" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b7922be017ee70900be125523f38bdd644f4f06a1b16e8fa5a8ee8c34bffd4" +checksum = "f87e292b4291f154971a43c3774364e2cbcaec599d3f5bf6fa9d122885dbc38a" dependencies = [ "itertools", "nom", @@ -5279,9 +5510,9 @@ dependencies = [ [[package]] name = "sqlx" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f82cbe94f41641d6c410ded25bbf5097c240cefdf8e3b06d04198d0a96af6a4" +checksum = "9249290c05928352f71c077cc44a464d880c63f26f7534728cca008e135c0428" dependencies = [ "sqlx-core", "sqlx-macros", @@ -5289,19 +5520,20 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b69bf218860335ddda60d6ce85ee39f6cf6e5630e300e19757d1de15886a093" +checksum = "dcbc16ddba161afc99e14d1713a453747a2b07fc097d2009f4c300ec99286105" dependencies = [ "ahash", "atoi", "base64", "bitflags", "byteorder", - "bytes", + "bytes 1.2.1", "crc", "crossbeam-queue", "dirs 4.0.0", + "dotenvy", "either", "event-listener", "futures-channel", @@ -5326,34 +5558,34 @@ dependencies = [ "rustls-pemfile", "serde", "serde_json", - "sha-1 0.10.0", - "sha2 0.10.2", + "sha1", + "sha2 0.10.6", "smallvec", "sqlformat", "sqlx-rt", "stringprep", "thiserror", - "time 0.3.11", + "time 0.3.15", "tokio-stream", "url", - "uuid 1.1.2", - "webpki-roots 0.22.3", + "uuid 1.2.1", + "webpki-roots 0.22.5", "whoami", ] [[package]] name = "sqlx-macros" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40c63177cf23d356b159b60acd27c54af7423f1736988502e36bae9a712118f" +checksum = "b850fa514dc11f2ee85be9d055c512aa866746adfacd1cb42d867d68e6a5b0d9" dependencies = [ - "dotenv", + "dotenvy", "either", "heck 0.4.0", "once_cell", "proc-macro2", "quote", - "sha2 0.10.2", + "sha2 0.10.6", "sqlx-core", "sqlx-rt", "syn", @@ -5362,9 +5594,9 @@ dependencies = [ [[package]] name = "sqlx-rt" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "874e93a365a598dc3dadb197565952cb143ae4aa716f7bcc933a8d836f6bf89f" +checksum = "24c5b2d25fa654cc5f841750b8e1cdedbe21189bf9a9382ee90bfa9dd3562396" dependencies = [ "once_cell", "tokio", @@ -5459,9 +5691,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.98" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" dependencies = [ "proc-macro2", "quote", @@ -5551,7 +5783,7 @@ dependencies = [ "context_menu", "dirs 4.0.0", "editor", - "futures", + "futures 0.3.24", "gpui", "itertools", "lazy_static", @@ -5607,9 +5839,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" [[package]] name = "theme" @@ -5644,18 +5876,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.31" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ "proc-macro2", "quote", @@ -5701,9 +5933,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.11" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217" +checksum = "d634a985c4d4238ec39cacaed2e7ae552fbd3c476b552c1deac3021b7d7eaf0c" dependencies = [ "itoa", "libc", @@ -5762,16 +5994,16 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.19.2" +version = "1.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" +checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" dependencies = [ - "bytes", + "autocfg 1.1.0", + "bytes 1.2.1", "libc", "memchr", "mio 0.8.4", "num_cpus", - "once_cell", "parking_lot 0.12.1", "pin-project-lite 0.2.9", "signal-hook-registry", @@ -5780,6 +6012,17 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "tokio-io" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.31", + "log", +] + [[package]] name = "tokio-io-timeout" version = "1.2.0" @@ -5824,9 +6067,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9" +checksum = "f6edf2d6bc038a43d31353570e27270603f4648d18f5ed10c0e179abe43255af" dependencies = [ "futures-core", "pin-project-lite 0.2.9", @@ -5835,14 +6078,14 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.17.1" +version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06cda1232a49558c46f8a504d5b93101d42c0bf7f911f12a105ba48168f821ae" +checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" dependencies = [ "futures-util", "log", "tokio", - "tungstenite 0.17.2", + "tungstenite 0.17.3", ] [[package]] @@ -5851,7 +6094,7 @@ version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" dependencies = [ - "bytes", + "bytes 1.2.1", "futures-core", "futures-sink", "log", @@ -5861,12 +6104,13 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" dependencies = [ - "bytes", + "bytes 1.2.1", "futures-core", + "futures-io", "futures-sink", "pin-project-lite 0.2.9", "tokio", @@ -5891,7 +6135,7 @@ dependencies = [ "async-stream", "async-trait", "base64", - "bytes", + "bytes 1.2.1", "futures-core", "futures-util", "h2", @@ -5927,7 +6171,7 @@ dependencies = [ "rand 0.8.5", "slab", "tokio", - "tokio-util 0.7.3", + "tokio-util 0.7.4", "tower-layer", "tower-service", "tracing", @@ -5940,7 +6184,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" dependencies = [ "bitflags", - "bytes", + "bytes 1.2.1", "futures-core", "futures-util", "http", @@ -5966,9 +6210,9 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.35" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if 1.0.0", "log", @@ -5979,9 +6223,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", @@ -5990,9 +6234,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", "valuable", @@ -6031,12 +6275,12 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.14" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a713421342a5a666b7577783721d3117f1b69a393df803ee17bb73b1e122a59" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" dependencies = [ - "ansi_term", "matchers", + "nu-ansi-term", "once_cell", "regex", "serde", @@ -6061,9 +6305,9 @@ dependencies = [ [[package]] name = "tree-sitter-c" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bdc5574c6cbc39c409246caeb1dd4d3c4bd6d30d4e9b399776086c20365fd24" +checksum = "cca211f4827d4b4dc79f388bf67b6fa3bc8a8cfa642161ef24f99f371ba34c7b" dependencies = [ "cc", "tree-sitter", @@ -6219,7 +6463,7 @@ checksum = "6ad3713a14ae247f22a728a0456a545df14acf3867f905adff84be99e23b3ad1" dependencies = [ "base64", "byteorder", - "bytes", + "bytes 1.2.1", "http", "httparse", "log", @@ -6232,13 +6476,13 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.17.2" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96a2dea40e7570482f28eb57afbe42d97551905da6a9400acc5c328d24004f5" +checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" dependencies = [ "base64", "byteorder", - "bytes", + "bytes 1.2.1", "http", "httparse", "log", @@ -6257,9 +6501,9 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "ucd-trie" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89570599c4fe5585de2b388aab47e99f7fa4e9238a1399f707a02e356058141c" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "unicase" @@ -6296,30 +6540,30 @@ checksum = "7f9af028e052a610d99e066b33304625dea9613170a2563314490a4e6ec5cf7f" [[package]] name = "unicode-ident" -version = "1.0.1" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[package]] name = "unicode-normalization" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "unicode-script" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58dd944fd05f2f0b5c674917aea8a4df6af84f2d8de3fe8d988b95d28fb8fb09" +checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc" [[package]] name = "unicode-segmentation" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" [[package]] name = "unicode-vo" @@ -6329,15 +6573,15 @@ checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" [[package]] name = "unicode-width" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "unicode-xid" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "unicode_categories" @@ -6347,9 +6591,9 @@ checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" [[package]] name = "unindent" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52fee519a3e570f7df377a06a1a7775cdbfb7aa460be7e08de2b1f0e69973a44" +checksum = "58ee9362deb4a96cef4d437d1ad49cffc9b9e92d202b6995674e928ce684f112" [[package]] name = "untrusted" @@ -6359,13 +6603,12 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.2.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", "idna", - "matches", "percent-encoding", "serde", ] @@ -6414,7 +6657,7 @@ name = "util" version = "0.1.0" dependencies = [ "anyhow", - "futures", + "futures 0.3.24", "git2", "lazy_static", "log", @@ -6434,9 +6677,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.1.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" +checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" dependencies = [ "getrandom 0.2.7", ] @@ -6484,6 +6727,8 @@ name = "vim" version = "0.1.0" dependencies = [ "assets", + "async-compat", + "async-trait", "collections", "command_palette", "editor", @@ -6492,10 +6737,13 @@ dependencies = [ "itertools", "language", "log", + "nvim-rs", "project", "search", "serde", + "serde_json", "settings", + "tokio", "util", "workspace", ] @@ -6567,9 +6815,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi-cap-std-sync" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1c4e73ed64b92ae87b416f4274b3c827180b02b67f835f66a86fc4267b77349" +checksum = "f086c5026d2fc3b268d138e65373f46422cc810f46d6e0776859c5027cb18728" dependencies = [ "anyhow", "async-trait", @@ -6591,9 +6839,9 @@ dependencies = [ [[package]] name = "wasi-common" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc983eb93607a61f64152ec8728bf453f4dfdf22e7ab1784faac3297fe9a035e" +checksum = "4e8844fede1c3787cc08853872f47e8bd91f6c939c7406bc7a5dba496b260c08" dependencies = [ "anyhow", "bitflags", @@ -6609,9 +6857,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -6619,13 +6867,13 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", "syn", @@ -6634,9 +6882,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.31" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f" +checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -6646,9 +6894,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6656,9 +6904,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", @@ -6669,15 +6917,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "wasm-encoder" -version = "0.14.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f76068e87fe9b837a6bc2ccded66784173eadb828c4168643e9fddf6f9ed2e61" +checksum = "c64ac98d5d61192cc45c701b7e4bd0b9aff91e2edfc7a088406cfe2288581e2c" dependencies = [ "leb128", ] @@ -6693,9 +6941,9 @@ dependencies = [ [[package]] name = "wasmtime" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e76e2b2833bb0ece666ccdbed7b71b617d447da11f1bb61f4f2bab2648f745ee" +checksum = "1f50eadf868ab6a04b7b511460233377d0bfbb92e417b2f6a98b98fef2e098f5" dependencies = [ "anyhow", "async-trait", @@ -6706,7 +6954,7 @@ dependencies = [ "lazy_static", "libc", "log", - "object", + "object 0.28.4", "once_cell", "paste", "psm", @@ -6727,9 +6975,9 @@ dependencies = [ [[package]] name = "wasmtime-cache" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743a9f142d93318262d7e1fe329394ff2e8f86a1df45ae5e4f0eedba215ca5ce" +checksum = "d1df23c642e1376892f3b72f311596976979cbf8b85469680cdd3a8a063d12a2" dependencies = [ "anyhow", "base64", @@ -6747,9 +6995,9 @@ dependencies = [ [[package]] name = "wasmtime-cranelift" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dc0f80afa1ce97083a7168e6b6948d015d6237369e9f4a511d38c9c4ac8fbb9" +checksum = "f264ff6b4df247d15584f2f53d009fbc90032cfdc2605b52b961bffc71b6eccd" dependencies = [ "anyhow", "cranelift-codegen", @@ -6760,7 +7008,7 @@ dependencies = [ "gimli", "log", "more-asserts", - "object", + "object 0.28.4", "target-lexicon", "thiserror", "wasmparser", @@ -6769,9 +7017,9 @@ dependencies = [ [[package]] name = "wasmtime-environ" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0816d9365196f1f447060087e0f87239ccded830bd54970a1168b0c9c8e824c9" +checksum = "839d2820e4b830f4b9e7aa08d4c0acabf4a5036105d639f6dfa1c6891c73bdc6" dependencies = [ "anyhow", "cranelift-entity", @@ -6779,7 +7027,7 @@ dependencies = [ "indexmap", "log", "more-asserts", - "object", + "object 0.28.4", "serde", "target-lexicon", "thiserror", @@ -6789,9 +7037,9 @@ dependencies = [ [[package]] name = "wasmtime-fiber" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715afdb87a3bcf1eae3f098c742d650fb783abdb8a7ca87076ea1cabecabea5d" +checksum = "3248be3c4911233535356025f6562193614a40155ee9094bb6a2b43f0dc82803" dependencies = [ "cc", "rustix", @@ -6800,9 +7048,9 @@ dependencies = [ [[package]] name = "wasmtime-jit" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c687f33cfa0f89ec1646929d0ff102087052cf9f0d15533de56526b0da0d1b3" +checksum = "ef0a0bcbfa18b946d890078ba0e1bc76bcc53eccfb40806c0020ec29dcd1bd49" dependencies = [ "addr2line", "anyhow", @@ -6812,7 +7060,7 @@ dependencies = [ "gimli", "ittapi-rs", "log", - "object", + "object 0.28.4", "region", "rustc-demangle", "rustix", @@ -6827,20 +7075,20 @@ dependencies = [ [[package]] name = "wasmtime-jit-debug" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b252d1d025f94f3954ba2111f12f3a22826a0764a11c150c2d46623115a69e27" +checksum = "4f4779d976206c458edd643d1ac622b6c37e4a0800a8b1d25dfbf245ac2f2cac" dependencies = [ "lazy_static", - "object", + "object 0.28.4", "rustix", ] [[package]] name = "wasmtime-runtime" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace251693103c9facbbd7df87a29a75e68016e48bc83c09133f2fda6b575e0ab" +checksum = "b7eb6ffa169eb5dcd18ac9473c817358cd57bc62c244622210566d473397954a" dependencies = [ "anyhow", "backtrace", @@ -6865,9 +7113,9 @@ dependencies = [ [[package]] name = "wasmtime-types" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d129b0487a95986692af8708ffde9c50b0568dcefd79200941d475713b4f40bb" +checksum = "8d932b0ac5336f7308d869703dd225610a6a3aeaa8e968c52b43eed96cefb1c2" dependencies = [ "cranelift-entity", "serde", @@ -6877,9 +7125,9 @@ dependencies = [ [[package]] name = "wasmtime-wasi" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb49791530b3a3375897a6d5a8bfa9914101ef8a672d01c951e70b46fd953c15" +checksum = "b68b7d77fb6f2975a6fe6cc4d0015d6b0cebb65c39fce1dd4cc00880dbf7789c" dependencies = [ "anyhow", "wasi-cap-std-sync", @@ -6899,9 +7147,9 @@ dependencies = [ [[package]] name = "wast" -version = "43.0.0" +version = "47.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "408feaebf6dbf9d154957873b14d00e8fba4cbc17a8cbb1bc9e4c1db425c50a8" +checksum = "02b98502f3978adea49551e801a6687678e6015317d7d9470a67fe813393f2a8" dependencies = [ "leb128", "memchr", @@ -6911,18 +7159,18 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.45" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b70bfff0cfaf33dc9d641196dbcd0023a2da8b4b9030c59535cb44e2884983b" +checksum = "7aab4e20c60429fbba9670a6cae0fff9520046ba0aa3e6d0b1cd2653bea14898" dependencies = [ - "wast 43.0.0", + "wast 47.0.1", ] [[package]] name = "web-sys" -version = "0.3.58" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" dependencies = [ "js-sys", "wasm-bindgen", @@ -6959,18 +7207,18 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.3" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d8de8415c823c8abd270ad483c6feeac771fad964890779f9a8cb24fbbc1bf" +checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be" dependencies = [ "webpki 0.22.0", ] [[package]] name = "weezl" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c97e489d8f836838d497091de568cf16b117486d529ec5579233521065bd5e4" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" [[package]] name = "wepoll-ffi" @@ -6983,30 +7231,31 @@ dependencies = [ [[package]] name = "which" -version = "4.2.5" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" +checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" dependencies = [ "either", - "lazy_static", "libc", + "once_cell", ] [[package]] name = "whoami" -version = "1.2.1" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524b58fa5a20a2fb3014dd6358b70e6579692a56ef6fce928834e488f42f65e8" +checksum = "d6631b6a2fd59b1841b622e8f1a7ad241ef0a46f2d580464ce8140ac94cbd571" dependencies = [ + "bumpalo", "wasm-bindgen", "web-sys", ] [[package]] name = "wiggle" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c38020359fabec5e5ce5a3f667af72e9a203bc6fe8caeb8931d3a870754d9d" +checksum = "67dadac11343d2aabc8a906a0db0aaf7cb5046ec3d6fffccdaf2847dccdef8d6" dependencies = [ "anyhow", "async-trait", @@ -7019,9 +7268,9 @@ dependencies = [ [[package]] name = "wiggle-generate" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc4e4420b496b04920ae3e41424029aba95c15a5e2e2b4012d14ec83770a3ef" +checksum = "63a1dccd6b3fbd9a27417f5d30ce9aa3ee9cf529aad453abbf88a49c5d605b79" dependencies = [ "anyhow", "heck 0.4.0", @@ -7034,9 +7283,9 @@ dependencies = [ [[package]] name = "wiggle-macro" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e541a0be1f2c4d53471d8a9df81c2d8725a3f023d8259f555c65b03d515aaab" +checksum = "f1c368d57d9560c34deaa67e06b0953ccf65edb906c525e5a2c866c849b48ec2" dependencies = [ "proc-macro2", "quote", @@ -7181,7 +7430,7 @@ dependencies = [ "collections", "context_menu", "drag_and_drop", - "futures", + "futures 0.3.24", "gpui", "language", "log", @@ -7265,7 +7514,7 @@ dependencies = [ "env_logger", "file_finder", "fsevent", - "futures", + "futures 0.3.24", "fuzzy", "go_to_line", "gpui", diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index 5262daab5f..651484afcd 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -38,7 +38,23 @@ } ], "%": "vim::Matching", - "escape": "editor::Cancel" + "escape": "editor::Cancel", + "i": [ + "vim::PushOperator", + { + "Object": { + "around": false + } + } + ], + "a": [ + "vim::PushOperator", + { + "Object": { + "around": true + } + } + ] } }, { @@ -134,6 +150,20 @@ "y": "vim::CurrentLine" } }, + { + "context": "Editor && VimObject", + "bindings": { + "w": "vim::Word", + "shift-w": [ + "vim::Word", + { + "ignorePunctuation": true + } + ], + "s": "vim::Sentence", + "p": "vim::Paragraph" + } + }, { "context": "Editor && vim_mode == visual", "bindings": { diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 58fc2e4fe7..ac85bf68ee 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -330,34 +330,91 @@ impl DisplaySnapshot { DisplayPoint(self.blocks_snapshot.max_point()) } + /// Returns text chunks starting at the given display row until the end of the file pub fn text_chunks(&self, display_row: u32) -> impl Iterator { self.blocks_snapshot .chunks(display_row..self.max_point().row() + 1, false, None) .map(|h| h.text) } + // Returns text chunks starting at the end of the given display row in reverse until the start of the file + pub fn reverse_text_chunks(&self, display_row: u32) -> impl Iterator { + (0..=display_row).into_iter().rev().flat_map(|row| { + self.blocks_snapshot + .chunks(row..row + 1, false, None) + .map(|h| h.text) + .collect::>() + .into_iter() + .rev() + }) + } + pub fn chunks(&self, display_rows: Range, language_aware: bool) -> DisplayChunks<'_> { self.blocks_snapshot .chunks(display_rows, language_aware, Some(&self.text_highlights)) } - pub fn chars_at(&self, point: DisplayPoint) -> impl Iterator + '_ { - let mut column = 0; - let mut chars = self.text_chunks(point.row()).flat_map(str::chars); - while column < point.column() { - if let Some(c) = chars.next() { - column += c.len_utf8() as u32; - } else { - break; - } - } - chars + pub fn chars_at( + &self, + mut point: DisplayPoint, + ) -> impl Iterator + '_ { + point = DisplayPoint(self.blocks_snapshot.clip_point(point.0, Bias::Left)); + self.text_chunks(point.row()) + .flat_map(str::chars) + .skip_while({ + let mut column = 0; + move |char| { + let at_point = column >= point.column(); + column += char.len_utf8() as u32; + !at_point + } + }) + .map(move |ch| { + let result = (ch, point); + if ch == '\n' { + *point.row_mut() += 1; + *point.column_mut() = 0; + } else { + *point.column_mut() += ch.len_utf8() as u32; + } + result + }) + } + + pub fn reverse_chars_at( + &self, + mut point: DisplayPoint, + ) -> impl Iterator + '_ { + point = DisplayPoint(self.blocks_snapshot.clip_point(point.0, Bias::Left)); + self.reverse_text_chunks(point.row()) + .flat_map(|chunk| chunk.chars().rev()) + .skip_while({ + let mut column = self.line_len(point.row()); + if self.max_point().row() > point.row() { + column += 1; + } + + move |char| { + let at_point = column <= point.column(); + column = column.saturating_sub(char.len_utf8() as u32); + !at_point + } + }) + .map(move |ch| { + if ch == '\n' { + *point.row_mut() -= 1; + *point.column_mut() = self.line_len(point.row()); + } else { + *point.column_mut() = point.column().saturating_sub(ch.len_utf8() as u32); + } + (ch, point) + }) } pub fn column_to_chars(&self, display_row: u32, target: u32) -> u32 { let mut count = 0; let mut column = 0; - for c in self.chars_at(DisplayPoint::new(display_row, 0)) { + for (c, _) in self.chars_at(DisplayPoint::new(display_row, 0)) { if column >= target { break; } @@ -370,7 +427,7 @@ impl DisplaySnapshot { pub fn column_from_chars(&self, display_row: u32, char_count: u32) -> u32 { let mut column = 0; - for (count, c) in self.chars_at(DisplayPoint::new(display_row, 0)).enumerate() { + for (count, (c, _)) in self.chars_at(DisplayPoint::new(display_row, 0)).enumerate() { if c == '\n' || count >= char_count as usize { break; } @@ -454,7 +511,7 @@ impl DisplaySnapshot { pub fn line_indent(&self, display_row: u32) -> (u32, bool) { let mut indent = 0; let mut is_blank = true; - for c in self.chars_at(DisplayPoint::new(display_row, 0)) { + for (c, _) in self.chars_at(DisplayPoint::new(display_row, 0)) { if c == ' ' { indent += 1; } else { diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 1abe65d482..e59b024ebc 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -4074,7 +4074,7 @@ impl Editor { self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_cursors_with(|map, head, _| { ( - movement::line_beginning(map, head, true), + movement::indented_line_beginning(map, head, true), SelectionGoal::None, ) }); @@ -4089,7 +4089,7 @@ impl Editor { self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_heads_with(|map, head, _| { ( - movement::line_beginning(map, head, action.stop_at_soft_wraps), + movement::indented_line_beginning(map, head, action.stop_at_soft_wraps), SelectionGoal::None, ) }); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index acf2e5887c..42bb027283 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -752,7 +752,7 @@ impl EditorElement { .snapshot .chars_at(cursor_position) .next() - .and_then(|character| { + .and_then(|(character, _)| { let font_id = cursor_row_layout.font_for_index(cursor_column)?; let text = character.to_string(); diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index 0db5cc0812..9261a6d445 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -101,6 +101,22 @@ pub fn line_beginning( map: &DisplaySnapshot, display_point: DisplayPoint, stop_at_soft_boundaries: bool, +) -> DisplayPoint { + let point = display_point.to_point(map); + let soft_line_start = map.clip_point(DisplayPoint::new(display_point.row(), 0), Bias::Right); + let line_start = map.prev_line_boundary(point).1; + + if stop_at_soft_boundaries && display_point != soft_line_start { + soft_line_start + } else { + line_start + } +} + +pub fn indented_line_beginning( + map: &DisplaySnapshot, + display_point: DisplayPoint, + stop_at_soft_boundaries: bool, ) -> DisplayPoint { let point = display_point.to_point(map); let soft_line_start = map.clip_point(DisplayPoint::new(display_point.row(), 0), Bias::Right); @@ -167,54 +183,79 @@ pub fn next_subword_end(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPo }) } -/// Scans for a boundary from the start of each line preceding the given end point until a boundary -/// is found, indicated by the given predicate returning true. The predicate is called with the -/// character to the left and right of the candidate boundary location, and will be called with `\n` -/// characters indicating the start or end of a line. If the predicate returns true multiple times -/// on a line, the *rightmost* boundary is returned. +/// Scans for a boundary preceding the given start point `from` until a boundary is found, indicated by the +/// given predicate returning true. The predicate is called with the character to the left and right +/// of the candidate boundary location, and will be called with `\n` characters indicating the start +/// or end of a line. pub fn find_preceding_boundary( map: &DisplaySnapshot, - end: DisplayPoint, + from: DisplayPoint, mut is_boundary: impl FnMut(char, char) -> bool, ) -> DisplayPoint { - let mut point = end; - loop { - *point.column_mut() = 0; - if point.row() > 0 { - if let Some(indent) = map.soft_wrap_indent(point.row() - 1) { - *point.column_mut() = indent; + let mut start_column = 0; + let mut soft_wrap_row = from.row() + 1; + + let mut prev = None; + for (ch, point) in map.reverse_chars_at(from) { + // Recompute soft_wrap_indent if the row has changed + if point.row() != soft_wrap_row { + soft_wrap_row = point.row(); + + if point.row() == 0 { + start_column = 0; + } else if let Some(indent) = map.soft_wrap_indent(point.row() - 1) { + start_column = indent; } } - let mut boundary = None; - let mut prev_ch = if point.is_zero() { None } else { Some('\n') }; - for ch in map.chars_at(point) { - if point >= end { - break; - } - - if let Some(prev_ch) = prev_ch { - if is_boundary(prev_ch, ch) { - boundary = Some(point); - } - } - - if ch == '\n' { - break; - } - - prev_ch = Some(ch); - *point.column_mut() += ch.len_utf8() as u32; + // If the current point is in the soft_wrap, skip comparing it + if point.column() < start_column { + continue; } - if let Some(boundary) = boundary { - return boundary; - } else if point.row() == 0 { - return DisplayPoint::zero(); - } else { - *point.row_mut() -= 1; + if let Some((prev_ch, prev_point)) = prev { + if is_boundary(ch, prev_ch) { + return prev_point; + } + } + + prev = Some((ch, point)); + } + DisplayPoint::zero() +} + +/// Scans for a boundary preceding the given start point `from` until a boundary is found, indicated by the +/// given predicate returning true. The predicate is called with the character to the left and right +/// of the candidate boundary location, and will be called with `\n` characters indicating the start +/// or end of a line. If no boundary is found, the start of the line is returned. +pub fn find_preceding_boundary_in_line( + map: &DisplaySnapshot, + from: DisplayPoint, + mut is_boundary: impl FnMut(char, char) -> bool, +) -> DisplayPoint { + let mut start_column = 0; + if from.row() > 0 { + if let Some(indent) = map.soft_wrap_indent(from.row() - 1) { + start_column = indent; } } + + let mut prev = None; + for (ch, point) in map.reverse_chars_at(from) { + if let Some((prev_ch, prev_point)) = prev { + if is_boundary(ch, prev_ch) { + return prev_point; + } + } + + if ch == '\n' || point.column() < start_column { + break; + } + + prev = Some((ch, point)); + } + + prev.map(|(_, point)| point).unwrap_or(from) } /// Scans for a boundary following the given start point until a boundary is found, indicated by the @@ -223,26 +264,48 @@ pub fn find_preceding_boundary( /// or end of a line. pub fn find_boundary( map: &DisplaySnapshot, - mut point: DisplayPoint, + from: DisplayPoint, mut is_boundary: impl FnMut(char, char) -> bool, ) -> DisplayPoint { let mut prev_ch = None; - for ch in map.chars_at(point) { + for (ch, point) in map.chars_at(from) { if let Some(prev_ch) = prev_ch { if is_boundary(prev_ch, ch) { - break; + return map.clip_point(point, Bias::Right); } } - if ch == '\n' { - *point.row_mut() += 1; - *point.column_mut() = 0; - } else { - *point.column_mut() += ch.len_utf8() as u32; - } prev_ch = Some(ch); } - map.clip_point(point, Bias::Right) + map.clip_point(map.max_point(), Bias::Right) +} + +/// Scans for a boundary following the given start point until a boundary is found, indicated by the +/// given predicate returning true. The predicate is called with the character to the left and right +/// of the candidate boundary location, and will be called with `\n` characters indicating the start +/// or end of a line. If no boundary is found, the end of the line is returned +pub fn find_boundary_in_line( + map: &DisplaySnapshot, + from: DisplayPoint, + mut is_boundary: impl FnMut(char, char) -> bool, +) -> DisplayPoint { + let mut prev = None; + for (ch, point) in map.chars_at(from) { + if let Some((prev_ch, _)) = prev { + if is_boundary(prev_ch, ch) { + return map.clip_point(point, Bias::Right); + } + } + + prev = Some((ch, point)); + + if ch == '\n' { + break; + } + } + + // Return the last position checked so that we give a point right before the newline or eof. + map.clip_point(prev.map(|(_, point)| point).unwrap_or(from), Bias::Right) } pub fn is_inside_word(map: &DisplaySnapshot, point: DisplayPoint) -> bool { diff --git a/crates/vim/Cargo.toml b/crates/vim/Cargo.toml index 5bc32cd5bd..6b72383fdb 100644 --- a/crates/vim/Cargo.toml +++ b/crates/vim/Cargo.toml @@ -7,7 +7,20 @@ edition = "2021" path = "src/vim.rs" doctest = false +[features] +neovim = ["nvim-rs", "async-compat", "async-trait", "tokio"] + [dependencies] +serde = { version = "1.0", features = ["derive", "rc"] } +itertools = "0.10" +log = { version = "0.4.16", features = ["kv_unstable_serde"] } + +async-compat = { version = "0.2.1", "optional" = true } +async-trait = { version = "0.1", "optional" = true } +nvim-rs = { git = "https://github.com/KillTheMule/nvim-rs", branch = "master", features = ["use_tokio"], optional = true } +tokio = { version = "1.15", "optional" = true } +serde_json = { version = "1.0", features = ["preserve_order"] } + assets = { path = "../assets" } collections = { path = "../collections" } command_palette = { path = "../command_palette" } @@ -15,14 +28,12 @@ editor = { path = "../editor" } gpui = { path = "../gpui" } language = { path = "../language" } search = { path = "../search" } -serde = { version = "1.0", features = ["derive", "rc"] } settings = { path = "../settings" } workspace = { path = "../workspace" } -itertools = "0.10" -log = { version = "0.4.16", features = ["kv_unstable_serde"] } [dev-dependencies] indoc = "1.0.4" + editor = { path = "../editor", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } language = { path = "../language", features = ["test-support"] } diff --git a/crates/vim/src/insert.rs b/crates/vim/src/insert.rs index a1d1c7b404..1b9b299bf3 100644 --- a/crates/vim/src/insert.rs +++ b/crates/vim/src/insert.rs @@ -26,7 +26,7 @@ fn normal_before(_: &mut Workspace, _: &NormalBefore, cx: &mut ViewContext DisplayPoint { map.clip_point(point, Bias::Left) } -fn right(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint { +pub(crate) fn right(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint { *point.column_mut() += 1; map.clip_point(point, Bias::Right) } -fn next_word_start( +pub(crate) fn next_word_start( map: &DisplaySnapshot, point: DisplayPoint, ignore_punctuation: bool, @@ -255,7 +255,7 @@ fn next_word_start( let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation); let at_newline = right == '\n'; - let found = (left_kind != right_kind && !right.is_whitespace()) + let found = (left_kind != right_kind && right_kind != CharKind::Whitespace) || at_newline && crossed_newline || at_newline && left == '\n'; // Prevents skipping repeated empty lines @@ -272,23 +272,28 @@ fn next_word_end( ignore_punctuation: bool, ) -> DisplayPoint { *point.column_mut() += 1; + dbg!(point); point = movement::find_boundary(map, point, |left, right| { + dbg!(left); let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation); let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation); - left_kind != right_kind && !left.is_whitespace() + left_kind != right_kind && left_kind != CharKind::Whitespace }); + + dbg!(point); + // find_boundary clips, so if the character after the next character is a newline or at the end of the document, we know // we have backtraced already if !map .chars_at(point) .nth(1) - .map(|c| c == '\n') + .map(|(c, _)| c == '\n') .unwrap_or(true) { *point.column_mut() = point.column().saturating_sub(1); } - map.clip_point(point, Bias::Left) + dbg!(map.clip_point(point, Bias::Left)) } fn previous_word_start( @@ -307,22 +312,21 @@ fn previous_word_start( point } -fn first_non_whitespace(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint { - let mut column = 0; - for ch in map.chars_at(DisplayPoint::new(point.row(), 0)) { +fn first_non_whitespace(map: &DisplaySnapshot, from: DisplayPoint) -> DisplayPoint { + let mut last_point = DisplayPoint::new(from.row(), 0); + for (ch, point) in map.chars_at(last_point) { if ch == '\n' { - return point; + return from; } + last_point = point; + if char_kind(ch) != CharKind::Whitespace { break; } - - column += ch.len_utf8() as u32; } - *point.column_mut() = column; - map.clip_point(point, Bias::Left) + map.clip_point(last_point, Bias::Left) } fn start_of_line(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint { diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index db5583b4ce..5345586743 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -6,6 +6,7 @@ use std::borrow::Cow; use crate::{ motion::Motion, + object::Object, state::{Mode, Operator}, Vim, }; @@ -16,7 +17,11 @@ use gpui::{actions, MutableAppContext, ViewContext}; use language::{AutoindentMode, Point, SelectionGoal}; use workspace::Workspace; -use self::{change::change_over, delete::delete_over, yank::yank_over}; +use self::{ + change::{change_motion, change_object}, + delete::{delete_motion, delete_object}, + yank::{yank_motion, yank_object}, +}; actions!( vim, @@ -43,22 +48,22 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action(insert_line_below); cx.add_action(|_: &mut Workspace, _: &DeleteLeft, cx| { Vim::update(cx, |vim, cx| { - delete_over(vim, Motion::Left, cx); + delete_motion(vim, Motion::Left, cx); }) }); cx.add_action(|_: &mut Workspace, _: &DeleteRight, cx| { Vim::update(cx, |vim, cx| { - delete_over(vim, Motion::Right, cx); + delete_motion(vim, Motion::Right, cx); }) }); cx.add_action(|_: &mut Workspace, _: &ChangeToEndOfLine, cx| { Vim::update(cx, |vim, cx| { - change_over(vim, Motion::EndOfLine, cx); + change_motion(vim, Motion::EndOfLine, cx); }) }); cx.add_action(|_: &mut Workspace, _: &DeleteToEndOfLine, cx| { Vim::update(cx, |vim, cx| { - delete_over(vim, Motion::EndOfLine, cx); + delete_motion(vim, Motion::EndOfLine, cx); }) }); cx.add_action(paste); @@ -70,17 +75,36 @@ pub fn normal_motion(motion: Motion, cx: &mut MutableAppContext) { Vim::update(cx, |vim, cx| { match vim.state.operator_stack.pop() { None => move_cursor(vim, motion, cx), - Some(Operator::Namespace(_)) => { - // Can't do anything for a namespace operator. Ignoring + Some(Operator::Change) => change_motion(vim, motion, cx), + Some(Operator::Delete) => delete_motion(vim, motion, cx), + Some(Operator::Yank) => yank_motion(vim, motion, cx), + _ => { + // Can't do anything for text objects or namespace operators. Ignoring } - Some(Operator::Change) => change_over(vim, motion, cx), - Some(Operator::Delete) => delete_over(vim, motion, cx), - Some(Operator::Yank) => yank_over(vim, motion, cx), } vim.clear_operator(cx); }); } +pub fn normal_object(object: Object, cx: &mut MutableAppContext) { + Vim::update(cx, |vim, cx| { + match vim.state.operator_stack.pop() { + Some(Operator::Object { around }) => match vim.state.operator_stack.pop() { + Some(Operator::Change) => change_object(vim, object, around, cx), + Some(Operator::Delete) => delete_object(vim, object, around, cx), + Some(Operator::Yank) => yank_object(vim, object, around, cx), + _ => { + // Can't do anything for namespace operators. Ignoring + } + }, + _ => { + // Can't do anything with change/delete/yank and text objects. Ignoring + } + } + vim.clear_operator(cx); + }) +} + fn move_cursor(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) { vim.update_active_editor(cx, |editor, cx| { editor.change_selections(Some(Autoscroll::Fit), cx, |s| { @@ -304,7 +328,7 @@ mod test { Mode::{self, *}, Namespace, Operator, }, - vim_test_context::VimTestContext, + test_contexts::VimTestContext, }; #[gpui::test] diff --git a/crates/vim/src/normal/change.rs b/crates/vim/src/normal/change.rs index 8695c9668b..6835ea3b1c 100644 --- a/crates/vim/src/normal/change.rs +++ b/crates/vim/src/normal/change.rs @@ -1,4 +1,4 @@ -use crate::{motion::Motion, state::Mode, utils::copy_selections_content, Vim}; +use crate::{motion::Motion, object::Object, state::Mode, utils::copy_selections_content, Vim}; use editor::{char_kind, movement, Autoscroll}; use gpui::{impl_actions, MutableAppContext, ViewContext}; use serde::Deserialize; @@ -17,7 +17,7 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action(change_word); } -pub fn change_over(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) { +pub fn change_motion(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) { vim.update_active_editor(cx, |editor, cx| { editor.transact(cx, |editor, cx| { // We are swapping to insert mode anyway. Just set the line end clipping behavior now @@ -34,6 +34,23 @@ pub fn change_over(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) { vim.switch_mode(Mode::Insert, false, cx) } +pub fn change_object(vim: &mut Vim, object: Object, around: bool, cx: &mut MutableAppContext) { + vim.update_active_editor(cx, |editor, cx| { + editor.transact(cx, |editor, cx| { + // We are swapping to insert mode anyway. Just set the line end clipping behavior now + editor.set_clip_at_line_ends(false, cx); + editor.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.move_with(|map, selection| { + object.expand_selection(map, selection, around); + }); + }); + copy_selections_content(editor, false, cx); + editor.insert("", cx); + }); + }); + vim.switch_mode(Mode::Insert, false, cx); +} + // From the docs https://vimhelp.org/change.txt.html#cw // Special case: When the cursor is in a word, "cw" and "cW" do not include the // white space after a word, they only change up to the end of the word. This is @@ -78,7 +95,7 @@ fn change_word( mod test { use indoc::indoc; - use crate::{state::Mode, vim_test_context::VimTestContext}; + use crate::{state::Mode, test_contexts::VimTestContext}; #[gpui::test] async fn test_change_h(cx: &mut gpui::TestAppContext) { @@ -170,8 +187,7 @@ mod test { test"}, indoc! {" Test test - ˇ - test"}, + ˇ"}, ); let mut cx = cx.binding(["c", "shift-e"]); @@ -193,6 +209,7 @@ mod test { Test ˇ test"}, ); + println!("Marker"); cx.assert( indoc! {" Test test diff --git a/crates/vim/src/normal/delete.rs b/crates/vim/src/normal/delete.rs index b2e228bdb1..e410024aea 100644 --- a/crates/vim/src/normal/delete.rs +++ b/crates/vim/src/normal/delete.rs @@ -1,9 +1,9 @@ -use crate::{motion::Motion, utils::copy_selections_content, Vim}; -use collections::HashMap; -use editor::{Autoscroll, Bias}; +use crate::{motion::Motion, object::Object, utils::copy_selections_content, Vim}; +use collections::{HashMap, HashSet}; +use editor::{display_map::ToDisplayPoint, Autoscroll, Bias}; use gpui::MutableAppContext; -pub fn delete_over(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) { +pub fn delete_motion(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) { vim.update_active_editor(cx, |editor, cx| { editor.transact(cx, |editor, cx| { editor.set_clip_at_line_ends(false, cx); @@ -36,11 +36,67 @@ pub fn delete_over(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) { }); } +pub fn delete_object(vim: &mut Vim, object: Object, around: bool, cx: &mut MutableAppContext) { + vim.update_active_editor(cx, |editor, cx| { + editor.transact(cx, |editor, cx| { + editor.set_clip_at_line_ends(false, cx); + // Emulates behavior in vim where if we expanded backwards to include a newline + // the cursor gets set back to the start of the line + let mut should_move_to_start: HashSet<_> = Default::default(); + editor.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.move_with(|map, selection| { + object.expand_selection(map, selection, around); + let offset_range = selection.map(|p| p.to_offset(map, Bias::Left)).range(); + let contains_only_newlines = map + .chars_at(selection.start) + .take_while(|(_, p)| p < &selection.end) + .all(|(char, _)| char == '\n') + || offset_range.is_empty(); + let end_at_newline = map + .chars_at(selection.end) + .next() + .map(|(c, _)| c == '\n') + .unwrap_or(false); + + // If expanded range contains only newlines and + // the object is around or sentence, expand to include a newline + // at the end or start + if (around || object == Object::Sentence) && contains_only_newlines { + if end_at_newline { + selection.end = + (offset_range.end + '\n'.len_utf8()).to_display_point(map); + } else if selection.start.row() > 0 { + should_move_to_start.insert(selection.id); + selection.start = + (offset_range.start - '\n'.len_utf8()).to_display_point(map); + } + } + }); + }); + copy_selections_content(editor, false, cx); + editor.insert("", cx); + + // Fixup cursor position after the deletion + editor.set_clip_at_line_ends(true, cx); + editor.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.move_with(|map, selection| { + let mut cursor = selection.head(); + if should_move_to_start.contains(&selection.id) { + *cursor.column_mut() = 0; + } + cursor = map.clip_point(cursor, Bias::Left); + selection.collapse_to(cursor, selection.goal) + }); + }); + }); + }); +} + #[cfg(test)] mod test { use indoc::indoc; - use crate::{state::Mode, vim_test_context::VimTestContext}; + use crate::{state::Mode, test_contexts::VimTestContext}; #[gpui::test] async fn test_delete_h(cx: &mut gpui::TestAppContext) { @@ -140,8 +196,7 @@ mod test { test"}, indoc! {" Test test - ˇ - test"}, + ˇ"}, ); let mut cx = cx.binding(["d", "shift-e"]); diff --git a/crates/vim/src/normal/yank.rs b/crates/vim/src/normal/yank.rs index 17a9e47d3d..11f5371cc6 100644 --- a/crates/vim/src/normal/yank.rs +++ b/crates/vim/src/normal/yank.rs @@ -1,8 +1,8 @@ -use crate::{motion::Motion, utils::copy_selections_content, Vim}; +use crate::{motion::Motion, object::Object, utils::copy_selections_content, Vim}; use collections::HashMap; use gpui::MutableAppContext; -pub fn yank_over(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) { +pub fn yank_motion(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) { vim.update_active_editor(cx, |editor, cx| { editor.transact(cx, |editor, cx| { editor.set_clip_at_line_ends(false, cx); @@ -24,3 +24,26 @@ pub fn yank_over(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) { }); }); } + +pub fn yank_object(vim: &mut Vim, object: Object, around: bool, cx: &mut MutableAppContext) { + vim.update_active_editor(cx, |editor, cx| { + editor.transact(cx, |editor, cx| { + editor.set_clip_at_line_ends(false, cx); + let mut original_positions: HashMap<_, _> = Default::default(); + editor.change_selections(None, cx, |s| { + s.move_with(|map, selection| { + let original_position = (selection.head(), selection.goal); + object.expand_selection(map, selection, around); + original_positions.insert(selection.id, original_position); + }); + }); + copy_selections_content(editor, false, cx); + editor.change_selections(None, cx, |s| { + s.move_with(|_, selection| { + let (head, goal) = original_positions.remove(&selection.id).unwrap(); + selection.collapse_to(head, goal); + }); + }); + }); + }); +} diff --git a/crates/vim/src/object.rs b/crates/vim/src/object.rs new file mode 100644 index 0000000000..5c03bda2d9 --- /dev/null +++ b/crates/vim/src/object.rs @@ -0,0 +1,488 @@ +use std::ops::Range; + +use editor::{char_kind, display_map::DisplaySnapshot, movement, Bias, CharKind, DisplayPoint}; +use gpui::{actions, impl_actions, MutableAppContext}; +use language::Selection; +use serde::Deserialize; +use workspace::Workspace; + +use crate::{motion, normal::normal_object, state::Mode, visual::visual_object, Vim}; + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Object { + Word { ignore_punctuation: bool }, + Sentence, + Paragraph, +} + +#[derive(Clone, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +struct Word { + #[serde(default)] + ignore_punctuation: bool, +} + +actions!(vim, [Sentence, Paragraph]); +impl_actions!(vim, [Word]); + +pub fn init(cx: &mut MutableAppContext) { + cx.add_action( + |_: &mut Workspace, &Word { ignore_punctuation }: &Word, cx: _| { + object(Object::Word { ignore_punctuation }, cx) + }, + ); + cx.add_action(|_: &mut Workspace, _: &Sentence, cx: _| object(Object::Sentence, cx)); + cx.add_action(|_: &mut Workspace, _: &Paragraph, cx: _| object(Object::Paragraph, cx)); +} + +fn object(object: Object, cx: &mut MutableAppContext) { + match Vim::read(cx).state.mode { + Mode::Normal => normal_object(object, cx), + Mode::Visual { .. } => visual_object(object, cx), + Mode::Insert => { + // Shouldn't execute a text object in insert mode. Ignoring + } + } +} + +impl Object { + pub fn object_range( + self, + map: &DisplaySnapshot, + relative_to: DisplayPoint, + around: bool, + ) -> Range { + match self { + Object::Word { ignore_punctuation } => { + if around { + around_word(map, relative_to, ignore_punctuation) + } else { + in_word(map, relative_to, ignore_punctuation) + } + } + Object::Sentence => sentence(map, relative_to, around), + _ => relative_to..relative_to, + } + } + + pub fn expand_selection( + self, + map: &DisplaySnapshot, + selection: &mut Selection, + around: bool, + ) { + let range = self.object_range(map, selection.head(), around); + selection.start = range.start; + selection.end = range.end; + } +} + +/// Return a range that surrounds the word relative_to is in +/// If relative_to is at the start of a word, return the word. +/// If relative_to is between words, return the space between +fn in_word( + map: &DisplaySnapshot, + relative_to: DisplayPoint, + ignore_punctuation: bool, +) -> Range { + // Use motion::right so that we consider the character under the cursor when looking for the start + let start = movement::find_preceding_boundary_in_line( + map, + motion::right(map, relative_to), + |left, right| { + char_kind(left).coerce_punctuation(ignore_punctuation) + != char_kind(right).coerce_punctuation(ignore_punctuation) + }, + ); + let end = movement::find_boundary_in_line(map, relative_to, |left, right| { + char_kind(left).coerce_punctuation(ignore_punctuation) + != char_kind(right).coerce_punctuation(ignore_punctuation) + }); + + start..end +} + +/// Return a range that surrounds the word and following whitespace +/// relative_to is in. +/// If relative_to is at the start of a word, return the word and following whitespace. +/// If relative_to is between words, return the whitespace back and the following word + +/// if in word +/// delete that word +/// if there is whitespace following the word, delete that as well +/// otherwise, delete any preceding whitespace +/// otherwise +/// delete whitespace around cursor +/// delete word following the cursor +fn around_word( + map: &DisplaySnapshot, + relative_to: DisplayPoint, + ignore_punctuation: bool, +) -> Range { + let in_word = map + .chars_at(relative_to) + .next() + .map(|(c, _)| char_kind(c) != CharKind::Whitespace) + .unwrap_or(false); + + if in_word { + around_containing_word(map, relative_to, ignore_punctuation) + } else { + around_next_word(map, relative_to, ignore_punctuation) + } +} + +fn around_containing_word( + map: &DisplaySnapshot, + relative_to: DisplayPoint, + ignore_punctuation: bool, +) -> Range { + expand_to_include_whitespace(map, in_word(map, relative_to, ignore_punctuation), true) +} + +fn around_next_word( + map: &DisplaySnapshot, + relative_to: DisplayPoint, + ignore_punctuation: bool, +) -> Range { + // Get the start of the word + let start = movement::find_preceding_boundary_in_line( + map, + motion::right(map, relative_to), + |left, right| { + char_kind(left).coerce_punctuation(ignore_punctuation) + != char_kind(right).coerce_punctuation(ignore_punctuation) + }, + ); + + let mut word_found = false; + let end = movement::find_boundary(map, relative_to, |left, right| { + let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation); + let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation); + + let found = (word_found && left_kind != right_kind) || right == '\n' && left == '\n'; + + if right_kind != CharKind::Whitespace { + word_found = true; + } + + found + }); + + start..end +} + +// /// Return the range containing a sentence. +// fn sentence(map: &DisplaySnapshot, relative_to: DisplayPoint, around: bool) -> Range { +// let mut previous_end = relative_to; +// let mut start = None; + +// // Seek backwards to find a period or double newline. Record the last non whitespace character as the +// // possible start of the sentence. Alternatively if two newlines are found right after each other, return that. +// let mut rev_chars = map.reverse_chars_at(relative_to).peekable(); +// while let Some((char, point)) = rev_chars.next() { +// dbg!(char, point); +// if char == '.' { +// break; +// } + +// if char == '\n' +// && (rev_chars.peek().map(|(c, _)| c == &'\n').unwrap_or(false) || start.is_none()) +// { +// break; +// } + +// if !char.is_whitespace() { +// start = Some(point); +// } + +// previous_end = point; +// } + +// let mut end = relative_to; +// let mut chars = map.chars_at(relative_to).peekable(); +// while let Some((char, point)) = chars.next() { +// if !char.is_whitespace() { +// if start.is_none() { +// start = Some(point); +// } + +// // Set the end to the point after the current non whitespace character +// end = point; +// *end.column_mut() += char.len_utf8() as u32; +// } + +// if char == '.' { +// break; +// } + +// if char == '\n' { +// if start.is_none() { +// if let Some((_, next_point)) = chars.peek() { +// end = *next_point; +// } +// break; + +// if chars.peek().map(|(c, _)| c == &'\n').unwrap_or(false) { +// break; +// } +// } +// } + +// start.unwrap_or(previous_end)..end +// } + +fn sentence(map: &DisplaySnapshot, relative_to: DisplayPoint, around: bool) -> Range { + let mut start = None; + let mut previous_end = relative_to; + + for (char, point) in map.reverse_chars_at(relative_to) { + if is_sentence_end(map, point) { + break; + } + + if is_possible_sentence_start(char) { + start = Some(point); + } + + previous_end = point; + } + + // Handle case where cursor was before the sentence start + let mut chars = map.chars_at(relative_to).peekable(); + if start.is_none() { + if let Some((char, point)) = chars.peek() { + if is_possible_sentence_start(*char) { + start = Some(*point); + } + } + } + + let mut end = relative_to; + for (char, point) in chars { + if start.is_some() { + if !char.is_whitespace() { + end = point; + *end.column_mut() += char.len_utf8() as u32; + end = map.clip_point(end, Bias::Left); + } + + if is_sentence_end(map, point) { + break; + } + } else if is_possible_sentence_start(char) { + if around { + start = Some(point); + } else { + end = point; + break; + } + } + } + + let mut range = start.unwrap_or(previous_end)..end; + if around { + range = expand_to_include_whitespace(map, range, false); + } + + range +} + +fn is_possible_sentence_start(character: char) -> bool { + !character.is_whitespace() && character != '.' +} + +const SENTENCE_END_PUNCTUATION: &[char] = &['.', '!', '?']; +const SENTENCE_END_FILLERS: &[char] = &[')', ']', '"', '\'']; +const SENTENCE_END_WHITESPACE: &[char] = &[' ', '\t', '\n']; +fn is_sentence_end(map: &DisplaySnapshot, point: DisplayPoint) -> bool { + let mut chars = map.chars_at(point).peekable(); + + if let Some((char, _)) = chars.next() { + if char == '\n' && chars.peek().map(|(c, _)| c == &'\n').unwrap_or(false) { + return true; + } + + if !SENTENCE_END_PUNCTUATION.contains(&char) { + return false; + } + } else { + return false; + } + + for (char, _) in chars { + if SENTENCE_END_WHITESPACE.contains(&char) { + return true; + } + + if !SENTENCE_END_FILLERS.contains(&char) { + return false; + } + } + + return true; +} + +/// Expands the passed range to include whitespace on one side or the other in a line. Attempts to add the +/// whitespace to the end first and falls back to the start if there was none. +fn expand_to_include_whitespace( + map: &DisplaySnapshot, + mut range: Range, + stop_at_newline: bool, +) -> Range { + let mut whitespace_included = false; + for (char, point) in map.chars_at(range.end) { + range.end = point; + + if char == '\n' && stop_at_newline { + break; + } + + if char.is_whitespace() { + whitespace_included = true; + } else { + break; + } + } + + if !whitespace_included { + for (char, point) in map.reverse_chars_at(range.start) { + if char == '\n' && stop_at_newline { + break; + } + + if !char.is_whitespace() { + break; + } + + range.start = point; + } + } + + range +} + +#[cfg(test)] +mod test { + use indoc::indoc; + + use crate::test_contexts::NeovimBackedTestContext; + + const WORD_LOCATIONS: &'static str = indoc! {" + The quick ˇbrowˇnˇ + fox ˇjuˇmpsˇ over + the lazy dogˇ + ˇ + ˇ + ˇ + Thˇeˇ-ˇquˇickˇ ˇbrownˇ + ˇ + ˇ + ˇ fox-jumpˇs over + the lazy dogˇ + ˇ + "}; + + #[gpui::test] + async fn test_change_in_word(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new("test_change_in_word", cx) + .await + .binding(["c", "i", "w"]); + cx.assert_all(WORD_LOCATIONS).await; + let mut cx = cx.consume().binding(["c", "i", "shift-w"]); + cx.assert_all(WORD_LOCATIONS).await; + } + + #[gpui::test] + async fn test_delete_in_word(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new("test_delete_in_word", cx) + .await + .binding(["d", "i", "w"]); + cx.assert_all(WORD_LOCATIONS).await; + let mut cx = cx.consume().binding(["d", "i", "shift-w"]); + cx.assert_all(WORD_LOCATIONS).await; + } + + #[gpui::test] + async fn test_change_around_word(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new("test_change_around_word", cx) + .await + .binding(["c", "a", "w"]); + cx.assert_all(WORD_LOCATIONS).await; + let mut cx = cx.consume().binding(["c", "a", "shift-w"]); + cx.assert_all(WORD_LOCATIONS).await; + } + + #[gpui::test] + async fn test_delete_around_word(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new("test_delete_around_word", cx) + .await + .binding(["d", "a", "w"]); + cx.assert_all(WORD_LOCATIONS).await; + let mut cx = cx.consume().binding(["d", "a", "shift-w"]); + cx.assert_all(WORD_LOCATIONS).await; + } + + const SENTENCE_EXAMPLES: &[&'static str] = &[ + "ˇThe quick ˇbrownˇ?ˇ ˇFox Jˇumpsˇ!ˇ Ovˇer theˇ lazyˇ.", + indoc! {" + ˇThe quick ˇbrownˇ + fox jumps over + the lazy doˇgˇ.ˇ ˇThe quick ˇ + brown fox jumps over + "}, + // Double newlines are broken currently + // indoc! {" + // The quick brown fox jumps. + // Over the lazy dog + // ˇ + // ˇ + // ˇ fox-jumpˇs over + // the lazy dog.ˇ + // ˇ + // "}, + r#"The quick brown.)]'" Brown fox jumps."#, + ]; + + #[gpui::test] + async fn test_change_in_sentence(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new("test_change_in_sentence", cx) + .await + .binding(["c", "i", "s"]); + for sentence_example in SENTENCE_EXAMPLES { + cx.assert_all(sentence_example).await; + } + } + + #[gpui::test] + async fn test_delete_in_sentence(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new("test_delete_in_sentence", cx) + .await + .binding(["d", "i", "s"]); + for sentence_example in SENTENCE_EXAMPLES { + cx.assert_all(sentence_example).await; + } + } + + #[gpui::test] + #[ignore] // End cursor position is incorrect + async fn test_change_around_sentence(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new("test_change_around_sentence", cx) + .await + .binding(["c", "a", "s"]); + for sentence_example in SENTENCE_EXAMPLES { + cx.assert_all(sentence_example).await; + } + } + + #[gpui::test] + #[ignore] // End cursor position is incorrect + async fn test_delete_around_sentence(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new("test_delete_around_sentence", cx) + .await + .binding(["d", "a", "s"]); + for sentence_example in SENTENCE_EXAMPLES { + cx.assert_all(sentence_example).await; + } + } +} diff --git a/crates/vim/src/state.rs b/crates/vim/src/state.rs index e556048ea8..29a51664ee 100644 --- a/crates/vim/src/state.rs +++ b/crates/vim/src/state.rs @@ -1,8 +1,8 @@ use editor::CursorShape; use gpui::keymap::Context; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] pub enum Mode { Normal, Insert, @@ -26,6 +26,7 @@ pub enum Operator { Change, Delete, Yank, + Object { around: bool }, } #[derive(Default)] @@ -77,7 +78,12 @@ impl VimState { context.set.insert("VimControl".to_string()); } - Operator::set_context(self.operator_stack.last(), &mut context); + let active_operator = self.operator_stack.last(); + if matches!(active_operator, Some(Operator::Object { .. })) { + context.set.insert("VimObject".to_string()); + } + + Operator::set_context(active_operator, &mut context); context } @@ -87,6 +93,8 @@ impl Operator { pub fn set_context(operator: Option<&Operator>, context: &mut Context) { let operator_context = match operator { Some(Operator::Namespace(Namespace::G)) => "g", + Some(Operator::Object { around: false }) => "i", + Some(Operator::Object { around: true }) => "a", Some(Operator::Change) => "c", Some(Operator::Delete) => "d", Some(Operator::Yank) => "y", diff --git a/crates/vim/src/test_contexts.rs b/crates/vim/src/test_contexts.rs new file mode 100644 index 0000000000..1a65be251b --- /dev/null +++ b/crates/vim/src/test_contexts.rs @@ -0,0 +1,9 @@ +mod neovim_backed_binding_test_context; +mod neovim_backed_test_context; +mod vim_binding_test_context; +mod vim_test_context; + +pub use neovim_backed_binding_test_context::*; +pub use neovim_backed_test_context::*; +pub use vim_binding_test_context::*; +pub use vim_test_context::*; diff --git a/crates/vim/src/test_contexts/neovim_backed_binding_test_context.rs b/crates/vim/src/test_contexts/neovim_backed_binding_test_context.rs new file mode 100644 index 0000000000..b732bfa19c --- /dev/null +++ b/crates/vim/src/test_contexts/neovim_backed_binding_test_context.rs @@ -0,0 +1,56 @@ +use std::ops::{Deref, DerefMut}; + +use util::test::marked_text_offsets; + +use super::NeovimBackedTestContext; + +pub struct NeovimBackedBindingTestContext<'a, const COUNT: usize> { + cx: NeovimBackedTestContext<'a>, + keystrokes_under_test: [&'static str; COUNT], +} + +impl<'a, const COUNT: usize> NeovimBackedBindingTestContext<'a, COUNT> { + pub fn new( + keystrokes_under_test: [&'static str; COUNT], + cx: NeovimBackedTestContext<'a>, + ) -> Self { + Self { + cx, + keystrokes_under_test, + } + } + + pub fn consume(self) -> NeovimBackedTestContext<'a> { + self.cx + } + + pub async fn assert(&mut self, initial_state: &str) { + self.cx + .assert_binding_matches(self.keystrokes_under_test, initial_state) + .await + } + + pub async fn assert_all(&mut self, marked_positions: &str) { + let (unmarked_text, cursor_offsets) = marked_text_offsets(marked_positions); + + for cursor_offset in cursor_offsets.iter() { + let mut marked_text = unmarked_text.clone(); + marked_text.insert(*cursor_offset, 'ˇ'); + self.assert(&marked_text).await; + } + } +} + +impl<'a, const COUNT: usize> Deref for NeovimBackedBindingTestContext<'a, COUNT> { + type Target = NeovimBackedTestContext<'a>; + + fn deref(&self) -> &Self::Target { + &self.cx + } +} + +impl<'a, const COUNT: usize> DerefMut for NeovimBackedBindingTestContext<'a, COUNT> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.cx + } +} diff --git a/crates/vim/src/test_contexts/neovim_backed_test_context.rs b/crates/vim/src/test_contexts/neovim_backed_test_context.rs new file mode 100644 index 0000000000..12654c7fb9 --- /dev/null +++ b/crates/vim/src/test_contexts/neovim_backed_test_context.rs @@ -0,0 +1,374 @@ +use std::{ + ops::{Deref, DerefMut}, + path::PathBuf, +}; + +use editor::DisplayPoint; +use gpui::keymap::Keystroke; + +#[cfg(feature = "neovim")] +use async_compat::Compat; +#[cfg(feature = "neovim")] +use async_trait::async_trait; +#[cfg(feature = "neovim")] +use nvim_rs::{ + create::tokio::new_child_cmd, error::LoopError, Handler, Neovim, UiAttachOptions, Value, +}; +#[cfg(feature = "neovim")] +use tokio::{ + process::{Child, ChildStdin, Command}, + task::JoinHandle, +}; + +use crate::state::Mode; + +use super::{NeovimBackedBindingTestContext, VimTestContext}; + +pub struct NeovimBackedTestContext<'a> { + cx: VimTestContext<'a>, + test_case_id: &'static str, + data_counter: usize, + #[cfg(feature = "neovim")] + nvim: Neovim>, + #[cfg(feature = "neovim")] + _join_handle: JoinHandle>>, + #[cfg(feature = "neovim")] + _child: Child, +} + +impl<'a> NeovimBackedTestContext<'a> { + pub async fn new( + test_case_id: &'static str, + cx: &'a mut gpui::TestAppContext, + ) -> NeovimBackedTestContext<'a> { + let cx = VimTestContext::new(cx, true).await; + + #[cfg(feature = "neovim")] + let handler = NvimHandler {}; + #[cfg(feature = "neovim")] + let (nvim, join_handle, child) = Compat::new(async { + let (nvim, join_handle, child) = new_child_cmd( + &mut Command::new("nvim").arg("--embed").arg("--clean"), + handler, + ) + .await + .expect("Could not connect to neovim process"); + + nvim.ui_attach(100, 100, &UiAttachOptions::default()) + .await + .expect("Could not attach to ui"); + + (nvim, join_handle, child) + }) + .await; + + let result = Self { + cx, + test_case_id, + data_counter: 0, + #[cfg(feature = "neovim")] + nvim, + #[cfg(feature = "neovim")] + _join_handle: join_handle, + #[cfg(feature = "neovim")] + _child: child, + }; + + #[cfg(feature = "neovim")] + { + result.clear_test_data() + } + + result + } + + pub async fn simulate_shared_keystroke(&mut self, keystroke_text: &str) { + let keystroke = Keystroke::parse(keystroke_text).unwrap(); + + #[cfg(feature = "neovim")] + { + let special = keystroke.shift + || keystroke.ctrl + || keystroke.alt + || keystroke.cmd + || keystroke.key.len() > 1; + let start = if special { "<" } else { "" }; + let shift = if keystroke.shift { "S-" } else { "" }; + let ctrl = if keystroke.ctrl { "C-" } else { "" }; + let alt = if keystroke.alt { "M-" } else { "" }; + let cmd = if keystroke.cmd { "D-" } else { "" }; + let end = if special { ">" } else { "" }; + + let key = format!("{start}{shift}{ctrl}{alt}{cmd}{}{end}", keystroke.key); + + self.nvim + .input(&key) + .await + .expect("Could not input keystroke"); + } + + let window_id = self.window_id; + self.cx.dispatch_keystroke(window_id, keystroke, false); + } + + pub async fn simulate_shared_keystrokes( + &mut self, + keystroke_texts: [&str; COUNT], + ) { + for keystroke_text in keystroke_texts.into_iter() { + self.simulate_shared_keystroke(keystroke_text).await; + } + } + + pub async fn set_shared_state(&mut self, marked_text: &str) { + self.set_state(marked_text, Mode::Normal); + + #[cfg(feature = "neovim")] + { + let cursor_point = + self.editor(|editor, cx| editor.selections.newest::(cx)); + let nvim_buffer = self + .nvim + .get_current_buf() + .await + .expect("Could not get neovim buffer"); + let mut lines = self + .buffer_text() + .lines() + .map(|line| line.to_string()) + .collect::>(); + + if lines.len() > 1 { + // Add final newline which is missing from buffer_text + lines.push("".to_string()); + } + + nvim_buffer + .set_lines(0, -1, false, lines) + .await + .expect("Could not set nvim buffer text"); + + self.nvim + .input("") + .await + .expect("Could not send escape to nvim"); + self.nvim + .input("") + .await + .expect("Could not send escape to nvim"); + + let nvim_window = self + .nvim + .get_current_win() + .await + .expect("Could not get neovim window"); + nvim_window + .set_cursor(( + cursor_point.head().row as i64 + 1, + cursor_point.head().column as i64, + )) + .await + .expect("Could not set nvim cursor position"); + } + } + + pub async fn assert_state_matches(&mut self) { + assert_eq!(self.neovim_text().await, self.buffer_text()); + + let zed_head = self.update_editor(|editor, cx| editor.selections.newest_display(cx).head()); + assert_eq!(self.neovim_head().await, zed_head); + + if let Some(neovim_mode) = self.neovim_mode().await { + assert_eq!(neovim_mode, self.mode()); + } + } + + #[cfg(feature = "neovim")] + pub async fn neovim_text(&mut self) -> String { + let nvim_buffer = self + .nvim + .get_current_buf() + .await + .expect("Could not get neovim buffer"); + let text = nvim_buffer + .get_lines(0, -1, false) + .await + .expect("Could not get buffer text") + .join("\n"); + + self.write_test_data(text.clone(), "text"); + text + } + + #[cfg(not(feature = "neovim"))] + pub async fn neovim_text(&mut self) -> String { + self.read_test_data("text") + } + + #[cfg(feature = "neovim")] + pub async fn neovim_head(&mut self) -> DisplayPoint { + let nvim_row: u32 = self + .nvim + .command_output("echo line('.')") + .await + .unwrap() + .parse::() + .unwrap() + - 1; // Neovim rows start at 1 + let nvim_column: u32 = self + .nvim + .command_output("echo col('.')") + .await + .unwrap() + .parse::() + .unwrap() + - 1; // Neovim columns start at 1 + + let serialized = format!("{},{}", nvim_row.to_string(), nvim_column.to_string()); + self.write_test_data(serialized, "head"); + + DisplayPoint::new(nvim_row, nvim_column) + } + + #[cfg(not(feature = "neovim"))] + pub async fn neovim_head(&mut self) -> DisplayPoint { + let serialized = self.read_test_data("head"); + let mut components = serialized.split(','); + let nvim_row = components.next().unwrap().parse::().unwrap(); + let nvim_column = components.next().unwrap().parse::().unwrap(); + + DisplayPoint::new(nvim_row, nvim_column) + } + + #[cfg(feature = "neovim")] + pub async fn neovim_mode(&mut self) -> Option { + let nvim_mode_text = self + .nvim + .get_mode() + .await + .expect("Could not get mode") + .into_iter() + .find_map(|(key, value)| { + if key.as_str() == Some("mode") { + Some(value.as_str().unwrap().to_owned()) + } else { + None + } + }) + .expect("Could not find mode value"); + + let mode = match nvim_mode_text.as_ref() { + "i" => Some(Mode::Insert), + "n" => Some(Mode::Normal), + "v" => Some(Mode::Visual { line: false }), + "V" => Some(Mode::Visual { line: true }), + _ => None, + }; + + let serialized = serde_json::to_string(&mode).expect("Could not serialize mode"); + + self.write_test_data(serialized, "mode"); + + mode + } + + #[cfg(not(feature = "neovim"))] + pub async fn neovim_mode(&mut self) -> Option { + let serialized = self.read_test_data("mode"); + serde_json::from_str(&serialized).expect("Could not deserialize test data") + } + + fn test_data_directory(&self) -> PathBuf { + let mut data_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + data_path.push("test_data"); + data_path.push(self.test_case_id); + data_path + } + + fn next_data_path(&mut self, kind: &str) -> PathBuf { + let mut data_path = self.test_data_directory(); + data_path.push(format!("{}{}.txt", self.data_counter, kind)); + self.data_counter += 1; + data_path + } + + #[cfg(not(feature = "neovim"))] + fn read_test_data(&mut self, kind: &str) -> String { + let path = self.next_data_path(kind); + std::fs::read_to_string(path).expect( + "Could not read test data. Is it generated? Try running test with '--features neovim'", + ) + } + + #[cfg(feature = "neovim")] + fn write_test_data(&mut self, data: String, kind: &str) { + let path = self.next_data_path(kind); + std::fs::create_dir_all(path.parent().unwrap()) + .expect("Could not create test data directory"); + std::fs::write(path, data).expect("Could not write out test data"); + } + + #[cfg(feature = "neovim")] + fn clear_test_data(&self) { + // If the path does not exist, no biggy, we will create it + std::fs::remove_dir_all(self.test_data_directory()).ok(); + } + + pub async fn assert_binding_matches( + &mut self, + keystrokes: [&str; COUNT], + initial_state: &str, + ) { + dbg!(keystrokes, initial_state); + self.set_shared_state(initial_state).await; + self.simulate_shared_keystrokes(keystrokes).await; + self.assert_state_matches().await; + } + + pub fn binding( + self, + keystrokes: [&'static str; COUNT], + ) -> NeovimBackedBindingTestContext<'a, COUNT> { + NeovimBackedBindingTestContext::new(keystrokes, self) + } +} + +#[derive(Clone)] +struct NvimHandler {} + +#[cfg(feature = "neovim")] +#[async_trait] +impl Handler for NvimHandler { + type Writer = nvim_rs::compat::tokio::Compat; + + async fn handle_request( + &self, + _event_name: String, + _arguments: Vec, + _neovim: Neovim, + ) -> Result { + unimplemented!(); + } + + async fn handle_notify( + &self, + _event_name: String, + _arguments: Vec, + _neovim: Neovim, + ) { + } +} + +impl<'a> Deref for NeovimBackedTestContext<'a> { + type Target = VimTestContext<'a>; + + fn deref(&self) -> &Self::Target { + &self.cx + } +} + +impl<'a> DerefMut for NeovimBackedTestContext<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.cx + } +} diff --git a/crates/vim/src/test_contexts/vim_binding_test_context.rs b/crates/vim/src/test_contexts/vim_binding_test_context.rs new file mode 100644 index 0000000000..0974684a34 --- /dev/null +++ b/crates/vim/src/test_contexts/vim_binding_test_context.rs @@ -0,0 +1,69 @@ +use std::ops::{Deref, DerefMut}; + +use crate::*; + +use super::VimTestContext; + +pub struct VimBindingTestContext<'a, const COUNT: usize> { + cx: VimTestContext<'a>, + keystrokes_under_test: [&'static str; COUNT], + mode_before: Mode, + mode_after: Mode, +} + +impl<'a, const COUNT: usize> VimBindingTestContext<'a, COUNT> { + pub fn new( + keystrokes_under_test: [&'static str; COUNT], + mode_before: Mode, + mode_after: Mode, + cx: VimTestContext<'a>, + ) -> Self { + Self { + cx, + keystrokes_under_test, + mode_before, + mode_after, + } + } + + pub fn binding( + self, + keystrokes_under_test: [&'static str; NEW_COUNT], + ) -> VimBindingTestContext<'a, NEW_COUNT> { + VimBindingTestContext { + keystrokes_under_test, + cx: self.cx, + mode_before: self.mode_before, + mode_after: self.mode_after, + } + } + + pub fn mode_after(mut self, mode_after: Mode) -> Self { + self.mode_after = mode_after; + self + } + + pub fn assert(&mut self, initial_state: &str, state_after: &str) { + self.cx.assert_binding( + self.keystrokes_under_test, + initial_state, + self.mode_before, + state_after, + self.mode_after, + ) + } +} + +impl<'a, const COUNT: usize> Deref for VimBindingTestContext<'a, COUNT> { + type Target = VimTestContext<'a>; + + fn deref(&self) -> &Self::Target { + &self.cx + } +} + +impl<'a, const COUNT: usize> DerefMut for VimBindingTestContext<'a, COUNT> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.cx + } +} diff --git a/crates/vim/src/vim_test_context.rs b/crates/vim/src/test_contexts/vim_test_context.rs similarity index 75% rename from crates/vim/src/vim_test_context.rs rename to crates/vim/src/test_contexts/vim_test_context.rs index 0e77b05ba2..229b5e0a8e 100644 --- a/crates/vim/src/vim_test_context.rs +++ b/crates/vim/src/test_contexts/vim_test_context.rs @@ -8,6 +8,8 @@ use workspace::{pane, AppState, WorkspaceHandle}; use crate::{state::Operator, *}; +use super::VimBindingTestContext; + pub struct VimTestContext<'a> { cx: EditorTestContext<'a>, workspace: ViewHandle, @@ -168,67 +170,3 @@ impl<'a> DerefMut for VimTestContext<'a> { &mut self.cx } } - -pub struct VimBindingTestContext<'a, const COUNT: usize> { - cx: VimTestContext<'a>, - keystrokes_under_test: [&'static str; COUNT], - mode_before: Mode, - mode_after: Mode, -} - -impl<'a, const COUNT: usize> VimBindingTestContext<'a, COUNT> { - pub fn new( - keystrokes_under_test: [&'static str; COUNT], - mode_before: Mode, - mode_after: Mode, - cx: VimTestContext<'a>, - ) -> Self { - Self { - cx, - keystrokes_under_test, - mode_before, - mode_after, - } - } - - pub fn binding( - self, - keystrokes_under_test: [&'static str; NEW_COUNT], - ) -> VimBindingTestContext<'a, NEW_COUNT> { - VimBindingTestContext { - keystrokes_under_test, - cx: self.cx, - mode_before: self.mode_before, - mode_after: self.mode_after, - } - } - - pub fn mode_after(mut self, mode_after: Mode) -> Self { - self.mode_after = mode_after; - self - } - - pub fn assert(&mut self, initial_state: &str, state_after: &str) { - self.cx.assert_binding( - self.keystrokes_under_test, - initial_state, - self.mode_before, - state_after, - self.mode_after, - ) - } -} - -impl<'a, const COUNT: usize> Deref for VimBindingTestContext<'a, COUNT> { - type Target = VimTestContext<'a>; - - fn deref(&self) -> &Self::Target { - &self.cx - } -} - -impl<'a, const COUNT: usize> DerefMut for VimBindingTestContext<'a, COUNT> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.cx - } -} diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index ecad33ce3f..234eb0361b 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -1,10 +1,11 @@ #[cfg(test)] -mod vim_test_context; +mod test_contexts; mod editor_events; mod insert; mod motion; mod normal; +mod object; mod state; mod utils; mod visual; @@ -32,6 +33,7 @@ pub fn init(cx: &mut MutableAppContext) { normal::init(cx); visual::init(cx); insert::init(cx); + object::init(cx); motion::init(cx); // Vim Actions @@ -144,7 +146,8 @@ impl Vim { } fn pop_operator(&mut self, cx: &mut MutableAppContext) -> Operator { - let popped_operator = self.state.operator_stack.pop().expect("Operator popped when no operator was on the stack. This likely means there is an invalid keymap config"); + let popped_operator = self.state.operator_stack.pop() + .expect("Operator popped when no operator was on the stack. This likely means there is an invalid keymap config"); self.sync_vim_settings(cx); popped_operator } @@ -210,7 +213,10 @@ mod test { use indoc::indoc; use search::BufferSearchBar; - use crate::{state::Mode, vim_test_context::VimTestContext}; + use crate::{ + state::Mode, + test_contexts::{NeovimBackedTestContext, VimTestContext}, + }; #[gpui::test] async fn test_initially_disabled(cx: &mut gpui::TestAppContext) { @@ -219,6 +225,19 @@ mod test { cx.assert_editor_state("hjklˇ"); } + #[gpui::test] + async fn test_neovim(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new("test_neovim", cx).await; + + cx.simulate_shared_keystroke("i").await; + cx.simulate_shared_keystrokes([ + "shift-T", "e", "s", "t", " ", "t", "e", "s", "t", "escape", "0", "d", "w", + ]) + .await; + cx.assert_state_matches().await; + cx.assert_editor_state("ˇtest"); + } + #[gpui::test] async fn test_toggle_through_settings(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; diff --git a/crates/vim/src/visual.rs b/crates/vim/src/visual.rs index d468393027..bbd6c8cfd9 100644 --- a/crates/vim/src/visual.rs +++ b/crates/vim/src/visual.rs @@ -6,7 +6,7 @@ use gpui::{actions, MutableAppContext, ViewContext}; use language::{AutoindentMode, SelectionGoal}; use workspace::Workspace; -use crate::{motion::Motion, state::Mode, utils::copy_selections_content, Vim}; +use crate::{motion::Motion, object::Object, state::Mode, utils::copy_selections_content, Vim}; actions!(vim, [VisualDelete, VisualChange, VisualYank, VisualPaste]); @@ -43,6 +43,8 @@ pub fn visual_motion(motion: Motion, cx: &mut MutableAppContext) { }); } +pub fn visual_object(_object: Object, _cx: &mut MutableAppContext) {} + pub fn change(_: &mut Workspace, _: &VisualChange, cx: &mut ViewContext) { Vim::update(cx, |vim, cx| { vim.update_active_editor(cx, |editor, cx| { @@ -274,7 +276,7 @@ pub fn paste(_: &mut Workspace, _: &VisualPaste, cx: &mut ViewContext mod test { use indoc::indoc; - use crate::{state::Mode, vim_test_context::VimTestContext}; + use crate::{state::Mode, test_contexts::VimTestContext}; #[gpui::test] async fn test_enter_visual_mode(cx: &mut gpui::TestAppContext) { diff --git a/crates/vim/test_data/test_change_around_sentence/0text.txt b/crates/vim/test_data/test_change_around_sentence/0text.txt new file mode 100644 index 0000000000..d2b92e8000 --- /dev/null +++ b/crates/vim/test_data/test_change_around_sentence/0text.txt @@ -0,0 +1 @@ +Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence/10head.txt b/crates/vim/test_data/test_change_around_sentence/10head.txt new file mode 100644 index 0000000000..5df80e23c6 --- /dev/null +++ b/crates/vim/test_data/test_change_around_sentence/10head.txt @@ -0,0 +1 @@ +0,16 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence/1head.txt b/crates/vim/test_data/test_change_around_sentence/1head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_change_around_sentence/1head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence/2mode.txt b/crates/vim/test_data/test_change_around_sentence/2mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_sentence/2mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence/3text.txt b/crates/vim/test_data/test_change_around_sentence/3text.txt new file mode 100644 index 0000000000..d2b92e8000 --- /dev/null +++ b/crates/vim/test_data/test_change_around_sentence/3text.txt @@ -0,0 +1 @@ +Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence/4head.txt b/crates/vim/test_data/test_change_around_sentence/4head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_change_around_sentence/4head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence/5mode.txt b/crates/vim/test_data/test_change_around_sentence/5mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_sentence/5mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence/6text.txt b/crates/vim/test_data/test_change_around_sentence/6text.txt new file mode 100644 index 0000000000..d2b92e8000 --- /dev/null +++ b/crates/vim/test_data/test_change_around_sentence/6text.txt @@ -0,0 +1 @@ +Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence/7head.txt b/crates/vim/test_data/test_change_around_sentence/7head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_change_around_sentence/7head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence/8mode.txt b/crates/vim/test_data/test_change_around_sentence/8mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_sentence/8mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence/9text.txt b/crates/vim/test_data/test_change_around_sentence/9text.txt new file mode 100644 index 0000000000..5e3a12964d --- /dev/null +++ b/crates/vim/test_data/test_change_around_sentence/9text.txt @@ -0,0 +1 @@ +The quick brown? Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/0text.txt b/crates/vim/test_data/test_change_around_word/0text.txt new file mode 100644 index 0000000000..366beed733 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/0text.txt @@ -0,0 +1,12 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/100head.txt b/crates/vim/test_data/test_change_around_word/100head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/100head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/101mode.txt b/crates/vim/test_data/test_change_around_word/101mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/101mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/102text.txt b/crates/vim/test_data/test_change_around_word/102text.txt new file mode 100644 index 0000000000..c8a5876179 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/102text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/103head.txt b/crates/vim/test_data/test_change_around_word/103head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/103head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/104mode.txt b/crates/vim/test_data/test_change_around_word/104mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/104mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/105text.txt b/crates/vim/test_data/test_change_around_word/105text.txt new file mode 100644 index 0000000000..c8a5876179 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/105text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/106head.txt b/crates/vim/test_data/test_change_around_word/106head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/106head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/107mode.txt b/crates/vim/test_data/test_change_around_word/107mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/107mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/108text.txt b/crates/vim/test_data/test_change_around_word/108text.txt new file mode 100644 index 0000000000..c8a5876179 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/108text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/109head.txt b/crates/vim/test_data/test_change_around_word/109head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/109head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/10head.txt b/crates/vim/test_data/test_change_around_word/10head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/10head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/110mode.txt b/crates/vim/test_data/test_change_around_word/110mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/110mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/111text.txt b/crates/vim/test_data/test_change_around_word/111text.txt new file mode 100644 index 0000000000..f05cdd88c1 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/111text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/112head.txt b/crates/vim/test_data/test_change_around_word/112head.txt new file mode 100644 index 0000000000..da809253d8 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/112head.txt @@ -0,0 +1 @@ +6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/113mode.txt b/crates/vim/test_data/test_change_around_word/113mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/113mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/114text.txt b/crates/vim/test_data/test_change_around_word/114text.txt new file mode 100644 index 0000000000..f05cdd88c1 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/114text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/115head.txt b/crates/vim/test_data/test_change_around_word/115head.txt new file mode 100644 index 0000000000..c5a50698dd --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/115head.txt @@ -0,0 +1 @@ +6,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/116mode.txt b/crates/vim/test_data/test_change_around_word/116mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/116mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/117text.txt b/crates/vim/test_data/test_change_around_word/117text.txt new file mode 100644 index 0000000000..0e9094161a --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/117text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/118head.txt b/crates/vim/test_data/test_change_around_word/118head.txt new file mode 100644 index 0000000000..d933eb81fa --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/118head.txt @@ -0,0 +1 @@ +6,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/119mode.txt b/crates/vim/test_data/test_change_around_word/119mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/119mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/11mode.txt b/crates/vim/test_data/test_change_around_word/11mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/11mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/120text.txt b/crates/vim/test_data/test_change_around_word/120text.txt new file mode 100644 index 0000000000..fa73eb37b8 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/120text.txt @@ -0,0 +1,10 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/121head.txt b/crates/vim/test_data/test_change_around_word/121head.txt new file mode 100644 index 0000000000..72c23ba506 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/121head.txt @@ -0,0 +1 @@ +7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/122mode.txt b/crates/vim/test_data/test_change_around_word/122mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/122mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/123text.txt b/crates/vim/test_data/test_change_around_word/123text.txt new file mode 100644 index 0000000000..4fe33b47eb --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/123text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/124head.txt b/crates/vim/test_data/test_change_around_word/124head.txt new file mode 100644 index 0000000000..ded56a5926 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/124head.txt @@ -0,0 +1 @@ +8,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/125mode.txt b/crates/vim/test_data/test_change_around_word/125mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/125mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/126text.txt b/crates/vim/test_data/test_change_around_word/126text.txt new file mode 100644 index 0000000000..35dd5816fc --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/126text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/127head.txt b/crates/vim/test_data/test_change_around_word/127head.txt new file mode 100644 index 0000000000..8e69e0ad79 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/127head.txt @@ -0,0 +1 @@ +9,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/128mode.txt b/crates/vim/test_data/test_change_around_word/128mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/128mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/129text.txt b/crates/vim/test_data/test_change_around_word/129text.txt new file mode 100644 index 0000000000..1bac4587a7 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/129text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/12text.txt b/crates/vim/test_data/test_change_around_word/12text.txt new file mode 100644 index 0000000000..2feb66cfb9 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/12text.txt @@ -0,0 +1,12 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/130head.txt b/crates/vim/test_data/test_change_around_word/130head.txt new file mode 100644 index 0000000000..edb3544f9a --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/130head.txt @@ -0,0 +1 @@ +9,2 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/131mode.txt b/crates/vim/test_data/test_change_around_word/131mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/131mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/132text.txt b/crates/vim/test_data/test_change_around_word/132text.txt new file mode 100644 index 0000000000..912316a9f7 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/132text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_change_around_word/133head.txt b/crates/vim/test_data/test_change_around_word/133head.txt new file mode 100644 index 0000000000..00f5ae6cbb --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/133head.txt @@ -0,0 +1 @@ +10,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/134mode.txt b/crates/vim/test_data/test_change_around_word/134mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/134mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/135text.txt b/crates/vim/test_data/test_change_around_word/135text.txt new file mode 100644 index 0000000000..1563710058 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/135text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_change_around_word/136head.txt b/crates/vim/test_data/test_change_around_word/136head.txt new file mode 100644 index 0000000000..94dbddc699 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/136head.txt @@ -0,0 +1 @@ +11,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/137mode.txt b/crates/vim/test_data/test_change_around_word/137mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/137mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/13head.txt b/crates/vim/test_data/test_change_around_word/13head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/13head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/14mode.txt b/crates/vim/test_data/test_change_around_word/14mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/14mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/15text.txt b/crates/vim/test_data/test_change_around_word/15text.txt new file mode 100644 index 0000000000..262feeae96 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/15text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/16head.txt b/crates/vim/test_data/test_change_around_word/16head.txt new file mode 100644 index 0000000000..bd712da63a --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/16head.txt @@ -0,0 +1 @@ +1,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/17mode.txt b/crates/vim/test_data/test_change_around_word/17mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/17mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/18text.txt b/crates/vim/test_data/test_change_around_word/18text.txt new file mode 100644 index 0000000000..ba0aea82d9 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/18text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/19head.txt b/crates/vim/test_data/test_change_around_word/19head.txt new file mode 100644 index 0000000000..28ce8bcc5c --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/19head.txt @@ -0,0 +1 @@ +2,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/1head.txt b/crates/vim/test_data/test_change_around_word/1head.txt new file mode 100644 index 0000000000..352f6067e6 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/1head.txt @@ -0,0 +1 @@ +0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/20mode.txt b/crates/vim/test_data/test_change_around_word/20mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/20mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/21text.txt b/crates/vim/test_data/test_change_around_word/21text.txt new file mode 100644 index 0000000000..783e049f86 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/21text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/22head.txt b/crates/vim/test_data/test_change_around_word/22head.txt new file mode 100644 index 0000000000..7cad756336 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/22head.txt @@ -0,0 +1 @@ +3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/23mode.txt b/crates/vim/test_data/test_change_around_word/23mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/23mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/24text.txt b/crates/vim/test_data/test_change_around_word/24text.txt new file mode 100644 index 0000000000..783e049f86 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/24text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/25head.txt b/crates/vim/test_data/test_change_around_word/25head.txt new file mode 100644 index 0000000000..b26cd1f1d3 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/25head.txt @@ -0,0 +1 @@ +4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/26mode.txt b/crates/vim/test_data/test_change_around_word/26mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/26mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/27text.txt b/crates/vim/test_data/test_change_around_word/27text.txt new file mode 100644 index 0000000000..252419d291 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/27text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + +-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/28head.txt b/crates/vim/test_data/test_change_around_word/28head.txt new file mode 100644 index 0000000000..880fda949a --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/28head.txt @@ -0,0 +1 @@ +5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/29mode.txt b/crates/vim/test_data/test_change_around_word/29mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/29mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/2mode.txt b/crates/vim/test_data/test_change_around_word/2mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/2mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/30text.txt b/crates/vim/test_data/test_change_around_word/30text.txt new file mode 100644 index 0000000000..609747299b --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/30text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/31head.txt b/crates/vim/test_data/test_change_around_word/31head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/31head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/32mode.txt b/crates/vim/test_data/test_change_around_word/32mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/32mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/33text.txt b/crates/vim/test_data/test_change_around_word/33text.txt new file mode 100644 index 0000000000..12ede0f513 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/33text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +Thequick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/34head.txt b/crates/vim/test_data/test_change_around_word/34head.txt new file mode 100644 index 0000000000..c20df71d5e --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/34head.txt @@ -0,0 +1 @@ +6,3 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/35mode.txt b/crates/vim/test_data/test_change_around_word/35mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/35mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/36text.txt b/crates/vim/test_data/test_change_around_word/36text.txt new file mode 100644 index 0000000000..4a6d943982 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/36text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/37head.txt b/crates/vim/test_data/test_change_around_word/37head.txt new file mode 100644 index 0000000000..95fb9c638e --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/37head.txt @@ -0,0 +1 @@ +6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/38mode.txt b/crates/vim/test_data/test_change_around_word/38mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/38mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/39text.txt b/crates/vim/test_data/test_change_around_word/39text.txt new file mode 100644 index 0000000000..4a6d943982 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/39text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/3text.txt b/crates/vim/test_data/test_change_around_word/3text.txt new file mode 100644 index 0000000000..366beed733 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/3text.txt @@ -0,0 +1,12 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/40head.txt b/crates/vim/test_data/test_change_around_word/40head.txt new file mode 100644 index 0000000000..95fb9c638e --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/40head.txt @@ -0,0 +1 @@ +6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/41mode.txt b/crates/vim/test_data/test_change_around_word/41mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/41mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/42text.txt b/crates/vim/test_data/test_change_around_word/42text.txt new file mode 100644 index 0000000000..f05cdd88c1 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/42text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/43head.txt b/crates/vim/test_data/test_change_around_word/43head.txt new file mode 100644 index 0000000000..da809253d8 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/43head.txt @@ -0,0 +1 @@ +6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/44mode.txt b/crates/vim/test_data/test_change_around_word/44mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/44mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/45text.txt b/crates/vim/test_data/test_change_around_word/45text.txt new file mode 100644 index 0000000000..f05cdd88c1 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/45text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/46head.txt b/crates/vim/test_data/test_change_around_word/46head.txt new file mode 100644 index 0000000000..c5a50698dd --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/46head.txt @@ -0,0 +1 @@ +6,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/47mode.txt b/crates/vim/test_data/test_change_around_word/47mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/47mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/48text.txt b/crates/vim/test_data/test_change_around_word/48text.txt new file mode 100644 index 0000000000..13b7d1cb51 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/48text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/49head.txt b/crates/vim/test_data/test_change_around_word/49head.txt new file mode 100644 index 0000000000..d933eb81fa --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/49head.txt @@ -0,0 +1 @@ +6,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/4head.txt b/crates/vim/test_data/test_change_around_word/4head.txt new file mode 100644 index 0000000000..352f6067e6 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/4head.txt @@ -0,0 +1 @@ +0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/50mode.txt b/crates/vim/test_data/test_change_around_word/50mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/50mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/51text.txt b/crates/vim/test_data/test_change_around_word/51text.txt new file mode 100644 index 0000000000..3976ef9c85 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/51text.txt @@ -0,0 +1,10 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown +-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/52head.txt b/crates/vim/test_data/test_change_around_word/52head.txt new file mode 100644 index 0000000000..72c23ba506 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/52head.txt @@ -0,0 +1 @@ +7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/53mode.txt b/crates/vim/test_data/test_change_around_word/53mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/53mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/54text.txt b/crates/vim/test_data/test_change_around_word/54text.txt new file mode 100644 index 0000000000..b5ca31babe --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/54text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + +-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/55head.txt b/crates/vim/test_data/test_change_around_word/55head.txt new file mode 100644 index 0000000000..ded56a5926 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/55head.txt @@ -0,0 +1 @@ +8,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/56mode.txt b/crates/vim/test_data/test_change_around_word/56mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/56mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/57text.txt b/crates/vim/test_data/test_change_around_word/57text.txt new file mode 100644 index 0000000000..0eaface506 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/57text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + +-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/58head.txt b/crates/vim/test_data/test_change_around_word/58head.txt new file mode 100644 index 0000000000..8e69e0ad79 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/58head.txt @@ -0,0 +1 @@ +9,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/59mode.txt b/crates/vim/test_data/test_change_around_word/59mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/59mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/5mode.txt b/crates/vim/test_data/test_change_around_word/5mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/5mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/60text.txt b/crates/vim/test_data/test_change_around_word/60text.txt new file mode 100644 index 0000000000..d8b9b05b79 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/60text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/61head.txt b/crates/vim/test_data/test_change_around_word/61head.txt new file mode 100644 index 0000000000..e0460d1bc5 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/61head.txt @@ -0,0 +1 @@ +9,6 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/62mode.txt b/crates/vim/test_data/test_change_around_word/62mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/62mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/63text.txt b/crates/vim/test_data/test_change_around_word/63text.txt new file mode 100644 index 0000000000..912316a9f7 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/63text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_change_around_word/64head.txt b/crates/vim/test_data/test_change_around_word/64head.txt new file mode 100644 index 0000000000..00f5ae6cbb --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/64head.txt @@ -0,0 +1 @@ +10,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/65mode.txt b/crates/vim/test_data/test_change_around_word/65mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/65mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/66text.txt b/crates/vim/test_data/test_change_around_word/66text.txt new file mode 100644 index 0000000000..1563710058 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/66text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_change_around_word/67head.txt b/crates/vim/test_data/test_change_around_word/67head.txt new file mode 100644 index 0000000000..94dbddc699 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/67head.txt @@ -0,0 +1 @@ +11,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/68mode.txt b/crates/vim/test_data/test_change_around_word/68mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/68mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/69text.txt b/crates/vim/test_data/test_change_around_word/69text.txt new file mode 100644 index 0000000000..366beed733 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/69text.txt @@ -0,0 +1,12 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/6text.txt b/crates/vim/test_data/test_change_around_word/6text.txt new file mode 100644 index 0000000000..3678c219a3 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/6text.txt @@ -0,0 +1,11 @@ +The quick brown jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/70head.txt b/crates/vim/test_data/test_change_around_word/70head.txt new file mode 100644 index 0000000000..352f6067e6 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/70head.txt @@ -0,0 +1 @@ +0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/71mode.txt b/crates/vim/test_data/test_change_around_word/71mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/71mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/72text.txt b/crates/vim/test_data/test_change_around_word/72text.txt new file mode 100644 index 0000000000..366beed733 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/72text.txt @@ -0,0 +1,12 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/73head.txt b/crates/vim/test_data/test_change_around_word/73head.txt new file mode 100644 index 0000000000..352f6067e6 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/73head.txt @@ -0,0 +1 @@ +0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/74mode.txt b/crates/vim/test_data/test_change_around_word/74mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/74mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/75text.txt b/crates/vim/test_data/test_change_around_word/75text.txt new file mode 100644 index 0000000000..3678c219a3 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/75text.txt @@ -0,0 +1,11 @@ +The quick brown jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/76head.txt b/crates/vim/test_data/test_change_around_word/76head.txt new file mode 100644 index 0000000000..5f7b20f0d9 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/76head.txt @@ -0,0 +1 @@ +0,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/77mode.txt b/crates/vim/test_data/test_change_around_word/77mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/77mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/78text.txt b/crates/vim/test_data/test_change_around_word/78text.txt new file mode 100644 index 0000000000..2feb66cfb9 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/78text.txt @@ -0,0 +1,12 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/79head.txt b/crates/vim/test_data/test_change_around_word/79head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/79head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/7head.txt b/crates/vim/test_data/test_change_around_word/7head.txt new file mode 100644 index 0000000000..5f7b20f0d9 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/7head.txt @@ -0,0 +1 @@ +0,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/80mode.txt b/crates/vim/test_data/test_change_around_word/80mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/80mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/81text.txt b/crates/vim/test_data/test_change_around_word/81text.txt new file mode 100644 index 0000000000..2feb66cfb9 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/81text.txt @@ -0,0 +1,12 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/82head.txt b/crates/vim/test_data/test_change_around_word/82head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/82head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/83mode.txt b/crates/vim/test_data/test_change_around_word/83mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/83mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/84text.txt b/crates/vim/test_data/test_change_around_word/84text.txt new file mode 100644 index 0000000000..262feeae96 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/84text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/85head.txt b/crates/vim/test_data/test_change_around_word/85head.txt new file mode 100644 index 0000000000..bd712da63a --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/85head.txt @@ -0,0 +1 @@ +1,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/86mode.txt b/crates/vim/test_data/test_change_around_word/86mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/86mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/87text.txt b/crates/vim/test_data/test_change_around_word/87text.txt new file mode 100644 index 0000000000..ba0aea82d9 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/87text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/88head.txt b/crates/vim/test_data/test_change_around_word/88head.txt new file mode 100644 index 0000000000..28ce8bcc5c --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/88head.txt @@ -0,0 +1 @@ +2,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/89mode.txt b/crates/vim/test_data/test_change_around_word/89mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/89mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/8mode.txt b/crates/vim/test_data/test_change_around_word/8mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/8mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/90text.txt b/crates/vim/test_data/test_change_around_word/90text.txt new file mode 100644 index 0000000000..783e049f86 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/90text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/91head.txt b/crates/vim/test_data/test_change_around_word/91head.txt new file mode 100644 index 0000000000..7cad756336 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/91head.txt @@ -0,0 +1 @@ +3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/92mode.txt b/crates/vim/test_data/test_change_around_word/92mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/92mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/93text.txt b/crates/vim/test_data/test_change_around_word/93text.txt new file mode 100644 index 0000000000..783e049f86 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/93text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/94head.txt b/crates/vim/test_data/test_change_around_word/94head.txt new file mode 100644 index 0000000000..b26cd1f1d3 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/94head.txt @@ -0,0 +1 @@ +4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/95mode.txt b/crates/vim/test_data/test_change_around_word/95mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/95mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/96text.txt b/crates/vim/test_data/test_change_around_word/96text.txt new file mode 100644 index 0000000000..523f6002f4 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/96text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + + brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/97head.txt b/crates/vim/test_data/test_change_around_word/97head.txt new file mode 100644 index 0000000000..880fda949a --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/97head.txt @@ -0,0 +1 @@ +5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/98mode.txt b/crates/vim/test_data/test_change_around_word/98mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/98mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/99text.txt b/crates/vim/test_data/test_change_around_word/99text.txt new file mode 100644 index 0000000000..c8a5876179 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/99text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_around_word/9text.txt b/crates/vim/test_data/test_change_around_word/9text.txt new file mode 100644 index 0000000000..2feb66cfb9 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word/9text.txt @@ -0,0 +1,12 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_sentence/0text.txt b/crates/vim/test_data/test_change_in_sentence/0text.txt new file mode 100644 index 0000000000..fd36730b8d --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/0text.txt @@ -0,0 +1 @@ + Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/10head.txt b/crates/vim/test_data/test_change_in_sentence/10head.txt new file mode 100644 index 0000000000..5df80e23c6 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/10head.txt @@ -0,0 +1 @@ +0,16 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/11mode.txt b/crates/vim/test_data/test_change_in_sentence/11mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/11mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/12text.txt b/crates/vim/test_data/test_change_in_sentence/12text.txt new file mode 100644 index 0000000000..40eacc6489 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/12text.txt @@ -0,0 +1 @@ +The quick brown? Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/13head.txt b/crates/vim/test_data/test_change_in_sentence/13head.txt new file mode 100644 index 0000000000..40d2f2ab55 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/13head.txt @@ -0,0 +1 @@ +0,17 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/14mode.txt b/crates/vim/test_data/test_change_in_sentence/14mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/14mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/15text.txt b/crates/vim/test_data/test_change_in_sentence/15text.txt new file mode 100644 index 0000000000..40eacc6489 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/15text.txt @@ -0,0 +1 @@ +The quick brown? Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/16head.txt b/crates/vim/test_data/test_change_in_sentence/16head.txt new file mode 100644 index 0000000000..40d2f2ab55 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/16head.txt @@ -0,0 +1 @@ +0,17 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/17mode.txt b/crates/vim/test_data/test_change_in_sentence/17mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/17mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/18text.txt b/crates/vim/test_data/test_change_in_sentence/18text.txt new file mode 100644 index 0000000000..40eacc6489 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/18text.txt @@ -0,0 +1 @@ +The quick brown? Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/19head.txt b/crates/vim/test_data/test_change_in_sentence/19head.txt new file mode 100644 index 0000000000..40d2f2ab55 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/19head.txt @@ -0,0 +1 @@ +0,17 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/1head.txt b/crates/vim/test_data/test_change_in_sentence/1head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/1head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/20mode.txt b/crates/vim/test_data/test_change_in_sentence/20mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/20mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/21text.txt b/crates/vim/test_data/test_change_in_sentence/21text.txt new file mode 100644 index 0000000000..ef17a5f7fb --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/21text.txt @@ -0,0 +1 @@ +The quick brown? Fox Jumps!Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/22head.txt b/crates/vim/test_data/test_change_in_sentence/22head.txt new file mode 100644 index 0000000000..17beb4d83f --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/22head.txt @@ -0,0 +1 @@ +0,27 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/23mode.txt b/crates/vim/test_data/test_change_in_sentence/23mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/23mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/24text.txt b/crates/vim/test_data/test_change_in_sentence/24text.txt new file mode 100644 index 0000000000..d81c10974f --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/24text.txt @@ -0,0 +1 @@ +The quick brown? Fox Jumps! \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/25head.txt b/crates/vim/test_data/test_change_in_sentence/25head.txt new file mode 100644 index 0000000000..588ae97095 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/25head.txt @@ -0,0 +1 @@ +0,28 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/26mode.txt b/crates/vim/test_data/test_change_in_sentence/26mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/26mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/27text.txt b/crates/vim/test_data/test_change_in_sentence/27text.txt new file mode 100644 index 0000000000..d81c10974f --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/27text.txt @@ -0,0 +1 @@ +The quick brown? Fox Jumps! \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/28head.txt b/crates/vim/test_data/test_change_in_sentence/28head.txt new file mode 100644 index 0000000000..588ae97095 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/28head.txt @@ -0,0 +1 @@ +0,28 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/29mode.txt b/crates/vim/test_data/test_change_in_sentence/29mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/29mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/2mode.txt b/crates/vim/test_data/test_change_in_sentence/2mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/2mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/30text.txt b/crates/vim/test_data/test_change_in_sentence/30text.txt new file mode 100644 index 0000000000..d81c10974f --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/30text.txt @@ -0,0 +1 @@ +The quick brown? Fox Jumps! \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/31head.txt b/crates/vim/test_data/test_change_in_sentence/31head.txt new file mode 100644 index 0000000000..588ae97095 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/31head.txt @@ -0,0 +1 @@ +0,28 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/32mode.txt b/crates/vim/test_data/test_change_in_sentence/32mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/32mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/33text.txt b/crates/vim/test_data/test_change_in_sentence/33text.txt new file mode 100644 index 0000000000..561ed0800c --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/33text.txt @@ -0,0 +1,2 @@ + The quick +brown fox jumps over diff --git a/crates/vim/test_data/test_change_in_sentence/34head.txt b/crates/vim/test_data/test_change_in_sentence/34head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/34head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/35mode.txt b/crates/vim/test_data/test_change_in_sentence/35mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/35mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/36text.txt b/crates/vim/test_data/test_change_in_sentence/36text.txt new file mode 100644 index 0000000000..561ed0800c --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/36text.txt @@ -0,0 +1,2 @@ + The quick +brown fox jumps over diff --git a/crates/vim/test_data/test_change_in_sentence/37head.txt b/crates/vim/test_data/test_change_in_sentence/37head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/37head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/38mode.txt b/crates/vim/test_data/test_change_in_sentence/38mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/38mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/39text.txt b/crates/vim/test_data/test_change_in_sentence/39text.txt new file mode 100644 index 0000000000..561ed0800c --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/39text.txt @@ -0,0 +1,2 @@ + The quick +brown fox jumps over diff --git a/crates/vim/test_data/test_change_in_sentence/3text.txt b/crates/vim/test_data/test_change_in_sentence/3text.txt new file mode 100644 index 0000000000..fd36730b8d --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/3text.txt @@ -0,0 +1 @@ + Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/40head.txt b/crates/vim/test_data/test_change_in_sentence/40head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/40head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/41mode.txt b/crates/vim/test_data/test_change_in_sentence/41mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/41mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/42text.txt b/crates/vim/test_data/test_change_in_sentence/42text.txt new file mode 100644 index 0000000000..561ed0800c --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/42text.txt @@ -0,0 +1,2 @@ + The quick +brown fox jumps over diff --git a/crates/vim/test_data/test_change_in_sentence/43head.txt b/crates/vim/test_data/test_change_in_sentence/43head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/43head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/44mode.txt b/crates/vim/test_data/test_change_in_sentence/44mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/44mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/45text.txt b/crates/vim/test_data/test_change_in_sentence/45text.txt new file mode 100644 index 0000000000..561ed0800c --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/45text.txt @@ -0,0 +1,2 @@ + The quick +brown fox jumps over diff --git a/crates/vim/test_data/test_change_in_sentence/46head.txt b/crates/vim/test_data/test_change_in_sentence/46head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/46head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/47mode.txt b/crates/vim/test_data/test_change_in_sentence/47mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/47mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/48text.txt b/crates/vim/test_data/test_change_in_sentence/48text.txt new file mode 100644 index 0000000000..bbf10c5693 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/48text.txt @@ -0,0 +1,4 @@ +The quick brown +fox jumps over +the lazy dog.The quick +brown fox jumps over diff --git a/crates/vim/test_data/test_change_in_sentence/49head.txt b/crates/vim/test_data/test_change_in_sentence/49head.txt new file mode 100644 index 0000000000..26560586d9 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/49head.txt @@ -0,0 +1 @@ +2,13 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/4head.txt b/crates/vim/test_data/test_change_in_sentence/4head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/4head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/50mode.txt b/crates/vim/test_data/test_change_in_sentence/50mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/50mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/51text.txt b/crates/vim/test_data/test_change_in_sentence/51text.txt new file mode 100644 index 0000000000..0b535bbbaf --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/51text.txt @@ -0,0 +1,3 @@ +The quick brown +fox jumps over +the lazy dog. diff --git a/crates/vim/test_data/test_change_in_sentence/52head.txt b/crates/vim/test_data/test_change_in_sentence/52head.txt new file mode 100644 index 0000000000..e9aafafade --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/52head.txt @@ -0,0 +1 @@ +2,14 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/53mode.txt b/crates/vim/test_data/test_change_in_sentence/53mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/53mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/54text.txt b/crates/vim/test_data/test_change_in_sentence/54text.txt new file mode 100644 index 0000000000..0b535bbbaf --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/54text.txt @@ -0,0 +1,3 @@ +The quick brown +fox jumps over +the lazy dog. diff --git a/crates/vim/test_data/test_change_in_sentence/55head.txt b/crates/vim/test_data/test_change_in_sentence/55head.txt new file mode 100644 index 0000000000..e9aafafade --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/55head.txt @@ -0,0 +1 @@ +2,14 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/56mode.txt b/crates/vim/test_data/test_change_in_sentence/56mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/56mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/5mode.txt b/crates/vim/test_data/test_change_in_sentence/5mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/5mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/6text.txt b/crates/vim/test_data/test_change_in_sentence/6text.txt new file mode 100644 index 0000000000..fd36730b8d --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/6text.txt @@ -0,0 +1 @@ + Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/7head.txt b/crates/vim/test_data/test_change_in_sentence/7head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/7head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/8mode.txt b/crates/vim/test_data/test_change_in_sentence/8mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/8mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/9text.txt b/crates/vim/test_data/test_change_in_sentence/9text.txt new file mode 100644 index 0000000000..9e4b6dcc50 --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence/9text.txt @@ -0,0 +1 @@ +The quick brown?Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/0text.txt b/crates/vim/test_data/test_change_in_word/0text.txt new file mode 100644 index 0000000000..481389e676 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/0text.txt @@ -0,0 +1,12 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/100head.txt b/crates/vim/test_data/test_change_in_word/100head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/100head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/101mode.txt b/crates/vim/test_data/test_change_in_word/101mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/101mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/102text.txt b/crates/vim/test_data/test_change_in_word/102text.txt new file mode 100644 index 0000000000..d90b2c4e48 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/102text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + + brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/103head.txt b/crates/vim/test_data/test_change_in_word/103head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/103head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/104mode.txt b/crates/vim/test_data/test_change_in_word/104mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/104mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/105text.txt b/crates/vim/test_data/test_change_in_word/105text.txt new file mode 100644 index 0000000000..d90b2c4e48 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/105text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + + brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/106head.txt b/crates/vim/test_data/test_change_in_word/106head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/106head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/107mode.txt b/crates/vim/test_data/test_change_in_word/107mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/107mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/108text.txt b/crates/vim/test_data/test_change_in_word/108text.txt new file mode 100644 index 0000000000..d90b2c4e48 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/108text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + + brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/109head.txt b/crates/vim/test_data/test_change_in_word/109head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/109head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/10head.txt b/crates/vim/test_data/test_change_in_word/10head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/10head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/110mode.txt b/crates/vim/test_data/test_change_in_word/110mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/110mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/111text.txt b/crates/vim/test_data/test_change_in_word/111text.txt new file mode 100644 index 0000000000..7d13d92d35 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/111text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quickbrown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/112head.txt b/crates/vim/test_data/test_change_in_word/112head.txt new file mode 100644 index 0000000000..da809253d8 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/112head.txt @@ -0,0 +1 @@ +6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/113mode.txt b/crates/vim/test_data/test_change_in_word/113mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/113mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/114text.txt b/crates/vim/test_data/test_change_in_word/114text.txt new file mode 100644 index 0000000000..95b76ce25b --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/114text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/115head.txt b/crates/vim/test_data/test_change_in_word/115head.txt new file mode 100644 index 0000000000..c5a50698dd --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/115head.txt @@ -0,0 +1 @@ +6,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/116mode.txt b/crates/vim/test_data/test_change_in_word/116mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/116mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/117text.txt b/crates/vim/test_data/test_change_in_word/117text.txt new file mode 100644 index 0000000000..805a5ebe4c --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/117text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/118head.txt b/crates/vim/test_data/test_change_in_word/118head.txt new file mode 100644 index 0000000000..d933eb81fa --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/118head.txt @@ -0,0 +1 @@ +6,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/119mode.txt b/crates/vim/test_data/test_change_in_word/119mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/119mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/11mode.txt b/crates/vim/test_data/test_change_in_word/11mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/11mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/120text.txt b/crates/vim/test_data/test_change_in_word/120text.txt new file mode 100644 index 0000000000..1d797e34b8 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/120text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/121head.txt b/crates/vim/test_data/test_change_in_word/121head.txt new file mode 100644 index 0000000000..72c23ba506 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/121head.txt @@ -0,0 +1 @@ +7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/122mode.txt b/crates/vim/test_data/test_change_in_word/122mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/122mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/123text.txt b/crates/vim/test_data/test_change_in_word/123text.txt new file mode 100644 index 0000000000..8a35adfa26 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/123text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/124head.txt b/crates/vim/test_data/test_change_in_word/124head.txt new file mode 100644 index 0000000000..ded56a5926 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/124head.txt @@ -0,0 +1 @@ +8,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/125mode.txt b/crates/vim/test_data/test_change_in_word/125mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/125mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/126text.txt b/crates/vim/test_data/test_change_in_word/126text.txt new file mode 100644 index 0000000000..26e9d0a1b6 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/126text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + +fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/127head.txt b/crates/vim/test_data/test_change_in_word/127head.txt new file mode 100644 index 0000000000..8e69e0ad79 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/127head.txt @@ -0,0 +1 @@ +9,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/128mode.txt b/crates/vim/test_data/test_change_in_word/128mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/128mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/129text.txt b/crates/vim/test_data/test_change_in_word/129text.txt new file mode 100644 index 0000000000..c2c9c0edb0 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/129text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/12text.txt b/crates/vim/test_data/test_change_in_word/12text.txt new file mode 100644 index 0000000000..38c6ccbeac --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/12text.txt @@ -0,0 +1,12 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/130head.txt b/crates/vim/test_data/test_change_in_word/130head.txt new file mode 100644 index 0000000000..edb3544f9a --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/130head.txt @@ -0,0 +1 @@ +9,2 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/131mode.txt b/crates/vim/test_data/test_change_in_word/131mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/131mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/132text.txt b/crates/vim/test_data/test_change_in_word/132text.txt new file mode 100644 index 0000000000..4b2cd80611 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/132text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/133head.txt b/crates/vim/test_data/test_change_in_word/133head.txt new file mode 100644 index 0000000000..00f5ae6cbb --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/133head.txt @@ -0,0 +1 @@ +10,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/134mode.txt b/crates/vim/test_data/test_change_in_word/134mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/134mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/135text.txt b/crates/vim/test_data/test_change_in_word/135text.txt new file mode 100644 index 0000000000..e971227436 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/135text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/136head.txt b/crates/vim/test_data/test_change_in_word/136head.txt new file mode 100644 index 0000000000..94dbddc699 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/136head.txt @@ -0,0 +1 @@ +11,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/137mode.txt b/crates/vim/test_data/test_change_in_word/137mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/137mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/13head.txt b/crates/vim/test_data/test_change_in_word/13head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/13head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/14mode.txt b/crates/vim/test_data/test_change_in_word/14mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/14mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/15text.txt b/crates/vim/test_data/test_change_in_word/15text.txt new file mode 100644 index 0000000000..282218da91 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/15text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumpsover +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/16head.txt b/crates/vim/test_data/test_change_in_word/16head.txt new file mode 100644 index 0000000000..bd712da63a --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/16head.txt @@ -0,0 +1 @@ +1,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/17mode.txt b/crates/vim/test_data/test_change_in_word/17mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/17mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/18text.txt b/crates/vim/test_data/test_change_in_word/18text.txt new file mode 100644 index 0000000000..0b042b7c39 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/18text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/19head.txt b/crates/vim/test_data/test_change_in_word/19head.txt new file mode 100644 index 0000000000..28ce8bcc5c --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/19head.txt @@ -0,0 +1 @@ +2,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/1head.txt b/crates/vim/test_data/test_change_in_word/1head.txt new file mode 100644 index 0000000000..352f6067e6 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/1head.txt @@ -0,0 +1 @@ +0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/20mode.txt b/crates/vim/test_data/test_change_in_word/20mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/20mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/21text.txt b/crates/vim/test_data/test_change_in_word/21text.txt new file mode 100644 index 0000000000..e971227436 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/21text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/22head.txt b/crates/vim/test_data/test_change_in_word/22head.txt new file mode 100644 index 0000000000..7cad756336 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/22head.txt @@ -0,0 +1 @@ +3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/23mode.txt b/crates/vim/test_data/test_change_in_word/23mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/23mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/24text.txt b/crates/vim/test_data/test_change_in_word/24text.txt new file mode 100644 index 0000000000..e971227436 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/24text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/25head.txt b/crates/vim/test_data/test_change_in_word/25head.txt new file mode 100644 index 0000000000..b26cd1f1d3 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/25head.txt @@ -0,0 +1 @@ +4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/26mode.txt b/crates/vim/test_data/test_change_in_word/26mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/26mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/27text.txt b/crates/vim/test_data/test_change_in_word/27text.txt new file mode 100644 index 0000000000..e971227436 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/27text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/28head.txt b/crates/vim/test_data/test_change_in_word/28head.txt new file mode 100644 index 0000000000..880fda949a --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/28head.txt @@ -0,0 +1 @@ +5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/29mode.txt b/crates/vim/test_data/test_change_in_word/29mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/29mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/2mode.txt b/crates/vim/test_data/test_change_in_word/2mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/2mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/30text.txt b/crates/vim/test_data/test_change_in_word/30text.txt new file mode 100644 index 0000000000..609747299b --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/30text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/31head.txt b/crates/vim/test_data/test_change_in_word/31head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/31head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/32mode.txt b/crates/vim/test_data/test_change_in_word/32mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/32mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/33text.txt b/crates/vim/test_data/test_change_in_word/33text.txt new file mode 100644 index 0000000000..12ede0f513 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/33text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +Thequick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/34head.txt b/crates/vim/test_data/test_change_in_word/34head.txt new file mode 100644 index 0000000000..c20df71d5e --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/34head.txt @@ -0,0 +1 @@ +6,3 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/35mode.txt b/crates/vim/test_data/test_change_in_word/35mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/35mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/36text.txt b/crates/vim/test_data/test_change_in_word/36text.txt new file mode 100644 index 0000000000..3b3f900a05 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/36text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The- brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/37head.txt b/crates/vim/test_data/test_change_in_word/37head.txt new file mode 100644 index 0000000000..95fb9c638e --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/37head.txt @@ -0,0 +1 @@ +6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/38mode.txt b/crates/vim/test_data/test_change_in_word/38mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/38mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/39text.txt b/crates/vim/test_data/test_change_in_word/39text.txt new file mode 100644 index 0000000000..3b3f900a05 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/39text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The- brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/3text.txt b/crates/vim/test_data/test_change_in_word/3text.txt new file mode 100644 index 0000000000..481389e676 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/3text.txt @@ -0,0 +1,12 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/40head.txt b/crates/vim/test_data/test_change_in_word/40head.txt new file mode 100644 index 0000000000..95fb9c638e --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/40head.txt @@ -0,0 +1 @@ +6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/41mode.txt b/crates/vim/test_data/test_change_in_word/41mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/41mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/42text.txt b/crates/vim/test_data/test_change_in_word/42text.txt new file mode 100644 index 0000000000..7d13d92d35 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/42text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quickbrown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/43head.txt b/crates/vim/test_data/test_change_in_word/43head.txt new file mode 100644 index 0000000000..da809253d8 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/43head.txt @@ -0,0 +1 @@ +6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/44mode.txt b/crates/vim/test_data/test_change_in_word/44mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/44mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/45text.txt b/crates/vim/test_data/test_change_in_word/45text.txt new file mode 100644 index 0000000000..95b76ce25b --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/45text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/46head.txt b/crates/vim/test_data/test_change_in_word/46head.txt new file mode 100644 index 0000000000..c5a50698dd --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/46head.txt @@ -0,0 +1 @@ +6,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/47mode.txt b/crates/vim/test_data/test_change_in_word/47mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/47mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/48text.txt b/crates/vim/test_data/test_change_in_word/48text.txt new file mode 100644 index 0000000000..805a5ebe4c --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/48text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/49head.txt b/crates/vim/test_data/test_change_in_word/49head.txt new file mode 100644 index 0000000000..d933eb81fa --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/49head.txt @@ -0,0 +1 @@ +6,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/4head.txt b/crates/vim/test_data/test_change_in_word/4head.txt new file mode 100644 index 0000000000..352f6067e6 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/4head.txt @@ -0,0 +1 @@ +0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/50mode.txt b/crates/vim/test_data/test_change_in_word/50mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/50mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/51text.txt b/crates/vim/test_data/test_change_in_word/51text.txt new file mode 100644 index 0000000000..1d797e34b8 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/51text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/52head.txt b/crates/vim/test_data/test_change_in_word/52head.txt new file mode 100644 index 0000000000..72c23ba506 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/52head.txt @@ -0,0 +1 @@ +7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/53mode.txt b/crates/vim/test_data/test_change_in_word/53mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/53mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/54text.txt b/crates/vim/test_data/test_change_in_word/54text.txt new file mode 100644 index 0000000000..8a35adfa26 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/54text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/55head.txt b/crates/vim/test_data/test_change_in_word/55head.txt new file mode 100644 index 0000000000..ded56a5926 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/55head.txt @@ -0,0 +1 @@ +8,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/56mode.txt b/crates/vim/test_data/test_change_in_word/56mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/56mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/57text.txt b/crates/vim/test_data/test_change_in_word/57text.txt new file mode 100644 index 0000000000..26e9d0a1b6 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/57text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + +fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/58head.txt b/crates/vim/test_data/test_change_in_word/58head.txt new file mode 100644 index 0000000000..8e69e0ad79 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/58head.txt @@ -0,0 +1 @@ +9,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/59mode.txt b/crates/vim/test_data/test_change_in_word/59mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/59mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/5mode.txt b/crates/vim/test_data/test_change_in_word/5mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/5mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/60text.txt b/crates/vim/test_data/test_change_in_word/60text.txt new file mode 100644 index 0000000000..816bf47cf0 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/60text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox- over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/61head.txt b/crates/vim/test_data/test_change_in_word/61head.txt new file mode 100644 index 0000000000..e0460d1bc5 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/61head.txt @@ -0,0 +1 @@ +9,6 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/62mode.txt b/crates/vim/test_data/test_change_in_word/62mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/62mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/63text.txt b/crates/vim/test_data/test_change_in_word/63text.txt new file mode 100644 index 0000000000..4b2cd80611 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/63text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/64head.txt b/crates/vim/test_data/test_change_in_word/64head.txt new file mode 100644 index 0000000000..00f5ae6cbb --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/64head.txt @@ -0,0 +1 @@ +10,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/65mode.txt b/crates/vim/test_data/test_change_in_word/65mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/65mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/66text.txt b/crates/vim/test_data/test_change_in_word/66text.txt new file mode 100644 index 0000000000..e971227436 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/66text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/67head.txt b/crates/vim/test_data/test_change_in_word/67head.txt new file mode 100644 index 0000000000..94dbddc699 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/67head.txt @@ -0,0 +1 @@ +11,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/68mode.txt b/crates/vim/test_data/test_change_in_word/68mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/68mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/69text.txt b/crates/vim/test_data/test_change_in_word/69text.txt new file mode 100644 index 0000000000..481389e676 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/69text.txt @@ -0,0 +1,12 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/6text.txt b/crates/vim/test_data/test_change_in_word/6text.txt new file mode 100644 index 0000000000..35fd5c89ba --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/6text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/70head.txt b/crates/vim/test_data/test_change_in_word/70head.txt new file mode 100644 index 0000000000..352f6067e6 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/70head.txt @@ -0,0 +1 @@ +0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/71mode.txt b/crates/vim/test_data/test_change_in_word/71mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/71mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/72text.txt b/crates/vim/test_data/test_change_in_word/72text.txt new file mode 100644 index 0000000000..481389e676 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/72text.txt @@ -0,0 +1,12 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/73head.txt b/crates/vim/test_data/test_change_in_word/73head.txt new file mode 100644 index 0000000000..352f6067e6 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/73head.txt @@ -0,0 +1 @@ +0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/74mode.txt b/crates/vim/test_data/test_change_in_word/74mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/74mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/75text.txt b/crates/vim/test_data/test_change_in_word/75text.txt new file mode 100644 index 0000000000..35fd5c89ba --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/75text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/76head.txt b/crates/vim/test_data/test_change_in_word/76head.txt new file mode 100644 index 0000000000..5f7b20f0d9 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/76head.txt @@ -0,0 +1 @@ +0,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/77mode.txt b/crates/vim/test_data/test_change_in_word/77mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/77mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/78text.txt b/crates/vim/test_data/test_change_in_word/78text.txt new file mode 100644 index 0000000000..38c6ccbeac --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/78text.txt @@ -0,0 +1,12 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/79head.txt b/crates/vim/test_data/test_change_in_word/79head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/79head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/7head.txt b/crates/vim/test_data/test_change_in_word/7head.txt new file mode 100644 index 0000000000..5f7b20f0d9 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/7head.txt @@ -0,0 +1 @@ +0,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/80mode.txt b/crates/vim/test_data/test_change_in_word/80mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/80mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/81text.txt b/crates/vim/test_data/test_change_in_word/81text.txt new file mode 100644 index 0000000000..38c6ccbeac --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/81text.txt @@ -0,0 +1,12 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/82head.txt b/crates/vim/test_data/test_change_in_word/82head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/82head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/83mode.txt b/crates/vim/test_data/test_change_in_word/83mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/83mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/84text.txt b/crates/vim/test_data/test_change_in_word/84text.txt new file mode 100644 index 0000000000..282218da91 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/84text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumpsover +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/85head.txt b/crates/vim/test_data/test_change_in_word/85head.txt new file mode 100644 index 0000000000..bd712da63a --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/85head.txt @@ -0,0 +1 @@ +1,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/86mode.txt b/crates/vim/test_data/test_change_in_word/86mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/86mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/87text.txt b/crates/vim/test_data/test_change_in_word/87text.txt new file mode 100644 index 0000000000..0b042b7c39 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/87text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/88head.txt b/crates/vim/test_data/test_change_in_word/88head.txt new file mode 100644 index 0000000000..28ce8bcc5c --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/88head.txt @@ -0,0 +1 @@ +2,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/89mode.txt b/crates/vim/test_data/test_change_in_word/89mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/89mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/8mode.txt b/crates/vim/test_data/test_change_in_word/8mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/8mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/90text.txt b/crates/vim/test_data/test_change_in_word/90text.txt new file mode 100644 index 0000000000..e971227436 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/90text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/91head.txt b/crates/vim/test_data/test_change_in_word/91head.txt new file mode 100644 index 0000000000..7cad756336 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/91head.txt @@ -0,0 +1 @@ +3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/92mode.txt b/crates/vim/test_data/test_change_in_word/92mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/92mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/93text.txt b/crates/vim/test_data/test_change_in_word/93text.txt new file mode 100644 index 0000000000..e971227436 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/93text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/94head.txt b/crates/vim/test_data/test_change_in_word/94head.txt new file mode 100644 index 0000000000..b26cd1f1d3 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/94head.txt @@ -0,0 +1 @@ +4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/95mode.txt b/crates/vim/test_data/test_change_in_word/95mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/95mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/96text.txt b/crates/vim/test_data/test_change_in_word/96text.txt new file mode 100644 index 0000000000..e971227436 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/96text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/97head.txt b/crates/vim/test_data/test_change_in_word/97head.txt new file mode 100644 index 0000000000..880fda949a --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/97head.txt @@ -0,0 +1 @@ +5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/98mode.txt b/crates/vim/test_data/test_change_in_word/98mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/98mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/99text.txt b/crates/vim/test_data/test_change_in_word/99text.txt new file mode 100644 index 0000000000..d90b2c4e48 --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/99text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + + brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_change_in_word/9text.txt b/crates/vim/test_data/test_change_in_word/9text.txt new file mode 100644 index 0000000000..38c6ccbeac --- /dev/null +++ b/crates/vim/test_data/test_change_in_word/9text.txt @@ -0,0 +1,12 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_sentence/0text.txt b/crates/vim/test_data/test_delete_around_sentence/0text.txt new file mode 100644 index 0000000000..d2b92e8000 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_sentence/0text.txt @@ -0,0 +1 @@ +Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence/10head.txt b/crates/vim/test_data/test_delete_around_sentence/10head.txt new file mode 100644 index 0000000000..5df80e23c6 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_sentence/10head.txt @@ -0,0 +1 @@ +0,16 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence/1head.txt b/crates/vim/test_data/test_delete_around_sentence/1head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_sentence/1head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence/2mode.txt b/crates/vim/test_data/test_delete_around_sentence/2mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_sentence/2mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence/3text.txt b/crates/vim/test_data/test_delete_around_sentence/3text.txt new file mode 100644 index 0000000000..d2b92e8000 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_sentence/3text.txt @@ -0,0 +1 @@ +Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence/4head.txt b/crates/vim/test_data/test_delete_around_sentence/4head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_sentence/4head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence/5mode.txt b/crates/vim/test_data/test_delete_around_sentence/5mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_sentence/5mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence/6text.txt b/crates/vim/test_data/test_delete_around_sentence/6text.txt new file mode 100644 index 0000000000..d2b92e8000 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_sentence/6text.txt @@ -0,0 +1 @@ +Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence/7head.txt b/crates/vim/test_data/test_delete_around_sentence/7head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_sentence/7head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence/8mode.txt b/crates/vim/test_data/test_delete_around_sentence/8mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_sentence/8mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence/9text.txt b/crates/vim/test_data/test_delete_around_sentence/9text.txt new file mode 100644 index 0000000000..5e3a12964d --- /dev/null +++ b/crates/vim/test_data/test_delete_around_sentence/9text.txt @@ -0,0 +1 @@ +The quick brown? Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/0text.txt b/crates/vim/test_data/test_delete_around_word/0text.txt new file mode 100644 index 0000000000..366beed733 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/0text.txt @@ -0,0 +1,12 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/100head.txt b/crates/vim/test_data/test_delete_around_word/100head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/100head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/101mode.txt b/crates/vim/test_data/test_delete_around_word/101mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/101mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/102text.txt b/crates/vim/test_data/test_delete_around_word/102text.txt new file mode 100644 index 0000000000..c8a5876179 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/102text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/103head.txt b/crates/vim/test_data/test_delete_around_word/103head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/103head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/104mode.txt b/crates/vim/test_data/test_delete_around_word/104mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/104mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/105text.txt b/crates/vim/test_data/test_delete_around_word/105text.txt new file mode 100644 index 0000000000..c8a5876179 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/105text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/106head.txt b/crates/vim/test_data/test_delete_around_word/106head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/106head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/107mode.txt b/crates/vim/test_data/test_delete_around_word/107mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/107mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/108text.txt b/crates/vim/test_data/test_delete_around_word/108text.txt new file mode 100644 index 0000000000..c8a5876179 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/108text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/109head.txt b/crates/vim/test_data/test_delete_around_word/109head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/109head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/10head.txt b/crates/vim/test_data/test_delete_around_word/10head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/10head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/110mode.txt b/crates/vim/test_data/test_delete_around_word/110mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/110mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/111text.txt b/crates/vim/test_data/test_delete_around_word/111text.txt new file mode 100644 index 0000000000..f05cdd88c1 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/111text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/112head.txt b/crates/vim/test_data/test_delete_around_word/112head.txt new file mode 100644 index 0000000000..da809253d8 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/112head.txt @@ -0,0 +1 @@ +6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/113mode.txt b/crates/vim/test_data/test_delete_around_word/113mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/113mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/114text.txt b/crates/vim/test_data/test_delete_around_word/114text.txt new file mode 100644 index 0000000000..f05cdd88c1 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/114text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/115head.txt b/crates/vim/test_data/test_delete_around_word/115head.txt new file mode 100644 index 0000000000..da809253d8 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/115head.txt @@ -0,0 +1 @@ +6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/116mode.txt b/crates/vim/test_data/test_delete_around_word/116mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/116mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/117text.txt b/crates/vim/test_data/test_delete_around_word/117text.txt new file mode 100644 index 0000000000..0e9094161a --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/117text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/118head.txt b/crates/vim/test_data/test_delete_around_word/118head.txt new file mode 100644 index 0000000000..d933eb81fa --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/118head.txt @@ -0,0 +1 @@ +6,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/119mode.txt b/crates/vim/test_data/test_delete_around_word/119mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/119mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/11mode.txt b/crates/vim/test_data/test_delete_around_word/11mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/11mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/120text.txt b/crates/vim/test_data/test_delete_around_word/120text.txt new file mode 100644 index 0000000000..fa73eb37b8 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/120text.txt @@ -0,0 +1,10 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/121head.txt b/crates/vim/test_data/test_delete_around_word/121head.txt new file mode 100644 index 0000000000..72c23ba506 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/121head.txt @@ -0,0 +1 @@ +7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/122mode.txt b/crates/vim/test_data/test_delete_around_word/122mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/122mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/123text.txt b/crates/vim/test_data/test_delete_around_word/123text.txt new file mode 100644 index 0000000000..4fe33b47eb --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/123text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/124head.txt b/crates/vim/test_data/test_delete_around_word/124head.txt new file mode 100644 index 0000000000..ded56a5926 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/124head.txt @@ -0,0 +1 @@ +8,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/125mode.txt b/crates/vim/test_data/test_delete_around_word/125mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/125mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/126text.txt b/crates/vim/test_data/test_delete_around_word/126text.txt new file mode 100644 index 0000000000..35dd5816fc --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/126text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/127head.txt b/crates/vim/test_data/test_delete_around_word/127head.txt new file mode 100644 index 0000000000..8e69e0ad79 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/127head.txt @@ -0,0 +1 @@ +9,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/128mode.txt b/crates/vim/test_data/test_delete_around_word/128mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/128mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/129text.txt b/crates/vim/test_data/test_delete_around_word/129text.txt new file mode 100644 index 0000000000..1bac4587a7 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/129text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/12text.txt b/crates/vim/test_data/test_delete_around_word/12text.txt new file mode 100644 index 0000000000..2feb66cfb9 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/12text.txt @@ -0,0 +1,12 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/130head.txt b/crates/vim/test_data/test_delete_around_word/130head.txt new file mode 100644 index 0000000000..edb3544f9a --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/130head.txt @@ -0,0 +1 @@ +9,2 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/131mode.txt b/crates/vim/test_data/test_delete_around_word/131mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/131mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/132text.txt b/crates/vim/test_data/test_delete_around_word/132text.txt new file mode 100644 index 0000000000..912316a9f7 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/132text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_delete_around_word/133head.txt b/crates/vim/test_data/test_delete_around_word/133head.txt new file mode 100644 index 0000000000..63ee4f53ac --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/133head.txt @@ -0,0 +1 @@ +10,11 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/134mode.txt b/crates/vim/test_data/test_delete_around_word/134mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/134mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/135text.txt b/crates/vim/test_data/test_delete_around_word/135text.txt new file mode 100644 index 0000000000..3c4e8c2398 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/135text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/136head.txt b/crates/vim/test_data/test_delete_around_word/136head.txt new file mode 100644 index 0000000000..ec50c7d44f --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/136head.txt @@ -0,0 +1 @@ +10,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/137mode.txt b/crates/vim/test_data/test_delete_around_word/137mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/137mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/13head.txt b/crates/vim/test_data/test_delete_around_word/13head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/13head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/14mode.txt b/crates/vim/test_data/test_delete_around_word/14mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/14mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/15text.txt b/crates/vim/test_data/test_delete_around_word/15text.txt new file mode 100644 index 0000000000..262feeae96 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/15text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/16head.txt b/crates/vim/test_data/test_delete_around_word/16head.txt new file mode 100644 index 0000000000..413d7dd4d2 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/16head.txt @@ -0,0 +1 @@ +1,8 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/17mode.txt b/crates/vim/test_data/test_delete_around_word/17mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/17mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/18text.txt b/crates/vim/test_data/test_delete_around_word/18text.txt new file mode 100644 index 0000000000..ba0aea82d9 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/18text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/19head.txt b/crates/vim/test_data/test_delete_around_word/19head.txt new file mode 100644 index 0000000000..06ce34087d --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/19head.txt @@ -0,0 +1 @@ +2,11 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/1head.txt b/crates/vim/test_data/test_delete_around_word/1head.txt new file mode 100644 index 0000000000..2871b36925 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/1head.txt @@ -0,0 +1 @@ +0,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/20mode.txt b/crates/vim/test_data/test_delete_around_word/20mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/20mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/21text.txt b/crates/vim/test_data/test_delete_around_word/21text.txt new file mode 100644 index 0000000000..ead25fb9d0 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/21text.txt @@ -0,0 +1,10 @@ +The quick brown +fox jumps over +the lazy dog + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/22head.txt b/crates/vim/test_data/test_delete_around_word/22head.txt new file mode 100644 index 0000000000..7cad756336 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/22head.txt @@ -0,0 +1 @@ +3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/23mode.txt b/crates/vim/test_data/test_delete_around_word/23mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/23mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/24text.txt b/crates/vim/test_data/test_delete_around_word/24text.txt new file mode 100644 index 0000000000..ead25fb9d0 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/24text.txt @@ -0,0 +1,10 @@ +The quick brown +fox jumps over +the lazy dog + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/25head.txt b/crates/vim/test_data/test_delete_around_word/25head.txt new file mode 100644 index 0000000000..b26cd1f1d3 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/25head.txt @@ -0,0 +1 @@ +4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/26mode.txt b/crates/vim/test_data/test_delete_around_word/26mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/26mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/27text.txt b/crates/vim/test_data/test_delete_around_word/27text.txt new file mode 100644 index 0000000000..252419d291 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/27text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + +-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/28head.txt b/crates/vim/test_data/test_delete_around_word/28head.txt new file mode 100644 index 0000000000..880fda949a --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/28head.txt @@ -0,0 +1 @@ +5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/29mode.txt b/crates/vim/test_data/test_delete_around_word/29mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/29mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/2mode.txt b/crates/vim/test_data/test_delete_around_word/2mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/2mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/30text.txt b/crates/vim/test_data/test_delete_around_word/30text.txt new file mode 100644 index 0000000000..609747299b --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/30text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/31head.txt b/crates/vim/test_data/test_delete_around_word/31head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/31head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/32mode.txt b/crates/vim/test_data/test_delete_around_word/32mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/32mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/33text.txt b/crates/vim/test_data/test_delete_around_word/33text.txt new file mode 100644 index 0000000000..12ede0f513 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/33text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +Thequick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/34head.txt b/crates/vim/test_data/test_delete_around_word/34head.txt new file mode 100644 index 0000000000..c20df71d5e --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/34head.txt @@ -0,0 +1 @@ +6,3 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/35mode.txt b/crates/vim/test_data/test_delete_around_word/35mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/35mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/36text.txt b/crates/vim/test_data/test_delete_around_word/36text.txt new file mode 100644 index 0000000000..4a6d943982 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/36text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/37head.txt b/crates/vim/test_data/test_delete_around_word/37head.txt new file mode 100644 index 0000000000..95fb9c638e --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/37head.txt @@ -0,0 +1 @@ +6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/38mode.txt b/crates/vim/test_data/test_delete_around_word/38mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/38mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/39text.txt b/crates/vim/test_data/test_delete_around_word/39text.txt new file mode 100644 index 0000000000..4a6d943982 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/39text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/3text.txt b/crates/vim/test_data/test_delete_around_word/3text.txt new file mode 100644 index 0000000000..366beed733 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/3text.txt @@ -0,0 +1,12 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/40head.txt b/crates/vim/test_data/test_delete_around_word/40head.txt new file mode 100644 index 0000000000..95fb9c638e --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/40head.txt @@ -0,0 +1 @@ +6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/41mode.txt b/crates/vim/test_data/test_delete_around_word/41mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/41mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/42text.txt b/crates/vim/test_data/test_delete_around_word/42text.txt new file mode 100644 index 0000000000..f05cdd88c1 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/42text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/43head.txt b/crates/vim/test_data/test_delete_around_word/43head.txt new file mode 100644 index 0000000000..da809253d8 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/43head.txt @@ -0,0 +1 @@ +6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/44mode.txt b/crates/vim/test_data/test_delete_around_word/44mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/44mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/45text.txt b/crates/vim/test_data/test_delete_around_word/45text.txt new file mode 100644 index 0000000000..f05cdd88c1 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/45text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/46head.txt b/crates/vim/test_data/test_delete_around_word/46head.txt new file mode 100644 index 0000000000..da809253d8 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/46head.txt @@ -0,0 +1 @@ +6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/47mode.txt b/crates/vim/test_data/test_delete_around_word/47mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/47mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/48text.txt b/crates/vim/test_data/test_delete_around_word/48text.txt new file mode 100644 index 0000000000..13b7d1cb51 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/48text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/49head.txt b/crates/vim/test_data/test_delete_around_word/49head.txt new file mode 100644 index 0000000000..d933eb81fa --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/49head.txt @@ -0,0 +1 @@ +6,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/4head.txt b/crates/vim/test_data/test_delete_around_word/4head.txt new file mode 100644 index 0000000000..2871b36925 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/4head.txt @@ -0,0 +1 @@ +0,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/50mode.txt b/crates/vim/test_data/test_delete_around_word/50mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/50mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/51text.txt b/crates/vim/test_data/test_delete_around_word/51text.txt new file mode 100644 index 0000000000..3976ef9c85 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/51text.txt @@ -0,0 +1,10 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown +-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/52head.txt b/crates/vim/test_data/test_delete_around_word/52head.txt new file mode 100644 index 0000000000..72c23ba506 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/52head.txt @@ -0,0 +1 @@ +7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/53mode.txt b/crates/vim/test_data/test_delete_around_word/53mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/53mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/54text.txt b/crates/vim/test_data/test_delete_around_word/54text.txt new file mode 100644 index 0000000000..b5ca31babe --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/54text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + +-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/55head.txt b/crates/vim/test_data/test_delete_around_word/55head.txt new file mode 100644 index 0000000000..ded56a5926 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/55head.txt @@ -0,0 +1 @@ +8,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/56mode.txt b/crates/vim/test_data/test_delete_around_word/56mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/56mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/57text.txt b/crates/vim/test_data/test_delete_around_word/57text.txt new file mode 100644 index 0000000000..0eaface506 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/57text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + +-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/58head.txt b/crates/vim/test_data/test_delete_around_word/58head.txt new file mode 100644 index 0000000000..8e69e0ad79 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/58head.txt @@ -0,0 +1 @@ +9,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/59mode.txt b/crates/vim/test_data/test_delete_around_word/59mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/59mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/5mode.txt b/crates/vim/test_data/test_delete_around_word/5mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/5mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/60text.txt b/crates/vim/test_data/test_delete_around_word/60text.txt new file mode 100644 index 0000000000..d8b9b05b79 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/60text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/61head.txt b/crates/vim/test_data/test_delete_around_word/61head.txt new file mode 100644 index 0000000000..e0460d1bc5 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/61head.txt @@ -0,0 +1 @@ +9,6 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/62mode.txt b/crates/vim/test_data/test_delete_around_word/62mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/62mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/63text.txt b/crates/vim/test_data/test_delete_around_word/63text.txt new file mode 100644 index 0000000000..912316a9f7 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/63text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_delete_around_word/64head.txt b/crates/vim/test_data/test_delete_around_word/64head.txt new file mode 100644 index 0000000000..63ee4f53ac --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/64head.txt @@ -0,0 +1 @@ +10,11 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/65mode.txt b/crates/vim/test_data/test_delete_around_word/65mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/65mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/66text.txt b/crates/vim/test_data/test_delete_around_word/66text.txt new file mode 100644 index 0000000000..3c4e8c2398 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/66text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/67head.txt b/crates/vim/test_data/test_delete_around_word/67head.txt new file mode 100644 index 0000000000..ec50c7d44f --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/67head.txt @@ -0,0 +1 @@ +10,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/68mode.txt b/crates/vim/test_data/test_delete_around_word/68mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/68mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/69text.txt b/crates/vim/test_data/test_delete_around_word/69text.txt new file mode 100644 index 0000000000..366beed733 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/69text.txt @@ -0,0 +1,12 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/6text.txt b/crates/vim/test_data/test_delete_around_word/6text.txt new file mode 100644 index 0000000000..3678c219a3 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/6text.txt @@ -0,0 +1,11 @@ +The quick brown jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/70head.txt b/crates/vim/test_data/test_delete_around_word/70head.txt new file mode 100644 index 0000000000..2871b36925 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/70head.txt @@ -0,0 +1 @@ +0,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/71mode.txt b/crates/vim/test_data/test_delete_around_word/71mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/71mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/72text.txt b/crates/vim/test_data/test_delete_around_word/72text.txt new file mode 100644 index 0000000000..366beed733 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/72text.txt @@ -0,0 +1,12 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/73head.txt b/crates/vim/test_data/test_delete_around_word/73head.txt new file mode 100644 index 0000000000..2871b36925 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/73head.txt @@ -0,0 +1 @@ +0,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/74mode.txt b/crates/vim/test_data/test_delete_around_word/74mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/74mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/75text.txt b/crates/vim/test_data/test_delete_around_word/75text.txt new file mode 100644 index 0000000000..3678c219a3 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/75text.txt @@ -0,0 +1,11 @@ +The quick brown jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/76head.txt b/crates/vim/test_data/test_delete_around_word/76head.txt new file mode 100644 index 0000000000..5f7b20f0d9 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/76head.txt @@ -0,0 +1 @@ +0,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/77mode.txt b/crates/vim/test_data/test_delete_around_word/77mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/77mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/78text.txt b/crates/vim/test_data/test_delete_around_word/78text.txt new file mode 100644 index 0000000000..2feb66cfb9 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/78text.txt @@ -0,0 +1,12 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/79head.txt b/crates/vim/test_data/test_delete_around_word/79head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/79head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/7head.txt b/crates/vim/test_data/test_delete_around_word/7head.txt new file mode 100644 index 0000000000..5f7b20f0d9 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/7head.txt @@ -0,0 +1 @@ +0,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/80mode.txt b/crates/vim/test_data/test_delete_around_word/80mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/80mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/81text.txt b/crates/vim/test_data/test_delete_around_word/81text.txt new file mode 100644 index 0000000000..2feb66cfb9 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/81text.txt @@ -0,0 +1,12 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/82head.txt b/crates/vim/test_data/test_delete_around_word/82head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/82head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/83mode.txt b/crates/vim/test_data/test_delete_around_word/83mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/83mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/84text.txt b/crates/vim/test_data/test_delete_around_word/84text.txt new file mode 100644 index 0000000000..262feeae96 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/84text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/85head.txt b/crates/vim/test_data/test_delete_around_word/85head.txt new file mode 100644 index 0000000000..413d7dd4d2 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/85head.txt @@ -0,0 +1 @@ +1,8 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/86mode.txt b/crates/vim/test_data/test_delete_around_word/86mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/86mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/87text.txt b/crates/vim/test_data/test_delete_around_word/87text.txt new file mode 100644 index 0000000000..ba0aea82d9 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/87text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/88head.txt b/crates/vim/test_data/test_delete_around_word/88head.txt new file mode 100644 index 0000000000..06ce34087d --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/88head.txt @@ -0,0 +1 @@ +2,11 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/89mode.txt b/crates/vim/test_data/test_delete_around_word/89mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/89mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/8mode.txt b/crates/vim/test_data/test_delete_around_word/8mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/8mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/90text.txt b/crates/vim/test_data/test_delete_around_word/90text.txt new file mode 100644 index 0000000000..ead25fb9d0 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/90text.txt @@ -0,0 +1,10 @@ +The quick brown +fox jumps over +the lazy dog + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/91head.txt b/crates/vim/test_data/test_delete_around_word/91head.txt new file mode 100644 index 0000000000..7cad756336 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/91head.txt @@ -0,0 +1 @@ +3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/92mode.txt b/crates/vim/test_data/test_delete_around_word/92mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/92mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/93text.txt b/crates/vim/test_data/test_delete_around_word/93text.txt new file mode 100644 index 0000000000..ead25fb9d0 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/93text.txt @@ -0,0 +1,10 @@ +The quick brown +fox jumps over +the lazy dog + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/94head.txt b/crates/vim/test_data/test_delete_around_word/94head.txt new file mode 100644 index 0000000000..b26cd1f1d3 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/94head.txt @@ -0,0 +1 @@ +4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/95mode.txt b/crates/vim/test_data/test_delete_around_word/95mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/95mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/96text.txt b/crates/vim/test_data/test_delete_around_word/96text.txt new file mode 100644 index 0000000000..523f6002f4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/96text.txt @@ -0,0 +1,11 @@ +The quick brown +fox jumps over +the lazy dog + + + brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/97head.txt b/crates/vim/test_data/test_delete_around_word/97head.txt new file mode 100644 index 0000000000..880fda949a --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/97head.txt @@ -0,0 +1 @@ +5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/98mode.txt b/crates/vim/test_data/test_delete_around_word/98mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/98mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/99text.txt b/crates/vim/test_data/test_delete_around_word/99text.txt new file mode 100644 index 0000000000..c8a5876179 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/99text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_around_word/9text.txt b/crates/vim/test_data/test_delete_around_word/9text.txt new file mode 100644 index 0000000000..2feb66cfb9 --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word/9text.txt @@ -0,0 +1,12 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_sentence/0text.txt b/crates/vim/test_data/test_delete_in_sentence/0text.txt new file mode 100644 index 0000000000..fd36730b8d --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/0text.txt @@ -0,0 +1 @@ + Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/10head.txt b/crates/vim/test_data/test_delete_in_sentence/10head.txt new file mode 100644 index 0000000000..5df80e23c6 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/10head.txt @@ -0,0 +1 @@ +0,16 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/11mode.txt b/crates/vim/test_data/test_delete_in_sentence/11mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/11mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/12text.txt b/crates/vim/test_data/test_delete_in_sentence/12text.txt new file mode 100644 index 0000000000..40eacc6489 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/12text.txt @@ -0,0 +1 @@ +The quick brown? Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/13head.txt b/crates/vim/test_data/test_delete_in_sentence/13head.txt new file mode 100644 index 0000000000..40d2f2ab55 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/13head.txt @@ -0,0 +1 @@ +0,17 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/14mode.txt b/crates/vim/test_data/test_delete_in_sentence/14mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/14mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/15text.txt b/crates/vim/test_data/test_delete_in_sentence/15text.txt new file mode 100644 index 0000000000..40eacc6489 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/15text.txt @@ -0,0 +1 @@ +The quick brown? Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/16head.txt b/crates/vim/test_data/test_delete_in_sentence/16head.txt new file mode 100644 index 0000000000..40d2f2ab55 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/16head.txt @@ -0,0 +1 @@ +0,17 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/17mode.txt b/crates/vim/test_data/test_delete_in_sentence/17mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/17mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/18text.txt b/crates/vim/test_data/test_delete_in_sentence/18text.txt new file mode 100644 index 0000000000..40eacc6489 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/18text.txt @@ -0,0 +1 @@ +The quick brown? Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/19head.txt b/crates/vim/test_data/test_delete_in_sentence/19head.txt new file mode 100644 index 0000000000..40d2f2ab55 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/19head.txt @@ -0,0 +1 @@ +0,17 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/1head.txt b/crates/vim/test_data/test_delete_in_sentence/1head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/1head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/20mode.txt b/crates/vim/test_data/test_delete_in_sentence/20mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/20mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/21text.txt b/crates/vim/test_data/test_delete_in_sentence/21text.txt new file mode 100644 index 0000000000..ef17a5f7fb --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/21text.txt @@ -0,0 +1 @@ +The quick brown? Fox Jumps!Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/22head.txt b/crates/vim/test_data/test_delete_in_sentence/22head.txt new file mode 100644 index 0000000000..17beb4d83f --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/22head.txt @@ -0,0 +1 @@ +0,27 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/23mode.txt b/crates/vim/test_data/test_delete_in_sentence/23mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/23mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/24text.txt b/crates/vim/test_data/test_delete_in_sentence/24text.txt new file mode 100644 index 0000000000..d81c10974f --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/24text.txt @@ -0,0 +1 @@ +The quick brown? Fox Jumps! \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/25head.txt b/crates/vim/test_data/test_delete_in_sentence/25head.txt new file mode 100644 index 0000000000..17beb4d83f --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/25head.txt @@ -0,0 +1 @@ +0,27 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/26mode.txt b/crates/vim/test_data/test_delete_in_sentence/26mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/26mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/27text.txt b/crates/vim/test_data/test_delete_in_sentence/27text.txt new file mode 100644 index 0000000000..d81c10974f --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/27text.txt @@ -0,0 +1 @@ +The quick brown? Fox Jumps! \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/28head.txt b/crates/vim/test_data/test_delete_in_sentence/28head.txt new file mode 100644 index 0000000000..17beb4d83f --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/28head.txt @@ -0,0 +1 @@ +0,27 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/29mode.txt b/crates/vim/test_data/test_delete_in_sentence/29mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/29mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/2mode.txt b/crates/vim/test_data/test_delete_in_sentence/2mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/2mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/30text.txt b/crates/vim/test_data/test_delete_in_sentence/30text.txt new file mode 100644 index 0000000000..d81c10974f --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/30text.txt @@ -0,0 +1 @@ +The quick brown? Fox Jumps! \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/31head.txt b/crates/vim/test_data/test_delete_in_sentence/31head.txt new file mode 100644 index 0000000000..17beb4d83f --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/31head.txt @@ -0,0 +1 @@ +0,27 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/32mode.txt b/crates/vim/test_data/test_delete_in_sentence/32mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/32mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/33text.txt b/crates/vim/test_data/test_delete_in_sentence/33text.txt new file mode 100644 index 0000000000..561ed0800c --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/33text.txt @@ -0,0 +1,2 @@ + The quick +brown fox jumps over diff --git a/crates/vim/test_data/test_delete_in_sentence/34head.txt b/crates/vim/test_data/test_delete_in_sentence/34head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/34head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/35mode.txt b/crates/vim/test_data/test_delete_in_sentence/35mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/35mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/36text.txt b/crates/vim/test_data/test_delete_in_sentence/36text.txt new file mode 100644 index 0000000000..561ed0800c --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/36text.txt @@ -0,0 +1,2 @@ + The quick +brown fox jumps over diff --git a/crates/vim/test_data/test_delete_in_sentence/37head.txt b/crates/vim/test_data/test_delete_in_sentence/37head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/37head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/38mode.txt b/crates/vim/test_data/test_delete_in_sentence/38mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/38mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/39text.txt b/crates/vim/test_data/test_delete_in_sentence/39text.txt new file mode 100644 index 0000000000..561ed0800c --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/39text.txt @@ -0,0 +1,2 @@ + The quick +brown fox jumps over diff --git a/crates/vim/test_data/test_delete_in_sentence/3text.txt b/crates/vim/test_data/test_delete_in_sentence/3text.txt new file mode 100644 index 0000000000..fd36730b8d --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/3text.txt @@ -0,0 +1 @@ + Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/40head.txt b/crates/vim/test_data/test_delete_in_sentence/40head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/40head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/41mode.txt b/crates/vim/test_data/test_delete_in_sentence/41mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/41mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/42text.txt b/crates/vim/test_data/test_delete_in_sentence/42text.txt new file mode 100644 index 0000000000..561ed0800c --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/42text.txt @@ -0,0 +1,2 @@ + The quick +brown fox jumps over diff --git a/crates/vim/test_data/test_delete_in_sentence/43head.txt b/crates/vim/test_data/test_delete_in_sentence/43head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/43head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/44mode.txt b/crates/vim/test_data/test_delete_in_sentence/44mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/44mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/45text.txt b/crates/vim/test_data/test_delete_in_sentence/45text.txt new file mode 100644 index 0000000000..561ed0800c --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/45text.txt @@ -0,0 +1,2 @@ + The quick +brown fox jumps over diff --git a/crates/vim/test_data/test_delete_in_sentence/46head.txt b/crates/vim/test_data/test_delete_in_sentence/46head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/46head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/47mode.txt b/crates/vim/test_data/test_delete_in_sentence/47mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/47mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/48text.txt b/crates/vim/test_data/test_delete_in_sentence/48text.txt new file mode 100644 index 0000000000..bbf10c5693 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/48text.txt @@ -0,0 +1,4 @@ +The quick brown +fox jumps over +the lazy dog.The quick +brown fox jumps over diff --git a/crates/vim/test_data/test_delete_in_sentence/49head.txt b/crates/vim/test_data/test_delete_in_sentence/49head.txt new file mode 100644 index 0000000000..26560586d9 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/49head.txt @@ -0,0 +1 @@ +2,13 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/4head.txt b/crates/vim/test_data/test_delete_in_sentence/4head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/4head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/50mode.txt b/crates/vim/test_data/test_delete_in_sentence/50mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/50mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/51text.txt b/crates/vim/test_data/test_delete_in_sentence/51text.txt new file mode 100644 index 0000000000..0b535bbbaf --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/51text.txt @@ -0,0 +1,3 @@ +The quick brown +fox jumps over +the lazy dog. diff --git a/crates/vim/test_data/test_delete_in_sentence/52head.txt b/crates/vim/test_data/test_delete_in_sentence/52head.txt new file mode 100644 index 0000000000..26560586d9 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/52head.txt @@ -0,0 +1 @@ +2,13 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/53mode.txt b/crates/vim/test_data/test_delete_in_sentence/53mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/53mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/54text.txt b/crates/vim/test_data/test_delete_in_sentence/54text.txt new file mode 100644 index 0000000000..0b535bbbaf --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/54text.txt @@ -0,0 +1,3 @@ +The quick brown +fox jumps over +the lazy dog. diff --git a/crates/vim/test_data/test_delete_in_sentence/55head.txt b/crates/vim/test_data/test_delete_in_sentence/55head.txt new file mode 100644 index 0000000000..26560586d9 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/55head.txt @@ -0,0 +1 @@ +2,13 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/56mode.txt b/crates/vim/test_data/test_delete_in_sentence/56mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/56mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/5mode.txt b/crates/vim/test_data/test_delete_in_sentence/5mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/5mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/6text.txt b/crates/vim/test_data/test_delete_in_sentence/6text.txt new file mode 100644 index 0000000000..fd36730b8d --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/6text.txt @@ -0,0 +1 @@ + Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/7head.txt b/crates/vim/test_data/test_delete_in_sentence/7head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/7head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/8mode.txt b/crates/vim/test_data/test_delete_in_sentence/8mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/8mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/9text.txt b/crates/vim/test_data/test_delete_in_sentence/9text.txt new file mode 100644 index 0000000000..9e4b6dcc50 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence/9text.txt @@ -0,0 +1 @@ +The quick brown?Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/0text.txt b/crates/vim/test_data/test_delete_in_word/0text.txt new file mode 100644 index 0000000000..481389e676 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/0text.txt @@ -0,0 +1,12 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/100head.txt b/crates/vim/test_data/test_delete_in_word/100head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/100head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/101mode.txt b/crates/vim/test_data/test_delete_in_word/101mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/101mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/102text.txt b/crates/vim/test_data/test_delete_in_word/102text.txt new file mode 100644 index 0000000000..d90b2c4e48 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/102text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + + brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/103head.txt b/crates/vim/test_data/test_delete_in_word/103head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/103head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/104mode.txt b/crates/vim/test_data/test_delete_in_word/104mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/104mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/105text.txt b/crates/vim/test_data/test_delete_in_word/105text.txt new file mode 100644 index 0000000000..d90b2c4e48 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/105text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + + brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/106head.txt b/crates/vim/test_data/test_delete_in_word/106head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/106head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/107mode.txt b/crates/vim/test_data/test_delete_in_word/107mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/107mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/108text.txt b/crates/vim/test_data/test_delete_in_word/108text.txt new file mode 100644 index 0000000000..d90b2c4e48 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/108text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + + brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/109head.txt b/crates/vim/test_data/test_delete_in_word/109head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/109head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/10head.txt b/crates/vim/test_data/test_delete_in_word/10head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/10head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/110mode.txt b/crates/vim/test_data/test_delete_in_word/110mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/110mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/111text.txt b/crates/vim/test_data/test_delete_in_word/111text.txt new file mode 100644 index 0000000000..7d13d92d35 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/111text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quickbrown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/112head.txt b/crates/vim/test_data/test_delete_in_word/112head.txt new file mode 100644 index 0000000000..da809253d8 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/112head.txt @@ -0,0 +1 @@ +6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/113mode.txt b/crates/vim/test_data/test_delete_in_word/113mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/113mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/114text.txt b/crates/vim/test_data/test_delete_in_word/114text.txt new file mode 100644 index 0000000000..95b76ce25b --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/114text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/115head.txt b/crates/vim/test_data/test_delete_in_word/115head.txt new file mode 100644 index 0000000000..c5a50698dd --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/115head.txt @@ -0,0 +1 @@ +6,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/116mode.txt b/crates/vim/test_data/test_delete_in_word/116mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/116mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/117text.txt b/crates/vim/test_data/test_delete_in_word/117text.txt new file mode 100644 index 0000000000..805a5ebe4c --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/117text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/118head.txt b/crates/vim/test_data/test_delete_in_word/118head.txt new file mode 100644 index 0000000000..afce6fe58c --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/118head.txt @@ -0,0 +1 @@ +6,14 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/119mode.txt b/crates/vim/test_data/test_delete_in_word/119mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/119mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/11mode.txt b/crates/vim/test_data/test_delete_in_word/11mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/11mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/120text.txt b/crates/vim/test_data/test_delete_in_word/120text.txt new file mode 100644 index 0000000000..1d797e34b8 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/120text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/121head.txt b/crates/vim/test_data/test_delete_in_word/121head.txt new file mode 100644 index 0000000000..72c23ba506 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/121head.txt @@ -0,0 +1 @@ +7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/122mode.txt b/crates/vim/test_data/test_delete_in_word/122mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/122mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/123text.txt b/crates/vim/test_data/test_delete_in_word/123text.txt new file mode 100644 index 0000000000..8a35adfa26 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/123text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/124head.txt b/crates/vim/test_data/test_delete_in_word/124head.txt new file mode 100644 index 0000000000..ded56a5926 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/124head.txt @@ -0,0 +1 @@ +8,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/125mode.txt b/crates/vim/test_data/test_delete_in_word/125mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/125mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/126text.txt b/crates/vim/test_data/test_delete_in_word/126text.txt new file mode 100644 index 0000000000..26e9d0a1b6 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/126text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + +fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/127head.txt b/crates/vim/test_data/test_delete_in_word/127head.txt new file mode 100644 index 0000000000..8e69e0ad79 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/127head.txt @@ -0,0 +1 @@ +9,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/128mode.txt b/crates/vim/test_data/test_delete_in_word/128mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/128mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/129text.txt b/crates/vim/test_data/test_delete_in_word/129text.txt new file mode 100644 index 0000000000..c2c9c0edb0 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/129text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/12text.txt b/crates/vim/test_data/test_delete_in_word/12text.txt new file mode 100644 index 0000000000..38c6ccbeac --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/12text.txt @@ -0,0 +1,12 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/130head.txt b/crates/vim/test_data/test_delete_in_word/130head.txt new file mode 100644 index 0000000000..edb3544f9a --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/130head.txt @@ -0,0 +1 @@ +9,2 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/131mode.txt b/crates/vim/test_data/test_delete_in_word/131mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/131mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/132text.txt b/crates/vim/test_data/test_delete_in_word/132text.txt new file mode 100644 index 0000000000..4b2cd80611 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/132text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/133head.txt b/crates/vim/test_data/test_delete_in_word/133head.txt new file mode 100644 index 0000000000..63ee4f53ac --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/133head.txt @@ -0,0 +1 @@ +10,11 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/134mode.txt b/crates/vim/test_data/test_delete_in_word/134mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/134mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/135text.txt b/crates/vim/test_data/test_delete_in_word/135text.txt new file mode 100644 index 0000000000..e971227436 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/135text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/136head.txt b/crates/vim/test_data/test_delete_in_word/136head.txt new file mode 100644 index 0000000000..94dbddc699 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/136head.txt @@ -0,0 +1 @@ +11,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/137mode.txt b/crates/vim/test_data/test_delete_in_word/137mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/137mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/13head.txt b/crates/vim/test_data/test_delete_in_word/13head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/13head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/14mode.txt b/crates/vim/test_data/test_delete_in_word/14mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/14mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/15text.txt b/crates/vim/test_data/test_delete_in_word/15text.txt new file mode 100644 index 0000000000..282218da91 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/15text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumpsover +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/16head.txt b/crates/vim/test_data/test_delete_in_word/16head.txt new file mode 100644 index 0000000000..bd712da63a --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/16head.txt @@ -0,0 +1 @@ +1,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/17mode.txt b/crates/vim/test_data/test_delete_in_word/17mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/17mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/18text.txt b/crates/vim/test_data/test_delete_in_word/18text.txt new file mode 100644 index 0000000000..0b042b7c39 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/18text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/19head.txt b/crates/vim/test_data/test_delete_in_word/19head.txt new file mode 100644 index 0000000000..06ce34087d --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/19head.txt @@ -0,0 +1 @@ +2,11 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/1head.txt b/crates/vim/test_data/test_delete_in_word/1head.txt new file mode 100644 index 0000000000..352f6067e6 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/1head.txt @@ -0,0 +1 @@ +0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/20mode.txt b/crates/vim/test_data/test_delete_in_word/20mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/20mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/21text.txt b/crates/vim/test_data/test_delete_in_word/21text.txt new file mode 100644 index 0000000000..e971227436 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/21text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/22head.txt b/crates/vim/test_data/test_delete_in_word/22head.txt new file mode 100644 index 0000000000..7cad756336 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/22head.txt @@ -0,0 +1 @@ +3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/23mode.txt b/crates/vim/test_data/test_delete_in_word/23mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/23mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/24text.txt b/crates/vim/test_data/test_delete_in_word/24text.txt new file mode 100644 index 0000000000..e971227436 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/24text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/25head.txt b/crates/vim/test_data/test_delete_in_word/25head.txt new file mode 100644 index 0000000000..b26cd1f1d3 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/25head.txt @@ -0,0 +1 @@ +4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/26mode.txt b/crates/vim/test_data/test_delete_in_word/26mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/26mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/27text.txt b/crates/vim/test_data/test_delete_in_word/27text.txt new file mode 100644 index 0000000000..e971227436 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/27text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/28head.txt b/crates/vim/test_data/test_delete_in_word/28head.txt new file mode 100644 index 0000000000..880fda949a --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/28head.txt @@ -0,0 +1 @@ +5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/29mode.txt b/crates/vim/test_data/test_delete_in_word/29mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/29mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/2mode.txt b/crates/vim/test_data/test_delete_in_word/2mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/2mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/30text.txt b/crates/vim/test_data/test_delete_in_word/30text.txt new file mode 100644 index 0000000000..609747299b --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/30text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/31head.txt b/crates/vim/test_data/test_delete_in_word/31head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/31head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/32mode.txt b/crates/vim/test_data/test_delete_in_word/32mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/32mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/33text.txt b/crates/vim/test_data/test_delete_in_word/33text.txt new file mode 100644 index 0000000000..12ede0f513 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/33text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +Thequick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/34head.txt b/crates/vim/test_data/test_delete_in_word/34head.txt new file mode 100644 index 0000000000..c20df71d5e --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/34head.txt @@ -0,0 +1 @@ +6,3 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/35mode.txt b/crates/vim/test_data/test_delete_in_word/35mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/35mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/36text.txt b/crates/vim/test_data/test_delete_in_word/36text.txt new file mode 100644 index 0000000000..3b3f900a05 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/36text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The- brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/37head.txt b/crates/vim/test_data/test_delete_in_word/37head.txt new file mode 100644 index 0000000000..95fb9c638e --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/37head.txt @@ -0,0 +1 @@ +6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/38mode.txt b/crates/vim/test_data/test_delete_in_word/38mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/38mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/39text.txt b/crates/vim/test_data/test_delete_in_word/39text.txt new file mode 100644 index 0000000000..3b3f900a05 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/39text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The- brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/3text.txt b/crates/vim/test_data/test_delete_in_word/3text.txt new file mode 100644 index 0000000000..481389e676 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/3text.txt @@ -0,0 +1,12 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/40head.txt b/crates/vim/test_data/test_delete_in_word/40head.txt new file mode 100644 index 0000000000..95fb9c638e --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/40head.txt @@ -0,0 +1 @@ +6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/41mode.txt b/crates/vim/test_data/test_delete_in_word/41mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/41mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/42text.txt b/crates/vim/test_data/test_delete_in_word/42text.txt new file mode 100644 index 0000000000..7d13d92d35 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/42text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quickbrown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/43head.txt b/crates/vim/test_data/test_delete_in_word/43head.txt new file mode 100644 index 0000000000..da809253d8 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/43head.txt @@ -0,0 +1 @@ +6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/44mode.txt b/crates/vim/test_data/test_delete_in_word/44mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/44mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/45text.txt b/crates/vim/test_data/test_delete_in_word/45text.txt new file mode 100644 index 0000000000..95b76ce25b --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/45text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/46head.txt b/crates/vim/test_data/test_delete_in_word/46head.txt new file mode 100644 index 0000000000..c5a50698dd --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/46head.txt @@ -0,0 +1 @@ +6,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/47mode.txt b/crates/vim/test_data/test_delete_in_word/47mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/47mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/48text.txt b/crates/vim/test_data/test_delete_in_word/48text.txt new file mode 100644 index 0000000000..805a5ebe4c --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/48text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/49head.txt b/crates/vim/test_data/test_delete_in_word/49head.txt new file mode 100644 index 0000000000..afce6fe58c --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/49head.txt @@ -0,0 +1 @@ +6,14 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/4head.txt b/crates/vim/test_data/test_delete_in_word/4head.txt new file mode 100644 index 0000000000..352f6067e6 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/4head.txt @@ -0,0 +1 @@ +0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/50mode.txt b/crates/vim/test_data/test_delete_in_word/50mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/50mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/51text.txt b/crates/vim/test_data/test_delete_in_word/51text.txt new file mode 100644 index 0000000000..1d797e34b8 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/51text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/52head.txt b/crates/vim/test_data/test_delete_in_word/52head.txt new file mode 100644 index 0000000000..72c23ba506 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/52head.txt @@ -0,0 +1 @@ +7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/53mode.txt b/crates/vim/test_data/test_delete_in_word/53mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/53mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/54text.txt b/crates/vim/test_data/test_delete_in_word/54text.txt new file mode 100644 index 0000000000..8a35adfa26 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/54text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/55head.txt b/crates/vim/test_data/test_delete_in_word/55head.txt new file mode 100644 index 0000000000..ded56a5926 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/55head.txt @@ -0,0 +1 @@ +8,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/56mode.txt b/crates/vim/test_data/test_delete_in_word/56mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/56mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/57text.txt b/crates/vim/test_data/test_delete_in_word/57text.txt new file mode 100644 index 0000000000..26e9d0a1b6 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/57text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + +fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/58head.txt b/crates/vim/test_data/test_delete_in_word/58head.txt new file mode 100644 index 0000000000..8e69e0ad79 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/58head.txt @@ -0,0 +1 @@ +9,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/59mode.txt b/crates/vim/test_data/test_delete_in_word/59mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/59mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/5mode.txt b/crates/vim/test_data/test_delete_in_word/5mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/5mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/60text.txt b/crates/vim/test_data/test_delete_in_word/60text.txt new file mode 100644 index 0000000000..816bf47cf0 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/60text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox- over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/61head.txt b/crates/vim/test_data/test_delete_in_word/61head.txt new file mode 100644 index 0000000000..e0460d1bc5 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/61head.txt @@ -0,0 +1 @@ +9,6 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/62mode.txt b/crates/vim/test_data/test_delete_in_word/62mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/62mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/63text.txt b/crates/vim/test_data/test_delete_in_word/63text.txt new file mode 100644 index 0000000000..4b2cd80611 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/63text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/64head.txt b/crates/vim/test_data/test_delete_in_word/64head.txt new file mode 100644 index 0000000000..63ee4f53ac --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/64head.txt @@ -0,0 +1 @@ +10,11 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/65mode.txt b/crates/vim/test_data/test_delete_in_word/65mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/65mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/66text.txt b/crates/vim/test_data/test_delete_in_word/66text.txt new file mode 100644 index 0000000000..e971227436 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/66text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/67head.txt b/crates/vim/test_data/test_delete_in_word/67head.txt new file mode 100644 index 0000000000..94dbddc699 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/67head.txt @@ -0,0 +1 @@ +11,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/68mode.txt b/crates/vim/test_data/test_delete_in_word/68mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/68mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/69text.txt b/crates/vim/test_data/test_delete_in_word/69text.txt new file mode 100644 index 0000000000..481389e676 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/69text.txt @@ -0,0 +1,12 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/6text.txt b/crates/vim/test_data/test_delete_in_word/6text.txt new file mode 100644 index 0000000000..35fd5c89ba --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/6text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/70head.txt b/crates/vim/test_data/test_delete_in_word/70head.txt new file mode 100644 index 0000000000..352f6067e6 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/70head.txt @@ -0,0 +1 @@ +0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/71mode.txt b/crates/vim/test_data/test_delete_in_word/71mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/71mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/72text.txt b/crates/vim/test_data/test_delete_in_word/72text.txt new file mode 100644 index 0000000000..481389e676 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/72text.txt @@ -0,0 +1,12 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/73head.txt b/crates/vim/test_data/test_delete_in_word/73head.txt new file mode 100644 index 0000000000..352f6067e6 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/73head.txt @@ -0,0 +1 @@ +0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/74mode.txt b/crates/vim/test_data/test_delete_in_word/74mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/74mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/75text.txt b/crates/vim/test_data/test_delete_in_word/75text.txt new file mode 100644 index 0000000000..35fd5c89ba --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/75text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/76head.txt b/crates/vim/test_data/test_delete_in_word/76head.txt new file mode 100644 index 0000000000..8b208cec2d --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/76head.txt @@ -0,0 +1 @@ +0,14 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/77mode.txt b/crates/vim/test_data/test_delete_in_word/77mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/77mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/78text.txt b/crates/vim/test_data/test_delete_in_word/78text.txt new file mode 100644 index 0000000000..38c6ccbeac --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/78text.txt @@ -0,0 +1,12 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/79head.txt b/crates/vim/test_data/test_delete_in_word/79head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/79head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/7head.txt b/crates/vim/test_data/test_delete_in_word/7head.txt new file mode 100644 index 0000000000..8b208cec2d --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/7head.txt @@ -0,0 +1 @@ +0,14 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/80mode.txt b/crates/vim/test_data/test_delete_in_word/80mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/80mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/81text.txt b/crates/vim/test_data/test_delete_in_word/81text.txt new file mode 100644 index 0000000000..38c6ccbeac --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/81text.txt @@ -0,0 +1,12 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/82head.txt b/crates/vim/test_data/test_delete_in_word/82head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/82head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/83mode.txt b/crates/vim/test_data/test_delete_in_word/83mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/83mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/84text.txt b/crates/vim/test_data/test_delete_in_word/84text.txt new file mode 100644 index 0000000000..282218da91 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/84text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumpsover +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/85head.txt b/crates/vim/test_data/test_delete_in_word/85head.txt new file mode 100644 index 0000000000..bd712da63a --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/85head.txt @@ -0,0 +1 @@ +1,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/86mode.txt b/crates/vim/test_data/test_delete_in_word/86mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/86mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/87text.txt b/crates/vim/test_data/test_delete_in_word/87text.txt new file mode 100644 index 0000000000..0b042b7c39 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/87text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/88head.txt b/crates/vim/test_data/test_delete_in_word/88head.txt new file mode 100644 index 0000000000..06ce34087d --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/88head.txt @@ -0,0 +1 @@ +2,11 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/89mode.txt b/crates/vim/test_data/test_delete_in_word/89mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/89mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/8mode.txt b/crates/vim/test_data/test_delete_in_word/8mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/8mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/90text.txt b/crates/vim/test_data/test_delete_in_word/90text.txt new file mode 100644 index 0000000000..e971227436 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/90text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/91head.txt b/crates/vim/test_data/test_delete_in_word/91head.txt new file mode 100644 index 0000000000..7cad756336 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/91head.txt @@ -0,0 +1 @@ +3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/92mode.txt b/crates/vim/test_data/test_delete_in_word/92mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/92mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/93text.txt b/crates/vim/test_data/test_delete_in_word/93text.txt new file mode 100644 index 0000000000..e971227436 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/93text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/94head.txt b/crates/vim/test_data/test_delete_in_word/94head.txt new file mode 100644 index 0000000000..b26cd1f1d3 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/94head.txt @@ -0,0 +1 @@ +4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/95mode.txt b/crates/vim/test_data/test_delete_in_word/95mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/95mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/96text.txt b/crates/vim/test_data/test_delete_in_word/96text.txt new file mode 100644 index 0000000000..e971227436 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/96text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/97head.txt b/crates/vim/test_data/test_delete_in_word/97head.txt new file mode 100644 index 0000000000..880fda949a --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/97head.txt @@ -0,0 +1 @@ +5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/98mode.txt b/crates/vim/test_data/test_delete_in_word/98mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/98mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/99text.txt b/crates/vim/test_data/test_delete_in_word/99text.txt new file mode 100644 index 0000000000..d90b2c4e48 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/99text.txt @@ -0,0 +1,12 @@ +The quick brown +fox jumps over +the lazy dog + + + + brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_delete_in_word/9text.txt b/crates/vim/test_data/test_delete_in_word/9text.txt new file mode 100644 index 0000000000..38c6ccbeac --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word/9text.txt @@ -0,0 +1,12 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + + + fox-jumps over +the lazy dog + diff --git a/crates/vim/test_data/test_neovim/0text.txt b/crates/vim/test_data/test_neovim/0text.txt new file mode 100644 index 0000000000..30d74d2584 --- /dev/null +++ b/crates/vim/test_data/test_neovim/0text.txt @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/crates/vim/test_data/test_neovim/1head.txt b/crates/vim/test_data/test_neovim/1head.txt new file mode 100644 index 0000000000..7de346d2f5 --- /dev/null +++ b/crates/vim/test_data/test_neovim/1head.txt @@ -0,0 +1 @@ +0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_neovim/2mode.txt b/crates/vim/test_data/test_neovim/2mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_neovim/2mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/0text.txt b/crates/vim/test_data/test_word_text_object/0text.txt new file mode 100644 index 0000000000..456c2b1038 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/0text.txt @@ -0,0 +1,9 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/100head.txt b/crates/vim/test_data/test_word_text_object/100head.txt new file mode 100644 index 0000000000..da809253d8 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/100head.txt @@ -0,0 +1 @@ +6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/101mode.txt b/crates/vim/test_data/test_word_text_object/101mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/101mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/102text.txt b/crates/vim/test_data/test_word_text_object/102text.txt new file mode 100644 index 0000000000..fb34ebac01 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/102text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/103head.txt b/crates/vim/test_data/test_word_text_object/103head.txt new file mode 100644 index 0000000000..c5a50698dd --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/103head.txt @@ -0,0 +1 @@ +6,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/104mode.txt b/crates/vim/test_data/test_word_text_object/104mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/104mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/105text.txt b/crates/vim/test_data/test_word_text_object/105text.txt new file mode 100644 index 0000000000..de6c722fe0 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/105text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/106head.txt b/crates/vim/test_data/test_word_text_object/106head.txt new file mode 100644 index 0000000000..d933eb81fa --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/106head.txt @@ -0,0 +1 @@ +6,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/107mode.txt b/crates/vim/test_data/test_word_text_object/107mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/107mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/108text.txt b/crates/vim/test_data/test_word_text_object/108text.txt new file mode 100644 index 0000000000..6d1c944902 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/108text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown +fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/109head.txt b/crates/vim/test_data/test_word_text_object/109head.txt new file mode 100644 index 0000000000..72c23ba506 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/109head.txt @@ -0,0 +1 @@ +7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/10head.txt b/crates/vim/test_data/test_word_text_object/10head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/10head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/110mode.txt b/crates/vim/test_data/test_word_text_object/110mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/110mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/111text.txt b/crates/vim/test_data/test_word_text_object/111text.txt new file mode 100644 index 0000000000..a4effc421e --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/111text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/112head.txt b/crates/vim/test_data/test_word_text_object/112head.txt new file mode 100644 index 0000000000..60ada3d5d5 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/112head.txt @@ -0,0 +1 @@ +7,2 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/113mode.txt b/crates/vim/test_data/test_word_text_object/113mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/113mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/114text.txt b/crates/vim/test_data/test_word_text_object/114text.txt new file mode 100644 index 0000000000..41651c8e63 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/114text.txt @@ -0,0 +1,9 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/115head.txt b/crates/vim/test_data/test_word_text_object/115head.txt new file mode 100644 index 0000000000..352f6067e6 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/115head.txt @@ -0,0 +1 @@ +0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/116mode.txt b/crates/vim/test_data/test_word_text_object/116mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/116mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/117text.txt b/crates/vim/test_data/test_word_text_object/117text.txt new file mode 100644 index 0000000000..41651c8e63 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/117text.txt @@ -0,0 +1,9 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/118head.txt b/crates/vim/test_data/test_word_text_object/118head.txt new file mode 100644 index 0000000000..352f6067e6 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/118head.txt @@ -0,0 +1 @@ +0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/119mode.txt b/crates/vim/test_data/test_word_text_object/119mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/119mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/11mode.txt b/crates/vim/test_data/test_word_text_object/11mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/11mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/120text.txt b/crates/vim/test_data/test_word_text_object/120text.txt new file mode 100644 index 0000000000..f4e4cfe812 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/120text.txt @@ -0,0 +1,8 @@ +The quick brown jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/121head.txt b/crates/vim/test_data/test_word_text_object/121head.txt new file mode 100644 index 0000000000..5f7b20f0d9 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/121head.txt @@ -0,0 +1 @@ +0,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/122mode.txt b/crates/vim/test_data/test_word_text_object/122mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/122mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/123text.txt b/crates/vim/test_data/test_word_text_object/123text.txt new file mode 100644 index 0000000000..1a715d7a9d --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/123text.txt @@ -0,0 +1,9 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/124head.txt b/crates/vim/test_data/test_word_text_object/124head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/124head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/125mode.txt b/crates/vim/test_data/test_word_text_object/125mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/125mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/126text.txt b/crates/vim/test_data/test_word_text_object/126text.txt new file mode 100644 index 0000000000..1a715d7a9d --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/126text.txt @@ -0,0 +1,9 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/127head.txt b/crates/vim/test_data/test_word_text_object/127head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/127head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/128mode.txt b/crates/vim/test_data/test_word_text_object/128mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/128mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/129text.txt b/crates/vim/test_data/test_word_text_object/129text.txt new file mode 100644 index 0000000000..cfeeac1a14 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/129text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/12text.txt b/crates/vim/test_data/test_word_text_object/12text.txt new file mode 100644 index 0000000000..bffcedd2db --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/12text.txt @@ -0,0 +1,9 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/130head.txt b/crates/vim/test_data/test_word_text_object/130head.txt new file mode 100644 index 0000000000..bd712da63a --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/130head.txt @@ -0,0 +1 @@ +1,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/131mode.txt b/crates/vim/test_data/test_word_text_object/131mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/131mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/132text.txt b/crates/vim/test_data/test_word_text_object/132text.txt new file mode 100644 index 0000000000..b57b97d090 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/132text.txt @@ -0,0 +1,8 @@ +The quick brown +fox jumps over +the lazy dog + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/133head.txt b/crates/vim/test_data/test_word_text_object/133head.txt new file mode 100644 index 0000000000..28ce8bcc5c --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/133head.txt @@ -0,0 +1 @@ +2,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/134mode.txt b/crates/vim/test_data/test_word_text_object/134mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/134mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/135text.txt b/crates/vim/test_data/test_word_text_object/135text.txt new file mode 100644 index 0000000000..e2405acd79 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/135text.txt @@ -0,0 +1,8 @@ +The quick brown +fox jumps over +the lazy dog + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/136head.txt b/crates/vim/test_data/test_word_text_object/136head.txt new file mode 100644 index 0000000000..7cad756336 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/136head.txt @@ -0,0 +1 @@ +3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/137mode.txt b/crates/vim/test_data/test_word_text_object/137mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/137mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/138text.txt b/crates/vim/test_data/test_word_text_object/138text.txt new file mode 100644 index 0000000000..e2405acd79 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/138text.txt @@ -0,0 +1,8 @@ +The quick brown +fox jumps over +the lazy dog + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/139head.txt b/crates/vim/test_data/test_word_text_object/139head.txt new file mode 100644 index 0000000000..b26cd1f1d3 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/139head.txt @@ -0,0 +1 @@ +4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/13head.txt b/crates/vim/test_data/test_word_text_object/13head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/13head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/140mode.txt b/crates/vim/test_data/test_word_text_object/140mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/140mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/141text.txt b/crates/vim/test_data/test_word_text_object/141text.txt new file mode 100644 index 0000000000..e075f5e01a --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/141text.txt @@ -0,0 +1,8 @@ +The quick brown +fox jumps over +the lazy dog + + +-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/142head.txt b/crates/vim/test_data/test_word_text_object/142head.txt new file mode 100644 index 0000000000..880fda949a --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/142head.txt @@ -0,0 +1 @@ +5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/143mode.txt b/crates/vim/test_data/test_word_text_object/143mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/143mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/144text.txt b/crates/vim/test_data/test_word_text_object/144text.txt new file mode 100644 index 0000000000..6042949b9a --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/144text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/145head.txt b/crates/vim/test_data/test_word_text_object/145head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/145head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/146mode.txt b/crates/vim/test_data/test_word_text_object/146mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/146mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/147text.txt b/crates/vim/test_data/test_word_text_object/147text.txt new file mode 100644 index 0000000000..edc3de8e10 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/147text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +Thequick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/148head.txt b/crates/vim/test_data/test_word_text_object/148head.txt new file mode 100644 index 0000000000..c20df71d5e --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/148head.txt @@ -0,0 +1 @@ +6,3 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/149mode.txt b/crates/vim/test_data/test_word_text_object/149mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/149mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/14mode.txt b/crates/vim/test_data/test_word_text_object/14mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/14mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/150text.txt b/crates/vim/test_data/test_word_text_object/150text.txt new file mode 100644 index 0000000000..44972e7628 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/150text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/151head.txt b/crates/vim/test_data/test_word_text_object/151head.txt new file mode 100644 index 0000000000..95fb9c638e --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/151head.txt @@ -0,0 +1 @@ +6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/152mode.txt b/crates/vim/test_data/test_word_text_object/152mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/152mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/153text.txt b/crates/vim/test_data/test_word_text_object/153text.txt new file mode 100644 index 0000000000..44972e7628 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/153text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/154head.txt b/crates/vim/test_data/test_word_text_object/154head.txt new file mode 100644 index 0000000000..95fb9c638e --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/154head.txt @@ -0,0 +1 @@ +6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/155mode.txt b/crates/vim/test_data/test_word_text_object/155mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/155mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/156text.txt b/crates/vim/test_data/test_word_text_object/156text.txt new file mode 100644 index 0000000000..d7c8eebb49 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/156text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/157head.txt b/crates/vim/test_data/test_word_text_object/157head.txt new file mode 100644 index 0000000000..da809253d8 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/157head.txt @@ -0,0 +1 @@ +6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/158mode.txt b/crates/vim/test_data/test_word_text_object/158mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/158mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/159text.txt b/crates/vim/test_data/test_word_text_object/159text.txt new file mode 100644 index 0000000000..d7c8eebb49 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/159text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/15text.txt b/crates/vim/test_data/test_word_text_object/15text.txt new file mode 100644 index 0000000000..3abea644f1 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/15text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumpsover +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/160head.txt b/crates/vim/test_data/test_word_text_object/160head.txt new file mode 100644 index 0000000000..c5a50698dd --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/160head.txt @@ -0,0 +1 @@ +6,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/161mode.txt b/crates/vim/test_data/test_word_text_object/161mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/161mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/162text.txt b/crates/vim/test_data/test_word_text_object/162text.txt new file mode 100644 index 0000000000..f0f447c8fa --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/162text.txt @@ -0,0 +1,8 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/163head.txt b/crates/vim/test_data/test_word_text_object/163head.txt new file mode 100644 index 0000000000..d933eb81fa --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/163head.txt @@ -0,0 +1 @@ +6,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/164mode.txt b/crates/vim/test_data/test_word_text_object/164mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/164mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/165text.txt b/crates/vim/test_data/test_word_text_object/165text.txt new file mode 100644 index 0000000000..3fcf6c5682 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/165text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown +-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/166head.txt b/crates/vim/test_data/test_word_text_object/166head.txt new file mode 100644 index 0000000000..72c23ba506 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/166head.txt @@ -0,0 +1 @@ +7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/167mode.txt b/crates/vim/test_data/test_word_text_object/167mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/167mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/168text.txt b/crates/vim/test_data/test_word_text_object/168text.txt new file mode 100644 index 0000000000..88098b63df --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/168text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + fox-over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/169head.txt b/crates/vim/test_data/test_word_text_object/169head.txt new file mode 100644 index 0000000000..6cf0fedea6 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/169head.txt @@ -0,0 +1 @@ +7,6 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/16head.txt b/crates/vim/test_data/test_word_text_object/16head.txt new file mode 100644 index 0000000000..bd712da63a --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/16head.txt @@ -0,0 +1 @@ +1,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/170mode.txt b/crates/vim/test_data/test_word_text_object/170mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/170mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/171text.txt b/crates/vim/test_data/test_word_text_object/171text.txt new file mode 100644 index 0000000000..41651c8e63 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/171text.txt @@ -0,0 +1,9 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/172head.txt b/crates/vim/test_data/test_word_text_object/172head.txt new file mode 100644 index 0000000000..352f6067e6 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/172head.txt @@ -0,0 +1 @@ +0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/173mode.txt b/crates/vim/test_data/test_word_text_object/173mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/173mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/174text.txt b/crates/vim/test_data/test_word_text_object/174text.txt new file mode 100644 index 0000000000..41651c8e63 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/174text.txt @@ -0,0 +1,9 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/175head.txt b/crates/vim/test_data/test_word_text_object/175head.txt new file mode 100644 index 0000000000..352f6067e6 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/175head.txt @@ -0,0 +1 @@ +0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/176mode.txt b/crates/vim/test_data/test_word_text_object/176mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/176mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/177text.txt b/crates/vim/test_data/test_word_text_object/177text.txt new file mode 100644 index 0000000000..f4e4cfe812 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/177text.txt @@ -0,0 +1,8 @@ +The quick brown jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/178head.txt b/crates/vim/test_data/test_word_text_object/178head.txt new file mode 100644 index 0000000000..5f7b20f0d9 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/178head.txt @@ -0,0 +1 @@ +0,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/179mode.txt b/crates/vim/test_data/test_word_text_object/179mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/179mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/17mode.txt b/crates/vim/test_data/test_word_text_object/17mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/17mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/180text.txt b/crates/vim/test_data/test_word_text_object/180text.txt new file mode 100644 index 0000000000..1a715d7a9d --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/180text.txt @@ -0,0 +1,9 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/181head.txt b/crates/vim/test_data/test_word_text_object/181head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/181head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/182mode.txt b/crates/vim/test_data/test_word_text_object/182mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/182mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/183text.txt b/crates/vim/test_data/test_word_text_object/183text.txt new file mode 100644 index 0000000000..1a715d7a9d --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/183text.txt @@ -0,0 +1,9 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/184head.txt b/crates/vim/test_data/test_word_text_object/184head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/184head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/185mode.txt b/crates/vim/test_data/test_word_text_object/185mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/185mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/186text.txt b/crates/vim/test_data/test_word_text_object/186text.txt new file mode 100644 index 0000000000..cfeeac1a14 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/186text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/187head.txt b/crates/vim/test_data/test_word_text_object/187head.txt new file mode 100644 index 0000000000..bd712da63a --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/187head.txt @@ -0,0 +1 @@ +1,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/188mode.txt b/crates/vim/test_data/test_word_text_object/188mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/188mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/189text.txt b/crates/vim/test_data/test_word_text_object/189text.txt new file mode 100644 index 0000000000..b57b97d090 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/189text.txt @@ -0,0 +1,8 @@ +The quick brown +fox jumps over +the lazy dog + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/18text.txt b/crates/vim/test_data/test_word_text_object/18text.txt new file mode 100644 index 0000000000..ebac2acd25 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/18text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/190head.txt b/crates/vim/test_data/test_word_text_object/190head.txt new file mode 100644 index 0000000000..28ce8bcc5c --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/190head.txt @@ -0,0 +1 @@ +2,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/191mode.txt b/crates/vim/test_data/test_word_text_object/191mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/191mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/192text.txt b/crates/vim/test_data/test_word_text_object/192text.txt new file mode 100644 index 0000000000..e2405acd79 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/192text.txt @@ -0,0 +1,8 @@ +The quick brown +fox jumps over +the lazy dog + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/193head.txt b/crates/vim/test_data/test_word_text_object/193head.txt new file mode 100644 index 0000000000..7cad756336 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/193head.txt @@ -0,0 +1 @@ +3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/194mode.txt b/crates/vim/test_data/test_word_text_object/194mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/194mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/195text.txt b/crates/vim/test_data/test_word_text_object/195text.txt new file mode 100644 index 0000000000..e2405acd79 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/195text.txt @@ -0,0 +1,8 @@ +The quick brown +fox jumps over +the lazy dog + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/196head.txt b/crates/vim/test_data/test_word_text_object/196head.txt new file mode 100644 index 0000000000..b26cd1f1d3 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/196head.txt @@ -0,0 +1 @@ +4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/197mode.txt b/crates/vim/test_data/test_word_text_object/197mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/197mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/198text.txt b/crates/vim/test_data/test_word_text_object/198text.txt new file mode 100644 index 0000000000..4363975ac2 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/198text.txt @@ -0,0 +1,8 @@ +The quick brown +fox jumps over +the lazy dog + + + brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/199head.txt b/crates/vim/test_data/test_word_text_object/199head.txt new file mode 100644 index 0000000000..880fda949a --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/199head.txt @@ -0,0 +1 @@ +5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/19head.txt b/crates/vim/test_data/test_word_text_object/19head.txt new file mode 100644 index 0000000000..28ce8bcc5c --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/19head.txt @@ -0,0 +1 @@ +2,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/1head.txt b/crates/vim/test_data/test_word_text_object/1head.txt new file mode 100644 index 0000000000..352f6067e6 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/1head.txt @@ -0,0 +1 @@ +0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/200mode.txt b/crates/vim/test_data/test_word_text_object/200mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/200mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/201text.txt b/crates/vim/test_data/test_word_text_object/201text.txt new file mode 100644 index 0000000000..7c0186083e --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/201text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/202head.txt b/crates/vim/test_data/test_word_text_object/202head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/202head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/203mode.txt b/crates/vim/test_data/test_word_text_object/203mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/203mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/204text.txt b/crates/vim/test_data/test_word_text_object/204text.txt new file mode 100644 index 0000000000..7c0186083e --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/204text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/205head.txt b/crates/vim/test_data/test_word_text_object/205head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/205head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/206mode.txt b/crates/vim/test_data/test_word_text_object/206mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/206mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/207text.txt b/crates/vim/test_data/test_word_text_object/207text.txt new file mode 100644 index 0000000000..7c0186083e --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/207text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/208head.txt b/crates/vim/test_data/test_word_text_object/208head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/208head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/209mode.txt b/crates/vim/test_data/test_word_text_object/209mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/209mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/20mode.txt b/crates/vim/test_data/test_word_text_object/20mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/20mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/210text.txt b/crates/vim/test_data/test_word_text_object/210text.txt new file mode 100644 index 0000000000..7c0186083e --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/210text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/211head.txt b/crates/vim/test_data/test_word_text_object/211head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/211head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/212mode.txt b/crates/vim/test_data/test_word_text_object/212mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/212mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/213text.txt b/crates/vim/test_data/test_word_text_object/213text.txt new file mode 100644 index 0000000000..d7c8eebb49 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/213text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/214head.txt b/crates/vim/test_data/test_word_text_object/214head.txt new file mode 100644 index 0000000000..da809253d8 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/214head.txt @@ -0,0 +1 @@ +6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/215mode.txt b/crates/vim/test_data/test_word_text_object/215mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/215mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/216text.txt b/crates/vim/test_data/test_word_text_object/216text.txt new file mode 100644 index 0000000000..d7c8eebb49 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/216text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/217head.txt b/crates/vim/test_data/test_word_text_object/217head.txt new file mode 100644 index 0000000000..c5a50698dd --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/217head.txt @@ -0,0 +1 @@ +6,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/218mode.txt b/crates/vim/test_data/test_word_text_object/218mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/218mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/219text.txt b/crates/vim/test_data/test_word_text_object/219text.txt new file mode 100644 index 0000000000..1cbb0c4272 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/219text.txt @@ -0,0 +1,8 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/21text.txt b/crates/vim/test_data/test_word_text_object/21text.txt new file mode 100644 index 0000000000..3b33deb2ae --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/21text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/220head.txt b/crates/vim/test_data/test_word_text_object/220head.txt new file mode 100644 index 0000000000..d933eb81fa --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/220head.txt @@ -0,0 +1 @@ +6,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/221mode.txt b/crates/vim/test_data/test_word_text_object/221mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/221mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/222text.txt b/crates/vim/test_data/test_word_text_object/222text.txt new file mode 100644 index 0000000000..bc43fca8a1 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/222text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/223head.txt b/crates/vim/test_data/test_word_text_object/223head.txt new file mode 100644 index 0000000000..72c23ba506 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/223head.txt @@ -0,0 +1 @@ +7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/224mode.txt b/crates/vim/test_data/test_word_text_object/224mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/224mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/225text.txt b/crates/vim/test_data/test_word_text_object/225text.txt new file mode 100644 index 0000000000..3fa4fd5f45 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/225text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/226head.txt b/crates/vim/test_data/test_word_text_object/226head.txt new file mode 100644 index 0000000000..60ada3d5d5 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/226head.txt @@ -0,0 +1 @@ +7,2 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/227mode.txt b/crates/vim/test_data/test_word_text_object/227mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/227mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/228text.txt b/crates/vim/test_data/test_word_text_object/228text.txt new file mode 100644 index 0000000000..456c2b1038 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/228text.txt @@ -0,0 +1,9 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/229head.txt b/crates/vim/test_data/test_word_text_object/229head.txt new file mode 100644 index 0000000000..352f6067e6 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/229head.txt @@ -0,0 +1 @@ +0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/22head.txt b/crates/vim/test_data/test_word_text_object/22head.txt new file mode 100644 index 0000000000..7cad756336 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/22head.txt @@ -0,0 +1 @@ +3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/230mode.txt b/crates/vim/test_data/test_word_text_object/230mode.txt new file mode 100644 index 0000000000..ab75e91dc4 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/230mode.txt @@ -0,0 +1 @@ +"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/231text.txt b/crates/vim/test_data/test_word_text_object/231text.txt new file mode 100644 index 0000000000..286752a955 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/231text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/23mode.txt b/crates/vim/test_data/test_word_text_object/23mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/23mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/24text.txt b/crates/vim/test_data/test_word_text_object/24text.txt new file mode 100644 index 0000000000..3b33deb2ae --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/24text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/25head.txt b/crates/vim/test_data/test_word_text_object/25head.txt new file mode 100644 index 0000000000..b26cd1f1d3 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/25head.txt @@ -0,0 +1 @@ +4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/26mode.txt b/crates/vim/test_data/test_word_text_object/26mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/26mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/27text.txt b/crates/vim/test_data/test_word_text_object/27text.txt new file mode 100644 index 0000000000..3b33deb2ae --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/27text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/28head.txt b/crates/vim/test_data/test_word_text_object/28head.txt new file mode 100644 index 0000000000..880fda949a --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/28head.txt @@ -0,0 +1 @@ +5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/29mode.txt b/crates/vim/test_data/test_word_text_object/29mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/29mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/2mode.txt b/crates/vim/test_data/test_word_text_object/2mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/2mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/30text.txt b/crates/vim/test_data/test_word_text_object/30text.txt new file mode 100644 index 0000000000..6042949b9a --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/30text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/31head.txt b/crates/vim/test_data/test_word_text_object/31head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/31head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/32mode.txt b/crates/vim/test_data/test_word_text_object/32mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/32mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/33text.txt b/crates/vim/test_data/test_word_text_object/33text.txt new file mode 100644 index 0000000000..edc3de8e10 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/33text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +Thequick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/34head.txt b/crates/vim/test_data/test_word_text_object/34head.txt new file mode 100644 index 0000000000..c20df71d5e --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/34head.txt @@ -0,0 +1 @@ +6,3 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/35mode.txt b/crates/vim/test_data/test_word_text_object/35mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/35mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/36text.txt b/crates/vim/test_data/test_word_text_object/36text.txt new file mode 100644 index 0000000000..dedd4d92b7 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/36text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The- brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/37head.txt b/crates/vim/test_data/test_word_text_object/37head.txt new file mode 100644 index 0000000000..95fb9c638e --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/37head.txt @@ -0,0 +1 @@ +6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/38mode.txt b/crates/vim/test_data/test_word_text_object/38mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/38mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/39text.txt b/crates/vim/test_data/test_word_text_object/39text.txt new file mode 100644 index 0000000000..dedd4d92b7 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/39text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The- brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/3text.txt b/crates/vim/test_data/test_word_text_object/3text.txt new file mode 100644 index 0000000000..456c2b1038 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/3text.txt @@ -0,0 +1,9 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/40head.txt b/crates/vim/test_data/test_word_text_object/40head.txt new file mode 100644 index 0000000000..95fb9c638e --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/40head.txt @@ -0,0 +1 @@ +6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/41mode.txt b/crates/vim/test_data/test_word_text_object/41mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/41mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/42text.txt b/crates/vim/test_data/test_word_text_object/42text.txt new file mode 100644 index 0000000000..5bb4f4effb --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/42text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quickbrown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/43head.txt b/crates/vim/test_data/test_word_text_object/43head.txt new file mode 100644 index 0000000000..da809253d8 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/43head.txt @@ -0,0 +1 @@ +6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/44mode.txt b/crates/vim/test_data/test_word_text_object/44mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/44mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/45text.txt b/crates/vim/test_data/test_word_text_object/45text.txt new file mode 100644 index 0000000000..fb34ebac01 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/45text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/46head.txt b/crates/vim/test_data/test_word_text_object/46head.txt new file mode 100644 index 0000000000..c5a50698dd --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/46head.txt @@ -0,0 +1 @@ +6,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/47mode.txt b/crates/vim/test_data/test_word_text_object/47mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/47mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/48text.txt b/crates/vim/test_data/test_word_text_object/48text.txt new file mode 100644 index 0000000000..de6c722fe0 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/48text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/49head.txt b/crates/vim/test_data/test_word_text_object/49head.txt new file mode 100644 index 0000000000..d933eb81fa --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/49head.txt @@ -0,0 +1 @@ +6,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/4head.txt b/crates/vim/test_data/test_word_text_object/4head.txt new file mode 100644 index 0000000000..352f6067e6 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/4head.txt @@ -0,0 +1 @@ +0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/50mode.txt b/crates/vim/test_data/test_word_text_object/50mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/50mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/51text.txt b/crates/vim/test_data/test_word_text_object/51text.txt new file mode 100644 index 0000000000..6d1c944902 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/51text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown +fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/52head.txt b/crates/vim/test_data/test_word_text_object/52head.txt new file mode 100644 index 0000000000..72c23ba506 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/52head.txt @@ -0,0 +1 @@ +7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/53mode.txt b/crates/vim/test_data/test_word_text_object/53mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/53mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/54text.txt b/crates/vim/test_data/test_word_text_object/54text.txt new file mode 100644 index 0000000000..0f7af08f72 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/54text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + fox- over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/55head.txt b/crates/vim/test_data/test_word_text_object/55head.txt new file mode 100644 index 0000000000..6cf0fedea6 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/55head.txt @@ -0,0 +1 @@ +7,6 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/56mode.txt b/crates/vim/test_data/test_word_text_object/56mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/56mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/57text.txt b/crates/vim/test_data/test_word_text_object/57text.txt new file mode 100644 index 0000000000..456c2b1038 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/57text.txt @@ -0,0 +1,9 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/58head.txt b/crates/vim/test_data/test_word_text_object/58head.txt new file mode 100644 index 0000000000..352f6067e6 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/58head.txt @@ -0,0 +1 @@ +0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/59mode.txt b/crates/vim/test_data/test_word_text_object/59mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/59mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/5mode.txt b/crates/vim/test_data/test_word_text_object/5mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/5mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/60text.txt b/crates/vim/test_data/test_word_text_object/60text.txt new file mode 100644 index 0000000000..456c2b1038 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/60text.txt @@ -0,0 +1,9 @@ +The quick +fox jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/61head.txt b/crates/vim/test_data/test_word_text_object/61head.txt new file mode 100644 index 0000000000..352f6067e6 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/61head.txt @@ -0,0 +1 @@ +0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/62mode.txt b/crates/vim/test_data/test_word_text_object/62mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/62mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/63text.txt b/crates/vim/test_data/test_word_text_object/63text.txt new file mode 100644 index 0000000000..286752a955 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/63text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/64head.txt b/crates/vim/test_data/test_word_text_object/64head.txt new file mode 100644 index 0000000000..5f7b20f0d9 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/64head.txt @@ -0,0 +1 @@ +0,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/65mode.txt b/crates/vim/test_data/test_word_text_object/65mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/65mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/66text.txt b/crates/vim/test_data/test_word_text_object/66text.txt new file mode 100644 index 0000000000..bffcedd2db --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/66text.txt @@ -0,0 +1,9 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/67head.txt b/crates/vim/test_data/test_word_text_object/67head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/67head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/68mode.txt b/crates/vim/test_data/test_word_text_object/68mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/68mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/69text.txt b/crates/vim/test_data/test_word_text_object/69text.txt new file mode 100644 index 0000000000..bffcedd2db --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/69text.txt @@ -0,0 +1,9 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/6text.txt b/crates/vim/test_data/test_word_text_object/6text.txt new file mode 100644 index 0000000000..286752a955 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/6text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/70head.txt b/crates/vim/test_data/test_word_text_object/70head.txt new file mode 100644 index 0000000000..28f7acb8c9 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/70head.txt @@ -0,0 +1 @@ +1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/71mode.txt b/crates/vim/test_data/test_word_text_object/71mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/71mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/72text.txt b/crates/vim/test_data/test_word_text_object/72text.txt new file mode 100644 index 0000000000..3abea644f1 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/72text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumpsover +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/73head.txt b/crates/vim/test_data/test_word_text_object/73head.txt new file mode 100644 index 0000000000..bd712da63a --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/73head.txt @@ -0,0 +1 @@ +1,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/74mode.txt b/crates/vim/test_data/test_word_text_object/74mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/74mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/75text.txt b/crates/vim/test_data/test_word_text_object/75text.txt new file mode 100644 index 0000000000..ebac2acd25 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/75text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/76head.txt b/crates/vim/test_data/test_word_text_object/76head.txt new file mode 100644 index 0000000000..28ce8bcc5c --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/76head.txt @@ -0,0 +1 @@ +2,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/77mode.txt b/crates/vim/test_data/test_word_text_object/77mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/77mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/78text.txt b/crates/vim/test_data/test_word_text_object/78text.txt new file mode 100644 index 0000000000..3b33deb2ae --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/78text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/79head.txt b/crates/vim/test_data/test_word_text_object/79head.txt new file mode 100644 index 0000000000..7cad756336 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/79head.txt @@ -0,0 +1 @@ +3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/7head.txt b/crates/vim/test_data/test_word_text_object/7head.txt new file mode 100644 index 0000000000..5f7b20f0d9 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/7head.txt @@ -0,0 +1 @@ +0,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/80mode.txt b/crates/vim/test_data/test_word_text_object/80mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/80mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/81text.txt b/crates/vim/test_data/test_word_text_object/81text.txt new file mode 100644 index 0000000000..3b33deb2ae --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/81text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/82head.txt b/crates/vim/test_data/test_word_text_object/82head.txt new file mode 100644 index 0000000000..b26cd1f1d3 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/82head.txt @@ -0,0 +1 @@ +4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/83mode.txt b/crates/vim/test_data/test_word_text_object/83mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/83mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/84text.txt b/crates/vim/test_data/test_word_text_object/84text.txt new file mode 100644 index 0000000000..3b33deb2ae --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/84text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/85head.txt b/crates/vim/test_data/test_word_text_object/85head.txt new file mode 100644 index 0000000000..880fda949a --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/85head.txt @@ -0,0 +1 @@ +5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/86mode.txt b/crates/vim/test_data/test_word_text_object/86mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/86mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/87text.txt b/crates/vim/test_data/test_word_text_object/87text.txt new file mode 100644 index 0000000000..a647811a26 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/87text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + + brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/88head.txt b/crates/vim/test_data/test_word_text_object/88head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/88head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/89mode.txt b/crates/vim/test_data/test_word_text_object/89mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/89mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/8mode.txt b/crates/vim/test_data/test_word_text_object/8mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/8mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/90text.txt b/crates/vim/test_data/test_word_text_object/90text.txt new file mode 100644 index 0000000000..a647811a26 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/90text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + + brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/91head.txt b/crates/vim/test_data/test_word_text_object/91head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/91head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/92mode.txt b/crates/vim/test_data/test_word_text_object/92mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/92mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/93text.txt b/crates/vim/test_data/test_word_text_object/93text.txt new file mode 100644 index 0000000000..a647811a26 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/93text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + + brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/94head.txt b/crates/vim/test_data/test_word_text_object/94head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/94head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/95mode.txt b/crates/vim/test_data/test_word_text_object/95mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/95mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/96text.txt b/crates/vim/test_data/test_word_text_object/96text.txt new file mode 100644 index 0000000000..a647811a26 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/96text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + + brown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/97head.txt b/crates/vim/test_data/test_word_text_object/97head.txt new file mode 100644 index 0000000000..f7c508be16 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/97head.txt @@ -0,0 +1 @@ +6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/98mode.txt b/crates/vim/test_data/test_word_text_object/98mode.txt new file mode 100644 index 0000000000..d0d2c59887 --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/98mode.txt @@ -0,0 +1 @@ +"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/99text.txt b/crates/vim/test_data/test_word_text_object/99text.txt new file mode 100644 index 0000000000..5bb4f4effb --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/99text.txt @@ -0,0 +1,9 @@ +The quick brown +fox jumps over +the lazy dog + + + +The-quickbrown + fox-jumps over +the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/9text.txt b/crates/vim/test_data/test_word_text_object/9text.txt new file mode 100644 index 0000000000..bffcedd2db --- /dev/null +++ b/crates/vim/test_data/test_word_text_object/9text.txt @@ -0,0 +1,9 @@ +The quick brown +fox over +the lazy dog + + + +The-quick brown + fox-jumps over +the lazy dog From 515c1ea1236764ab7b2c8f0ee6958e45c63ef971 Mon Sep 17 00:00:00 2001 From: K Simmons Date: Sat, 8 Oct 2022 21:20:47 -0700 Subject: [PATCH 202/314] Fixed some neovim test context issues, added repeated commands in vim mode, and ported some tests to use the neovim testing strategy --- Cargo.lock | 7629 ----------------- assets/keymaps/vim.json | 59 +- crates/collab/src/integration_tests.rs | 2 + crates/editor/src/test.rs | 57 +- crates/gpui/src/app.rs | 3 + crates/gpui/src/test.rs | 2 + crates/gpui_macros/src/gpui_macros.rs | 7 +- crates/vim/Cargo.toml | 1 + crates/vim/src/motion.rs | 35 +- crates/vim/src/normal.rs | 555 +- crates/vim/src/normal/change.rs | 164 +- crates/vim/src/normal/delete.rs | 4 +- crates/vim/src/normal/yank.rs | 4 +- crates/vim/src/object.rs | 178 +- crates/vim/src/state.rs | 3 + .../neovim_backed_binding_test_context.rs | 39 +- .../neovim_backed_test_context.rs | 398 +- .../vim/src/test_contexts/vim_test_context.rs | 3 +- crates/vim/src/vim.rs | 28 +- crates/vim/src/visual.rs | 10 +- .../neovim_backed_test_context_works.json | 1 + crates/vim/test_data/test_a.json | 1 + crates/vim/test_data/test_backspace.json | 1 + .../test_change_around_sentence.json | 1 + .../test_change_around_sentence/0text.txt | 1 - .../test_change_around_sentence/10head.txt | 1 - .../test_change_around_sentence/1head.txt | 1 - .../test_change_around_sentence/2mode.txt | 1 - .../test_change_around_sentence/3text.txt | 1 - .../test_change_around_sentence/4head.txt | 1 - .../test_change_around_sentence/5mode.txt | 1 - .../test_change_around_sentence/6text.txt | 1 - .../test_change_around_sentence/7head.txt | 1 - .../test_change_around_sentence/8mode.txt | 1 - .../test_change_around_sentence/9text.txt | 1 - .../test_data/test_change_around_word.json | 1 + .../test_change_around_word/0text.txt | 12 - .../test_change_around_word/100head.txt | 1 - .../test_change_around_word/101mode.txt | 1 - .../test_change_around_word/102text.txt | 12 - .../test_change_around_word/103head.txt | 1 - .../test_change_around_word/104mode.txt | 1 - .../test_change_around_word/105text.txt | 12 - .../test_change_around_word/106head.txt | 1 - .../test_change_around_word/107mode.txt | 1 - .../test_change_around_word/108text.txt | 12 - .../test_change_around_word/109head.txt | 1 - .../test_change_around_word/10head.txt | 1 - .../test_change_around_word/110mode.txt | 1 - .../test_change_around_word/111text.txt | 12 - .../test_change_around_word/112head.txt | 1 - .../test_change_around_word/113mode.txt | 1 - .../test_change_around_word/114text.txt | 12 - .../test_change_around_word/115head.txt | 1 - .../test_change_around_word/116mode.txt | 1 - .../test_change_around_word/117text.txt | 9 - .../test_change_around_word/118head.txt | 1 - .../test_change_around_word/119mode.txt | 1 - .../test_change_around_word/11mode.txt | 1 - .../test_change_around_word/120text.txt | 10 - .../test_change_around_word/121head.txt | 1 - .../test_change_around_word/122mode.txt | 1 - .../test_change_around_word/123text.txt | 11 - .../test_change_around_word/124head.txt | 1 - .../test_change_around_word/125mode.txt | 1 - .../test_change_around_word/126text.txt | 12 - .../test_change_around_word/127head.txt | 1 - .../test_change_around_word/128mode.txt | 1 - .../test_change_around_word/129text.txt | 12 - .../test_change_around_word/12text.txt | 12 - .../test_change_around_word/130head.txt | 1 - .../test_change_around_word/131mode.txt | 1 - .../test_change_around_word/132text.txt | 11 - .../test_change_around_word/133head.txt | 1 - .../test_change_around_word/134mode.txt | 1 - .../test_change_around_word/135text.txt | 11 - .../test_change_around_word/136head.txt | 1 - .../test_change_around_word/137mode.txt | 1 - .../test_change_around_word/13head.txt | 1 - .../test_change_around_word/14mode.txt | 1 - .../test_change_around_word/15text.txt | 12 - .../test_change_around_word/16head.txt | 1 - .../test_change_around_word/17mode.txt | 1 - .../test_change_around_word/18text.txt | 11 - .../test_change_around_word/19head.txt | 1 - .../test_change_around_word/1head.txt | 1 - .../test_change_around_word/20mode.txt | 1 - .../test_change_around_word/21text.txt | 11 - .../test_change_around_word/22head.txt | 1 - .../test_change_around_word/23mode.txt | 1 - .../test_change_around_word/24text.txt | 11 - .../test_change_around_word/25head.txt | 1 - .../test_change_around_word/26mode.txt | 1 - .../test_change_around_word/27text.txt | 11 - .../test_change_around_word/28head.txt | 1 - .../test_change_around_word/29mode.txt | 1 - .../test_change_around_word/2mode.txt | 1 - .../test_change_around_word/30text.txt | 12 - .../test_change_around_word/31head.txt | 1 - .../test_change_around_word/32mode.txt | 1 - .../test_change_around_word/33text.txt | 12 - .../test_change_around_word/34head.txt | 1 - .../test_change_around_word/35mode.txt | 1 - .../test_change_around_word/36text.txt | 12 - .../test_change_around_word/37head.txt | 1 - .../test_change_around_word/38mode.txt | 1 - .../test_change_around_word/39text.txt | 12 - .../test_change_around_word/3text.txt | 12 - .../test_change_around_word/40head.txt | 1 - .../test_change_around_word/41mode.txt | 1 - .../test_change_around_word/42text.txt | 12 - .../test_change_around_word/43head.txt | 1 - .../test_change_around_word/44mode.txt | 1 - .../test_change_around_word/45text.txt | 12 - .../test_change_around_word/46head.txt | 1 - .../test_change_around_word/47mode.txt | 1 - .../test_change_around_word/48text.txt | 9 - .../test_change_around_word/49head.txt | 1 - .../test_change_around_word/4head.txt | 1 - .../test_change_around_word/50mode.txt | 1 - .../test_change_around_word/51text.txt | 10 - .../test_change_around_word/52head.txt | 1 - .../test_change_around_word/53mode.txt | 1 - .../test_change_around_word/54text.txt | 11 - .../test_change_around_word/55head.txt | 1 - .../test_change_around_word/56mode.txt | 1 - .../test_change_around_word/57text.txt | 12 - .../test_change_around_word/58head.txt | 1 - .../test_change_around_word/59mode.txt | 1 - .../test_change_around_word/5mode.txt | 1 - .../test_change_around_word/60text.txt | 12 - .../test_change_around_word/61head.txt | 1 - .../test_change_around_word/62mode.txt | 1 - .../test_change_around_word/63text.txt | 11 - .../test_change_around_word/64head.txt | 1 - .../test_change_around_word/65mode.txt | 1 - .../test_change_around_word/66text.txt | 11 - .../test_change_around_word/67head.txt | 1 - .../test_change_around_word/68mode.txt | 1 - .../test_change_around_word/69text.txt | 12 - .../test_change_around_word/6text.txt | 11 - .../test_change_around_word/70head.txt | 1 - .../test_change_around_word/71mode.txt | 1 - .../test_change_around_word/72text.txt | 12 - .../test_change_around_word/73head.txt | 1 - .../test_change_around_word/74mode.txt | 1 - .../test_change_around_word/75text.txt | 11 - .../test_change_around_word/76head.txt | 1 - .../test_change_around_word/77mode.txt | 1 - .../test_change_around_word/78text.txt | 12 - .../test_change_around_word/79head.txt | 1 - .../test_change_around_word/7head.txt | 1 - .../test_change_around_word/80mode.txt | 1 - .../test_change_around_word/81text.txt | 12 - .../test_change_around_word/82head.txt | 1 - .../test_change_around_word/83mode.txt | 1 - .../test_change_around_word/84text.txt | 12 - .../test_change_around_word/85head.txt | 1 - .../test_change_around_word/86mode.txt | 1 - .../test_change_around_word/87text.txt | 11 - .../test_change_around_word/88head.txt | 1 - .../test_change_around_word/89mode.txt | 1 - .../test_change_around_word/8mode.txt | 1 - .../test_change_around_word/90text.txt | 11 - .../test_change_around_word/91head.txt | 1 - .../test_change_around_word/92mode.txt | 1 - .../test_change_around_word/93text.txt | 11 - .../test_change_around_word/94head.txt | 1 - .../test_change_around_word/95mode.txt | 1 - .../test_change_around_word/96text.txt | 11 - .../test_change_around_word/97head.txt | 1 - .../test_change_around_word/98mode.txt | 1 - .../test_change_around_word/99text.txt | 12 - .../test_change_around_word/9text.txt | 12 - .../test_data/test_change_in_sentence.json | 1 + .../test_change_in_sentence/0text.txt | 1 - .../test_change_in_sentence/10head.txt | 1 - .../test_change_in_sentence/11mode.txt | 1 - .../test_change_in_sentence/12text.txt | 1 - .../test_change_in_sentence/13head.txt | 1 - .../test_change_in_sentence/14mode.txt | 1 - .../test_change_in_sentence/15text.txt | 1 - .../test_change_in_sentence/16head.txt | 1 - .../test_change_in_sentence/17mode.txt | 1 - .../test_change_in_sentence/18text.txt | 1 - .../test_change_in_sentence/19head.txt | 1 - .../test_change_in_sentence/1head.txt | 1 - .../test_change_in_sentence/20mode.txt | 1 - .../test_change_in_sentence/21text.txt | 1 - .../test_change_in_sentence/22head.txt | 1 - .../test_change_in_sentence/23mode.txt | 1 - .../test_change_in_sentence/24text.txt | 1 - .../test_change_in_sentence/25head.txt | 1 - .../test_change_in_sentence/26mode.txt | 1 - .../test_change_in_sentence/27text.txt | 1 - .../test_change_in_sentence/28head.txt | 1 - .../test_change_in_sentence/29mode.txt | 1 - .../test_change_in_sentence/2mode.txt | 1 - .../test_change_in_sentence/30text.txt | 1 - .../test_change_in_sentence/31head.txt | 1 - .../test_change_in_sentence/32mode.txt | 1 - .../test_change_in_sentence/33text.txt | 2 - .../test_change_in_sentence/34head.txt | 1 - .../test_change_in_sentence/35mode.txt | 1 - .../test_change_in_sentence/36text.txt | 2 - .../test_change_in_sentence/37head.txt | 1 - .../test_change_in_sentence/38mode.txt | 1 - .../test_change_in_sentence/39text.txt | 2 - .../test_change_in_sentence/3text.txt | 1 - .../test_change_in_sentence/40head.txt | 1 - .../test_change_in_sentence/41mode.txt | 1 - .../test_change_in_sentence/42text.txt | 2 - .../test_change_in_sentence/43head.txt | 1 - .../test_change_in_sentence/44mode.txt | 1 - .../test_change_in_sentence/45text.txt | 2 - .../test_change_in_sentence/46head.txt | 1 - .../test_change_in_sentence/47mode.txt | 1 - .../test_change_in_sentence/48text.txt | 4 - .../test_change_in_sentence/49head.txt | 1 - .../test_change_in_sentence/4head.txt | 1 - .../test_change_in_sentence/50mode.txt | 1 - .../test_change_in_sentence/51text.txt | 3 - .../test_change_in_sentence/52head.txt | 1 - .../test_change_in_sentence/53mode.txt | 1 - .../test_change_in_sentence/54text.txt | 3 - .../test_change_in_sentence/55head.txt | 1 - .../test_change_in_sentence/56mode.txt | 1 - .../test_change_in_sentence/5mode.txt | 1 - .../test_change_in_sentence/6text.txt | 1 - .../test_change_in_sentence/7head.txt | 1 - .../test_change_in_sentence/8mode.txt | 1 - .../test_change_in_sentence/9text.txt | 1 - crates/vim/test_data/test_change_in_word.json | 1 + .../test_data/test_change_in_word/0text.txt | 12 - .../test_data/test_change_in_word/100head.txt | 1 - .../test_data/test_change_in_word/101mode.txt | 1 - .../test_data/test_change_in_word/102text.txt | 12 - .../test_data/test_change_in_word/103head.txt | 1 - .../test_data/test_change_in_word/104mode.txt | 1 - .../test_data/test_change_in_word/105text.txt | 12 - .../test_data/test_change_in_word/106head.txt | 1 - .../test_data/test_change_in_word/107mode.txt | 1 - .../test_data/test_change_in_word/108text.txt | 12 - .../test_data/test_change_in_word/109head.txt | 1 - .../test_data/test_change_in_word/10head.txt | 1 - .../test_data/test_change_in_word/110mode.txt | 1 - .../test_data/test_change_in_word/111text.txt | 12 - .../test_data/test_change_in_word/112head.txt | 1 - .../test_data/test_change_in_word/113mode.txt | 1 - .../test_data/test_change_in_word/114text.txt | 12 - .../test_data/test_change_in_word/115head.txt | 1 - .../test_data/test_change_in_word/116mode.txt | 1 - .../test_data/test_change_in_word/117text.txt | 12 - .../test_data/test_change_in_word/118head.txt | 1 - .../test_data/test_change_in_word/119mode.txt | 1 - .../test_data/test_change_in_word/11mode.txt | 1 - .../test_data/test_change_in_word/120text.txt | 12 - .../test_data/test_change_in_word/121head.txt | 1 - .../test_data/test_change_in_word/122mode.txt | 1 - .../test_data/test_change_in_word/123text.txt | 12 - .../test_data/test_change_in_word/124head.txt | 1 - .../test_data/test_change_in_word/125mode.txt | 1 - .../test_data/test_change_in_word/126text.txt | 12 - .../test_data/test_change_in_word/127head.txt | 1 - .../test_data/test_change_in_word/128mode.txt | 1 - .../test_data/test_change_in_word/129text.txt | 12 - .../test_data/test_change_in_word/12text.txt | 12 - .../test_data/test_change_in_word/130head.txt | 1 - .../test_data/test_change_in_word/131mode.txt | 1 - .../test_data/test_change_in_word/132text.txt | 12 - .../test_data/test_change_in_word/133head.txt | 1 - .../test_data/test_change_in_word/134mode.txt | 1 - .../test_data/test_change_in_word/135text.txt | 12 - .../test_data/test_change_in_word/136head.txt | 1 - .../test_data/test_change_in_word/137mode.txt | 1 - .../test_data/test_change_in_word/13head.txt | 1 - .../test_data/test_change_in_word/14mode.txt | 1 - .../test_data/test_change_in_word/15text.txt | 12 - .../test_data/test_change_in_word/16head.txt | 1 - .../test_data/test_change_in_word/17mode.txt | 1 - .../test_data/test_change_in_word/18text.txt | 12 - .../test_data/test_change_in_word/19head.txt | 1 - .../test_data/test_change_in_word/1head.txt | 1 - .../test_data/test_change_in_word/20mode.txt | 1 - .../test_data/test_change_in_word/21text.txt | 12 - .../test_data/test_change_in_word/22head.txt | 1 - .../test_data/test_change_in_word/23mode.txt | 1 - .../test_data/test_change_in_word/24text.txt | 12 - .../test_data/test_change_in_word/25head.txt | 1 - .../test_data/test_change_in_word/26mode.txt | 1 - .../test_data/test_change_in_word/27text.txt | 12 - .../test_data/test_change_in_word/28head.txt | 1 - .../test_data/test_change_in_word/29mode.txt | 1 - .../test_data/test_change_in_word/2mode.txt | 1 - .../test_data/test_change_in_word/30text.txt | 12 - .../test_data/test_change_in_word/31head.txt | 1 - .../test_data/test_change_in_word/32mode.txt | 1 - .../test_data/test_change_in_word/33text.txt | 12 - .../test_data/test_change_in_word/34head.txt | 1 - .../test_data/test_change_in_word/35mode.txt | 1 - .../test_data/test_change_in_word/36text.txt | 12 - .../test_data/test_change_in_word/37head.txt | 1 - .../test_data/test_change_in_word/38mode.txt | 1 - .../test_data/test_change_in_word/39text.txt | 12 - .../test_data/test_change_in_word/3text.txt | 12 - .../test_data/test_change_in_word/40head.txt | 1 - .../test_data/test_change_in_word/41mode.txt | 1 - .../test_data/test_change_in_word/42text.txt | 12 - .../test_data/test_change_in_word/43head.txt | 1 - .../test_data/test_change_in_word/44mode.txt | 1 - .../test_data/test_change_in_word/45text.txt | 12 - .../test_data/test_change_in_word/46head.txt | 1 - .../test_data/test_change_in_word/47mode.txt | 1 - .../test_data/test_change_in_word/48text.txt | 12 - .../test_data/test_change_in_word/49head.txt | 1 - .../test_data/test_change_in_word/4head.txt | 1 - .../test_data/test_change_in_word/50mode.txt | 1 - .../test_data/test_change_in_word/51text.txt | 12 - .../test_data/test_change_in_word/52head.txt | 1 - .../test_data/test_change_in_word/53mode.txt | 1 - .../test_data/test_change_in_word/54text.txt | 12 - .../test_data/test_change_in_word/55head.txt | 1 - .../test_data/test_change_in_word/56mode.txt | 1 - .../test_data/test_change_in_word/57text.txt | 12 - .../test_data/test_change_in_word/58head.txt | 1 - .../test_data/test_change_in_word/59mode.txt | 1 - .../test_data/test_change_in_word/5mode.txt | 1 - .../test_data/test_change_in_word/60text.txt | 12 - .../test_data/test_change_in_word/61head.txt | 1 - .../test_data/test_change_in_word/62mode.txt | 1 - .../test_data/test_change_in_word/63text.txt | 12 - .../test_data/test_change_in_word/64head.txt | 1 - .../test_data/test_change_in_word/65mode.txt | 1 - .../test_data/test_change_in_word/66text.txt | 12 - .../test_data/test_change_in_word/67head.txt | 1 - .../test_data/test_change_in_word/68mode.txt | 1 - .../test_data/test_change_in_word/69text.txt | 12 - .../test_data/test_change_in_word/6text.txt | 12 - .../test_data/test_change_in_word/70head.txt | 1 - .../test_data/test_change_in_word/71mode.txt | 1 - .../test_data/test_change_in_word/72text.txt | 12 - .../test_data/test_change_in_word/73head.txt | 1 - .../test_data/test_change_in_word/74mode.txt | 1 - .../test_data/test_change_in_word/75text.txt | 12 - .../test_data/test_change_in_word/76head.txt | 1 - .../test_data/test_change_in_word/77mode.txt | 1 - .../test_data/test_change_in_word/78text.txt | 12 - .../test_data/test_change_in_word/79head.txt | 1 - .../test_data/test_change_in_word/7head.txt | 1 - .../test_data/test_change_in_word/80mode.txt | 1 - .../test_data/test_change_in_word/81text.txt | 12 - .../test_data/test_change_in_word/82head.txt | 1 - .../test_data/test_change_in_word/83mode.txt | 1 - .../test_data/test_change_in_word/84text.txt | 12 - .../test_data/test_change_in_word/85head.txt | 1 - .../test_data/test_change_in_word/86mode.txt | 1 - .../test_data/test_change_in_word/87text.txt | 12 - .../test_data/test_change_in_word/88head.txt | 1 - .../test_data/test_change_in_word/89mode.txt | 1 - .../test_data/test_change_in_word/8mode.txt | 1 - .../test_data/test_change_in_word/90text.txt | 12 - .../test_data/test_change_in_word/91head.txt | 1 - .../test_data/test_change_in_word/92mode.txt | 1 - .../test_data/test_change_in_word/93text.txt | 12 - .../test_data/test_change_in_word/94head.txt | 1 - .../test_data/test_change_in_word/95mode.txt | 1 - .../test_data/test_change_in_word/96text.txt | 12 - .../test_data/test_change_in_word/97head.txt | 1 - .../test_data/test_change_in_word/98mode.txt | 1 - .../test_data/test_change_in_word/99text.txt | 12 - .../test_data/test_change_in_word/9text.txt | 12 - .../test_delete_around_sentence.json | 1 + .../test_delete_around_sentence/0text.txt | 1 - .../test_delete_around_sentence/10head.txt | 1 - .../test_delete_around_sentence/1head.txt | 1 - .../test_delete_around_sentence/2mode.txt | 1 - .../test_delete_around_sentence/3text.txt | 1 - .../test_delete_around_sentence/4head.txt | 1 - .../test_delete_around_sentence/5mode.txt | 1 - .../test_delete_around_sentence/6text.txt | 1 - .../test_delete_around_sentence/7head.txt | 1 - .../test_delete_around_sentence/8mode.txt | 1 - .../test_delete_around_sentence/9text.txt | 1 - .../test_data/test_delete_around_word.json | 1 + .../test_delete_around_word/0text.txt | 12 - .../test_delete_around_word/100head.txt | 1 - .../test_delete_around_word/101mode.txt | 1 - .../test_delete_around_word/102text.txt | 12 - .../test_delete_around_word/103head.txt | 1 - .../test_delete_around_word/104mode.txt | 1 - .../test_delete_around_word/105text.txt | 12 - .../test_delete_around_word/106head.txt | 1 - .../test_delete_around_word/107mode.txt | 1 - .../test_delete_around_word/108text.txt | 12 - .../test_delete_around_word/109head.txt | 1 - .../test_delete_around_word/10head.txt | 1 - .../test_delete_around_word/110mode.txt | 1 - .../test_delete_around_word/111text.txt | 12 - .../test_delete_around_word/112head.txt | 1 - .../test_delete_around_word/113mode.txt | 1 - .../test_delete_around_word/114text.txt | 12 - .../test_delete_around_word/115head.txt | 1 - .../test_delete_around_word/116mode.txt | 1 - .../test_delete_around_word/117text.txt | 9 - .../test_delete_around_word/118head.txt | 1 - .../test_delete_around_word/119mode.txt | 1 - .../test_delete_around_word/11mode.txt | 1 - .../test_delete_around_word/120text.txt | 10 - .../test_delete_around_word/121head.txt | 1 - .../test_delete_around_word/122mode.txt | 1 - .../test_delete_around_word/123text.txt | 11 - .../test_delete_around_word/124head.txt | 1 - .../test_delete_around_word/125mode.txt | 1 - .../test_delete_around_word/126text.txt | 12 - .../test_delete_around_word/127head.txt | 1 - .../test_delete_around_word/128mode.txt | 1 - .../test_delete_around_word/129text.txt | 12 - .../test_delete_around_word/12text.txt | 12 - .../test_delete_around_word/130head.txt | 1 - .../test_delete_around_word/131mode.txt | 1 - .../test_delete_around_word/132text.txt | 11 - .../test_delete_around_word/133head.txt | 1 - .../test_delete_around_word/134mode.txt | 1 - .../test_delete_around_word/135text.txt | 11 - .../test_delete_around_word/136head.txt | 1 - .../test_delete_around_word/137mode.txt | 1 - .../test_delete_around_word/13head.txt | 1 - .../test_delete_around_word/14mode.txt | 1 - .../test_delete_around_word/15text.txt | 12 - .../test_delete_around_word/16head.txt | 1 - .../test_delete_around_word/17mode.txt | 1 - .../test_delete_around_word/18text.txt | 11 - .../test_delete_around_word/19head.txt | 1 - .../test_delete_around_word/1head.txt | 1 - .../test_delete_around_word/20mode.txt | 1 - .../test_delete_around_word/21text.txt | 10 - .../test_delete_around_word/22head.txt | 1 - .../test_delete_around_word/23mode.txt | 1 - .../test_delete_around_word/24text.txt | 10 - .../test_delete_around_word/25head.txt | 1 - .../test_delete_around_word/26mode.txt | 1 - .../test_delete_around_word/27text.txt | 11 - .../test_delete_around_word/28head.txt | 1 - .../test_delete_around_word/29mode.txt | 1 - .../test_delete_around_word/2mode.txt | 1 - .../test_delete_around_word/30text.txt | 12 - .../test_delete_around_word/31head.txt | 1 - .../test_delete_around_word/32mode.txt | 1 - .../test_delete_around_word/33text.txt | 12 - .../test_delete_around_word/34head.txt | 1 - .../test_delete_around_word/35mode.txt | 1 - .../test_delete_around_word/36text.txt | 12 - .../test_delete_around_word/37head.txt | 1 - .../test_delete_around_word/38mode.txt | 1 - .../test_delete_around_word/39text.txt | 12 - .../test_delete_around_word/3text.txt | 12 - .../test_delete_around_word/40head.txt | 1 - .../test_delete_around_word/41mode.txt | 1 - .../test_delete_around_word/42text.txt | 12 - .../test_delete_around_word/43head.txt | 1 - .../test_delete_around_word/44mode.txt | 1 - .../test_delete_around_word/45text.txt | 12 - .../test_delete_around_word/46head.txt | 1 - .../test_delete_around_word/47mode.txt | 1 - .../test_delete_around_word/48text.txt | 9 - .../test_delete_around_word/49head.txt | 1 - .../test_delete_around_word/4head.txt | 1 - .../test_delete_around_word/50mode.txt | 1 - .../test_delete_around_word/51text.txt | 10 - .../test_delete_around_word/52head.txt | 1 - .../test_delete_around_word/53mode.txt | 1 - .../test_delete_around_word/54text.txt | 11 - .../test_delete_around_word/55head.txt | 1 - .../test_delete_around_word/56mode.txt | 1 - .../test_delete_around_word/57text.txt | 12 - .../test_delete_around_word/58head.txt | 1 - .../test_delete_around_word/59mode.txt | 1 - .../test_delete_around_word/5mode.txt | 1 - .../test_delete_around_word/60text.txt | 12 - .../test_delete_around_word/61head.txt | 1 - .../test_delete_around_word/62mode.txt | 1 - .../test_delete_around_word/63text.txt | 11 - .../test_delete_around_word/64head.txt | 1 - .../test_delete_around_word/65mode.txt | 1 - .../test_delete_around_word/66text.txt | 11 - .../test_delete_around_word/67head.txt | 1 - .../test_delete_around_word/68mode.txt | 1 - .../test_delete_around_word/69text.txt | 12 - .../test_delete_around_word/6text.txt | 11 - .../test_delete_around_word/70head.txt | 1 - .../test_delete_around_word/71mode.txt | 1 - .../test_delete_around_word/72text.txt | 12 - .../test_delete_around_word/73head.txt | 1 - .../test_delete_around_word/74mode.txt | 1 - .../test_delete_around_word/75text.txt | 11 - .../test_delete_around_word/76head.txt | 1 - .../test_delete_around_word/77mode.txt | 1 - .../test_delete_around_word/78text.txt | 12 - .../test_delete_around_word/79head.txt | 1 - .../test_delete_around_word/7head.txt | 1 - .../test_delete_around_word/80mode.txt | 1 - .../test_delete_around_word/81text.txt | 12 - .../test_delete_around_word/82head.txt | 1 - .../test_delete_around_word/83mode.txt | 1 - .../test_delete_around_word/84text.txt | 12 - .../test_delete_around_word/85head.txt | 1 - .../test_delete_around_word/86mode.txt | 1 - .../test_delete_around_word/87text.txt | 11 - .../test_delete_around_word/88head.txt | 1 - .../test_delete_around_word/89mode.txt | 1 - .../test_delete_around_word/8mode.txt | 1 - .../test_delete_around_word/90text.txt | 10 - .../test_delete_around_word/91head.txt | 1 - .../test_delete_around_word/92mode.txt | 1 - .../test_delete_around_word/93text.txt | 10 - .../test_delete_around_word/94head.txt | 1 - .../test_delete_around_word/95mode.txt | 1 - .../test_delete_around_word/96text.txt | 11 - .../test_delete_around_word/97head.txt | 1 - .../test_delete_around_word/98mode.txt | 1 - .../test_delete_around_word/99text.txt | 12 - .../test_delete_around_word/9text.txt | 12 - .../test_data/test_delete_in_sentence.json | 1 + .../test_delete_in_sentence/0text.txt | 1 - .../test_delete_in_sentence/10head.txt | 1 - .../test_delete_in_sentence/11mode.txt | 1 - .../test_delete_in_sentence/12text.txt | 1 - .../test_delete_in_sentence/13head.txt | 1 - .../test_delete_in_sentence/14mode.txt | 1 - .../test_delete_in_sentence/15text.txt | 1 - .../test_delete_in_sentence/16head.txt | 1 - .../test_delete_in_sentence/17mode.txt | 1 - .../test_delete_in_sentence/18text.txt | 1 - .../test_delete_in_sentence/19head.txt | 1 - .../test_delete_in_sentence/1head.txt | 1 - .../test_delete_in_sentence/20mode.txt | 1 - .../test_delete_in_sentence/21text.txt | 1 - .../test_delete_in_sentence/22head.txt | 1 - .../test_delete_in_sentence/23mode.txt | 1 - .../test_delete_in_sentence/24text.txt | 1 - .../test_delete_in_sentence/25head.txt | 1 - .../test_delete_in_sentence/26mode.txt | 1 - .../test_delete_in_sentence/27text.txt | 1 - .../test_delete_in_sentence/28head.txt | 1 - .../test_delete_in_sentence/29mode.txt | 1 - .../test_delete_in_sentence/2mode.txt | 1 - .../test_delete_in_sentence/30text.txt | 1 - .../test_delete_in_sentence/31head.txt | 1 - .../test_delete_in_sentence/32mode.txt | 1 - .../test_delete_in_sentence/33text.txt | 2 - .../test_delete_in_sentence/34head.txt | 1 - .../test_delete_in_sentence/35mode.txt | 1 - .../test_delete_in_sentence/36text.txt | 2 - .../test_delete_in_sentence/37head.txt | 1 - .../test_delete_in_sentence/38mode.txt | 1 - .../test_delete_in_sentence/39text.txt | 2 - .../test_delete_in_sentence/3text.txt | 1 - .../test_delete_in_sentence/40head.txt | 1 - .../test_delete_in_sentence/41mode.txt | 1 - .../test_delete_in_sentence/42text.txt | 2 - .../test_delete_in_sentence/43head.txt | 1 - .../test_delete_in_sentence/44mode.txt | 1 - .../test_delete_in_sentence/45text.txt | 2 - .../test_delete_in_sentence/46head.txt | 1 - .../test_delete_in_sentence/47mode.txt | 1 - .../test_delete_in_sentence/48text.txt | 4 - .../test_delete_in_sentence/49head.txt | 1 - .../test_delete_in_sentence/4head.txt | 1 - .../test_delete_in_sentence/50mode.txt | 1 - .../test_delete_in_sentence/51text.txt | 3 - .../test_delete_in_sentence/52head.txt | 1 - .../test_delete_in_sentence/53mode.txt | 1 - .../test_delete_in_sentence/54text.txt | 3 - .../test_delete_in_sentence/55head.txt | 1 - .../test_delete_in_sentence/56mode.txt | 1 - .../test_delete_in_sentence/5mode.txt | 1 - .../test_delete_in_sentence/6text.txt | 1 - .../test_delete_in_sentence/7head.txt | 1 - .../test_delete_in_sentence/8mode.txt | 1 - .../test_delete_in_sentence/9text.txt | 1 - crates/vim/test_data/test_delete_in_word.json | 1 + .../test_data/test_delete_in_word/0text.txt | 12 - .../test_data/test_delete_in_word/100head.txt | 1 - .../test_data/test_delete_in_word/101mode.txt | 1 - .../test_data/test_delete_in_word/102text.txt | 12 - .../test_data/test_delete_in_word/103head.txt | 1 - .../test_data/test_delete_in_word/104mode.txt | 1 - .../test_data/test_delete_in_word/105text.txt | 12 - .../test_data/test_delete_in_word/106head.txt | 1 - .../test_data/test_delete_in_word/107mode.txt | 1 - .../test_data/test_delete_in_word/108text.txt | 12 - .../test_data/test_delete_in_word/109head.txt | 1 - .../test_data/test_delete_in_word/10head.txt | 1 - .../test_data/test_delete_in_word/110mode.txt | 1 - .../test_data/test_delete_in_word/111text.txt | 12 - .../test_data/test_delete_in_word/112head.txt | 1 - .../test_data/test_delete_in_word/113mode.txt | 1 - .../test_data/test_delete_in_word/114text.txt | 12 - .../test_data/test_delete_in_word/115head.txt | 1 - .../test_data/test_delete_in_word/116mode.txt | 1 - .../test_data/test_delete_in_word/117text.txt | 12 - .../test_data/test_delete_in_word/118head.txt | 1 - .../test_data/test_delete_in_word/119mode.txt | 1 - .../test_data/test_delete_in_word/11mode.txt | 1 - .../test_data/test_delete_in_word/120text.txt | 12 - .../test_data/test_delete_in_word/121head.txt | 1 - .../test_data/test_delete_in_word/122mode.txt | 1 - .../test_data/test_delete_in_word/123text.txt | 12 - .../test_data/test_delete_in_word/124head.txt | 1 - .../test_data/test_delete_in_word/125mode.txt | 1 - .../test_data/test_delete_in_word/126text.txt | 12 - .../test_data/test_delete_in_word/127head.txt | 1 - .../test_data/test_delete_in_word/128mode.txt | 1 - .../test_data/test_delete_in_word/129text.txt | 12 - .../test_data/test_delete_in_word/12text.txt | 12 - .../test_data/test_delete_in_word/130head.txt | 1 - .../test_data/test_delete_in_word/131mode.txt | 1 - .../test_data/test_delete_in_word/132text.txt | 12 - .../test_data/test_delete_in_word/133head.txt | 1 - .../test_data/test_delete_in_word/134mode.txt | 1 - .../test_data/test_delete_in_word/135text.txt | 12 - .../test_data/test_delete_in_word/136head.txt | 1 - .../test_data/test_delete_in_word/137mode.txt | 1 - .../test_data/test_delete_in_word/13head.txt | 1 - .../test_data/test_delete_in_word/14mode.txt | 1 - .../test_data/test_delete_in_word/15text.txt | 12 - .../test_data/test_delete_in_word/16head.txt | 1 - .../test_data/test_delete_in_word/17mode.txt | 1 - .../test_data/test_delete_in_word/18text.txt | 12 - .../test_data/test_delete_in_word/19head.txt | 1 - .../test_data/test_delete_in_word/1head.txt | 1 - .../test_data/test_delete_in_word/20mode.txt | 1 - .../test_data/test_delete_in_word/21text.txt | 12 - .../test_data/test_delete_in_word/22head.txt | 1 - .../test_data/test_delete_in_word/23mode.txt | 1 - .../test_data/test_delete_in_word/24text.txt | 12 - .../test_data/test_delete_in_word/25head.txt | 1 - .../test_data/test_delete_in_word/26mode.txt | 1 - .../test_data/test_delete_in_word/27text.txt | 12 - .../test_data/test_delete_in_word/28head.txt | 1 - .../test_data/test_delete_in_word/29mode.txt | 1 - .../test_data/test_delete_in_word/2mode.txt | 1 - .../test_data/test_delete_in_word/30text.txt | 12 - .../test_data/test_delete_in_word/31head.txt | 1 - .../test_data/test_delete_in_word/32mode.txt | 1 - .../test_data/test_delete_in_word/33text.txt | 12 - .../test_data/test_delete_in_word/34head.txt | 1 - .../test_data/test_delete_in_word/35mode.txt | 1 - .../test_data/test_delete_in_word/36text.txt | 12 - .../test_data/test_delete_in_word/37head.txt | 1 - .../test_data/test_delete_in_word/38mode.txt | 1 - .../test_data/test_delete_in_word/39text.txt | 12 - .../test_data/test_delete_in_word/3text.txt | 12 - .../test_data/test_delete_in_word/40head.txt | 1 - .../test_data/test_delete_in_word/41mode.txt | 1 - .../test_data/test_delete_in_word/42text.txt | 12 - .../test_data/test_delete_in_word/43head.txt | 1 - .../test_data/test_delete_in_word/44mode.txt | 1 - .../test_data/test_delete_in_word/45text.txt | 12 - .../test_data/test_delete_in_word/46head.txt | 1 - .../test_data/test_delete_in_word/47mode.txt | 1 - .../test_data/test_delete_in_word/48text.txt | 12 - .../test_data/test_delete_in_word/49head.txt | 1 - .../test_data/test_delete_in_word/4head.txt | 1 - .../test_data/test_delete_in_word/50mode.txt | 1 - .../test_data/test_delete_in_word/51text.txt | 12 - .../test_data/test_delete_in_word/52head.txt | 1 - .../test_data/test_delete_in_word/53mode.txt | 1 - .../test_data/test_delete_in_word/54text.txt | 12 - .../test_data/test_delete_in_word/55head.txt | 1 - .../test_data/test_delete_in_word/56mode.txt | 1 - .../test_data/test_delete_in_word/57text.txt | 12 - .../test_data/test_delete_in_word/58head.txt | 1 - .../test_data/test_delete_in_word/59mode.txt | 1 - .../test_data/test_delete_in_word/5mode.txt | 1 - .../test_data/test_delete_in_word/60text.txt | 12 - .../test_data/test_delete_in_word/61head.txt | 1 - .../test_data/test_delete_in_word/62mode.txt | 1 - .../test_data/test_delete_in_word/63text.txt | 12 - .../test_data/test_delete_in_word/64head.txt | 1 - .../test_data/test_delete_in_word/65mode.txt | 1 - .../test_data/test_delete_in_word/66text.txt | 12 - .../test_data/test_delete_in_word/67head.txt | 1 - .../test_data/test_delete_in_word/68mode.txt | 1 - .../test_data/test_delete_in_word/69text.txt | 12 - .../test_data/test_delete_in_word/6text.txt | 12 - .../test_data/test_delete_in_word/70head.txt | 1 - .../test_data/test_delete_in_word/71mode.txt | 1 - .../test_data/test_delete_in_word/72text.txt | 12 - .../test_data/test_delete_in_word/73head.txt | 1 - .../test_data/test_delete_in_word/74mode.txt | 1 - .../test_data/test_delete_in_word/75text.txt | 12 - .../test_data/test_delete_in_word/76head.txt | 1 - .../test_data/test_delete_in_word/77mode.txt | 1 - .../test_data/test_delete_in_word/78text.txt | 12 - .../test_data/test_delete_in_word/79head.txt | 1 - .../test_data/test_delete_in_word/7head.txt | 1 - .../test_data/test_delete_in_word/80mode.txt | 1 - .../test_data/test_delete_in_word/81text.txt | 12 - .../test_data/test_delete_in_word/82head.txt | 1 - .../test_data/test_delete_in_word/83mode.txt | 1 - .../test_data/test_delete_in_word/84text.txt | 12 - .../test_data/test_delete_in_word/85head.txt | 1 - .../test_data/test_delete_in_word/86mode.txt | 1 - .../test_data/test_delete_in_word/87text.txt | 12 - .../test_data/test_delete_in_word/88head.txt | 1 - .../test_data/test_delete_in_word/89mode.txt | 1 - .../test_data/test_delete_in_word/8mode.txt | 1 - .../test_data/test_delete_in_word/90text.txt | 12 - .../test_data/test_delete_in_word/91head.txt | 1 - .../test_data/test_delete_in_word/92mode.txt | 1 - .../test_data/test_delete_in_word/93text.txt | 12 - .../test_data/test_delete_in_word/94head.txt | 1 - .../test_data/test_delete_in_word/95mode.txt | 1 - .../test_data/test_delete_in_word/96text.txt | 12 - .../test_data/test_delete_in_word/97head.txt | 1 - .../test_data/test_delete_in_word/98mode.txt | 1 - .../test_data/test_delete_in_word/99text.txt | 12 - .../test_data/test_delete_in_word/9text.txt | 12 - crates/vim/test_data/test_e.json | 1 + crates/vim/test_data/test_gg.json | 1 + crates/vim/test_data/test_h.json | 1 + .../test_data/test_insert_end_of_line.json | 1 + .../vim/test_data/test_insert_line_above.json | 1 + crates/vim/test_data/test_j.json | 1 + crates/vim/test_data/test_jump_to_end.json | 1 + .../test_jump_to_line_boundaries.json | 1 + crates/vim/test_data/test_k.json | 1 + crates/vim/test_data/test_l.json | 1 + crates/vim/test_data/test_neovim.json | 1 + crates/vim/test_data/test_neovim/0text.txt | 1 - crates/vim/test_data/test_neovim/1head.txt | 1 - crates/vim/test_data/test_neovim/2mode.txt | 1 - crates/vim/test_data/test_repeated_cb.json | 1 + crates/vim/test_data/test_repeated_ce.json | 1 + crates/vim/test_data/test_repeated_cj.json | 1 + crates/vim/test_data/test_repeated_cl.json | 1 + crates/vim/test_data/test_repeated_word.json | 1 + crates/vim/test_data/test_w.json | 1 + .../test_data/test_word_text_object/0text.txt | 9 - .../test_word_text_object/100head.txt | 1 - .../test_word_text_object/101mode.txt | 1 - .../test_word_text_object/102text.txt | 9 - .../test_word_text_object/103head.txt | 1 - .../test_word_text_object/104mode.txt | 1 - .../test_word_text_object/105text.txt | 9 - .../test_word_text_object/106head.txt | 1 - .../test_word_text_object/107mode.txt | 1 - .../test_word_text_object/108text.txt | 9 - .../test_word_text_object/109head.txt | 1 - .../test_word_text_object/10head.txt | 1 - .../test_word_text_object/110mode.txt | 1 - .../test_word_text_object/111text.txt | 9 - .../test_word_text_object/112head.txt | 1 - .../test_word_text_object/113mode.txt | 1 - .../test_word_text_object/114text.txt | 9 - .../test_word_text_object/115head.txt | 1 - .../test_word_text_object/116mode.txt | 1 - .../test_word_text_object/117text.txt | 9 - .../test_word_text_object/118head.txt | 1 - .../test_word_text_object/119mode.txt | 1 - .../test_word_text_object/11mode.txt | 1 - .../test_word_text_object/120text.txt | 8 - .../test_word_text_object/121head.txt | 1 - .../test_word_text_object/122mode.txt | 1 - .../test_word_text_object/123text.txt | 9 - .../test_word_text_object/124head.txt | 1 - .../test_word_text_object/125mode.txt | 1 - .../test_word_text_object/126text.txt | 9 - .../test_word_text_object/127head.txt | 1 - .../test_word_text_object/128mode.txt | 1 - .../test_word_text_object/129text.txt | 9 - .../test_word_text_object/12text.txt | 9 - .../test_word_text_object/130head.txt | 1 - .../test_word_text_object/131mode.txt | 1 - .../test_word_text_object/132text.txt | 8 - .../test_word_text_object/133head.txt | 1 - .../test_word_text_object/134mode.txt | 1 - .../test_word_text_object/135text.txt | 8 - .../test_word_text_object/136head.txt | 1 - .../test_word_text_object/137mode.txt | 1 - .../test_word_text_object/138text.txt | 8 - .../test_word_text_object/139head.txt | 1 - .../test_word_text_object/13head.txt | 1 - .../test_word_text_object/140mode.txt | 1 - .../test_word_text_object/141text.txt | 8 - .../test_word_text_object/142head.txt | 1 - .../test_word_text_object/143mode.txt | 1 - .../test_word_text_object/144text.txt | 9 - .../test_word_text_object/145head.txt | 1 - .../test_word_text_object/146mode.txt | 1 - .../test_word_text_object/147text.txt | 9 - .../test_word_text_object/148head.txt | 1 - .../test_word_text_object/149mode.txt | 1 - .../test_word_text_object/14mode.txt | 1 - .../test_word_text_object/150text.txt | 9 - .../test_word_text_object/151head.txt | 1 - .../test_word_text_object/152mode.txt | 1 - .../test_word_text_object/153text.txt | 9 - .../test_word_text_object/154head.txt | 1 - .../test_word_text_object/155mode.txt | 1 - .../test_word_text_object/156text.txt | 9 - .../test_word_text_object/157head.txt | 1 - .../test_word_text_object/158mode.txt | 1 - .../test_word_text_object/159text.txt | 9 - .../test_word_text_object/15text.txt | 9 - .../test_word_text_object/160head.txt | 1 - .../test_word_text_object/161mode.txt | 1 - .../test_word_text_object/162text.txt | 8 - .../test_word_text_object/163head.txt | 1 - .../test_word_text_object/164mode.txt | 1 - .../test_word_text_object/165text.txt | 9 - .../test_word_text_object/166head.txt | 1 - .../test_word_text_object/167mode.txt | 1 - .../test_word_text_object/168text.txt | 9 - .../test_word_text_object/169head.txt | 1 - .../test_word_text_object/16head.txt | 1 - .../test_word_text_object/170mode.txt | 1 - .../test_word_text_object/171text.txt | 9 - .../test_word_text_object/172head.txt | 1 - .../test_word_text_object/173mode.txt | 1 - .../test_word_text_object/174text.txt | 9 - .../test_word_text_object/175head.txt | 1 - .../test_word_text_object/176mode.txt | 1 - .../test_word_text_object/177text.txt | 8 - .../test_word_text_object/178head.txt | 1 - .../test_word_text_object/179mode.txt | 1 - .../test_word_text_object/17mode.txt | 1 - .../test_word_text_object/180text.txt | 9 - .../test_word_text_object/181head.txt | 1 - .../test_word_text_object/182mode.txt | 1 - .../test_word_text_object/183text.txt | 9 - .../test_word_text_object/184head.txt | 1 - .../test_word_text_object/185mode.txt | 1 - .../test_word_text_object/186text.txt | 9 - .../test_word_text_object/187head.txt | 1 - .../test_word_text_object/188mode.txt | 1 - .../test_word_text_object/189text.txt | 8 - .../test_word_text_object/18text.txt | 9 - .../test_word_text_object/190head.txt | 1 - .../test_word_text_object/191mode.txt | 1 - .../test_word_text_object/192text.txt | 8 - .../test_word_text_object/193head.txt | 1 - .../test_word_text_object/194mode.txt | 1 - .../test_word_text_object/195text.txt | 8 - .../test_word_text_object/196head.txt | 1 - .../test_word_text_object/197mode.txt | 1 - .../test_word_text_object/198text.txt | 8 - .../test_word_text_object/199head.txt | 1 - .../test_word_text_object/19head.txt | 1 - .../test_data/test_word_text_object/1head.txt | 1 - .../test_word_text_object/200mode.txt | 1 - .../test_word_text_object/201text.txt | 9 - .../test_word_text_object/202head.txt | 1 - .../test_word_text_object/203mode.txt | 1 - .../test_word_text_object/204text.txt | 9 - .../test_word_text_object/205head.txt | 1 - .../test_word_text_object/206mode.txt | 1 - .../test_word_text_object/207text.txt | 9 - .../test_word_text_object/208head.txt | 1 - .../test_word_text_object/209mode.txt | 1 - .../test_word_text_object/20mode.txt | 1 - .../test_word_text_object/210text.txt | 9 - .../test_word_text_object/211head.txt | 1 - .../test_word_text_object/212mode.txt | 1 - .../test_word_text_object/213text.txt | 9 - .../test_word_text_object/214head.txt | 1 - .../test_word_text_object/215mode.txt | 1 - .../test_word_text_object/216text.txt | 9 - .../test_word_text_object/217head.txt | 1 - .../test_word_text_object/218mode.txt | 1 - .../test_word_text_object/219text.txt | 8 - .../test_word_text_object/21text.txt | 9 - .../test_word_text_object/220head.txt | 1 - .../test_word_text_object/221mode.txt | 1 - .../test_word_text_object/222text.txt | 9 - .../test_word_text_object/223head.txt | 1 - .../test_word_text_object/224mode.txt | 1 - .../test_word_text_object/225text.txt | 9 - .../test_word_text_object/226head.txt | 1 - .../test_word_text_object/227mode.txt | 1 - .../test_word_text_object/228text.txt | 9 - .../test_word_text_object/229head.txt | 1 - .../test_word_text_object/22head.txt | 1 - .../test_word_text_object/230mode.txt | 1 - .../test_word_text_object/231text.txt | 9 - .../test_word_text_object/23mode.txt | 1 - .../test_word_text_object/24text.txt | 9 - .../test_word_text_object/25head.txt | 1 - .../test_word_text_object/26mode.txt | 1 - .../test_word_text_object/27text.txt | 9 - .../test_word_text_object/28head.txt | 1 - .../test_word_text_object/29mode.txt | 1 - .../test_data/test_word_text_object/2mode.txt | 1 - .../test_word_text_object/30text.txt | 9 - .../test_word_text_object/31head.txt | 1 - .../test_word_text_object/32mode.txt | 1 - .../test_word_text_object/33text.txt | 9 - .../test_word_text_object/34head.txt | 1 - .../test_word_text_object/35mode.txt | 1 - .../test_word_text_object/36text.txt | 9 - .../test_word_text_object/37head.txt | 1 - .../test_word_text_object/38mode.txt | 1 - .../test_word_text_object/39text.txt | 9 - .../test_data/test_word_text_object/3text.txt | 9 - .../test_word_text_object/40head.txt | 1 - .../test_word_text_object/41mode.txt | 1 - .../test_word_text_object/42text.txt | 9 - .../test_word_text_object/43head.txt | 1 - .../test_word_text_object/44mode.txt | 1 - .../test_word_text_object/45text.txt | 9 - .../test_word_text_object/46head.txt | 1 - .../test_word_text_object/47mode.txt | 1 - .../test_word_text_object/48text.txt | 9 - .../test_word_text_object/49head.txt | 1 - .../test_data/test_word_text_object/4head.txt | 1 - .../test_word_text_object/50mode.txt | 1 - .../test_word_text_object/51text.txt | 9 - .../test_word_text_object/52head.txt | 1 - .../test_word_text_object/53mode.txt | 1 - .../test_word_text_object/54text.txt | 9 - .../test_word_text_object/55head.txt | 1 - .../test_word_text_object/56mode.txt | 1 - .../test_word_text_object/57text.txt | 9 - .../test_word_text_object/58head.txt | 1 - .../test_word_text_object/59mode.txt | 1 - .../test_data/test_word_text_object/5mode.txt | 1 - .../test_word_text_object/60text.txt | 9 - .../test_word_text_object/61head.txt | 1 - .../test_word_text_object/62mode.txt | 1 - .../test_word_text_object/63text.txt | 9 - .../test_word_text_object/64head.txt | 1 - .../test_word_text_object/65mode.txt | 1 - .../test_word_text_object/66text.txt | 9 - .../test_word_text_object/67head.txt | 1 - .../test_word_text_object/68mode.txt | 1 - .../test_word_text_object/69text.txt | 9 - .../test_data/test_word_text_object/6text.txt | 9 - .../test_word_text_object/70head.txt | 1 - .../test_word_text_object/71mode.txt | 1 - .../test_word_text_object/72text.txt | 9 - .../test_word_text_object/73head.txt | 1 - .../test_word_text_object/74mode.txt | 1 - .../test_word_text_object/75text.txt | 9 - .../test_word_text_object/76head.txt | 1 - .../test_word_text_object/77mode.txt | 1 - .../test_word_text_object/78text.txt | 9 - .../test_word_text_object/79head.txt | 1 - .../test_data/test_word_text_object/7head.txt | 1 - .../test_word_text_object/80mode.txt | 1 - .../test_word_text_object/81text.txt | 9 - .../test_word_text_object/82head.txt | 1 - .../test_word_text_object/83mode.txt | 1 - .../test_word_text_object/84text.txt | 9 - .../test_word_text_object/85head.txt | 1 - .../test_word_text_object/86mode.txt | 1 - .../test_word_text_object/87text.txt | 9 - .../test_word_text_object/88head.txt | 1 - .../test_word_text_object/89mode.txt | 1 - .../test_data/test_word_text_object/8mode.txt | 1 - .../test_word_text_object/90text.txt | 9 - .../test_word_text_object/91head.txt | 1 - .../test_word_text_object/92mode.txt | 1 - .../test_word_text_object/93text.txt | 9 - .../test_word_text_object/94head.txt | 1 - .../test_word_text_object/95mode.txt | 1 - .../test_word_text_object/96text.txt | 9 - .../test_word_text_object/97head.txt | 1 - .../test_word_text_object/98mode.txt | 1 - .../test_word_text_object/99text.txt | 9 - .../test_data/test_word_text_object/9text.txt | 9 - 971 files changed, 838 insertions(+), 11898 deletions(-) delete mode 100644 Cargo.lock create mode 100644 crates/vim/test_data/neovim_backed_test_context_works.json create mode 100644 crates/vim/test_data/test_a.json create mode 100644 crates/vim/test_data/test_backspace.json create mode 100644 crates/vim/test_data/test_change_around_sentence.json delete mode 100644 crates/vim/test_data/test_change_around_sentence/0text.txt delete mode 100644 crates/vim/test_data/test_change_around_sentence/10head.txt delete mode 100644 crates/vim/test_data/test_change_around_sentence/1head.txt delete mode 100644 crates/vim/test_data/test_change_around_sentence/2mode.txt delete mode 100644 crates/vim/test_data/test_change_around_sentence/3text.txt delete mode 100644 crates/vim/test_data/test_change_around_sentence/4head.txt delete mode 100644 crates/vim/test_data/test_change_around_sentence/5mode.txt delete mode 100644 crates/vim/test_data/test_change_around_sentence/6text.txt delete mode 100644 crates/vim/test_data/test_change_around_sentence/7head.txt delete mode 100644 crates/vim/test_data/test_change_around_sentence/8mode.txt delete mode 100644 crates/vim/test_data/test_change_around_sentence/9text.txt create mode 100644 crates/vim/test_data/test_change_around_word.json delete mode 100644 crates/vim/test_data/test_change_around_word/0text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/100head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/101mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/102text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/103head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/104mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/105text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/106head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/107mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/108text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/109head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/10head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/110mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/111text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/112head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/113mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/114text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/115head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/116mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/117text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/118head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/119mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/11mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/120text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/121head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/122mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/123text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/124head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/125mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/126text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/127head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/128mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/129text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/12text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/130head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/131mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/132text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/133head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/134mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/135text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/136head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/137mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/13head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/14mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/15text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/16head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/17mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/18text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/19head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/1head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/20mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/21text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/22head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/23mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/24text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/25head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/26mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/27text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/28head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/29mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/2mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/30text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/31head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/32mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/33text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/34head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/35mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/36text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/37head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/38mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/39text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/3text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/40head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/41mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/42text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/43head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/44mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/45text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/46head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/47mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/48text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/49head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/4head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/50mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/51text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/52head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/53mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/54text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/55head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/56mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/57text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/58head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/59mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/5mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/60text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/61head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/62mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/63text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/64head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/65mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/66text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/67head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/68mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/69text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/6text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/70head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/71mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/72text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/73head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/74mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/75text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/76head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/77mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/78text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/79head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/7head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/80mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/81text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/82head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/83mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/84text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/85head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/86mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/87text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/88head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/89mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/8mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/90text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/91head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/92mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/93text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/94head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/95mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/96text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/97head.txt delete mode 100644 crates/vim/test_data/test_change_around_word/98mode.txt delete mode 100644 crates/vim/test_data/test_change_around_word/99text.txt delete mode 100644 crates/vim/test_data/test_change_around_word/9text.txt create mode 100644 crates/vim/test_data/test_change_in_sentence.json delete mode 100644 crates/vim/test_data/test_change_in_sentence/0text.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/10head.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/11mode.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/12text.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/13head.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/14mode.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/15text.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/16head.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/17mode.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/18text.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/19head.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/1head.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/20mode.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/21text.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/22head.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/23mode.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/24text.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/25head.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/26mode.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/27text.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/28head.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/29mode.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/2mode.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/30text.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/31head.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/32mode.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/33text.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/34head.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/35mode.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/36text.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/37head.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/38mode.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/39text.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/3text.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/40head.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/41mode.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/42text.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/43head.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/44mode.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/45text.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/46head.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/47mode.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/48text.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/49head.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/4head.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/50mode.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/51text.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/52head.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/53mode.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/54text.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/55head.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/56mode.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/5mode.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/6text.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/7head.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/8mode.txt delete mode 100644 crates/vim/test_data/test_change_in_sentence/9text.txt create mode 100644 crates/vim/test_data/test_change_in_word.json delete mode 100644 crates/vim/test_data/test_change_in_word/0text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/100head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/101mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/102text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/103head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/104mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/105text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/106head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/107mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/108text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/109head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/10head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/110mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/111text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/112head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/113mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/114text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/115head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/116mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/117text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/118head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/119mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/11mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/120text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/121head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/122mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/123text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/124head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/125mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/126text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/127head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/128mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/129text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/12text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/130head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/131mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/132text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/133head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/134mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/135text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/136head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/137mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/13head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/14mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/15text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/16head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/17mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/18text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/19head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/1head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/20mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/21text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/22head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/23mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/24text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/25head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/26mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/27text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/28head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/29mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/2mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/30text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/31head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/32mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/33text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/34head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/35mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/36text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/37head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/38mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/39text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/3text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/40head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/41mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/42text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/43head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/44mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/45text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/46head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/47mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/48text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/49head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/4head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/50mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/51text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/52head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/53mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/54text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/55head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/56mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/57text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/58head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/59mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/5mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/60text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/61head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/62mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/63text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/64head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/65mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/66text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/67head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/68mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/69text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/6text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/70head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/71mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/72text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/73head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/74mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/75text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/76head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/77mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/78text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/79head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/7head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/80mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/81text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/82head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/83mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/84text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/85head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/86mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/87text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/88head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/89mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/8mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/90text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/91head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/92mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/93text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/94head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/95mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/96text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/97head.txt delete mode 100644 crates/vim/test_data/test_change_in_word/98mode.txt delete mode 100644 crates/vim/test_data/test_change_in_word/99text.txt delete mode 100644 crates/vim/test_data/test_change_in_word/9text.txt create mode 100644 crates/vim/test_data/test_delete_around_sentence.json delete mode 100644 crates/vim/test_data/test_delete_around_sentence/0text.txt delete mode 100644 crates/vim/test_data/test_delete_around_sentence/10head.txt delete mode 100644 crates/vim/test_data/test_delete_around_sentence/1head.txt delete mode 100644 crates/vim/test_data/test_delete_around_sentence/2mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_sentence/3text.txt delete mode 100644 crates/vim/test_data/test_delete_around_sentence/4head.txt delete mode 100644 crates/vim/test_data/test_delete_around_sentence/5mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_sentence/6text.txt delete mode 100644 crates/vim/test_data/test_delete_around_sentence/7head.txt delete mode 100644 crates/vim/test_data/test_delete_around_sentence/8mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_sentence/9text.txt create mode 100644 crates/vim/test_data/test_delete_around_word.json delete mode 100644 crates/vim/test_data/test_delete_around_word/0text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/100head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/101mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/102text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/103head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/104mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/105text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/106head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/107mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/108text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/109head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/10head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/110mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/111text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/112head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/113mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/114text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/115head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/116mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/117text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/118head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/119mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/11mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/120text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/121head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/122mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/123text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/124head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/125mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/126text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/127head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/128mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/129text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/12text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/130head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/131mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/132text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/133head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/134mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/135text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/136head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/137mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/13head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/14mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/15text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/16head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/17mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/18text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/19head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/1head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/20mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/21text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/22head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/23mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/24text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/25head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/26mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/27text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/28head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/29mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/2mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/30text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/31head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/32mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/33text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/34head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/35mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/36text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/37head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/38mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/39text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/3text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/40head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/41mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/42text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/43head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/44mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/45text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/46head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/47mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/48text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/49head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/4head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/50mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/51text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/52head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/53mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/54text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/55head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/56mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/57text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/58head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/59mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/5mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/60text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/61head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/62mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/63text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/64head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/65mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/66text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/67head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/68mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/69text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/6text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/70head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/71mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/72text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/73head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/74mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/75text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/76head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/77mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/78text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/79head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/7head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/80mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/81text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/82head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/83mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/84text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/85head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/86mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/87text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/88head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/89mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/8mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/90text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/91head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/92mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/93text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/94head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/95mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/96text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/97head.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/98mode.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/99text.txt delete mode 100644 crates/vim/test_data/test_delete_around_word/9text.txt create mode 100644 crates/vim/test_data/test_delete_in_sentence.json delete mode 100644 crates/vim/test_data/test_delete_in_sentence/0text.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/10head.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/11mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/12text.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/13head.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/14mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/15text.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/16head.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/17mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/18text.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/19head.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/1head.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/20mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/21text.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/22head.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/23mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/24text.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/25head.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/26mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/27text.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/28head.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/29mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/2mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/30text.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/31head.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/32mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/33text.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/34head.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/35mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/36text.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/37head.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/38mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/39text.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/3text.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/40head.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/41mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/42text.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/43head.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/44mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/45text.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/46head.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/47mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/48text.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/49head.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/4head.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/50mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/51text.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/52head.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/53mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/54text.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/55head.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/56mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/5mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/6text.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/7head.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/8mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_sentence/9text.txt create mode 100644 crates/vim/test_data/test_delete_in_word.json delete mode 100644 crates/vim/test_data/test_delete_in_word/0text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/100head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/101mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/102text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/103head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/104mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/105text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/106head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/107mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/108text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/109head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/10head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/110mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/111text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/112head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/113mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/114text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/115head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/116mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/117text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/118head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/119mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/11mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/120text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/121head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/122mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/123text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/124head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/125mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/126text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/127head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/128mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/129text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/12text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/130head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/131mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/132text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/133head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/134mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/135text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/136head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/137mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/13head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/14mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/15text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/16head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/17mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/18text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/19head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/1head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/20mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/21text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/22head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/23mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/24text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/25head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/26mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/27text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/28head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/29mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/2mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/30text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/31head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/32mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/33text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/34head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/35mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/36text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/37head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/38mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/39text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/3text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/40head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/41mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/42text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/43head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/44mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/45text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/46head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/47mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/48text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/49head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/4head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/50mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/51text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/52head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/53mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/54text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/55head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/56mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/57text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/58head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/59mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/5mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/60text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/61head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/62mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/63text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/64head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/65mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/66text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/67head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/68mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/69text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/6text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/70head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/71mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/72text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/73head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/74mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/75text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/76head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/77mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/78text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/79head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/7head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/80mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/81text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/82head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/83mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/84text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/85head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/86mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/87text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/88head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/89mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/8mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/90text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/91head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/92mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/93text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/94head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/95mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/96text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/97head.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/98mode.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/99text.txt delete mode 100644 crates/vim/test_data/test_delete_in_word/9text.txt create mode 100644 crates/vim/test_data/test_e.json create mode 100644 crates/vim/test_data/test_gg.json create mode 100644 crates/vim/test_data/test_h.json create mode 100644 crates/vim/test_data/test_insert_end_of_line.json create mode 100644 crates/vim/test_data/test_insert_line_above.json create mode 100644 crates/vim/test_data/test_j.json create mode 100644 crates/vim/test_data/test_jump_to_end.json create mode 100644 crates/vim/test_data/test_jump_to_line_boundaries.json create mode 100644 crates/vim/test_data/test_k.json create mode 100644 crates/vim/test_data/test_l.json create mode 100644 crates/vim/test_data/test_neovim.json delete mode 100644 crates/vim/test_data/test_neovim/0text.txt delete mode 100644 crates/vim/test_data/test_neovim/1head.txt delete mode 100644 crates/vim/test_data/test_neovim/2mode.txt create mode 100644 crates/vim/test_data/test_repeated_cb.json create mode 100644 crates/vim/test_data/test_repeated_ce.json create mode 100644 crates/vim/test_data/test_repeated_cj.json create mode 100644 crates/vim/test_data/test_repeated_cl.json create mode 100644 crates/vim/test_data/test_repeated_word.json create mode 100644 crates/vim/test_data/test_w.json delete mode 100644 crates/vim/test_data/test_word_text_object/0text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/100head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/101mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/102text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/103head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/104mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/105text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/106head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/107mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/108text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/109head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/10head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/110mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/111text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/112head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/113mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/114text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/115head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/116mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/117text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/118head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/119mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/11mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/120text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/121head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/122mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/123text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/124head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/125mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/126text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/127head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/128mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/129text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/12text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/130head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/131mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/132text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/133head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/134mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/135text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/136head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/137mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/138text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/139head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/13head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/140mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/141text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/142head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/143mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/144text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/145head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/146mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/147text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/148head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/149mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/14mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/150text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/151head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/152mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/153text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/154head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/155mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/156text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/157head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/158mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/159text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/15text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/160head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/161mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/162text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/163head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/164mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/165text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/166head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/167mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/168text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/169head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/16head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/170mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/171text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/172head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/173mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/174text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/175head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/176mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/177text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/178head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/179mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/17mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/180text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/181head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/182mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/183text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/184head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/185mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/186text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/187head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/188mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/189text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/18text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/190head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/191mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/192text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/193head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/194mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/195text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/196head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/197mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/198text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/199head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/19head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/1head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/200mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/201text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/202head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/203mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/204text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/205head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/206mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/207text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/208head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/209mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/20mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/210text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/211head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/212mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/213text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/214head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/215mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/216text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/217head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/218mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/219text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/21text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/220head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/221mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/222text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/223head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/224mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/225text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/226head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/227mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/228text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/229head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/22head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/230mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/231text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/23mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/24text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/25head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/26mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/27text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/28head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/29mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/2mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/30text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/31head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/32mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/33text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/34head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/35mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/36text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/37head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/38mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/39text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/3text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/40head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/41mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/42text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/43head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/44mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/45text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/46head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/47mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/48text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/49head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/4head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/50mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/51text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/52head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/53mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/54text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/55head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/56mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/57text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/58head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/59mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/5mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/60text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/61head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/62mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/63text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/64head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/65mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/66text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/67head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/68mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/69text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/6text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/70head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/71mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/72text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/73head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/74mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/75text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/76head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/77mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/78text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/79head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/7head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/80mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/81text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/82head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/83mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/84text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/85head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/86mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/87text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/88head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/89mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/8mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/90text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/91head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/92mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/93text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/94head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/95mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/96text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/97head.txt delete mode 100644 crates/vim/test_data/test_word_text_object/98mode.txt delete mode 100644 crates/vim/test_data/test_word_text_object/99text.txt delete mode 100644 crates/vim/test_data/test_word_text_object/9text.txt diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index e5f183c9ef..0000000000 --- a/Cargo.lock +++ /dev/null @@ -1,7629 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "activity_indicator" -version = "0.1.0" -dependencies = [ - "auto_update", - "editor", - "futures 0.3.24", - "gpui", - "language", - "project", - "settings", - "smallvec", - "util", - "workspace", -] - -[[package]] -name = "addr2line" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "adler32" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" - -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom 0.2.7", - "once_cell", - "version_check", -] - -[[package]] -name = "aho-corasick" -version = "0.7.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" -dependencies = [ - "memchr", -] - -[[package]] -name = "alacritty_config" -version = "0.1.1-dev" -source = "git+https://github.com/zed-industries/alacritty?rev=a51dbe25d67e84d6ed4261e640d3954fbdd9be45#a51dbe25d67e84d6ed4261e640d3954fbdd9be45" -dependencies = [ - "log", - "serde", - "serde_yaml", -] - -[[package]] -name = "alacritty_config_derive" -version = "0.2.1-dev" -source = "git+https://github.com/zed-industries/alacritty?rev=a51dbe25d67e84d6ed4261e640d3954fbdd9be45#a51dbe25d67e84d6ed4261e640d3954fbdd9be45" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "alacritty_terminal" -version = "0.17.1-dev" -source = "git+https://github.com/zed-industries/alacritty?rev=a51dbe25d67e84d6ed4261e640d3954fbdd9be45#a51dbe25d67e84d6ed4261e640d3954fbdd9be45" -dependencies = [ - "alacritty_config", - "alacritty_config_derive", - "base64", - "bitflags", - "dirs 4.0.0", - "libc", - "log", - "mio 0.6.23", - "mio-anonymous-pipes", - "mio-extras", - "miow 0.3.7", - "nix", - "parking_lot 0.12.1", - "regex-automata", - "serde", - "serde_yaml", - "signal-hook", - "signal-hook-mio", - "unicode-width", - "vte", - "winapi 0.3.9", -] - -[[package]] -name = "ambient-authority" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8ad6edb4840b78c5c3d88de606b22252d552b55f3a4699fbb10fc070ec3049" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi 0.3.9", -] - -[[package]] -name = "anyhow" -version = "1.0.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" - -[[package]] -name = "arrayref" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "arrayvec" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" - -[[package]] -name = "ascii" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" - -[[package]] -name = "assets" -version = "0.1.0" -dependencies = [ - "anyhow", - "gpui", - "rust-embed", -] - -[[package]] -name = "async-broadcast" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90622698a1218e0b2fb846c97b5f19a0831f6baddee73d9454156365ccfa473b" -dependencies = [ - "easy-parallel", - "event-listener", - "futures-core", -] - -[[package]] -name = "async-channel" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" -dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", -] - -[[package]] -name = "async-compat" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b48b4ff0c2026db683dea961cd8ea874737f56cffca86fa84415eaddc51c00d" -dependencies = [ - "futures-core", - "futures-io", - "once_cell", - "pin-project-lite 0.2.9", - "tokio", -] - -[[package]] -name = "async-compression" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" -dependencies = [ - "flate2", - "futures-core", - "futures-io", - "memchr", - "pin-project-lite 0.2.9", -] - -[[package]] -name = "async-executor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "once_cell", - "slab", -] - -[[package]] -name = "async-fs" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" -dependencies = [ - "async-lock", - "autocfg 1.1.0", - "blocking", - "futures-lite", -] - -[[package]] -name = "async-io" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e21f3a490c72b3b0cf44962180e60045de2925d8dff97918f7ee43c8f637c7" -dependencies = [ - "autocfg 1.1.0", - "concurrent-queue", - "futures-lite", - "libc", - "log", - "once_cell", - "parking", - "polling", - "slab", - "socket2", - "waker-fn", - "winapi 0.3.9", -] - -[[package]] -name = "async-lock" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" -dependencies = [ - "event-listener", -] - -[[package]] -name = "async-net" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4051e67316bc7eff608fe723df5d32ed639946adcd69e07df41fd42a7b411f1f" -dependencies = [ - "async-io", - "autocfg 1.1.0", - "blocking", - "futures-lite", -] - -[[package]] -name = "async-pipe" -version = "0.1.3" -source = "git+https://github.com/zed-industries/async-pipe-rs?rev=82d00a04211cf4e1236029aa03e6b6ce2a74c553#82d00a04211cf4e1236029aa03e6b6ce2a74c553" -dependencies = [ - "futures 0.3.24", - "log", -] - -[[package]] -name = "async-process" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02111fd8655a613c25069ea89fc8d9bb89331fa77486eb3bc059ee757cfa481c" -dependencies = [ - "async-io", - "autocfg 1.1.0", - "blocking", - "cfg-if 1.0.0", - "event-listener", - "futures-lite", - "libc", - "once_cell", - "signal-hook", - "winapi 0.3.9", -] - -[[package]] -name = "async-recursion" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "async-stream" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" -dependencies = [ - "async-stream-impl", - "futures-core", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "async-task" -version = "4.0.3" -source = "git+https://github.com/zed-industries/async-task?rev=341b57d6de98cdfd7b418567b8de2022ca993a6e#341b57d6de98cdfd7b418567b8de2022ca993a6e" - -[[package]] -name = "async-tls" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f23d769dbf1838d5df5156e7b1ad404f4c463d1ac2c6aeb6cd943630f8a8400" -dependencies = [ - "futures-core", - "futures-io", - "rustls 0.19.1", - "webpki 0.21.4", - "webpki-roots 0.21.1", -] - -[[package]] -name = "async-trait" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "async-tungstenite" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5682ea0913e5c20780fe5785abacb85a411e7437bf52a1bedb93ddb3972cb8dd" -dependencies = [ - "async-tls", - "futures-io", - "futures-util", - "log", - "pin-project-lite 0.2.9", - "tungstenite 0.16.0", -] - -[[package]] -name = "atoi" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e" -dependencies = [ - "num-traits", -] - -[[package]] -name = "atomic" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b88d82667eca772c4aa12f0f1348b3ae643424c8876448f3f7bd5787032e234c" -dependencies = [ - "autocfg 1.1.0", -] - -[[package]] -name = "atomic-waker" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi 0.3.9", -] - -[[package]] -name = "auto_update" -version = "0.1.0" -dependencies = [ - "anyhow", - "client", - "gpui", - "isahc", - "lazy_static", - "log", - "menu", - "project", - "serde", - "serde_json", - "settings", - "smol", - "tempdir", - "theme", - "util", - "workspace", -] - -[[package]] -name = "autocfg" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" -dependencies = [ - "autocfg 1.1.0", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "axum" -version = "0.5.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e3356844c4d6a6d6467b8da2cffb4a2820be256f50a3a386c9d152bab31043" -dependencies = [ - "async-trait", - "axum-core", - "base64", - "bitflags", - "bytes 1.2.1", - "futures-util", - "headers", - "http", - "http-body", - "hyper", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite 0.2.9", - "serde", - "serde_json", - "serde_urlencoded", - "sha-1 0.10.0", - "sync_wrapper", - "tokio", - "tokio-tungstenite", - "tower", - "tower-http", - "tower-layer", - "tower-service", -] - -[[package]] -name = "axum-core" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f0c0a60006f2a293d82d571f635042a72edf927539b7685bd62d361963839b" -dependencies = [ - "async-trait", - "bytes 1.2.1", - "futures-util", - "http", - "http-body", - "mime", - "tower-layer", - "tower-service", -] - -[[package]] -name = "axum-extra" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69034b3b0fd97923eee2ce8a47540edb21e07f48f87f67d44bb4271cec622bdb" -dependencies = [ - "axum", - "bytes 1.2.1", - "futures-util", - "http", - "mime", - "pin-project-lite 0.2.9", - "serde", - "serde_json", - "tokio", - "tower", - "tower-http", - "tower-layer", - "tower-service", -] - -[[package]] -name = "backtrace" -version = "0.3.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" -dependencies = [ - "addr2line", - "cc", - "cfg-if 1.0.0", - "libc", - "miniz_oxide 0.5.4", - "object 0.29.0", - "rustc-demangle", -] - -[[package]] -name = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" - -[[package]] -name = "base64ct" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2b2456fd614d856680dcd9fcc660a51a820fa09daef2e49772b56a193c8474" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bindgen" -version = "0.59.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" -dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "clap 2.34.0", - "env_logger", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "which", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-buffer" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" -dependencies = [ - "generic-array", -] - -[[package]] -name = "blocking" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" -dependencies = [ - "async-channel", - "async-task", - "atomic-waker", - "fastrand", - "futures-lite", - "once_cell", -] - -[[package]] -name = "breadcrumbs" -version = "0.1.0" -dependencies = [ - "collections", - "editor", - "gpui", - "itertools", - "language", - "project", - "search", - "settings", - "theme", - "workspace", -] - -[[package]] -name = "bromberg_sl2" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ed88064f69518b7e3ea50ecfc1b61d43f19248618a377b95ae5c8b611134d4d" -dependencies = [ - "digest 0.9.0", - "lazy_static", - "rayon", - "seq-macro", -] - -[[package]] -name = "bstr" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" -dependencies = [ - "memchr", -] - -[[package]] -name = "bumpalo" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" - -[[package]] -name = "bytemuck" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" -dependencies = [ - "byteorder", - "iovec", -] - -[[package]] -name = "bytes" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" - -[[package]] -name = "bzip2-sys" -version = "0.1.11+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "cache-padded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" - -[[package]] -name = "cap-fs-ext" -version = "0.24.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e54b86398b5852ddd45784b1d9b196b98beb39171821bad4b8b44534a1e87927" -dependencies = [ - "cap-primitives", - "cap-std", - "io-lifetimes", - "winapi 0.3.9", -] - -[[package]] -name = "cap-primitives" -version = "0.24.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb8fca3e81fae1d91a36e9784ca22a39ef623702b5f7904d89dc31f10184a178" -dependencies = [ - "ambient-authority", - "errno", - "fs-set-times", - "io-extras", - "io-lifetimes", - "ipnet", - "maybe-owned", - "rustix", - "winapi 0.3.9", - "winapi-util", - "winx", -] - -[[package]] -name = "cap-rand" -version = "0.24.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3b27294116983d706f4c8168f6d10c84f9f5daed0c28bc7d0296cf16bcf971" -dependencies = [ - "ambient-authority", - "rand 0.8.5", -] - -[[package]] -name = "cap-std" -version = "0.24.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2247568946095c7765ad2b441a56caffc08027734c634a6d5edda648f04e32eb" -dependencies = [ - "cap-primitives", - "io-extras", - "io-lifetimes", - "ipnet", - "rustix", -] - -[[package]] -name = "cap-time-ext" -version = "0.24.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50472b6ebc302af0401fa3fb939694cd8ff00e0d4c9182001e434fc822ab83a" -dependencies = [ - "cap-primitives", - "once_cell", - "rustix", - "winx", -] - -[[package]] -name = "capture" -version = "0.1.0" -dependencies = [ - "anyhow", - "bindgen", - "block", - "byteorder", - "bytes 1.2.1", - "cocoa", - "core-foundation", - "core-graphics", - "foreign-types", - "futures 0.3.24", - "gpui", - "hmac 0.12.1", - "jwt", - "live_kit", - "log", - "media", - "objc", - "parking_lot 0.11.2", - "postage", - "serde", - "sha2 0.10.6", - "simplelog", -] - -[[package]] -name = "castaway" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" - -[[package]] -name = "cc" -version = "1.0.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" -dependencies = [ - "jobserver", -] - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chat_panel" -version = "0.1.0" -dependencies = [ - "client", - "editor", - "gpui", - "menu", - "postage", - "settings", - "theme", - "time 0.3.15", - "util", - "workspace", -] - -[[package]] -name = "chrono" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-integer", - "num-traits", - "time 0.1.44", - "wasm-bindgen", - "winapi 0.3.9", -] - -[[package]] -name = "chunked_transfer" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e" - -[[package]] -name = "cipher" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" -dependencies = [ - "generic-array", -] - -[[package]] -name = "clang-sys" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim 0.8.0", - "textwrap 0.11.0", - "unicode-width", - "vec_map", -] - -[[package]] -name = "clap" -version = "3.2.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" -dependencies = [ - "atty", - "bitflags", - "clap_derive", - "clap_lex", - "indexmap", - "once_cell", - "strsim 0.10.0", - "termcolor", - "textwrap 0.15.1", -] - -[[package]] -name = "clap_derive" -version = "3.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" -dependencies = [ - "heck 0.4.0", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "cli" -version = "0.1.0" -dependencies = [ - "anyhow", - "clap 3.2.22", - "core-foundation", - "core-services", - "dirs 3.0.2", - "ipc-channel", - "plist", - "serde", -] - -[[package]] -name = "client" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-recursion", - "async-tungstenite", - "collections", - "db", - "futures 0.3.24", - "gpui", - "image", - "isahc", - "lazy_static", - "log", - "parking_lot 0.11.2", - "postage", - "rand 0.8.5", - "rpc", - "serde", - "smol", - "sum_tree", - "tempfile", - "thiserror", - "time 0.3.15", - "tiny_http", - "url", - "util", - "uuid 1.2.1", -] - -[[package]] -name = "clock" -version = "0.1.0" -dependencies = [ - "smallvec", -] - -[[package]] -name = "cmake" -version = "0.1.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a" -dependencies = [ - "cc", -] - -[[package]] -name = "cocoa" -version = "0.24.0" -source = "git+https://github.com/servo/core-foundation-rs?rev=079665882507dd5e2ff77db3de5070c1f6c0fb85#079665882507dd5e2ff77db3de5070c1f6c0fb85" -dependencies = [ - "bitflags", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.1" -source = "git+https://github.com/servo/core-foundation-rs?rev=079665882507dd5e2ff77db3de5070c1f6c0fb85#079665882507dd5e2ff77db3de5070c1f6c0fb85" -dependencies = [ - "bitflags", - "block", - "core-foundation", - "core-graphics-types", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - -[[package]] -name = "collab" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-trait", - "async-tungstenite", - "axum", - "axum-extra", - "base64", - "clap 3.2.22", - "client", - "collections", - "ctor", - "editor", - "env_logger", - "envy", - "futures 0.3.24", - "git", - "gpui", - "hyper", - "language", - "lazy_static", - "lipsum", - "log", - "lsp", - "nanoid", - "parking_lot 0.11.2", - "project", - "prometheus", - "rand 0.8.5", - "reqwest", - "rpc", - "scrypt", - "serde", - "serde_json", - "settings", - "sha-1 0.9.8", - "sqlx", - "theme", - "time 0.3.15", - "tokio", - "tokio-tungstenite", - "toml", - "tonic", - "tower", - "tracing", - "tracing-log", - "tracing-subscriber", - "unindent", - "util", - "workspace", -] - -[[package]] -name = "collections" -version = "0.1.0" -dependencies = [ - "seahash", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - -[[package]] -name = "command_palette" -version = "0.1.0" -dependencies = [ - "collections", - "ctor", - "editor", - "env_logger", - "fuzzy", - "gpui", - "picker", - "project", - "serde_json", - "settings", - "theme", - "util", - "workspace", -] - -[[package]] -name = "concurrent-queue" -version = "1.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" -dependencies = [ - "cache-padded", -] - -[[package]] -name = "contacts_panel" -version = "0.1.0" -dependencies = [ - "anyhow", - "client", - "collections", - "editor", - "futures 0.3.24", - "fuzzy", - "gpui", - "language", - "log", - "menu", - "picker", - "postage", - "project", - "serde", - "settings", - "theme", - "util", - "workspace", -] - -[[package]] -name = "contacts_status_item" -version = "0.1.0" -dependencies = [ - "anyhow", - "client", - "collections", - "editor", - "futures 0.3.24", - "fuzzy", - "gpui", - "language", - "log", - "menu", - "picker", - "postage", - "project", - "serde", - "settings", - "theme", - "util", - "workspace", -] - -[[package]] -name = "context_menu" -version = "0.1.0" -dependencies = [ - "gpui", - "menu", - "settings", - "smallvec", - "theme", -] - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "git+https://github.com/servo/core-foundation-rs?rev=079665882507dd5e2ff77db3de5070c1f6c0fb85#079665882507dd5e2ff77db3de5070c1f6c0fb85" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "git+https://github.com/servo/core-foundation-rs?rev=079665882507dd5e2ff77db3de5070c1f6c0fb85#079665882507dd5e2ff77db3de5070c1f6c0fb85" - -[[package]] -name = "core-graphics" -version = "0.22.3" -source = "git+https://github.com/servo/core-foundation-rs?rev=079665882507dd5e2ff77db3de5070c1f6c0fb85#079665882507dd5e2ff77db3de5070c1f6c0fb85" -dependencies = [ - "bitflags", - "core-foundation", - "core-graphics-types", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.1" -source = "git+https://github.com/servo/core-foundation-rs?rev=079665882507dd5e2ff77db3de5070c1f6c0fb85#079665882507dd5e2ff77db3de5070c1f6c0fb85" -dependencies = [ - "bitflags", - "core-foundation", - "foreign-types", - "libc", -] - -[[package]] -name = "core-services" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b344b958cae90858bf6086f49599ecc5ec8698eacad0ea155509ba11fab347" -dependencies = [ - "core-foundation", -] - -[[package]] -name = "core-text" -version = "19.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" -dependencies = [ - "core-foundation", - "core-graphics", - "foreign-types", - "libc", -] - -[[package]] -name = "cpp_demangle" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "cpufeatures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" -dependencies = [ - "libc", -] - -[[package]] -name = "cranelift-bforest" -version = "0.85.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "749d0d6022c9038dccf480bdde2a38d435937335bf2bb0f14e815d94517cdce8" -dependencies = [ - "cranelift-entity", -] - -[[package]] -name = "cranelift-codegen" -version = "0.85.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94370cc7b37bf652ccd8bb8f09bd900997f7ccf97520edfc75554bb5c4abbea" -dependencies = [ - "cranelift-bforest", - "cranelift-codegen-meta", - "cranelift-codegen-shared", - "cranelift-entity", - "cranelift-isle", - "gimli", - "log", - "regalloc2", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cranelift-codegen-meta" -version = "0.85.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a3cea8fdab90e44018c5b9a1dfd460d8ee265ac354337150222a354628bdb6" -dependencies = [ - "cranelift-codegen-shared", -] - -[[package]] -name = "cranelift-codegen-shared" -version = "0.85.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac72f76f2698598951ab26d8c96eaa854810e693e7dd52523958b5909fde6b2" - -[[package]] -name = "cranelift-entity" -version = "0.85.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09eaeacfcd2356fe0e66b295e8f9d59fdd1ac3ace53ba50de14d628ec902f72d" -dependencies = [ - "serde", -] - -[[package]] -name = "cranelift-frontend" -version = "0.85.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dba69c9980d5ffd62c18a2bde927855fcd7c8dc92f29feaf8636052662cbd99c" -dependencies = [ - "cranelift-codegen", - "log", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cranelift-isle" -version = "0.85.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2920dc1e05cac40304456ed3301fde2c09bd6a9b0210bcfa2f101398d628d5b" - -[[package]] -name = "cranelift-native" -version = "0.85.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04dfa45f9b2a6f587c564d6b63388e00cd6589d2df6ea2758cf79e1a13285e6" -dependencies = [ - "cranelift-codegen", - "libc", - "target-lexicon", -] - -[[package]] -name = "cranelift-wasm" -version = "0.85.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31a46513ae6f26f3f267d8d75b5373d555fbbd1e68681f348d99df43f747ec54" -dependencies = [ - "cranelift-codegen", - "cranelift-entity", - "cranelift-frontend", - "itertools", - "log", - "smallvec", - "wasmparser", - "wasmtime-types", -] - -[[package]] -name = "crc" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d0165d2900ae6778e36e80bbc4da3b5eefccee9ba939761f9c2882a5d9af3ff" - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "crossbeam-channel" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" -dependencies = [ - "crossbeam-utils 0.7.2", - "maybe-uninit", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils 0.8.12", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-epoch", - "crossbeam-utils 0.8.12", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" -dependencies = [ - "autocfg 1.1.0", - "cfg-if 1.0.0", - "crossbeam-utils 0.8.12", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd42583b04998a5363558e5f9291ee5a5ff6b49944332103f251e7479a82aa7" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils 0.8.12", -] - -[[package]] -name = "crossbeam-utils" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" -dependencies = [ - "autocfg 1.1.0", - "cfg-if 0.1.10", - "lazy_static", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "ctor" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "curl" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22" -dependencies = [ - "curl-sys", - "libc", - "openssl-probe", - "openssl-sys", - "schannel", - "socket2", - "winapi 0.3.9", -] - -[[package]] -name = "curl-sys" -version = "0.4.56+curl-7.83.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093e169dd4de29e468fa649fbae11cdcd5551c81fe5bf1b0677adad7ef3d26f" -dependencies = [ - "cc", - "libc", - "libnghttp2-sys", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", - "winapi 0.3.9", -] - -[[package]] -name = "cxx" -version = "1.0.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f39818dcfc97d45b03953c1292efc4e80954e1583c4aa770bac1383e2310a4" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e580d70777c116df50c390d1211993f62d40302881e54d4b79727acb83d0199" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56a46460b88d1cec95112c8c363f0e2c39afdb237f60583b0b36343bf627ea9c" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747b608fecf06b0d72d440f27acc99288207324b793be2c17991839f3d4995ea" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "data-url" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30bfce702bcfa94e906ef82421f2c0e61c076ad76030c16ee5d2e9a32fe193" -dependencies = [ - "matches", -] - -[[package]] -name = "db" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-trait", - "collections", - "gpui", - "parking_lot 0.11.2", - "rocksdb", - "tempdir", -] - -[[package]] -name = "deflate" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" -dependencies = [ - "adler32", - "byteorder", -] - -[[package]] -name = "dhat" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0684eaa19a59be283a6f99369917b679bd4d1d06604b2eb2e2f87b4bbd67668d" -dependencies = [ - "backtrace", - "lazy_static", - "parking_lot 0.12.1", - "rustc-hash", - "serde", - "serde_json", - "thousands", -] - -[[package]] -name = "diagnostics" -version = "0.1.0" -dependencies = [ - "anyhow", - "client", - "collections", - "editor", - "gpui", - "language", - "postage", - "project", - "serde_json", - "settings", - "smallvec", - "theme", - "unindent", - "util", - "workspace", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "digest" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" -dependencies = [ - "block-buffer 0.10.3", - "crypto-common", - "subtle", -] - -[[package]] -name = "directories-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" -dependencies = [ - "cfg-if 1.0.0", - "dirs-sys-next", -] - -[[package]] -name = "dirs" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if 1.0.0", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi 0.3.9", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi 0.3.9", -] - -[[package]] -name = "dotenvy" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9155c8f4dc55c7470ae9da3f63c6785245093b3f6aeb0f5bf2e968efbba314" -dependencies = [ - "dirs 4.0.0", -] - -[[package]] -name = "drag_and_drop" -version = "0.1.0" -dependencies = [ - "collections", - "gpui", -] - -[[package]] -name = "dwrote" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" -dependencies = [ - "lazy_static", - "libc", - "winapi 0.3.9", - "wio", -] - -[[package]] -name = "dyn-clone" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2" - -[[package]] -name = "easy-parallel" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6907e25393cdcc1f4f3f513d9aac1e840eb1cc341a0fccb01171f7d14d10b946" - -[[package]] -name = "editor" -version = "0.1.0" -dependencies = [ - "aho-corasick", - "anyhow", - "clock", - "collections", - "context_menu", - "ctor", - "env_logger", - "futures 0.3.24", - "fuzzy", - "git", - "gpui", - "indoc", - "itertools", - "language", - "lazy_static", - "log", - "lsp", - "ordered-float", - "parking_lot 0.11.2", - "postage", - "project", - "rand 0.8.5", - "rpc", - "serde", - "settings", - "smallvec", - "smol", - "snippet", - "sum_tree", - "text", - "theme", - "tree-sitter", - "tree-sitter-html", - "tree-sitter-javascript", - "tree-sitter-rust", - "unindent", - "util", - "workspace", -] - -[[package]] -name = "either" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" - -[[package]] -name = "encoding_rs" -version = "0.8.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "env_logger" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "envy" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f47e0157f2cb54f5ae1bd371b30a2ae4311e1c028f575cd4e81de7353215965" -dependencies = [ - "serde", -] - -[[package]] -name = "erased-serde" -version = "0.3.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54558e0ba96fbe24280072642eceb9d7d442e32c7ec0ea9e7ecd7b4ea2cf4e11" -dependencies = [ - "serde", -] - -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi 0.3.9", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "etagere" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6301151a318f367f392c31395beb1cfba5ccd9abc44d1db0db3a4b27b9601c89" -dependencies = [ - "euclid", - "svg_fmt", -] - -[[package]] -name = "euclid" -version = "0.22.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b52c2ef4a78da0ba68fbe1fd920627411096d2ac478f7f4c9f3a54ba6705bade" -dependencies = [ - "num-traits", -] - -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "expat-sys" -version = "2.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658f19728920138342f68408b7cf7644d90d4784353d8ebc32e7e8663dbe45fa" -dependencies = [ - "cmake", - "pkg-config", -] - -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - -[[package]] -name = "fastrand" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" -dependencies = [ - "instant", -] - -[[package]] -name = "file-per-thread-logger" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e16290574b39ee41c71aeb90ae960c504ebaf1e2a1c87bd52aa56ed6e1a02f" -dependencies = [ - "env_logger", - "log", -] - -[[package]] -name = "file_finder" -version = "0.1.0" -dependencies = [ - "ctor", - "editor", - "env_logger", - "fuzzy", - "gpui", - "menu", - "picker", - "postage", - "project", - "serde_json", - "settings", - "theme", - "util", - "workspace", -] - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "flate2" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" -dependencies = [ - "crc32fast", - "miniz_oxide 0.5.4", -] - -[[package]] -name = "float-cmp" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75224bec9bfe1a65e2d34132933f2de7fe79900c96a0174307554244ece8150e" - -[[package]] -name = "float-ord" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bad48618fdb549078c333a7a8528acb57af271d0433bdecd523eb620628364e" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "font-kit" -version = "0.10.0" -source = "git+https://github.com/zed-industries/font-kit?rev=8eaf7a918eafa28b0a37dc759e2e0e7683fa24f1#8eaf7a918eafa28b0a37dc759e2e0e7683fa24f1" -dependencies = [ - "bitflags", - "byteorder", - "core-foundation", - "core-graphics", - "core-text", - "dirs-next", - "dwrote", - "float-ord", - "freetype", - "lazy_static", - "libc", - "log", - "pathfinder_geometry", - "pathfinder_simd", - "servo-fontconfig", - "walkdir", - "winapi 0.3.9", -] - -[[package]] -name = "fontdb" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e58903f4f8d5b58c7d300908e4ebe5289c1bfdf5587964330f12023b8ff17fd1" -dependencies = [ - "log", - "memmap2", - "ttf-parser 0.12.3", -] - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "freetype" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee38378a9e3db1cc693b4f88d166ae375338a0ff75cb8263e1c601d51f35dc6" -dependencies = [ - "freetype-sys", - "libc", -] - -[[package]] -name = "freetype-sys" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" -dependencies = [ - "cmake", - "libc", - "pkg-config", -] - -[[package]] -name = "fs-set-times" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df62ee66ee2d532ea8d567b5a3f0d03ecd64636b98bad5be1e93dcc918b92aa" -dependencies = [ - "io-lifetimes", - "rustix", - "winapi 0.3.9", -] - -[[package]] -name = "fsevent" -version = "2.0.2" -dependencies = [ - "bitflags", - "fsevent-sys", - "parking_lot 0.11.2", - "tempdir", -] - -[[package]] -name = "fsevent-sys" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6f5e6817058771c10f0eb0f05ddf1e35844266f972004fe8e4b21fda295bd5" -dependencies = [ - "libc", -] - -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -dependencies = [ - "bitflags", - "fuchsia-zircon-sys", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" - -[[package]] -name = "futures" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" - -[[package]] -name = "futures" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" - -[[package]] -name = "futures-executor" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-intrusive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62007592ac46aa7c2b6416f7deb9a8a8f63a01e0f1d6e1787d5630170db2b63e" -dependencies = [ - "futures-core", - "lock_api", - "parking_lot 0.11.2", -] - -[[package]] -name = "futures-io" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" - -[[package]] -name = "futures-lite" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite 0.2.9", - "waker-fn", -] - -[[package]] -name = "futures-macro" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" - -[[package]] -name = "futures-task" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" - -[[package]] -name = "futures-util" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" -dependencies = [ - "futures 0.1.31", - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite 0.2.9", - "pin-utils", - "slab", - "tokio-io", -] - -[[package]] -name = "fuzzy" -version = "0.1.0" -dependencies = [ - "gpui", - "util", -] - -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "generic-array" -version = "0.14.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "gif" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" -dependencies = [ - "color_quant", - "weezl", -] - -[[package]] -name = "gimli" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" -dependencies = [ - "fallible-iterator", - "indexmap", - "stable_deref_trait", -] - -[[package]] -name = "git" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-trait", - "clock", - "collections", - "futures 0.3.24", - "git2", - "lazy_static", - "log", - "parking_lot 0.11.2", - "smol", - "sum_tree", - "text", - "unindent", - "util", -] - -[[package]] -name = "git2" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2994bee4a3a6a51eb90c218523be382fd7ea09b16380b9312e9dbe955ff7c7d1" -dependencies = [ - "bitflags", - "libc", - "libgit2-sys", - "log", - "url", -] - -[[package]] -name = "glob" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" - -[[package]] -name = "globset" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" -dependencies = [ - "aho-corasick", - "bstr", - "fnv", - "log", - "regex", -] - -[[package]] -name = "go_to_line" -version = "0.1.0" -dependencies = [ - "editor", - "gpui", - "menu", - "postage", - "settings", - "text", - "workspace", -] - -[[package]] -name = "gpui" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-task", - "backtrace", - "bindgen", - "block", - "cc", - "cocoa", - "collections", - "core-foundation", - "core-graphics", - "core-text", - "ctor", - "dhat", - "env_logger", - "etagere", - "font-kit", - "foreign-types", - "futures 0.3.24", - "gpui_macros", - "image", - "lazy_static", - "log", - "media", - "metal", - "num_cpus", - "objc", - "ordered-float", - "parking", - "parking_lot 0.11.2", - "pathfinder_color", - "pathfinder_geometry", - "png", - "postage", - "rand 0.8.5", - "resvg", - "seahash", - "serde", - "serde_json", - "simplelog", - "smallvec", - "smol", - "sum_tree", - "time 0.3.15", - "tiny-skia", - "tree-sitter", - "usvg", - "util", - "waker-fn", -] - -[[package]] -name = "gpui_macros" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "h2" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" -dependencies = [ - "bytes 1.2.1", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util 0.7.4", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashlink" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa" -dependencies = [ - "hashbrown 0.12.3", -] - -[[package]] -name = "headers" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" -dependencies = [ - "base64", - "bitflags", - "bytes 1.2.1", - "headers-core", - "http", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" -dependencies = [ - "http", -] - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "heck" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hkdf" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" -dependencies = [ - "hmac 0.12.1", -] - -[[package]] -name = "hmac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" -dependencies = [ - "crypto-mac", - "digest 0.9.0", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.5", -] - -[[package]] -name = "http" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" -dependencies = [ - "bytes 1.2.1", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes 1.2.1", - "http", - "pin-project-lite 0.2.9", -] - -[[package]] -name = "http-range-header" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "hyper" -version = "0.14.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" -dependencies = [ - "bytes 1.2.1", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite 0.2.9", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-timeout" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" -dependencies = [ - "hyper", - "pin-project-lite 0.2.9", - "tokio", - "tokio-io-timeout", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes 1.2.1", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.51" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5a6ef98976b22b3b7f2f3a806f858cb862044cfa66805aa3ad84cb3d3b785ed" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "winapi 0.3.9", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fde6edd6cef363e9359ed3c98ba64590ba9eecba2293eb5a723ab32aee8926aa" -dependencies = [ - "cxx", - "cxx-build", -] - -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "ignore" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" -dependencies = [ - "crossbeam-utils 0.8.12", - "globset", - "lazy_static", - "log", - "memchr", - "regex", - "same-file", - "thread_local", - "walkdir", - "winapi-util", -] - -[[package]] -name = "image" -version = "0.23.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "gif", - "jpeg-decoder", - "num-iter", - "num-rational", - "num-traits", - "png", - "scoped_threadpool", - "tiff", -] - -[[package]] -name = "indexmap" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" -dependencies = [ - "autocfg 1.1.0", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indoc" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3" - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "io-extras" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c937cc9891c12eaa8c63ad347e4a288364b1328b924886970b47a14ab8f8f8" -dependencies = [ - "io-lifetimes", - "winapi 0.3.9", -] - -[[package]] -name = "io-lifetimes" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec58677acfea8a15352d42fc87d11d63596ade9239e0a7c9352914417515dbe6" -dependencies = [ - "libc", - "winapi 0.3.9", -] - -[[package]] -name = "iovec" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" -dependencies = [ - "libc", -] - -[[package]] -name = "ipc-channel" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cb1d9211085f0ea6f1379d944b93c4d07e8207aa3bcf49f37eda12b85081887" -dependencies = [ - "bincode", - "crossbeam-channel 0.4.4", - "fnv", - "lazy_static", - "libc", - "mio 0.6.23", - "rand 0.7.3", - "serde", - "tempfile", - "uuid 0.8.2", - "winapi 0.3.9", -] - -[[package]] -name = "ipnet" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" - -[[package]] -name = "is-terminal" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c89a757e762896bdbdfadf2860d0f8b0cea5e363d8cf3e7bdfeb63d1d976352" -dependencies = [ - "hermit-abi 0.2.6", - "io-lifetimes", - "rustix", - "winapi 0.3.9", -] - -[[package]] -name = "isahc" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9" -dependencies = [ - "async-channel", - "castaway", - "crossbeam-utils 0.8.12", - "curl", - "curl-sys", - "encoding_rs", - "event-listener", - "futures-lite", - "http", - "log", - "mime", - "once_cell", - "polling", - "slab", - "sluice", - "tracing", - "tracing-futures", - "url", - "waker-fn", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" - -[[package]] -name = "ittapi-rs" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f712648a1ad72fbfb7adc2772c331e8d90f022f8cf30cbabefba2878dd3172b0" -dependencies = [ - "cc", -] - -[[package]] -name = "jobserver" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" -dependencies = [ - "libc", -] - -[[package]] -name = "journal" -version = "0.1.0" -dependencies = [ - "chrono", - "dirs 4.0.0", - "editor", - "gpui", - "log", - "util", - "workspace", -] - -[[package]] -name = "jpeg-decoder" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" -dependencies = [ - "rayon", -] - -[[package]] -name = "js-sys" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "json_comments" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ee439ee368ba4a77ac70d04f14015415af8600d6c894dc1f11bd79758c57d5" - -[[package]] -name = "jwt" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6204285f77fe7d9784db3fdc449ecce1a0114927a51d5a41c4c7a292011c015f" -dependencies = [ - "base64", - "crypto-common", - "digest 0.10.5", - "hmac 0.12.1", - "serde", - "serde_json", - "sha2 0.10.6", -] - -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - -[[package]] -name = "kurbo" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a53776d271cfb873b17c618af0298445c88afc52837f3e948fa3fafd131f449" -dependencies = [ - "arrayvec 0.7.2", -] - -[[package]] -name = "language" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-broadcast", - "async-trait", - "client", - "clock", - "collections", - "ctor", - "env_logger", - "futures 0.3.24", - "fuzzy", - "git", - "gpui", - "lazy_static", - "log", - "lsp", - "parking_lot 0.11.2", - "postage", - "rand 0.8.5", - "regex", - "rpc", - "serde", - "serde_json", - "settings", - "similar", - "smallvec", - "smol", - "sum_tree", - "text", - "theme", - "tree-sitter", - "tree-sitter-html", - "tree-sitter-javascript", - "tree-sitter-json 0.19.0", - "tree-sitter-python", - "tree-sitter-rust", - "tree-sitter-typescript", - "unindent", - "util", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -dependencies = [ - "spin", -] - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - -[[package]] -name = "leb128" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" - -[[package]] -name = "libc" -version = "0.2.134" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" - -[[package]] -name = "libgit2-sys" -version = "0.14.0+1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47a00859c70c8a4f7218e6d1cc32875c4b55f6799445b842b0d8ed5e4c3d959b" -dependencies = [ - "cc", - "libc", - "libz-sys", - "pkg-config", -] - -[[package]] -name = "libloading" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" -dependencies = [ - "cfg-if 1.0.0", - "winapi 0.3.9", -] - -[[package]] -name = "libm" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "292a948cd991e376cf75541fe5b97a1081d713c618b4f1b9500f8844e49eb565" - -[[package]] -name = "libnghttp2-sys" -version = "0.1.7+1.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ed28aba195b38d5ff02b9170cbff627e336a20925e43b4945390401c5dc93f" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "librocksdb-sys" -version = "0.7.1+7.3.1" -source = "git+https://github.com/rust-rocksdb/rust-rocksdb?rev=39dc822dde743b2a26eb160b660e8fbdab079d49#39dc822dde743b2a26eb160b660e8fbdab079d49" -dependencies = [ - "bindgen", - "bzip2-sys", - "cc", - "glob", - "libc", - "libz-sys", - "zstd-sys", -] - -[[package]] -name = "libz-sys" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "line-wrap" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" -dependencies = [ - "safemem", -] - -[[package]] -name = "link-cplusplus" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" -dependencies = [ - "cc", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "linux-raw-sys" -version = "0.0.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5284f00d480e1c39af34e72f8ad60b94f47007e3481cd3b731c1d67190ddc7b7" - -[[package]] -name = "lipsum" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8451846f1f337e44486666989fbce40be804da139d5a4477d6b88ece5dc69f4" -dependencies = [ - "rand 0.8.5", - "rand_chacha 0.3.1", -] - -[[package]] -name = "live_kit" -version = "0.1.0" -dependencies = [ - "anyhow", - "core-foundation", - "core-graphics", - "futures 0.3.24", - "media", - "parking_lot 0.11.2", - "serde", - "serde_json", -] - -[[package]] -name = "lock_api" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg 1.1.0", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if 1.0.0", - "serde", - "value-bag", -] - -[[package]] -name = "lsp" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-pipe", - "collections", - "ctor", - "env_logger", - "futures 0.3.24", - "gpui", - "log", - "lsp-types", - "parking_lot 0.11.2", - "postage", - "serde", - "serde_json", - "smol", - "unindent", - "util", -] - -[[package]] -name = "lsp-types" -version = "0.91.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2368312c59425dd133cb9a327afee65be0a633a8ce471d248e2202a48f8f68ae" -dependencies = [ - "bitflags", - "serde", - "serde_json", - "serde_repr", - "url", -] - -[[package]] -name = "mach" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" -dependencies = [ - "libc", -] - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata", -] - -[[package]] -name = "matches" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - -[[package]] -name = "matchit" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" - -[[package]] -name = "maybe-owned" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" - -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - -[[package]] -name = "md-5" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" -dependencies = [ - "digest 0.10.5", -] - -[[package]] -name = "media" -version = "0.1.0" -dependencies = [ - "anyhow", - "bindgen", - "block", - "bytes 1.2.1", - "core-foundation", - "foreign-types", - "metal", - "objc", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memfd" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6627dc657574b49d6ad27105ed671822be56e0d2547d413bfbf3e8d8fa92e7a" -dependencies = [ - "libc", -] - -[[package]] -name = "memmap2" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" -dependencies = [ - "libc", -] - -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg 1.1.0", -] - -[[package]] -name = "menu" -version = "0.1.0" -dependencies = [ - "gpui", -] - -[[package]] -name = "metal" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4598d719460ade24c7d91f335daf055bf2a7eec030728ce751814c50cdd6a26c" -dependencies = [ - "bitflags", - "block", - "cocoa-foundation", - "foreign-types", - "log", - "objc", -] - -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" -dependencies = [ - "adler32", -] - -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg 1.1.0", -] - -[[package]] -name = "miniz_oxide" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "0.6.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" -dependencies = [ - "cfg-if 0.1.10", - "fuchsia-zircon", - "fuchsia-zircon-sys", - "iovec", - "kernel32-sys", - "libc", - "log", - "miow 0.2.2", - "net2", - "slab", - "winapi 0.2.8", -] - -[[package]] -name = "mio" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" -dependencies = [ - "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", -] - -[[package]] -name = "mio-anonymous-pipes" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bc513025fe5005a3aa561b50fdb2cda5a150b84800ae02acd8aa9ed62ca1a6b" -dependencies = [ - "mio 0.6.23", - "miow 0.3.7", - "parking_lot 0.11.2", - "spsc-buffer", - "winapi 0.3.9", -] - -[[package]] -name = "mio-extras" -version = "2.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" -dependencies = [ - "lazycell", - "log", - "mio 0.6.23", - "slab", -] - -[[package]] -name = "mio-uds" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" -dependencies = [ - "iovec", - "libc", - "mio 0.6.23", -] - -[[package]] -name = "miow" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" -dependencies = [ - "kernel32-sys", - "net2", - "winapi 0.2.8", - "ws2_32-sys", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi 0.3.9", -] - -[[package]] -name = "more-asserts" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" - -[[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - -[[package]] -name = "nanoid" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ffa00dec017b5b1a8b7cf5e2c008bfda1aa7e0697ac1508b491fdf2622fb4d8" -dependencies = [ - "rand 0.8.5", -] - -[[package]] -name = "native-tls" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "net2" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" -dependencies = [ - "cfg-if 0.1.10", - "libc", - "winapi 0.3.9", -] - -[[package]] -name = "nix" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" -dependencies = [ - "bitflags", - "cfg-if 1.0.0", - "libc", - "memoffset", -] - -[[package]] -name = "nom" -version = "7.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "ntapi" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" -dependencies = [ - "winapi 0.3.9", -] - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi 0.3.9", -] - -[[package]] -name = "num-bigint" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" -dependencies = [ - "autocfg 1.1.0", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-bigint-dig" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4547ee5541c18742396ae2c895d0717d0f886d8823b8399cdaf7b07d63ad0480" -dependencies = [ - "autocfg 0.1.8", - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.5", - "smallvec", - "zeroize", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg 1.1.0", - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" -dependencies = [ - "autocfg 1.1.0", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" -dependencies = [ - "autocfg 1.1.0", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg 1.1.0", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" -dependencies = [ - "hermit-abi 0.1.19", - "libc", -] - -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", -] - -[[package]] -name = "nvim-rs" -version = "0.5.0" -source = "git+https://github.com/KillTheMule/nvim-rs?branch=master#d701c2790dcb2579f8f4d7003ba30e2100a7d25b" -dependencies = [ - "async-trait", - "futures 0.3.24", - "log", - "parity-tokio-ipc", - "rmp", - "rmpv", - "tokio", - "tokio-util 0.7.4", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", - "objc_exception", -] - -[[package]] -name = "objc_exception" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" -dependencies = [ - "cc", -] - -[[package]] -name = "object" -version = "0.28.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" -dependencies = [ - "crc32fast", - "hashbrown 0.11.2", - "indexmap", - "memchr", -] - -[[package]] -name = "object" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "openssl" -version = "0.10.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" -dependencies = [ - "bitflags", - "cfg-if 1.0.0", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" -dependencies = [ - "autocfg 1.1.0", - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "ordered-float" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87" -dependencies = [ - "num-traits", -] - -[[package]] -name = "os_str_bytes" -version = "6.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" - -[[package]] -name = "outline" -version = "0.1.0" -dependencies = [ - "editor", - "fuzzy", - "gpui", - "language", - "ordered-float", - "picker", - "postage", - "settings", - "smol", - "text", - "workspace", -] - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "parity-tokio-ipc" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9981e32fb75e004cc148f5fb70342f393830e0a4aa62e3cc93b50976218d42b6" -dependencies = [ - "futures 0.3.24", - "libc", - "log", - "rand 0.7.3", - "tokio", - "winapi 0.3.9", -] - -[[package]] -name = "parking" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.5", -] - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core 0.9.3", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" -dependencies = [ - "cfg-if 1.0.0", - "instant", - "libc", - "redox_syscall", - "smallvec", - "winapi 0.3.9", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "redox_syscall", - "smallvec", - "windows-sys", -] - -[[package]] -name = "password-hash" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e0b28ace46c5a396546bcf443bf422b57049617433d8854227352a4a9b24e7" -dependencies = [ - "base64ct", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "paste" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" - -[[package]] -name = "pathfinder_color" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69bdc0d277d559e35e1b374de56df9262a6b71e091ca04a8831a239f8c7f0c62" -dependencies = [ - "pathfinder_simd", -] - -[[package]] -name = "pathfinder_geometry" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" -dependencies = [ - "log", - "pathfinder_simd", -] - -[[package]] -name = "pathfinder_simd" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39fe46acc5503595e5949c17b818714d26fdf9b4920eacf3b2947f0199f4a6ff" -dependencies = [ - "rustc_version", -] - -[[package]] -name = "pbkdf2" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" -dependencies = [ - "crypto-mac", -] - -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - -[[package]] -name = "pem" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" -dependencies = [ - "base64", - "once_cell", - "regex", -] - -[[package]] -name = "percent-encoding" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" - -[[package]] -name = "pest" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc7bc69c062e492337d74d59b120c274fd3d261b6bf6d3207d499b4b379c41a" -dependencies = [ - "thiserror", - "ucd-trie", -] - -[[package]] -name = "petgraph" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" -dependencies = [ - "fixedbitset", - "indexmap", -] - -[[package]] -name = "picker" -version = "0.1.0" -dependencies = [ - "ctor", - "editor", - "env_logger", - "gpui", - "menu", - "serde_json", - "settings", - "theme", - "util", - "workspace", -] - -[[package]] -name = "pico-args" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" - -[[package]] -name = "pin-project" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pin-project-lite" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" - -[[package]] -name = "plist" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd39bc6cdc9355ad1dc5eeedefee696bb35c34caf21768741e81826c0bbd7225" -dependencies = [ - "base64", - "indexmap", - "line-wrap", - "serde", - "time 0.3.15", - "xml-rs", -] - -[[package]] -name = "plugin" -version = "0.1.0" -dependencies = [ - "bincode", - "plugin_macros", - "serde", -] - -[[package]] -name = "plugin_macros" -version = "0.1.0" -dependencies = [ - "bincode", - "proc-macro2", - "quote", - "serde", - "syn", -] - -[[package]] -name = "plugin_runtime" -version = "0.1.0" -dependencies = [ - "anyhow", - "bincode", - "pollster", - "serde", - "serde_json", - "smol", - "wasi-common", - "wasmtime", - "wasmtime-wasi", -] - -[[package]] -name = "png" -version = "0.16.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" -dependencies = [ - "bitflags", - "crc32fast", - "deflate", - "miniz_oxide 0.3.7", -] - -[[package]] -name = "polling" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899b00b9c8ab553c743b3e11e87c5c7d423b2a2de229ba95b24a756344748011" -dependencies = [ - "autocfg 1.1.0", - "cfg-if 1.0.0", - "libc", - "log", - "wepoll-ffi", - "winapi 0.3.9", -] - -[[package]] -name = "pollster" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7" - -[[package]] -name = "postage" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a63d25391d04a097954b76aba742b6b5b74f213dfe3dbaeeb36e8ddc1c657f0b" -dependencies = [ - "atomic", - "crossbeam-queue", - "futures 0.3.24", - "log", - "pin-project", - "pollster", - "static_assertions", - "thiserror", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "procinfo" -version = "0.1.0" -source = "git+https://github.com/zed-industries/wezterm?rev=5cd757e5f2eb039ed0c6bb6512223e69d5efc64d#5cd757e5f2eb039ed0c6bb6512223e69d5efc64d" -dependencies = [ - "libc", - "log", - "ntapi", - "winapi 0.3.9", -] - -[[package]] -name = "project" -version = "0.1.0" -dependencies = [ - "aho-corasick", - "anyhow", - "async-trait", - "client", - "clock", - "collections", - "db", - "fsevent", - "futures 0.3.24", - "fuzzy", - "git", - "gpui", - "ignore", - "language", - "lazy_static", - "libc", - "log", - "lsp", - "parking_lot 0.11.2", - "postage", - "pulldown-cmark", - "rand 0.8.5", - "regex", - "rocksdb", - "rpc", - "serde", - "serde_json", - "settings", - "sha2 0.10.6", - "similar", - "smol", - "sum_tree", - "tempdir", - "text", - "thiserror", - "toml", - "unindent", - "util", -] - -[[package]] -name = "project_panel" -version = "0.1.0" -dependencies = [ - "context_menu", - "editor", - "futures 0.3.24", - "gpui", - "menu", - "postage", - "project", - "serde_json", - "settings", - "theme", - "unicase", - "util", - "workspace", -] - -[[package]] -name = "project_symbols" -version = "0.1.0" -dependencies = [ - "anyhow", - "editor", - "futures 0.3.24", - "fuzzy", - "gpui", - "language", - "lsp", - "ordered-float", - "picker", - "postage", - "project", - "settings", - "smol", - "text", - "util", - "workspace", -] - -[[package]] -name = "prometheus" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c8babc29389186697fe5a2a4859d697825496b83db5d0b65271cdc0488e88c" -dependencies = [ - "cfg-if 1.0.0", - "fnv", - "lazy_static", - "memchr", - "parking_lot 0.12.1", - "protobuf", - "thiserror", -] - -[[package]] -name = "prost" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020" -dependencies = [ - "bytes 1.2.1", - "prost-derive 0.8.0", -] - -[[package]] -name = "prost" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" -dependencies = [ - "bytes 1.2.1", - "prost-derive 0.9.0", -] - -[[package]] -name = "prost-build" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62941722fb675d463659e49c4f3fe1fe792ff24fe5bbaa9c08cd3b98a1c354f5" -dependencies = [ - "bytes 1.2.1", - "heck 0.3.3", - "itertools", - "lazy_static", - "log", - "multimap", - "petgraph", - "prost 0.9.0", - "prost-types", - "regex", - "tempfile", - "which", -] - -[[package]] -name = "prost-derive" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "600d2f334aa05acb02a755e217ef1ab6dea4d51b58b7846588b747edec04efba" -dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "prost-derive" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" -dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "prost-types" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a" -dependencies = [ - "bytes 1.2.1", - "prost 0.9.0", -] - -[[package]] -name = "protobuf" -version = "2.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" - -[[package]] -name = "psm" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" -dependencies = [ - "cc", -] - -[[package]] -name = "pulldown-cmark" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" -dependencies = [ - "bitflags", - "memchr", - "unicase", -] - -[[package]] -name = "quote" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -dependencies = [ - "fuchsia-cprng", - "libc", - "rand_core 0.3.1", - "rdrand", - "winapi 0.3.9", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.7", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rayon" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" -dependencies = [ - "autocfg 1.1.0", - "crossbeam-deque", - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" -dependencies = [ - "crossbeam-channel 0.5.6", - "crossbeam-deque", - "crossbeam-utils 0.8.12", - "num_cpus", -] - -[[package]] -name = "rctree" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be9e29cb19c8fe84169fcb07f8f11e66bc9e6e0280efd4715c54818296f8a4a8" - -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_users" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" -dependencies = [ - "getrandom 0.2.7", - "redox_syscall", - "thiserror", -] - -[[package]] -name = "regalloc2" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a8d23b35d7177df3b9d31ed8a9ab4bf625c668be77a319d4f5efd4a5257701c" -dependencies = [ - "fxhash", - "log", - "slice-group-by", - "smallvec", -] - -[[package]] -name = "regex" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" - -[[package]] -name = "region" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877e54ea2adcd70d80e9179344c97f93ef0dffd6b03e1f4529e6e83ab2fa9ae0" -dependencies = [ - "bitflags", - "libc", - "mach", - "winapi 0.3.9", -] - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi 0.3.9", -] - -[[package]] -name = "reqwest" -version = "0.11.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" -dependencies = [ - "base64", - "bytes 1.2.1", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite 0.2.9", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - -[[package]] -name = "resvg" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09697862c5c3f940cbaffef91969c62188b5c8ed385b0aef43a5ff01ddc8000f" -dependencies = [ - "jpeg-decoder", - "log", - "pico-args", - "png", - "rgb", - "svgfilters", - "tiny-skia", - "usvg", -] - -[[package]] -name = "rgb" -version = "0.8.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3603b7d71ca82644f79b5a06d1220e9a58ede60bd32255f698cb1af8838b8db3" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi 0.3.9", -] - -[[package]] -name = "rmp" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44519172358fd6d58656c86ab8e7fbc9e1490c3e8f14d35ed78ca0dd07403c9f" -dependencies = [ - "byteorder", - "num-traits", - "paste", -] - -[[package]] -name = "rmpv" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de8813b3a2f95c5138fe5925bfb8784175d88d6bff059ba8ce090aa891319754" -dependencies = [ - "num-traits", - "rmp", -] - -[[package]] -name = "rocksdb" -version = "0.18.0" -source = "git+https://github.com/rust-rocksdb/rust-rocksdb?rev=39dc822dde743b2a26eb160b660e8fbdab079d49#39dc822dde743b2a26eb160b660e8fbdab079d49" -dependencies = [ - "libc", - "librocksdb-sys", -] - -[[package]] -name = "roxmltree" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b" -dependencies = [ - "xmlparser", -] - -[[package]] -name = "rpc" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-lock", - "async-tungstenite", - "base64", - "clock", - "collections", - "ctor", - "env_logger", - "futures 0.3.24", - "gpui", - "parking_lot 0.11.2", - "prost 0.8.0", - "prost-build", - "rand 0.8.5", - "rsa", - "serde", - "smol", - "smol-timeout", - "tempdir", - "tracing", - "util", - "zstd", -] - -[[package]] -name = "rsa" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0aeddcca1082112a6eeb43bf25fd7820b066aaf6eaef776e19d0a1febe38fe" -dependencies = [ - "byteorder", - "digest 0.9.0", - "lazy_static", - "num-bigint-dig", - "num-integer", - "num-iter", - "num-traits", - "pem", - "rand 0.8.5", - "simple_asn1", - "subtle", - "zeroize", -] - -[[package]] -name = "rust-embed" -version = "6.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e26934cd67a1da1165efe61cba4047cc1b4a526019da609fcce13a1000afb5fa" -dependencies = [ - "rust-embed-impl", - "rust-embed-utils", - "walkdir", -] - -[[package]] -name = "rust-embed-impl" -version = "6.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35d7b402e273544cc08e0824aa3404333fab8a90ac43589d3d5b72f4b346e12" -dependencies = [ - "proc-macro2", - "quote", - "rust-embed-utils", - "syn", - "walkdir", -] - -[[package]] -name = "rust-embed-utils" -version = "7.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1669d81dfabd1b5f8e2856b8bbe146c6192b0ba22162edc738ac0a5de18f054" -dependencies = [ - "globset", - "sha2 0.10.6", - "walkdir", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc_version" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.33.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938a344304321a9da4973b9ff4f9f8db9caf4597dfd9dda6a60b523340a0fff0" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes", - "itoa", - "libc", - "linux-raw-sys", - "once_cell", - "winapi 0.3.9", -] - -[[package]] -name = "rustls" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" -dependencies = [ - "base64", - "log", - "ring", - "sct 0.6.1", - "webpki 0.21.4", -] - -[[package]] -name = "rustls" -version = "0.20.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" -dependencies = [ - "log", - "ring", - "sct 0.7.0", - "webpki 0.22.0", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" -dependencies = [ - "base64", -] - -[[package]] -name = "rustybuzz" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab463a295d00f3692e0974a0bfd83c7a9bcd119e27e07c2beecdb1b44a09d10" -dependencies = [ - "bitflags", - "bytemuck", - "smallvec", - "ttf-parser 0.9.0", - "unicode-bidi-mirroring", - "unicode-ccc", - "unicode-general-category", - "unicode-script", -] - -[[package]] -name = "ryu" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" - -[[package]] -name = "safe_arch" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "safemem" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" - -[[package]] -name = "salsa20" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecbd2eb639fd7cab5804a0837fe373cc2172d15437e804c054a9fb885cb923b0" -dependencies = [ - "cipher", -] - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" -dependencies = [ - "lazy_static", - "windows-sys", -] - -[[package]] -name = "schemars" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307" -dependencies = [ - "dyn-clone", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn", -] - -[[package]] -name = "scoped_threadpool" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "scratch" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" - -[[package]] -name = "scrypt" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879588d8f90906e73302547e20fffefdd240eb3e0e744e142321f5d49dea0518" -dependencies = [ - "base64ct", - "hmac 0.11.0", - "password-hash", - "pbkdf2", - "salsa20", - "sha2 0.9.9", -] - -[[package]] -name = "sct" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "sct" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - -[[package]] -name = "search" -version = "0.1.0" -dependencies = [ - "anyhow", - "collections", - "editor", - "gpui", - "language", - "log", - "menu", - "postage", - "project", - "serde", - "serde_json", - "settings", - "smallvec", - "theme", - "unindent", - "util", - "workspace", -] - -[[package]] -name = "security-framework" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - -[[package]] -name = "seq-macro" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9f47faea3cad316faa914d013d24f471cd90bfca1a0c70f05a3f42c6441e99" - -[[package]] -name = "serde" -version = "1.0.145" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.145" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_derive_internals" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_fmt" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2963a69a2b3918c1dc75a45a18bd3fcd1120e31d3f59deb1b2f9b5d5ffb8baa4" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_json" -version = "1.0.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" -dependencies = [ - "indexmap", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_path_to_error" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "184c643044780f7ceb59104cef98a5a6f12cb2288a7bc701ab93a362b49fd47d" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_repr" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_yaml" -version = "0.8.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" -dependencies = [ - "indexmap", - "ryu", - "serde", - "yaml-rust", -] - -[[package]] -name = "servo-fontconfig" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e3e22fe5fd73d04ebf0daa049d3efe3eae55369ce38ab16d07ddd9ac5c217c" -dependencies = [ - "libc", - "servo-fontconfig-sys", -] - -[[package]] -name = "servo-fontconfig-sys" -version = "5.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36b879db9892dfa40f95da1c38a835d41634b825fbd8c4c418093d53c24b388" -dependencies = [ - "expat-sys", - "freetype-sys", - "pkg-config", -] - -[[package]] -name = "settings" -version = "0.1.0" -dependencies = [ - "anyhow", - "assets", - "collections", - "gpui", - "json_comments", - "schemars", - "serde", - "serde_json", - "serde_path_to_error", - "theme", - "toml", - "util", -] - -[[package]] -name = "sha-1" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha-1" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" -dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.10.5", -] - -[[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.10.5", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" -dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.10.5", -] - -[[package]] -name = "sharded-slab" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shellexpand" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4" -dependencies = [ - "dirs 4.0.0", -] - -[[package]] -name = "shlex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" - -[[package]] -name = "signal-hook" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" -dependencies = [ - "libc", - "mio 0.6.23", - "mio-uds", - "signal-hook", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" -dependencies = [ - "libc", -] - -[[package]] -name = "similar" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad1d488a557b235fc46dae55512ffbfc429d2482b08b4d9435ab07384ca8aec" - -[[package]] -name = "simple_asn1" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eb4ea60fb301dc81dfc113df680571045d375ab7345d171c5dc7d7e13107a80" -dependencies = [ - "chrono", - "num-bigint", - "num-traits", - "thiserror", -] - -[[package]] -name = "simplecss" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d" -dependencies = [ - "log", -] - -[[package]] -name = "simplelog" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bc0ffd69814a9b251d43afcabf96dad1b29f5028378056257be9e3fecc9f720" -dependencies = [ - "chrono", - "log", - "termcolor", -] - -[[package]] -name = "siphasher" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" - -[[package]] -name = "slab" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" -dependencies = [ - "autocfg 1.1.0", -] - -[[package]] -name = "slice-group-by" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" - -[[package]] -name = "sluice" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" -dependencies = [ - "async-channel", - "futures-core", - "futures-io", -] - -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "smol" -version = "1.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cf3b5351f3e783c1d79ab5fc604eeed8b8ae9abd36b166e8b87a089efd85e4" -dependencies = [ - "async-channel", - "async-executor", - "async-fs", - "async-io", - "async-lock", - "async-net", - "async-process", - "blocking", - "futures-lite", - "once_cell", -] - -[[package]] -name = "smol-timeout" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "847d777e2c6c166bad26264479e80a9820f3d364fcb4a0e23cd57bbfa8e94961" -dependencies = [ - "async-io", - "pin-project-lite 0.1.12", -] - -[[package]] -name = "snippet" -version = "0.1.0" -dependencies = [ - "anyhow", - "smallvec", -] - -[[package]] -name = "socket2" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" -dependencies = [ - "libc", - "winapi 0.3.9", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spsc-buffer" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be6c3f39c37a4283ee4b43d1311c828f2e1fb0541e76ea0cb1a2abd9ef2f5b3b" - -[[package]] -name = "sqlformat" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87e292b4291f154971a43c3774364e2cbcaec599d3f5bf6fa9d122885dbc38a" -dependencies = [ - "itertools", - "nom", - "unicode_categories", -] - -[[package]] -name = "sqlx" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9249290c05928352f71c077cc44a464d880c63f26f7534728cca008e135c0428" -dependencies = [ - "sqlx-core", - "sqlx-macros", -] - -[[package]] -name = "sqlx-core" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbc16ddba161afc99e14d1713a453747a2b07fc097d2009f4c300ec99286105" -dependencies = [ - "ahash", - "atoi", - "base64", - "bitflags", - "byteorder", - "bytes 1.2.1", - "crc", - "crossbeam-queue", - "dirs 4.0.0", - "dotenvy", - "either", - "event-listener", - "futures-channel", - "futures-core", - "futures-intrusive", - "futures-util", - "hashlink", - "hex", - "hkdf", - "hmac 0.12.1", - "indexmap", - "itoa", - "libc", - "log", - "md-5", - "memchr", - "once_cell", - "paste", - "percent-encoding", - "rand 0.8.5", - "rustls 0.20.6", - "rustls-pemfile", - "serde", - "serde_json", - "sha1", - "sha2 0.10.6", - "smallvec", - "sqlformat", - "sqlx-rt", - "stringprep", - "thiserror", - "time 0.3.15", - "tokio-stream", - "url", - "uuid 1.2.1", - "webpki-roots 0.22.5", - "whoami", -] - -[[package]] -name = "sqlx-macros" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b850fa514dc11f2ee85be9d055c512aa866746adfacd1cb42d867d68e6a5b0d9" -dependencies = [ - "dotenvy", - "either", - "heck 0.4.0", - "once_cell", - "proc-macro2", - "quote", - "sha2 0.10.6", - "sqlx-core", - "sqlx-rt", - "syn", - "url", -] - -[[package]] -name = "sqlx-rt" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24c5b2d25fa654cc5f841750b8e1cdedbe21189bf9a9382ee90bfa9dd3562396" -dependencies = [ - "once_cell", - "tokio", - "tokio-rustls", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "stringprep" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "sum_tree" -version = "0.1.0" -dependencies = [ - "arrayvec 0.7.2", - "ctor", - "env_logger", - "log", - "rand 0.8.5", -] - -[[package]] -name = "sval" -version = "1.0.0-alpha.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45f6ee7c7b87caf59549e9fe45d6a69c75c8019e79e212a835c5da0e92f0ba08" -dependencies = [ - "serde", -] - -[[package]] -name = "svg_fmt" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2" - -[[package]] -name = "svgfilters" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb0dce2fee79ac40c21dafba48565ff7a5fa275e23ffe9ce047a40c9574ba34e" -dependencies = [ - "float-cmp", - "rgb", -] - -[[package]] -name = "svgtypes" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c536faaff1a10837cfe373142583f6e27d81e96beba339147e77b67c9f260ff" -dependencies = [ - "float-cmp", - "siphasher", -] - -[[package]] -name = "syn" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" - -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", -] - -[[package]] -name = "system-interface" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e09bb3fb4e02ec4b87e182ea9718fadbc0fa3e50085b40a9af9690572b67f9e" -dependencies = [ - "atty", - "bitflags", - "cap-fs-ext", - "cap-std", - "io-lifetimes", - "rustix", - "winapi 0.3.9", - "winx", -] - -[[package]] -name = "target-lexicon" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02424087780c9b71cc96799eaeddff35af2bc513278cda5c99fc1f5d026d3c1" - -[[package]] -name = "tempdir" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" -dependencies = [ - "rand 0.4.6", - "remove_dir_all", -] - -[[package]] -name = "tempfile" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" -dependencies = [ - "cfg-if 1.0.0", - "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi 0.3.9", -] - -[[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "terminal" -version = "0.1.0" -dependencies = [ - "alacritty_terminal", - "anyhow", - "client", - "context_menu", - "dirs 4.0.0", - "editor", - "futures 0.3.24", - "gpui", - "itertools", - "lazy_static", - "libc", - "mio-extras", - "ordered-float", - "procinfo", - "project", - "rand 0.8.5", - "serde", - "settings", - "shellexpand", - "smallvec", - "smol", - "theme", - "thiserror", - "util", - "workspace", -] - -[[package]] -name = "text" -version = "0.1.0" -dependencies = [ - "anyhow", - "arrayvec 0.7.2", - "bromberg_sl2", - "clock", - "collections", - "ctor", - "digest 0.9.0", - "env_logger", - "gpui", - "lazy_static", - "log", - "parking_lot 0.11.2", - "postage", - "rand 0.8.5", - "regex", - "smallvec", - "sum_tree", - "util", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "textwrap" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" - -[[package]] -name = "theme" -version = "0.1.0" -dependencies = [ - "anyhow", - "gpui", - "indexmap", - "parking_lot 0.11.2", - "serde", - "serde_json", - "serde_path_to_error", - "toml", -] - -[[package]] -name = "theme_selector" -version = "0.1.0" -dependencies = [ - "editor", - "fuzzy", - "gpui", - "log", - "parking_lot 0.11.2", - "picker", - "postage", - "settings", - "smol", - "theme", - "workspace", -] - -[[package]] -name = "thiserror" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thousands" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" - -[[package]] -name = "thread_local" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" -dependencies = [ - "once_cell", -] - -[[package]] -name = "tiff" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437" -dependencies = [ - "jpeg-decoder", - "miniz_oxide 0.4.4", - "weezl", -] - -[[package]] -name = "time" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi 0.3.9", -] - -[[package]] -name = "time" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d634a985c4d4238ec39cacaed2e7ae552fbd3c476b552c1deac3021b7d7eaf0c" -dependencies = [ - "itoa", - "libc", - "num_threads", - "serde", - "time-macros", -] - -[[package]] -name = "time-macros" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" - -[[package]] -name = "tiny-skia" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf81f2900d2e235220e6f31ec9f63ade6a7f59090c556d74fe949bb3b15e9fe" -dependencies = [ - "arrayref", - "arrayvec 0.5.2", - "bytemuck", - "cfg-if 1.0.0", - "png", - "safe_arch", -] - -[[package]] -name = "tiny_http" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce51b50006056f590c9b7c3808c3bd70f0d1101666629713866c227d6e58d39" -dependencies = [ - "ascii", - "chrono", - "chunked_transfer", - "log", - "url", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - -[[package]] -name = "tokio" -version = "1.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" -dependencies = [ - "autocfg 1.1.0", - "bytes 1.2.1", - "libc", - "memchr", - "mio 0.8.4", - "num_cpus", - "parking_lot 0.12.1", - "pin-project-lite 0.2.9", - "signal-hook-registry", - "socket2", - "tokio-macros", - "winapi 0.3.9", -] - -[[package]] -name = "tokio-io" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" -dependencies = [ - "bytes 0.4.12", - "futures 0.1.31", - "log", -] - -[[package]] -name = "tokio-io-timeout" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" -dependencies = [ - "pin-project-lite 0.2.9", - "tokio", -] - -[[package]] -name = "tokio-macros" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.23.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" -dependencies = [ - "rustls 0.20.6", - "tokio", - "webpki 0.22.0", -] - -[[package]] -name = "tokio-stream" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6edf2d6bc038a43d31353570e27270603f4648d18f5ed10c0e179abe43255af" -dependencies = [ - "futures-core", - "pin-project-lite 0.2.9", - "tokio", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite 0.17.3", -] - -[[package]] -name = "tokio-util" -version = "0.6.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" -dependencies = [ - "bytes 1.2.1", - "futures-core", - "futures-sink", - "log", - "pin-project-lite 0.2.9", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" -dependencies = [ - "bytes 1.2.1", - "futures-core", - "futures-io", - "futures-sink", - "pin-project-lite 0.2.9", - "tokio", - "tracing", -] - -[[package]] -name = "toml" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" -dependencies = [ - "serde", -] - -[[package]] -name = "tonic" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff08f4649d10a70ffa3522ca559031285d8e421d727ac85c60825761818f5d0a" -dependencies = [ - "async-stream", - "async-trait", - "base64", - "bytes 1.2.1", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-timeout", - "percent-encoding", - "pin-project", - "prost 0.9.0", - "prost-derive 0.9.0", - "tokio", - "tokio-stream", - "tokio-util 0.6.10", - "tower", - "tower-layer", - "tower-service", - "tracing", - "tracing-futures", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "indexmap", - "pin-project", - "pin-project-lite 0.2.9", - "rand 0.8.5", - "slab", - "tokio", - "tokio-util 0.7.4", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" -dependencies = [ - "bitflags", - "bytes 1.2.1", - "futures-core", - "futures-util", - "http", - "http-body", - "http-range-header", - "pin-project-lite 0.2.9", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if 1.0.0", - "log", - "pin-project-lite 0.2.9", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-core" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - -[[package]] -name = "tracing-log" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" -dependencies = [ - "lazy_static", - "log", - "tracing-core", -] - -[[package]] -name = "tracing-serde" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" -dependencies = [ - "serde", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "serde", - "serde_json", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", - "tracing-serde", -] - -[[package]] -name = "tree-sitter" -version = "0.20.8" -source = "git+https://github.com/tree-sitter/tree-sitter?rev=366210ae925d7ea0891bc7a0c738f60c77c04d7b#366210ae925d7ea0891bc7a0c738f60c77c04d7b" -dependencies = [ - "cc", - "regex", -] - -[[package]] -name = "tree-sitter-c" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cca211f4827d4b4dc79f388bf67b6fa3bc8a8cfa642161ef24f99f371ba34c7b" -dependencies = [ - "cc", - "tree-sitter", -] - -[[package]] -name = "tree-sitter-cpp" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a869e3c5cef4e5db4e9ab16a8dc84d73010e60ada14cdc60d2f6d8aed17779d" -dependencies = [ - "cc", - "tree-sitter", -] - -[[package]] -name = "tree-sitter-css" -version = "0.19.0" -source = "git+https://github.com/tree-sitter/tree-sitter-css?rev=769203d0f9abe1a9a691ac2b9fe4bb4397a73c51#769203d0f9abe1a9a691ac2b9fe4bb4397a73c51" -dependencies = [ - "cc", - "tree-sitter", -] - -[[package]] -name = "tree-sitter-elixir" -version = "0.19.0" -source = "git+https://github.com/elixir-lang/tree-sitter-elixir?rev=05e3631c6a0701c1fa518b0fee7be95a2ceef5e2#05e3631c6a0701c1fa518b0fee7be95a2ceef5e2" -dependencies = [ - "cc", - "tree-sitter", -] - -[[package]] -name = "tree-sitter-go" -version = "0.19.1" -source = "git+https://github.com/tree-sitter/tree-sitter-go?rev=aeb2f33b366fd78d5789ff104956ce23508b85db#aeb2f33b366fd78d5789ff104956ce23508b85db" -dependencies = [ - "cc", - "tree-sitter", -] - -[[package]] -name = "tree-sitter-html" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "184e6b77953a354303dc87bf5fe36558c83569ce92606e7b382a0dc1b7443443" -dependencies = [ - "cc", - "tree-sitter", -] - -[[package]] -name = "tree-sitter-javascript" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2490fab08630b2c8943c320f7b63473cbf65511c8d83aec551beb9b4375906ed" -dependencies = [ - "cc", - "tree-sitter", -] - -[[package]] -name = "tree-sitter-json" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90b04c4e1a92139535eb9fca4ec8fa9666cc96b618005d3ae35f3c957fa92f92" -dependencies = [ - "cc", - "tree-sitter", -] - -[[package]] -name = "tree-sitter-json" -version = "0.20.0" -source = "git+https://github.com/tree-sitter/tree-sitter-json?rev=137e1ce6a02698fc246cdb9c6b886ed1de9a1ed8#137e1ce6a02698fc246cdb9c6b886ed1de9a1ed8" -dependencies = [ - "cc", - "tree-sitter", -] - -[[package]] -name = "tree-sitter-markdown" -version = "0.0.1" -source = "git+https://github.com/MDeiml/tree-sitter-markdown?rev=330ecab87a3e3a7211ac69bbadc19eabecdb1cca#330ecab87a3e3a7211ac69bbadc19eabecdb1cca" -dependencies = [ - "cc", - "tree-sitter", -] - -[[package]] -name = "tree-sitter-python" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dda114f58048f5059dcf158aff691dffb8e113e6d2b50d94263fd68711975287" -dependencies = [ - "cc", - "tree-sitter", -] - -[[package]] -name = "tree-sitter-rust" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13470fafb7327a3acf96f5bc1013b5539a899a182f01c59b5af53f6b93195717" -dependencies = [ - "cc", - "tree-sitter", -] - -[[package]] -name = "tree-sitter-toml" -version = "0.5.1" -source = "git+https://github.com/tree-sitter/tree-sitter-toml?rev=342d9be207c2dba869b9967124c679b5e6fd0ebe#342d9be207c2dba869b9967124c679b5e6fd0ebe" -dependencies = [ - "cc", - "tree-sitter", -] - -[[package]] -name = "tree-sitter-typescript" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e8ed0ecb931cdff13c6a13f45ccd615156e2779d9ffb0395864e05505e6e86d" -dependencies = [ - "cc", - "tree-sitter", -] - -[[package]] -name = "try-lock" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" - -[[package]] -name = "ttf-parser" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ddb402ac6c2af6f7a2844243887631c4e94b51585b229fcfddb43958cd55ca" - -[[package]] -name = "ttf-parser" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ae2f58a822f08abdaf668897e96a5656fe72f5a9ce66422423e8849384872e6" - -[[package]] -name = "tungstenite" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ad3713a14ae247f22a728a0456a545df14acf3867f905adff84be99e23b3ad1" -dependencies = [ - "base64", - "byteorder", - "bytes 1.2.1", - "http", - "httparse", - "log", - "rand 0.8.5", - "sha-1 0.9.8", - "thiserror", - "url", - "utf-8", -] - -[[package]] -name = "tungstenite" -version = "0.17.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" -dependencies = [ - "base64", - "byteorder", - "bytes 1.2.1", - "http", - "httparse", - "log", - "rand 0.8.5", - "sha-1 0.10.0", - "thiserror", - "url", - "utf-8", -] - -[[package]] -name = "typenum" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" - -[[package]] -name = "ucd-trie" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" - -[[package]] -name = "unicase" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" - -[[package]] -name = "unicode-bidi-mirroring" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694" - -[[package]] -name = "unicode-ccc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1" - -[[package]] -name = "unicode-general-category" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9af028e052a610d99e066b33304625dea9613170a2563314490a4e6ec5cf7f" - -[[package]] -name = "unicode-ident" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-script" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc" - -[[package]] -name = "unicode-segmentation" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" - -[[package]] -name = "unicode-vo" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" - -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "unicode_categories" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" - -[[package]] -name = "unindent" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ee9362deb4a96cef4d437d1ad49cffc9b9e92d202b6995674e928ce684f112" - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "url" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "usvg" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8352f317d8f9a918ba5154797fb2a93e2730244041cf7d5be35148266adfa5" -dependencies = [ - "base64", - "data-url", - "flate2", - "fontdb", - "kurbo", - "log", - "memmap2", - "pico-args", - "rctree", - "roxmltree", - "rustybuzz", - "simplecss", - "siphasher", - "svgtypes", - "ttf-parser 0.12.3", - "unicode-bidi", - "unicode-script", - "unicode-vo", - "xmlwriter", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8parse" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" - -[[package]] -name = "util" -version = "0.1.0" -dependencies = [ - "anyhow", - "futures 0.3.24", - "git2", - "lazy_static", - "log", - "rand 0.8.5", - "serde_json", - "tempdir", -] - -[[package]] -name = "uuid" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" -dependencies = [ - "getrandom 0.2.7", -] - -[[package]] -name = "uuid" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" -dependencies = [ - "getrandom 0.2.7", -] - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "value-bag" -version = "1.0.0-alpha.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" -dependencies = [ - "ctor", - "erased-serde", - "serde", - "serde_fmt", - "sval", - "version_check", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "vim" -version = "0.1.0" -dependencies = [ - "assets", - "async-compat", - "async-trait", - "collections", - "command_palette", - "editor", - "gpui", - "indoc", - "itertools", - "language", - "log", - "nvim-rs", - "project", - "search", - "serde", - "serde_json", - "settings", - "tokio", - "util", - "workspace", -] - -[[package]] -name = "vte" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983" -dependencies = [ - "utf8parse", - "vte_generate_state_changes", -] - -[[package]] -name = "vte_generate_state_changes" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "waker-fn" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" - -[[package]] -name = "walkdir" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" -dependencies = [ - "same-file", - "winapi 0.3.9", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasi-cap-std-sync" -version = "0.38.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f086c5026d2fc3b268d138e65373f46422cc810f46d6e0776859c5027cb18728" -dependencies = [ - "anyhow", - "async-trait", - "cap-fs-ext", - "cap-rand", - "cap-std", - "cap-time-ext", - "fs-set-times", - "io-extras", - "io-lifetimes", - "is-terminal", - "lazy_static", - "rustix", - "system-interface", - "tracing", - "wasi-common", - "winapi 0.3.9", -] - -[[package]] -name = "wasi-common" -version = "0.38.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e8844fede1c3787cc08853872f47e8bd91f6c939c7406bc7a5dba496b260c08" -dependencies = [ - "anyhow", - "bitflags", - "cap-rand", - "cap-std", - "io-extras", - "rustix", - "thiserror", - "tracing", - "wiggle", - "winapi 0.3.9", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" -dependencies = [ - "cfg-if 1.0.0", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" -dependencies = [ - "cfg-if 1.0.0", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" - -[[package]] -name = "wasm-encoder" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64ac98d5d61192cc45c701b7e4bd0b9aff91e2edfc7a088406cfe2288581e2c" -dependencies = [ - "leb128", -] - -[[package]] -name = "wasmparser" -version = "0.85.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "570460c58b21e9150d2df0eaaedbb7816c34bcec009ae0dcc976e40ba81463e7" -dependencies = [ - "indexmap", -] - -[[package]] -name = "wasmtime" -version = "0.38.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f50eadf868ab6a04b7b511460233377d0bfbb92e417b2f6a98b98fef2e098f5" -dependencies = [ - "anyhow", - "async-trait", - "backtrace", - "bincode", - "cfg-if 1.0.0", - "indexmap", - "lazy_static", - "libc", - "log", - "object 0.28.4", - "once_cell", - "paste", - "psm", - "rayon", - "region", - "serde", - "target-lexicon", - "wasmparser", - "wasmtime-cache", - "wasmtime-cranelift", - "wasmtime-environ", - "wasmtime-fiber", - "wasmtime-jit", - "wasmtime-runtime", - "wat", - "winapi 0.3.9", -] - -[[package]] -name = "wasmtime-cache" -version = "0.38.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1df23c642e1376892f3b72f311596976979cbf8b85469680cdd3a8a063d12a2" -dependencies = [ - "anyhow", - "base64", - "bincode", - "directories-next", - "file-per-thread-logger", - "log", - "rustix", - "serde", - "sha2 0.9.9", - "toml", - "winapi 0.3.9", - "zstd", -] - -[[package]] -name = "wasmtime-cranelift" -version = "0.38.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f264ff6b4df247d15584f2f53d009fbc90032cfdc2605b52b961bffc71b6eccd" -dependencies = [ - "anyhow", - "cranelift-codegen", - "cranelift-entity", - "cranelift-frontend", - "cranelift-native", - "cranelift-wasm", - "gimli", - "log", - "more-asserts", - "object 0.28.4", - "target-lexicon", - "thiserror", - "wasmparser", - "wasmtime-environ", -] - -[[package]] -name = "wasmtime-environ" -version = "0.38.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "839d2820e4b830f4b9e7aa08d4c0acabf4a5036105d639f6dfa1c6891c73bdc6" -dependencies = [ - "anyhow", - "cranelift-entity", - "gimli", - "indexmap", - "log", - "more-asserts", - "object 0.28.4", - "serde", - "target-lexicon", - "thiserror", - "wasmparser", - "wasmtime-types", -] - -[[package]] -name = "wasmtime-fiber" -version = "0.38.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3248be3c4911233535356025f6562193614a40155ee9094bb6a2b43f0dc82803" -dependencies = [ - "cc", - "rustix", - "winapi 0.3.9", -] - -[[package]] -name = "wasmtime-jit" -version = "0.38.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef0a0bcbfa18b946d890078ba0e1bc76bcc53eccfb40806c0020ec29dcd1bd49" -dependencies = [ - "addr2line", - "anyhow", - "bincode", - "cfg-if 1.0.0", - "cpp_demangle", - "gimli", - "ittapi-rs", - "log", - "object 0.28.4", - "region", - "rustc-demangle", - "rustix", - "serde", - "target-lexicon", - "thiserror", - "wasmtime-environ", - "wasmtime-jit-debug", - "wasmtime-runtime", - "winapi 0.3.9", -] - -[[package]] -name = "wasmtime-jit-debug" -version = "0.38.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4779d976206c458edd643d1ac622b6c37e4a0800a8b1d25dfbf245ac2f2cac" -dependencies = [ - "lazy_static", - "object 0.28.4", - "rustix", -] - -[[package]] -name = "wasmtime-runtime" -version = "0.38.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7eb6ffa169eb5dcd18ac9473c817358cd57bc62c244622210566d473397954a" -dependencies = [ - "anyhow", - "backtrace", - "cc", - "cfg-if 1.0.0", - "indexmap", - "libc", - "log", - "mach", - "memfd", - "memoffset", - "more-asserts", - "rand 0.8.5", - "region", - "rustix", - "thiserror", - "wasmtime-environ", - "wasmtime-fiber", - "wasmtime-jit-debug", - "winapi 0.3.9", -] - -[[package]] -name = "wasmtime-types" -version = "0.38.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d932b0ac5336f7308d869703dd225610a6a3aeaa8e968c52b43eed96cefb1c2" -dependencies = [ - "cranelift-entity", - "serde", - "thiserror", - "wasmparser", -] - -[[package]] -name = "wasmtime-wasi" -version = "0.38.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b68b7d77fb6f2975a6fe6cc4d0015d6b0cebb65c39fce1dd4cc00880dbf7789c" -dependencies = [ - "anyhow", - "wasi-cap-std-sync", - "wasi-common", - "wasmtime", - "wiggle", -] - -[[package]] -name = "wast" -version = "35.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ef140f1b49946586078353a453a1d28ba90adfc54dde75710bc1931de204d68" -dependencies = [ - "leb128", -] - -[[package]] -name = "wast" -version = "47.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b98502f3978adea49551e801a6687678e6015317d7d9470a67fe813393f2a8" -dependencies = [ - "leb128", - "memchr", - "unicode-width", - "wasm-encoder", -] - -[[package]] -name = "wat" -version = "1.0.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aab4e20c60429fbba9670a6cae0fff9520046ba0aa3e6d0b1cd2653bea14898" -dependencies = [ - "wast 47.0.1", -] - -[[package]] -name = "web-sys" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" -dependencies = [ - "webpki 0.21.4", -] - -[[package]] -name = "webpki-roots" -version = "0.22.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be" -dependencies = [ - "webpki 0.22.0", -] - -[[package]] -name = "weezl" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" - -[[package]] -name = "wepoll-ffi" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" -dependencies = [ - "cc", -] - -[[package]] -name = "which" -version = "4.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" -dependencies = [ - "either", - "libc", - "once_cell", -] - -[[package]] -name = "whoami" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6631b6a2fd59b1841b622e8f1a7ad241ef0a46f2d580464ce8140ac94cbd571" -dependencies = [ - "bumpalo", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wiggle" -version = "0.38.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67dadac11343d2aabc8a906a0db0aaf7cb5046ec3d6fffccdaf2847dccdef8d6" -dependencies = [ - "anyhow", - "async-trait", - "bitflags", - "thiserror", - "tracing", - "wasmtime", - "wiggle-macro", -] - -[[package]] -name = "wiggle-generate" -version = "0.38.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63a1dccd6b3fbd9a27417f5d30ce9aa3ee9cf529aad453abbf88a49c5d605b79" -dependencies = [ - "anyhow", - "heck 0.4.0", - "proc-macro2", - "quote", - "shellexpand", - "syn", - "witx", -] - -[[package]] -name = "wiggle-macro" -version = "0.38.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c368d57d9560c34deaa67e06b0953ccf65edb906c525e5a2c866c849b48ec2" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wiggle-generate", -] - -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi 0.3.9", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - -[[package]] -name = "windows_i686_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - -[[package]] -name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" - -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi 0.3.9", -] - -[[package]] -name = "winx" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d5973cb8cd94a77d03ad7e23bbe14889cb29805da1cec0e4aff75e21aebded" -dependencies = [ - "bitflags", - "io-lifetimes", - "winapi 0.3.9", -] - -[[package]] -name = "wio" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" -dependencies = [ - "winapi 0.3.9", -] - -[[package]] -name = "witx" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e366f27a5cabcddb2706a78296a40b8fcc451e1a6aba2fc1d94b4a01bdaaef4b" -dependencies = [ - "anyhow", - "log", - "thiserror", - "wast 35.0.2", -] - -[[package]] -name = "workspace" -version = "0.1.0" -dependencies = [ - "anyhow", - "client", - "clock", - "collections", - "context_menu", - "drag_and_drop", - "futures 0.3.24", - "gpui", - "language", - "log", - "menu", - "parking_lot 0.11.2", - "postage", - "project", - "serde", - "serde_json", - "settings", - "smallvec", - "theme", - "util", -] - -[[package]] -name = "ws2_32-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - -[[package]] -name = "xml-rs" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" - -[[package]] -name = "xmlparser" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "114ba2b24d2167ef6d67d7d04c8cc86522b87f490025f39f0303b7db5bf5e3d8" - -[[package]] -name = "xmlwriter" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" - -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "zed" -version = "0.59.0" -dependencies = [ - "activity_indicator", - "anyhow", - "assets", - "async-compression", - "async-recursion", - "async-trait", - "auto_update", - "backtrace", - "breadcrumbs", - "chat_panel", - "chrono", - "cli", - "client", - "clock", - "collections", - "command_palette", - "contacts_panel", - "contacts_status_item", - "context_menu", - "ctor", - "diagnostics", - "dirs 3.0.2", - "easy-parallel", - "editor", - "env_logger", - "file_finder", - "fsevent", - "futures 0.3.24", - "fuzzy", - "go_to_line", - "gpui", - "ignore", - "image", - "indexmap", - "isahc", - "journal", - "language", - "lazy_static", - "libc", - "log", - "lsp", - "num_cpus", - "outline", - "parking_lot 0.11.2", - "plugin_runtime", - "postage", - "project", - "project_panel", - "project_symbols", - "rand 0.8.5", - "regex", - "rpc", - "rsa", - "rust-embed", - "search", - "serde", - "serde_json", - "serde_path_to_error", - "settings", - "simplelog", - "smallvec", - "smol", - "sum_tree", - "tempdir", - "terminal", - "text", - "theme", - "theme_selector", - "thiserror", - "tiny_http", - "toml", - "tree-sitter", - "tree-sitter-c", - "tree-sitter-cpp", - "tree-sitter-css", - "tree-sitter-elixir", - "tree-sitter-go", - "tree-sitter-html", - "tree-sitter-json 0.20.0", - "tree-sitter-markdown", - "tree-sitter-python", - "tree-sitter-rust", - "tree-sitter-toml", - "tree-sitter-typescript", - "unindent", - "url", - "util", - "vim", - "workspace", -] - -[[package]] -name = "zeroize" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zstd" -version = "0.11.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "5.0.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.1+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fd07cbbc53846d9145dbffdf6dd09a7a0aa52be46741825f5c97bdd4f73f12b" -dependencies = [ - "cc", - "libc", -] diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index 651484afcd..75d0eae5ff 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -9,11 +9,10 @@ } ], "h": "vim::Left", - "backspace": "vim::Left", + "backspace": "vim::Backspace", "j": "vim::Down", "k": "vim::Up", "l": "vim::Right", - "0": "vim::StartOfLine", "$": "vim::EndOfLine", "shift-g": "vim::EndOfDocument", "w": "vim::NextWordStart", @@ -54,6 +53,43 @@ "around": true } } + ], + "0": "vim::StartOfLine", // When no number operator present, use start of line motion + "1": [ + "vim::Number", + 1 + ], + "2": [ + "vim::Number", + 2 + ], + "3": [ + "vim::Number", + 3 + ], + "4": [ + "vim::Number", + 4 + ], + "5": [ + "vim::Number", + 5 + ], + "6": [ + "vim::Number", + 6 + ], + "7": [ + "vim::Number", + 7 + ], + "8": [ + "vim::Number", + 8 + ], + "9": [ + "vim::Number", + 9 ] } }, @@ -114,6 +150,15 @@ ] } }, + { + "context": "Editor && vim_operator == n", + "bindings": { + "0": [ + "vim::Number", + 0 + ] + } + }, { "context": "Editor && vim_operator == g", "bindings": { @@ -128,13 +173,6 @@ { "context": "Editor && vim_operator == c", "bindings": { - "w": "vim::ChangeWord", - "shift-w": [ - "vim::ChangeWord", - { - "ignorePunctuation": true - } - ], "c": "vim::CurrentLine" } }, @@ -160,8 +198,7 @@ "ignorePunctuation": true } ], - "s": "vim::Sentence", - "p": "vim::Paragraph" + "s": "vim::Sentence" } }, { diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 7e84c70601..bc17588133 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -4961,6 +4961,7 @@ async fn test_random_collaboration( cx.font_cache(), cx.leak_detector(), next_entity_id, + cx.function_name.clone(), ); let host = server.create_client(&mut host_cx, "host").await; let host_project = host_cx.update(|cx| { @@ -5194,6 +5195,7 @@ async fn test_random_collaboration( cx.font_cache(), cx.leak_detector(), next_entity_id, + cx.function_name.clone(), ); deterministic.start_waiting(); diff --git a/crates/editor/src/test.rs b/crates/editor/src/test.rs index 75bc5fe76a..acee1216d1 100644 --- a/crates/editor/src/test.rs +++ b/crates/editor/src/test.rs @@ -4,19 +4,25 @@ use crate::{ AnchorRangeExt, Autoscroll, DisplayPoint, Editor, EditorMode, MultiBuffer, ToPoint, }; use anyhow::Result; +use collections::BTreeMap; use futures::{Future, StreamExt}; use gpui::{ json, keymap::Keystroke, AppContext, ModelContext, ModelHandle, ViewContext, ViewHandle, }; use indoc::indoc; +use itertools::Itertools; use language::{point_to_lsp, Buffer, BufferSnapshot, FakeLspAdapter, Language, LanguageConfig}; use lsp::{notification, request}; +use parking_lot::RwLock; use project::Project; use settings::Settings; use std::{ any::TypeId, ops::{Deref, DerefMut, Range}, - sync::Arc, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }, }; use util::{ assert_set_eq, set_eq, @@ -85,6 +91,7 @@ pub struct EditorTestContext<'a> { pub cx: &'a mut gpui::TestAppContext, pub window_id: usize, pub editor: ViewHandle, + pub assertion_context: AssertionContextManager, } impl<'a> EditorTestContext<'a> { @@ -106,9 +113,14 @@ impl<'a> EditorTestContext<'a> { cx, window_id, editor, + assertion_context: AssertionContextManager::new(), } } + pub fn add_assertion_context(&self, context: String) -> ContextHandle { + self.assertion_context.add_context(context) + } + pub fn condition( &self, predicate: impl FnMut(&Editor, &AppContext) -> bool, @@ -394,6 +406,7 @@ impl<'a> EditorLspTestContext<'a> { cx, window_id, editor, + assertion_context: AssertionContextManager::new(), }, lsp, workspace, @@ -507,3 +520,45 @@ impl<'a> DerefMut for EditorLspTestContext<'a> { &mut self.cx } } + +#[derive(Clone)] +pub struct AssertionContextManager { + id: Arc, + contexts: Arc>>, +} + +impl AssertionContextManager { + pub fn new() -> Self { + Self { + id: Arc::new(AtomicUsize::new(0)), + contexts: Arc::new(RwLock::new(BTreeMap::new())), + } + } + + pub fn add_context(&self, context: String) -> ContextHandle { + let id = self.id.fetch_add(1, Ordering::Relaxed); + let mut contexts = self.contexts.write(); + contexts.insert(id, context); + ContextHandle { + id, + manager: self.clone(), + } + } + + pub fn context(&self) -> String { + let contexts = self.contexts.read(); + format!("\n{}\n", contexts.values().join("\n")) + } +} + +pub struct ContextHandle { + id: usize, + manager: AssertionContextManager, +} + +impl Drop for ContextHandle { + fn drop(&mut self) { + let mut contexts = self.manager.contexts.write(); + contexts.remove(&self.id); + } +} diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 308ea6c831..1da9f35dc8 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -182,6 +182,7 @@ pub struct TestAppContext { cx: Rc>, foreground_platform: Rc, condition_duration: Option, + pub function_name: String, } pub struct WindowInputHandler { @@ -437,6 +438,7 @@ impl TestAppContext { font_cache: Arc, leak_detector: Arc>, first_entity_id: usize, + function_name: String, ) -> Self { let mut cx = MutableAppContext::new( foreground, @@ -456,6 +458,7 @@ impl TestAppContext { cx: Rc::new(RefCell::new(cx)), foreground_platform, condition_duration: None, + function_name, }; cx.cx.borrow_mut().weak_self = Some(Rc::downgrade(&cx.cx)); cx diff --git a/crates/gpui/src/test.rs b/crates/gpui/src/test.rs index 4122ad09b7..ffabafe725 100644 --- a/crates/gpui/src/test.rs +++ b/crates/gpui/src/test.rs @@ -37,6 +37,7 @@ pub fn run_test( u64, bool, )), + fn_name: String, ) { // let _profiler = dhat::Profiler::new_heap(); @@ -78,6 +79,7 @@ pub fn run_test( font_cache.clone(), leak_detector.clone(), 0, + fn_name.clone(), ); cx.update(|cx| { test_fn( diff --git a/crates/gpui_macros/src/gpui_macros.rs b/crates/gpui_macros/src/gpui_macros.rs index a60d385e8f..79bc808154 100644 --- a/crates/gpui_macros/src/gpui_macros.rs +++ b/crates/gpui_macros/src/gpui_macros.rs @@ -117,6 +117,7 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream { cx.font_cache().clone(), cx.leak_detector(), #first_entity_id, + stringify!(#outer_fn_name).to_string(), ); )); cx_teardowns.extend(quote!( @@ -149,7 +150,8 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream { #cx_vars cx.foreground().run(#inner_fn_name(#inner_fn_args)); #cx_teardowns - } + }, + stringify!(#outer_fn_name).to_string(), ); } } @@ -187,7 +189,8 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream { #num_iterations as u64, #starting_seed as u64, #max_retries, - &mut |cx, _, _, seed, is_last_iteration| #inner_fn_name(#inner_fn_args) + &mut |cx, _, _, seed, is_last_iteration| #inner_fn_name(#inner_fn_args), + stringify!(#outer_fn_name).to_string(), ); } } diff --git a/crates/vim/Cargo.toml b/crates/vim/Cargo.toml index 6b72383fdb..3acd3f3a90 100644 --- a/crates/vim/Cargo.toml +++ b/crates/vim/Cargo.toml @@ -33,6 +33,7 @@ workspace = { path = "../workspace" } [dev-dependencies] indoc = "1.0.4" +parking_lot = "0.11.1" editor = { path = "../editor", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } diff --git a/crates/vim/src/motion.rs b/crates/vim/src/motion.rs index b4457e261b..5d25ece562 100644 --- a/crates/vim/src/motion.rs +++ b/crates/vim/src/motion.rs @@ -18,6 +18,7 @@ use crate::{ #[derive(Copy, Clone, Debug)] pub enum Motion { Left, + Backspace, Down, Up, Right, @@ -58,6 +59,7 @@ actions!( vim, [ Left, + Backspace, Down, Up, Right, @@ -74,6 +76,7 @@ impl_actions!(vim, [NextWordStart, NextWordEnd, PreviousWordStart]); pub fn init(cx: &mut MutableAppContext) { cx.add_action(|_: &mut Workspace, _: &Left, cx: _| motion(Motion::Left, cx)); + cx.add_action(|_: &mut Workspace, _: &Backspace, cx: _| motion(Motion::Backspace, cx)); cx.add_action(|_: &mut Workspace, _: &Down, cx: _| motion(Motion::Down, cx)); cx.add_action(|_: &mut Workspace, _: &Up, cx: _| motion(Motion::Up, cx)); cx.add_action(|_: &mut Workspace, _: &Right, cx: _| motion(Motion::Right, cx)); @@ -106,19 +109,21 @@ pub fn init(cx: &mut MutableAppContext) { ); } -fn motion(motion: Motion, cx: &mut MutableAppContext) { - Vim::update(cx, |vim, cx| { - if let Some(Operator::Namespace(_)) = vim.active_operator() { - vim.pop_operator(cx); - } - }); +pub(crate) fn motion(motion: Motion, cx: &mut MutableAppContext) { + if let Some(Operator::Namespace(_)) = Vim::read(cx).active_operator() { + Vim::update(cx, |vim, cx| vim.pop_operator(cx)); + } + + let times = Vim::update(cx, |vim, cx| vim.pop_number_operator(cx)); + let operator = Vim::read(cx).active_operator(); match Vim::read(cx).state.mode { - Mode::Normal => normal_motion(motion, cx), - Mode::Visual { .. } => visual_motion(motion, cx), + Mode::Normal => normal_motion(motion, operator, times, cx), + Mode::Visual { .. } => visual_motion(motion, times, cx), Mode::Insert => { // Shouldn't execute a motion in insert mode. Ignoring } } + Vim::update(cx, |vim, cx| vim.clear_operator(cx)); } // Motion handling is specified here: @@ -154,6 +159,7 @@ impl Motion { use Motion::*; match self { Left => (left(map, point), SelectionGoal::None), + Backspace => (movement::left(map, point), SelectionGoal::None), Down => movement::down(map, point, goal, true), Up => movement::up(map, point, goal, true), Right => (right(map, point), SelectionGoal::None), @@ -184,10 +190,13 @@ impl Motion { self, map: &DisplaySnapshot, selection: &mut Selection, + times: usize, expand_to_surrounding_newline: bool, ) { - let (head, goal) = self.move_point(map, selection.head(), selection.goal); - selection.set_head(head, goal); + for _ in 0..times { + let (head, goal) = self.move_point(map, selection.head(), selection.goal); + selection.set_head(head, goal); + } if self.linewise() { selection.start = map.prev_line_boundary(selection.start.to_point(map)).1; @@ -272,17 +281,13 @@ fn next_word_end( ignore_punctuation: bool, ) -> DisplayPoint { *point.column_mut() += 1; - dbg!(point); point = movement::find_boundary(map, point, |left, right| { - dbg!(left); let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation); let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation); left_kind != right_kind && left_kind != CharKind::Whitespace }); - dbg!(point); - // find_boundary clips, so if the character after the next character is a newline or at the end of the document, we know // we have backtraced already if !map @@ -293,7 +298,7 @@ fn next_word_end( { *point.column_mut() = point.column().saturating_sub(1); } - dbg!(map.clip_point(point, Bias::Left)) + map.clip_point(point, Bias::Left) } fn previous_word_start( diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index 5345586743..f10227de86 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -10,7 +10,6 @@ use crate::{ state::{Mode, Operator}, Vim, }; -use change::init as change_init; use collections::HashSet; use editor::{Autoscroll, Bias, ClipboardSelection, DisplayPoint}; use gpui::{actions, MutableAppContext, ViewContext}; @@ -48,41 +47,47 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action(insert_line_below); cx.add_action(|_: &mut Workspace, _: &DeleteLeft, cx| { Vim::update(cx, |vim, cx| { - delete_motion(vim, Motion::Left, cx); + let times = vim.pop_number_operator(cx); + delete_motion(vim, Motion::Left, times, cx); }) }); cx.add_action(|_: &mut Workspace, _: &DeleteRight, cx| { Vim::update(cx, |vim, cx| { - delete_motion(vim, Motion::Right, cx); + let times = vim.pop_number_operator(cx); + delete_motion(vim, Motion::Right, times, cx); }) }); cx.add_action(|_: &mut Workspace, _: &ChangeToEndOfLine, cx| { Vim::update(cx, |vim, cx| { - change_motion(vim, Motion::EndOfLine, cx); + let times = vim.pop_number_operator(cx); + change_motion(vim, Motion::EndOfLine, times, cx); }) }); cx.add_action(|_: &mut Workspace, _: &DeleteToEndOfLine, cx| { Vim::update(cx, |vim, cx| { - delete_motion(vim, Motion::EndOfLine, cx); + let times = vim.pop_number_operator(cx); + delete_motion(vim, Motion::EndOfLine, times, cx); }) }); cx.add_action(paste); - - change_init(cx); } -pub fn normal_motion(motion: Motion, cx: &mut MutableAppContext) { +pub fn normal_motion( + motion: Motion, + operator: Option, + times: usize, + cx: &mut MutableAppContext, +) { Vim::update(cx, |vim, cx| { - match vim.state.operator_stack.pop() { - None => move_cursor(vim, motion, cx), - Some(Operator::Change) => change_motion(vim, motion, cx), - Some(Operator::Delete) => delete_motion(vim, motion, cx), - Some(Operator::Yank) => yank_motion(vim, motion, cx), + match operator { + None => move_cursor(vim, motion, times, cx), + Some(Operator::Change) => change_motion(vim, motion, times, cx), + Some(Operator::Delete) => delete_motion(vim, motion, times, cx), + Some(Operator::Yank) => yank_motion(vim, motion, times, cx), _ => { // Can't do anything for text objects or namespace operators. Ignoring } } - vim.clear_operator(cx); }); } @@ -105,10 +110,16 @@ pub fn normal_object(object: Object, cx: &mut MutableAppContext) { }) } -fn move_cursor(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) { +fn move_cursor(vim: &mut Vim, motion: Motion, times: usize, cx: &mut MutableAppContext) { vim.update_active_editor(cx, |editor, cx| { editor.change_selections(Some(Autoscroll::Fit), cx, |s| { - s.move_cursors_with(|map, cursor, goal| motion.move_point(map, cursor, goal)) + s.move_cursors_with(|map, cursor, goal| { + let mut result = (cursor, goal); + for _ in 0..times { + result = motion.move_point(map, result.0, result.1); + } + result + }) }) }); } @@ -328,311 +339,139 @@ mod test { Mode::{self, *}, Namespace, Operator, }, - test_contexts::VimTestContext, + test_contexts::{NeovimBackedTestContext, VimTestContext}, }; #[gpui::test] async fn test_h(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["h"]); - cx.assert("The qˇuick", "The ˇquick"); - cx.assert("ˇThe quick", "ˇThe quick"); - cx.assert( - indoc! {" - The quick - ˇbrown"}, - indoc! {" - The quick - ˇbrown"}, - ); + let mut cx = NeovimBackedTestContext::new(cx).await.binding(["h"]); + cx.assert_all(indoc! {" + ˇThe qˇuick + ˇbrown" + }) + .await; } #[gpui::test] async fn test_backspace(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["backspace"]); - cx.assert("The qˇuick", "The ˇquick"); - cx.assert("ˇThe quick", "ˇThe quick"); - cx.assert( - indoc! {" - The quick - ˇbrown"}, - indoc! {" - The quick - ˇbrown"}, - ); + let mut cx = NeovimBackedTestContext::new(cx) + .await + .binding(["backspace"]); + cx.assert_all(indoc! {" + ˇThe qˇuick + ˇbrown" + }) + .await; } #[gpui::test] async fn test_j(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["j"]); - cx.assert( - indoc! {" - The ˇquick - brown fox"}, - indoc! {" - The quick - browˇn fox"}, - ); - cx.assert( - indoc! {" - The quick - browˇn fox"}, - indoc! {" - The quick - browˇn fox"}, - ); - cx.assert( - indoc! {" - The quicˇk - brown"}, - indoc! {" - The quick - browˇn"}, - ); - cx.assert( - indoc! {" - The quick - ˇbrown"}, - indoc! {" - The quick - ˇbrown"}, - ); + let mut cx = NeovimBackedTestContext::new(cx).await.binding(["j"]); + cx.assert_all(indoc! {" + ˇThe qˇuick broˇwn + ˇfox jumps" + }) + .await; } #[gpui::test] async fn test_k(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["k"]); - cx.assert( - indoc! {" - The ˇquick - brown fox"}, - indoc! {" - The ˇquick - brown fox"}, - ); - cx.assert( - indoc! {" - The quick - browˇn fox"}, - indoc! {" - The ˇquick - brown fox"}, - ); - cx.assert( - indoc! {" - The - quicˇk"}, - indoc! {" - Thˇe - quick"}, - ); + let mut cx = NeovimBackedTestContext::new(cx).await.binding(["k"]); + cx.assert_all(indoc! {" + ˇThe qˇuick + ˇbrown fˇox jumˇps" + }) + .await; } #[gpui::test] async fn test_l(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["l"]); - cx.assert("The qˇuick", "The quˇick"); - cx.assert("The quicˇk", "The quicˇk"); - cx.assert( - indoc! {" - The quicˇk - brown"}, - indoc! {" - The quicˇk - brown"}, - ); + let mut cx = NeovimBackedTestContext::new(cx).await.binding(["l"]); + cx.assert_all(indoc! {" + ˇThe qˇuicˇk + ˇbrowˇn"}) + .await; } #[gpui::test] async fn test_jump_to_line_boundaries(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["$"]); - cx.assert("Tˇest test", "Test tesˇt"); - cx.assert("Test tesˇt", "Test tesˇt"); - cx.assert( + let mut cx = NeovimBackedTestContext::new(cx).await; + cx.assert_binding_matches_all( + ["$"], indoc! {" - The ˇquick - brown"}, + ˇThe qˇuicˇk + ˇbrowˇn"}, + ) + .await; + cx.assert_binding_matches_all( + ["0"], indoc! {" - The quicˇk - brown"}, - ); - cx.assert( - indoc! {" - The quicˇk - brown"}, - indoc! {" - The quicˇk - brown"}, - ); - - let mut cx = cx.binding(["0"]); - cx.assert("Test ˇtest", "ˇTest test"); - cx.assert("ˇTest test", "ˇTest test"); - cx.assert( - indoc! {" - The ˇquick - brown"}, - indoc! {" - ˇThe quick - brown"}, - ); - cx.assert( - indoc! {" - ˇThe quick - brown"}, - indoc! {" - ˇThe quick - brown"}, - ); + ˇThe qˇuicˇk + ˇbrowˇn"}, + ) + .await; } #[gpui::test] async fn test_jump_to_end(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["shift-g"]); + let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-g"]); - cx.assert( - indoc! {" + cx.assert_all(indoc! {" The ˇquick brown fox jumps - over the lazy dog"}, - indoc! {" - The quick - - brown fox jumps - overˇ the lazy dog"}, - ); - cx.assert( - indoc! {" - The quick - - brown fox jumps - overˇ the lazy dog"}, - indoc! {" - The quick - - brown fox jumps - overˇ the lazy dog"}, - ); - cx.assert( - indoc! {" + overˇ the lazy doˇg"}) + .await; + cx.assert(indoc! {" The quiˇck - brown"}, - indoc! {" - The quick - - browˇn"}, - ); - cx.assert( - indoc! {" + brown"}) + .await; + cx.assert(indoc! {" The quiˇck - "}, - indoc! {" - The quick - - ˇ"}, - ); + "}) + .await; } #[gpui::test] async fn test_w(cx: &mut gpui::TestAppContext) { - let mut cx = VimTestContext::new(cx, true).await; - let (_, cursor_offsets) = marked_text_offsets(indoc! {" + let mut cx = NeovimBackedTestContext::new(cx).await.binding(["w"]); + cx.assert_all(indoc! {" The ˇquickˇ-ˇbrown ˇ ˇ ˇfox_jumps ˇover - ˇthˇˇe"}); - cx.set_state( - indoc! {" - ˇThe quick-brown - - - fox_jumps over - the"}, - Mode::Normal, - ); - - for cursor_offset in cursor_offsets { - cx.simulate_keystroke("w"); - cx.assert_editor_selections(vec![cursor_offset..cursor_offset]); - } - - // Reset and test ignoring punctuation - let (_, cursor_offsets) = marked_text_offsets(indoc! {" - The ˇquick-brown + ˇthˇe"}) + .await; + let mut cx = cx.binding(["shift-w"]); + cx.assert_all(indoc! {" + The ˇquickˇ-ˇbrown ˇ ˇ ˇfox_jumps ˇover - ˇthˇˇe"}); - cx.set_state( - indoc! {" - ˇThe quick-brown - - - fox_jumps over - the"}, - Mode::Normal, - ); - - for cursor_offset in cursor_offsets { - cx.simulate_keystroke("shift-w"); - cx.assert_editor_selections(vec![cursor_offset..cursor_offset]); - } + ˇthˇe"}) + .await; } #[gpui::test] async fn test_e(cx: &mut gpui::TestAppContext) { - let mut cx = VimTestContext::new(cx, true).await; - let (_, cursor_offsets) = marked_text_offsets(indoc! {" + let mut cx = NeovimBackedTestContext::new(cx).await.binding(["e"]); + cx.assert_all(indoc! {" Thˇe quicˇkˇ-browˇn fox_jumpˇs oveˇr - thˇe"}); - cx.set_state( - indoc! {" - ˇThe quick-brown - - - fox_jumps over - the"}, - Mode::Normal, - ); - - for cursor_offset in cursor_offsets { - cx.simulate_keystroke("e"); - cx.assert_editor_selections(vec![cursor_offset..cursor_offset]); - } - - // Reset and test ignoring punctuation - let (_, cursor_offsets) = marked_text_offsets(indoc! {" - Thˇe quick-browˇn + thˇe"}) + .await; + let mut cx = cx.binding(["shift-e"]); + cx.assert_all(indoc! {" + Thˇe quicˇkˇ-browˇn fox_jumpˇs oveˇr - thˇˇe"}); - cx.set_state( - indoc! {" - ˇThe quick-brown - - - fox_jumps over - the"}, - Mode::Normal, - ); - for cursor_offset in cursor_offsets { - cx.simulate_keystroke("shift-e"); - cx.assert_editor_selections(vec![cursor_offset..cursor_offset]); - } + thˇe"}) + .await; } #[gpui::test] @@ -699,90 +538,35 @@ mod test { #[gpui::test] async fn test_gg(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["g", "g"]); - cx.assert( - indoc! {" - The quick + let mut cx = NeovimBackedTestContext::new(cx).await.binding(["g", "g"]); + cx.assert_all(indoc! {" + The qˇuick + + brown fox jumps + over ˇthe laˇzy dog"}) + .await; + cx.assert(indoc! {" - brown fox jumps - over ˇthe lazy dog"}, - indoc! {" - The qˇuick - - brown fox jumps - over the lazy dog"}, - ); - cx.assert( - indoc! {" - The qˇuick - - brown fox jumps - over the lazy dog"}, - indoc! {" - The qˇuick - - brown fox jumps - over the lazy dog"}, - ); - cx.assert( - indoc! {" - The quick - - brown fox jumps - over the laˇzy dog"}, - indoc! {" - The quicˇk - - brown fox jumps - over the lazy dog"}, - ); - cx.assert( - indoc! {" - - - brown fox jumps - over the laˇzy dog"}, - indoc! {" - ˇ - - brown fox jumps - over the lazy dog"}, - ); + + brown fox jumps + over the laˇzy dog"}) + .await; } #[gpui::test] async fn test_a(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["a"]).mode_after(Mode::Insert); - - cx.assert("The qˇuick", "The quˇick"); - cx.assert("The quicˇk", "The quickˇ"); + let mut cx = NeovimBackedTestContext::new(cx).await.binding(["a"]); + cx.assert_all("The qˇuicˇk").await; } #[gpui::test] async fn test_insert_end_of_line(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["shift-a"]).mode_after(Mode::Insert); - cx.assert("The qˇuick", "The quickˇ"); - cx.assert("The qˇuick ", "The quick ˇ"); - cx.assert("ˇ", "ˇ"); - cx.assert( - indoc! {" - The qˇuick - brown fox"}, - indoc! {" - The quickˇ - brown fox"}, - ); - cx.assert( - indoc! {" - ˇ - The quick"}, - indoc! {" - ˇ - The quick"}, - ); + let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-a"]); + cx.assert_all(indoc! {" + ˇ + The qˇuick + brown ˇfox "}) + .await; } #[gpui::test] @@ -984,84 +768,45 @@ mod test { #[gpui::test] async fn test_insert_line_above(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["shift-o"]).mode_after(Mode::Insert); + let cx = NeovimBackedTestContext::new(cx).await; + let mut cx = cx.binding(["shift-o"]); + cx.assert("ˇ").await; + cx.assert("The ˇquick").await; + cx.assert_all(indoc! {" + The qˇuick + brown ˇfox + jumps ˇover"}) + .await; + cx.assert(indoc! {" + The quick + ˇ + brown fox"}) + .await; - cx.assert( - "ˇ", - indoc! {" - ˇ - "}, - ); - cx.assert( - "The ˇquick", - indoc! {" - ˇ - The quick"}, - ); - cx.assert( - indoc! {" - The quick - brown ˇfox - jumps over"}, - indoc! {" - The quick - ˇ - brown fox - jumps over"}, - ); - cx.assert( - indoc! {" - The quick - brown fox - jumps ˇover"}, - indoc! {" - The quick - brown fox - ˇ - jumps over"}, - ); - cx.assert( - indoc! {" - The qˇuick - brown fox - jumps over"}, - indoc! {" - ˇ - The quick - brown fox - jumps over"}, - ); - cx.assert( - indoc! {" - The quick - ˇ - brown fox"}, - indoc! {" - The quick - ˇ - - brown fox"}, - ); - cx.assert( + // Our indentation is smarter than vims. So we don't match here + cx.assert_manual( indoc! {" fn test() println!(ˇ);"}, + Mode::Normal, indoc! {" fn test() ˇ println!();"}, + Mode::Insert, ); - cx.assert( + cx.assert_manual( indoc! {" fn test(ˇ) { println!(); }"}, + Mode::Normal, indoc! {" ˇ fn test() { println!(); }"}, + Mode::Insert, ); } @@ -1208,4 +953,22 @@ mod test { Mode::Normal, ); } + + #[gpui::test] + async fn test_repeated_word(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + for count in 1..=5 { + cx.assert_binding_matches_all( + [&count.to_string(), "w"], + indoc! {" + ˇThe quˇickˇ browˇn + ˇ + ˇfox ˇjumpsˇ-ˇoˇver + ˇthe lazy dog + "}, + ) + .await; + } + } } diff --git a/crates/vim/src/normal/change.rs b/crates/vim/src/normal/change.rs index 6835ea3b1c..924fc9d708 100644 --- a/crates/vim/src/normal/change.rs +++ b/crates/vim/src/normal/change.rs @@ -1,30 +1,20 @@ use crate::{motion::Motion, object::Object, state::Mode, utils::copy_selections_content, Vim}; -use editor::{char_kind, movement, Autoscroll}; -use gpui::{impl_actions, MutableAppContext, ViewContext}; -use serde::Deserialize; -use workspace::Workspace; +use editor::{char_kind, display_map::DisplaySnapshot, movement, Autoscroll, DisplayPoint}; +use gpui::MutableAppContext; +use language::Selection; -#[derive(Clone, Deserialize, PartialEq)] -#[serde(rename_all = "camelCase")] -struct ChangeWord { - #[serde(default)] - ignore_punctuation: bool, -} - -impl_actions!(vim, [ChangeWord]); - -pub fn init(cx: &mut MutableAppContext) { - cx.add_action(change_word); -} - -pub fn change_motion(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) { +pub fn change_motion(vim: &mut Vim, motion: Motion, times: usize, cx: &mut MutableAppContext) { vim.update_active_editor(cx, |editor, cx| { editor.transact(cx, |editor, cx| { // We are swapping to insert mode anyway. Just set the line end clipping behavior now editor.set_clip_at_line_ends(false, cx); editor.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_with(|map, selection| { - motion.expand_selection(map, selection, false); + if let Motion::NextWordStart { ignore_punctuation } = motion { + expand_changed_word_selection(map, selection, times, ignore_punctuation); + } else { + motion.expand_selection(map, selection, times, false); + } }); }); copy_selections_content(editor, motion.linewise(), cx); @@ -56,38 +46,30 @@ pub fn change_object(vim: &mut Vim, object: Object, around: bool, cx: &mut Mutab // white space after a word, they only change up to the end of the word. This is // because Vim interprets "cw" as change-word, and a word does not include the // following white space. -fn change_word( - _: &mut Workspace, - &ChangeWord { ignore_punctuation }: &ChangeWord, - cx: &mut ViewContext, +fn expand_changed_word_selection( + map: &DisplaySnapshot, + selection: &mut Selection, + times: usize, + ignore_punctuation: bool, ) { - Vim::update(cx, |vim, cx| { - vim.update_active_editor(cx, |editor, cx| { - editor.transact(cx, |editor, cx| { - // We are swapping to insert mode anyway. Just set the line end clipping behavior now - editor.set_clip_at_line_ends(false, cx); - editor.change_selections(Some(Autoscroll::Fit), cx, |s| { - s.move_with(|map, selection| { - if selection.end.column() == map.line_len(selection.end.row()) { - return; - } + if times > 1 { + Motion::NextWordStart { ignore_punctuation }.expand_selection( + map, + selection, + times - 1, + false, + ); + } - selection.end = - movement::find_boundary(map, selection.end, |left, right| { - let left_kind = - char_kind(left).coerce_punctuation(ignore_punctuation); - let right_kind = - char_kind(right).coerce_punctuation(ignore_punctuation); + if times == 1 && selection.end.column() == map.line_len(selection.end.row()) { + return; + } - left_kind != right_kind || left == '\n' || right == '\n' - }); - }); - }); - copy_selections_content(editor, false, cx); - editor.insert("", cx); - }); - }); - vim.switch_mode(Mode::Insert, false, cx); + selection.end = movement::find_boundary(map, selection.end, |left, right| { + let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation); + let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation); + + left_kind != right_kind || left == '\n' || right == '\n' }); } @@ -95,7 +77,10 @@ fn change_word( mod test { use indoc::indoc; - use crate::{state::Mode, test_contexts::VimTestContext}; + use crate::{ + state::Mode, + test_contexts::{NeovimBackedTestContext, VimTestContext}, + }; #[gpui::test] async fn test_change_h(cx: &mut gpui::TestAppContext) { @@ -459,4 +444,85 @@ mod test { the lazy"}, ); } + + #[gpui::test] + async fn test_repeated_cj(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + for count in 1..=5 { + cx.assert_binding_matches_all( + ["c", &count.to_string(), "j"], + indoc! {" + ˇThe quˇickˇ browˇn + ˇ + ˇfox ˇjumpsˇ-ˇoˇver + ˇthe lazy dog + "}, + ) + .await; + } + } + + #[gpui::test] + async fn test_repeated_cl(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + for count in 1..=5 { + cx.assert_binding_matches_all( + ["c", &count.to_string(), "l"], + indoc! {" + ˇThe quˇickˇ browˇn + ˇ + ˇfox ˇjumpsˇ-ˇoˇver + ˇthe lazy dog + "}, + ) + .await; + } + } + + #[gpui::test] + async fn test_repeated_cb(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + // Changing back any number of times from the start of the file doesn't + // switch to insert mode in vim. This is weird and painful to implement + cx.add_initial_state_exemption(indoc! {" + ˇThe quick brown + + fox jumps-over + the lazy dog + "}); + + for count in 1..=5 { + cx.assert_binding_matches_all( + ["c", &count.to_string(), "b"], + indoc! {" + ˇThe quˇickˇ browˇn + ˇ + ˇfox ˇjumpsˇ-ˇoˇver + ˇthe lazy dog + "}, + ) + .await; + } + } + + #[gpui::test] + async fn test_repeated_ce(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + for count in 1..=5 { + cx.assert_binding_matches_all( + ["c", &count.to_string(), "e"], + indoc! {" + ˇThe quˇickˇ browˇn + ˇ + ˇfox ˇjumpsˇ-ˇoˇver + ˇthe lazy dog + "}, + ) + .await; + } + } } diff --git a/crates/vim/src/normal/delete.rs b/crates/vim/src/normal/delete.rs index e410024aea..b0fd3ea5a1 100644 --- a/crates/vim/src/normal/delete.rs +++ b/crates/vim/src/normal/delete.rs @@ -3,7 +3,7 @@ use collections::{HashMap, HashSet}; use editor::{display_map::ToDisplayPoint, Autoscroll, Bias}; use gpui::MutableAppContext; -pub fn delete_motion(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) { +pub fn delete_motion(vim: &mut Vim, motion: Motion, times: usize, cx: &mut MutableAppContext) { vim.update_active_editor(cx, |editor, cx| { editor.transact(cx, |editor, cx| { editor.set_clip_at_line_ends(false, cx); @@ -11,8 +11,8 @@ pub fn delete_motion(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) editor.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_with(|map, selection| { let original_head = selection.head(); - motion.expand_selection(map, selection, true); original_columns.insert(selection.id, original_head.column()); + motion.expand_selection(map, selection, times, true); }); }); copy_selections_content(editor, motion.linewise(), cx); diff --git a/crates/vim/src/normal/yank.rs b/crates/vim/src/normal/yank.rs index 11f5371cc6..e7d6b3076b 100644 --- a/crates/vim/src/normal/yank.rs +++ b/crates/vim/src/normal/yank.rs @@ -2,7 +2,7 @@ use crate::{motion::Motion, object::Object, utils::copy_selections_content, Vim} use collections::HashMap; use gpui::MutableAppContext; -pub fn yank_motion(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) { +pub fn yank_motion(vim: &mut Vim, motion: Motion, times: usize, cx: &mut MutableAppContext) { vim.update_active_editor(cx, |editor, cx| { editor.transact(cx, |editor, cx| { editor.set_clip_at_line_ends(false, cx); @@ -10,8 +10,8 @@ pub fn yank_motion(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) { editor.change_selections(None, cx, |s| { s.move_with(|map, selection| { let original_position = (selection.head(), selection.goal); - motion.expand_selection(map, selection, true); original_positions.insert(selection.id, original_position); + motion.expand_selection(map, selection, times, true); }); }); copy_selections_content(editor, motion.linewise(), cx); diff --git a/crates/vim/src/object.rs b/crates/vim/src/object.rs index 5c03bda2d9..b55545682f 100644 --- a/crates/vim/src/object.rs +++ b/crates/vim/src/object.rs @@ -12,7 +12,6 @@ use crate::{motion, normal::normal_object, state::Mode, visual::visual_object, V pub enum Object { Word { ignore_punctuation: bool }, Sentence, - Paragraph, } #[derive(Clone, Deserialize, PartialEq)] @@ -22,7 +21,7 @@ struct Word { ignore_punctuation: bool, } -actions!(vim, [Sentence, Paragraph]); +actions!(vim, [Sentence]); impl_actions!(vim, [Word]); pub fn init(cx: &mut MutableAppContext) { @@ -32,7 +31,6 @@ pub fn init(cx: &mut MutableAppContext) { }, ); cx.add_action(|_: &mut Workspace, _: &Sentence, cx: _| object(Object::Sentence, cx)); - cx.add_action(|_: &mut Workspace, _: &Paragraph, cx: _| object(Object::Paragraph, cx)); } fn object(object: Object, cx: &mut MutableAppContext) { @@ -61,7 +59,6 @@ impl Object { } } Object::Sentence => sentence(map, relative_to, around), - _ => relative_to..relative_to, } } @@ -172,71 +169,19 @@ fn around_next_word( start..end } -// /// Return the range containing a sentence. -// fn sentence(map: &DisplaySnapshot, relative_to: DisplayPoint, around: bool) -> Range { -// let mut previous_end = relative_to; -// let mut start = None; - -// // Seek backwards to find a period or double newline. Record the last non whitespace character as the -// // possible start of the sentence. Alternatively if two newlines are found right after each other, return that. -// let mut rev_chars = map.reverse_chars_at(relative_to).peekable(); -// while let Some((char, point)) = rev_chars.next() { -// dbg!(char, point); -// if char == '.' { -// break; -// } - -// if char == '\n' -// && (rev_chars.peek().map(|(c, _)| c == &'\n').unwrap_or(false) || start.is_none()) -// { -// break; -// } - -// if !char.is_whitespace() { -// start = Some(point); -// } - -// previous_end = point; -// } - -// let mut end = relative_to; -// let mut chars = map.chars_at(relative_to).peekable(); -// while let Some((char, point)) = chars.next() { -// if !char.is_whitespace() { -// if start.is_none() { -// start = Some(point); -// } - -// // Set the end to the point after the current non whitespace character -// end = point; -// *end.column_mut() += char.len_utf8() as u32; -// } - -// if char == '.' { -// break; -// } - -// if char == '\n' { -// if start.is_none() { -// if let Some((_, next_point)) = chars.peek() { -// end = *next_point; -// } -// break; - -// if chars.peek().map(|(c, _)| c == &'\n').unwrap_or(false) { -// break; -// } -// } -// } - -// start.unwrap_or(previous_end)..end -// } - fn sentence(map: &DisplaySnapshot, relative_to: DisplayPoint, around: bool) -> Range { let mut start = None; let mut previous_end = relative_to; - for (char, point) in map.reverse_chars_at(relative_to) { + let mut chars = map.chars_at(relative_to).peekable(); + + // Search backwards for the previous sentence end or current sentence start. Include the character under relative_to + for (char, point) in chars + .peek() + .cloned() + .into_iter() + .chain(map.reverse_chars_at(relative_to)) + { if is_sentence_end(map, point) { break; } @@ -248,36 +193,26 @@ fn sentence(map: &DisplaySnapshot, relative_to: DisplayPoint, around: bool) -> R previous_end = point; } - // Handle case where cursor was before the sentence start - let mut chars = map.chars_at(relative_to).peekable(); - if start.is_none() { - if let Some((char, point)) = chars.peek() { - if is_possible_sentence_start(*char) { - start = Some(*point); - } - } - } - + // Search forward for the end of the current sentence or if we are between sentences, the start of the next one let mut end = relative_to; for (char, point) in chars { - if start.is_some() { - if !char.is_whitespace() { - end = point; - *end.column_mut() += char.len_utf8() as u32; - end = map.clip_point(end, Bias::Left); - } - - if is_sentence_end(map, point) { - break; - } - } else if is_possible_sentence_start(char) { + if start.is_none() && is_possible_sentence_start(char) { if around { start = Some(point); + continue; } else { end = point; break; } } + + end = point; + *end.column_mut() += char.len_utf8() as u32; + end = map.clip_point(end, Bias::Left); + + if is_sentence_end(map, end) { + break; + } } let mut range = start.unwrap_or(previous_end)..end; @@ -296,22 +231,21 @@ const SENTENCE_END_PUNCTUATION: &[char] = &['.', '!', '?']; const SENTENCE_END_FILLERS: &[char] = &[')', ']', '"', '\'']; const SENTENCE_END_WHITESPACE: &[char] = &[' ', '\t', '\n']; fn is_sentence_end(map: &DisplaySnapshot, point: DisplayPoint) -> bool { - let mut chars = map.chars_at(point).peekable(); - - if let Some((char, _)) = chars.next() { - if char == '\n' && chars.peek().map(|(c, _)| c == &'\n').unwrap_or(false) { + let mut next_chars = map.chars_at(point).peekable(); + if let Some((char, _)) = next_chars.next() { + // We are at a double newline. This position is a sentence end. + if char == '\n' && next_chars.peek().map(|(c, _)| c == &'\n').unwrap_or(false) { return true; } - if !SENTENCE_END_PUNCTUATION.contains(&char) { + // The next text is not a valid whitespace. This is not a sentence end + if !SENTENCE_END_WHITESPACE.contains(&char) { return false; } - } else { - return false; } - for (char, _) in chars { - if SENTENCE_END_WHITESPACE.contains(&char) { + for (char, _) in map.reverse_chars_at(point) { + if SENTENCE_END_PUNCTUATION.contains(&char) { return true; } @@ -320,7 +254,7 @@ fn is_sentence_end(map: &DisplaySnapshot, point: DisplayPoint) -> bool { } } - return true; + return false; } /// Expands the passed range to include whitespace on one side or the other in a line. Attempts to add the @@ -331,16 +265,26 @@ fn expand_to_include_whitespace( stop_at_newline: bool, ) -> Range { let mut whitespace_included = false; - for (char, point) in map.chars_at(range.end) { - range.end = point; + let mut chars = map.chars_at(range.end).peekable(); + while let Some((char, point)) = chars.next() { if char == '\n' && stop_at_newline { break; } if char.is_whitespace() { - whitespace_included = true; + // Set end to the next display_point or the character position after the current display_point + range.end = chars.peek().map(|(_, point)| *point).unwrap_or_else(|| { + let mut end = point; + *end.column_mut() += char.len_utf8() as u32; + map.clip_point(end, Bias::Left) + }); + + if char != '\n' { + whitespace_included = true; + } } else { + // Found non whitespace. Quit out. break; } } @@ -385,7 +329,7 @@ mod test { #[gpui::test] async fn test_change_in_word(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new("test_change_in_word", cx) + let mut cx = NeovimBackedTestContext::new(cx) .await .binding(["c", "i", "w"]); cx.assert_all(WORD_LOCATIONS).await; @@ -395,7 +339,7 @@ mod test { #[gpui::test] async fn test_delete_in_word(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new("test_delete_in_word", cx) + let mut cx = NeovimBackedTestContext::new(cx) .await .binding(["d", "i", "w"]); cx.assert_all(WORD_LOCATIONS).await; @@ -405,7 +349,7 @@ mod test { #[gpui::test] async fn test_change_around_word(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new("test_change_around_word", cx) + let mut cx = NeovimBackedTestContext::new(cx) .await .binding(["c", "a", "w"]); cx.assert_all(WORD_LOCATIONS).await; @@ -415,7 +359,7 @@ mod test { #[gpui::test] async fn test_delete_around_word(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new("test_delete_around_word", cx) + let mut cx = NeovimBackedTestContext::new(cx) .await .binding(["d", "a", "w"]); cx.assert_all(WORD_LOCATIONS).await; @@ -431,7 +375,8 @@ mod test { the lazy doˇgˇ.ˇ ˇThe quick ˇ brown fox jumps over "}, - // Double newlines are broken currently + // Position of the cursor after deletion between lines isn't quite right. + // Deletion in a sentence at the start of a line with whitespace is incorrect. // indoc! {" // The quick brown fox jumps. // Over the lazy dog @@ -441,12 +386,12 @@ mod test { // the lazy dog.ˇ // ˇ // "}, - r#"The quick brown.)]'" Brown fox jumps."#, + r#"ˇThe ˇquick brownˇ.)ˇ]ˇ'ˇ" Brown ˇfox jumpsˇ.ˇ "#, ]; #[gpui::test] async fn test_change_in_sentence(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new("test_change_in_sentence", cx) + let mut cx = NeovimBackedTestContext::new(cx) .await .binding(["c", "i", "s"]); for sentence_example in SENTENCE_EXAMPLES { @@ -456,31 +401,42 @@ mod test { #[gpui::test] async fn test_delete_in_sentence(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new("test_delete_in_sentence", cx) + let mut cx = NeovimBackedTestContext::new(cx) .await .binding(["d", "i", "s"]); + for sentence_example in SENTENCE_EXAMPLES { cx.assert_all(sentence_example).await; } } #[gpui::test] - #[ignore] // End cursor position is incorrect async fn test_change_around_sentence(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new("test_change_around_sentence", cx) + let mut cx = NeovimBackedTestContext::new(cx) .await .binding(["c", "a", "s"]); + + // Resulting position is slightly incorrect for unintuitive reasons. + cx.add_initial_state_exemption("The quick brown?ˇ Fox Jumps! Over the lazy."); + // Changing around the sentence at the end of the line doesn't remove whitespace.' + cx.add_initial_state_exemption("The quick brown.)]\'\" Brown fox jumps.ˇ "); + for sentence_example in SENTENCE_EXAMPLES { cx.assert_all(sentence_example).await; } } #[gpui::test] - #[ignore] // End cursor position is incorrect async fn test_delete_around_sentence(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new("test_delete_around_sentence", cx) + let mut cx = NeovimBackedTestContext::new(cx) .await .binding(["d", "a", "s"]); + + // Resulting position is slightly incorrect for unintuitive reasons. + cx.add_initial_state_exemption("The quick brown?ˇ Fox Jumps! Over the lazy."); + // Changing around the sentence at the end of the line doesn't remove whitespace.' + cx.add_initial_state_exemption("The quick brown.)]\'\" Brown fox jumps.ˇ "); + for sentence_example in SENTENCE_EXAMPLES { cx.assert_all(sentence_example).await; } diff --git a/crates/vim/src/state.rs b/crates/vim/src/state.rs index 29a51664ee..fef0da2099 100644 --- a/crates/vim/src/state.rs +++ b/crates/vim/src/state.rs @@ -22,6 +22,7 @@ pub enum Namespace { #[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)] pub enum Operator { + Number(usize), Namespace(Namespace), Change, Delete, @@ -92,12 +93,14 @@ impl VimState { impl Operator { pub fn set_context(operator: Option<&Operator>, context: &mut Context) { let operator_context = match operator { + Some(Operator::Number(_)) => "n", Some(Operator::Namespace(Namespace::G)) => "g", Some(Operator::Object { around: false }) => "i", Some(Operator::Object { around: true }) => "a", Some(Operator::Change) => "c", Some(Operator::Delete) => "d", Some(Operator::Yank) => "y", + None => "none", } .to_owned(); diff --git a/crates/vim/src/test_contexts/neovim_backed_binding_test_context.rs b/crates/vim/src/test_contexts/neovim_backed_binding_test_context.rs index b732bfa19c..3f6b8f99f8 100644 --- a/crates/vim/src/test_contexts/neovim_backed_binding_test_context.rs +++ b/crates/vim/src/test_contexts/neovim_backed_binding_test_context.rs @@ -1,6 +1,6 @@ use std::ops::{Deref, DerefMut}; -use util::test::marked_text_offsets; +use crate::state::Mode; use super::NeovimBackedTestContext; @@ -24,20 +24,39 @@ impl<'a, const COUNT: usize> NeovimBackedBindingTestContext<'a, COUNT> { self.cx } - pub async fn assert(&mut self, initial_state: &str) { + pub fn binding( + self, + keystrokes: [&'static str; NEW_COUNT], + ) -> NeovimBackedBindingTestContext<'a, NEW_COUNT> { + self.consume().binding(keystrokes) + } + + pub async fn assert(&mut self, marked_positions: &str) { self.cx - .assert_binding_matches(self.keystrokes_under_test, initial_state) + .assert_binding_matches(self.keystrokes_under_test, marked_positions) .await } - pub async fn assert_all(&mut self, marked_positions: &str) { - let (unmarked_text, cursor_offsets) = marked_text_offsets(marked_positions); + pub fn assert_manual( + &mut self, + initial_state: &str, + mode_before: Mode, + state_after: &str, + mode_after: Mode, + ) { + self.cx.assert_binding( + self.keystrokes_under_test, + initial_state, + mode_before, + state_after, + mode_after, + ); + } - for cursor_offset in cursor_offsets.iter() { - let mut marked_text = unmarked_text.clone(); - marked_text.insert(*cursor_offset, 'ˇ'); - self.assert(&marked_text).await; - } + pub async fn assert_all(&mut self, marked_positions: &str) { + self.cx + .assert_binding_matches_all(self.keystrokes_under_test, marked_positions) + .await } } diff --git a/crates/vim/src/test_contexts/neovim_backed_test_context.rs b/crates/vim/src/test_contexts/neovim_backed_test_context.rs index 12654c7fb9..d537abb530 100644 --- a/crates/vim/src/test_contexts/neovim_backed_test_context.rs +++ b/crates/vim/src/test_contexts/neovim_backed_test_context.rs @@ -3,6 +3,7 @@ use std::{ path::PathBuf, }; +use collections::{HashMap, HashSet, VecDeque}; use editor::DisplayPoint; use gpui::keymap::Keystroke; @@ -14,11 +15,13 @@ use async_trait::async_trait; use nvim_rs::{ create::tokio::new_child_cmd, error::LoopError, Handler, Neovim, UiAttachOptions, Value, }; +use serde::{Deserialize, Serialize}; #[cfg(feature = "neovim")] use tokio::{ process::{Child, ChildStdin, Command}, task::JoinHandle, }; +use util::test::marked_text_offsets; use crate::state::Mode; @@ -26,60 +29,43 @@ use super::{NeovimBackedBindingTestContext, VimTestContext}; pub struct NeovimBackedTestContext<'a> { cx: VimTestContext<'a>, - test_case_id: &'static str, - data_counter: usize, - #[cfg(feature = "neovim")] - nvim: Neovim>, - #[cfg(feature = "neovim")] - _join_handle: JoinHandle>>, - #[cfg(feature = "neovim")] - _child: Child, + // Lookup for exempted assertions. Keyed by the insertion text, and with a value indicating which + // bindings are exempted. If None, all bindings are ignored for that insertion text. + exemptions: HashMap>>, + neovim: NeovimConnection, } impl<'a> NeovimBackedTestContext<'a> { - pub async fn new( - test_case_id: &'static str, - cx: &'a mut gpui::TestAppContext, - ) -> NeovimBackedTestContext<'a> { + pub async fn new(cx: &'a mut gpui::TestAppContext) -> NeovimBackedTestContext<'a> { + let function_name = cx.function_name.clone(); let cx = VimTestContext::new(cx, true).await; - - #[cfg(feature = "neovim")] - let handler = NvimHandler {}; - #[cfg(feature = "neovim")] - let (nvim, join_handle, child) = Compat::new(async { - let (nvim, join_handle, child) = new_child_cmd( - &mut Command::new("nvim").arg("--embed").arg("--clean"), - handler, - ) - .await - .expect("Could not connect to neovim process"); - - nvim.ui_attach(100, 100, &UiAttachOptions::default()) - .await - .expect("Could not attach to ui"); - - (nvim, join_handle, child) - }) - .await; - - let result = Self { + Self { cx, - test_case_id, - data_counter: 0, - #[cfg(feature = "neovim")] - nvim, - #[cfg(feature = "neovim")] - _join_handle: join_handle, - #[cfg(feature = "neovim")] - _child: child, - }; - - #[cfg(feature = "neovim")] - { - result.clear_test_data() + exemptions: Default::default(), + neovim: NeovimConnection::new(function_name).await, } + } - result + pub fn add_initial_state_exemption(&mut self, initial_state: &str) { + let initial_state = initial_state.to_string(); + // None represents all keybindings being exempted for that initial state + self.exemptions.insert(initial_state, None); + } + + pub fn add_keybinding_exemption( + &mut self, + keybinding: [&str; COUNT], + initial_state: &str, + ) { + let initial_state = initial_state.to_string(); + let exempted_keybindings = self + .exemptions + .entry(initial_state) + .or_insert(Some(Default::default())); + + if let Some(exempted_bindings) = exempted_keybindings.as_mut() { + exempted_bindings.insert(format!("{keybinding:?}")); + } } pub async fn simulate_shared_keystroke(&mut self, keystroke_text: &str) { @@ -101,7 +87,7 @@ impl<'a> NeovimBackedTestContext<'a> { let key = format!("{start}{shift}{ctrl}{alt}{cmd}{}{end}", keystroke.key); - self.nvim + self.neovim .input(&key) .await .expect("Could not input keystroke"); @@ -128,37 +114,32 @@ impl<'a> NeovimBackedTestContext<'a> { let cursor_point = self.editor(|editor, cx| editor.selections.newest::(cx)); let nvim_buffer = self - .nvim + .neovim .get_current_buf() .await .expect("Could not get neovim buffer"); let mut lines = self .buffer_text() - .lines() + .split('\n') .map(|line| line.to_string()) .collect::>(); - if lines.len() > 1 { - // Add final newline which is missing from buffer_text - lines.push("".to_string()); - } - nvim_buffer .set_lines(0, -1, false, lines) .await .expect("Could not set nvim buffer text"); - self.nvim + self.neovim .input("") .await .expect("Could not send escape to nvim"); - self.nvim + self.neovim .input("") .await .expect("Could not send escape to nvim"); let nvim_window = self - .nvim + .neovim .get_current_win() .await .expect("Could not get neovim window"); @@ -173,18 +154,161 @@ impl<'a> NeovimBackedTestContext<'a> { } pub async fn assert_state_matches(&mut self) { - assert_eq!(self.neovim_text().await, self.buffer_text()); + assert_eq!( + self.neovim.text().await, + self.buffer_text(), + "{}", + self.assertion_context.context() + ); let zed_head = self.update_editor(|editor, cx| editor.selections.newest_display(cx).head()); - assert_eq!(self.neovim_head().await, zed_head); + assert_eq!( + self.neovim.head().await, + zed_head, + "{}", + self.assertion_context.context() + ); - if let Some(neovim_mode) = self.neovim_mode().await { - assert_eq!(neovim_mode, self.mode()); + if let Some(neovim_mode) = self.neovim.mode().await { + assert_eq!( + neovim_mode, + self.mode(), + "{}", + self.assertion_context.context() + ); + } + } + + pub async fn assert_binding_matches( + &mut self, + keystrokes: [&str; COUNT], + initial_state: &str, + ) { + if let Some(possible_exempted_keystrokes) = self.exemptions.get(initial_state) { + match possible_exempted_keystrokes { + Some(exempted_keystrokes) => { + if exempted_keystrokes.contains(&format!("{keystrokes:?}")) { + // This keystroke was exempted for this insertion text + return; + } + } + None => { + // All keystrokes for this insertion text are exempted + return; + } + } + } + + let _keybinding_context_handle = + self.add_assertion_context(format!("Key Binding Under Test: {:?}", keystrokes)); + let _initial_state_context_handle = self.add_assertion_context(format!( + "Initial State: \"{}\"", + initial_state.escape_debug().to_string() + )); + self.set_shared_state(initial_state).await; + self.simulate_shared_keystrokes(keystrokes).await; + self.assert_state_matches().await; + } + + pub async fn assert_binding_matches_all( + &mut self, + keystrokes: [&str; COUNT], + marked_positions: &str, + ) { + let (unmarked_text, cursor_offsets) = marked_text_offsets(marked_positions); + + for cursor_offset in cursor_offsets.iter() { + let mut marked_text = unmarked_text.clone(); + marked_text.insert(*cursor_offset, 'ˇ'); + + self.assert_binding_matches(keystrokes, &marked_text).await; + } + } + + pub fn binding( + self, + keystrokes: [&'static str; COUNT], + ) -> NeovimBackedBindingTestContext<'a, COUNT> { + NeovimBackedBindingTestContext::new(keystrokes, self) + } +} + +impl<'a> Deref for NeovimBackedTestContext<'a> { + type Target = VimTestContext<'a>; + + fn deref(&self) -> &Self::Target { + &self.cx + } +} + +impl<'a> DerefMut for NeovimBackedTestContext<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.cx + } +} + +#[derive(Serialize, Deserialize)] +pub enum NeovimData { + Text(String), + Head { row: u32, column: u32 }, + Mode(Option), +} + +struct NeovimConnection { + data: VecDeque, + #[cfg(feature = "neovim")] + test_case_id: String, + #[cfg(feature = "neovim")] + nvim: Neovim>, + #[cfg(feature = "neovim")] + _join_handle: JoinHandle>>, + #[cfg(feature = "neovim")] + _child: Child, +} + +impl NeovimConnection { + async fn new(test_case_id: String) -> Self { + #[cfg(feature = "neovim")] + let handler = NvimHandler {}; + #[cfg(feature = "neovim")] + let (nvim, join_handle, child) = Compat::new(async { + let (nvim, join_handle, child) = new_child_cmd( + &mut Command::new("nvim").arg("--embed").arg("--clean"), + handler, + ) + .await + .expect("Could not connect to neovim process"); + + nvim.ui_attach(100, 100, &UiAttachOptions::default()) + .await + .expect("Could not attach to ui"); + + nvim.set_option("smartindent", nvim_rs::Value::Boolean(true)) + .await + .expect("Could not set smartindent on startup"); + + (nvim, join_handle, child) + }) + .await; + + Self { + #[cfg(feature = "neovim")] + data: Default::default(), + #[cfg(not(feature = "neovim"))] + data: Self::read_test_data(&test_case_id), + #[cfg(feature = "neovim")] + test_case_id, + #[cfg(feature = "neovim")] + nvim, + #[cfg(feature = "neovim")] + _join_handle: join_handle, + #[cfg(feature = "neovim")] + _child: child, } } #[cfg(feature = "neovim")] - pub async fn neovim_text(&mut self) -> String { + pub async fn text(&mut self) -> String { let nvim_buffer = self .nvim .get_current_buf() @@ -196,17 +320,22 @@ impl<'a> NeovimBackedTestContext<'a> { .expect("Could not get buffer text") .join("\n"); - self.write_test_data(text.clone(), "text"); + self.data.push_back(NeovimData::Text(text.clone())); + text } #[cfg(not(feature = "neovim"))] - pub async fn neovim_text(&mut self) -> String { - self.read_test_data("text") + pub async fn text(&mut self) -> String { + if let Some(NeovimData::Text(text)) = self.data.pop_front() { + text + } else { + panic!("Invalid test data. Is test deterministic? Try running with '--features neovim' to regenerate"); + } } #[cfg(feature = "neovim")] - pub async fn neovim_head(&mut self) -> DisplayPoint { + pub async fn head(&mut self) -> DisplayPoint { let nvim_row: u32 = self .nvim .command_output("echo line('.')") @@ -224,24 +353,25 @@ impl<'a> NeovimBackedTestContext<'a> { .unwrap() - 1; // Neovim columns start at 1 - let serialized = format!("{},{}", nvim_row.to_string(), nvim_column.to_string()); - self.write_test_data(serialized, "head"); + self.data.push_back(NeovimData::Head { + row: nvim_row, + column: nvim_column, + }); DisplayPoint::new(nvim_row, nvim_column) } #[cfg(not(feature = "neovim"))] - pub async fn neovim_head(&mut self) -> DisplayPoint { - let serialized = self.read_test_data("head"); - let mut components = serialized.split(','); - let nvim_row = components.next().unwrap().parse::().unwrap(); - let nvim_column = components.next().unwrap().parse::().unwrap(); - - DisplayPoint::new(nvim_row, nvim_column) + pub async fn head(&mut self) -> DisplayPoint { + if let Some(NeovimData::Head { row, column }) = self.data.pop_front() { + DisplayPoint::new(row, column) + } else { + panic!("Invalid test data. Is test deterministic? Try running with '--features neovim' to regenerate"); + } } #[cfg(feature = "neovim")] - pub async fn neovim_mode(&mut self) -> Option { + pub async fn mode(&mut self) -> Option { let nvim_mode_text = self .nvim .get_mode() @@ -265,74 +395,67 @@ impl<'a> NeovimBackedTestContext<'a> { _ => None, }; - let serialized = serde_json::to_string(&mode).expect("Could not serialize mode"); - - self.write_test_data(serialized, "mode"); + self.data.push_back(NeovimData::Mode(mode.clone())); mode } #[cfg(not(feature = "neovim"))] - pub async fn neovim_mode(&mut self) -> Option { - let serialized = self.read_test_data("mode"); - serde_json::from_str(&serialized).expect("Could not deserialize test data") + pub async fn mode(&mut self) -> Option { + if let Some(NeovimData::Mode(mode)) = self.data.pop_front() { + mode + } else { + panic!("Invalid test data. Is test deterministic? Try running with '--features neovim' to regenerate"); + } } - fn test_data_directory(&self) -> PathBuf { + fn test_data_path(test_case_id: &str) -> PathBuf { let mut data_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); data_path.push("test_data"); - data_path.push(self.test_case_id); - data_path - } - - fn next_data_path(&mut self, kind: &str) -> PathBuf { - let mut data_path = self.test_data_directory(); - data_path.push(format!("{}{}.txt", self.data_counter, kind)); - self.data_counter += 1; + data_path.push(format!("{}.json", test_case_id)); data_path } #[cfg(not(feature = "neovim"))] - fn read_test_data(&mut self, kind: &str) -> String { - let path = self.next_data_path(kind); - std::fs::read_to_string(path).expect( + fn read_test_data(test_case_id: &str) -> VecDeque { + let path = Self::test_data_path(test_case_id); + let json = std::fs::read_to_string(path).expect( "Could not read test data. Is it generated? Try running test with '--features neovim'", - ) - } + ); - #[cfg(feature = "neovim")] - fn write_test_data(&mut self, data: String, kind: &str) { - let path = self.next_data_path(kind); - std::fs::create_dir_all(path.parent().unwrap()) - .expect("Could not create test data directory"); - std::fs::write(path, data).expect("Could not write out test data"); - } - - #[cfg(feature = "neovim")] - fn clear_test_data(&self) { - // If the path does not exist, no biggy, we will create it - std::fs::remove_dir_all(self.test_data_directory()).ok(); - } - - pub async fn assert_binding_matches( - &mut self, - keystrokes: [&str; COUNT], - initial_state: &str, - ) { - dbg!(keystrokes, initial_state); - self.set_shared_state(initial_state).await; - self.simulate_shared_keystrokes(keystrokes).await; - self.assert_state_matches().await; - } - - pub fn binding( - self, - keystrokes: [&'static str; COUNT], - ) -> NeovimBackedBindingTestContext<'a, COUNT> { - NeovimBackedBindingTestContext::new(keystrokes, self) + serde_json::from_str(&json) + .expect("Test data corrupted. Try regenerating it with '--features neovim'") } } +#[cfg(feature = "neovim")] +impl Deref for NeovimConnection { + type Target = Neovim>; + + fn deref(&self) -> &Self::Target { + &self.nvim + } +} + +#[cfg(feature = "neovim")] +impl DerefMut for NeovimConnection { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.nvim + } +} + +#[cfg(feature = "neovim")] +impl Drop for NeovimConnection { + fn drop(&mut self) { + let path = Self::test_data_path(&self.test_case_id); + std::fs::create_dir_all(path.parent().unwrap()) + .expect("Could not create test data directory"); + let json = serde_json::to_string(&self.data).expect("Could not serialize test data"); + std::fs::write(path, json).expect("Could not write out test data"); + } +} + +#[cfg(feature = "neovim")] #[derive(Clone)] struct NvimHandler {} @@ -359,16 +482,17 @@ impl Handler for NvimHandler { } } -impl<'a> Deref for NeovimBackedTestContext<'a> { - type Target = VimTestContext<'a>; +#[cfg(test)] +mod test { + use gpui::TestAppContext; - fn deref(&self) -> &Self::Target { - &self.cx - } -} - -impl<'a> DerefMut for NeovimBackedTestContext<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.cx + use crate::test_contexts::NeovimBackedTestContext; + + #[gpui::test] + async fn neovim_backed_test_context_works(cx: &mut TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + cx.assert_state_matches().await; + cx.set_shared_state("This is a tesˇt").await; + cx.assert_state_matches().await; } } diff --git a/crates/vim/src/test_contexts/vim_test_context.rs b/crates/vim/src/test_contexts/vim_test_context.rs index 229b5e0a8e..711e9d610c 100644 --- a/crates/vim/src/test_contexts/vim_test_context.rs +++ b/crates/vim/src/test_contexts/vim_test_context.rs @@ -1,6 +1,6 @@ use std::ops::{Deref, DerefMut}; -use editor::test::EditorTestContext; +use editor::test::{AssertionContextManager, EditorTestContext}; use gpui::{json::json, AppContext, ViewHandle}; use project::Project; use search::{BufferSearchBar, ProjectSearchBar}; @@ -82,6 +82,7 @@ impl<'a> VimTestContext<'a> { cx, window_id, editor, + assertion_context: AssertionContextManager::new(), }, workspace, } diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index 234eb0361b..5c9f23b41f 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -26,7 +26,10 @@ pub struct SwitchMode(pub Mode); #[derive(Clone, Deserialize, PartialEq)] pub struct PushOperator(pub Operator); -impl_actions!(vim, [SwitchMode, PushOperator]); +#[derive(Clone, Deserialize, PartialEq)] +struct Number(u8); + +impl_actions!(vim, [Number, SwitchMode, PushOperator]); pub fn init(cx: &mut MutableAppContext) { editor_events::init(cx); @@ -45,6 +48,9 @@ pub fn init(cx: &mut MutableAppContext) { Vim::update(cx, |vim, cx| vim.push_operator(operator, cx)) }, ); + cx.add_action(|_: &mut Workspace, n: &Number, cx: _| { + Vim::update(cx, |vim, cx| vim.push_number(n, cx)); + }); // Editor Actions cx.add_action(|_: &mut Editor, _: &Cancel, cx| { @@ -145,6 +151,15 @@ impl Vim { self.sync_vim_settings(cx); } + fn push_number(&mut self, Number(number): &Number, cx: &mut MutableAppContext) { + if let Some(Operator::Number(current_number)) = self.active_operator() { + self.pop_operator(cx); + self.push_operator(Operator::Number(current_number * 10 + *number as usize), cx); + } else { + self.push_operator(Operator::Number(*number as usize), cx); + } + } + fn pop_operator(&mut self, cx: &mut MutableAppContext) -> Operator { let popped_operator = self.state.operator_stack.pop() .expect("Operator popped when no operator was on the stack. This likely means there is an invalid keymap config"); @@ -152,6 +167,15 @@ impl Vim { popped_operator } + fn pop_number_operator(&mut self, cx: &mut MutableAppContext) -> usize { + let mut times = 1; + if let Some(Operator::Number(number)) = self.active_operator() { + times = number; + self.pop_operator(cx); + } + times + } + fn clear_operator(&mut self, cx: &mut MutableAppContext) { self.state.operator_stack.clear(); self.sync_vim_settings(cx); @@ -227,7 +251,7 @@ mod test { #[gpui::test] async fn test_neovim(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new("test_neovim", cx).await; + let mut cx = NeovimBackedTestContext::new(cx).await; cx.simulate_shared_keystroke("i").await; cx.simulate_shared_keystrokes([ diff --git a/crates/vim/src/visual.rs b/crates/vim/src/visual.rs index bbd6c8cfd9..c4c1ddf9a6 100644 --- a/crates/vim/src/visual.rs +++ b/crates/vim/src/visual.rs @@ -17,14 +17,18 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action(paste); } -pub fn visual_motion(motion: Motion, cx: &mut MutableAppContext) { +pub fn visual_motion(motion: Motion, times: usize, cx: &mut MutableAppContext) { Vim::update(cx, |vim, cx| { vim.update_active_editor(cx, |editor, cx| { editor.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_with(|map, selection| { - let (new_head, goal) = motion.move_point(map, selection.head(), selection.goal); let was_reversed = selection.reversed; - selection.set_head(new_head, goal); + + for _ in 0..times { + let (new_head, goal) = + motion.move_point(map, selection.head(), selection.goal); + selection.set_head(new_head, goal); + } if was_reversed && !selection.reversed { // Head was at the start of the selection, and now is at the end. We need to move the start diff --git a/crates/vim/test_data/neovim_backed_test_context_works.json b/crates/vim/test_data/neovim_backed_test_context_works.json new file mode 100644 index 0000000000..4b0312565e --- /dev/null +++ b/crates/vim/test_data/neovim_backed_test_context_works.json @@ -0,0 +1 @@ +[{"Text":""},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"This is a test"},{"Head":{"row":0,"column":13}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_a.json b/crates/vim/test_data/test_a.json new file mode 100644 index 0000000000..fc9d170879 --- /dev/null +++ b/crates/vim/test_data/test_a.json @@ -0,0 +1 @@ +[{"Text":"The quick"},{"Head":{"row":0,"column":6}},{"Mode":"Insert"},{"Text":"The quick"},{"Head":{"row":0,"column":9}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_backspace.json b/crates/vim/test_data/test_backspace.json new file mode 100644 index 0000000000..3659773665 --- /dev/null +++ b/crates/vim/test_data/test_backspace.json @@ -0,0 +1 @@ +[{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":4}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":8}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence.json b/crates/vim/test_data/test_change_around_sentence.json new file mode 100644 index 0000000000..9e01890d9f --- /dev/null +++ b/crates/vim/test_data/test_change_around_sentence.json @@ -0,0 +1 @@ +[{"Text":"Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!"},{"Head":{"row":0,"column":27}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!"},{"Head":{"row":0,"column":27}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!"},{"Head":{"row":0,"column":27}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!"},{"Head":{"row":0,"column":27}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Head":{"row":2,"column":13}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Head":{"row":2,"column":13}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Head":{"row":2,"column":13}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" "},{"Head":{"row":0,"column":21}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" "},{"Head":{"row":0,"column":21}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence/0text.txt b/crates/vim/test_data/test_change_around_sentence/0text.txt deleted file mode 100644 index d2b92e8000..0000000000 --- a/crates/vim/test_data/test_change_around_sentence/0text.txt +++ /dev/null @@ -1 +0,0 @@ -Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence/10head.txt b/crates/vim/test_data/test_change_around_sentence/10head.txt deleted file mode 100644 index 5df80e23c6..0000000000 --- a/crates/vim/test_data/test_change_around_sentence/10head.txt +++ /dev/null @@ -1 +0,0 @@ -0,16 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence/1head.txt b/crates/vim/test_data/test_change_around_sentence/1head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_change_around_sentence/1head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence/2mode.txt b/crates/vim/test_data/test_change_around_sentence/2mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_sentence/2mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence/3text.txt b/crates/vim/test_data/test_change_around_sentence/3text.txt deleted file mode 100644 index d2b92e8000..0000000000 --- a/crates/vim/test_data/test_change_around_sentence/3text.txt +++ /dev/null @@ -1 +0,0 @@ -Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence/4head.txt b/crates/vim/test_data/test_change_around_sentence/4head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_change_around_sentence/4head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence/5mode.txt b/crates/vim/test_data/test_change_around_sentence/5mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_sentence/5mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence/6text.txt b/crates/vim/test_data/test_change_around_sentence/6text.txt deleted file mode 100644 index d2b92e8000..0000000000 --- a/crates/vim/test_data/test_change_around_sentence/6text.txt +++ /dev/null @@ -1 +0,0 @@ -Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence/7head.txt b/crates/vim/test_data/test_change_around_sentence/7head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_change_around_sentence/7head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence/8mode.txt b/crates/vim/test_data/test_change_around_sentence/8mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_sentence/8mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence/9text.txt b/crates/vim/test_data/test_change_around_sentence/9text.txt deleted file mode 100644 index 5e3a12964d..0000000000 --- a/crates/vim/test_data/test_change_around_sentence/9text.txt +++ /dev/null @@ -1 +0,0 @@ -The quick brown? Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word.json b/crates/vim/test_data/test_change_around_word.json new file mode 100644 index 0000000000..c8724a6810 --- /dev/null +++ b/crates/vim/test_data/test_change_around_word.json @@ -0,0 +1 @@ +[{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":15}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":2,"column":12}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":4,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":5,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThequick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":3}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":15}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n-jumps over\nthe lazy dog \n\n"},{"Head":{"row":7,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n-jumps over\nthe lazy dog \n\n"},{"Head":{"row":8,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n-jumps over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":6}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n"},{"Head":{"row":10,"column":12}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n"},{"Head":{"row":11,"column":0}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":15}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":2,"column":12}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":4,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":5,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":15}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n over\nthe lazy dog \n\n"},{"Head":{"row":7,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n over\nthe lazy dog \n\n"},{"Head":{"row":8,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":2}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n"},{"Head":{"row":10,"column":12}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n"},{"Head":{"row":11,"column":0}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/0text.txt b/crates/vim/test_data/test_change_around_word/0text.txt deleted file mode 100644 index 366beed733..0000000000 --- a/crates/vim/test_data/test_change_around_word/0text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/100head.txt b/crates/vim/test_data/test_change_around_word/100head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_change_around_word/100head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/101mode.txt b/crates/vim/test_data/test_change_around_word/101mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/101mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/102text.txt b/crates/vim/test_data/test_change_around_word/102text.txt deleted file mode 100644 index c8a5876179..0000000000 --- a/crates/vim/test_data/test_change_around_word/102text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/103head.txt b/crates/vim/test_data/test_change_around_word/103head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_change_around_word/103head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/104mode.txt b/crates/vim/test_data/test_change_around_word/104mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/104mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/105text.txt b/crates/vim/test_data/test_change_around_word/105text.txt deleted file mode 100644 index c8a5876179..0000000000 --- a/crates/vim/test_data/test_change_around_word/105text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/106head.txt b/crates/vim/test_data/test_change_around_word/106head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_change_around_word/106head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/107mode.txt b/crates/vim/test_data/test_change_around_word/107mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/107mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/108text.txt b/crates/vim/test_data/test_change_around_word/108text.txt deleted file mode 100644 index c8a5876179..0000000000 --- a/crates/vim/test_data/test_change_around_word/108text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/109head.txt b/crates/vim/test_data/test_change_around_word/109head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_change_around_word/109head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/10head.txt b/crates/vim/test_data/test_change_around_word/10head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_change_around_word/10head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/110mode.txt b/crates/vim/test_data/test_change_around_word/110mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/110mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/111text.txt b/crates/vim/test_data/test_change_around_word/111text.txt deleted file mode 100644 index f05cdd88c1..0000000000 --- a/crates/vim/test_data/test_change_around_word/111text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/112head.txt b/crates/vim/test_data/test_change_around_word/112head.txt deleted file mode 100644 index da809253d8..0000000000 --- a/crates/vim/test_data/test_change_around_word/112head.txt +++ /dev/null @@ -1 +0,0 @@ -6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/113mode.txt b/crates/vim/test_data/test_change_around_word/113mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/113mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/114text.txt b/crates/vim/test_data/test_change_around_word/114text.txt deleted file mode 100644 index f05cdd88c1..0000000000 --- a/crates/vim/test_data/test_change_around_word/114text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/115head.txt b/crates/vim/test_data/test_change_around_word/115head.txt deleted file mode 100644 index c5a50698dd..0000000000 --- a/crates/vim/test_data/test_change_around_word/115head.txt +++ /dev/null @@ -1 +0,0 @@ -6,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/116mode.txt b/crates/vim/test_data/test_change_around_word/116mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/116mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/117text.txt b/crates/vim/test_data/test_change_around_word/117text.txt deleted file mode 100644 index 0e9094161a..0000000000 --- a/crates/vim/test_data/test_change_around_word/117text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/118head.txt b/crates/vim/test_data/test_change_around_word/118head.txt deleted file mode 100644 index d933eb81fa..0000000000 --- a/crates/vim/test_data/test_change_around_word/118head.txt +++ /dev/null @@ -1 +0,0 @@ -6,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/119mode.txt b/crates/vim/test_data/test_change_around_word/119mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/119mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/11mode.txt b/crates/vim/test_data/test_change_around_word/11mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/11mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/120text.txt b/crates/vim/test_data/test_change_around_word/120text.txt deleted file mode 100644 index fa73eb37b8..0000000000 --- a/crates/vim/test_data/test_change_around_word/120text.txt +++ /dev/null @@ -1,10 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/121head.txt b/crates/vim/test_data/test_change_around_word/121head.txt deleted file mode 100644 index 72c23ba506..0000000000 --- a/crates/vim/test_data/test_change_around_word/121head.txt +++ /dev/null @@ -1 +0,0 @@ -7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/122mode.txt b/crates/vim/test_data/test_change_around_word/122mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/122mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/123text.txt b/crates/vim/test_data/test_change_around_word/123text.txt deleted file mode 100644 index 4fe33b47eb..0000000000 --- a/crates/vim/test_data/test_change_around_word/123text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/124head.txt b/crates/vim/test_data/test_change_around_word/124head.txt deleted file mode 100644 index ded56a5926..0000000000 --- a/crates/vim/test_data/test_change_around_word/124head.txt +++ /dev/null @@ -1 +0,0 @@ -8,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/125mode.txt b/crates/vim/test_data/test_change_around_word/125mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/125mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/126text.txt b/crates/vim/test_data/test_change_around_word/126text.txt deleted file mode 100644 index 35dd5816fc..0000000000 --- a/crates/vim/test_data/test_change_around_word/126text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/127head.txt b/crates/vim/test_data/test_change_around_word/127head.txt deleted file mode 100644 index 8e69e0ad79..0000000000 --- a/crates/vim/test_data/test_change_around_word/127head.txt +++ /dev/null @@ -1 +0,0 @@ -9,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/128mode.txt b/crates/vim/test_data/test_change_around_word/128mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/128mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/129text.txt b/crates/vim/test_data/test_change_around_word/129text.txt deleted file mode 100644 index 1bac4587a7..0000000000 --- a/crates/vim/test_data/test_change_around_word/129text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/12text.txt b/crates/vim/test_data/test_change_around_word/12text.txt deleted file mode 100644 index 2feb66cfb9..0000000000 --- a/crates/vim/test_data/test_change_around_word/12text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/130head.txt b/crates/vim/test_data/test_change_around_word/130head.txt deleted file mode 100644 index edb3544f9a..0000000000 --- a/crates/vim/test_data/test_change_around_word/130head.txt +++ /dev/null @@ -1 +0,0 @@ -9,2 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/131mode.txt b/crates/vim/test_data/test_change_around_word/131mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/131mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/132text.txt b/crates/vim/test_data/test_change_around_word/132text.txt deleted file mode 100644 index 912316a9f7..0000000000 --- a/crates/vim/test_data/test_change_around_word/132text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_change_around_word/133head.txt b/crates/vim/test_data/test_change_around_word/133head.txt deleted file mode 100644 index 00f5ae6cbb..0000000000 --- a/crates/vim/test_data/test_change_around_word/133head.txt +++ /dev/null @@ -1 +0,0 @@ -10,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/134mode.txt b/crates/vim/test_data/test_change_around_word/134mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/134mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/135text.txt b/crates/vim/test_data/test_change_around_word/135text.txt deleted file mode 100644 index 1563710058..0000000000 --- a/crates/vim/test_data/test_change_around_word/135text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_change_around_word/136head.txt b/crates/vim/test_data/test_change_around_word/136head.txt deleted file mode 100644 index 94dbddc699..0000000000 --- a/crates/vim/test_data/test_change_around_word/136head.txt +++ /dev/null @@ -1 +0,0 @@ -11,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/137mode.txt b/crates/vim/test_data/test_change_around_word/137mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/137mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/13head.txt b/crates/vim/test_data/test_change_around_word/13head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_change_around_word/13head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/14mode.txt b/crates/vim/test_data/test_change_around_word/14mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/14mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/15text.txt b/crates/vim/test_data/test_change_around_word/15text.txt deleted file mode 100644 index 262feeae96..0000000000 --- a/crates/vim/test_data/test_change_around_word/15text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/16head.txt b/crates/vim/test_data/test_change_around_word/16head.txt deleted file mode 100644 index bd712da63a..0000000000 --- a/crates/vim/test_data/test_change_around_word/16head.txt +++ /dev/null @@ -1 +0,0 @@ -1,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/17mode.txt b/crates/vim/test_data/test_change_around_word/17mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/17mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/18text.txt b/crates/vim/test_data/test_change_around_word/18text.txt deleted file mode 100644 index ba0aea82d9..0000000000 --- a/crates/vim/test_data/test_change_around_word/18text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/19head.txt b/crates/vim/test_data/test_change_around_word/19head.txt deleted file mode 100644 index 28ce8bcc5c..0000000000 --- a/crates/vim/test_data/test_change_around_word/19head.txt +++ /dev/null @@ -1 +0,0 @@ -2,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/1head.txt b/crates/vim/test_data/test_change_around_word/1head.txt deleted file mode 100644 index 352f6067e6..0000000000 --- a/crates/vim/test_data/test_change_around_word/1head.txt +++ /dev/null @@ -1 +0,0 @@ -0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/20mode.txt b/crates/vim/test_data/test_change_around_word/20mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/20mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/21text.txt b/crates/vim/test_data/test_change_around_word/21text.txt deleted file mode 100644 index 783e049f86..0000000000 --- a/crates/vim/test_data/test_change_around_word/21text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/22head.txt b/crates/vim/test_data/test_change_around_word/22head.txt deleted file mode 100644 index 7cad756336..0000000000 --- a/crates/vim/test_data/test_change_around_word/22head.txt +++ /dev/null @@ -1 +0,0 @@ -3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/23mode.txt b/crates/vim/test_data/test_change_around_word/23mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/23mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/24text.txt b/crates/vim/test_data/test_change_around_word/24text.txt deleted file mode 100644 index 783e049f86..0000000000 --- a/crates/vim/test_data/test_change_around_word/24text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/25head.txt b/crates/vim/test_data/test_change_around_word/25head.txt deleted file mode 100644 index b26cd1f1d3..0000000000 --- a/crates/vim/test_data/test_change_around_word/25head.txt +++ /dev/null @@ -1 +0,0 @@ -4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/26mode.txt b/crates/vim/test_data/test_change_around_word/26mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/26mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/27text.txt b/crates/vim/test_data/test_change_around_word/27text.txt deleted file mode 100644 index 252419d291..0000000000 --- a/crates/vim/test_data/test_change_around_word/27text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - --quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/28head.txt b/crates/vim/test_data/test_change_around_word/28head.txt deleted file mode 100644 index 880fda949a..0000000000 --- a/crates/vim/test_data/test_change_around_word/28head.txt +++ /dev/null @@ -1 +0,0 @@ -5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/29mode.txt b/crates/vim/test_data/test_change_around_word/29mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/29mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/2mode.txt b/crates/vim/test_data/test_change_around_word/2mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/2mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/30text.txt b/crates/vim/test_data/test_change_around_word/30text.txt deleted file mode 100644 index 609747299b..0000000000 --- a/crates/vim/test_data/test_change_around_word/30text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - --quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/31head.txt b/crates/vim/test_data/test_change_around_word/31head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_change_around_word/31head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/32mode.txt b/crates/vim/test_data/test_change_around_word/32mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/32mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/33text.txt b/crates/vim/test_data/test_change_around_word/33text.txt deleted file mode 100644 index 12ede0f513..0000000000 --- a/crates/vim/test_data/test_change_around_word/33text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -Thequick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/34head.txt b/crates/vim/test_data/test_change_around_word/34head.txt deleted file mode 100644 index c20df71d5e..0000000000 --- a/crates/vim/test_data/test_change_around_word/34head.txt +++ /dev/null @@ -1 +0,0 @@ -6,3 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/35mode.txt b/crates/vim/test_data/test_change_around_word/35mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/35mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/36text.txt b/crates/vim/test_data/test_change_around_word/36text.txt deleted file mode 100644 index 4a6d943982..0000000000 --- a/crates/vim/test_data/test_change_around_word/36text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/37head.txt b/crates/vim/test_data/test_change_around_word/37head.txt deleted file mode 100644 index 95fb9c638e..0000000000 --- a/crates/vim/test_data/test_change_around_word/37head.txt +++ /dev/null @@ -1 +0,0 @@ -6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/38mode.txt b/crates/vim/test_data/test_change_around_word/38mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/38mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/39text.txt b/crates/vim/test_data/test_change_around_word/39text.txt deleted file mode 100644 index 4a6d943982..0000000000 --- a/crates/vim/test_data/test_change_around_word/39text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/3text.txt b/crates/vim/test_data/test_change_around_word/3text.txt deleted file mode 100644 index 366beed733..0000000000 --- a/crates/vim/test_data/test_change_around_word/3text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/40head.txt b/crates/vim/test_data/test_change_around_word/40head.txt deleted file mode 100644 index 95fb9c638e..0000000000 --- a/crates/vim/test_data/test_change_around_word/40head.txt +++ /dev/null @@ -1 +0,0 @@ -6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/41mode.txt b/crates/vim/test_data/test_change_around_word/41mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/41mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/42text.txt b/crates/vim/test_data/test_change_around_word/42text.txt deleted file mode 100644 index f05cdd88c1..0000000000 --- a/crates/vim/test_data/test_change_around_word/42text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/43head.txt b/crates/vim/test_data/test_change_around_word/43head.txt deleted file mode 100644 index da809253d8..0000000000 --- a/crates/vim/test_data/test_change_around_word/43head.txt +++ /dev/null @@ -1 +0,0 @@ -6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/44mode.txt b/crates/vim/test_data/test_change_around_word/44mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/44mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/45text.txt b/crates/vim/test_data/test_change_around_word/45text.txt deleted file mode 100644 index f05cdd88c1..0000000000 --- a/crates/vim/test_data/test_change_around_word/45text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/46head.txt b/crates/vim/test_data/test_change_around_word/46head.txt deleted file mode 100644 index c5a50698dd..0000000000 --- a/crates/vim/test_data/test_change_around_word/46head.txt +++ /dev/null @@ -1 +0,0 @@ -6,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/47mode.txt b/crates/vim/test_data/test_change_around_word/47mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/47mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/48text.txt b/crates/vim/test_data/test_change_around_word/48text.txt deleted file mode 100644 index 13b7d1cb51..0000000000 --- a/crates/vim/test_data/test_change_around_word/48text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/49head.txt b/crates/vim/test_data/test_change_around_word/49head.txt deleted file mode 100644 index d933eb81fa..0000000000 --- a/crates/vim/test_data/test_change_around_word/49head.txt +++ /dev/null @@ -1 +0,0 @@ -6,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/4head.txt b/crates/vim/test_data/test_change_around_word/4head.txt deleted file mode 100644 index 352f6067e6..0000000000 --- a/crates/vim/test_data/test_change_around_word/4head.txt +++ /dev/null @@ -1 +0,0 @@ -0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/50mode.txt b/crates/vim/test_data/test_change_around_word/50mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/50mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/51text.txt b/crates/vim/test_data/test_change_around_word/51text.txt deleted file mode 100644 index 3976ef9c85..0000000000 --- a/crates/vim/test_data/test_change_around_word/51text.txt +++ /dev/null @@ -1,10 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown --jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/52head.txt b/crates/vim/test_data/test_change_around_word/52head.txt deleted file mode 100644 index 72c23ba506..0000000000 --- a/crates/vim/test_data/test_change_around_word/52head.txt +++ /dev/null @@ -1 +0,0 @@ -7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/53mode.txt b/crates/vim/test_data/test_change_around_word/53mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/53mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/54text.txt b/crates/vim/test_data/test_change_around_word/54text.txt deleted file mode 100644 index b5ca31babe..0000000000 --- a/crates/vim/test_data/test_change_around_word/54text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - --jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/55head.txt b/crates/vim/test_data/test_change_around_word/55head.txt deleted file mode 100644 index ded56a5926..0000000000 --- a/crates/vim/test_data/test_change_around_word/55head.txt +++ /dev/null @@ -1 +0,0 @@ -8,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/56mode.txt b/crates/vim/test_data/test_change_around_word/56mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/56mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/57text.txt b/crates/vim/test_data/test_change_around_word/57text.txt deleted file mode 100644 index 0eaface506..0000000000 --- a/crates/vim/test_data/test_change_around_word/57text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - --jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/58head.txt b/crates/vim/test_data/test_change_around_word/58head.txt deleted file mode 100644 index 8e69e0ad79..0000000000 --- a/crates/vim/test_data/test_change_around_word/58head.txt +++ /dev/null @@ -1 +0,0 @@ -9,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/59mode.txt b/crates/vim/test_data/test_change_around_word/59mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/59mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/5mode.txt b/crates/vim/test_data/test_change_around_word/5mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/5mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/60text.txt b/crates/vim/test_data/test_change_around_word/60text.txt deleted file mode 100644 index d8b9b05b79..0000000000 --- a/crates/vim/test_data/test_change_around_word/60text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/61head.txt b/crates/vim/test_data/test_change_around_word/61head.txt deleted file mode 100644 index e0460d1bc5..0000000000 --- a/crates/vim/test_data/test_change_around_word/61head.txt +++ /dev/null @@ -1 +0,0 @@ -9,6 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/62mode.txt b/crates/vim/test_data/test_change_around_word/62mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/62mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/63text.txt b/crates/vim/test_data/test_change_around_word/63text.txt deleted file mode 100644 index 912316a9f7..0000000000 --- a/crates/vim/test_data/test_change_around_word/63text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_change_around_word/64head.txt b/crates/vim/test_data/test_change_around_word/64head.txt deleted file mode 100644 index 00f5ae6cbb..0000000000 --- a/crates/vim/test_data/test_change_around_word/64head.txt +++ /dev/null @@ -1 +0,0 @@ -10,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/65mode.txt b/crates/vim/test_data/test_change_around_word/65mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/65mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/66text.txt b/crates/vim/test_data/test_change_around_word/66text.txt deleted file mode 100644 index 1563710058..0000000000 --- a/crates/vim/test_data/test_change_around_word/66text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_change_around_word/67head.txt b/crates/vim/test_data/test_change_around_word/67head.txt deleted file mode 100644 index 94dbddc699..0000000000 --- a/crates/vim/test_data/test_change_around_word/67head.txt +++ /dev/null @@ -1 +0,0 @@ -11,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/68mode.txt b/crates/vim/test_data/test_change_around_word/68mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/68mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/69text.txt b/crates/vim/test_data/test_change_around_word/69text.txt deleted file mode 100644 index 366beed733..0000000000 --- a/crates/vim/test_data/test_change_around_word/69text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/6text.txt b/crates/vim/test_data/test_change_around_word/6text.txt deleted file mode 100644 index 3678c219a3..0000000000 --- a/crates/vim/test_data/test_change_around_word/6text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/70head.txt b/crates/vim/test_data/test_change_around_word/70head.txt deleted file mode 100644 index 352f6067e6..0000000000 --- a/crates/vim/test_data/test_change_around_word/70head.txt +++ /dev/null @@ -1 +0,0 @@ -0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/71mode.txt b/crates/vim/test_data/test_change_around_word/71mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/71mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/72text.txt b/crates/vim/test_data/test_change_around_word/72text.txt deleted file mode 100644 index 366beed733..0000000000 --- a/crates/vim/test_data/test_change_around_word/72text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/73head.txt b/crates/vim/test_data/test_change_around_word/73head.txt deleted file mode 100644 index 352f6067e6..0000000000 --- a/crates/vim/test_data/test_change_around_word/73head.txt +++ /dev/null @@ -1 +0,0 @@ -0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/74mode.txt b/crates/vim/test_data/test_change_around_word/74mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/74mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/75text.txt b/crates/vim/test_data/test_change_around_word/75text.txt deleted file mode 100644 index 3678c219a3..0000000000 --- a/crates/vim/test_data/test_change_around_word/75text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/76head.txt b/crates/vim/test_data/test_change_around_word/76head.txt deleted file mode 100644 index 5f7b20f0d9..0000000000 --- a/crates/vim/test_data/test_change_around_word/76head.txt +++ /dev/null @@ -1 +0,0 @@ -0,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/77mode.txt b/crates/vim/test_data/test_change_around_word/77mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/77mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/78text.txt b/crates/vim/test_data/test_change_around_word/78text.txt deleted file mode 100644 index 2feb66cfb9..0000000000 --- a/crates/vim/test_data/test_change_around_word/78text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/79head.txt b/crates/vim/test_data/test_change_around_word/79head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_change_around_word/79head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/7head.txt b/crates/vim/test_data/test_change_around_word/7head.txt deleted file mode 100644 index 5f7b20f0d9..0000000000 --- a/crates/vim/test_data/test_change_around_word/7head.txt +++ /dev/null @@ -1 +0,0 @@ -0,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/80mode.txt b/crates/vim/test_data/test_change_around_word/80mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/80mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/81text.txt b/crates/vim/test_data/test_change_around_word/81text.txt deleted file mode 100644 index 2feb66cfb9..0000000000 --- a/crates/vim/test_data/test_change_around_word/81text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/82head.txt b/crates/vim/test_data/test_change_around_word/82head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_change_around_word/82head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/83mode.txt b/crates/vim/test_data/test_change_around_word/83mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/83mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/84text.txt b/crates/vim/test_data/test_change_around_word/84text.txt deleted file mode 100644 index 262feeae96..0000000000 --- a/crates/vim/test_data/test_change_around_word/84text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/85head.txt b/crates/vim/test_data/test_change_around_word/85head.txt deleted file mode 100644 index bd712da63a..0000000000 --- a/crates/vim/test_data/test_change_around_word/85head.txt +++ /dev/null @@ -1 +0,0 @@ -1,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/86mode.txt b/crates/vim/test_data/test_change_around_word/86mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/86mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/87text.txt b/crates/vim/test_data/test_change_around_word/87text.txt deleted file mode 100644 index ba0aea82d9..0000000000 --- a/crates/vim/test_data/test_change_around_word/87text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/88head.txt b/crates/vim/test_data/test_change_around_word/88head.txt deleted file mode 100644 index 28ce8bcc5c..0000000000 --- a/crates/vim/test_data/test_change_around_word/88head.txt +++ /dev/null @@ -1 +0,0 @@ -2,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/89mode.txt b/crates/vim/test_data/test_change_around_word/89mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/89mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/8mode.txt b/crates/vim/test_data/test_change_around_word/8mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/8mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/90text.txt b/crates/vim/test_data/test_change_around_word/90text.txt deleted file mode 100644 index 783e049f86..0000000000 --- a/crates/vim/test_data/test_change_around_word/90text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/91head.txt b/crates/vim/test_data/test_change_around_word/91head.txt deleted file mode 100644 index 7cad756336..0000000000 --- a/crates/vim/test_data/test_change_around_word/91head.txt +++ /dev/null @@ -1 +0,0 @@ -3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/92mode.txt b/crates/vim/test_data/test_change_around_word/92mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/92mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/93text.txt b/crates/vim/test_data/test_change_around_word/93text.txt deleted file mode 100644 index 783e049f86..0000000000 --- a/crates/vim/test_data/test_change_around_word/93text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/94head.txt b/crates/vim/test_data/test_change_around_word/94head.txt deleted file mode 100644 index b26cd1f1d3..0000000000 --- a/crates/vim/test_data/test_change_around_word/94head.txt +++ /dev/null @@ -1 +0,0 @@ -4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/95mode.txt b/crates/vim/test_data/test_change_around_word/95mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/95mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/96text.txt b/crates/vim/test_data/test_change_around_word/96text.txt deleted file mode 100644 index 523f6002f4..0000000000 --- a/crates/vim/test_data/test_change_around_word/96text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/97head.txt b/crates/vim/test_data/test_change_around_word/97head.txt deleted file mode 100644 index 880fda949a..0000000000 --- a/crates/vim/test_data/test_change_around_word/97head.txt +++ /dev/null @@ -1 +0,0 @@ -5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/98mode.txt b/crates/vim/test_data/test_change_around_word/98mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_around_word/98mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word/99text.txt b/crates/vim/test_data/test_change_around_word/99text.txt deleted file mode 100644 index c8a5876179..0000000000 --- a/crates/vim/test_data/test_change_around_word/99text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_around_word/9text.txt b/crates/vim/test_data/test_change_around_word/9text.txt deleted file mode 100644 index 2feb66cfb9..0000000000 --- a/crates/vim/test_data/test_change_around_word/9text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_sentence.json b/crates/vim/test_data/test_change_in_sentence.json new file mode 100644 index 0000000000..08dbca368a --- /dev/null +++ b/crates/vim/test_data/test_change_in_sentence.json @@ -0,0 +1 @@ +[{"Text":" Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown?Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":16}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!Over the lazy."},{"Head":{"row":0,"column":27}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps! "},{"Head":{"row":0,"column":28}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps! "},{"Head":{"row":0,"column":28}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps! "},{"Head":{"row":0,"column":28}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.The quick \nbrown fox jumps over\n"},{"Head":{"row":2,"column":13}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. \n"},{"Head":{"row":2,"column":14}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. \n"},{"Head":{"row":2,"column":14}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" "},{"Head":{"row":0,"column":21}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" "},{"Head":{"row":0,"column":21}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" Brown fox jumps."},{"Head":{"row":0,"column":37}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/0text.txt b/crates/vim/test_data/test_change_in_sentence/0text.txt deleted file mode 100644 index fd36730b8d..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/0text.txt +++ /dev/null @@ -1 +0,0 @@ - Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/10head.txt b/crates/vim/test_data/test_change_in_sentence/10head.txt deleted file mode 100644 index 5df80e23c6..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/10head.txt +++ /dev/null @@ -1 +0,0 @@ -0,16 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/11mode.txt b/crates/vim/test_data/test_change_in_sentence/11mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/11mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/12text.txt b/crates/vim/test_data/test_change_in_sentence/12text.txt deleted file mode 100644 index 40eacc6489..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/12text.txt +++ /dev/null @@ -1 +0,0 @@ -The quick brown? Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/13head.txt b/crates/vim/test_data/test_change_in_sentence/13head.txt deleted file mode 100644 index 40d2f2ab55..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/13head.txt +++ /dev/null @@ -1 +0,0 @@ -0,17 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/14mode.txt b/crates/vim/test_data/test_change_in_sentence/14mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/14mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/15text.txt b/crates/vim/test_data/test_change_in_sentence/15text.txt deleted file mode 100644 index 40eacc6489..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/15text.txt +++ /dev/null @@ -1 +0,0 @@ -The quick brown? Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/16head.txt b/crates/vim/test_data/test_change_in_sentence/16head.txt deleted file mode 100644 index 40d2f2ab55..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/16head.txt +++ /dev/null @@ -1 +0,0 @@ -0,17 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/17mode.txt b/crates/vim/test_data/test_change_in_sentence/17mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/17mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/18text.txt b/crates/vim/test_data/test_change_in_sentence/18text.txt deleted file mode 100644 index 40eacc6489..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/18text.txt +++ /dev/null @@ -1 +0,0 @@ -The quick brown? Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/19head.txt b/crates/vim/test_data/test_change_in_sentence/19head.txt deleted file mode 100644 index 40d2f2ab55..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/19head.txt +++ /dev/null @@ -1 +0,0 @@ -0,17 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/1head.txt b/crates/vim/test_data/test_change_in_sentence/1head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/1head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/20mode.txt b/crates/vim/test_data/test_change_in_sentence/20mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/20mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/21text.txt b/crates/vim/test_data/test_change_in_sentence/21text.txt deleted file mode 100644 index ef17a5f7fb..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/21text.txt +++ /dev/null @@ -1 +0,0 @@ -The quick brown? Fox Jumps!Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/22head.txt b/crates/vim/test_data/test_change_in_sentence/22head.txt deleted file mode 100644 index 17beb4d83f..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/22head.txt +++ /dev/null @@ -1 +0,0 @@ -0,27 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/23mode.txt b/crates/vim/test_data/test_change_in_sentence/23mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/23mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/24text.txt b/crates/vim/test_data/test_change_in_sentence/24text.txt deleted file mode 100644 index d81c10974f..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/24text.txt +++ /dev/null @@ -1 +0,0 @@ -The quick brown? Fox Jumps! \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/25head.txt b/crates/vim/test_data/test_change_in_sentence/25head.txt deleted file mode 100644 index 588ae97095..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/25head.txt +++ /dev/null @@ -1 +0,0 @@ -0,28 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/26mode.txt b/crates/vim/test_data/test_change_in_sentence/26mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/26mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/27text.txt b/crates/vim/test_data/test_change_in_sentence/27text.txt deleted file mode 100644 index d81c10974f..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/27text.txt +++ /dev/null @@ -1 +0,0 @@ -The quick brown? Fox Jumps! \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/28head.txt b/crates/vim/test_data/test_change_in_sentence/28head.txt deleted file mode 100644 index 588ae97095..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/28head.txt +++ /dev/null @@ -1 +0,0 @@ -0,28 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/29mode.txt b/crates/vim/test_data/test_change_in_sentence/29mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/29mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/2mode.txt b/crates/vim/test_data/test_change_in_sentence/2mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/2mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/30text.txt b/crates/vim/test_data/test_change_in_sentence/30text.txt deleted file mode 100644 index d81c10974f..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/30text.txt +++ /dev/null @@ -1 +0,0 @@ -The quick brown? Fox Jumps! \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/31head.txt b/crates/vim/test_data/test_change_in_sentence/31head.txt deleted file mode 100644 index 588ae97095..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/31head.txt +++ /dev/null @@ -1 +0,0 @@ -0,28 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/32mode.txt b/crates/vim/test_data/test_change_in_sentence/32mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/32mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/33text.txt b/crates/vim/test_data/test_change_in_sentence/33text.txt deleted file mode 100644 index 561ed0800c..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/33text.txt +++ /dev/null @@ -1,2 +0,0 @@ - The quick -brown fox jumps over diff --git a/crates/vim/test_data/test_change_in_sentence/34head.txt b/crates/vim/test_data/test_change_in_sentence/34head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/34head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/35mode.txt b/crates/vim/test_data/test_change_in_sentence/35mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/35mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/36text.txt b/crates/vim/test_data/test_change_in_sentence/36text.txt deleted file mode 100644 index 561ed0800c..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/36text.txt +++ /dev/null @@ -1,2 +0,0 @@ - The quick -brown fox jumps over diff --git a/crates/vim/test_data/test_change_in_sentence/37head.txt b/crates/vim/test_data/test_change_in_sentence/37head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/37head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/38mode.txt b/crates/vim/test_data/test_change_in_sentence/38mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/38mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/39text.txt b/crates/vim/test_data/test_change_in_sentence/39text.txt deleted file mode 100644 index 561ed0800c..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/39text.txt +++ /dev/null @@ -1,2 +0,0 @@ - The quick -brown fox jumps over diff --git a/crates/vim/test_data/test_change_in_sentence/3text.txt b/crates/vim/test_data/test_change_in_sentence/3text.txt deleted file mode 100644 index fd36730b8d..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/3text.txt +++ /dev/null @@ -1 +0,0 @@ - Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/40head.txt b/crates/vim/test_data/test_change_in_sentence/40head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/40head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/41mode.txt b/crates/vim/test_data/test_change_in_sentence/41mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/41mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/42text.txt b/crates/vim/test_data/test_change_in_sentence/42text.txt deleted file mode 100644 index 561ed0800c..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/42text.txt +++ /dev/null @@ -1,2 +0,0 @@ - The quick -brown fox jumps over diff --git a/crates/vim/test_data/test_change_in_sentence/43head.txt b/crates/vim/test_data/test_change_in_sentence/43head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/43head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/44mode.txt b/crates/vim/test_data/test_change_in_sentence/44mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/44mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/45text.txt b/crates/vim/test_data/test_change_in_sentence/45text.txt deleted file mode 100644 index 561ed0800c..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/45text.txt +++ /dev/null @@ -1,2 +0,0 @@ - The quick -brown fox jumps over diff --git a/crates/vim/test_data/test_change_in_sentence/46head.txt b/crates/vim/test_data/test_change_in_sentence/46head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/46head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/47mode.txt b/crates/vim/test_data/test_change_in_sentence/47mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/47mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/48text.txt b/crates/vim/test_data/test_change_in_sentence/48text.txt deleted file mode 100644 index bbf10c5693..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/48text.txt +++ /dev/null @@ -1,4 +0,0 @@ -The quick brown -fox jumps over -the lazy dog.The quick -brown fox jumps over diff --git a/crates/vim/test_data/test_change_in_sentence/49head.txt b/crates/vim/test_data/test_change_in_sentence/49head.txt deleted file mode 100644 index 26560586d9..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/49head.txt +++ /dev/null @@ -1 +0,0 @@ -2,13 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/4head.txt b/crates/vim/test_data/test_change_in_sentence/4head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/4head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/50mode.txt b/crates/vim/test_data/test_change_in_sentence/50mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/50mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/51text.txt b/crates/vim/test_data/test_change_in_sentence/51text.txt deleted file mode 100644 index 0b535bbbaf..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/51text.txt +++ /dev/null @@ -1,3 +0,0 @@ -The quick brown -fox jumps over -the lazy dog. diff --git a/crates/vim/test_data/test_change_in_sentence/52head.txt b/crates/vim/test_data/test_change_in_sentence/52head.txt deleted file mode 100644 index e9aafafade..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/52head.txt +++ /dev/null @@ -1 +0,0 @@ -2,14 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/53mode.txt b/crates/vim/test_data/test_change_in_sentence/53mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/53mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/54text.txt b/crates/vim/test_data/test_change_in_sentence/54text.txt deleted file mode 100644 index 0b535bbbaf..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/54text.txt +++ /dev/null @@ -1,3 +0,0 @@ -The quick brown -fox jumps over -the lazy dog. diff --git a/crates/vim/test_data/test_change_in_sentence/55head.txt b/crates/vim/test_data/test_change_in_sentence/55head.txt deleted file mode 100644 index e9aafafade..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/55head.txt +++ /dev/null @@ -1 +0,0 @@ -2,14 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/56mode.txt b/crates/vim/test_data/test_change_in_sentence/56mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/56mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/5mode.txt b/crates/vim/test_data/test_change_in_sentence/5mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/5mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/6text.txt b/crates/vim/test_data/test_change_in_sentence/6text.txt deleted file mode 100644 index fd36730b8d..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/6text.txt +++ /dev/null @@ -1 +0,0 @@ - Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/7head.txt b/crates/vim/test_data/test_change_in_sentence/7head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/7head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/8mode.txt b/crates/vim/test_data/test_change_in_sentence/8mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/8mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence/9text.txt b/crates/vim/test_data/test_change_in_sentence/9text.txt deleted file mode 100644 index 9e4b6dcc50..0000000000 --- a/crates/vim/test_data/test_change_in_sentence/9text.txt +++ /dev/null @@ -1 +0,0 @@ -The quick brown?Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word.json b/crates/vim/test_data/test_change_in_word.json new file mode 100644 index 0000000000..eaa390e50f --- /dev/null +++ b/crates/vim/test_data/test_change_in_word.json @@ -0,0 +1 @@ +[{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":15}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumpsover\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":2,"column":12}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":4,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":5,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThequick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":3}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe- brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe- brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quickbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown\n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":15}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n\n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":7,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n\n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":8,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \nfox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox- over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":6}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n\n"},{"Head":{"row":10,"column":12}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":11,"column":0}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":15}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumpsover\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":2,"column":12}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":4,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":5,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quickbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown\n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":15}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n\n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":7,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n\n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":8,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \nfox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":2}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n\n"},{"Head":{"row":10,"column":12}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":11,"column":0}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/0text.txt b/crates/vim/test_data/test_change_in_word/0text.txt deleted file mode 100644 index 481389e676..0000000000 --- a/crates/vim/test_data/test_change_in_word/0text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/100head.txt b/crates/vim/test_data/test_change_in_word/100head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_change_in_word/100head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/101mode.txt b/crates/vim/test_data/test_change_in_word/101mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/101mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/102text.txt b/crates/vim/test_data/test_change_in_word/102text.txt deleted file mode 100644 index d90b2c4e48..0000000000 --- a/crates/vim/test_data/test_change_in_word/102text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - - brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/103head.txt b/crates/vim/test_data/test_change_in_word/103head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_change_in_word/103head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/104mode.txt b/crates/vim/test_data/test_change_in_word/104mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/104mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/105text.txt b/crates/vim/test_data/test_change_in_word/105text.txt deleted file mode 100644 index d90b2c4e48..0000000000 --- a/crates/vim/test_data/test_change_in_word/105text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - - brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/106head.txt b/crates/vim/test_data/test_change_in_word/106head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_change_in_word/106head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/107mode.txt b/crates/vim/test_data/test_change_in_word/107mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/107mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/108text.txt b/crates/vim/test_data/test_change_in_word/108text.txt deleted file mode 100644 index d90b2c4e48..0000000000 --- a/crates/vim/test_data/test_change_in_word/108text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - - brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/109head.txt b/crates/vim/test_data/test_change_in_word/109head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_change_in_word/109head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/10head.txt b/crates/vim/test_data/test_change_in_word/10head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_change_in_word/10head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/110mode.txt b/crates/vim/test_data/test_change_in_word/110mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/110mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/111text.txt b/crates/vim/test_data/test_change_in_word/111text.txt deleted file mode 100644 index 7d13d92d35..0000000000 --- a/crates/vim/test_data/test_change_in_word/111text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quickbrown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/112head.txt b/crates/vim/test_data/test_change_in_word/112head.txt deleted file mode 100644 index da809253d8..0000000000 --- a/crates/vim/test_data/test_change_in_word/112head.txt +++ /dev/null @@ -1 +0,0 @@ -6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/113mode.txt b/crates/vim/test_data/test_change_in_word/113mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/113mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/114text.txt b/crates/vim/test_data/test_change_in_word/114text.txt deleted file mode 100644 index 95b76ce25b..0000000000 --- a/crates/vim/test_data/test_change_in_word/114text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/115head.txt b/crates/vim/test_data/test_change_in_word/115head.txt deleted file mode 100644 index c5a50698dd..0000000000 --- a/crates/vim/test_data/test_change_in_word/115head.txt +++ /dev/null @@ -1 +0,0 @@ -6,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/116mode.txt b/crates/vim/test_data/test_change_in_word/116mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/116mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/117text.txt b/crates/vim/test_data/test_change_in_word/117text.txt deleted file mode 100644 index 805a5ebe4c..0000000000 --- a/crates/vim/test_data/test_change_in_word/117text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/118head.txt b/crates/vim/test_data/test_change_in_word/118head.txt deleted file mode 100644 index d933eb81fa..0000000000 --- a/crates/vim/test_data/test_change_in_word/118head.txt +++ /dev/null @@ -1 +0,0 @@ -6,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/119mode.txt b/crates/vim/test_data/test_change_in_word/119mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/119mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/11mode.txt b/crates/vim/test_data/test_change_in_word/11mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/11mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/120text.txt b/crates/vim/test_data/test_change_in_word/120text.txt deleted file mode 100644 index 1d797e34b8..0000000000 --- a/crates/vim/test_data/test_change_in_word/120text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/121head.txt b/crates/vim/test_data/test_change_in_word/121head.txt deleted file mode 100644 index 72c23ba506..0000000000 --- a/crates/vim/test_data/test_change_in_word/121head.txt +++ /dev/null @@ -1 +0,0 @@ -7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/122mode.txt b/crates/vim/test_data/test_change_in_word/122mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/122mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/123text.txt b/crates/vim/test_data/test_change_in_word/123text.txt deleted file mode 100644 index 8a35adfa26..0000000000 --- a/crates/vim/test_data/test_change_in_word/123text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/124head.txt b/crates/vim/test_data/test_change_in_word/124head.txt deleted file mode 100644 index ded56a5926..0000000000 --- a/crates/vim/test_data/test_change_in_word/124head.txt +++ /dev/null @@ -1 +0,0 @@ -8,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/125mode.txt b/crates/vim/test_data/test_change_in_word/125mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/125mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/126text.txt b/crates/vim/test_data/test_change_in_word/126text.txt deleted file mode 100644 index 26e9d0a1b6..0000000000 --- a/crates/vim/test_data/test_change_in_word/126text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - -fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/127head.txt b/crates/vim/test_data/test_change_in_word/127head.txt deleted file mode 100644 index 8e69e0ad79..0000000000 --- a/crates/vim/test_data/test_change_in_word/127head.txt +++ /dev/null @@ -1 +0,0 @@ -9,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/128mode.txt b/crates/vim/test_data/test_change_in_word/128mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/128mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/129text.txt b/crates/vim/test_data/test_change_in_word/129text.txt deleted file mode 100644 index c2c9c0edb0..0000000000 --- a/crates/vim/test_data/test_change_in_word/129text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/12text.txt b/crates/vim/test_data/test_change_in_word/12text.txt deleted file mode 100644 index 38c6ccbeac..0000000000 --- a/crates/vim/test_data/test_change_in_word/12text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/130head.txt b/crates/vim/test_data/test_change_in_word/130head.txt deleted file mode 100644 index edb3544f9a..0000000000 --- a/crates/vim/test_data/test_change_in_word/130head.txt +++ /dev/null @@ -1 +0,0 @@ -9,2 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/131mode.txt b/crates/vim/test_data/test_change_in_word/131mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/131mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/132text.txt b/crates/vim/test_data/test_change_in_word/132text.txt deleted file mode 100644 index 4b2cd80611..0000000000 --- a/crates/vim/test_data/test_change_in_word/132text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/133head.txt b/crates/vim/test_data/test_change_in_word/133head.txt deleted file mode 100644 index 00f5ae6cbb..0000000000 --- a/crates/vim/test_data/test_change_in_word/133head.txt +++ /dev/null @@ -1 +0,0 @@ -10,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/134mode.txt b/crates/vim/test_data/test_change_in_word/134mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/134mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/135text.txt b/crates/vim/test_data/test_change_in_word/135text.txt deleted file mode 100644 index e971227436..0000000000 --- a/crates/vim/test_data/test_change_in_word/135text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/136head.txt b/crates/vim/test_data/test_change_in_word/136head.txt deleted file mode 100644 index 94dbddc699..0000000000 --- a/crates/vim/test_data/test_change_in_word/136head.txt +++ /dev/null @@ -1 +0,0 @@ -11,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/137mode.txt b/crates/vim/test_data/test_change_in_word/137mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/137mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/13head.txt b/crates/vim/test_data/test_change_in_word/13head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_change_in_word/13head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/14mode.txt b/crates/vim/test_data/test_change_in_word/14mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/14mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/15text.txt b/crates/vim/test_data/test_change_in_word/15text.txt deleted file mode 100644 index 282218da91..0000000000 --- a/crates/vim/test_data/test_change_in_word/15text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumpsover -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/16head.txt b/crates/vim/test_data/test_change_in_word/16head.txt deleted file mode 100644 index bd712da63a..0000000000 --- a/crates/vim/test_data/test_change_in_word/16head.txt +++ /dev/null @@ -1 +0,0 @@ -1,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/17mode.txt b/crates/vim/test_data/test_change_in_word/17mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/17mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/18text.txt b/crates/vim/test_data/test_change_in_word/18text.txt deleted file mode 100644 index 0b042b7c39..0000000000 --- a/crates/vim/test_data/test_change_in_word/18text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/19head.txt b/crates/vim/test_data/test_change_in_word/19head.txt deleted file mode 100644 index 28ce8bcc5c..0000000000 --- a/crates/vim/test_data/test_change_in_word/19head.txt +++ /dev/null @@ -1 +0,0 @@ -2,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/1head.txt b/crates/vim/test_data/test_change_in_word/1head.txt deleted file mode 100644 index 352f6067e6..0000000000 --- a/crates/vim/test_data/test_change_in_word/1head.txt +++ /dev/null @@ -1 +0,0 @@ -0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/20mode.txt b/crates/vim/test_data/test_change_in_word/20mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/20mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/21text.txt b/crates/vim/test_data/test_change_in_word/21text.txt deleted file mode 100644 index e971227436..0000000000 --- a/crates/vim/test_data/test_change_in_word/21text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/22head.txt b/crates/vim/test_data/test_change_in_word/22head.txt deleted file mode 100644 index 7cad756336..0000000000 --- a/crates/vim/test_data/test_change_in_word/22head.txt +++ /dev/null @@ -1 +0,0 @@ -3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/23mode.txt b/crates/vim/test_data/test_change_in_word/23mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/23mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/24text.txt b/crates/vim/test_data/test_change_in_word/24text.txt deleted file mode 100644 index e971227436..0000000000 --- a/crates/vim/test_data/test_change_in_word/24text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/25head.txt b/crates/vim/test_data/test_change_in_word/25head.txt deleted file mode 100644 index b26cd1f1d3..0000000000 --- a/crates/vim/test_data/test_change_in_word/25head.txt +++ /dev/null @@ -1 +0,0 @@ -4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/26mode.txt b/crates/vim/test_data/test_change_in_word/26mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/26mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/27text.txt b/crates/vim/test_data/test_change_in_word/27text.txt deleted file mode 100644 index e971227436..0000000000 --- a/crates/vim/test_data/test_change_in_word/27text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/28head.txt b/crates/vim/test_data/test_change_in_word/28head.txt deleted file mode 100644 index 880fda949a..0000000000 --- a/crates/vim/test_data/test_change_in_word/28head.txt +++ /dev/null @@ -1 +0,0 @@ -5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/29mode.txt b/crates/vim/test_data/test_change_in_word/29mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/29mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/2mode.txt b/crates/vim/test_data/test_change_in_word/2mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/2mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/30text.txt b/crates/vim/test_data/test_change_in_word/30text.txt deleted file mode 100644 index 609747299b..0000000000 --- a/crates/vim/test_data/test_change_in_word/30text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - --quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/31head.txt b/crates/vim/test_data/test_change_in_word/31head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_change_in_word/31head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/32mode.txt b/crates/vim/test_data/test_change_in_word/32mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/32mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/33text.txt b/crates/vim/test_data/test_change_in_word/33text.txt deleted file mode 100644 index 12ede0f513..0000000000 --- a/crates/vim/test_data/test_change_in_word/33text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -Thequick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/34head.txt b/crates/vim/test_data/test_change_in_word/34head.txt deleted file mode 100644 index c20df71d5e..0000000000 --- a/crates/vim/test_data/test_change_in_word/34head.txt +++ /dev/null @@ -1 +0,0 @@ -6,3 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/35mode.txt b/crates/vim/test_data/test_change_in_word/35mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/35mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/36text.txt b/crates/vim/test_data/test_change_in_word/36text.txt deleted file mode 100644 index 3b3f900a05..0000000000 --- a/crates/vim/test_data/test_change_in_word/36text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The- brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/37head.txt b/crates/vim/test_data/test_change_in_word/37head.txt deleted file mode 100644 index 95fb9c638e..0000000000 --- a/crates/vim/test_data/test_change_in_word/37head.txt +++ /dev/null @@ -1 +0,0 @@ -6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/38mode.txt b/crates/vim/test_data/test_change_in_word/38mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/38mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/39text.txt b/crates/vim/test_data/test_change_in_word/39text.txt deleted file mode 100644 index 3b3f900a05..0000000000 --- a/crates/vim/test_data/test_change_in_word/39text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The- brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/3text.txt b/crates/vim/test_data/test_change_in_word/3text.txt deleted file mode 100644 index 481389e676..0000000000 --- a/crates/vim/test_data/test_change_in_word/3text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/40head.txt b/crates/vim/test_data/test_change_in_word/40head.txt deleted file mode 100644 index 95fb9c638e..0000000000 --- a/crates/vim/test_data/test_change_in_word/40head.txt +++ /dev/null @@ -1 +0,0 @@ -6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/41mode.txt b/crates/vim/test_data/test_change_in_word/41mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/41mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/42text.txt b/crates/vim/test_data/test_change_in_word/42text.txt deleted file mode 100644 index 7d13d92d35..0000000000 --- a/crates/vim/test_data/test_change_in_word/42text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quickbrown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/43head.txt b/crates/vim/test_data/test_change_in_word/43head.txt deleted file mode 100644 index da809253d8..0000000000 --- a/crates/vim/test_data/test_change_in_word/43head.txt +++ /dev/null @@ -1 +0,0 @@ -6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/44mode.txt b/crates/vim/test_data/test_change_in_word/44mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/44mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/45text.txt b/crates/vim/test_data/test_change_in_word/45text.txt deleted file mode 100644 index 95b76ce25b..0000000000 --- a/crates/vim/test_data/test_change_in_word/45text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/46head.txt b/crates/vim/test_data/test_change_in_word/46head.txt deleted file mode 100644 index c5a50698dd..0000000000 --- a/crates/vim/test_data/test_change_in_word/46head.txt +++ /dev/null @@ -1 +0,0 @@ -6,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/47mode.txt b/crates/vim/test_data/test_change_in_word/47mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/47mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/48text.txt b/crates/vim/test_data/test_change_in_word/48text.txt deleted file mode 100644 index 805a5ebe4c..0000000000 --- a/crates/vim/test_data/test_change_in_word/48text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/49head.txt b/crates/vim/test_data/test_change_in_word/49head.txt deleted file mode 100644 index d933eb81fa..0000000000 --- a/crates/vim/test_data/test_change_in_word/49head.txt +++ /dev/null @@ -1 +0,0 @@ -6,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/4head.txt b/crates/vim/test_data/test_change_in_word/4head.txt deleted file mode 100644 index 352f6067e6..0000000000 --- a/crates/vim/test_data/test_change_in_word/4head.txt +++ /dev/null @@ -1 +0,0 @@ -0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/50mode.txt b/crates/vim/test_data/test_change_in_word/50mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/50mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/51text.txt b/crates/vim/test_data/test_change_in_word/51text.txt deleted file mode 100644 index 1d797e34b8..0000000000 --- a/crates/vim/test_data/test_change_in_word/51text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/52head.txt b/crates/vim/test_data/test_change_in_word/52head.txt deleted file mode 100644 index 72c23ba506..0000000000 --- a/crates/vim/test_data/test_change_in_word/52head.txt +++ /dev/null @@ -1 +0,0 @@ -7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/53mode.txt b/crates/vim/test_data/test_change_in_word/53mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/53mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/54text.txt b/crates/vim/test_data/test_change_in_word/54text.txt deleted file mode 100644 index 8a35adfa26..0000000000 --- a/crates/vim/test_data/test_change_in_word/54text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/55head.txt b/crates/vim/test_data/test_change_in_word/55head.txt deleted file mode 100644 index ded56a5926..0000000000 --- a/crates/vim/test_data/test_change_in_word/55head.txt +++ /dev/null @@ -1 +0,0 @@ -8,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/56mode.txt b/crates/vim/test_data/test_change_in_word/56mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/56mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/57text.txt b/crates/vim/test_data/test_change_in_word/57text.txt deleted file mode 100644 index 26e9d0a1b6..0000000000 --- a/crates/vim/test_data/test_change_in_word/57text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - -fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/58head.txt b/crates/vim/test_data/test_change_in_word/58head.txt deleted file mode 100644 index 8e69e0ad79..0000000000 --- a/crates/vim/test_data/test_change_in_word/58head.txt +++ /dev/null @@ -1 +0,0 @@ -9,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/59mode.txt b/crates/vim/test_data/test_change_in_word/59mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/59mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/5mode.txt b/crates/vim/test_data/test_change_in_word/5mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/5mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/60text.txt b/crates/vim/test_data/test_change_in_word/60text.txt deleted file mode 100644 index 816bf47cf0..0000000000 --- a/crates/vim/test_data/test_change_in_word/60text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox- over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/61head.txt b/crates/vim/test_data/test_change_in_word/61head.txt deleted file mode 100644 index e0460d1bc5..0000000000 --- a/crates/vim/test_data/test_change_in_word/61head.txt +++ /dev/null @@ -1 +0,0 @@ -9,6 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/62mode.txt b/crates/vim/test_data/test_change_in_word/62mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/62mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/63text.txt b/crates/vim/test_data/test_change_in_word/63text.txt deleted file mode 100644 index 4b2cd80611..0000000000 --- a/crates/vim/test_data/test_change_in_word/63text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/64head.txt b/crates/vim/test_data/test_change_in_word/64head.txt deleted file mode 100644 index 00f5ae6cbb..0000000000 --- a/crates/vim/test_data/test_change_in_word/64head.txt +++ /dev/null @@ -1 +0,0 @@ -10,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/65mode.txt b/crates/vim/test_data/test_change_in_word/65mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/65mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/66text.txt b/crates/vim/test_data/test_change_in_word/66text.txt deleted file mode 100644 index e971227436..0000000000 --- a/crates/vim/test_data/test_change_in_word/66text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/67head.txt b/crates/vim/test_data/test_change_in_word/67head.txt deleted file mode 100644 index 94dbddc699..0000000000 --- a/crates/vim/test_data/test_change_in_word/67head.txt +++ /dev/null @@ -1 +0,0 @@ -11,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/68mode.txt b/crates/vim/test_data/test_change_in_word/68mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/68mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/69text.txt b/crates/vim/test_data/test_change_in_word/69text.txt deleted file mode 100644 index 481389e676..0000000000 --- a/crates/vim/test_data/test_change_in_word/69text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/6text.txt b/crates/vim/test_data/test_change_in_word/6text.txt deleted file mode 100644 index 35fd5c89ba..0000000000 --- a/crates/vim/test_data/test_change_in_word/6text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/70head.txt b/crates/vim/test_data/test_change_in_word/70head.txt deleted file mode 100644 index 352f6067e6..0000000000 --- a/crates/vim/test_data/test_change_in_word/70head.txt +++ /dev/null @@ -1 +0,0 @@ -0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/71mode.txt b/crates/vim/test_data/test_change_in_word/71mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/71mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/72text.txt b/crates/vim/test_data/test_change_in_word/72text.txt deleted file mode 100644 index 481389e676..0000000000 --- a/crates/vim/test_data/test_change_in_word/72text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/73head.txt b/crates/vim/test_data/test_change_in_word/73head.txt deleted file mode 100644 index 352f6067e6..0000000000 --- a/crates/vim/test_data/test_change_in_word/73head.txt +++ /dev/null @@ -1 +0,0 @@ -0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/74mode.txt b/crates/vim/test_data/test_change_in_word/74mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/74mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/75text.txt b/crates/vim/test_data/test_change_in_word/75text.txt deleted file mode 100644 index 35fd5c89ba..0000000000 --- a/crates/vim/test_data/test_change_in_word/75text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/76head.txt b/crates/vim/test_data/test_change_in_word/76head.txt deleted file mode 100644 index 5f7b20f0d9..0000000000 --- a/crates/vim/test_data/test_change_in_word/76head.txt +++ /dev/null @@ -1 +0,0 @@ -0,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/77mode.txt b/crates/vim/test_data/test_change_in_word/77mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/77mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/78text.txt b/crates/vim/test_data/test_change_in_word/78text.txt deleted file mode 100644 index 38c6ccbeac..0000000000 --- a/crates/vim/test_data/test_change_in_word/78text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/79head.txt b/crates/vim/test_data/test_change_in_word/79head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_change_in_word/79head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/7head.txt b/crates/vim/test_data/test_change_in_word/7head.txt deleted file mode 100644 index 5f7b20f0d9..0000000000 --- a/crates/vim/test_data/test_change_in_word/7head.txt +++ /dev/null @@ -1 +0,0 @@ -0,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/80mode.txt b/crates/vim/test_data/test_change_in_word/80mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/80mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/81text.txt b/crates/vim/test_data/test_change_in_word/81text.txt deleted file mode 100644 index 38c6ccbeac..0000000000 --- a/crates/vim/test_data/test_change_in_word/81text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/82head.txt b/crates/vim/test_data/test_change_in_word/82head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_change_in_word/82head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/83mode.txt b/crates/vim/test_data/test_change_in_word/83mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/83mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/84text.txt b/crates/vim/test_data/test_change_in_word/84text.txt deleted file mode 100644 index 282218da91..0000000000 --- a/crates/vim/test_data/test_change_in_word/84text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumpsover -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/85head.txt b/crates/vim/test_data/test_change_in_word/85head.txt deleted file mode 100644 index bd712da63a..0000000000 --- a/crates/vim/test_data/test_change_in_word/85head.txt +++ /dev/null @@ -1 +0,0 @@ -1,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/86mode.txt b/crates/vim/test_data/test_change_in_word/86mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/86mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/87text.txt b/crates/vim/test_data/test_change_in_word/87text.txt deleted file mode 100644 index 0b042b7c39..0000000000 --- a/crates/vim/test_data/test_change_in_word/87text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/88head.txt b/crates/vim/test_data/test_change_in_word/88head.txt deleted file mode 100644 index 28ce8bcc5c..0000000000 --- a/crates/vim/test_data/test_change_in_word/88head.txt +++ /dev/null @@ -1 +0,0 @@ -2,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/89mode.txt b/crates/vim/test_data/test_change_in_word/89mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/89mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/8mode.txt b/crates/vim/test_data/test_change_in_word/8mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/8mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/90text.txt b/crates/vim/test_data/test_change_in_word/90text.txt deleted file mode 100644 index e971227436..0000000000 --- a/crates/vim/test_data/test_change_in_word/90text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/91head.txt b/crates/vim/test_data/test_change_in_word/91head.txt deleted file mode 100644 index 7cad756336..0000000000 --- a/crates/vim/test_data/test_change_in_word/91head.txt +++ /dev/null @@ -1 +0,0 @@ -3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/92mode.txt b/crates/vim/test_data/test_change_in_word/92mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/92mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/93text.txt b/crates/vim/test_data/test_change_in_word/93text.txt deleted file mode 100644 index e971227436..0000000000 --- a/crates/vim/test_data/test_change_in_word/93text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/94head.txt b/crates/vim/test_data/test_change_in_word/94head.txt deleted file mode 100644 index b26cd1f1d3..0000000000 --- a/crates/vim/test_data/test_change_in_word/94head.txt +++ /dev/null @@ -1 +0,0 @@ -4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/95mode.txt b/crates/vim/test_data/test_change_in_word/95mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/95mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/96text.txt b/crates/vim/test_data/test_change_in_word/96text.txt deleted file mode 100644 index e971227436..0000000000 --- a/crates/vim/test_data/test_change_in_word/96text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/97head.txt b/crates/vim/test_data/test_change_in_word/97head.txt deleted file mode 100644 index 880fda949a..0000000000 --- a/crates/vim/test_data/test_change_in_word/97head.txt +++ /dev/null @@ -1 +0,0 @@ -5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/98mode.txt b/crates/vim/test_data/test_change_in_word/98mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_change_in_word/98mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word/99text.txt b/crates/vim/test_data/test_change_in_word/99text.txt deleted file mode 100644 index d90b2c4e48..0000000000 --- a/crates/vim/test_data/test_change_in_word/99text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - - brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_change_in_word/9text.txt b/crates/vim/test_data/test_change_in_word/9text.txt deleted file mode 100644 index 38c6ccbeac..0000000000 --- a/crates/vim/test_data/test_change_in_word/9text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_sentence.json b/crates/vim/test_data/test_delete_around_sentence.json new file mode 100644 index 0000000000..479f0cc77c --- /dev/null +++ b/crates/vim/test_data/test_delete_around_sentence.json @@ -0,0 +1 @@ +[{"Text":"Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!"},{"Head":{"row":0,"column":26}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!"},{"Head":{"row":0,"column":26}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!"},{"Head":{"row":0,"column":26}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!"},{"Head":{"row":0,"column":26}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Head":{"row":2,"column":12}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Head":{"row":2,"column":12}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Head":{"row":2,"column":12}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" "},{"Head":{"row":0,"column":20}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" "},{"Head":{"row":0,"column":20}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence/0text.txt b/crates/vim/test_data/test_delete_around_sentence/0text.txt deleted file mode 100644 index d2b92e8000..0000000000 --- a/crates/vim/test_data/test_delete_around_sentence/0text.txt +++ /dev/null @@ -1 +0,0 @@ -Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence/10head.txt b/crates/vim/test_data/test_delete_around_sentence/10head.txt deleted file mode 100644 index 5df80e23c6..0000000000 --- a/crates/vim/test_data/test_delete_around_sentence/10head.txt +++ /dev/null @@ -1 +0,0 @@ -0,16 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence/1head.txt b/crates/vim/test_data/test_delete_around_sentence/1head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_delete_around_sentence/1head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence/2mode.txt b/crates/vim/test_data/test_delete_around_sentence/2mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_sentence/2mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence/3text.txt b/crates/vim/test_data/test_delete_around_sentence/3text.txt deleted file mode 100644 index d2b92e8000..0000000000 --- a/crates/vim/test_data/test_delete_around_sentence/3text.txt +++ /dev/null @@ -1 +0,0 @@ -Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence/4head.txt b/crates/vim/test_data/test_delete_around_sentence/4head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_delete_around_sentence/4head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence/5mode.txt b/crates/vim/test_data/test_delete_around_sentence/5mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_sentence/5mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence/6text.txt b/crates/vim/test_data/test_delete_around_sentence/6text.txt deleted file mode 100644 index d2b92e8000..0000000000 --- a/crates/vim/test_data/test_delete_around_sentence/6text.txt +++ /dev/null @@ -1 +0,0 @@ -Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence/7head.txt b/crates/vim/test_data/test_delete_around_sentence/7head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_delete_around_sentence/7head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence/8mode.txt b/crates/vim/test_data/test_delete_around_sentence/8mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_sentence/8mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence/9text.txt b/crates/vim/test_data/test_delete_around_sentence/9text.txt deleted file mode 100644 index 5e3a12964d..0000000000 --- a/crates/vim/test_data/test_delete_around_sentence/9text.txt +++ /dev/null @@ -1 +0,0 @@ -The quick brown? Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word.json b/crates/vim/test_data/test_delete_around_word.json new file mode 100644 index 0000000000..37e370d3bd --- /dev/null +++ b/crates/vim/test_data/test_delete_around_word.json @@ -0,0 +1 @@ +[{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":9}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":15}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":8}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":2,"column":11}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":5,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThequick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":3}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":15}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n-jumps over\nthe lazy dog \n\n"},{"Head":{"row":7,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n-jumps over\nthe lazy dog \n\n"},{"Head":{"row":8,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n-jumps over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":6}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n"},{"Head":{"row":10,"column":11}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog "},{"Head":{"row":10,"column":0}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":9}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":15}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":8}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":2,"column":11}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":5,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":15}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n over\nthe lazy dog \n\n"},{"Head":{"row":7,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n over\nthe lazy dog \n\n"},{"Head":{"row":8,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":2}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n"},{"Head":{"row":10,"column":11}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog "},{"Head":{"row":10,"column":0}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/0text.txt b/crates/vim/test_data/test_delete_around_word/0text.txt deleted file mode 100644 index 366beed733..0000000000 --- a/crates/vim/test_data/test_delete_around_word/0text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/100head.txt b/crates/vim/test_data/test_delete_around_word/100head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_delete_around_word/100head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/101mode.txt b/crates/vim/test_data/test_delete_around_word/101mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/101mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/102text.txt b/crates/vim/test_data/test_delete_around_word/102text.txt deleted file mode 100644 index c8a5876179..0000000000 --- a/crates/vim/test_data/test_delete_around_word/102text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/103head.txt b/crates/vim/test_data/test_delete_around_word/103head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_delete_around_word/103head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/104mode.txt b/crates/vim/test_data/test_delete_around_word/104mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/104mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/105text.txt b/crates/vim/test_data/test_delete_around_word/105text.txt deleted file mode 100644 index c8a5876179..0000000000 --- a/crates/vim/test_data/test_delete_around_word/105text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/106head.txt b/crates/vim/test_data/test_delete_around_word/106head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_delete_around_word/106head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/107mode.txt b/crates/vim/test_data/test_delete_around_word/107mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/107mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/108text.txt b/crates/vim/test_data/test_delete_around_word/108text.txt deleted file mode 100644 index c8a5876179..0000000000 --- a/crates/vim/test_data/test_delete_around_word/108text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/109head.txt b/crates/vim/test_data/test_delete_around_word/109head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_delete_around_word/109head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/10head.txt b/crates/vim/test_data/test_delete_around_word/10head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_delete_around_word/10head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/110mode.txt b/crates/vim/test_data/test_delete_around_word/110mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/110mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/111text.txt b/crates/vim/test_data/test_delete_around_word/111text.txt deleted file mode 100644 index f05cdd88c1..0000000000 --- a/crates/vim/test_data/test_delete_around_word/111text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/112head.txt b/crates/vim/test_data/test_delete_around_word/112head.txt deleted file mode 100644 index da809253d8..0000000000 --- a/crates/vim/test_data/test_delete_around_word/112head.txt +++ /dev/null @@ -1 +0,0 @@ -6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/113mode.txt b/crates/vim/test_data/test_delete_around_word/113mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/113mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/114text.txt b/crates/vim/test_data/test_delete_around_word/114text.txt deleted file mode 100644 index f05cdd88c1..0000000000 --- a/crates/vim/test_data/test_delete_around_word/114text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/115head.txt b/crates/vim/test_data/test_delete_around_word/115head.txt deleted file mode 100644 index da809253d8..0000000000 --- a/crates/vim/test_data/test_delete_around_word/115head.txt +++ /dev/null @@ -1 +0,0 @@ -6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/116mode.txt b/crates/vim/test_data/test_delete_around_word/116mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/116mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/117text.txt b/crates/vim/test_data/test_delete_around_word/117text.txt deleted file mode 100644 index 0e9094161a..0000000000 --- a/crates/vim/test_data/test_delete_around_word/117text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/118head.txt b/crates/vim/test_data/test_delete_around_word/118head.txt deleted file mode 100644 index d933eb81fa..0000000000 --- a/crates/vim/test_data/test_delete_around_word/118head.txt +++ /dev/null @@ -1 +0,0 @@ -6,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/119mode.txt b/crates/vim/test_data/test_delete_around_word/119mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/119mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/11mode.txt b/crates/vim/test_data/test_delete_around_word/11mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/11mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/120text.txt b/crates/vim/test_data/test_delete_around_word/120text.txt deleted file mode 100644 index fa73eb37b8..0000000000 --- a/crates/vim/test_data/test_delete_around_word/120text.txt +++ /dev/null @@ -1,10 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/121head.txt b/crates/vim/test_data/test_delete_around_word/121head.txt deleted file mode 100644 index 72c23ba506..0000000000 --- a/crates/vim/test_data/test_delete_around_word/121head.txt +++ /dev/null @@ -1 +0,0 @@ -7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/122mode.txt b/crates/vim/test_data/test_delete_around_word/122mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/122mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/123text.txt b/crates/vim/test_data/test_delete_around_word/123text.txt deleted file mode 100644 index 4fe33b47eb..0000000000 --- a/crates/vim/test_data/test_delete_around_word/123text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/124head.txt b/crates/vim/test_data/test_delete_around_word/124head.txt deleted file mode 100644 index ded56a5926..0000000000 --- a/crates/vim/test_data/test_delete_around_word/124head.txt +++ /dev/null @@ -1 +0,0 @@ -8,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/125mode.txt b/crates/vim/test_data/test_delete_around_word/125mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/125mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/126text.txt b/crates/vim/test_data/test_delete_around_word/126text.txt deleted file mode 100644 index 35dd5816fc..0000000000 --- a/crates/vim/test_data/test_delete_around_word/126text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/127head.txt b/crates/vim/test_data/test_delete_around_word/127head.txt deleted file mode 100644 index 8e69e0ad79..0000000000 --- a/crates/vim/test_data/test_delete_around_word/127head.txt +++ /dev/null @@ -1 +0,0 @@ -9,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/128mode.txt b/crates/vim/test_data/test_delete_around_word/128mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/128mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/129text.txt b/crates/vim/test_data/test_delete_around_word/129text.txt deleted file mode 100644 index 1bac4587a7..0000000000 --- a/crates/vim/test_data/test_delete_around_word/129text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/12text.txt b/crates/vim/test_data/test_delete_around_word/12text.txt deleted file mode 100644 index 2feb66cfb9..0000000000 --- a/crates/vim/test_data/test_delete_around_word/12text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/130head.txt b/crates/vim/test_data/test_delete_around_word/130head.txt deleted file mode 100644 index edb3544f9a..0000000000 --- a/crates/vim/test_data/test_delete_around_word/130head.txt +++ /dev/null @@ -1 +0,0 @@ -9,2 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/131mode.txt b/crates/vim/test_data/test_delete_around_word/131mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/131mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/132text.txt b/crates/vim/test_data/test_delete_around_word/132text.txt deleted file mode 100644 index 912316a9f7..0000000000 --- a/crates/vim/test_data/test_delete_around_word/132text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_delete_around_word/133head.txt b/crates/vim/test_data/test_delete_around_word/133head.txt deleted file mode 100644 index 63ee4f53ac..0000000000 --- a/crates/vim/test_data/test_delete_around_word/133head.txt +++ /dev/null @@ -1 +0,0 @@ -10,11 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/134mode.txt b/crates/vim/test_data/test_delete_around_word/134mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/134mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/135text.txt b/crates/vim/test_data/test_delete_around_word/135text.txt deleted file mode 100644 index 3c4e8c2398..0000000000 --- a/crates/vim/test_data/test_delete_around_word/135text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/136head.txt b/crates/vim/test_data/test_delete_around_word/136head.txt deleted file mode 100644 index ec50c7d44f..0000000000 --- a/crates/vim/test_data/test_delete_around_word/136head.txt +++ /dev/null @@ -1 +0,0 @@ -10,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/137mode.txt b/crates/vim/test_data/test_delete_around_word/137mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/137mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/13head.txt b/crates/vim/test_data/test_delete_around_word/13head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_delete_around_word/13head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/14mode.txt b/crates/vim/test_data/test_delete_around_word/14mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/14mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/15text.txt b/crates/vim/test_data/test_delete_around_word/15text.txt deleted file mode 100644 index 262feeae96..0000000000 --- a/crates/vim/test_data/test_delete_around_word/15text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/16head.txt b/crates/vim/test_data/test_delete_around_word/16head.txt deleted file mode 100644 index 413d7dd4d2..0000000000 --- a/crates/vim/test_data/test_delete_around_word/16head.txt +++ /dev/null @@ -1 +0,0 @@ -1,8 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/17mode.txt b/crates/vim/test_data/test_delete_around_word/17mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/17mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/18text.txt b/crates/vim/test_data/test_delete_around_word/18text.txt deleted file mode 100644 index ba0aea82d9..0000000000 --- a/crates/vim/test_data/test_delete_around_word/18text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/19head.txt b/crates/vim/test_data/test_delete_around_word/19head.txt deleted file mode 100644 index 06ce34087d..0000000000 --- a/crates/vim/test_data/test_delete_around_word/19head.txt +++ /dev/null @@ -1 +0,0 @@ -2,11 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/1head.txt b/crates/vim/test_data/test_delete_around_word/1head.txt deleted file mode 100644 index 2871b36925..0000000000 --- a/crates/vim/test_data/test_delete_around_word/1head.txt +++ /dev/null @@ -1 +0,0 @@ -0,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/20mode.txt b/crates/vim/test_data/test_delete_around_word/20mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/20mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/21text.txt b/crates/vim/test_data/test_delete_around_word/21text.txt deleted file mode 100644 index ead25fb9d0..0000000000 --- a/crates/vim/test_data/test_delete_around_word/21text.txt +++ /dev/null @@ -1,10 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/22head.txt b/crates/vim/test_data/test_delete_around_word/22head.txt deleted file mode 100644 index 7cad756336..0000000000 --- a/crates/vim/test_data/test_delete_around_word/22head.txt +++ /dev/null @@ -1 +0,0 @@ -3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/23mode.txt b/crates/vim/test_data/test_delete_around_word/23mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/23mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/24text.txt b/crates/vim/test_data/test_delete_around_word/24text.txt deleted file mode 100644 index ead25fb9d0..0000000000 --- a/crates/vim/test_data/test_delete_around_word/24text.txt +++ /dev/null @@ -1,10 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/25head.txt b/crates/vim/test_data/test_delete_around_word/25head.txt deleted file mode 100644 index b26cd1f1d3..0000000000 --- a/crates/vim/test_data/test_delete_around_word/25head.txt +++ /dev/null @@ -1 +0,0 @@ -4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/26mode.txt b/crates/vim/test_data/test_delete_around_word/26mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/26mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/27text.txt b/crates/vim/test_data/test_delete_around_word/27text.txt deleted file mode 100644 index 252419d291..0000000000 --- a/crates/vim/test_data/test_delete_around_word/27text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - --quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/28head.txt b/crates/vim/test_data/test_delete_around_word/28head.txt deleted file mode 100644 index 880fda949a..0000000000 --- a/crates/vim/test_data/test_delete_around_word/28head.txt +++ /dev/null @@ -1 +0,0 @@ -5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/29mode.txt b/crates/vim/test_data/test_delete_around_word/29mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/29mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/2mode.txt b/crates/vim/test_data/test_delete_around_word/2mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/2mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/30text.txt b/crates/vim/test_data/test_delete_around_word/30text.txt deleted file mode 100644 index 609747299b..0000000000 --- a/crates/vim/test_data/test_delete_around_word/30text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - --quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/31head.txt b/crates/vim/test_data/test_delete_around_word/31head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_delete_around_word/31head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/32mode.txt b/crates/vim/test_data/test_delete_around_word/32mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/32mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/33text.txt b/crates/vim/test_data/test_delete_around_word/33text.txt deleted file mode 100644 index 12ede0f513..0000000000 --- a/crates/vim/test_data/test_delete_around_word/33text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -Thequick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/34head.txt b/crates/vim/test_data/test_delete_around_word/34head.txt deleted file mode 100644 index c20df71d5e..0000000000 --- a/crates/vim/test_data/test_delete_around_word/34head.txt +++ /dev/null @@ -1 +0,0 @@ -6,3 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/35mode.txt b/crates/vim/test_data/test_delete_around_word/35mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/35mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/36text.txt b/crates/vim/test_data/test_delete_around_word/36text.txt deleted file mode 100644 index 4a6d943982..0000000000 --- a/crates/vim/test_data/test_delete_around_word/36text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/37head.txt b/crates/vim/test_data/test_delete_around_word/37head.txt deleted file mode 100644 index 95fb9c638e..0000000000 --- a/crates/vim/test_data/test_delete_around_word/37head.txt +++ /dev/null @@ -1 +0,0 @@ -6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/38mode.txt b/crates/vim/test_data/test_delete_around_word/38mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/38mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/39text.txt b/crates/vim/test_data/test_delete_around_word/39text.txt deleted file mode 100644 index 4a6d943982..0000000000 --- a/crates/vim/test_data/test_delete_around_word/39text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/3text.txt b/crates/vim/test_data/test_delete_around_word/3text.txt deleted file mode 100644 index 366beed733..0000000000 --- a/crates/vim/test_data/test_delete_around_word/3text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/40head.txt b/crates/vim/test_data/test_delete_around_word/40head.txt deleted file mode 100644 index 95fb9c638e..0000000000 --- a/crates/vim/test_data/test_delete_around_word/40head.txt +++ /dev/null @@ -1 +0,0 @@ -6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/41mode.txt b/crates/vim/test_data/test_delete_around_word/41mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/41mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/42text.txt b/crates/vim/test_data/test_delete_around_word/42text.txt deleted file mode 100644 index f05cdd88c1..0000000000 --- a/crates/vim/test_data/test_delete_around_word/42text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/43head.txt b/crates/vim/test_data/test_delete_around_word/43head.txt deleted file mode 100644 index da809253d8..0000000000 --- a/crates/vim/test_data/test_delete_around_word/43head.txt +++ /dev/null @@ -1 +0,0 @@ -6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/44mode.txt b/crates/vim/test_data/test_delete_around_word/44mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/44mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/45text.txt b/crates/vim/test_data/test_delete_around_word/45text.txt deleted file mode 100644 index f05cdd88c1..0000000000 --- a/crates/vim/test_data/test_delete_around_word/45text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/46head.txt b/crates/vim/test_data/test_delete_around_word/46head.txt deleted file mode 100644 index da809253d8..0000000000 --- a/crates/vim/test_data/test_delete_around_word/46head.txt +++ /dev/null @@ -1 +0,0 @@ -6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/47mode.txt b/crates/vim/test_data/test_delete_around_word/47mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/47mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/48text.txt b/crates/vim/test_data/test_delete_around_word/48text.txt deleted file mode 100644 index 13b7d1cb51..0000000000 --- a/crates/vim/test_data/test_delete_around_word/48text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/49head.txt b/crates/vim/test_data/test_delete_around_word/49head.txt deleted file mode 100644 index d933eb81fa..0000000000 --- a/crates/vim/test_data/test_delete_around_word/49head.txt +++ /dev/null @@ -1 +0,0 @@ -6,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/4head.txt b/crates/vim/test_data/test_delete_around_word/4head.txt deleted file mode 100644 index 2871b36925..0000000000 --- a/crates/vim/test_data/test_delete_around_word/4head.txt +++ /dev/null @@ -1 +0,0 @@ -0,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/50mode.txt b/crates/vim/test_data/test_delete_around_word/50mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/50mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/51text.txt b/crates/vim/test_data/test_delete_around_word/51text.txt deleted file mode 100644 index 3976ef9c85..0000000000 --- a/crates/vim/test_data/test_delete_around_word/51text.txt +++ /dev/null @@ -1,10 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown --jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/52head.txt b/crates/vim/test_data/test_delete_around_word/52head.txt deleted file mode 100644 index 72c23ba506..0000000000 --- a/crates/vim/test_data/test_delete_around_word/52head.txt +++ /dev/null @@ -1 +0,0 @@ -7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/53mode.txt b/crates/vim/test_data/test_delete_around_word/53mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/53mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/54text.txt b/crates/vim/test_data/test_delete_around_word/54text.txt deleted file mode 100644 index b5ca31babe..0000000000 --- a/crates/vim/test_data/test_delete_around_word/54text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - --jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/55head.txt b/crates/vim/test_data/test_delete_around_word/55head.txt deleted file mode 100644 index ded56a5926..0000000000 --- a/crates/vim/test_data/test_delete_around_word/55head.txt +++ /dev/null @@ -1 +0,0 @@ -8,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/56mode.txt b/crates/vim/test_data/test_delete_around_word/56mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/56mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/57text.txt b/crates/vim/test_data/test_delete_around_word/57text.txt deleted file mode 100644 index 0eaface506..0000000000 --- a/crates/vim/test_data/test_delete_around_word/57text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - --jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/58head.txt b/crates/vim/test_data/test_delete_around_word/58head.txt deleted file mode 100644 index 8e69e0ad79..0000000000 --- a/crates/vim/test_data/test_delete_around_word/58head.txt +++ /dev/null @@ -1 +0,0 @@ -9,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/59mode.txt b/crates/vim/test_data/test_delete_around_word/59mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/59mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/5mode.txt b/crates/vim/test_data/test_delete_around_word/5mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/5mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/60text.txt b/crates/vim/test_data/test_delete_around_word/60text.txt deleted file mode 100644 index d8b9b05b79..0000000000 --- a/crates/vim/test_data/test_delete_around_word/60text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/61head.txt b/crates/vim/test_data/test_delete_around_word/61head.txt deleted file mode 100644 index e0460d1bc5..0000000000 --- a/crates/vim/test_data/test_delete_around_word/61head.txt +++ /dev/null @@ -1 +0,0 @@ -9,6 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/62mode.txt b/crates/vim/test_data/test_delete_around_word/62mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/62mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/63text.txt b/crates/vim/test_data/test_delete_around_word/63text.txt deleted file mode 100644 index 912316a9f7..0000000000 --- a/crates/vim/test_data/test_delete_around_word/63text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_delete_around_word/64head.txt b/crates/vim/test_data/test_delete_around_word/64head.txt deleted file mode 100644 index 63ee4f53ac..0000000000 --- a/crates/vim/test_data/test_delete_around_word/64head.txt +++ /dev/null @@ -1 +0,0 @@ -10,11 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/65mode.txt b/crates/vim/test_data/test_delete_around_word/65mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/65mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/66text.txt b/crates/vim/test_data/test_delete_around_word/66text.txt deleted file mode 100644 index 3c4e8c2398..0000000000 --- a/crates/vim/test_data/test_delete_around_word/66text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/67head.txt b/crates/vim/test_data/test_delete_around_word/67head.txt deleted file mode 100644 index ec50c7d44f..0000000000 --- a/crates/vim/test_data/test_delete_around_word/67head.txt +++ /dev/null @@ -1 +0,0 @@ -10,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/68mode.txt b/crates/vim/test_data/test_delete_around_word/68mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/68mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/69text.txt b/crates/vim/test_data/test_delete_around_word/69text.txt deleted file mode 100644 index 366beed733..0000000000 --- a/crates/vim/test_data/test_delete_around_word/69text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/6text.txt b/crates/vim/test_data/test_delete_around_word/6text.txt deleted file mode 100644 index 3678c219a3..0000000000 --- a/crates/vim/test_data/test_delete_around_word/6text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/70head.txt b/crates/vim/test_data/test_delete_around_word/70head.txt deleted file mode 100644 index 2871b36925..0000000000 --- a/crates/vim/test_data/test_delete_around_word/70head.txt +++ /dev/null @@ -1 +0,0 @@ -0,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/71mode.txt b/crates/vim/test_data/test_delete_around_word/71mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/71mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/72text.txt b/crates/vim/test_data/test_delete_around_word/72text.txt deleted file mode 100644 index 366beed733..0000000000 --- a/crates/vim/test_data/test_delete_around_word/72text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/73head.txt b/crates/vim/test_data/test_delete_around_word/73head.txt deleted file mode 100644 index 2871b36925..0000000000 --- a/crates/vim/test_data/test_delete_around_word/73head.txt +++ /dev/null @@ -1 +0,0 @@ -0,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/74mode.txt b/crates/vim/test_data/test_delete_around_word/74mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/74mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/75text.txt b/crates/vim/test_data/test_delete_around_word/75text.txt deleted file mode 100644 index 3678c219a3..0000000000 --- a/crates/vim/test_data/test_delete_around_word/75text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/76head.txt b/crates/vim/test_data/test_delete_around_word/76head.txt deleted file mode 100644 index 5f7b20f0d9..0000000000 --- a/crates/vim/test_data/test_delete_around_word/76head.txt +++ /dev/null @@ -1 +0,0 @@ -0,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/77mode.txt b/crates/vim/test_data/test_delete_around_word/77mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/77mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/78text.txt b/crates/vim/test_data/test_delete_around_word/78text.txt deleted file mode 100644 index 2feb66cfb9..0000000000 --- a/crates/vim/test_data/test_delete_around_word/78text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/79head.txt b/crates/vim/test_data/test_delete_around_word/79head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_delete_around_word/79head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/7head.txt b/crates/vim/test_data/test_delete_around_word/7head.txt deleted file mode 100644 index 5f7b20f0d9..0000000000 --- a/crates/vim/test_data/test_delete_around_word/7head.txt +++ /dev/null @@ -1 +0,0 @@ -0,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/80mode.txt b/crates/vim/test_data/test_delete_around_word/80mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/80mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/81text.txt b/crates/vim/test_data/test_delete_around_word/81text.txt deleted file mode 100644 index 2feb66cfb9..0000000000 --- a/crates/vim/test_data/test_delete_around_word/81text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/82head.txt b/crates/vim/test_data/test_delete_around_word/82head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_delete_around_word/82head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/83mode.txt b/crates/vim/test_data/test_delete_around_word/83mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/83mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/84text.txt b/crates/vim/test_data/test_delete_around_word/84text.txt deleted file mode 100644 index 262feeae96..0000000000 --- a/crates/vim/test_data/test_delete_around_word/84text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/85head.txt b/crates/vim/test_data/test_delete_around_word/85head.txt deleted file mode 100644 index 413d7dd4d2..0000000000 --- a/crates/vim/test_data/test_delete_around_word/85head.txt +++ /dev/null @@ -1 +0,0 @@ -1,8 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/86mode.txt b/crates/vim/test_data/test_delete_around_word/86mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/86mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/87text.txt b/crates/vim/test_data/test_delete_around_word/87text.txt deleted file mode 100644 index ba0aea82d9..0000000000 --- a/crates/vim/test_data/test_delete_around_word/87text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/88head.txt b/crates/vim/test_data/test_delete_around_word/88head.txt deleted file mode 100644 index 06ce34087d..0000000000 --- a/crates/vim/test_data/test_delete_around_word/88head.txt +++ /dev/null @@ -1 +0,0 @@ -2,11 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/89mode.txt b/crates/vim/test_data/test_delete_around_word/89mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/89mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/8mode.txt b/crates/vim/test_data/test_delete_around_word/8mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/8mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/90text.txt b/crates/vim/test_data/test_delete_around_word/90text.txt deleted file mode 100644 index ead25fb9d0..0000000000 --- a/crates/vim/test_data/test_delete_around_word/90text.txt +++ /dev/null @@ -1,10 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/91head.txt b/crates/vim/test_data/test_delete_around_word/91head.txt deleted file mode 100644 index 7cad756336..0000000000 --- a/crates/vim/test_data/test_delete_around_word/91head.txt +++ /dev/null @@ -1 +0,0 @@ -3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/92mode.txt b/crates/vim/test_data/test_delete_around_word/92mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/92mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/93text.txt b/crates/vim/test_data/test_delete_around_word/93text.txt deleted file mode 100644 index ead25fb9d0..0000000000 --- a/crates/vim/test_data/test_delete_around_word/93text.txt +++ /dev/null @@ -1,10 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/94head.txt b/crates/vim/test_data/test_delete_around_word/94head.txt deleted file mode 100644 index b26cd1f1d3..0000000000 --- a/crates/vim/test_data/test_delete_around_word/94head.txt +++ /dev/null @@ -1 +0,0 @@ -4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/95mode.txt b/crates/vim/test_data/test_delete_around_word/95mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/95mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/96text.txt b/crates/vim/test_data/test_delete_around_word/96text.txt deleted file mode 100644 index 523f6002f4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/96text.txt +++ /dev/null @@ -1,11 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/97head.txt b/crates/vim/test_data/test_delete_around_word/97head.txt deleted file mode 100644 index 880fda949a..0000000000 --- a/crates/vim/test_data/test_delete_around_word/97head.txt +++ /dev/null @@ -1 +0,0 @@ -5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/98mode.txt b/crates/vim/test_data/test_delete_around_word/98mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_around_word/98mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word/99text.txt b/crates/vim/test_data/test_delete_around_word/99text.txt deleted file mode 100644 index c8a5876179..0000000000 --- a/crates/vim/test_data/test_delete_around_word/99text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_around_word/9text.txt b/crates/vim/test_data/test_delete_around_word/9text.txt deleted file mode 100644 index 2feb66cfb9..0000000000 --- a/crates/vim/test_data/test_delete_around_word/9text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_sentence.json b/crates/vim/test_data/test_delete_in_sentence.json new file mode 100644 index 0000000000..1ecaa01ee6 --- /dev/null +++ b/crates/vim/test_data/test_delete_in_sentence.json @@ -0,0 +1 @@ +[{"Text":" Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":" Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":" Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown?Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":16}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!Over the lazy."},{"Head":{"row":0,"column":27}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps! "},{"Head":{"row":0,"column":27}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps! "},{"Head":{"row":0,"column":27}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps! "},{"Head":{"row":0,"column":27}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.The quick \nbrown fox jumps over\n"},{"Head":{"row":2,"column":13}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. \n"},{"Head":{"row":2,"column":13}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. \n"},{"Head":{"row":2,"column":13}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" "},{"Head":{"row":0,"column":21}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" "},{"Head":{"row":0,"column":21}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" Brown fox jumps."},{"Head":{"row":0,"column":36}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/0text.txt b/crates/vim/test_data/test_delete_in_sentence/0text.txt deleted file mode 100644 index fd36730b8d..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/0text.txt +++ /dev/null @@ -1 +0,0 @@ - Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/10head.txt b/crates/vim/test_data/test_delete_in_sentence/10head.txt deleted file mode 100644 index 5df80e23c6..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/10head.txt +++ /dev/null @@ -1 +0,0 @@ -0,16 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/11mode.txt b/crates/vim/test_data/test_delete_in_sentence/11mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/11mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/12text.txt b/crates/vim/test_data/test_delete_in_sentence/12text.txt deleted file mode 100644 index 40eacc6489..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/12text.txt +++ /dev/null @@ -1 +0,0 @@ -The quick brown? Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/13head.txt b/crates/vim/test_data/test_delete_in_sentence/13head.txt deleted file mode 100644 index 40d2f2ab55..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/13head.txt +++ /dev/null @@ -1 +0,0 @@ -0,17 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/14mode.txt b/crates/vim/test_data/test_delete_in_sentence/14mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/14mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/15text.txt b/crates/vim/test_data/test_delete_in_sentence/15text.txt deleted file mode 100644 index 40eacc6489..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/15text.txt +++ /dev/null @@ -1 +0,0 @@ -The quick brown? Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/16head.txt b/crates/vim/test_data/test_delete_in_sentence/16head.txt deleted file mode 100644 index 40d2f2ab55..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/16head.txt +++ /dev/null @@ -1 +0,0 @@ -0,17 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/17mode.txt b/crates/vim/test_data/test_delete_in_sentence/17mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/17mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/18text.txt b/crates/vim/test_data/test_delete_in_sentence/18text.txt deleted file mode 100644 index 40eacc6489..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/18text.txt +++ /dev/null @@ -1 +0,0 @@ -The quick brown? Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/19head.txt b/crates/vim/test_data/test_delete_in_sentence/19head.txt deleted file mode 100644 index 40d2f2ab55..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/19head.txt +++ /dev/null @@ -1 +0,0 @@ -0,17 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/1head.txt b/crates/vim/test_data/test_delete_in_sentence/1head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/1head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/20mode.txt b/crates/vim/test_data/test_delete_in_sentence/20mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/20mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/21text.txt b/crates/vim/test_data/test_delete_in_sentence/21text.txt deleted file mode 100644 index ef17a5f7fb..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/21text.txt +++ /dev/null @@ -1 +0,0 @@ -The quick brown? Fox Jumps!Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/22head.txt b/crates/vim/test_data/test_delete_in_sentence/22head.txt deleted file mode 100644 index 17beb4d83f..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/22head.txt +++ /dev/null @@ -1 +0,0 @@ -0,27 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/23mode.txt b/crates/vim/test_data/test_delete_in_sentence/23mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/23mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/24text.txt b/crates/vim/test_data/test_delete_in_sentence/24text.txt deleted file mode 100644 index d81c10974f..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/24text.txt +++ /dev/null @@ -1 +0,0 @@ -The quick brown? Fox Jumps! \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/25head.txt b/crates/vim/test_data/test_delete_in_sentence/25head.txt deleted file mode 100644 index 17beb4d83f..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/25head.txt +++ /dev/null @@ -1 +0,0 @@ -0,27 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/26mode.txt b/crates/vim/test_data/test_delete_in_sentence/26mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/26mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/27text.txt b/crates/vim/test_data/test_delete_in_sentence/27text.txt deleted file mode 100644 index d81c10974f..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/27text.txt +++ /dev/null @@ -1 +0,0 @@ -The quick brown? Fox Jumps! \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/28head.txt b/crates/vim/test_data/test_delete_in_sentence/28head.txt deleted file mode 100644 index 17beb4d83f..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/28head.txt +++ /dev/null @@ -1 +0,0 @@ -0,27 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/29mode.txt b/crates/vim/test_data/test_delete_in_sentence/29mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/29mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/2mode.txt b/crates/vim/test_data/test_delete_in_sentence/2mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/2mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/30text.txt b/crates/vim/test_data/test_delete_in_sentence/30text.txt deleted file mode 100644 index d81c10974f..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/30text.txt +++ /dev/null @@ -1 +0,0 @@ -The quick brown? Fox Jumps! \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/31head.txt b/crates/vim/test_data/test_delete_in_sentence/31head.txt deleted file mode 100644 index 17beb4d83f..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/31head.txt +++ /dev/null @@ -1 +0,0 @@ -0,27 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/32mode.txt b/crates/vim/test_data/test_delete_in_sentence/32mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/32mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/33text.txt b/crates/vim/test_data/test_delete_in_sentence/33text.txt deleted file mode 100644 index 561ed0800c..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/33text.txt +++ /dev/null @@ -1,2 +0,0 @@ - The quick -brown fox jumps over diff --git a/crates/vim/test_data/test_delete_in_sentence/34head.txt b/crates/vim/test_data/test_delete_in_sentence/34head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/34head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/35mode.txt b/crates/vim/test_data/test_delete_in_sentence/35mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/35mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/36text.txt b/crates/vim/test_data/test_delete_in_sentence/36text.txt deleted file mode 100644 index 561ed0800c..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/36text.txt +++ /dev/null @@ -1,2 +0,0 @@ - The quick -brown fox jumps over diff --git a/crates/vim/test_data/test_delete_in_sentence/37head.txt b/crates/vim/test_data/test_delete_in_sentence/37head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/37head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/38mode.txt b/crates/vim/test_data/test_delete_in_sentence/38mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/38mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/39text.txt b/crates/vim/test_data/test_delete_in_sentence/39text.txt deleted file mode 100644 index 561ed0800c..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/39text.txt +++ /dev/null @@ -1,2 +0,0 @@ - The quick -brown fox jumps over diff --git a/crates/vim/test_data/test_delete_in_sentence/3text.txt b/crates/vim/test_data/test_delete_in_sentence/3text.txt deleted file mode 100644 index fd36730b8d..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/3text.txt +++ /dev/null @@ -1 +0,0 @@ - Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/40head.txt b/crates/vim/test_data/test_delete_in_sentence/40head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/40head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/41mode.txt b/crates/vim/test_data/test_delete_in_sentence/41mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/41mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/42text.txt b/crates/vim/test_data/test_delete_in_sentence/42text.txt deleted file mode 100644 index 561ed0800c..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/42text.txt +++ /dev/null @@ -1,2 +0,0 @@ - The quick -brown fox jumps over diff --git a/crates/vim/test_data/test_delete_in_sentence/43head.txt b/crates/vim/test_data/test_delete_in_sentence/43head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/43head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/44mode.txt b/crates/vim/test_data/test_delete_in_sentence/44mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/44mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/45text.txt b/crates/vim/test_data/test_delete_in_sentence/45text.txt deleted file mode 100644 index 561ed0800c..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/45text.txt +++ /dev/null @@ -1,2 +0,0 @@ - The quick -brown fox jumps over diff --git a/crates/vim/test_data/test_delete_in_sentence/46head.txt b/crates/vim/test_data/test_delete_in_sentence/46head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/46head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/47mode.txt b/crates/vim/test_data/test_delete_in_sentence/47mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/47mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/48text.txt b/crates/vim/test_data/test_delete_in_sentence/48text.txt deleted file mode 100644 index bbf10c5693..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/48text.txt +++ /dev/null @@ -1,4 +0,0 @@ -The quick brown -fox jumps over -the lazy dog.The quick -brown fox jumps over diff --git a/crates/vim/test_data/test_delete_in_sentence/49head.txt b/crates/vim/test_data/test_delete_in_sentence/49head.txt deleted file mode 100644 index 26560586d9..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/49head.txt +++ /dev/null @@ -1 +0,0 @@ -2,13 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/4head.txt b/crates/vim/test_data/test_delete_in_sentence/4head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/4head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/50mode.txt b/crates/vim/test_data/test_delete_in_sentence/50mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/50mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/51text.txt b/crates/vim/test_data/test_delete_in_sentence/51text.txt deleted file mode 100644 index 0b535bbbaf..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/51text.txt +++ /dev/null @@ -1,3 +0,0 @@ -The quick brown -fox jumps over -the lazy dog. diff --git a/crates/vim/test_data/test_delete_in_sentence/52head.txt b/crates/vim/test_data/test_delete_in_sentence/52head.txt deleted file mode 100644 index 26560586d9..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/52head.txt +++ /dev/null @@ -1 +0,0 @@ -2,13 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/53mode.txt b/crates/vim/test_data/test_delete_in_sentence/53mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/53mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/54text.txt b/crates/vim/test_data/test_delete_in_sentence/54text.txt deleted file mode 100644 index 0b535bbbaf..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/54text.txt +++ /dev/null @@ -1,3 +0,0 @@ -The quick brown -fox jumps over -the lazy dog. diff --git a/crates/vim/test_data/test_delete_in_sentence/55head.txt b/crates/vim/test_data/test_delete_in_sentence/55head.txt deleted file mode 100644 index 26560586d9..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/55head.txt +++ /dev/null @@ -1 +0,0 @@ -2,13 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/56mode.txt b/crates/vim/test_data/test_delete_in_sentence/56mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/56mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/5mode.txt b/crates/vim/test_data/test_delete_in_sentence/5mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/5mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/6text.txt b/crates/vim/test_data/test_delete_in_sentence/6text.txt deleted file mode 100644 index fd36730b8d..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/6text.txt +++ /dev/null @@ -1 +0,0 @@ - Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/7head.txt b/crates/vim/test_data/test_delete_in_sentence/7head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/7head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/8mode.txt b/crates/vim/test_data/test_delete_in_sentence/8mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/8mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence/9text.txt b/crates/vim/test_data/test_delete_in_sentence/9text.txt deleted file mode 100644 index 9e4b6dcc50..0000000000 --- a/crates/vim/test_data/test_delete_in_sentence/9text.txt +++ /dev/null @@ -1 +0,0 @@ -The quick brown?Fox Jumps! Over the lazy. \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word.json b/crates/vim/test_data/test_delete_in_word.json new file mode 100644 index 0000000000..1960dce67a --- /dev/null +++ b/crates/vim/test_data/test_delete_in_word.json @@ -0,0 +1 @@ +[{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":14}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumpsover\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":2,"column":11}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":5,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThequick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":3}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe- brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe- brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quickbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown\n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":14}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n\n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":7,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n\n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":8,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \nfox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox- over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":6}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n\n"},{"Head":{"row":10,"column":11}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":11,"column":0}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":14}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumpsover\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":2,"column":11}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":5,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quickbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown\n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":14}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n\n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":7,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n\n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":8,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \nfox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":2}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n\n"},{"Head":{"row":10,"column":11}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":11,"column":0}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/0text.txt b/crates/vim/test_data/test_delete_in_word/0text.txt deleted file mode 100644 index 481389e676..0000000000 --- a/crates/vim/test_data/test_delete_in_word/0text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/100head.txt b/crates/vim/test_data/test_delete_in_word/100head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_delete_in_word/100head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/101mode.txt b/crates/vim/test_data/test_delete_in_word/101mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/101mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/102text.txt b/crates/vim/test_data/test_delete_in_word/102text.txt deleted file mode 100644 index d90b2c4e48..0000000000 --- a/crates/vim/test_data/test_delete_in_word/102text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - - brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/103head.txt b/crates/vim/test_data/test_delete_in_word/103head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_delete_in_word/103head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/104mode.txt b/crates/vim/test_data/test_delete_in_word/104mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/104mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/105text.txt b/crates/vim/test_data/test_delete_in_word/105text.txt deleted file mode 100644 index d90b2c4e48..0000000000 --- a/crates/vim/test_data/test_delete_in_word/105text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - - brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/106head.txt b/crates/vim/test_data/test_delete_in_word/106head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_delete_in_word/106head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/107mode.txt b/crates/vim/test_data/test_delete_in_word/107mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/107mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/108text.txt b/crates/vim/test_data/test_delete_in_word/108text.txt deleted file mode 100644 index d90b2c4e48..0000000000 --- a/crates/vim/test_data/test_delete_in_word/108text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - - brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/109head.txt b/crates/vim/test_data/test_delete_in_word/109head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_delete_in_word/109head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/10head.txt b/crates/vim/test_data/test_delete_in_word/10head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_delete_in_word/10head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/110mode.txt b/crates/vim/test_data/test_delete_in_word/110mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/110mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/111text.txt b/crates/vim/test_data/test_delete_in_word/111text.txt deleted file mode 100644 index 7d13d92d35..0000000000 --- a/crates/vim/test_data/test_delete_in_word/111text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quickbrown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/112head.txt b/crates/vim/test_data/test_delete_in_word/112head.txt deleted file mode 100644 index da809253d8..0000000000 --- a/crates/vim/test_data/test_delete_in_word/112head.txt +++ /dev/null @@ -1 +0,0 @@ -6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/113mode.txt b/crates/vim/test_data/test_delete_in_word/113mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/113mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/114text.txt b/crates/vim/test_data/test_delete_in_word/114text.txt deleted file mode 100644 index 95b76ce25b..0000000000 --- a/crates/vim/test_data/test_delete_in_word/114text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/115head.txt b/crates/vim/test_data/test_delete_in_word/115head.txt deleted file mode 100644 index c5a50698dd..0000000000 --- a/crates/vim/test_data/test_delete_in_word/115head.txt +++ /dev/null @@ -1 +0,0 @@ -6,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/116mode.txt b/crates/vim/test_data/test_delete_in_word/116mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/116mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/117text.txt b/crates/vim/test_data/test_delete_in_word/117text.txt deleted file mode 100644 index 805a5ebe4c..0000000000 --- a/crates/vim/test_data/test_delete_in_word/117text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/118head.txt b/crates/vim/test_data/test_delete_in_word/118head.txt deleted file mode 100644 index afce6fe58c..0000000000 --- a/crates/vim/test_data/test_delete_in_word/118head.txt +++ /dev/null @@ -1 +0,0 @@ -6,14 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/119mode.txt b/crates/vim/test_data/test_delete_in_word/119mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/119mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/11mode.txt b/crates/vim/test_data/test_delete_in_word/11mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/11mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/120text.txt b/crates/vim/test_data/test_delete_in_word/120text.txt deleted file mode 100644 index 1d797e34b8..0000000000 --- a/crates/vim/test_data/test_delete_in_word/120text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/121head.txt b/crates/vim/test_data/test_delete_in_word/121head.txt deleted file mode 100644 index 72c23ba506..0000000000 --- a/crates/vim/test_data/test_delete_in_word/121head.txt +++ /dev/null @@ -1 +0,0 @@ -7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/122mode.txt b/crates/vim/test_data/test_delete_in_word/122mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/122mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/123text.txt b/crates/vim/test_data/test_delete_in_word/123text.txt deleted file mode 100644 index 8a35adfa26..0000000000 --- a/crates/vim/test_data/test_delete_in_word/123text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/124head.txt b/crates/vim/test_data/test_delete_in_word/124head.txt deleted file mode 100644 index ded56a5926..0000000000 --- a/crates/vim/test_data/test_delete_in_word/124head.txt +++ /dev/null @@ -1 +0,0 @@ -8,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/125mode.txt b/crates/vim/test_data/test_delete_in_word/125mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/125mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/126text.txt b/crates/vim/test_data/test_delete_in_word/126text.txt deleted file mode 100644 index 26e9d0a1b6..0000000000 --- a/crates/vim/test_data/test_delete_in_word/126text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - -fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/127head.txt b/crates/vim/test_data/test_delete_in_word/127head.txt deleted file mode 100644 index 8e69e0ad79..0000000000 --- a/crates/vim/test_data/test_delete_in_word/127head.txt +++ /dev/null @@ -1 +0,0 @@ -9,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/128mode.txt b/crates/vim/test_data/test_delete_in_word/128mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/128mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/129text.txt b/crates/vim/test_data/test_delete_in_word/129text.txt deleted file mode 100644 index c2c9c0edb0..0000000000 --- a/crates/vim/test_data/test_delete_in_word/129text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/12text.txt b/crates/vim/test_data/test_delete_in_word/12text.txt deleted file mode 100644 index 38c6ccbeac..0000000000 --- a/crates/vim/test_data/test_delete_in_word/12text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/130head.txt b/crates/vim/test_data/test_delete_in_word/130head.txt deleted file mode 100644 index edb3544f9a..0000000000 --- a/crates/vim/test_data/test_delete_in_word/130head.txt +++ /dev/null @@ -1 +0,0 @@ -9,2 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/131mode.txt b/crates/vim/test_data/test_delete_in_word/131mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/131mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/132text.txt b/crates/vim/test_data/test_delete_in_word/132text.txt deleted file mode 100644 index 4b2cd80611..0000000000 --- a/crates/vim/test_data/test_delete_in_word/132text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/133head.txt b/crates/vim/test_data/test_delete_in_word/133head.txt deleted file mode 100644 index 63ee4f53ac..0000000000 --- a/crates/vim/test_data/test_delete_in_word/133head.txt +++ /dev/null @@ -1 +0,0 @@ -10,11 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/134mode.txt b/crates/vim/test_data/test_delete_in_word/134mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/134mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/135text.txt b/crates/vim/test_data/test_delete_in_word/135text.txt deleted file mode 100644 index e971227436..0000000000 --- a/crates/vim/test_data/test_delete_in_word/135text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/136head.txt b/crates/vim/test_data/test_delete_in_word/136head.txt deleted file mode 100644 index 94dbddc699..0000000000 --- a/crates/vim/test_data/test_delete_in_word/136head.txt +++ /dev/null @@ -1 +0,0 @@ -11,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/137mode.txt b/crates/vim/test_data/test_delete_in_word/137mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/137mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/13head.txt b/crates/vim/test_data/test_delete_in_word/13head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_delete_in_word/13head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/14mode.txt b/crates/vim/test_data/test_delete_in_word/14mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/14mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/15text.txt b/crates/vim/test_data/test_delete_in_word/15text.txt deleted file mode 100644 index 282218da91..0000000000 --- a/crates/vim/test_data/test_delete_in_word/15text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumpsover -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/16head.txt b/crates/vim/test_data/test_delete_in_word/16head.txt deleted file mode 100644 index bd712da63a..0000000000 --- a/crates/vim/test_data/test_delete_in_word/16head.txt +++ /dev/null @@ -1 +0,0 @@ -1,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/17mode.txt b/crates/vim/test_data/test_delete_in_word/17mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/17mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/18text.txt b/crates/vim/test_data/test_delete_in_word/18text.txt deleted file mode 100644 index 0b042b7c39..0000000000 --- a/crates/vim/test_data/test_delete_in_word/18text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/19head.txt b/crates/vim/test_data/test_delete_in_word/19head.txt deleted file mode 100644 index 06ce34087d..0000000000 --- a/crates/vim/test_data/test_delete_in_word/19head.txt +++ /dev/null @@ -1 +0,0 @@ -2,11 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/1head.txt b/crates/vim/test_data/test_delete_in_word/1head.txt deleted file mode 100644 index 352f6067e6..0000000000 --- a/crates/vim/test_data/test_delete_in_word/1head.txt +++ /dev/null @@ -1 +0,0 @@ -0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/20mode.txt b/crates/vim/test_data/test_delete_in_word/20mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/20mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/21text.txt b/crates/vim/test_data/test_delete_in_word/21text.txt deleted file mode 100644 index e971227436..0000000000 --- a/crates/vim/test_data/test_delete_in_word/21text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/22head.txt b/crates/vim/test_data/test_delete_in_word/22head.txt deleted file mode 100644 index 7cad756336..0000000000 --- a/crates/vim/test_data/test_delete_in_word/22head.txt +++ /dev/null @@ -1 +0,0 @@ -3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/23mode.txt b/crates/vim/test_data/test_delete_in_word/23mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/23mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/24text.txt b/crates/vim/test_data/test_delete_in_word/24text.txt deleted file mode 100644 index e971227436..0000000000 --- a/crates/vim/test_data/test_delete_in_word/24text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/25head.txt b/crates/vim/test_data/test_delete_in_word/25head.txt deleted file mode 100644 index b26cd1f1d3..0000000000 --- a/crates/vim/test_data/test_delete_in_word/25head.txt +++ /dev/null @@ -1 +0,0 @@ -4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/26mode.txt b/crates/vim/test_data/test_delete_in_word/26mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/26mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/27text.txt b/crates/vim/test_data/test_delete_in_word/27text.txt deleted file mode 100644 index e971227436..0000000000 --- a/crates/vim/test_data/test_delete_in_word/27text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/28head.txt b/crates/vim/test_data/test_delete_in_word/28head.txt deleted file mode 100644 index 880fda949a..0000000000 --- a/crates/vim/test_data/test_delete_in_word/28head.txt +++ /dev/null @@ -1 +0,0 @@ -5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/29mode.txt b/crates/vim/test_data/test_delete_in_word/29mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/29mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/2mode.txt b/crates/vim/test_data/test_delete_in_word/2mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/2mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/30text.txt b/crates/vim/test_data/test_delete_in_word/30text.txt deleted file mode 100644 index 609747299b..0000000000 --- a/crates/vim/test_data/test_delete_in_word/30text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - --quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/31head.txt b/crates/vim/test_data/test_delete_in_word/31head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_delete_in_word/31head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/32mode.txt b/crates/vim/test_data/test_delete_in_word/32mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/32mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/33text.txt b/crates/vim/test_data/test_delete_in_word/33text.txt deleted file mode 100644 index 12ede0f513..0000000000 --- a/crates/vim/test_data/test_delete_in_word/33text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -Thequick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/34head.txt b/crates/vim/test_data/test_delete_in_word/34head.txt deleted file mode 100644 index c20df71d5e..0000000000 --- a/crates/vim/test_data/test_delete_in_word/34head.txt +++ /dev/null @@ -1 +0,0 @@ -6,3 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/35mode.txt b/crates/vim/test_data/test_delete_in_word/35mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/35mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/36text.txt b/crates/vim/test_data/test_delete_in_word/36text.txt deleted file mode 100644 index 3b3f900a05..0000000000 --- a/crates/vim/test_data/test_delete_in_word/36text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The- brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/37head.txt b/crates/vim/test_data/test_delete_in_word/37head.txt deleted file mode 100644 index 95fb9c638e..0000000000 --- a/crates/vim/test_data/test_delete_in_word/37head.txt +++ /dev/null @@ -1 +0,0 @@ -6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/38mode.txt b/crates/vim/test_data/test_delete_in_word/38mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/38mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/39text.txt b/crates/vim/test_data/test_delete_in_word/39text.txt deleted file mode 100644 index 3b3f900a05..0000000000 --- a/crates/vim/test_data/test_delete_in_word/39text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The- brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/3text.txt b/crates/vim/test_data/test_delete_in_word/3text.txt deleted file mode 100644 index 481389e676..0000000000 --- a/crates/vim/test_data/test_delete_in_word/3text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/40head.txt b/crates/vim/test_data/test_delete_in_word/40head.txt deleted file mode 100644 index 95fb9c638e..0000000000 --- a/crates/vim/test_data/test_delete_in_word/40head.txt +++ /dev/null @@ -1 +0,0 @@ -6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/41mode.txt b/crates/vim/test_data/test_delete_in_word/41mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/41mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/42text.txt b/crates/vim/test_data/test_delete_in_word/42text.txt deleted file mode 100644 index 7d13d92d35..0000000000 --- a/crates/vim/test_data/test_delete_in_word/42text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quickbrown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/43head.txt b/crates/vim/test_data/test_delete_in_word/43head.txt deleted file mode 100644 index da809253d8..0000000000 --- a/crates/vim/test_data/test_delete_in_word/43head.txt +++ /dev/null @@ -1 +0,0 @@ -6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/44mode.txt b/crates/vim/test_data/test_delete_in_word/44mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/44mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/45text.txt b/crates/vim/test_data/test_delete_in_word/45text.txt deleted file mode 100644 index 95b76ce25b..0000000000 --- a/crates/vim/test_data/test_delete_in_word/45text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/46head.txt b/crates/vim/test_data/test_delete_in_word/46head.txt deleted file mode 100644 index c5a50698dd..0000000000 --- a/crates/vim/test_data/test_delete_in_word/46head.txt +++ /dev/null @@ -1 +0,0 @@ -6,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/47mode.txt b/crates/vim/test_data/test_delete_in_word/47mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/47mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/48text.txt b/crates/vim/test_data/test_delete_in_word/48text.txt deleted file mode 100644 index 805a5ebe4c..0000000000 --- a/crates/vim/test_data/test_delete_in_word/48text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/49head.txt b/crates/vim/test_data/test_delete_in_word/49head.txt deleted file mode 100644 index afce6fe58c..0000000000 --- a/crates/vim/test_data/test_delete_in_word/49head.txt +++ /dev/null @@ -1 +0,0 @@ -6,14 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/4head.txt b/crates/vim/test_data/test_delete_in_word/4head.txt deleted file mode 100644 index 352f6067e6..0000000000 --- a/crates/vim/test_data/test_delete_in_word/4head.txt +++ /dev/null @@ -1 +0,0 @@ -0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/50mode.txt b/crates/vim/test_data/test_delete_in_word/50mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/50mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/51text.txt b/crates/vim/test_data/test_delete_in_word/51text.txt deleted file mode 100644 index 1d797e34b8..0000000000 --- a/crates/vim/test_data/test_delete_in_word/51text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/52head.txt b/crates/vim/test_data/test_delete_in_word/52head.txt deleted file mode 100644 index 72c23ba506..0000000000 --- a/crates/vim/test_data/test_delete_in_word/52head.txt +++ /dev/null @@ -1 +0,0 @@ -7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/53mode.txt b/crates/vim/test_data/test_delete_in_word/53mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/53mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/54text.txt b/crates/vim/test_data/test_delete_in_word/54text.txt deleted file mode 100644 index 8a35adfa26..0000000000 --- a/crates/vim/test_data/test_delete_in_word/54text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/55head.txt b/crates/vim/test_data/test_delete_in_word/55head.txt deleted file mode 100644 index ded56a5926..0000000000 --- a/crates/vim/test_data/test_delete_in_word/55head.txt +++ /dev/null @@ -1 +0,0 @@ -8,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/56mode.txt b/crates/vim/test_data/test_delete_in_word/56mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/56mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/57text.txt b/crates/vim/test_data/test_delete_in_word/57text.txt deleted file mode 100644 index 26e9d0a1b6..0000000000 --- a/crates/vim/test_data/test_delete_in_word/57text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - -fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/58head.txt b/crates/vim/test_data/test_delete_in_word/58head.txt deleted file mode 100644 index 8e69e0ad79..0000000000 --- a/crates/vim/test_data/test_delete_in_word/58head.txt +++ /dev/null @@ -1 +0,0 @@ -9,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/59mode.txt b/crates/vim/test_data/test_delete_in_word/59mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/59mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/5mode.txt b/crates/vim/test_data/test_delete_in_word/5mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/5mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/60text.txt b/crates/vim/test_data/test_delete_in_word/60text.txt deleted file mode 100644 index 816bf47cf0..0000000000 --- a/crates/vim/test_data/test_delete_in_word/60text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox- over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/61head.txt b/crates/vim/test_data/test_delete_in_word/61head.txt deleted file mode 100644 index e0460d1bc5..0000000000 --- a/crates/vim/test_data/test_delete_in_word/61head.txt +++ /dev/null @@ -1 +0,0 @@ -9,6 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/62mode.txt b/crates/vim/test_data/test_delete_in_word/62mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/62mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/63text.txt b/crates/vim/test_data/test_delete_in_word/63text.txt deleted file mode 100644 index 4b2cd80611..0000000000 --- a/crates/vim/test_data/test_delete_in_word/63text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/64head.txt b/crates/vim/test_data/test_delete_in_word/64head.txt deleted file mode 100644 index 63ee4f53ac..0000000000 --- a/crates/vim/test_data/test_delete_in_word/64head.txt +++ /dev/null @@ -1 +0,0 @@ -10,11 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/65mode.txt b/crates/vim/test_data/test_delete_in_word/65mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/65mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/66text.txt b/crates/vim/test_data/test_delete_in_word/66text.txt deleted file mode 100644 index e971227436..0000000000 --- a/crates/vim/test_data/test_delete_in_word/66text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/67head.txt b/crates/vim/test_data/test_delete_in_word/67head.txt deleted file mode 100644 index 94dbddc699..0000000000 --- a/crates/vim/test_data/test_delete_in_word/67head.txt +++ /dev/null @@ -1 +0,0 @@ -11,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/68mode.txt b/crates/vim/test_data/test_delete_in_word/68mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/68mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/69text.txt b/crates/vim/test_data/test_delete_in_word/69text.txt deleted file mode 100644 index 481389e676..0000000000 --- a/crates/vim/test_data/test_delete_in_word/69text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/6text.txt b/crates/vim/test_data/test_delete_in_word/6text.txt deleted file mode 100644 index 35fd5c89ba..0000000000 --- a/crates/vim/test_data/test_delete_in_word/6text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/70head.txt b/crates/vim/test_data/test_delete_in_word/70head.txt deleted file mode 100644 index 352f6067e6..0000000000 --- a/crates/vim/test_data/test_delete_in_word/70head.txt +++ /dev/null @@ -1 +0,0 @@ -0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/71mode.txt b/crates/vim/test_data/test_delete_in_word/71mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/71mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/72text.txt b/crates/vim/test_data/test_delete_in_word/72text.txt deleted file mode 100644 index 481389e676..0000000000 --- a/crates/vim/test_data/test_delete_in_word/72text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/73head.txt b/crates/vim/test_data/test_delete_in_word/73head.txt deleted file mode 100644 index 352f6067e6..0000000000 --- a/crates/vim/test_data/test_delete_in_word/73head.txt +++ /dev/null @@ -1 +0,0 @@ -0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/74mode.txt b/crates/vim/test_data/test_delete_in_word/74mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/74mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/75text.txt b/crates/vim/test_data/test_delete_in_word/75text.txt deleted file mode 100644 index 35fd5c89ba..0000000000 --- a/crates/vim/test_data/test_delete_in_word/75text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/76head.txt b/crates/vim/test_data/test_delete_in_word/76head.txt deleted file mode 100644 index 8b208cec2d..0000000000 --- a/crates/vim/test_data/test_delete_in_word/76head.txt +++ /dev/null @@ -1 +0,0 @@ -0,14 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/77mode.txt b/crates/vim/test_data/test_delete_in_word/77mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/77mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/78text.txt b/crates/vim/test_data/test_delete_in_word/78text.txt deleted file mode 100644 index 38c6ccbeac..0000000000 --- a/crates/vim/test_data/test_delete_in_word/78text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/79head.txt b/crates/vim/test_data/test_delete_in_word/79head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_delete_in_word/79head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/7head.txt b/crates/vim/test_data/test_delete_in_word/7head.txt deleted file mode 100644 index 8b208cec2d..0000000000 --- a/crates/vim/test_data/test_delete_in_word/7head.txt +++ /dev/null @@ -1 +0,0 @@ -0,14 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/80mode.txt b/crates/vim/test_data/test_delete_in_word/80mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/80mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/81text.txt b/crates/vim/test_data/test_delete_in_word/81text.txt deleted file mode 100644 index 38c6ccbeac..0000000000 --- a/crates/vim/test_data/test_delete_in_word/81text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/82head.txt b/crates/vim/test_data/test_delete_in_word/82head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_delete_in_word/82head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/83mode.txt b/crates/vim/test_data/test_delete_in_word/83mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/83mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/84text.txt b/crates/vim/test_data/test_delete_in_word/84text.txt deleted file mode 100644 index 282218da91..0000000000 --- a/crates/vim/test_data/test_delete_in_word/84text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumpsover -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/85head.txt b/crates/vim/test_data/test_delete_in_word/85head.txt deleted file mode 100644 index bd712da63a..0000000000 --- a/crates/vim/test_data/test_delete_in_word/85head.txt +++ /dev/null @@ -1 +0,0 @@ -1,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/86mode.txt b/crates/vim/test_data/test_delete_in_word/86mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/86mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/87text.txt b/crates/vim/test_data/test_delete_in_word/87text.txt deleted file mode 100644 index 0b042b7c39..0000000000 --- a/crates/vim/test_data/test_delete_in_word/87text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/88head.txt b/crates/vim/test_data/test_delete_in_word/88head.txt deleted file mode 100644 index 06ce34087d..0000000000 --- a/crates/vim/test_data/test_delete_in_word/88head.txt +++ /dev/null @@ -1 +0,0 @@ -2,11 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/89mode.txt b/crates/vim/test_data/test_delete_in_word/89mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/89mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/8mode.txt b/crates/vim/test_data/test_delete_in_word/8mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/8mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/90text.txt b/crates/vim/test_data/test_delete_in_word/90text.txt deleted file mode 100644 index e971227436..0000000000 --- a/crates/vim/test_data/test_delete_in_word/90text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/91head.txt b/crates/vim/test_data/test_delete_in_word/91head.txt deleted file mode 100644 index 7cad756336..0000000000 --- a/crates/vim/test_data/test_delete_in_word/91head.txt +++ /dev/null @@ -1 +0,0 @@ -3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/92mode.txt b/crates/vim/test_data/test_delete_in_word/92mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/92mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/93text.txt b/crates/vim/test_data/test_delete_in_word/93text.txt deleted file mode 100644 index e971227436..0000000000 --- a/crates/vim/test_data/test_delete_in_word/93text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/94head.txt b/crates/vim/test_data/test_delete_in_word/94head.txt deleted file mode 100644 index b26cd1f1d3..0000000000 --- a/crates/vim/test_data/test_delete_in_word/94head.txt +++ /dev/null @@ -1 +0,0 @@ -4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/95mode.txt b/crates/vim/test_data/test_delete_in_word/95mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/95mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/96text.txt b/crates/vim/test_data/test_delete_in_word/96text.txt deleted file mode 100644 index e971227436..0000000000 --- a/crates/vim/test_data/test_delete_in_word/96text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/97head.txt b/crates/vim/test_data/test_delete_in_word/97head.txt deleted file mode 100644 index 880fda949a..0000000000 --- a/crates/vim/test_data/test_delete_in_word/97head.txt +++ /dev/null @@ -1 +0,0 @@ -5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/98mode.txt b/crates/vim/test_data/test_delete_in_word/98mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_delete_in_word/98mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word/99text.txt b/crates/vim/test_data/test_delete_in_word/99text.txt deleted file mode 100644 index d90b2c4e48..0000000000 --- a/crates/vim/test_data/test_delete_in_word/99text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - - brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_delete_in_word/9text.txt b/crates/vim/test_data/test_delete_in_word/9text.txt deleted file mode 100644 index 38c6ccbeac..0000000000 --- a/crates/vim/test_data/test_delete_in_word/9text.txt +++ /dev/null @@ -1,12 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - - - fox-jumps over -the lazy dog - diff --git a/crates/vim/test_data/test_e.json b/crates/vim/test_data/test_e.json new file mode 100644 index 0000000000..bd3c513846 --- /dev/null +++ b/crates/vim/test_data/test_e.json @@ -0,0 +1 @@ +[{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":0,"column":8}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":0,"column":9}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":0,"column":14}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":3,"column":8}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":3,"column":13}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":4,"column":2}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":4,"column":2}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":0,"column":14}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":0,"column":14}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":0,"column":14}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":3,"column":8}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":3,"column":13}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":4,"column":2}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":4,"column":2}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_gg.json b/crates/vim/test_data/test_gg.json new file mode 100644 index 0000000000..7ce13ba6cb --- /dev/null +++ b/crates/vim/test_data/test_gg.json @@ -0,0 +1 @@ +[{"Text":"The quick\n\nbrown fox jumps\nover the lazy dog"},{"Head":{"row":0,"column":5}},{"Mode":"Normal"},{"Text":"The quick\n\nbrown fox jumps\nover the lazy dog"},{"Head":{"row":0,"column":5}},{"Mode":"Normal"},{"Text":"The quick\n\nbrown fox jumps\nover the lazy dog"},{"Head":{"row":0,"column":8}},{"Mode":"Normal"},{"Text":"\n\nbrown fox jumps\nover the lazy dog"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_h.json b/crates/vim/test_data/test_h.json new file mode 100644 index 0000000000..7626f1b121 --- /dev/null +++ b/crates/vim/test_data/test_h.json @@ -0,0 +1 @@ +[{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":4}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_insert_end_of_line.json b/crates/vim/test_data/test_insert_end_of_line.json new file mode 100644 index 0000000000..887ba4dabc --- /dev/null +++ b/crates/vim/test_data/test_insert_end_of_line.json @@ -0,0 +1 @@ +[{"Text":"\nThe quick\nbrown fox "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\nThe quick\nbrown fox "},{"Head":{"row":1,"column":9}},{"Mode":"Insert"},{"Text":"\nThe quick\nbrown fox "},{"Head":{"row":2,"column":10}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_insert_line_above.json b/crates/vim/test_data/test_insert_line_above.json new file mode 100644 index 0000000000..0dc54b6f88 --- /dev/null +++ b/crates/vim/test_data/test_insert_line_above.json @@ -0,0 +1 @@ +[{"Text":"\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\nThe quick"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\nThe quick\nbrown fox\njumps over"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick\n\nbrown fox\njumps over"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick\nbrown fox\n\njumps over"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick\n\n\nbrown fox"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_j.json b/crates/vim/test_data/test_j.json new file mode 100644 index 0000000000..b3202075f5 --- /dev/null +++ b/crates/vim/test_data/test_j.json @@ -0,0 +1 @@ +[{"Text":"The quick brown\nfox jumps"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps"},{"Head":{"row":1,"column":5}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps"},{"Head":{"row":1,"column":8}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_jump_to_end.json b/crates/vim/test_data/test_jump_to_end.json new file mode 100644 index 0000000000..71bd396efe --- /dev/null +++ b/crates/vim/test_data/test_jump_to_end.json @@ -0,0 +1 @@ +[{"Text":"The quick\n\nbrown fox jumps\nover the lazy dog"},{"Head":{"row":3,"column":4}},{"Mode":"Normal"},{"Text":"The quick\n\nbrown fox jumps\nover the lazy dog"},{"Head":{"row":3,"column":4}},{"Mode":"Normal"},{"Text":"The quick\n\nbrown fox jumps\nover the lazy dog"},{"Head":{"row":3,"column":16}},{"Mode":"Normal"},{"Text":"The quick\n\nbrown"},{"Head":{"row":2,"column":4}},{"Mode":"Normal"},{"Text":"The quick\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_jump_to_line_boundaries.json b/crates/vim/test_data/test_jump_to_line_boundaries.json new file mode 100644 index 0000000000..48dd51a219 --- /dev/null +++ b/crates/vim/test_data/test_jump_to_line_boundaries.json @@ -0,0 +1 @@ +[{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":8}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":8}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":8}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":1,"column":4}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":1,"column":4}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_k.json b/crates/vim/test_data/test_k.json new file mode 100644 index 0000000000..d99237d71a --- /dev/null +++ b/crates/vim/test_data/test_k.json @@ -0,0 +1 @@ +[{"Text":"The quick\nbrown fox jumps"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox jumps"},{"Head":{"row":0,"column":5}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox jumps"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox jumps"},{"Head":{"row":0,"column":7}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox jumps"},{"Head":{"row":0,"column":8}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_l.json b/crates/vim/test_data/test_l.json new file mode 100644 index 0000000000..33f70e2ee9 --- /dev/null +++ b/crates/vim/test_data/test_l.json @@ -0,0 +1 @@ +[{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":1}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":6}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":8}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":1,"column":1}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":1,"column":4}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_neovim.json b/crates/vim/test_data/test_neovim.json new file mode 100644 index 0000000000..678e4f7852 --- /dev/null +++ b/crates/vim/test_data/test_neovim.json @@ -0,0 +1 @@ +[{"Text":"test"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_neovim/0text.txt b/crates/vim/test_data/test_neovim/0text.txt deleted file mode 100644 index 30d74d2584..0000000000 --- a/crates/vim/test_data/test_neovim/0text.txt +++ /dev/null @@ -1 +0,0 @@ -test \ No newline at end of file diff --git a/crates/vim/test_data/test_neovim/1head.txt b/crates/vim/test_data/test_neovim/1head.txt deleted file mode 100644 index 7de346d2f5..0000000000 --- a/crates/vim/test_data/test_neovim/1head.txt +++ /dev/null @@ -1 +0,0 @@ -0,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_neovim/2mode.txt b/crates/vim/test_data/test_neovim/2mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_neovim/2mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_repeated_cb.json b/crates/vim/test_data/test_repeated_cb.json new file mode 100644 index 0000000000..357e37a8c9 --- /dev/null +++ b/crates/vim/test_data/test_repeated_cb.json @@ -0,0 +1 @@ +[{"Text":"The ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":4}},{"Mode":"Insert"},{"Text":"The brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":4}},{"Mode":"Insert"},{"Text":"The quick n\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick \n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\njumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox -over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumpsover\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-ver\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The n\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":4}},{"Mode":"Insert"},{"Text":"The \n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":4}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\njumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumpsver\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"n\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The \nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":4}},{"Mode":"Insert"},{"Text":"The quick jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nover\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox ver\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox \nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"n\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":4}},{"Mode":"Insert"},{"Text":"The quick -over\nthe lazy dog\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\nover\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nver\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"n\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The -over\nthe lazy dog\n"},{"Head":{"row":0,"column":4}},{"Mode":"Insert"},{"Text":"The quick over\nthe lazy dog\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\nver\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_repeated_ce.json b/crates/vim/test_data/test_repeated_ce.json new file mode 100644 index 0000000000..32394077e6 --- /dev/null +++ b/crates/vim/test_data/test_repeated_ce.json @@ -0,0 +1 @@ +[{"Text":" quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The qu brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":6}},{"Mode":"Insert"},{"Text":"The quick\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":9}},{"Mode":"Insert"},{"Text":"The quick brow jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":14}},{"Mode":"Insert"},{"Text":"The quick brown\n jumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox -over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o\nthe lazy dog\n"},{"Head":{"row":2,"column":11}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n lazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The qu\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":6}},{"Mode":"Insert"},{"Text":"The quick jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":9}},{"Mode":"Insert"},{"Text":"The quick brow-over\nthe lazy dog\n"},{"Head":{"row":0,"column":14}},{"Mode":"Insert"},{"Text":"The quick brown\n-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps- lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o lazy dog\n"},{"Head":{"row":2,"column":11}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":"\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The qu jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":6}},{"Mode":"Insert"},{"Text":"The quick-over\nthe lazy dog\n"},{"Head":{"row":0,"column":9}},{"Mode":"Insert"},{"Text":"The quick browover\nthe lazy dog\n"},{"Head":{"row":0,"column":14}},{"Mode":"Insert"},{"Text":"The quick brown\nover\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nover\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox \nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps- dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o dog\n"},{"Head":{"row":2,"column":11}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":" jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The qu-over\nthe lazy dog\n"},{"Head":{"row":0,"column":6}},{"Mode":"Insert"},{"Text":"The quickover\nthe lazy dog\n"},{"Head":{"row":0,"column":9}},{"Mode":"Insert"},{"Text":"The quick brow\nthe lazy dog\n"},{"Head":{"row":0,"column":14}},{"Mode":"Insert"},{"Text":"The quick brown\n\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-\n"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o\n"},{"Head":{"row":2,"column":11}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":"-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quover\nthe lazy dog\n"},{"Head":{"row":0,"column":6}},{"Mode":"Insert"},{"Text":"The quick\nthe lazy dog\n"},{"Head":{"row":0,"column":9}},{"Mode":"Insert"},{"Text":"The quick brow lazy dog\n"},{"Head":{"row":0,"column":14}},{"Mode":"Insert"},{"Text":"The quick brown\n lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o"},{"Head":{"row":2,"column":11}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_repeated_cj.json b/crates/vim/test_data/test_repeated_cj.json new file mode 100644 index 0000000000..f6c81c9a4a --- /dev/null +++ b/crates/vim/test_data/test_repeated_cj.json @@ -0,0 +1 @@ +[{"Text":"\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":"\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":"\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":""},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":""},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":""},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":""},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":""},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":""},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":""},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":""},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_repeated_cl.json b/crates/vim/test_data/test_repeated_cl.json new file mode 100644 index 0000000000..7eaf17b24a --- /dev/null +++ b/crates/vim/test_data/test_repeated_cl.json @@ -0,0 +1 @@ +[{"Text":"he quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quck brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":6}},{"Mode":"Insert"},{"Text":"The quickbrown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":9}},{"Mode":"Insert"},{"Text":"The quick brow\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":14}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox umps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumpsover\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-ver\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-oer\nthe lazy dog\n"},{"Head":{"row":2,"column":11}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nhe lazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":"e quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quk brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":6}},{"Mode":"Insert"},{"Text":"The quickrown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":9}},{"Mode":"Insert"},{"Text":"The quick brow\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":14}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nx jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox mps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumpsver\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-er\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-or\nthe lazy dog\n"},{"Head":{"row":2,"column":11}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\ne lazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":" quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The qu brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":6}},{"Mode":"Insert"},{"Text":"The quickown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":9}},{"Mode":"Insert"},{"Text":"The quick brow\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":14}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox ps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumpser\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-r\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o\nthe lazy dog\n"},{"Head":{"row":2,"column":11}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n lazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":"quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The qubrown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":6}},{"Mode":"Insert"},{"Text":"The quickwn\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":9}},{"Mode":"Insert"},{"Text":"The quick brow\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":14}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\njumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox s-over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumpsr\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o\nthe lazy dog\n"},{"Head":{"row":2,"column":11}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nlazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":"uick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The qurown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":6}},{"Mode":"Insert"},{"Text":"The quickn\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":9}},{"Mode":"Insert"},{"Text":"The quick brow\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":14}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\numps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox -over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o\nthe lazy dog\n"},{"Head":{"row":2,"column":11}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_repeated_word.json b/crates/vim/test_data/test_repeated_word.json new file mode 100644 index 0000000000..330ada2013 --- /dev/null +++ b/crates/vim/test_data/test_repeated_word.json @@ -0,0 +1 @@ +[{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_w.json b/crates/vim/test_data/test_w.json new file mode 100644 index 0000000000..73954548f9 --- /dev/null +++ b/crates/vim/test_data/test_w.json @@ -0,0 +1 @@ +[{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":0,"column":9}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":0,"column":10}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":2,"column":0}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":3,"column":10}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":4,"column":2}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":4,"column":2}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":2,"column":0}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":3,"column":10}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":4,"column":2}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":4,"column":2}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/0text.txt b/crates/vim/test_data/test_word_text_object/0text.txt deleted file mode 100644 index 456c2b1038..0000000000 --- a/crates/vim/test_data/test_word_text_object/0text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/100head.txt b/crates/vim/test_data/test_word_text_object/100head.txt deleted file mode 100644 index da809253d8..0000000000 --- a/crates/vim/test_data/test_word_text_object/100head.txt +++ /dev/null @@ -1 +0,0 @@ -6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/101mode.txt b/crates/vim/test_data/test_word_text_object/101mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/101mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/102text.txt b/crates/vim/test_data/test_word_text_object/102text.txt deleted file mode 100644 index fb34ebac01..0000000000 --- a/crates/vim/test_data/test_word_text_object/102text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/103head.txt b/crates/vim/test_data/test_word_text_object/103head.txt deleted file mode 100644 index c5a50698dd..0000000000 --- a/crates/vim/test_data/test_word_text_object/103head.txt +++ /dev/null @@ -1 +0,0 @@ -6,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/104mode.txt b/crates/vim/test_data/test_word_text_object/104mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/104mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/105text.txt b/crates/vim/test_data/test_word_text_object/105text.txt deleted file mode 100644 index de6c722fe0..0000000000 --- a/crates/vim/test_data/test_word_text_object/105text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/106head.txt b/crates/vim/test_data/test_word_text_object/106head.txt deleted file mode 100644 index d933eb81fa..0000000000 --- a/crates/vim/test_data/test_word_text_object/106head.txt +++ /dev/null @@ -1 +0,0 @@ -6,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/107mode.txt b/crates/vim/test_data/test_word_text_object/107mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/107mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/108text.txt b/crates/vim/test_data/test_word_text_object/108text.txt deleted file mode 100644 index 6d1c944902..0000000000 --- a/crates/vim/test_data/test_word_text_object/108text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown -fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/109head.txt b/crates/vim/test_data/test_word_text_object/109head.txt deleted file mode 100644 index 72c23ba506..0000000000 --- a/crates/vim/test_data/test_word_text_object/109head.txt +++ /dev/null @@ -1 +0,0 @@ -7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/10head.txt b/crates/vim/test_data/test_word_text_object/10head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_word_text_object/10head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/110mode.txt b/crates/vim/test_data/test_word_text_object/110mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/110mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/111text.txt b/crates/vim/test_data/test_word_text_object/111text.txt deleted file mode 100644 index a4effc421e..0000000000 --- a/crates/vim/test_data/test_word_text_object/111text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/112head.txt b/crates/vim/test_data/test_word_text_object/112head.txt deleted file mode 100644 index 60ada3d5d5..0000000000 --- a/crates/vim/test_data/test_word_text_object/112head.txt +++ /dev/null @@ -1 +0,0 @@ -7,2 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/113mode.txt b/crates/vim/test_data/test_word_text_object/113mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/113mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/114text.txt b/crates/vim/test_data/test_word_text_object/114text.txt deleted file mode 100644 index 41651c8e63..0000000000 --- a/crates/vim/test_data/test_word_text_object/114text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/115head.txt b/crates/vim/test_data/test_word_text_object/115head.txt deleted file mode 100644 index 352f6067e6..0000000000 --- a/crates/vim/test_data/test_word_text_object/115head.txt +++ /dev/null @@ -1 +0,0 @@ -0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/116mode.txt b/crates/vim/test_data/test_word_text_object/116mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/116mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/117text.txt b/crates/vim/test_data/test_word_text_object/117text.txt deleted file mode 100644 index 41651c8e63..0000000000 --- a/crates/vim/test_data/test_word_text_object/117text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/118head.txt b/crates/vim/test_data/test_word_text_object/118head.txt deleted file mode 100644 index 352f6067e6..0000000000 --- a/crates/vim/test_data/test_word_text_object/118head.txt +++ /dev/null @@ -1 +0,0 @@ -0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/119mode.txt b/crates/vim/test_data/test_word_text_object/119mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/119mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/11mode.txt b/crates/vim/test_data/test_word_text_object/11mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/11mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/120text.txt b/crates/vim/test_data/test_word_text_object/120text.txt deleted file mode 100644 index f4e4cfe812..0000000000 --- a/crates/vim/test_data/test_word_text_object/120text.txt +++ /dev/null @@ -1,8 +0,0 @@ -The quick brown jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/121head.txt b/crates/vim/test_data/test_word_text_object/121head.txt deleted file mode 100644 index 5f7b20f0d9..0000000000 --- a/crates/vim/test_data/test_word_text_object/121head.txt +++ /dev/null @@ -1 +0,0 @@ -0,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/122mode.txt b/crates/vim/test_data/test_word_text_object/122mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/122mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/123text.txt b/crates/vim/test_data/test_word_text_object/123text.txt deleted file mode 100644 index 1a715d7a9d..0000000000 --- a/crates/vim/test_data/test_word_text_object/123text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/124head.txt b/crates/vim/test_data/test_word_text_object/124head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_word_text_object/124head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/125mode.txt b/crates/vim/test_data/test_word_text_object/125mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/125mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/126text.txt b/crates/vim/test_data/test_word_text_object/126text.txt deleted file mode 100644 index 1a715d7a9d..0000000000 --- a/crates/vim/test_data/test_word_text_object/126text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/127head.txt b/crates/vim/test_data/test_word_text_object/127head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_word_text_object/127head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/128mode.txt b/crates/vim/test_data/test_word_text_object/128mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/128mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/129text.txt b/crates/vim/test_data/test_word_text_object/129text.txt deleted file mode 100644 index cfeeac1a14..0000000000 --- a/crates/vim/test_data/test_word_text_object/129text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/12text.txt b/crates/vim/test_data/test_word_text_object/12text.txt deleted file mode 100644 index bffcedd2db..0000000000 --- a/crates/vim/test_data/test_word_text_object/12text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/130head.txt b/crates/vim/test_data/test_word_text_object/130head.txt deleted file mode 100644 index bd712da63a..0000000000 --- a/crates/vim/test_data/test_word_text_object/130head.txt +++ /dev/null @@ -1 +0,0 @@ -1,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/131mode.txt b/crates/vim/test_data/test_word_text_object/131mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/131mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/132text.txt b/crates/vim/test_data/test_word_text_object/132text.txt deleted file mode 100644 index b57b97d090..0000000000 --- a/crates/vim/test_data/test_word_text_object/132text.txt +++ /dev/null @@ -1,8 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/133head.txt b/crates/vim/test_data/test_word_text_object/133head.txt deleted file mode 100644 index 28ce8bcc5c..0000000000 --- a/crates/vim/test_data/test_word_text_object/133head.txt +++ /dev/null @@ -1 +0,0 @@ -2,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/134mode.txt b/crates/vim/test_data/test_word_text_object/134mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/134mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/135text.txt b/crates/vim/test_data/test_word_text_object/135text.txt deleted file mode 100644 index e2405acd79..0000000000 --- a/crates/vim/test_data/test_word_text_object/135text.txt +++ /dev/null @@ -1,8 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/136head.txt b/crates/vim/test_data/test_word_text_object/136head.txt deleted file mode 100644 index 7cad756336..0000000000 --- a/crates/vim/test_data/test_word_text_object/136head.txt +++ /dev/null @@ -1 +0,0 @@ -3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/137mode.txt b/crates/vim/test_data/test_word_text_object/137mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/137mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/138text.txt b/crates/vim/test_data/test_word_text_object/138text.txt deleted file mode 100644 index e2405acd79..0000000000 --- a/crates/vim/test_data/test_word_text_object/138text.txt +++ /dev/null @@ -1,8 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/139head.txt b/crates/vim/test_data/test_word_text_object/139head.txt deleted file mode 100644 index b26cd1f1d3..0000000000 --- a/crates/vim/test_data/test_word_text_object/139head.txt +++ /dev/null @@ -1 +0,0 @@ -4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/13head.txt b/crates/vim/test_data/test_word_text_object/13head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_word_text_object/13head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/140mode.txt b/crates/vim/test_data/test_word_text_object/140mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/140mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/141text.txt b/crates/vim/test_data/test_word_text_object/141text.txt deleted file mode 100644 index e075f5e01a..0000000000 --- a/crates/vim/test_data/test_word_text_object/141text.txt +++ /dev/null @@ -1,8 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - --quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/142head.txt b/crates/vim/test_data/test_word_text_object/142head.txt deleted file mode 100644 index 880fda949a..0000000000 --- a/crates/vim/test_data/test_word_text_object/142head.txt +++ /dev/null @@ -1 +0,0 @@ -5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/143mode.txt b/crates/vim/test_data/test_word_text_object/143mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/143mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/144text.txt b/crates/vim/test_data/test_word_text_object/144text.txt deleted file mode 100644 index 6042949b9a..0000000000 --- a/crates/vim/test_data/test_word_text_object/144text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - --quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/145head.txt b/crates/vim/test_data/test_word_text_object/145head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_word_text_object/145head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/146mode.txt b/crates/vim/test_data/test_word_text_object/146mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/146mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/147text.txt b/crates/vim/test_data/test_word_text_object/147text.txt deleted file mode 100644 index edc3de8e10..0000000000 --- a/crates/vim/test_data/test_word_text_object/147text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -Thequick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/148head.txt b/crates/vim/test_data/test_word_text_object/148head.txt deleted file mode 100644 index c20df71d5e..0000000000 --- a/crates/vim/test_data/test_word_text_object/148head.txt +++ /dev/null @@ -1 +0,0 @@ -6,3 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/149mode.txt b/crates/vim/test_data/test_word_text_object/149mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/149mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/14mode.txt b/crates/vim/test_data/test_word_text_object/14mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/14mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/150text.txt b/crates/vim/test_data/test_word_text_object/150text.txt deleted file mode 100644 index 44972e7628..0000000000 --- a/crates/vim/test_data/test_word_text_object/150text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/151head.txt b/crates/vim/test_data/test_word_text_object/151head.txt deleted file mode 100644 index 95fb9c638e..0000000000 --- a/crates/vim/test_data/test_word_text_object/151head.txt +++ /dev/null @@ -1 +0,0 @@ -6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/152mode.txt b/crates/vim/test_data/test_word_text_object/152mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/152mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/153text.txt b/crates/vim/test_data/test_word_text_object/153text.txt deleted file mode 100644 index 44972e7628..0000000000 --- a/crates/vim/test_data/test_word_text_object/153text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/154head.txt b/crates/vim/test_data/test_word_text_object/154head.txt deleted file mode 100644 index 95fb9c638e..0000000000 --- a/crates/vim/test_data/test_word_text_object/154head.txt +++ /dev/null @@ -1 +0,0 @@ -6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/155mode.txt b/crates/vim/test_data/test_word_text_object/155mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/155mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/156text.txt b/crates/vim/test_data/test_word_text_object/156text.txt deleted file mode 100644 index d7c8eebb49..0000000000 --- a/crates/vim/test_data/test_word_text_object/156text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/157head.txt b/crates/vim/test_data/test_word_text_object/157head.txt deleted file mode 100644 index da809253d8..0000000000 --- a/crates/vim/test_data/test_word_text_object/157head.txt +++ /dev/null @@ -1 +0,0 @@ -6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/158mode.txt b/crates/vim/test_data/test_word_text_object/158mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/158mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/159text.txt b/crates/vim/test_data/test_word_text_object/159text.txt deleted file mode 100644 index d7c8eebb49..0000000000 --- a/crates/vim/test_data/test_word_text_object/159text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/15text.txt b/crates/vim/test_data/test_word_text_object/15text.txt deleted file mode 100644 index 3abea644f1..0000000000 --- a/crates/vim/test_data/test_word_text_object/15text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumpsover -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/160head.txt b/crates/vim/test_data/test_word_text_object/160head.txt deleted file mode 100644 index c5a50698dd..0000000000 --- a/crates/vim/test_data/test_word_text_object/160head.txt +++ /dev/null @@ -1 +0,0 @@ -6,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/161mode.txt b/crates/vim/test_data/test_word_text_object/161mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/161mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/162text.txt b/crates/vim/test_data/test_word_text_object/162text.txt deleted file mode 100644 index f0f447c8fa..0000000000 --- a/crates/vim/test_data/test_word_text_object/162text.txt +++ /dev/null @@ -1,8 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/163head.txt b/crates/vim/test_data/test_word_text_object/163head.txt deleted file mode 100644 index d933eb81fa..0000000000 --- a/crates/vim/test_data/test_word_text_object/163head.txt +++ /dev/null @@ -1 +0,0 @@ -6,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/164mode.txt b/crates/vim/test_data/test_word_text_object/164mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/164mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/165text.txt b/crates/vim/test_data/test_word_text_object/165text.txt deleted file mode 100644 index 3fcf6c5682..0000000000 --- a/crates/vim/test_data/test_word_text_object/165text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown --jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/166head.txt b/crates/vim/test_data/test_word_text_object/166head.txt deleted file mode 100644 index 72c23ba506..0000000000 --- a/crates/vim/test_data/test_word_text_object/166head.txt +++ /dev/null @@ -1 +0,0 @@ -7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/167mode.txt b/crates/vim/test_data/test_word_text_object/167mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/167mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/168text.txt b/crates/vim/test_data/test_word_text_object/168text.txt deleted file mode 100644 index 88098b63df..0000000000 --- a/crates/vim/test_data/test_word_text_object/168text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - fox-over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/169head.txt b/crates/vim/test_data/test_word_text_object/169head.txt deleted file mode 100644 index 6cf0fedea6..0000000000 --- a/crates/vim/test_data/test_word_text_object/169head.txt +++ /dev/null @@ -1 +0,0 @@ -7,6 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/16head.txt b/crates/vim/test_data/test_word_text_object/16head.txt deleted file mode 100644 index bd712da63a..0000000000 --- a/crates/vim/test_data/test_word_text_object/16head.txt +++ /dev/null @@ -1 +0,0 @@ -1,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/170mode.txt b/crates/vim/test_data/test_word_text_object/170mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/170mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/171text.txt b/crates/vim/test_data/test_word_text_object/171text.txt deleted file mode 100644 index 41651c8e63..0000000000 --- a/crates/vim/test_data/test_word_text_object/171text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/172head.txt b/crates/vim/test_data/test_word_text_object/172head.txt deleted file mode 100644 index 352f6067e6..0000000000 --- a/crates/vim/test_data/test_word_text_object/172head.txt +++ /dev/null @@ -1 +0,0 @@ -0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/173mode.txt b/crates/vim/test_data/test_word_text_object/173mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/173mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/174text.txt b/crates/vim/test_data/test_word_text_object/174text.txt deleted file mode 100644 index 41651c8e63..0000000000 --- a/crates/vim/test_data/test_word_text_object/174text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/175head.txt b/crates/vim/test_data/test_word_text_object/175head.txt deleted file mode 100644 index 352f6067e6..0000000000 --- a/crates/vim/test_data/test_word_text_object/175head.txt +++ /dev/null @@ -1 +0,0 @@ -0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/176mode.txt b/crates/vim/test_data/test_word_text_object/176mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/176mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/177text.txt b/crates/vim/test_data/test_word_text_object/177text.txt deleted file mode 100644 index f4e4cfe812..0000000000 --- a/crates/vim/test_data/test_word_text_object/177text.txt +++ /dev/null @@ -1,8 +0,0 @@ -The quick brown jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/178head.txt b/crates/vim/test_data/test_word_text_object/178head.txt deleted file mode 100644 index 5f7b20f0d9..0000000000 --- a/crates/vim/test_data/test_word_text_object/178head.txt +++ /dev/null @@ -1 +0,0 @@ -0,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/179mode.txt b/crates/vim/test_data/test_word_text_object/179mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/179mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/17mode.txt b/crates/vim/test_data/test_word_text_object/17mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/17mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/180text.txt b/crates/vim/test_data/test_word_text_object/180text.txt deleted file mode 100644 index 1a715d7a9d..0000000000 --- a/crates/vim/test_data/test_word_text_object/180text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/181head.txt b/crates/vim/test_data/test_word_text_object/181head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_word_text_object/181head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/182mode.txt b/crates/vim/test_data/test_word_text_object/182mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/182mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/183text.txt b/crates/vim/test_data/test_word_text_object/183text.txt deleted file mode 100644 index 1a715d7a9d..0000000000 --- a/crates/vim/test_data/test_word_text_object/183text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/184head.txt b/crates/vim/test_data/test_word_text_object/184head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_word_text_object/184head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/185mode.txt b/crates/vim/test_data/test_word_text_object/185mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/185mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/186text.txt b/crates/vim/test_data/test_word_text_object/186text.txt deleted file mode 100644 index cfeeac1a14..0000000000 --- a/crates/vim/test_data/test_word_text_object/186text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/187head.txt b/crates/vim/test_data/test_word_text_object/187head.txt deleted file mode 100644 index bd712da63a..0000000000 --- a/crates/vim/test_data/test_word_text_object/187head.txt +++ /dev/null @@ -1 +0,0 @@ -1,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/188mode.txt b/crates/vim/test_data/test_word_text_object/188mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/188mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/189text.txt b/crates/vim/test_data/test_word_text_object/189text.txt deleted file mode 100644 index b57b97d090..0000000000 --- a/crates/vim/test_data/test_word_text_object/189text.txt +++ /dev/null @@ -1,8 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/18text.txt b/crates/vim/test_data/test_word_text_object/18text.txt deleted file mode 100644 index ebac2acd25..0000000000 --- a/crates/vim/test_data/test_word_text_object/18text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/190head.txt b/crates/vim/test_data/test_word_text_object/190head.txt deleted file mode 100644 index 28ce8bcc5c..0000000000 --- a/crates/vim/test_data/test_word_text_object/190head.txt +++ /dev/null @@ -1 +0,0 @@ -2,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/191mode.txt b/crates/vim/test_data/test_word_text_object/191mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/191mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/192text.txt b/crates/vim/test_data/test_word_text_object/192text.txt deleted file mode 100644 index e2405acd79..0000000000 --- a/crates/vim/test_data/test_word_text_object/192text.txt +++ /dev/null @@ -1,8 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/193head.txt b/crates/vim/test_data/test_word_text_object/193head.txt deleted file mode 100644 index 7cad756336..0000000000 --- a/crates/vim/test_data/test_word_text_object/193head.txt +++ /dev/null @@ -1 +0,0 @@ -3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/194mode.txt b/crates/vim/test_data/test_word_text_object/194mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/194mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/195text.txt b/crates/vim/test_data/test_word_text_object/195text.txt deleted file mode 100644 index e2405acd79..0000000000 --- a/crates/vim/test_data/test_word_text_object/195text.txt +++ /dev/null @@ -1,8 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/196head.txt b/crates/vim/test_data/test_word_text_object/196head.txt deleted file mode 100644 index b26cd1f1d3..0000000000 --- a/crates/vim/test_data/test_word_text_object/196head.txt +++ /dev/null @@ -1 +0,0 @@ -4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/197mode.txt b/crates/vim/test_data/test_word_text_object/197mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/197mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/198text.txt b/crates/vim/test_data/test_word_text_object/198text.txt deleted file mode 100644 index 4363975ac2..0000000000 --- a/crates/vim/test_data/test_word_text_object/198text.txt +++ /dev/null @@ -1,8 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/199head.txt b/crates/vim/test_data/test_word_text_object/199head.txt deleted file mode 100644 index 880fda949a..0000000000 --- a/crates/vim/test_data/test_word_text_object/199head.txt +++ /dev/null @@ -1 +0,0 @@ -5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/19head.txt b/crates/vim/test_data/test_word_text_object/19head.txt deleted file mode 100644 index 28ce8bcc5c..0000000000 --- a/crates/vim/test_data/test_word_text_object/19head.txt +++ /dev/null @@ -1 +0,0 @@ -2,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/1head.txt b/crates/vim/test_data/test_word_text_object/1head.txt deleted file mode 100644 index 352f6067e6..0000000000 --- a/crates/vim/test_data/test_word_text_object/1head.txt +++ /dev/null @@ -1 +0,0 @@ -0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/200mode.txt b/crates/vim/test_data/test_word_text_object/200mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/200mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/201text.txt b/crates/vim/test_data/test_word_text_object/201text.txt deleted file mode 100644 index 7c0186083e..0000000000 --- a/crates/vim/test_data/test_word_text_object/201text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/202head.txt b/crates/vim/test_data/test_word_text_object/202head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_word_text_object/202head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/203mode.txt b/crates/vim/test_data/test_word_text_object/203mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/203mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/204text.txt b/crates/vim/test_data/test_word_text_object/204text.txt deleted file mode 100644 index 7c0186083e..0000000000 --- a/crates/vim/test_data/test_word_text_object/204text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/205head.txt b/crates/vim/test_data/test_word_text_object/205head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_word_text_object/205head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/206mode.txt b/crates/vim/test_data/test_word_text_object/206mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/206mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/207text.txt b/crates/vim/test_data/test_word_text_object/207text.txt deleted file mode 100644 index 7c0186083e..0000000000 --- a/crates/vim/test_data/test_word_text_object/207text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/208head.txt b/crates/vim/test_data/test_word_text_object/208head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_word_text_object/208head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/209mode.txt b/crates/vim/test_data/test_word_text_object/209mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/209mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/20mode.txt b/crates/vim/test_data/test_word_text_object/20mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/20mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/210text.txt b/crates/vim/test_data/test_word_text_object/210text.txt deleted file mode 100644 index 7c0186083e..0000000000 --- a/crates/vim/test_data/test_word_text_object/210text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/211head.txt b/crates/vim/test_data/test_word_text_object/211head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_word_text_object/211head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/212mode.txt b/crates/vim/test_data/test_word_text_object/212mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/212mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/213text.txt b/crates/vim/test_data/test_word_text_object/213text.txt deleted file mode 100644 index d7c8eebb49..0000000000 --- a/crates/vim/test_data/test_word_text_object/213text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/214head.txt b/crates/vim/test_data/test_word_text_object/214head.txt deleted file mode 100644 index da809253d8..0000000000 --- a/crates/vim/test_data/test_word_text_object/214head.txt +++ /dev/null @@ -1 +0,0 @@ -6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/215mode.txt b/crates/vim/test_data/test_word_text_object/215mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/215mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/216text.txt b/crates/vim/test_data/test_word_text_object/216text.txt deleted file mode 100644 index d7c8eebb49..0000000000 --- a/crates/vim/test_data/test_word_text_object/216text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/217head.txt b/crates/vim/test_data/test_word_text_object/217head.txt deleted file mode 100644 index c5a50698dd..0000000000 --- a/crates/vim/test_data/test_word_text_object/217head.txt +++ /dev/null @@ -1 +0,0 @@ -6,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/218mode.txt b/crates/vim/test_data/test_word_text_object/218mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/218mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/219text.txt b/crates/vim/test_data/test_word_text_object/219text.txt deleted file mode 100644 index 1cbb0c4272..0000000000 --- a/crates/vim/test_data/test_word_text_object/219text.txt +++ /dev/null @@ -1,8 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/21text.txt b/crates/vim/test_data/test_word_text_object/21text.txt deleted file mode 100644 index 3b33deb2ae..0000000000 --- a/crates/vim/test_data/test_word_text_object/21text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/220head.txt b/crates/vim/test_data/test_word_text_object/220head.txt deleted file mode 100644 index d933eb81fa..0000000000 --- a/crates/vim/test_data/test_word_text_object/220head.txt +++ /dev/null @@ -1 +0,0 @@ -6,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/221mode.txt b/crates/vim/test_data/test_word_text_object/221mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/221mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/222text.txt b/crates/vim/test_data/test_word_text_object/222text.txt deleted file mode 100644 index bc43fca8a1..0000000000 --- a/crates/vim/test_data/test_word_text_object/222text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/223head.txt b/crates/vim/test_data/test_word_text_object/223head.txt deleted file mode 100644 index 72c23ba506..0000000000 --- a/crates/vim/test_data/test_word_text_object/223head.txt +++ /dev/null @@ -1 +0,0 @@ -7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/224mode.txt b/crates/vim/test_data/test_word_text_object/224mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/224mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/225text.txt b/crates/vim/test_data/test_word_text_object/225text.txt deleted file mode 100644 index 3fa4fd5f45..0000000000 --- a/crates/vim/test_data/test_word_text_object/225text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/226head.txt b/crates/vim/test_data/test_word_text_object/226head.txt deleted file mode 100644 index 60ada3d5d5..0000000000 --- a/crates/vim/test_data/test_word_text_object/226head.txt +++ /dev/null @@ -1 +0,0 @@ -7,2 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/227mode.txt b/crates/vim/test_data/test_word_text_object/227mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/227mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/228text.txt b/crates/vim/test_data/test_word_text_object/228text.txt deleted file mode 100644 index 456c2b1038..0000000000 --- a/crates/vim/test_data/test_word_text_object/228text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/229head.txt b/crates/vim/test_data/test_word_text_object/229head.txt deleted file mode 100644 index 352f6067e6..0000000000 --- a/crates/vim/test_data/test_word_text_object/229head.txt +++ /dev/null @@ -1 +0,0 @@ -0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/22head.txt b/crates/vim/test_data/test_word_text_object/22head.txt deleted file mode 100644 index 7cad756336..0000000000 --- a/crates/vim/test_data/test_word_text_object/22head.txt +++ /dev/null @@ -1 +0,0 @@ -3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/230mode.txt b/crates/vim/test_data/test_word_text_object/230mode.txt deleted file mode 100644 index ab75e91dc4..0000000000 --- a/crates/vim/test_data/test_word_text_object/230mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Normal" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/231text.txt b/crates/vim/test_data/test_word_text_object/231text.txt deleted file mode 100644 index 286752a955..0000000000 --- a/crates/vim/test_data/test_word_text_object/231text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/23mode.txt b/crates/vim/test_data/test_word_text_object/23mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/23mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/24text.txt b/crates/vim/test_data/test_word_text_object/24text.txt deleted file mode 100644 index 3b33deb2ae..0000000000 --- a/crates/vim/test_data/test_word_text_object/24text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/25head.txt b/crates/vim/test_data/test_word_text_object/25head.txt deleted file mode 100644 index b26cd1f1d3..0000000000 --- a/crates/vim/test_data/test_word_text_object/25head.txt +++ /dev/null @@ -1 +0,0 @@ -4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/26mode.txt b/crates/vim/test_data/test_word_text_object/26mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/26mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/27text.txt b/crates/vim/test_data/test_word_text_object/27text.txt deleted file mode 100644 index 3b33deb2ae..0000000000 --- a/crates/vim/test_data/test_word_text_object/27text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/28head.txt b/crates/vim/test_data/test_word_text_object/28head.txt deleted file mode 100644 index 880fda949a..0000000000 --- a/crates/vim/test_data/test_word_text_object/28head.txt +++ /dev/null @@ -1 +0,0 @@ -5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/29mode.txt b/crates/vim/test_data/test_word_text_object/29mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/29mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/2mode.txt b/crates/vim/test_data/test_word_text_object/2mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/2mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/30text.txt b/crates/vim/test_data/test_word_text_object/30text.txt deleted file mode 100644 index 6042949b9a..0000000000 --- a/crates/vim/test_data/test_word_text_object/30text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - --quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/31head.txt b/crates/vim/test_data/test_word_text_object/31head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_word_text_object/31head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/32mode.txt b/crates/vim/test_data/test_word_text_object/32mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/32mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/33text.txt b/crates/vim/test_data/test_word_text_object/33text.txt deleted file mode 100644 index edc3de8e10..0000000000 --- a/crates/vim/test_data/test_word_text_object/33text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -Thequick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/34head.txt b/crates/vim/test_data/test_word_text_object/34head.txt deleted file mode 100644 index c20df71d5e..0000000000 --- a/crates/vim/test_data/test_word_text_object/34head.txt +++ /dev/null @@ -1 +0,0 @@ -6,3 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/35mode.txt b/crates/vim/test_data/test_word_text_object/35mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/35mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/36text.txt b/crates/vim/test_data/test_word_text_object/36text.txt deleted file mode 100644 index dedd4d92b7..0000000000 --- a/crates/vim/test_data/test_word_text_object/36text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The- brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/37head.txt b/crates/vim/test_data/test_word_text_object/37head.txt deleted file mode 100644 index 95fb9c638e..0000000000 --- a/crates/vim/test_data/test_word_text_object/37head.txt +++ /dev/null @@ -1 +0,0 @@ -6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/38mode.txt b/crates/vim/test_data/test_word_text_object/38mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/38mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/39text.txt b/crates/vim/test_data/test_word_text_object/39text.txt deleted file mode 100644 index dedd4d92b7..0000000000 --- a/crates/vim/test_data/test_word_text_object/39text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The- brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/3text.txt b/crates/vim/test_data/test_word_text_object/3text.txt deleted file mode 100644 index 456c2b1038..0000000000 --- a/crates/vim/test_data/test_word_text_object/3text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/40head.txt b/crates/vim/test_data/test_word_text_object/40head.txt deleted file mode 100644 index 95fb9c638e..0000000000 --- a/crates/vim/test_data/test_word_text_object/40head.txt +++ /dev/null @@ -1 +0,0 @@ -6,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/41mode.txt b/crates/vim/test_data/test_word_text_object/41mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/41mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/42text.txt b/crates/vim/test_data/test_word_text_object/42text.txt deleted file mode 100644 index 5bb4f4effb..0000000000 --- a/crates/vim/test_data/test_word_text_object/42text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quickbrown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/43head.txt b/crates/vim/test_data/test_word_text_object/43head.txt deleted file mode 100644 index da809253d8..0000000000 --- a/crates/vim/test_data/test_word_text_object/43head.txt +++ /dev/null @@ -1 +0,0 @@ -6,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/44mode.txt b/crates/vim/test_data/test_word_text_object/44mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/44mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/45text.txt b/crates/vim/test_data/test_word_text_object/45text.txt deleted file mode 100644 index fb34ebac01..0000000000 --- a/crates/vim/test_data/test_word_text_object/45text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/46head.txt b/crates/vim/test_data/test_word_text_object/46head.txt deleted file mode 100644 index c5a50698dd..0000000000 --- a/crates/vim/test_data/test_word_text_object/46head.txt +++ /dev/null @@ -1 +0,0 @@ -6,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/47mode.txt b/crates/vim/test_data/test_word_text_object/47mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/47mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/48text.txt b/crates/vim/test_data/test_word_text_object/48text.txt deleted file mode 100644 index de6c722fe0..0000000000 --- a/crates/vim/test_data/test_word_text_object/48text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/49head.txt b/crates/vim/test_data/test_word_text_object/49head.txt deleted file mode 100644 index d933eb81fa..0000000000 --- a/crates/vim/test_data/test_word_text_object/49head.txt +++ /dev/null @@ -1 +0,0 @@ -6,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/4head.txt b/crates/vim/test_data/test_word_text_object/4head.txt deleted file mode 100644 index 352f6067e6..0000000000 --- a/crates/vim/test_data/test_word_text_object/4head.txt +++ /dev/null @@ -1 +0,0 @@ -0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/50mode.txt b/crates/vim/test_data/test_word_text_object/50mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/50mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/51text.txt b/crates/vim/test_data/test_word_text_object/51text.txt deleted file mode 100644 index 6d1c944902..0000000000 --- a/crates/vim/test_data/test_word_text_object/51text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown -fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/52head.txt b/crates/vim/test_data/test_word_text_object/52head.txt deleted file mode 100644 index 72c23ba506..0000000000 --- a/crates/vim/test_data/test_word_text_object/52head.txt +++ /dev/null @@ -1 +0,0 @@ -7,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/53mode.txt b/crates/vim/test_data/test_word_text_object/53mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/53mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/54text.txt b/crates/vim/test_data/test_word_text_object/54text.txt deleted file mode 100644 index 0f7af08f72..0000000000 --- a/crates/vim/test_data/test_word_text_object/54text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - fox- over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/55head.txt b/crates/vim/test_data/test_word_text_object/55head.txt deleted file mode 100644 index 6cf0fedea6..0000000000 --- a/crates/vim/test_data/test_word_text_object/55head.txt +++ /dev/null @@ -1 +0,0 @@ -7,6 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/56mode.txt b/crates/vim/test_data/test_word_text_object/56mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/56mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/57text.txt b/crates/vim/test_data/test_word_text_object/57text.txt deleted file mode 100644 index 456c2b1038..0000000000 --- a/crates/vim/test_data/test_word_text_object/57text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/58head.txt b/crates/vim/test_data/test_word_text_object/58head.txt deleted file mode 100644 index 352f6067e6..0000000000 --- a/crates/vim/test_data/test_word_text_object/58head.txt +++ /dev/null @@ -1 +0,0 @@ -0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/59mode.txt b/crates/vim/test_data/test_word_text_object/59mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/59mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/5mode.txt b/crates/vim/test_data/test_word_text_object/5mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/5mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/60text.txt b/crates/vim/test_data/test_word_text_object/60text.txt deleted file mode 100644 index 456c2b1038..0000000000 --- a/crates/vim/test_data/test_word_text_object/60text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick -fox jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/61head.txt b/crates/vim/test_data/test_word_text_object/61head.txt deleted file mode 100644 index 352f6067e6..0000000000 --- a/crates/vim/test_data/test_word_text_object/61head.txt +++ /dev/null @@ -1 +0,0 @@ -0,10 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/62mode.txt b/crates/vim/test_data/test_word_text_object/62mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/62mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/63text.txt b/crates/vim/test_data/test_word_text_object/63text.txt deleted file mode 100644 index 286752a955..0000000000 --- a/crates/vim/test_data/test_word_text_object/63text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/64head.txt b/crates/vim/test_data/test_word_text_object/64head.txt deleted file mode 100644 index 5f7b20f0d9..0000000000 --- a/crates/vim/test_data/test_word_text_object/64head.txt +++ /dev/null @@ -1 +0,0 @@ -0,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/65mode.txt b/crates/vim/test_data/test_word_text_object/65mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/65mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/66text.txt b/crates/vim/test_data/test_word_text_object/66text.txt deleted file mode 100644 index bffcedd2db..0000000000 --- a/crates/vim/test_data/test_word_text_object/66text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/67head.txt b/crates/vim/test_data/test_word_text_object/67head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_word_text_object/67head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/68mode.txt b/crates/vim/test_data/test_word_text_object/68mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/68mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/69text.txt b/crates/vim/test_data/test_word_text_object/69text.txt deleted file mode 100644 index bffcedd2db..0000000000 --- a/crates/vim/test_data/test_word_text_object/69text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/6text.txt b/crates/vim/test_data/test_word_text_object/6text.txt deleted file mode 100644 index 286752a955..0000000000 --- a/crates/vim/test_data/test_word_text_object/6text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/70head.txt b/crates/vim/test_data/test_word_text_object/70head.txt deleted file mode 100644 index 28f7acb8c9..0000000000 --- a/crates/vim/test_data/test_word_text_object/70head.txt +++ /dev/null @@ -1 +0,0 @@ -1,4 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/71mode.txt b/crates/vim/test_data/test_word_text_object/71mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/71mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/72text.txt b/crates/vim/test_data/test_word_text_object/72text.txt deleted file mode 100644 index 3abea644f1..0000000000 --- a/crates/vim/test_data/test_word_text_object/72text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumpsover -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/73head.txt b/crates/vim/test_data/test_word_text_object/73head.txt deleted file mode 100644 index bd712da63a..0000000000 --- a/crates/vim/test_data/test_word_text_object/73head.txt +++ /dev/null @@ -1 +0,0 @@ -1,9 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/74mode.txt b/crates/vim/test_data/test_word_text_object/74mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/74mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/75text.txt b/crates/vim/test_data/test_word_text_object/75text.txt deleted file mode 100644 index ebac2acd25..0000000000 --- a/crates/vim/test_data/test_word_text_object/75text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/76head.txt b/crates/vim/test_data/test_word_text_object/76head.txt deleted file mode 100644 index 28ce8bcc5c..0000000000 --- a/crates/vim/test_data/test_word_text_object/76head.txt +++ /dev/null @@ -1 +0,0 @@ -2,12 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/77mode.txt b/crates/vim/test_data/test_word_text_object/77mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/77mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/78text.txt b/crates/vim/test_data/test_word_text_object/78text.txt deleted file mode 100644 index 3b33deb2ae..0000000000 --- a/crates/vim/test_data/test_word_text_object/78text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/79head.txt b/crates/vim/test_data/test_word_text_object/79head.txt deleted file mode 100644 index 7cad756336..0000000000 --- a/crates/vim/test_data/test_word_text_object/79head.txt +++ /dev/null @@ -1 +0,0 @@ -3,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/7head.txt b/crates/vim/test_data/test_word_text_object/7head.txt deleted file mode 100644 index 5f7b20f0d9..0000000000 --- a/crates/vim/test_data/test_word_text_object/7head.txt +++ /dev/null @@ -1 +0,0 @@ -0,15 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/80mode.txt b/crates/vim/test_data/test_word_text_object/80mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/80mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/81text.txt b/crates/vim/test_data/test_word_text_object/81text.txt deleted file mode 100644 index 3b33deb2ae..0000000000 --- a/crates/vim/test_data/test_word_text_object/81text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/82head.txt b/crates/vim/test_data/test_word_text_object/82head.txt deleted file mode 100644 index b26cd1f1d3..0000000000 --- a/crates/vim/test_data/test_word_text_object/82head.txt +++ /dev/null @@ -1 +0,0 @@ -4,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/83mode.txt b/crates/vim/test_data/test_word_text_object/83mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/83mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/84text.txt b/crates/vim/test_data/test_word_text_object/84text.txt deleted file mode 100644 index 3b33deb2ae..0000000000 --- a/crates/vim/test_data/test_word_text_object/84text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/85head.txt b/crates/vim/test_data/test_word_text_object/85head.txt deleted file mode 100644 index 880fda949a..0000000000 --- a/crates/vim/test_data/test_word_text_object/85head.txt +++ /dev/null @@ -1 +0,0 @@ -5,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/86mode.txt b/crates/vim/test_data/test_word_text_object/86mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/86mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/87text.txt b/crates/vim/test_data/test_word_text_object/87text.txt deleted file mode 100644 index a647811a26..0000000000 --- a/crates/vim/test_data/test_word_text_object/87text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - - brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/88head.txt b/crates/vim/test_data/test_word_text_object/88head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_word_text_object/88head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/89mode.txt b/crates/vim/test_data/test_word_text_object/89mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/89mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/8mode.txt b/crates/vim/test_data/test_word_text_object/8mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/8mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/90text.txt b/crates/vim/test_data/test_word_text_object/90text.txt deleted file mode 100644 index a647811a26..0000000000 --- a/crates/vim/test_data/test_word_text_object/90text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - - brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/91head.txt b/crates/vim/test_data/test_word_text_object/91head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_word_text_object/91head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/92mode.txt b/crates/vim/test_data/test_word_text_object/92mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/92mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/93text.txt b/crates/vim/test_data/test_word_text_object/93text.txt deleted file mode 100644 index a647811a26..0000000000 --- a/crates/vim/test_data/test_word_text_object/93text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - - brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/94head.txt b/crates/vim/test_data/test_word_text_object/94head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_word_text_object/94head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/95mode.txt b/crates/vim/test_data/test_word_text_object/95mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/95mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/96text.txt b/crates/vim/test_data/test_word_text_object/96text.txt deleted file mode 100644 index a647811a26..0000000000 --- a/crates/vim/test_data/test_word_text_object/96text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - - brown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/97head.txt b/crates/vim/test_data/test_word_text_object/97head.txt deleted file mode 100644 index f7c508be16..0000000000 --- a/crates/vim/test_data/test_word_text_object/97head.txt +++ /dev/null @@ -1 +0,0 @@ -6,0 \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/98mode.txt b/crates/vim/test_data/test_word_text_object/98mode.txt deleted file mode 100644 index d0d2c59887..0000000000 --- a/crates/vim/test_data/test_word_text_object/98mode.txt +++ /dev/null @@ -1 +0,0 @@ -"Insert" \ No newline at end of file diff --git a/crates/vim/test_data/test_word_text_object/99text.txt b/crates/vim/test_data/test_word_text_object/99text.txt deleted file mode 100644 index 5bb4f4effb..0000000000 --- a/crates/vim/test_data/test_word_text_object/99text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox jumps over -the lazy dog - - - -The-quickbrown - fox-jumps over -the lazy dog diff --git a/crates/vim/test_data/test_word_text_object/9text.txt b/crates/vim/test_data/test_word_text_object/9text.txt deleted file mode 100644 index bffcedd2db..0000000000 --- a/crates/vim/test_data/test_word_text_object/9text.txt +++ /dev/null @@ -1,9 +0,0 @@ -The quick brown -fox over -the lazy dog - - - -The-quick brown - fox-jumps over -the lazy dog From f90b693ed52c54d24598d760398c2710422a418c Mon Sep 17 00:00:00 2001 From: K Simmons Date: Sat, 8 Oct 2022 23:49:04 -0700 Subject: [PATCH 203/314] fix some warnings and merge errors --- Cargo.lock | 7630 +++++++++++++++++ .../neovim_backed_test_context.rs | 18 +- 2 files changed, 7631 insertions(+), 17 deletions(-) create mode 100644 Cargo.lock diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000000..1da2348ff9 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7630 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "activity_indicator" +version = "0.1.0" +dependencies = [ + "auto_update", + "editor", + "futures 0.3.24", + "gpui", + "language", + "project", + "settings", + "smallvec", + "util", + "workspace", +] + +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.7", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +dependencies = [ + "memchr", +] + +[[package]] +name = "alacritty_config" +version = "0.1.1-dev" +source = "git+https://github.com/zed-industries/alacritty?rev=a51dbe25d67e84d6ed4261e640d3954fbdd9be45#a51dbe25d67e84d6ed4261e640d3954fbdd9be45" +dependencies = [ + "log", + "serde", + "serde_yaml", +] + +[[package]] +name = "alacritty_config_derive" +version = "0.2.1-dev" +source = "git+https://github.com/zed-industries/alacritty?rev=a51dbe25d67e84d6ed4261e640d3954fbdd9be45#a51dbe25d67e84d6ed4261e640d3954fbdd9be45" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "alacritty_terminal" +version = "0.17.1-dev" +source = "git+https://github.com/zed-industries/alacritty?rev=a51dbe25d67e84d6ed4261e640d3954fbdd9be45#a51dbe25d67e84d6ed4261e640d3954fbdd9be45" +dependencies = [ + "alacritty_config", + "alacritty_config_derive", + "base64", + "bitflags", + "dirs 4.0.0", + "libc", + "log", + "mio 0.6.23", + "mio-anonymous-pipes", + "mio-extras", + "miow 0.3.7", + "nix", + "parking_lot 0.12.1", + "regex-automata", + "serde", + "serde_yaml", + "signal-hook", + "signal-hook-mio", + "unicode-width", + "vte", + "winapi 0.3.9", +] + +[[package]] +name = "ambient-authority" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8ad6edb4840b78c5c3d88de606b22252d552b55f3a4699fbb10fc070ec3049" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "anyhow" +version = "1.0.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + +[[package]] +name = "assets" +version = "0.1.0" +dependencies = [ + "anyhow", + "gpui", + "rust-embed", +] + +[[package]] +name = "async-broadcast" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90622698a1218e0b2fb846c97b5f19a0831f6baddee73d9454156365ccfa473b" +dependencies = [ + "easy-parallel", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-compat" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b48b4ff0c2026db683dea961cd8ea874737f56cffca86fa84415eaddc51c00d" +dependencies = [ + "futures-core", + "futures-io", + "once_cell", + "pin-project-lite 0.2.9", + "tokio", +] + +[[package]] +name = "async-compression" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" +dependencies = [ + "flate2", + "futures-core", + "futures-io", + "memchr", + "pin-project-lite 0.2.9", +] + +[[package]] +name = "async-executor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "once_cell", + "slab", +] + +[[package]] +name = "async-fs" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" +dependencies = [ + "async-lock", + "autocfg 1.1.0", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e21f3a490c72b3b0cf44962180e60045de2925d8dff97918f7ee43c8f637c7" +dependencies = [ + "autocfg 1.1.0", + "concurrent-queue", + "futures-lite", + "libc", + "log", + "once_cell", + "parking", + "polling", + "slab", + "socket2", + "waker-fn", + "winapi 0.3.9", +] + +[[package]] +name = "async-lock" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-net" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4051e67316bc7eff608fe723df5d32ed639946adcd69e07df41fd42a7b411f1f" +dependencies = [ + "async-io", + "autocfg 1.1.0", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-pipe" +version = "0.1.3" +source = "git+https://github.com/zed-industries/async-pipe-rs?rev=82d00a04211cf4e1236029aa03e6b6ce2a74c553#82d00a04211cf4e1236029aa03e6b6ce2a74c553" +dependencies = [ + "futures 0.3.24", + "log", +] + +[[package]] +name = "async-process" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02111fd8655a613c25069ea89fc8d9bb89331fa77486eb3bc059ee757cfa481c" +dependencies = [ + "async-io", + "autocfg 1.1.0", + "blocking", + "cfg-if 1.0.0", + "event-listener", + "futures-lite", + "libc", + "once_cell", + "signal-hook", + "winapi 0.3.9", +] + +[[package]] +name = "async-recursion" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" +dependencies = [ + "async-stream-impl", + "futures-core", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-task" +version = "4.0.3" +source = "git+https://github.com/zed-industries/async-task?rev=341b57d6de98cdfd7b418567b8de2022ca993a6e#341b57d6de98cdfd7b418567b8de2022ca993a6e" + +[[package]] +name = "async-tls" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f23d769dbf1838d5df5156e7b1ad404f4c463d1ac2c6aeb6cd943630f8a8400" +dependencies = [ + "futures-core", + "futures-io", + "rustls 0.19.1", + "webpki 0.21.4", + "webpki-roots 0.21.1", +] + +[[package]] +name = "async-trait" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-tungstenite" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5682ea0913e5c20780fe5785abacb85a411e7437bf52a1bedb93ddb3972cb8dd" +dependencies = [ + "async-tls", + "futures-io", + "futures-util", + "log", + "pin-project-lite 0.2.9", + "tungstenite 0.16.0", +] + +[[package]] +name = "atoi" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e" +dependencies = [ + "num-traits", +] + +[[package]] +name = "atomic" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b88d82667eca772c4aa12f0f1348b3ae643424c8876448f3f7bd5787032e234c" +dependencies = [ + "autocfg 1.1.0", +] + +[[package]] +name = "atomic-waker" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "auto_update" +version = "0.1.0" +dependencies = [ + "anyhow", + "client", + "gpui", + "isahc", + "lazy_static", + "log", + "menu", + "project", + "serde", + "serde_json", + "settings", + "smol", + "tempdir", + "theme", + "util", + "workspace", +] + +[[package]] +name = "autocfg" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" +dependencies = [ + "autocfg 1.1.0", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "axum" +version = "0.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e3356844c4d6a6d6467b8da2cffb4a2820be256f50a3a386c9d152bab31043" +dependencies = [ + "async-trait", + "axum-core", + "base64", + "bitflags", + "bytes 1.2.1", + "futures-util", + "headers", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite 0.2.9", + "serde", + "serde_json", + "serde_urlencoded", + "sha-1 0.10.0", + "sync_wrapper", + "tokio", + "tokio-tungstenite", + "tower", + "tower-http", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f0c0a60006f2a293d82d571f635042a72edf927539b7685bd62d361963839b" +dependencies = [ + "async-trait", + "bytes 1.2.1", + "futures-util", + "http", + "http-body", + "mime", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-extra" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69034b3b0fd97923eee2ce8a47540edb21e07f48f87f67d44bb4271cec622bdb" +dependencies = [ + "axum", + "bytes 1.2.1", + "futures-util", + "http", + "mime", + "pin-project-lite 0.2.9", + "serde", + "serde_json", + "tokio", + "tower", + "tower-http", + "tower-layer", + "tower-service", +] + +[[package]] +name = "backtrace" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide 0.5.4", + "object 0.29.0", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "base64ct" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2b2456fd614d856680dcd9fcc660a51a820fa09daef2e49772b56a193c8474" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bindgen" +version = "0.59.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "clap 2.34.0", + "env_logger", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" +dependencies = [ + "async-channel", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "once_cell", +] + +[[package]] +name = "breadcrumbs" +version = "0.1.0" +dependencies = [ + "collections", + "editor", + "gpui", + "itertools", + "language", + "project", + "search", + "settings", + "theme", + "workspace", +] + +[[package]] +name = "bromberg_sl2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ed88064f69518b7e3ea50ecfc1b61d43f19248618a377b95ae5c8b611134d4d" +dependencies = [ + "digest 0.9.0", + "lazy_static", + "rayon", + "seq-macro", +] + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "memchr", +] + +[[package]] +name = "bumpalo" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" + +[[package]] +name = "bytemuck" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +dependencies = [ + "byteorder", + "iovec", +] + +[[package]] +name = "bytes" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "cache-padded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" + +[[package]] +name = "cap-fs-ext" +version = "0.24.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e54b86398b5852ddd45784b1d9b196b98beb39171821bad4b8b44534a1e87927" +dependencies = [ + "cap-primitives", + "cap-std", + "io-lifetimes", + "winapi 0.3.9", +] + +[[package]] +name = "cap-primitives" +version = "0.24.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb8fca3e81fae1d91a36e9784ca22a39ef623702b5f7904d89dc31f10184a178" +dependencies = [ + "ambient-authority", + "errno", + "fs-set-times", + "io-extras", + "io-lifetimes", + "ipnet", + "maybe-owned", + "rustix", + "winapi 0.3.9", + "winapi-util", + "winx", +] + +[[package]] +name = "cap-rand" +version = "0.24.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3b27294116983d706f4c8168f6d10c84f9f5daed0c28bc7d0296cf16bcf971" +dependencies = [ + "ambient-authority", + "rand 0.8.5", +] + +[[package]] +name = "cap-std" +version = "0.24.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2247568946095c7765ad2b441a56caffc08027734c634a6d5edda648f04e32eb" +dependencies = [ + "cap-primitives", + "io-extras", + "io-lifetimes", + "ipnet", + "rustix", +] + +[[package]] +name = "cap-time-ext" +version = "0.24.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50472b6ebc302af0401fa3fb939694cd8ff00e0d4c9182001e434fc822ab83a" +dependencies = [ + "cap-primitives", + "once_cell", + "rustix", + "winx", +] + +[[package]] +name = "capture" +version = "0.1.0" +dependencies = [ + "anyhow", + "bindgen", + "block", + "byteorder", + "bytes 1.2.1", + "cocoa", + "core-foundation", + "core-graphics", + "foreign-types", + "futures 0.3.24", + "gpui", + "hmac 0.12.1", + "jwt", + "live_kit", + "log", + "media", + "objc", + "parking_lot 0.11.2", + "postage", + "serde", + "sha2 0.10.6", + "simplelog", +] + +[[package]] +name = "castaway" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chat_panel" +version = "0.1.0" +dependencies = [ + "client", + "editor", + "gpui", + "menu", + "postage", + "settings", + "theme", + "time 0.3.15", + "util", + "workspace", +] + +[[package]] +name = "chrono" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "time 0.1.44", + "wasm-bindgen", + "winapi 0.3.9", +] + +[[package]] +name = "chunked_transfer" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e" + +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + +[[package]] +name = "clang-sys" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim 0.8.0", + "textwrap 0.11.0", + "unicode-width", + "vec_map", +] + +[[package]] +name = "clap" +version = "3.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "clap_lex", + "indexmap", + "once_cell", + "strsim 0.10.0", + "termcolor", + "textwrap 0.15.1", +] + +[[package]] +name = "clap_derive" +version = "3.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +dependencies = [ + "heck 0.4.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "cli" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap 3.2.22", + "core-foundation", + "core-services", + "dirs 3.0.2", + "ipc-channel", + "plist", + "serde", +] + +[[package]] +name = "client" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-recursion", + "async-tungstenite", + "collections", + "db", + "futures 0.3.24", + "gpui", + "image", + "isahc", + "lazy_static", + "log", + "parking_lot 0.11.2", + "postage", + "rand 0.8.5", + "rpc", + "serde", + "smol", + "sum_tree", + "tempfile", + "thiserror", + "time 0.3.15", + "tiny_http", + "url", + "util", + "uuid 1.2.1", +] + +[[package]] +name = "clock" +version = "0.1.0" +dependencies = [ + "smallvec", +] + +[[package]] +name = "cmake" +version = "0.1.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a" +dependencies = [ + "cc", +] + +[[package]] +name = "cocoa" +version = "0.24.0" +source = "git+https://github.com/servo/core-foundation-rs?rev=079665882507dd5e2ff77db3de5070c1f6c0fb85#079665882507dd5e2ff77db3de5070c1f6c0fb85" +dependencies = [ + "bitflags", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.1" +source = "git+https://github.com/servo/core-foundation-rs?rev=079665882507dd5e2ff77db3de5070c1f6c0fb85#079665882507dd5e2ff77db3de5070c1f6c0fb85" +dependencies = [ + "bitflags", + "block", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "collab" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "async-tungstenite", + "axum", + "axum-extra", + "base64", + "clap 3.2.22", + "client", + "collections", + "ctor", + "editor", + "env_logger", + "envy", + "futures 0.3.24", + "git", + "gpui", + "hyper", + "language", + "lazy_static", + "lipsum", + "log", + "lsp", + "nanoid", + "parking_lot 0.11.2", + "project", + "prometheus", + "rand 0.8.5", + "reqwest", + "rpc", + "scrypt", + "serde", + "serde_json", + "settings", + "sha-1 0.9.8", + "sqlx", + "theme", + "time 0.3.15", + "tokio", + "tokio-tungstenite", + "toml", + "tonic", + "tower", + "tracing", + "tracing-log", + "tracing-subscriber", + "unindent", + "util", + "workspace", +] + +[[package]] +name = "collections" +version = "0.1.0" +dependencies = [ + "seahash", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "command_palette" +version = "0.1.0" +dependencies = [ + "collections", + "ctor", + "editor", + "env_logger", + "fuzzy", + "gpui", + "picker", + "project", + "serde_json", + "settings", + "theme", + "util", + "workspace", +] + +[[package]] +name = "concurrent-queue" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" +dependencies = [ + "cache-padded", +] + +[[package]] +name = "contacts_panel" +version = "0.1.0" +dependencies = [ + "anyhow", + "client", + "collections", + "editor", + "futures 0.3.24", + "fuzzy", + "gpui", + "language", + "log", + "menu", + "picker", + "postage", + "project", + "serde", + "settings", + "theme", + "util", + "workspace", +] + +[[package]] +name = "contacts_status_item" +version = "0.1.0" +dependencies = [ + "anyhow", + "client", + "collections", + "editor", + "futures 0.3.24", + "fuzzy", + "gpui", + "language", + "log", + "menu", + "picker", + "postage", + "project", + "serde", + "settings", + "theme", + "util", + "workspace", +] + +[[package]] +name = "context_menu" +version = "0.1.0" +dependencies = [ + "gpui", + "menu", + "settings", + "smallvec", + "theme", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "git+https://github.com/servo/core-foundation-rs?rev=079665882507dd5e2ff77db3de5070c1f6c0fb85#079665882507dd5e2ff77db3de5070c1f6c0fb85" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "git+https://github.com/servo/core-foundation-rs?rev=079665882507dd5e2ff77db3de5070c1f6c0fb85#079665882507dd5e2ff77db3de5070c1f6c0fb85" + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "git+https://github.com/servo/core-foundation-rs?rev=079665882507dd5e2ff77db3de5070c1f6c0fb85#079665882507dd5e2ff77db3de5070c1f6c0fb85" +dependencies = [ + "bitflags", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.1" +source = "git+https://github.com/servo/core-foundation-rs?rev=079665882507dd5e2ff77db3de5070c1f6c0fb85#079665882507dd5e2ff77db3de5070c1f6c0fb85" +dependencies = [ + "bitflags", + "core-foundation", + "foreign-types", + "libc", +] + +[[package]] +name = "core-services" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b344b958cae90858bf6086f49599ecc5ec8698eacad0ea155509ba11fab347" +dependencies = [ + "core-foundation", +] + +[[package]] +name = "core-text" +version = "19.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types", + "libc", +] + +[[package]] +name = "cpp_demangle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "cranelift-bforest" +version = "0.85.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "749d0d6022c9038dccf480bdde2a38d435937335bf2bb0f14e815d94517cdce8" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.85.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94370cc7b37bf652ccd8bb8f09bd900997f7ccf97520edfc75554bb5c4abbea" +dependencies = [ + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-entity", + "cranelift-isle", + "gimli", + "log", + "regalloc2", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.85.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a3cea8fdab90e44018c5b9a1dfd460d8ee265ac354337150222a354628bdb6" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.85.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ac72f76f2698598951ab26d8c96eaa854810e693e7dd52523958b5909fde6b2" + +[[package]] +name = "cranelift-entity" +version = "0.85.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09eaeacfcd2356fe0e66b295e8f9d59fdd1ac3ace53ba50de14d628ec902f72d" +dependencies = [ + "serde", +] + +[[package]] +name = "cranelift-frontend" +version = "0.85.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dba69c9980d5ffd62c18a2bde927855fcd7c8dc92f29feaf8636052662cbd99c" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.85.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2920dc1e05cac40304456ed3301fde2c09bd6a9b0210bcfa2f101398d628d5b" + +[[package]] +name = "cranelift-native" +version = "0.85.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04dfa45f9b2a6f587c564d6b63388e00cd6589d2df6ea2758cf79e1a13285e6" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon", +] + +[[package]] +name = "cranelift-wasm" +version = "0.85.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31a46513ae6f26f3f267d8d75b5373d555fbbd1e68681f348d99df43f747ec54" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "itertools", + "log", + "smallvec", + "wasmparser", + "wasmtime-types", +] + +[[package]] +name = "crc" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d0165d2900ae6778e36e80bbc4da3b5eefccee9ba939761f9c2882a5d9af3ff" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "crossbeam-channel" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" +dependencies = [ + "crossbeam-utils 0.7.2", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils 0.8.12", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils 0.8.12", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" +dependencies = [ + "autocfg 1.1.0", + "cfg-if 1.0.0", + "crossbeam-utils 0.8.12", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd42583b04998a5363558e5f9291ee5a5ff6b49944332103f251e7479a82aa7" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils 0.8.12", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg 1.1.0", + "cfg-if 0.1.10", + "lazy_static", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "ctor" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "curl" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22" +dependencies = [ + "curl-sys", + "libc", + "openssl-probe", + "openssl-sys", + "schannel", + "socket2", + "winapi 0.3.9", +] + +[[package]] +name = "curl-sys" +version = "0.4.56+curl-7.83.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6093e169dd4de29e468fa649fbae11cdcd5551c81fe5bf1b0677adad7ef3d26f" +dependencies = [ + "cc", + "libc", + "libnghttp2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", + "winapi 0.3.9", +] + +[[package]] +name = "cxx" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f39818dcfc97d45b03953c1292efc4e80954e1583c4aa770bac1383e2310a4" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e580d70777c116df50c390d1211993f62d40302881e54d4b79727acb83d0199" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56a46460b88d1cec95112c8c363f0e2c39afdb237f60583b0b36343bf627ea9c" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747b608fecf06b0d72d440f27acc99288207324b793be2c17991839f3d4995ea" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "data-url" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30bfce702bcfa94e906ef82421f2c0e61c076ad76030c16ee5d2e9a32fe193" +dependencies = [ + "matches", +] + +[[package]] +name = "db" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "collections", + "gpui", + "parking_lot 0.11.2", + "rocksdb", + "tempdir", +] + +[[package]] +name = "deflate" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" +dependencies = [ + "adler32", + "byteorder", +] + +[[package]] +name = "dhat" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0684eaa19a59be283a6f99369917b679bd4d1d06604b2eb2e2f87b4bbd67668d" +dependencies = [ + "backtrace", + "lazy_static", + "parking_lot 0.12.1", + "rustc-hash", + "serde", + "serde_json", + "thousands", +] + +[[package]] +name = "diagnostics" +version = "0.1.0" +dependencies = [ + "anyhow", + "client", + "collections", + "editor", + "gpui", + "language", + "postage", + "project", + "serde_json", + "settings", + "smallvec", + "theme", + "unindent", + "util", + "workspace", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +dependencies = [ + "block-buffer 0.10.3", + "crypto-common", + "subtle", +] + +[[package]] +name = "directories-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +dependencies = [ + "cfg-if 1.0.0", + "dirs-sys-next", +] + +[[package]] +name = "dirs" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if 1.0.0", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi 0.3.9", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi 0.3.9", +] + +[[package]] +name = "dotenvy" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9155c8f4dc55c7470ae9da3f63c6785245093b3f6aeb0f5bf2e968efbba314" +dependencies = [ + "dirs 4.0.0", +] + +[[package]] +name = "drag_and_drop" +version = "0.1.0" +dependencies = [ + "collections", + "gpui", +] + +[[package]] +name = "dwrote" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" +dependencies = [ + "lazy_static", + "libc", + "winapi 0.3.9", + "wio", +] + +[[package]] +name = "dyn-clone" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2" + +[[package]] +name = "easy-parallel" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6907e25393cdcc1f4f3f513d9aac1e840eb1cc341a0fccb01171f7d14d10b946" + +[[package]] +name = "editor" +version = "0.1.0" +dependencies = [ + "aho-corasick", + "anyhow", + "clock", + "collections", + "context_menu", + "ctor", + "env_logger", + "futures 0.3.24", + "fuzzy", + "git", + "gpui", + "indoc", + "itertools", + "language", + "lazy_static", + "log", + "lsp", + "ordered-float", + "parking_lot 0.11.2", + "postage", + "project", + "rand 0.8.5", + "rpc", + "serde", + "settings", + "smallvec", + "smol", + "snippet", + "sum_tree", + "text", + "theme", + "tree-sitter", + "tree-sitter-html", + "tree-sitter-javascript", + "tree-sitter-rust", + "unindent", + "util", + "workspace", +] + +[[package]] +name = "either" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" + +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "env_logger" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "envy" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f47e0157f2cb54f5ae1bd371b30a2ae4311e1c028f575cd4e81de7353215965" +dependencies = [ + "serde", +] + +[[package]] +name = "erased-serde" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54558e0ba96fbe24280072642eceb9d7d442e32c7ec0ea9e7ecd7b4ea2cf4e11" +dependencies = [ + "serde", +] + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "etagere" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6301151a318f367f392c31395beb1cfba5ccd9abc44d1db0db3a4b27b9601c89" +dependencies = [ + "euclid", + "svg_fmt", +] + +[[package]] +name = "euclid" +version = "0.22.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b52c2ef4a78da0ba68fbe1fd920627411096d2ac478f7f4c9f3a54ba6705bade" +dependencies = [ + "num-traits", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "expat-sys" +version = "2.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658f19728920138342f68408b7cf7644d90d4784353d8ebc32e7e8663dbe45fa" +dependencies = [ + "cmake", + "pkg-config", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + +[[package]] +name = "file-per-thread-logger" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e16290574b39ee41c71aeb90ae960c504ebaf1e2a1c87bd52aa56ed6e1a02f" +dependencies = [ + "env_logger", + "log", +] + +[[package]] +name = "file_finder" +version = "0.1.0" +dependencies = [ + "ctor", + "editor", + "env_logger", + "fuzzy", + "gpui", + "menu", + "picker", + "postage", + "project", + "serde_json", + "settings", + "theme", + "util", + "workspace", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +dependencies = [ + "crc32fast", + "miniz_oxide 0.5.4", +] + +[[package]] +name = "float-cmp" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75224bec9bfe1a65e2d34132933f2de7fe79900c96a0174307554244ece8150e" + +[[package]] +name = "float-ord" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bad48618fdb549078c333a7a8528acb57af271d0433bdecd523eb620628364e" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "font-kit" +version = "0.10.0" +source = "git+https://github.com/zed-industries/font-kit?rev=8eaf7a918eafa28b0a37dc759e2e0e7683fa24f1#8eaf7a918eafa28b0a37dc759e2e0e7683fa24f1" +dependencies = [ + "bitflags", + "byteorder", + "core-foundation", + "core-graphics", + "core-text", + "dirs-next", + "dwrote", + "float-ord", + "freetype", + "lazy_static", + "libc", + "log", + "pathfinder_geometry", + "pathfinder_simd", + "servo-fontconfig", + "walkdir", + "winapi 0.3.9", +] + +[[package]] +name = "fontdb" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e58903f4f8d5b58c7d300908e4ebe5289c1bfdf5587964330f12023b8ff17fd1" +dependencies = [ + "log", + "memmap2", + "ttf-parser 0.12.3", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "freetype" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee38378a9e3db1cc693b4f88d166ae375338a0ff75cb8263e1c601d51f35dc6" +dependencies = [ + "freetype-sys", + "libc", +] + +[[package]] +name = "freetype-sys" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" +dependencies = [ + "cmake", + "libc", + "pkg-config", +] + +[[package]] +name = "fs-set-times" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df62ee66ee2d532ea8d567b5a3f0d03ecd64636b98bad5be1e93dcc918b92aa" +dependencies = [ + "io-lifetimes", + "rustix", + "winapi 0.3.9", +] + +[[package]] +name = "fsevent" +version = "2.0.2" +dependencies = [ + "bitflags", + "fsevent-sys", + "parking_lot 0.11.2", + "tempdir", +] + +[[package]] +name = "fsevent-sys" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6f5e6817058771c10f0eb0f05ddf1e35844266f972004fe8e4b21fda295bd5" +dependencies = [ + "libc", +] + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "futures" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" + +[[package]] +name = "futures" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" + +[[package]] +name = "futures-executor" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-intrusive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62007592ac46aa7c2b6416f7deb9a8a8f63a01e0f1d6e1787d5630170db2b63e" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot 0.11.2", +] + +[[package]] +name = "futures-io" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" + +[[package]] +name = "futures-lite" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite 0.2.9", + "waker-fn", +] + +[[package]] +name = "futures-macro" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" + +[[package]] +name = "futures-task" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" + +[[package]] +name = "futures-util" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +dependencies = [ + "futures 0.1.31", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite 0.2.9", + "pin-utils", + "slab", + "tokio-io", +] + +[[package]] +name = "fuzzy" +version = "0.1.0" +dependencies = [ + "gpui", + "util", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gif" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + +[[package]] +name = "git" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "clock", + "collections", + "futures 0.3.24", + "git2", + "lazy_static", + "log", + "parking_lot 0.11.2", + "smol", + "sum_tree", + "text", + "unindent", + "util", +] + +[[package]] +name = "git2" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2994bee4a3a6a51eb90c218523be382fd7ea09b16380b9312e9dbe955ff7c7d1" +dependencies = [ + "bitflags", + "libc", + "libgit2-sys", + "log", + "url", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "globset" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "go_to_line" +version = "0.1.0" +dependencies = [ + "editor", + "gpui", + "menu", + "postage", + "settings", + "text", + "workspace", +] + +[[package]] +name = "gpui" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-task", + "backtrace", + "bindgen", + "block", + "cc", + "cocoa", + "collections", + "core-foundation", + "core-graphics", + "core-text", + "ctor", + "dhat", + "env_logger", + "etagere", + "font-kit", + "foreign-types", + "futures 0.3.24", + "gpui_macros", + "image", + "lazy_static", + "log", + "media", + "metal", + "num_cpus", + "objc", + "ordered-float", + "parking", + "parking_lot 0.11.2", + "pathfinder_color", + "pathfinder_geometry", + "png", + "postage", + "rand 0.8.5", + "resvg", + "seahash", + "serde", + "serde_json", + "simplelog", + "smallvec", + "smol", + "sum_tree", + "time 0.3.15", + "tiny-skia", + "tree-sitter", + "usvg", + "util", + "waker-fn", +] + +[[package]] +name = "gpui_macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "h2" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +dependencies = [ + "bytes 1.2.1", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util 0.7.4", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashlink" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa" +dependencies = [ + "hashbrown 0.12.3", +] + +[[package]] +name = "headers" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" +dependencies = [ + "base64", + "bitflags", + "bytes 1.2.1", + "headers-core", + "http", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac 0.12.1", +] + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.5", +] + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes 1.2.1", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes 1.2.1", + "http", + "pin-project-lite 0.2.9", +] + +[[package]] +name = "http-range-header" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +dependencies = [ + "bytes 1.2.1", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite 0.2.9", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite 0.2.9", + "tokio", + "tokio-io-timeout", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes 1.2.1", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5a6ef98976b22b3b7f2f3a806f858cb862044cfa66805aa3ad84cb3d3b785ed" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi 0.3.9", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fde6edd6cef363e9359ed3c98ba64590ba9eecba2293eb5a723ab32aee8926aa" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "ignore" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" +dependencies = [ + "crossbeam-utils 0.8.12", + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + +[[package]] +name = "image" +version = "0.23.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "gif", + "jpeg-decoder", + "num-iter", + "num-rational", + "num-traits", + "png", + "scoped_threadpool", + "tiff", +] + +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg 1.1.0", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indoc" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3" + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "io-extras" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0c937cc9891c12eaa8c63ad347e4a288364b1328b924886970b47a14ab8f8f8" +dependencies = [ + "io-lifetimes", + "winapi 0.3.9", +] + +[[package]] +name = "io-lifetimes" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec58677acfea8a15352d42fc87d11d63596ade9239e0a7c9352914417515dbe6" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "ipc-channel" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cb1d9211085f0ea6f1379d944b93c4d07e8207aa3bcf49f37eda12b85081887" +dependencies = [ + "bincode", + "crossbeam-channel 0.4.4", + "fnv", + "lazy_static", + "libc", + "mio 0.6.23", + "rand 0.7.3", + "serde", + "tempfile", + "uuid 0.8.2", + "winapi 0.3.9", +] + +[[package]] +name = "ipnet" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" + +[[package]] +name = "is-terminal" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c89a757e762896bdbdfadf2860d0f8b0cea5e363d8cf3e7bdfeb63d1d976352" +dependencies = [ + "hermit-abi 0.2.6", + "io-lifetimes", + "rustix", + "winapi 0.3.9", +] + +[[package]] +name = "isahc" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9" +dependencies = [ + "async-channel", + "castaway", + "crossbeam-utils 0.8.12", + "curl", + "curl-sys", + "encoding_rs", + "event-listener", + "futures-lite", + "http", + "log", + "mime", + "once_cell", + "polling", + "slab", + "sluice", + "tracing", + "tracing-futures", + "url", + "waker-fn", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" + +[[package]] +name = "ittapi-rs" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f712648a1ad72fbfb7adc2772c331e8d90f022f8cf30cbabefba2878dd3172b0" +dependencies = [ + "cc", +] + +[[package]] +name = "jobserver" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +dependencies = [ + "libc", +] + +[[package]] +name = "journal" +version = "0.1.0" +dependencies = [ + "chrono", + "dirs 4.0.0", + "editor", + "gpui", + "log", + "util", + "workspace", +] + +[[package]] +name = "jpeg-decoder" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" +dependencies = [ + "rayon", +] + +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "json_comments" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ee439ee368ba4a77ac70d04f14015415af8600d6c894dc1f11bd79758c57d5" + +[[package]] +name = "jwt" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6204285f77fe7d9784db3fdc449ecce1a0114927a51d5a41c4c7a292011c015f" +dependencies = [ + "base64", + "crypto-common", + "digest 0.10.5", + "hmac 0.12.1", + "serde", + "serde_json", + "sha2 0.10.6", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "kurbo" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a53776d271cfb873b17c618af0298445c88afc52837f3e948fa3fafd131f449" +dependencies = [ + "arrayvec 0.7.2", +] + +[[package]] +name = "language" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-broadcast", + "async-trait", + "client", + "clock", + "collections", + "ctor", + "env_logger", + "futures 0.3.24", + "fuzzy", + "git", + "gpui", + "lazy_static", + "log", + "lsp", + "parking_lot 0.11.2", + "postage", + "rand 0.8.5", + "regex", + "rpc", + "serde", + "serde_json", + "settings", + "similar", + "smallvec", + "smol", + "sum_tree", + "text", + "theme", + "tree-sitter", + "tree-sitter-html", + "tree-sitter-javascript", + "tree-sitter-json 0.19.0", + "tree-sitter-python", + "tree-sitter-rust", + "tree-sitter-typescript", + "unindent", + "util", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.134" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" + +[[package]] +name = "libgit2-sys" +version = "0.14.0+1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47a00859c70c8a4f7218e6d1cc32875c4b55f6799445b842b0d8ed5e4c3d959b" +dependencies = [ + "cc", + "libc", + "libz-sys", + "pkg-config", +] + +[[package]] +name = "libloading" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" +dependencies = [ + "cfg-if 1.0.0", + "winapi 0.3.9", +] + +[[package]] +name = "libm" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "292a948cd991e376cf75541fe5b97a1081d713c618b4f1b9500f8844e49eb565" + +[[package]] +name = "libnghttp2-sys" +version = "0.1.7+1.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ed28aba195b38d5ff02b9170cbff627e336a20925e43b4945390401c5dc93f" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "librocksdb-sys" +version = "0.7.1+7.3.1" +source = "git+https://github.com/rust-rocksdb/rust-rocksdb?rev=39dc822dde743b2a26eb160b660e8fbdab079d49#39dc822dde743b2a26eb160b660e8fbdab079d49" +dependencies = [ + "bindgen", + "bzip2-sys", + "cc", + "glob", + "libc", + "libz-sys", + "zstd-sys", +] + +[[package]] +name = "libz-sys" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "line-wrap" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" +dependencies = [ + "safemem", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +dependencies = [ + "cc", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5284f00d480e1c39af34e72f8ad60b94f47007e3481cd3b731c1d67190ddc7b7" + +[[package]] +name = "lipsum" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8451846f1f337e44486666989fbce40be804da139d5a4477d6b88ece5dc69f4" +dependencies = [ + "rand 0.8.5", + "rand_chacha 0.3.1", +] + +[[package]] +name = "live_kit" +version = "0.1.0" +dependencies = [ + "anyhow", + "core-foundation", + "core-graphics", + "futures 0.3.24", + "media", + "parking_lot 0.11.2", + "serde", + "serde_json", +] + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg 1.1.0", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if 1.0.0", + "serde", + "value-bag", +] + +[[package]] +name = "lsp" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-pipe", + "collections", + "ctor", + "env_logger", + "futures 0.3.24", + "gpui", + "log", + "lsp-types", + "parking_lot 0.11.2", + "postage", + "serde", + "serde_json", + "smol", + "unindent", + "util", +] + +[[package]] +name = "lsp-types" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2368312c59425dd133cb9a327afee65be0a633a8ce471d248e2202a48f8f68ae" +dependencies = [ + "bitflags", + "serde", + "serde_json", + "serde_repr", + "url", +] + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "matchit" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" + +[[package]] +name = "maybe-owned" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + +[[package]] +name = "md-5" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest 0.10.5", +] + +[[package]] +name = "media" +version = "0.1.0" +dependencies = [ + "anyhow", + "bindgen", + "block", + "bytes 1.2.1", + "core-foundation", + "foreign-types", + "metal", + "objc", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memfd" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6627dc657574b49d6ad27105ed671822be56e0d2547d413bfbf3e8d8fa92e7a" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg 1.1.0", +] + +[[package]] +name = "menu" +version = "0.1.0" +dependencies = [ + "gpui", +] + +[[package]] +name = "metal" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4598d719460ade24c7d91f335daf055bf2a7eec030728ce751814c50cdd6a26c" +dependencies = [ + "bitflags", + "block", + "cocoa-foundation", + "foreign-types", + "log", + "objc", +] + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg 1.1.0", +] + +[[package]] +name = "miniz_oxide" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +dependencies = [ + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow 0.2.2", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", +] + +[[package]] +name = "mio-anonymous-pipes" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bc513025fe5005a3aa561b50fdb2cda5a150b84800ae02acd8aa9ed62ca1a6b" +dependencies = [ + "mio 0.6.23", + "miow 0.3.7", + "parking_lot 0.11.2", + "spsc-buffer", + "winapi 0.3.9", +] + +[[package]] +name = "mio-extras" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" +dependencies = [ + "lazycell", + "log", + "mio 0.6.23", + "slab", +] + +[[package]] +name = "mio-uds" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" +dependencies = [ + "iovec", + "libc", + "mio 0.6.23", +] + +[[package]] +name = "miow" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "more-asserts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" + +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "nanoid" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ffa00dec017b5b1a8b7cf5e2c008bfda1aa7e0697ac1508b491fdf2622fb4d8" +dependencies = [ + "rand 0.8.5", +] + +[[package]] +name = "native-tls" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "net2" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "nix" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "libc", + "memoffset", +] + +[[package]] +name = "nom" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "ntapi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi 0.3.9", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg 1.1.0", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4547ee5541c18742396ae2c895d0717d0f886d8823b8399cdaf7b07d63ad0480" +dependencies = [ + "autocfg 0.1.8", + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg 1.1.0", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg 1.1.0", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg 1.1.0", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg 1.1.0", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi 0.1.19", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + +[[package]] +name = "nvim-rs" +version = "0.5.0" +source = "git+https://github.com/KillTheMule/nvim-rs?branch=master#d701c2790dcb2579f8f4d7003ba30e2100a7d25b" +dependencies = [ + "async-trait", + "futures 0.3.24", + "log", + "parity-tokio-ipc", + "rmp", + "rmpv", + "tokio", + "tokio-util 0.7.4", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[package]] +name = "object" +version = "0.28.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" +dependencies = [ + "crc32fast", + "hashbrown 0.11.2", + "indexmap", + "memchr", +] + +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" +dependencies = [ + "autocfg 1.1.0", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ordered-float" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87" +dependencies = [ + "num-traits", +] + +[[package]] +name = "os_str_bytes" +version = "6.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" + +[[package]] +name = "outline" +version = "0.1.0" +dependencies = [ + "editor", + "fuzzy", + "gpui", + "language", + "ordered-float", + "picker", + "postage", + "settings", + "smol", + "text", + "workspace", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parity-tokio-ipc" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9981e32fb75e004cc148f5fb70342f393830e0a4aa62e3cc93b50976218d42b6" +dependencies = [ + "futures 0.3.24", + "libc", + "log", + "rand 0.7.3", + "tokio", + "winapi 0.3.9", +] + +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.5", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.3", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi 0.3.9", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "password-hash" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e0b28ace46c5a396546bcf443bf422b57049617433d8854227352a4a9b24e7" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "paste" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" + +[[package]] +name = "pathfinder_color" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69bdc0d277d559e35e1b374de56df9262a6b71e091ca04a8831a239f8c7f0c62" +dependencies = [ + "pathfinder_simd", +] + +[[package]] +name = "pathfinder_geometry" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" +dependencies = [ + "log", + "pathfinder_simd", +] + +[[package]] +name = "pathfinder_simd" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39fe46acc5503595e5949c17b818714d26fdf9b4920eacf3b2947f0199f4a6ff" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "pbkdf2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +dependencies = [ + "crypto-mac", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "pem" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" +dependencies = [ + "base64", + "once_cell", + "regex", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pest" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc7bc69c062e492337d74d59b120c274fd3d261b6bf6d3207d499b4b379c41a" +dependencies = [ + "thiserror", + "ucd-trie", +] + +[[package]] +name = "petgraph" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "picker" +version = "0.1.0" +dependencies = [ + "ctor", + "editor", + "env_logger", + "gpui", + "menu", + "serde_json", + "settings", + "theme", + "util", + "workspace", +] + +[[package]] +name = "pico-args" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" + +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" + +[[package]] +name = "plist" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd39bc6cdc9355ad1dc5eeedefee696bb35c34caf21768741e81826c0bbd7225" +dependencies = [ + "base64", + "indexmap", + "line-wrap", + "serde", + "time 0.3.15", + "xml-rs", +] + +[[package]] +name = "plugin" +version = "0.1.0" +dependencies = [ + "bincode", + "plugin_macros", + "serde", +] + +[[package]] +name = "plugin_macros" +version = "0.1.0" +dependencies = [ + "bincode", + "proc-macro2", + "quote", + "serde", + "syn", +] + +[[package]] +name = "plugin_runtime" +version = "0.1.0" +dependencies = [ + "anyhow", + "bincode", + "pollster", + "serde", + "serde_json", + "smol", + "wasi-common", + "wasmtime", + "wasmtime-wasi", +] + +[[package]] +name = "png" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" +dependencies = [ + "bitflags", + "crc32fast", + "deflate", + "miniz_oxide 0.3.7", +] + +[[package]] +name = "polling" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899b00b9c8ab553c743b3e11e87c5c7d423b2a2de229ba95b24a756344748011" +dependencies = [ + "autocfg 1.1.0", + "cfg-if 1.0.0", + "libc", + "log", + "wepoll-ffi", + "winapi 0.3.9", +] + +[[package]] +name = "pollster" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7" + +[[package]] +name = "postage" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a63d25391d04a097954b76aba742b6b5b74f213dfe3dbaeeb36e8ddc1c657f0b" +dependencies = [ + "atomic", + "crossbeam-queue", + "futures 0.3.24", + "log", + "pin-project", + "pollster", + "static_assertions", + "thiserror", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "procinfo" +version = "0.1.0" +source = "git+https://github.com/zed-industries/wezterm?rev=5cd757e5f2eb039ed0c6bb6512223e69d5efc64d#5cd757e5f2eb039ed0c6bb6512223e69d5efc64d" +dependencies = [ + "libc", + "log", + "ntapi", + "winapi 0.3.9", +] + +[[package]] +name = "project" +version = "0.1.0" +dependencies = [ + "aho-corasick", + "anyhow", + "async-trait", + "client", + "clock", + "collections", + "db", + "fsevent", + "futures 0.3.24", + "fuzzy", + "git", + "gpui", + "ignore", + "language", + "lazy_static", + "libc", + "log", + "lsp", + "parking_lot 0.11.2", + "postage", + "pulldown-cmark", + "rand 0.8.5", + "regex", + "rocksdb", + "rpc", + "serde", + "serde_json", + "settings", + "sha2 0.10.6", + "similar", + "smol", + "sum_tree", + "tempdir", + "text", + "thiserror", + "toml", + "unindent", + "util", +] + +[[package]] +name = "project_panel" +version = "0.1.0" +dependencies = [ + "context_menu", + "editor", + "futures 0.3.24", + "gpui", + "menu", + "postage", + "project", + "serde_json", + "settings", + "theme", + "unicase", + "util", + "workspace", +] + +[[package]] +name = "project_symbols" +version = "0.1.0" +dependencies = [ + "anyhow", + "editor", + "futures 0.3.24", + "fuzzy", + "gpui", + "language", + "lsp", + "ordered-float", + "picker", + "postage", + "project", + "settings", + "smol", + "text", + "util", + "workspace", +] + +[[package]] +name = "prometheus" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45c8babc29389186697fe5a2a4859d697825496b83db5d0b65271cdc0488e88c" +dependencies = [ + "cfg-if 1.0.0", + "fnv", + "lazy_static", + "memchr", + "parking_lot 0.12.1", + "protobuf", + "thiserror", +] + +[[package]] +name = "prost" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020" +dependencies = [ + "bytes 1.2.1", + "prost-derive 0.8.0", +] + +[[package]] +name = "prost" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" +dependencies = [ + "bytes 1.2.1", + "prost-derive 0.9.0", +] + +[[package]] +name = "prost-build" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62941722fb675d463659e49c4f3fe1fe792ff24fe5bbaa9c08cd3b98a1c354f5" +dependencies = [ + "bytes 1.2.1", + "heck 0.3.3", + "itertools", + "lazy_static", + "log", + "multimap", + "petgraph", + "prost 0.9.0", + "prost-types", + "regex", + "tempfile", + "which", +] + +[[package]] +name = "prost-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "600d2f334aa05acb02a755e217ef1ab6dea4d51b58b7846588b747edec04efba" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a" +dependencies = [ + "bytes 1.2.1", + "prost 0.9.0", +] + +[[package]] +name = "protobuf" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" + +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + +[[package]] +name = "pulldown-cmark" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" +dependencies = [ + "bitflags", + "memchr", + "unicase", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi 0.3.9", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.7", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rayon" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +dependencies = [ + "autocfg 1.1.0", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +dependencies = [ + "crossbeam-channel 0.5.6", + "crossbeam-deque", + "crossbeam-utils 0.8.12", + "num_cpus", +] + +[[package]] +name = "rctree" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be9e29cb19c8fe84169fcb07f8f11e66bc9e6e0280efd4715c54818296f8a4a8" + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom 0.2.7", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "regalloc2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a8d23b35d7177df3b9d31ed8a9ab4bf625c668be77a319d4f5efd4a5257701c" +dependencies = [ + "fxhash", + "log", + "slice-group-by", + "smallvec", +] + +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + +[[package]] +name = "region" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877e54ea2adcd70d80e9179344c97f93ef0dffd6b03e1f4529e6e83ab2fa9ae0" +dependencies = [ + "bitflags", + "libc", + "mach", + "winapi 0.3.9", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "reqwest" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +dependencies = [ + "base64", + "bytes 1.2.1", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite 0.2.9", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "resvg" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09697862c5c3f940cbaffef91969c62188b5c8ed385b0aef43a5ff01ddc8000f" +dependencies = [ + "jpeg-decoder", + "log", + "pico-args", + "png", + "rgb", + "svgfilters", + "tiny-skia", + "usvg", +] + +[[package]] +name = "rgb" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3603b7d71ca82644f79b5a06d1220e9a58ede60bd32255f698cb1af8838b8db3" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi 0.3.9", +] + +[[package]] +name = "rmp" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44519172358fd6d58656c86ab8e7fbc9e1490c3e8f14d35ed78ca0dd07403c9f" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmpv" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de8813b3a2f95c5138fe5925bfb8784175d88d6bff059ba8ce090aa891319754" +dependencies = [ + "num-traits", + "rmp", +] + +[[package]] +name = "rocksdb" +version = "0.18.0" +source = "git+https://github.com/rust-rocksdb/rust-rocksdb?rev=39dc822dde743b2a26eb160b660e8fbdab079d49#39dc822dde743b2a26eb160b660e8fbdab079d49" +dependencies = [ + "libc", + "librocksdb-sys", +] + +[[package]] +name = "roxmltree" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "rpc" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-lock", + "async-tungstenite", + "base64", + "clock", + "collections", + "ctor", + "env_logger", + "futures 0.3.24", + "gpui", + "parking_lot 0.11.2", + "prost 0.8.0", + "prost-build", + "rand 0.8.5", + "rsa", + "serde", + "smol", + "smol-timeout", + "tempdir", + "tracing", + "util", + "zstd", +] + +[[package]] +name = "rsa" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0aeddcca1082112a6eeb43bf25fd7820b066aaf6eaef776e19d0a1febe38fe" +dependencies = [ + "byteorder", + "digest 0.9.0", + "lazy_static", + "num-bigint-dig", + "num-integer", + "num-iter", + "num-traits", + "pem", + "rand 0.8.5", + "simple_asn1", + "subtle", + "zeroize", +] + +[[package]] +name = "rust-embed" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e26934cd67a1da1165efe61cba4047cc1b4a526019da609fcce13a1000afb5fa" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "6.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35d7b402e273544cc08e0824aa3404333fab8a90ac43589d3d5b72f4b346e12" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "7.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1669d81dfabd1b5f8e2856b8bbe146c6192b0ba22162edc738ac0a5de18f054" +dependencies = [ + "globset", + "sha2 0.10.6", + "walkdir", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.33.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938a344304321a9da4973b9ff4f9f8db9caf4597dfd9dda6a60b523340a0fff0" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "itoa", + "libc", + "linux-raw-sys", + "once_cell", + "winapi 0.3.9", +] + +[[package]] +name = "rustls" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +dependencies = [ + "base64", + "log", + "ring", + "sct 0.6.1", + "webpki 0.21.4", +] + +[[package]] +name = "rustls" +version = "0.20.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" +dependencies = [ + "log", + "ring", + "sct 0.7.0", + "webpki 0.22.0", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +dependencies = [ + "base64", +] + +[[package]] +name = "rustybuzz" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab463a295d00f3692e0974a0bfd83c7a9bcd119e27e07c2beecdb1b44a09d10" +dependencies = [ + "bitflags", + "bytemuck", + "smallvec", + "ttf-parser 0.9.0", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-general-category", + "unicode-script", +] + +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + +[[package]] +name = "safe_arch" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "salsa20" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecbd2eb639fd7cab5804a0837fe373cc2172d15437e804c054a9fb885cb923b0" +dependencies = [ + "cipher", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +dependencies = [ + "lazy_static", + "windows-sys", +] + +[[package]] +name = "schemars" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn", +] + +[[package]] +name = "scoped_threadpool" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "scratch" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" + +[[package]] +name = "scrypt" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879588d8f90906e73302547e20fffefdd240eb3e0e744e142321f5d49dea0518" +dependencies = [ + "base64ct", + "hmac 0.11.0", + "password-hash", + "pbkdf2", + "salsa20", + "sha2 0.9.9", +] + +[[package]] +name = "sct" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "search" +version = "0.1.0" +dependencies = [ + "anyhow", + "collections", + "editor", + "gpui", + "language", + "log", + "menu", + "postage", + "project", + "serde", + "serde_json", + "settings", + "smallvec", + "theme", + "unindent", + "util", + "workspace", +] + +[[package]] +name = "security-framework" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + +[[package]] +name = "seq-macro" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9f47faea3cad316faa914d013d24f471cd90bfca1a0c70f05a3f42c6441e99" + +[[package]] +name = "serde" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_fmt" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2963a69a2b3918c1dc75a45a18bd3fcd1120e31d3f59deb1b2f9b5d5ffb8baa4" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_json" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "184c643044780f7ceb59104cef98a5a6f12cb2288a7bc701ab93a362b49fd47d" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "servo-fontconfig" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e3e22fe5fd73d04ebf0daa049d3efe3eae55369ce38ab16d07ddd9ac5c217c" +dependencies = [ + "libc", + "servo-fontconfig-sys", +] + +[[package]] +name = "servo-fontconfig-sys" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36b879db9892dfa40f95da1c38a835d41634b825fbd8c4c418093d53c24b388" +dependencies = [ + "expat-sys", + "freetype-sys", + "pkg-config", +] + +[[package]] +name = "settings" +version = "0.1.0" +dependencies = [ + "anyhow", + "assets", + "collections", + "gpui", + "json_comments", + "schemars", + "serde", + "serde_json", + "serde_path_to_error", + "theme", + "toml", + "util", +] + +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.5", +] + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.5", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.5", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shellexpand" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4" +dependencies = [ + "dirs 4.0.0", +] + +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + +[[package]] +name = "signal-hook" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio 0.6.23", + "mio-uds", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "similar" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad1d488a557b235fc46dae55512ffbfc429d2482b08b4d9435ab07384ca8aec" + +[[package]] +name = "simple_asn1" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb4ea60fb301dc81dfc113df680571045d375ab7345d171c5dc7d7e13107a80" +dependencies = [ + "chrono", + "num-bigint", + "num-traits", + "thiserror", +] + +[[package]] +name = "simplecss" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d" +dependencies = [ + "log", +] + +[[package]] +name = "simplelog" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bc0ffd69814a9b251d43afcabf96dad1b29f5028378056257be9e3fecc9f720" +dependencies = [ + "chrono", + "log", + "termcolor", +] + +[[package]] +name = "siphasher" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg 1.1.0", +] + +[[package]] +name = "slice-group-by" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" + +[[package]] +name = "sluice" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" +dependencies = [ + "async-channel", + "futures-core", + "futures-io", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "smol" +version = "1.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cf3b5351f3e783c1d79ab5fc604eeed8b8ae9abd36b166e8b87a089efd85e4" +dependencies = [ + "async-channel", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-net", + "async-process", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "smol-timeout" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "847d777e2c6c166bad26264479e80a9820f3d364fcb4a0e23cd57bbfa8e94961" +dependencies = [ + "async-io", + "pin-project-lite 0.1.12", +] + +[[package]] +name = "snippet" +version = "0.1.0" +dependencies = [ + "anyhow", + "smallvec", +] + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spsc-buffer" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be6c3f39c37a4283ee4b43d1311c828f2e1fb0541e76ea0cb1a2abd9ef2f5b3b" + +[[package]] +name = "sqlformat" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87e292b4291f154971a43c3774364e2cbcaec599d3f5bf6fa9d122885dbc38a" +dependencies = [ + "itertools", + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9249290c05928352f71c077cc44a464d880c63f26f7534728cca008e135c0428" +dependencies = [ + "sqlx-core", + "sqlx-macros", +] + +[[package]] +name = "sqlx-core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbc16ddba161afc99e14d1713a453747a2b07fc097d2009f4c300ec99286105" +dependencies = [ + "ahash", + "atoi", + "base64", + "bitflags", + "byteorder", + "bytes 1.2.1", + "crc", + "crossbeam-queue", + "dirs 4.0.0", + "dotenvy", + "either", + "event-listener", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-util", + "hashlink", + "hex", + "hkdf", + "hmac 0.12.1", + "indexmap", + "itoa", + "libc", + "log", + "md-5", + "memchr", + "once_cell", + "paste", + "percent-encoding", + "rand 0.8.5", + "rustls 0.20.6", + "rustls-pemfile", + "serde", + "serde_json", + "sha1", + "sha2 0.10.6", + "smallvec", + "sqlformat", + "sqlx-rt", + "stringprep", + "thiserror", + "time 0.3.15", + "tokio-stream", + "url", + "uuid 1.2.1", + "webpki-roots 0.22.5", + "whoami", +] + +[[package]] +name = "sqlx-macros" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b850fa514dc11f2ee85be9d055c512aa866746adfacd1cb42d867d68e6a5b0d9" +dependencies = [ + "dotenvy", + "either", + "heck 0.4.0", + "once_cell", + "proc-macro2", + "quote", + "sha2 0.10.6", + "sqlx-core", + "sqlx-rt", + "syn", + "url", +] + +[[package]] +name = "sqlx-rt" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24c5b2d25fa654cc5f841750b8e1cdedbe21189bf9a9382ee90bfa9dd3562396" +dependencies = [ + "once_cell", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "stringprep" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "sum_tree" +version = "0.1.0" +dependencies = [ + "arrayvec 0.7.2", + "ctor", + "env_logger", + "log", + "rand 0.8.5", +] + +[[package]] +name = "sval" +version = "1.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45f6ee7c7b87caf59549e9fe45d6a69c75c8019e79e212a835c5da0e92f0ba08" +dependencies = [ + "serde", +] + +[[package]] +name = "svg_fmt" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2" + +[[package]] +name = "svgfilters" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb0dce2fee79ac40c21dafba48565ff7a5fa275e23ffe9ce047a40c9574ba34e" +dependencies = [ + "float-cmp", + "rgb", +] + +[[package]] +name = "svgtypes" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c536faaff1a10837cfe373142583f6e27d81e96beba339147e77b67c9f260ff" +dependencies = [ + "float-cmp", + "siphasher", +] + +[[package]] +name = "syn" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "system-interface" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e09bb3fb4e02ec4b87e182ea9718fadbc0fa3e50085b40a9af9690572b67f9e" +dependencies = [ + "atty", + "bitflags", + "cap-fs-ext", + "cap-std", + "io-lifetimes", + "rustix", + "winapi 0.3.9", + "winx", +] + +[[package]] +name = "target-lexicon" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02424087780c9b71cc96799eaeddff35af2bc513278cda5c99fc1f5d026d3c1" + +[[package]] +name = "tempdir" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +dependencies = [ + "rand 0.4.6", + "remove_dir_all", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi 0.3.9", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "terminal" +version = "0.1.0" +dependencies = [ + "alacritty_terminal", + "anyhow", + "client", + "context_menu", + "dirs 4.0.0", + "editor", + "futures 0.3.24", + "gpui", + "itertools", + "lazy_static", + "libc", + "mio-extras", + "ordered-float", + "procinfo", + "project", + "rand 0.8.5", + "serde", + "settings", + "shellexpand", + "smallvec", + "smol", + "theme", + "thiserror", + "util", + "workspace", +] + +[[package]] +name = "text" +version = "0.1.0" +dependencies = [ + "anyhow", + "arrayvec 0.7.2", + "bromberg_sl2", + "clock", + "collections", + "ctor", + "digest 0.9.0", + "env_logger", + "gpui", + "lazy_static", + "log", + "parking_lot 0.11.2", + "postage", + "rand 0.8.5", + "regex", + "smallvec", + "sum_tree", + "util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "textwrap" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" + +[[package]] +name = "theme" +version = "0.1.0" +dependencies = [ + "anyhow", + "gpui", + "indexmap", + "parking_lot 0.11.2", + "serde", + "serde_json", + "serde_path_to_error", + "toml", +] + +[[package]] +name = "theme_selector" +version = "0.1.0" +dependencies = [ + "editor", + "fuzzy", + "gpui", + "log", + "parking_lot 0.11.2", + "picker", + "postage", + "settings", + "smol", + "theme", + "workspace", +] + +[[package]] +name = "thiserror" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thousands" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" + +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tiff" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437" +dependencies = [ + "jpeg-decoder", + "miniz_oxide 0.4.4", + "weezl", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi 0.3.9", +] + +[[package]] +name = "time" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d634a985c4d4238ec39cacaed2e7ae552fbd3c476b552c1deac3021b7d7eaf0c" +dependencies = [ + "itoa", + "libc", + "num_threads", + "serde", + "time-macros", +] + +[[package]] +name = "time-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" + +[[package]] +name = "tiny-skia" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf81f2900d2e235220e6f31ec9f63ade6a7f59090c556d74fe949bb3b15e9fe" +dependencies = [ + "arrayref", + "arrayvec 0.5.2", + "bytemuck", + "cfg-if 1.0.0", + "png", + "safe_arch", +] + +[[package]] +name = "tiny_http" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce51b50006056f590c9b7c3808c3bd70f0d1101666629713866c227d6e58d39" +dependencies = [ + "ascii", + "chrono", + "chunked_transfer", + "log", + "url", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +dependencies = [ + "autocfg 1.1.0", + "bytes 1.2.1", + "libc", + "memchr", + "mio 0.8.4", + "num_cpus", + "parking_lot 0.12.1", + "pin-project-lite 0.2.9", + "signal-hook-registry", + "socket2", + "tokio-macros", + "winapi 0.3.9", +] + +[[package]] +name = "tokio-io" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.31", + "log", +] + +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite 0.2.9", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls 0.20.6", + "tokio", + "webpki 0.22.0", +] + +[[package]] +name = "tokio-stream" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6edf2d6bc038a43d31353570e27270603f4648d18f5ed10c0e179abe43255af" +dependencies = [ + "futures-core", + "pin-project-lite 0.2.9", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.17.3", +] + +[[package]] +name = "tokio-util" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" +dependencies = [ + "bytes 1.2.1", + "futures-core", + "futures-sink", + "log", + "pin-project-lite 0.2.9", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +dependencies = [ + "bytes 1.2.1", + "futures-core", + "futures-io", + "futures-sink", + "pin-project-lite 0.2.9", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + +[[package]] +name = "tonic" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff08f4649d10a70ffa3522ca559031285d8e421d727ac85c60825761818f5d0a" +dependencies = [ + "async-stream", + "async-trait", + "base64", + "bytes 1.2.1", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost 0.9.0", + "prost-derive 0.9.0", + "tokio", + "tokio-stream", + "tokio-util 0.6.10", + "tower", + "tower-layer", + "tower-service", + "tracing", + "tracing-futures", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap", + "pin-project", + "pin-project-lite 0.2.9", + "rand 0.8.5", + "slab", + "tokio", + "tokio-util 0.7.4", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" +dependencies = [ + "bitflags", + "bytes 1.2.1", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "pin-project-lite 0.2.9", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if 1.0.0", + "log", + "pin-project-lite 0.2.9", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "tree-sitter" +version = "0.20.8" +source = "git+https://github.com/tree-sitter/tree-sitter?rev=366210ae925d7ea0891bc7a0c738f60c77c04d7b#366210ae925d7ea0891bc7a0c738f60c77c04d7b" +dependencies = [ + "cc", + "regex", +] + +[[package]] +name = "tree-sitter-c" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cca211f4827d4b4dc79f388bf67b6fa3bc8a8cfa642161ef24f99f371ba34c7b" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-cpp" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a869e3c5cef4e5db4e9ab16a8dc84d73010e60ada14cdc60d2f6d8aed17779d" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-css" +version = "0.19.0" +source = "git+https://github.com/tree-sitter/tree-sitter-css?rev=769203d0f9abe1a9a691ac2b9fe4bb4397a73c51#769203d0f9abe1a9a691ac2b9fe4bb4397a73c51" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-elixir" +version = "0.19.0" +source = "git+https://github.com/elixir-lang/tree-sitter-elixir?rev=05e3631c6a0701c1fa518b0fee7be95a2ceef5e2#05e3631c6a0701c1fa518b0fee7be95a2ceef5e2" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-go" +version = "0.19.1" +source = "git+https://github.com/tree-sitter/tree-sitter-go?rev=aeb2f33b366fd78d5789ff104956ce23508b85db#aeb2f33b366fd78d5789ff104956ce23508b85db" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-html" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "184e6b77953a354303dc87bf5fe36558c83569ce92606e7b382a0dc1b7443443" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-javascript" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2490fab08630b2c8943c320f7b63473cbf65511c8d83aec551beb9b4375906ed" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-json" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90b04c4e1a92139535eb9fca4ec8fa9666cc96b618005d3ae35f3c957fa92f92" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-json" +version = "0.20.0" +source = "git+https://github.com/tree-sitter/tree-sitter-json?rev=137e1ce6a02698fc246cdb9c6b886ed1de9a1ed8#137e1ce6a02698fc246cdb9c6b886ed1de9a1ed8" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-markdown" +version = "0.0.1" +source = "git+https://github.com/MDeiml/tree-sitter-markdown?rev=330ecab87a3e3a7211ac69bbadc19eabecdb1cca#330ecab87a3e3a7211ac69bbadc19eabecdb1cca" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-python" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dda114f58048f5059dcf158aff691dffb8e113e6d2b50d94263fd68711975287" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-rust" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13470fafb7327a3acf96f5bc1013b5539a899a182f01c59b5af53f6b93195717" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-toml" +version = "0.5.1" +source = "git+https://github.com/tree-sitter/tree-sitter-toml?rev=342d9be207c2dba869b9967124c679b5e6fd0ebe#342d9be207c2dba869b9967124c679b5e6fd0ebe" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-typescript" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e8ed0ecb931cdff13c6a13f45ccd615156e2779d9ffb0395864e05505e6e86d" +dependencies = [ + "cc", + "tree-sitter", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "ttf-parser" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ddb402ac6c2af6f7a2844243887631c4e94b51585b229fcfddb43958cd55ca" + +[[package]] +name = "ttf-parser" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ae2f58a822f08abdaf668897e96a5656fe72f5a9ce66422423e8849384872e6" + +[[package]] +name = "tungstenite" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ad3713a14ae247f22a728a0456a545df14acf3867f905adff84be99e23b3ad1" +dependencies = [ + "base64", + "byteorder", + "bytes 1.2.1", + "http", + "httparse", + "log", + "rand 0.8.5", + "sha-1 0.9.8", + "thiserror", + "url", + "utf-8", +] + +[[package]] +name = "tungstenite" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" +dependencies = [ + "base64", + "byteorder", + "bytes 1.2.1", + "http", + "httparse", + "log", + "rand 0.8.5", + "sha-1 0.10.0", + "thiserror", + "url", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "ucd-trie" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-bidi-mirroring" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694" + +[[package]] +name = "unicode-ccc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1" + +[[package]] +name = "unicode-general-category" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9af028e052a610d99e066b33304625dea9613170a2563314490a4e6ec5cf7f" + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-script" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc" + +[[package]] +name = "unicode-segmentation" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" + +[[package]] +name = "unicode-vo" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "unindent" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58ee9362deb4a96cef4d437d1ad49cffc9b9e92d202b6995674e928ce684f112" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "usvg" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8352f317d8f9a918ba5154797fb2a93e2730244041cf7d5be35148266adfa5" +dependencies = [ + "base64", + "data-url", + "flate2", + "fontdb", + "kurbo", + "log", + "memmap2", + "pico-args", + "rctree", + "roxmltree", + "rustybuzz", + "simplecss", + "siphasher", + "svgtypes", + "ttf-parser 0.12.3", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "xmlwriter", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" + +[[package]] +name = "util" +version = "0.1.0" +dependencies = [ + "anyhow", + "futures 0.3.24", + "git2", + "lazy_static", + "log", + "rand 0.8.5", + "serde_json", + "tempdir", +] + +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom 0.2.7", +] + +[[package]] +name = "uuid" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" +dependencies = [ + "getrandom 0.2.7", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "value-bag" +version = "1.0.0-alpha.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" +dependencies = [ + "ctor", + "erased-serde", + "serde", + "serde_fmt", + "sval", + "version_check", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "vim" +version = "0.1.0" +dependencies = [ + "assets", + "async-compat", + "async-trait", + "collections", + "command_palette", + "editor", + "gpui", + "indoc", + "itertools", + "language", + "log", + "nvim-rs", + "parking_lot 0.11.2", + "project", + "search", + "serde", + "serde_json", + "settings", + "tokio", + "util", + "workspace", +] + +[[package]] +name = "vte" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983" +dependencies = [ + "utf8parse", + "vte_generate_state_changes", +] + +[[package]] +name = "vte_generate_state_changes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi 0.3.9", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi-cap-std-sync" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f086c5026d2fc3b268d138e65373f46422cc810f46d6e0776859c5027cb18728" +dependencies = [ + "anyhow", + "async-trait", + "cap-fs-ext", + "cap-rand", + "cap-std", + "cap-time-ext", + "fs-set-times", + "io-extras", + "io-lifetimes", + "is-terminal", + "lazy_static", + "rustix", + "system-interface", + "tracing", + "wasi-common", + "winapi 0.3.9", +] + +[[package]] +name = "wasi-common" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e8844fede1c3787cc08853872f47e8bd91f6c939c7406bc7a5dba496b260c08" +dependencies = [ + "anyhow", + "bitflags", + "cap-rand", + "cap-std", + "io-extras", + "rustix", + "thiserror", + "tracing", + "wiggle", + "winapi 0.3.9", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + +[[package]] +name = "wasm-encoder" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64ac98d5d61192cc45c701b7e4bd0b9aff91e2edfc7a088406cfe2288581e2c" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasmparser" +version = "0.85.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "570460c58b21e9150d2df0eaaedbb7816c34bcec009ae0dcc976e40ba81463e7" +dependencies = [ + "indexmap", +] + +[[package]] +name = "wasmtime" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f50eadf868ab6a04b7b511460233377d0bfbb92e417b2f6a98b98fef2e098f5" +dependencies = [ + "anyhow", + "async-trait", + "backtrace", + "bincode", + "cfg-if 1.0.0", + "indexmap", + "lazy_static", + "libc", + "log", + "object 0.28.4", + "once_cell", + "paste", + "psm", + "rayon", + "region", + "serde", + "target-lexicon", + "wasmparser", + "wasmtime-cache", + "wasmtime-cranelift", + "wasmtime-environ", + "wasmtime-fiber", + "wasmtime-jit", + "wasmtime-runtime", + "wat", + "winapi 0.3.9", +] + +[[package]] +name = "wasmtime-cache" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1df23c642e1376892f3b72f311596976979cbf8b85469680cdd3a8a063d12a2" +dependencies = [ + "anyhow", + "base64", + "bincode", + "directories-next", + "file-per-thread-logger", + "log", + "rustix", + "serde", + "sha2 0.9.9", + "toml", + "winapi 0.3.9", + "zstd", +] + +[[package]] +name = "wasmtime-cranelift" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f264ff6b4df247d15584f2f53d009fbc90032cfdc2605b52b961bffc71b6eccd" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "gimli", + "log", + "more-asserts", + "object 0.28.4", + "target-lexicon", + "thiserror", + "wasmparser", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-environ" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "839d2820e4b830f4b9e7aa08d4c0acabf4a5036105d639f6dfa1c6891c73bdc6" +dependencies = [ + "anyhow", + "cranelift-entity", + "gimli", + "indexmap", + "log", + "more-asserts", + "object 0.28.4", + "serde", + "target-lexicon", + "thiserror", + "wasmparser", + "wasmtime-types", +] + +[[package]] +name = "wasmtime-fiber" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3248be3c4911233535356025f6562193614a40155ee9094bb6a2b43f0dc82803" +dependencies = [ + "cc", + "rustix", + "winapi 0.3.9", +] + +[[package]] +name = "wasmtime-jit" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef0a0bcbfa18b946d890078ba0e1bc76bcc53eccfb40806c0020ec29dcd1bd49" +dependencies = [ + "addr2line", + "anyhow", + "bincode", + "cfg-if 1.0.0", + "cpp_demangle", + "gimli", + "ittapi-rs", + "log", + "object 0.28.4", + "region", + "rustc-demangle", + "rustix", + "serde", + "target-lexicon", + "thiserror", + "wasmtime-environ", + "wasmtime-jit-debug", + "wasmtime-runtime", + "winapi 0.3.9", +] + +[[package]] +name = "wasmtime-jit-debug" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f4779d976206c458edd643d1ac622b6c37e4a0800a8b1d25dfbf245ac2f2cac" +dependencies = [ + "lazy_static", + "object 0.28.4", + "rustix", +] + +[[package]] +name = "wasmtime-runtime" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7eb6ffa169eb5dcd18ac9473c817358cd57bc62c244622210566d473397954a" +dependencies = [ + "anyhow", + "backtrace", + "cc", + "cfg-if 1.0.0", + "indexmap", + "libc", + "log", + "mach", + "memfd", + "memoffset", + "more-asserts", + "rand 0.8.5", + "region", + "rustix", + "thiserror", + "wasmtime-environ", + "wasmtime-fiber", + "wasmtime-jit-debug", + "winapi 0.3.9", +] + +[[package]] +name = "wasmtime-types" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d932b0ac5336f7308d869703dd225610a6a3aeaa8e968c52b43eed96cefb1c2" +dependencies = [ + "cranelift-entity", + "serde", + "thiserror", + "wasmparser", +] + +[[package]] +name = "wasmtime-wasi" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b68b7d77fb6f2975a6fe6cc4d0015d6b0cebb65c39fce1dd4cc00880dbf7789c" +dependencies = [ + "anyhow", + "wasi-cap-std-sync", + "wasi-common", + "wasmtime", + "wiggle", +] + +[[package]] +name = "wast" +version = "35.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ef140f1b49946586078353a453a1d28ba90adfc54dde75710bc1931de204d68" +dependencies = [ + "leb128", +] + +[[package]] +name = "wast" +version = "47.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b98502f3978adea49551e801a6687678e6015317d7d9470a67fe813393f2a8" +dependencies = [ + "leb128", + "memchr", + "unicode-width", + "wasm-encoder", +] + +[[package]] +name = "wat" +version = "1.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aab4e20c60429fbba9670a6cae0fff9520046ba0aa3e6d0b1cd2653bea14898" +dependencies = [ + "wast 47.0.1", +] + +[[package]] +name = "web-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" +dependencies = [ + "webpki 0.21.4", +] + +[[package]] +name = "webpki-roots" +version = "0.22.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be" +dependencies = [ + "webpki 0.22.0", +] + +[[package]] +name = "weezl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" + +[[package]] +name = "wepoll-ffi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" +dependencies = [ + "cc", +] + +[[package]] +name = "which" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" +dependencies = [ + "either", + "libc", + "once_cell", +] + +[[package]] +name = "whoami" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6631b6a2fd59b1841b622e8f1a7ad241ef0a46f2d580464ce8140ac94cbd571" +dependencies = [ + "bumpalo", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wiggle" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dadac11343d2aabc8a906a0db0aaf7cb5046ec3d6fffccdaf2847dccdef8d6" +dependencies = [ + "anyhow", + "async-trait", + "bitflags", + "thiserror", + "tracing", + "wasmtime", + "wiggle-macro", +] + +[[package]] +name = "wiggle-generate" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63a1dccd6b3fbd9a27417f5d30ce9aa3ee9cf529aad453abbf88a49c5d605b79" +dependencies = [ + "anyhow", + "heck 0.4.0", + "proc-macro2", + "quote", + "shellexpand", + "syn", + "witx", +] + +[[package]] +name = "wiggle-macro" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1c368d57d9560c34deaa67e06b0953ccf65edb906c525e5a2c866c849b48ec2" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wiggle-generate", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "winx" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d5973cb8cd94a77d03ad7e23bbe14889cb29805da1cec0e4aff75e21aebded" +dependencies = [ + "bitflags", + "io-lifetimes", + "winapi 0.3.9", +] + +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "witx" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e366f27a5cabcddb2706a78296a40b8fcc451e1a6aba2fc1d94b4a01bdaaef4b" +dependencies = [ + "anyhow", + "log", + "thiserror", + "wast 35.0.2", +] + +[[package]] +name = "workspace" +version = "0.1.0" +dependencies = [ + "anyhow", + "client", + "clock", + "collections", + "context_menu", + "drag_and_drop", + "futures 0.3.24", + "gpui", + "language", + "log", + "menu", + "parking_lot 0.11.2", + "postage", + "project", + "serde", + "serde_json", + "settings", + "smallvec", + "theme", + "util", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "xml-rs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" + +[[package]] +name = "xmlparser" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "114ba2b24d2167ef6d67d7d04c8cc86522b87f490025f39f0303b7db5bf5e3d8" + +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "zed" +version = "0.59.0" +dependencies = [ + "activity_indicator", + "anyhow", + "assets", + "async-compression", + "async-recursion", + "async-trait", + "auto_update", + "backtrace", + "breadcrumbs", + "chat_panel", + "chrono", + "cli", + "client", + "clock", + "collections", + "command_palette", + "contacts_panel", + "contacts_status_item", + "context_menu", + "ctor", + "diagnostics", + "dirs 3.0.2", + "easy-parallel", + "editor", + "env_logger", + "file_finder", + "fsevent", + "futures 0.3.24", + "fuzzy", + "go_to_line", + "gpui", + "ignore", + "image", + "indexmap", + "isahc", + "journal", + "language", + "lazy_static", + "libc", + "log", + "lsp", + "num_cpus", + "outline", + "parking_lot 0.11.2", + "plugin_runtime", + "postage", + "project", + "project_panel", + "project_symbols", + "rand 0.8.5", + "regex", + "rpc", + "rsa", + "rust-embed", + "search", + "serde", + "serde_json", + "serde_path_to_error", + "settings", + "simplelog", + "smallvec", + "smol", + "sum_tree", + "tempdir", + "terminal", + "text", + "theme", + "theme_selector", + "thiserror", + "tiny_http", + "toml", + "tree-sitter", + "tree-sitter-c", + "tree-sitter-cpp", + "tree-sitter-css", + "tree-sitter-elixir", + "tree-sitter-go", + "tree-sitter-html", + "tree-sitter-json 0.20.0", + "tree-sitter-markdown", + "tree-sitter-python", + "tree-sitter-rust", + "tree-sitter-toml", + "tree-sitter-typescript", + "unindent", + "url", + "util", + "vim", + "workspace", +] + +[[package]] +name = "zeroize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.1+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fd07cbbc53846d9145dbffdf6dd09a7a0aa52be46741825f5c97bdd4f73f12b" +dependencies = [ + "cc", + "libc", +] diff --git a/crates/vim/src/test_contexts/neovim_backed_test_context.rs b/crates/vim/src/test_contexts/neovim_backed_test_context.rs index d537abb530..6eea30e7f7 100644 --- a/crates/vim/src/test_contexts/neovim_backed_test_context.rs +++ b/crates/vim/src/test_contexts/neovim_backed_test_context.rs @@ -52,22 +52,6 @@ impl<'a> NeovimBackedTestContext<'a> { self.exemptions.insert(initial_state, None); } - pub fn add_keybinding_exemption( - &mut self, - keybinding: [&str; COUNT], - initial_state: &str, - ) { - let initial_state = initial_state.to_string(); - let exempted_keybindings = self - .exemptions - .entry(initial_state) - .or_insert(Some(Default::default())); - - if let Some(exempted_bindings) = exempted_keybindings.as_mut() { - exempted_bindings.insert(format!("{keybinding:?}")); - } - } - pub async fn simulate_shared_keystroke(&mut self, keystroke_text: &str) { let keystroke = Keystroke::parse(keystroke_text).unwrap(); @@ -118,7 +102,7 @@ impl<'a> NeovimBackedTestContext<'a> { .get_current_buf() .await .expect("Could not get neovim buffer"); - let mut lines = self + let lines = self .buffer_text() .split('\n') .map(|line| line.to_string()) From 5fec8c8bfdf0f0e55a7ee84399fb8869f0d5d1eb Mon Sep 17 00:00:00 2001 From: K Simmons Date: Sun, 9 Oct 2022 01:19:22 -0700 Subject: [PATCH 204/314] Enable verifying of visual mode selections in neovim backed tests --- .../neovim_backed_test_context.rs | 96 +++++--- crates/vim/src/visual.rs | 228 ++++-------------- .../neovim_backed_test_context_works.json | 2 +- crates/vim/test_data/test_a.json | 2 +- crates/vim/test_data/test_backspace.json | 2 +- .../test_change_around_sentence.json | 2 +- .../test_data/test_change_around_word.json | 2 +- .../test_data/test_change_in_sentence.json | 2 +- crates/vim/test_data/test_change_in_word.json | 2 +- .../test_delete_around_sentence.json | 2 +- .../test_data/test_delete_around_word.json | 2 +- .../test_data/test_delete_in_sentence.json | 2 +- crates/vim/test_data/test_delete_in_word.json | 2 +- crates/vim/test_data/test_e.json | 2 +- .../vim/test_data/test_enter_visual_mode.json | 1 + crates/vim/test_data/test_gg.json | 2 +- crates/vim/test_data/test_h.json | 2 +- .../test_data/test_insert_end_of_line.json | 2 +- .../vim/test_data/test_insert_line_above.json | 2 +- crates/vim/test_data/test_j.json | 2 +- crates/vim/test_data/test_jump_to_end.json | 2 +- .../test_jump_to_line_boundaries.json | 2 +- crates/vim/test_data/test_k.json | 2 +- crates/vim/test_data/test_l.json | 2 +- crates/vim/test_data/test_neovim.json | 2 +- crates/vim/test_data/test_repeated_cb.json | 2 +- crates/vim/test_data/test_repeated_ce.json | 2 +- crates/vim/test_data/test_repeated_cj.json | 2 +- crates/vim/test_data/test_repeated_cl.json | 2 +- crates/vim/test_data/test_repeated_word.json | 2 +- crates/vim/test_data/test_visual_delete.json | 1 + .../test_data/test_visual_line_delete.json | 1 + crates/vim/test_data/test_w.json | 2 +- 33 files changed, 147 insertions(+), 236 deletions(-) create mode 100644 crates/vim/test_data/test_enter_visual_mode.json create mode 100644 crates/vim/test_data/test_visual_delete.json create mode 100644 crates/vim/test_data/test_visual_line_delete.json diff --git a/crates/vim/src/test_contexts/neovim_backed_test_context.rs b/crates/vim/src/test_contexts/neovim_backed_test_context.rs index 6eea30e7f7..aa52f0c40b 100644 --- a/crates/vim/src/test_contexts/neovim_backed_test_context.rs +++ b/crates/vim/src/test_contexts/neovim_backed_test_context.rs @@ -1,5 +1,5 @@ use std::{ - ops::{Deref, DerefMut}, + ops::{Deref, DerefMut, Range}, path::PathBuf, }; @@ -145,10 +145,17 @@ impl<'a> NeovimBackedTestContext<'a> { self.assertion_context.context() ); - let zed_head = self.update_editor(|editor, cx| editor.selections.newest_display(cx).head()); + let zed_selection = self.update_editor(|editor, cx| editor.selections.newest_display(cx)); + let mut zed_selection_range = zed_selection.range(); + // Zed selections adjust themselves to make the end point visually make sense + if zed_selection.reversed { + *zed_selection_range.end.column_mut() = + zed_selection_range.end.column().saturating_sub(1); + } + let neovim_selection = self.neovim.selection().await; assert_eq!( - self.neovim.head().await, - zed_head, + neovim_selection, + zed_selection_range, "{}", self.assertion_context.context() ); @@ -234,7 +241,7 @@ impl<'a> DerefMut for NeovimBackedTestContext<'a> { #[derive(Serialize, Deserialize)] pub enum NeovimData { Text(String), - Head { row: u32, column: u32 }, + Selection { start: (u32, u32), end: (u32, u32) }, Mode(Option), } @@ -267,6 +274,7 @@ impl NeovimConnection { .await .expect("Could not attach to ui"); + // Makes system act a little more like zed in terms of indentation nvim.set_option("smartindent", nvim_rs::Value::Boolean(true)) .await .expect("Could not set smartindent on startup"); @@ -319,36 +327,64 @@ impl NeovimConnection { } #[cfg(feature = "neovim")] - pub async fn head(&mut self) -> DisplayPoint { - let nvim_row: u32 = self - .nvim - .command_output("echo line('.')") - .await - .unwrap() - .parse::() - .unwrap() - - 1; // Neovim rows start at 1 - let nvim_column: u32 = self - .nvim - .command_output("echo col('.')") - .await - .unwrap() - .parse::() - .unwrap() - - 1; // Neovim columns start at 1 + pub async fn selection(&mut self) -> Range { + let (start, end) = if let Some(Mode::Visual { .. }) = self.mode().await { + self.nvim + .input("") + .await + .expect("Could not exit visual mode"); + let nvim_buffer = self + .nvim + .get_current_buf() + .await + .expect("Could not get neovim buffer"); + let (start_row, start_col) = nvim_buffer + .get_mark("<") + .await + .expect("Could not get selection start"); + let (end_row, end_col) = nvim_buffer + .get_mark(">") + .await + .expect("Could not get selection end"); + self.nvim + .input("gv") + .await + .expect("Could not reselect visual selection"); - self.data.push_back(NeovimData::Head { - row: nvim_row, - column: nvim_column, - }); + ( + (start_row as u32 - 1, start_col as u32), + (end_row as u32 - 1, end_col as u32), + ) + } else { + let nvim_row: u32 = self + .nvim + .command_output("echo line('.')") + .await + .unwrap() + .parse::() + .unwrap() + - 1; // Neovim rows start at 1 + let nvim_column: u32 = self + .nvim + .command_output("echo col('.')") + .await + .unwrap() + .parse::() + .unwrap() + - 1; // Neovim columns start at 1 - DisplayPoint::new(nvim_row, nvim_column) + ((nvim_row, nvim_column), (nvim_row, nvim_column)) + }; + + self.data.push_back(NeovimData::Selection { start, end }); + + DisplayPoint::new(start.0, start.1)..DisplayPoint::new(end.0, end.1) } #[cfg(not(feature = "neovim"))] - pub async fn head(&mut self) -> DisplayPoint { - if let Some(NeovimData::Head { row, column }) = self.data.pop_front() { - DisplayPoint::new(row, column) + pub async fn selection(&mut self) -> Range { + if let Some(NeovimData::Selection { start, end }) = self.data.pop_front() { + DisplayPoint::new(start.0, start.1)..DisplayPoint::new(end.0, end.1) } else { panic!("Invalid test data. Is test deterministic? Try running with '--features neovim' to regenerate"); } diff --git a/crates/vim/src/visual.rs b/crates/vim/src/visual.rs index c4c1ddf9a6..eb222346ce 100644 --- a/crates/vim/src/visual.rs +++ b/crates/vim/src/visual.rs @@ -280,220 +280,92 @@ pub fn paste(_: &mut Workspace, _: &VisualPaste, cx: &mut ViewContext mod test { use indoc::indoc; - use crate::{state::Mode, test_contexts::VimTestContext}; + use crate::{ + state::Mode, + test_contexts::{NeovimBackedTestContext, VimTestContext}, + }; #[gpui::test] async fn test_enter_visual_mode(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx - .binding(["v", "w", "j"]) - .mode_after(Mode::Visual { line: false }); - cx.assert( - indoc! {" + let mut cx = NeovimBackedTestContext::new(cx) + .await + .binding(["v", "w", "j"]); + cx.assert_all(indoc! {" The ˇquick brown - fox jumps over - the lazy dog"}, - indoc! {" - The «quick brown - fox jumps ˇ»over - the lazy dog"}, - ); - cx.assert( - indoc! {" - The quick brown - fox jumps over - the ˇlazy dog"}, - indoc! {" - The quick brown - fox jumps over - the «lazy ˇ»dog"}, - ); - cx.assert( - indoc! {" - The quick brown fox jumps ˇover - the lazy dog"}, - indoc! {" - The quick brown - fox jumps «over - ˇ»the lazy dog"}, - ); - let mut cx = cx - .binding(["v", "b", "k"]) - .mode_after(Mode::Visual { line: false }); - cx.assert( - indoc! {" + the ˇlazy dog"}) + .await; + let mut cx = cx.binding(["v", "b", "k"]); + cx.assert_all(indoc! {" The ˇquick brown - fox jumps over - the lazy dog"}, - indoc! {" - «ˇThe q»uick brown - fox jumps over - the lazy dog"}, - ); - cx.assert( - indoc! {" - The quick brown - fox jumps over - the ˇlazy dog"}, - indoc! {" - The quick brown - «ˇfox jumps over - the l»azy dog"}, - ); - cx.assert( - indoc! {" - The quick brown fox jumps ˇover - the lazy dog"}, - indoc! {" - The «ˇquick brown - fox jumps o»ver - the lazy dog"}, - ); + the ˇlazy dog"}) + .await; } #[gpui::test] async fn test_visual_delete(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["v", "w", "x"]); - cx.assert("The quick ˇbrown", "The quickˇ "); + let mut cx = NeovimBackedTestContext::new(cx) + .await + .binding(["v", "w", "x"]); + cx.assert("The quick ˇbrown").await; let mut cx = cx.binding(["v", "w", "j", "x"]); - cx.assert( - indoc! {" + cx.assert(indoc! {" The ˇquick brown fox jumps over - the lazy dog"}, - indoc! {" - The ˇver - the lazy dog"}, - ); + the lazy dog"}) + .await; // Test pasting code copied on delete - cx.simulate_keystrokes(["j", "p"]); - cx.assert_editor_state(indoc! {" - The ver - the lˇquick brown - fox jumps oazy dog"}); + cx.simulate_shared_keystrokes(["j", "p"]).await; + cx.assert_state_matches().await; - cx.assert( - indoc! {" - The quick brown - fox jumps over - the ˇlazy dog"}, - indoc! {" - The quick brown - fox jumps over - the ˇog"}, - ); - cx.assert( - indoc! {" - The quick brown - fox jumps ˇover - the lazy dog"}, - indoc! {" - The quick brown - fox jumps ˇhe lazy dog"}, - ); - let mut cx = cx.binding(["v", "b", "k", "x"]); - cx.assert( - indoc! {" + cx.assert_all(indoc! {" The ˇquick brown fox jumps over - the lazy dog"}, - indoc! {" - ˇuick brown - fox jumps over - the lazy dog"}, - ); - cx.assert( - indoc! {" - The quick brown - fox jumps over - the ˇlazy dog"}, - indoc! {" - The quick brown - ˇazy dog"}, - ); - cx.assert( - indoc! {" - The quick brown + the ˇlazy dog"}) + .await; + let mut cx = cx.binding(["v", "b", "k", "x"]); + cx.assert_all(indoc! {" + The ˇquick brown fox jumps ˇover - the lazy dog"}, - indoc! {" - The ˇver - the lazy dog"}, - ); + the ˇlazy dog"}) + .await; } #[gpui::test] async fn test_visual_line_delete(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["shift-v", "x"]); - cx.assert( - indoc! {" + let mut cx = NeovimBackedTestContext::new(cx) + .await + .binding(["shift-v", "x"]); + cx.assert(indoc! {" The quˇick brown fox jumps over - the lazy dog"}, - indoc! {" - fox juˇmps over - the lazy dog"}, - ); + the lazy dog"}) + .await; // Test pasting code copied on delete - cx.simulate_keystroke("p"); - cx.assert_editor_state(indoc! {" - fox jumps over - ˇThe quick brown - the lazy dog"}); + cx.simulate_shared_keystroke("p").await; + cx.assert_state_matches().await; - cx.assert( - indoc! {" + cx.assert_all(indoc! {" The quick brown fox juˇmps over - the lazy dog"}, - indoc! {" - The quick brown - the laˇzy dog"}, - ); - cx.assert( - indoc! {" - The quick brown - fox jumps over - the laˇzy dog"}, - indoc! {" - The quick brown - fox juˇmps over"}, - ); + the laˇzy dog"}) + .await; let mut cx = cx.binding(["shift-v", "j", "x"]); - cx.assert( - indoc! {" + cx.assert(indoc! {" The quˇick brown fox jumps over - the lazy dog"}, - "the laˇzy dog", - ); + the lazy dog"}) + .await; // Test pasting code copied on delete - cx.simulate_keystroke("p"); - cx.assert_editor_state(indoc! {" - the lazy dog - ˇThe quick brown - fox jumps over"}); + cx.simulate_shared_keystroke("p").await; + cx.assert_state_matches().await; - cx.assert( - indoc! {" + cx.assert_all(indoc! {" The quick brown fox juˇmps over - the lazy dog"}, - "The quˇick brown", - ); - cx.assert( - indoc! {" - The quick brown - fox jumps over - the laˇzy dog"}, - indoc! {" - The quick brown - fox juˇmps over"}, - ); + the laˇzy dog"}) + .await; } #[gpui::test] diff --git a/crates/vim/test_data/neovim_backed_test_context_works.json b/crates/vim/test_data/neovim_backed_test_context_works.json index 4b0312565e..807c9010e8 100644 --- a/crates/vim/test_data/neovim_backed_test_context_works.json +++ b/crates/vim/test_data/neovim_backed_test_context_works.json @@ -1 +1 @@ -[{"Text":""},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"This is a test"},{"Head":{"row":0,"column":13}},{"Mode":"Normal"}] \ No newline at end of file +[{"Text":""},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"This is a test"},{"Mode":"Normal"},{"Selection":{"start":[0,13],"end":[0,13]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_a.json b/crates/vim/test_data/test_a.json index fc9d170879..32ea8ac6a6 100644 --- a/crates/vim/test_data/test_a.json +++ b/crates/vim/test_data/test_a.json @@ -1 +1 @@ -[{"Text":"The quick"},{"Head":{"row":0,"column":6}},{"Mode":"Insert"},{"Text":"The quick"},{"Head":{"row":0,"column":9}},{"Mode":"Insert"}] \ No newline at end of file +[{"Text":"The quick"},{"Mode":"Insert"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Insert"},{"Text":"The quick"},{"Mode":"Insert"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_backspace.json b/crates/vim/test_data/test_backspace.json index 3659773665..d002dfa718 100644 --- a/crates/vim/test_data/test_backspace.json +++ b/crates/vim/test_data/test_backspace.json @@ -1 +1 @@ -[{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":4}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":8}},{"Mode":"Normal"}] \ No newline at end of file +[{"Text":"The quick\nbrown"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Mode":"Normal"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Mode":"Normal"},{"Selection":{"start":[0,8],"end":[0,8]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_sentence.json b/crates/vim/test_data/test_change_around_sentence.json index 9e01890d9f..187e766f11 100644 --- a/crates/vim/test_data/test_change_around_sentence.json +++ b/crates/vim/test_data/test_change_around_sentence.json @@ -1 +1 @@ -[{"Text":"Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!"},{"Head":{"row":0,"column":27}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!"},{"Head":{"row":0,"column":27}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!"},{"Head":{"row":0,"column":27}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!"},{"Head":{"row":0,"column":27}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Head":{"row":2,"column":13}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Head":{"row":2,"column":13}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Head":{"row":2,"column":13}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" "},{"Head":{"row":0,"column":21}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" "},{"Head":{"row":0,"column":21}},{"Mode":"Insert"}] \ No newline at end of file +[{"Text":"Fox Jumps! Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Fox Jumps! Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Fox Jumps! Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Insert"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Insert"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Insert"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Insert"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Mode":"Insert"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Mode":"Insert"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Mode":"Insert"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" "},{"Mode":"Insert"},{"Selection":{"start":[0,21],"end":[0,21]}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" "},{"Mode":"Insert"},{"Selection":{"start":[0,21],"end":[0,21]}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word.json b/crates/vim/test_data/test_change_around_word.json index c8724a6810..3463c5c6d6 100644 --- a/crates/vim/test_data/test_change_around_word.json +++ b/crates/vim/test_data/test_change_around_word.json @@ -1 +1 @@ -[{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":15}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":2,"column":12}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":4,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":5,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThequick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":3}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":15}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n-jumps over\nthe lazy dog \n\n"},{"Head":{"row":7,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n-jumps over\nthe lazy dog \n\n"},{"Head":{"row":8,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n-jumps over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":6}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n"},{"Head":{"row":10,"column":12}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n"},{"Head":{"row":11,"column":0}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":15}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":2,"column":12}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":4,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":5,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":15}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n over\nthe lazy dog \n\n"},{"Head":{"row":7,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n over\nthe lazy dog \n\n"},{"Head":{"row":8,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":2}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n"},{"Head":{"row":10,"column":12}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n"},{"Head":{"row":11,"column":0}},{"Mode":"Insert"}] \ No newline at end of file +[{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,15],"end":[0,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,9],"end":[1,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThequick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,3],"end":[6,3]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,10],"end":[6,10]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,15],"end":[6,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,6],"end":[9,6]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[10,12],"end":[10,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n"},{"Mode":"Insert"},{"Selection":{"start":[11,0],"end":[11,0]}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,15],"end":[0,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,9],"end":[1,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,10],"end":[6,10]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,15],"end":[6,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,2],"end":[9,2]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[10,12],"end":[10,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n"},{"Mode":"Insert"},{"Selection":{"start":[11,0],"end":[11,0]}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence.json b/crates/vim/test_data/test_change_in_sentence.json index 08dbca368a..5058d5b569 100644 --- a/crates/vim/test_data/test_change_in_sentence.json +++ b/crates/vim/test_data/test_change_in_sentence.json @@ -1 +1 @@ -[{"Text":" Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown?Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":16}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!Over the lazy."},{"Head":{"row":0,"column":27}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps! "},{"Head":{"row":0,"column":28}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps! "},{"Head":{"row":0,"column":28}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps! "},{"Head":{"row":0,"column":28}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.The quick \nbrown fox jumps over\n"},{"Head":{"row":2,"column":13}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. \n"},{"Head":{"row":2,"column":14}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. \n"},{"Head":{"row":2,"column":14}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" "},{"Head":{"row":0,"column":21}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" "},{"Head":{"row":0,"column":21}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" Brown fox jumps."},{"Head":{"row":0,"column":37}},{"Mode":"Insert"}] \ No newline at end of file +[{"Text":" Fox Jumps! Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" Fox Jumps! Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" Fox Jumps! Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown?Fox Jumps! Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,16],"end":[0,16]}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps! "},{"Mode":"Insert"},{"Selection":{"start":[0,28],"end":[0,28]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps! "},{"Mode":"Insert"},{"Selection":{"start":[0,28],"end":[0,28]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps! "},{"Mode":"Insert"},{"Selection":{"start":[0,28],"end":[0,28]}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. \n"},{"Mode":"Insert"},{"Selection":{"start":[2,14],"end":[2,14]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. \n"},{"Mode":"Insert"},{"Selection":{"start":[2,14],"end":[2,14]}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" "},{"Mode":"Insert"},{"Selection":{"start":[0,21],"end":[0,21]}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" "},{"Mode":"Insert"},{"Selection":{"start":[0,21],"end":[0,21]}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" Brown fox jumps."},{"Mode":"Insert"},{"Selection":{"start":[0,37],"end":[0,37]}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word.json b/crates/vim/test_data/test_change_in_word.json index eaa390e50f..ef65a09291 100644 --- a/crates/vim/test_data/test_change_in_word.json +++ b/crates/vim/test_data/test_change_in_word.json @@ -1 +1 @@ -[{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":15}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumpsover\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":2,"column":12}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":4,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":5,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThequick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":3}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe- brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe- brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quickbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown\n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":15}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n\n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":7,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n\n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":8,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \nfox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox- over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":6}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n\n"},{"Head":{"row":10,"column":12}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":11,"column":0}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":15}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumpsover\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":2,"column":12}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":4,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":5,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quickbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown\n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":15}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n\n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":7,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n\n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":8,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \nfox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":2}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n\n"},{"Head":{"row":10,"column":12}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":11,"column":0}},{"Mode":"Insert"}] \ No newline at end of file +[{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown\nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,15],"end":[0,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumpsover\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,9],"end":[1,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThequick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,3],"end":[6,3]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe- brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe- brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quickbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,10],"end":[6,10]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown\n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,15],"end":[6,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n\n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n\n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \nfox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox- over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,6],"end":[9,6]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n\n"},{"Mode":"Insert"},{"Selection":{"start":[10,12],"end":[10,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[11,0],"end":[11,0]}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown\nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,15],"end":[0,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumpsover\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,9],"end":[1,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quickbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,10],"end":[6,10]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown\n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,15],"end":[6,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n\n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n\n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \nfox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,2],"end":[9,2]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n\n"},{"Mode":"Insert"},{"Selection":{"start":[10,12],"end":[10,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[11,0],"end":[11,0]}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence.json b/crates/vim/test_data/test_delete_around_sentence.json index 479f0cc77c..b18c6ffa76 100644 --- a/crates/vim/test_data/test_delete_around_sentence.json +++ b/crates/vim/test_data/test_delete_around_sentence.json @@ -1 +1 @@ -[{"Text":"Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!"},{"Head":{"row":0,"column":26}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!"},{"Head":{"row":0,"column":26}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!"},{"Head":{"row":0,"column":26}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!"},{"Head":{"row":0,"column":26}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Head":{"row":2,"column":12}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Head":{"row":2,"column":12}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Head":{"row":2,"column":12}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" "},{"Head":{"row":0,"column":20}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" "},{"Head":{"row":0,"column":20}},{"Mode":"Normal"}] \ No newline at end of file +[{"Text":"Fox Jumps! Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Fox Jumps! Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Fox Jumps! Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Normal"},{"Selection":{"start":[0,26],"end":[0,26]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Normal"},{"Selection":{"start":[0,26],"end":[0,26]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Normal"},{"Selection":{"start":[0,26],"end":[0,26]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Normal"},{"Selection":{"start":[0,26],"end":[0,26]}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Mode":"Normal"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Mode":"Normal"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Mode":"Normal"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" "},{"Mode":"Normal"},{"Selection":{"start":[0,20],"end":[0,20]}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" "},{"Mode":"Normal"},{"Selection":{"start":[0,20],"end":[0,20]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word.json b/crates/vim/test_data/test_delete_around_word.json index 37e370d3bd..aec3fdd39a 100644 --- a/crates/vim/test_data/test_delete_around_word.json +++ b/crates/vim/test_data/test_delete_around_word.json @@ -1 +1 @@ -[{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":9}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":15}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":8}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":2,"column":11}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":5,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThequick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":3}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":15}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n-jumps over\nthe lazy dog \n\n"},{"Head":{"row":7,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n-jumps over\nthe lazy dog \n\n"},{"Head":{"row":8,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n-jumps over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":6}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n"},{"Head":{"row":10,"column":11}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog "},{"Head":{"row":10,"column":0}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":9}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":15}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":8}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":2,"column":11}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":5,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":15}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n over\nthe lazy dog \n\n"},{"Head":{"row":7,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n over\nthe lazy dog \n\n"},{"Head":{"row":8,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":2}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n"},{"Head":{"row":10,"column":11}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog "},{"Head":{"row":10,"column":0}},{"Mode":"Normal"}] \ No newline at end of file +[{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Normal"},{"Text":"The quick brown jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,15],"end":[0,15]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,8],"end":[1,8]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThequick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,3],"end":[6,3]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,15],"end":[6,15]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,6],"end":[9,6]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[10,11],"end":[10,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog "},{"Mode":"Normal"},{"Selection":{"start":[10,0],"end":[10,0]}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Normal"},{"Text":"The quick brown jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,15],"end":[0,15]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,8],"end":[1,8]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,15],"end":[6,15]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,2],"end":[9,2]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[10,11],"end":[10,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog "},{"Mode":"Normal"},{"Selection":{"start":[10,0],"end":[10,0]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence.json b/crates/vim/test_data/test_delete_in_sentence.json index 1ecaa01ee6..31891d19cb 100644 --- a/crates/vim/test_data/test_delete_in_sentence.json +++ b/crates/vim/test_data/test_delete_in_sentence.json @@ -1 +1 @@ -[{"Text":" Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":" Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":" Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown?Fox Jumps! Over the lazy."},{"Head":{"row":0,"column":16}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Head":{"row":0,"column":17}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!Over the lazy."},{"Head":{"row":0,"column":27}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps! "},{"Head":{"row":0,"column":27}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps! "},{"Head":{"row":0,"column":27}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps! "},{"Head":{"row":0,"column":27}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.The quick \nbrown fox jumps over\n"},{"Head":{"row":2,"column":13}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. \n"},{"Head":{"row":2,"column":13}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. \n"},{"Head":{"row":2,"column":13}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" "},{"Head":{"row":0,"column":21}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" "},{"Head":{"row":0,"column":21}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" Brown fox jumps."},{"Head":{"row":0,"column":36}},{"Mode":"Normal"}] \ No newline at end of file +[{"Text":" Fox Jumps! Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" Fox Jumps! Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" Fox Jumps! Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick brown?Fox Jumps! Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,16],"end":[0,16]}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps! "},{"Mode":"Normal"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps! "},{"Mode":"Normal"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps! "},{"Mode":"Normal"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. \n"},{"Mode":"Normal"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. \n"},{"Mode":"Normal"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" "},{"Mode":"Normal"},{"Selection":{"start":[0,21],"end":[0,21]}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" "},{"Mode":"Normal"},{"Selection":{"start":[0,21],"end":[0,21]}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" Brown fox jumps."},{"Mode":"Normal"},{"Selection":{"start":[0,36],"end":[0,36]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word.json b/crates/vim/test_data/test_delete_in_word.json index 1960dce67a..c15c6ce208 100644 --- a/crates/vim/test_data/test_delete_in_word.json +++ b/crates/vim/test_data/test_delete_in_word.json @@ -1 +1 @@ -[{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":14}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumpsover\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":2,"column":11}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":5,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThequick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":3}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe- brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe- brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quickbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown\n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":14}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n\n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":7,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n\n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":8,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \nfox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox- over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":6}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n\n"},{"Head":{"row":10,"column":11}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":11,"column":0}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":0,"column":14}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumpsover\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":1,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":2,"column":11}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":5,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quickbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown\n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":6,"column":14}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n\n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":7,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n\n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":8,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \nfox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Head":{"row":9,"column":2}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n\n"},{"Head":{"row":10,"column":11}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Head":{"row":11,"column":0}},{"Mode":"Normal"}] \ No newline at end of file +[{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,14],"end":[0,14]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumpsover\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,9],"end":[1,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThequick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,3],"end":[6,3]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe- brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe- brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quickbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,10],"end":[6,10]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown\n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,14],"end":[6,14]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n\n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n\n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \nfox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox- over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,6],"end":[9,6]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n\n"},{"Mode":"Normal"},{"Selection":{"start":[10,11],"end":[10,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[11,0],"end":[11,0]}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,14],"end":[0,14]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumpsover\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,9],"end":[1,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quickbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,10],"end":[6,10]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown\n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,14],"end":[6,14]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n\n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n\n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \nfox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,2],"end":[9,2]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n\n"},{"Mode":"Normal"},{"Selection":{"start":[10,11],"end":[10,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[11,0],"end":[11,0]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_e.json b/crates/vim/test_data/test_e.json index bd3c513846..c4650284dd 100644 --- a/crates/vim/test_data/test_e.json +++ b/crates/vim/test_data/test_e.json @@ -1 +1 @@ -[{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":0,"column":8}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":0,"column":9}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":0,"column":14}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":3,"column":8}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":3,"column":13}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":4,"column":2}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":4,"column":2}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":0,"column":14}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":0,"column":14}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":0,"column":14}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":3,"column":8}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":3,"column":13}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":4,"column":2}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":4,"column":2}},{"Mode":"Normal"}] \ No newline at end of file +[{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[0,8],"end":[0,8]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[0,14],"end":[0,14]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[3,8],"end":[3,8]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[3,13],"end":[3,13]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[4,2],"end":[4,2]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[4,2],"end":[4,2]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[0,14],"end":[0,14]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[0,14],"end":[0,14]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[0,14],"end":[0,14]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[3,8],"end":[3,8]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[3,13],"end":[3,13]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[4,2],"end":[4,2]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[4,2],"end":[4,2]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_enter_visual_mode.json b/crates/vim/test_data/test_enter_visual_mode.json new file mode 100644 index 0000000000..43d1e0559a --- /dev/null +++ b/crates/vim/test_data/test_enter_visual_mode.json @@ -0,0 +1 @@ +[{"Text":"The quick brown\nfox jumps over\nthe lazy dog"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,4],"end":[1,10]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown\nfox jumps over\nthe lazy dog"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[1,10],"end":[2,0]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown\nfox jumps over\nthe lazy dog"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[2,4],"end":[2,9]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown\nfox jumps over\nthe lazy dog"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,0],"end":[0,4]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown\nfox jumps over\nthe lazy dog"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,4],"end":[1,10]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown\nfox jumps over\nthe lazy dog"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[1,0],"end":[2,4]}},{"Mode":{"Visual":{"line":false}}}] \ No newline at end of file diff --git a/crates/vim/test_data/test_gg.json b/crates/vim/test_data/test_gg.json index 7ce13ba6cb..ef301ba221 100644 --- a/crates/vim/test_data/test_gg.json +++ b/crates/vim/test_data/test_gg.json @@ -1 +1 @@ -[{"Text":"The quick\n\nbrown fox jumps\nover the lazy dog"},{"Head":{"row":0,"column":5}},{"Mode":"Normal"},{"Text":"The quick\n\nbrown fox jumps\nover the lazy dog"},{"Head":{"row":0,"column":5}},{"Mode":"Normal"},{"Text":"The quick\n\nbrown fox jumps\nover the lazy dog"},{"Head":{"row":0,"column":8}},{"Mode":"Normal"},{"Text":"\n\nbrown fox jumps\nover the lazy dog"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"}] \ No newline at end of file +[{"Text":"The quick\n\nbrown fox jumps\nover the lazy dog"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"The quick\n\nbrown fox jumps\nover the lazy dog"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"The quick\n\nbrown fox jumps\nover the lazy dog"},{"Mode":"Normal"},{"Selection":{"start":[0,8],"end":[0,8]}},{"Mode":"Normal"},{"Text":"\n\nbrown fox jumps\nover the lazy dog"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_h.json b/crates/vim/test_data/test_h.json index 7626f1b121..6eeff7997f 100644 --- a/crates/vim/test_data/test_h.json +++ b/crates/vim/test_data/test_h.json @@ -1 +1 @@ -[{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":4}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"}] \ No newline at end of file +[{"Text":"The quick\nbrown"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Mode":"Normal"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_insert_end_of_line.json b/crates/vim/test_data/test_insert_end_of_line.json index 887ba4dabc..6b377b0d20 100644 --- a/crates/vim/test_data/test_insert_end_of_line.json +++ b/crates/vim/test_data/test_insert_end_of_line.json @@ -1 +1 @@ -[{"Text":"\nThe quick\nbrown fox "},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\nThe quick\nbrown fox "},{"Head":{"row":1,"column":9}},{"Mode":"Insert"},{"Text":"\nThe quick\nbrown fox "},{"Head":{"row":2,"column":10}},{"Mode":"Insert"}] \ No newline at end of file +[{"Text":"\nThe quick\nbrown fox "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nThe quick\nbrown fox "},{"Mode":"Insert"},{"Selection":{"start":[1,9],"end":[1,9]}},{"Mode":"Insert"},{"Text":"\nThe quick\nbrown fox "},{"Mode":"Insert"},{"Selection":{"start":[2,10],"end":[2,10]}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_insert_line_above.json b/crates/vim/test_data/test_insert_line_above.json index 0dc54b6f88..45854367ae 100644 --- a/crates/vim/test_data/test_insert_line_above.json +++ b/crates/vim/test_data/test_insert_line_above.json @@ -1 +1 @@ -[{"Text":"\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\nThe quick"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\nThe quick\nbrown fox\njumps over"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick\n\nbrown fox\njumps over"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick\nbrown fox\n\njumps over"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick\n\n\nbrown fox"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"}] \ No newline at end of file +[{"Text":"\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nThe quick"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nThe quick\nbrown fox\njumps over"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick\n\nbrown fox\njumps over"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick\nbrown fox\n\njumps over"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick\n\n\nbrown fox"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_j.json b/crates/vim/test_data/test_j.json index b3202075f5..da373bbcad 100644 --- a/crates/vim/test_data/test_j.json +++ b/crates/vim/test_data/test_j.json @@ -1 +1 @@ -[{"Text":"The quick brown\nfox jumps"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps"},{"Head":{"row":1,"column":5}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps"},{"Head":{"row":1,"column":8}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"}] \ No newline at end of file +[{"Text":"The quick brown\nfox jumps"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps"},{"Mode":"Normal"},{"Selection":{"start":[1,5],"end":[1,5]}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps"},{"Mode":"Normal"},{"Selection":{"start":[1,8],"end":[1,8]}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_jump_to_end.json b/crates/vim/test_data/test_jump_to_end.json index 71bd396efe..c556c7cb16 100644 --- a/crates/vim/test_data/test_jump_to_end.json +++ b/crates/vim/test_data/test_jump_to_end.json @@ -1 +1 @@ -[{"Text":"The quick\n\nbrown fox jumps\nover the lazy dog"},{"Head":{"row":3,"column":4}},{"Mode":"Normal"},{"Text":"The quick\n\nbrown fox jumps\nover the lazy dog"},{"Head":{"row":3,"column":4}},{"Mode":"Normal"},{"Text":"The quick\n\nbrown fox jumps\nover the lazy dog"},{"Head":{"row":3,"column":16}},{"Mode":"Normal"},{"Text":"The quick\n\nbrown"},{"Head":{"row":2,"column":4}},{"Mode":"Normal"},{"Text":"The quick\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Normal"}] \ No newline at end of file +[{"Text":"The quick\n\nbrown fox jumps\nover the lazy dog"},{"Mode":"Normal"},{"Selection":{"start":[3,4],"end":[3,4]}},{"Mode":"Normal"},{"Text":"The quick\n\nbrown fox jumps\nover the lazy dog"},{"Mode":"Normal"},{"Selection":{"start":[3,4],"end":[3,4]}},{"Mode":"Normal"},{"Text":"The quick\n\nbrown fox jumps\nover the lazy dog"},{"Mode":"Normal"},{"Selection":{"start":[3,16],"end":[3,16]}},{"Mode":"Normal"},{"Text":"The quick\n\nbrown"},{"Mode":"Normal"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Normal"},{"Text":"The quick\n\n"},{"Mode":"Normal"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_jump_to_line_boundaries.json b/crates/vim/test_data/test_jump_to_line_boundaries.json index 48dd51a219..386a2db23f 100644 --- a/crates/vim/test_data/test_jump_to_line_boundaries.json +++ b/crates/vim/test_data/test_jump_to_line_boundaries.json @@ -1 +1 @@ -[{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":8}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":8}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":8}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":1,"column":4}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":1,"column":4}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"}] \ No newline at end of file +[{"Text":"The quick\nbrown"},{"Mode":"Normal"},{"Selection":{"start":[0,8],"end":[0,8]}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Mode":"Normal"},{"Selection":{"start":[0,8],"end":[0,8]}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Mode":"Normal"},{"Selection":{"start":[0,8],"end":[0,8]}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_k.json b/crates/vim/test_data/test_k.json index d99237d71a..cd5242c0f3 100644 --- a/crates/vim/test_data/test_k.json +++ b/crates/vim/test_data/test_k.json @@ -1 +1 @@ -[{"Text":"The quick\nbrown fox jumps"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox jumps"},{"Head":{"row":0,"column":5}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox jumps"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox jumps"},{"Head":{"row":0,"column":7}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox jumps"},{"Head":{"row":0,"column":8}},{"Mode":"Normal"}] \ No newline at end of file +[{"Text":"The quick\nbrown fox jumps"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox jumps"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox jumps"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox jumps"},{"Mode":"Normal"},{"Selection":{"start":[0,7],"end":[0,7]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox jumps"},{"Mode":"Normal"},{"Selection":{"start":[0,8],"end":[0,8]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_l.json b/crates/vim/test_data/test_l.json index 33f70e2ee9..3d6c4b3493 100644 --- a/crates/vim/test_data/test_l.json +++ b/crates/vim/test_data/test_l.json @@ -1 +1 @@ -[{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":1}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":6}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":0,"column":8}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":1,"column":1}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Head":{"row":1,"column":4}},{"Mode":"Normal"}] \ No newline at end of file +[{"Text":"The quick\nbrown"},{"Mode":"Normal"},{"Selection":{"start":[0,1],"end":[0,1]}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Mode":"Normal"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Mode":"Normal"},{"Selection":{"start":[0,8],"end":[0,8]}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Mode":"Normal"},{"Selection":{"start":[1,1],"end":[1,1]}},{"Mode":"Normal"},{"Text":"The quick\nbrown"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_neovim.json b/crates/vim/test_data/test_neovim.json index 678e4f7852..eba763f676 100644 --- a/crates/vim/test_data/test_neovim.json +++ b/crates/vim/test_data/test_neovim.json @@ -1 +1 @@ -[{"Text":"test"},{"Head":{"row":0,"column":0}},{"Mode":"Normal"}] \ No newline at end of file +[{"Text":"test"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_repeated_cb.json b/crates/vim/test_data/test_repeated_cb.json index 357e37a8c9..b00195b2d9 100644 --- a/crates/vim/test_data/test_repeated_cb.json +++ b/crates/vim/test_data/test_repeated_cb.json @@ -1 +1 @@ -[{"Text":"The ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":4}},{"Mode":"Insert"},{"Text":"The brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":4}},{"Mode":"Insert"},{"Text":"The quick n\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick \n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\njumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox -over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumpsover\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-ver\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The n\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":4}},{"Mode":"Insert"},{"Text":"The \n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":4}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\njumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumpsver\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"n\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The \nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":4}},{"Mode":"Insert"},{"Text":"The quick jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nover\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox ver\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox \nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"n\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":4}},{"Mode":"Insert"},{"Text":"The quick -over\nthe lazy dog\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\nover\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nver\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"n\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The -over\nthe lazy dog\n"},{"Head":{"row":0,"column":4}},{"Mode":"Insert"},{"Text":"The quick over\nthe lazy dog\n"},{"Head":{"row":0,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\nver\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"}] \ No newline at end of file +[{"Text":"The ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The quick n\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick \n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\njumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox -over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumpsover\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-ver\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,10],"end":[2,10]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,10],"end":[2,10]}},{"Mode":"Insert"},{"Text":"ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The n\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The \n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown\njumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumpsver\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Insert"},{"Text":"ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"n\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The \nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The quick jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown\n-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nover\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox ver\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox \nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"n\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The quick -over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown\nover\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nver\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"ick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"n\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The -over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The quick over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown\nver\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_repeated_ce.json b/crates/vim/test_data/test_repeated_ce.json index 32394077e6..3251127aed 100644 --- a/crates/vim/test_data/test_repeated_ce.json +++ b/crates/vim/test_data/test_repeated_ce.json @@ -1 +1 @@ -[{"Text":" quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The qu brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":6}},{"Mode":"Insert"},{"Text":"The quick\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":9}},{"Mode":"Insert"},{"Text":"The quick brow jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":14}},{"Mode":"Insert"},{"Text":"The quick brown\n jumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox -over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o\nthe lazy dog\n"},{"Head":{"row":2,"column":11}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n lazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The qu\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":6}},{"Mode":"Insert"},{"Text":"The quick jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":9}},{"Mode":"Insert"},{"Text":"The quick brow-over\nthe lazy dog\n"},{"Head":{"row":0,"column":14}},{"Mode":"Insert"},{"Text":"The quick brown\n-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps- lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o lazy dog\n"},{"Head":{"row":2,"column":11}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":"\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The qu jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":6}},{"Mode":"Insert"},{"Text":"The quick-over\nthe lazy dog\n"},{"Head":{"row":0,"column":9}},{"Mode":"Insert"},{"Text":"The quick browover\nthe lazy dog\n"},{"Head":{"row":0,"column":14}},{"Mode":"Insert"},{"Text":"The quick brown\nover\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nover\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox \nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps- dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o dog\n"},{"Head":{"row":2,"column":11}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":" jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The qu-over\nthe lazy dog\n"},{"Head":{"row":0,"column":6}},{"Mode":"Insert"},{"Text":"The quickover\nthe lazy dog\n"},{"Head":{"row":0,"column":9}},{"Mode":"Insert"},{"Text":"The quick brow\nthe lazy dog\n"},{"Head":{"row":0,"column":14}},{"Mode":"Insert"},{"Text":"The quick brown\n\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-\n"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o\n"},{"Head":{"row":2,"column":11}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":"-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quover\nthe lazy dog\n"},{"Head":{"row":0,"column":6}},{"Mode":"Insert"},{"Text":"The quick\nthe lazy dog\n"},{"Head":{"row":0,"column":9}},{"Mode":"Insert"},{"Text":"The quick brow lazy dog\n"},{"Head":{"row":0,"column":14}},{"Mode":"Insert"},{"Text":"The quick brown\n lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o"},{"Head":{"row":2,"column":11}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"}] \ No newline at end of file +[{"Text":" quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The qu brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Insert"},{"Text":"The quick\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Insert"},{"Text":"The quick brow jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,14],"end":[0,14]}},{"Mode":"Insert"},{"Text":"The quick brown\n jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox -over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,10],"end":[2,10]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":" brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The qu\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Insert"},{"Text":"The quick jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Insert"},{"Text":"The quick brow-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,14],"end":[0,14]}},{"Mode":"Insert"},{"Text":"The quick brown\n-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps- lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,10],"end":[2,10]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n dog\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":"\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The qu jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Insert"},{"Text":"The quick-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Insert"},{"Text":"The quick browover\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,14],"end":[0,14]}},{"Mode":"Insert"},{"Text":"The quick brown\nover\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nover\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox \nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps- dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,10],"end":[2,10]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":" jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The qu-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Insert"},{"Text":"The quickover\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Insert"},{"Text":"The quick brow\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,14],"end":[0,14]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps\n"},{"Mode":"Insert"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-\n"},{"Mode":"Insert"},{"Selection":{"start":[2,10],"end":[2,10]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o\n"},{"Mode":"Insert"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":"-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quover\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Insert"},{"Text":"The quick\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Insert"},{"Text":"The quick brow lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,14],"end":[0,14]}},{"Mode":"Insert"},{"Text":"The quick brown\n lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps"},{"Mode":"Insert"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-"},{"Mode":"Insert"},{"Selection":{"start":[2,10],"end":[2,10]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o"},{"Mode":"Insert"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_repeated_cj.json b/crates/vim/test_data/test_repeated_cj.json index f6c81c9a4a..0b8ac80442 100644 --- a/crates/vim/test_data/test_repeated_cj.json +++ b/crates/vim/test_data/test_repeated_cj.json @@ -1 +1 @@ -[{"Text":"\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":"\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":"\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":""},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":""},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":""},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":""},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":""},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":""},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":""},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":""},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"}] \ No newline at end of file +[{"Text":"\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":"\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":"\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":""},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":""},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":""},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":""},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":""},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":""},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":""},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":""},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_repeated_cl.json b/crates/vim/test_data/test_repeated_cl.json index 7eaf17b24a..89f6a104af 100644 --- a/crates/vim/test_data/test_repeated_cl.json +++ b/crates/vim/test_data/test_repeated_cl.json @@ -1 +1 @@ -[{"Text":"he quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quck brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":6}},{"Mode":"Insert"},{"Text":"The quickbrown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":9}},{"Mode":"Insert"},{"Text":"The quick brow\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":14}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox umps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumpsover\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-ver\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-oer\nthe lazy dog\n"},{"Head":{"row":2,"column":11}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nhe lazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":"e quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The quk brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":6}},{"Mode":"Insert"},{"Text":"The quickrown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":9}},{"Mode":"Insert"},{"Text":"The quick brow\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":14}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nx jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox mps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumpsver\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-er\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-or\nthe lazy dog\n"},{"Head":{"row":2,"column":11}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\ne lazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":" quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The qu brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":6}},{"Mode":"Insert"},{"Text":"The quickown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":9}},{"Mode":"Insert"},{"Text":"The quick brow\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":14}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\n jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox ps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumpser\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-r\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o\nthe lazy dog\n"},{"Head":{"row":2,"column":11}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n lazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":"quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The qubrown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":6}},{"Mode":"Insert"},{"Text":"The quickwn\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":9}},{"Mode":"Insert"},{"Text":"The quick brow\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":14}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\njumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox s-over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumpsr\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o\nthe lazy dog\n"},{"Head":{"row":2,"column":11}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nlazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"},{"Text":"uick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":0}},{"Mode":"Insert"},{"Text":"The qurown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":6}},{"Mode":"Insert"},{"Text":"The quickn\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":9}},{"Mode":"Insert"},{"Text":"The quick brow\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":14}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\numps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox -over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o\nthe lazy dog\n"},{"Head":{"row":2,"column":11}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Insert"}] \ No newline at end of file +[{"Text":"he quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quck brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Insert"},{"Text":"The quickbrown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Insert"},{"Text":"The quick brow\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,14],"end":[0,14]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox umps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumpsover\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-ver\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,10],"end":[2,10]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-oer\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nhe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":"e quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quk brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Insert"},{"Text":"The quickrown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Insert"},{"Text":"The quick brow\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,14],"end":[0,14]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nx jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox mps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumpsver\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-er\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,10],"end":[2,10]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-or\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\ne lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":" quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The qu brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Insert"},{"Text":"The quickown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Insert"},{"Text":"The quick brow\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,14],"end":[0,14]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\n jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox ps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumpser\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-r\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,10],"end":[2,10]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\n lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":"quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The qubrown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Insert"},{"Text":"The quickwn\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Insert"},{"Text":"The quick brow\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,14],"end":[0,14]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\njumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox s-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumpsr\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,10],"end":[2,10]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nlazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":"uick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The qurown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Insert"},{"Text":"The quickn\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Insert"},{"Text":"The quick brow\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[0,14],"end":[0,14]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\numps-over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox -over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,10],"end":[2,10]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-o\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Insert"},{"Text":"The quick brown\n\nfox jumps-over\nazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_repeated_word.json b/crates/vim/test_data/test_repeated_word.json index 330ada2013..54caf22a3e 100644 --- a/crates/vim/test_data/test_repeated_word.json +++ b/crates/vim/test_data/test_repeated_word.json @@ -1 +1 @@ -[{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":0,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":2,"column":10}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":4}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":3,"column":9}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"}] \ No newline at end of file +[{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[2,10],"end":[2,10]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[3,4],"end":[3,4]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[2,10],"end":[2,10]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[3,4],"end":[3,4]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[3,4],"end":[3,4]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[3,9],"end":[3,9]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[2,10],"end":[2,10]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[3,4],"end":[3,4]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[3,9],"end":[3,9]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[3,9],"end":[3,9]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[2,10],"end":[2,10]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[3,4],"end":[3,4]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[3,9],"end":[3,9]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[2,9],"end":[2,9]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[2,10],"end":[2,10]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[3,4],"end":[3,4]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[3,9],"end":[3,9]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nfox jumps-over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_visual_delete.json b/crates/vim/test_data/test_visual_delete.json new file mode 100644 index 0000000000..50750714d0 --- /dev/null +++ b/crates/vim/test_data/test_visual_delete.json @@ -0,0 +1 @@ +[{"Text":"The quick "},{"Mode":"Normal"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Normal"},{"Text":"The ver\nthe lazy dog"},{"Mode":"Normal"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Normal"},{"Text":"The ver\nthe lquick brown\nfox jumps oazy dog"},{"Mode":"Normal"},{"Selection":{"start":[1,5],"end":[1,5]}},{"Mode":"Normal"},{"Text":"The ver\nthe lazy dog"},{"Mode":"Normal"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps over\nthe og"},{"Mode":"Normal"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Normal"},{"Text":"uick brown\nfox jumps over\nthe lazy dog"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The ver\nthe lazy dog"},{"Mode":"Normal"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Normal"},{"Text":"The quick brown\nazy dog"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_visual_line_delete.json b/crates/vim/test_data/test_visual_line_delete.json new file mode 100644 index 0000000000..e291fe1034 --- /dev/null +++ b/crates/vim/test_data/test_visual_line_delete.json @@ -0,0 +1 @@ +[{"Text":"fox jumps over\nthe lazy dog"},{"Mode":"Normal"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Normal"},{"Text":"fox jumps over\nThe quick brown\nthe lazy dog"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"},{"Text":"The quick brown\nthe lazy dog"},{"Mode":"Normal"},{"Selection":{"start":[1,6],"end":[1,6]}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps over"},{"Mode":"Normal"},{"Selection":{"start":[1,6],"end":[1,6]}},{"Mode":"Normal"},{"Text":"the lazy dog"},{"Mode":"Normal"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Normal"},{"Text":"the lazy dog\nThe quick brown\nfox jumps over"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"},{"Text":"The quick brown"},{"Mode":"Normal"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps over"},{"Mode":"Normal"},{"Selection":{"start":[1,6],"end":[1,6]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_w.json b/crates/vim/test_data/test_w.json index 73954548f9..b6fadf7ec1 100644 --- a/crates/vim/test_data/test_w.json +++ b/crates/vim/test_data/test_w.json @@ -1 +1 @@ -[{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":0,"column":9}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":0,"column":10}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":2,"column":0}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":3,"column":10}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":4,"column":2}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":4,"column":2}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":1,"column":0}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":2,"column":0}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":3,"column":0}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":3,"column":10}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":4,"column":0}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":4,"column":2}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Head":{"row":4,"column":2}},{"Mode":"Normal"}] \ No newline at end of file +[{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[3,10],"end":[3,10]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[4,2],"end":[4,2]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[4,2],"end":[4,2]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[3,10],"end":[3,10]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[4,2],"end":[4,2]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[4,2],"end":[4,2]}},{"Mode":"Normal"}] \ No newline at end of file From 1af4b263b2a9dd4b56dd4c033823cca9eb6779ed Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Sun, 9 Oct 2022 19:19:40 -0700 Subject: [PATCH 205/314] Implemented page up and page down for the editor --- crates/editor/src/editor.rs | 32 +++++++++++++++++++++++++++----- crates/editor/src/element.rs | 2 ++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 1abe65d482..ee795bb0eb 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -451,6 +451,7 @@ pub struct Editor { leader_replica_id: Option, hover_state: HoverState, link_go_to_definition_state: LinkGoToDefinitionState, + lines: Option, _subscriptions: Vec, } @@ -1052,6 +1053,7 @@ impl Editor { leader_replica_id: None, hover_state: Default::default(), link_go_to_definition_state: Default::default(), + lines: None, _subscriptions: vec![ cx.observe(&buffer, Self::on_buffer_changed), cx.subscribe(&buffer, Self::on_buffer_event), @@ -1163,9 +1165,9 @@ impl Editor { ) { let map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - if scroll_position.y() == 0. { + if scroll_position.y() <= 0. { self.scroll_top_anchor = Anchor::min(); - self.scroll_position = scroll_position; + self.scroll_position = scroll_position.max(vec2f(0., 0.)); } else { let scroll_top_buffer_offset = DisplayPoint::new(scroll_position.y() as u32, 0).to_offset(&map, Bias::Right); @@ -1186,6 +1188,10 @@ impl Editor { cx.notify(); } + fn set_lines(&mut self, lines: f32) { + self.lines = Some(lines) + } + fn set_scroll_top_anchor( &mut self, anchor: Anchor, @@ -5514,12 +5520,28 @@ impl Editor { } } - pub fn page_up(&mut self, _: &PageUp, _: &mut ViewContext) { + pub fn page_up(&mut self, _: &PageUp, cx: &mut ViewContext) { log::info!("Editor::page_up"); + let lines = match self.lines { + Some(lines) => lines, + None => return, + }; + + let cur_position = self.scroll_position(cx); + let new_pos = cur_position - vec2f(0., lines + 1.); + self.set_scroll_position(new_pos, cx); } - pub fn page_down(&mut self, _: &PageDown, _: &mut ViewContext) { - log::info!("Editor::page_down"); + pub fn page_down(&mut self, _: &PageDown, cx: &mut ViewContext) { + log::info!("Editor::page_up"); + let lines = match self.lines { + Some(lines) => lines, + None => return, + }; + + let cur_position = self.scroll_position(cx); + let new_pos = cur_position + vec2f(0., lines - 1.); + self.set_scroll_position(new_pos, cx); } pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext) { diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index acf2e5887c..d1ab64d7b7 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1495,6 +1495,8 @@ impl Element for EditorElement { let mut highlighted_rows = None; let mut highlighted_ranges = Vec::new(); self.update_view(cx.app, |view, cx| { + view.set_lines(size.y() / line_height); + let display_map = view.display_map.update(cx, |map, cx| map.snapshot(cx)); highlighted_rows = view.highlighted_rows(); From 6f4edf6df58390a8ba63309951892f780ccfc7a5 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 10 Oct 2022 09:56:21 +0200 Subject: [PATCH 206/314] Move contact finder into contacts popover --- crates/collab_ui/src/collab_ui.rs | 2 + crates/collab_ui/src/contact_finder.rs | 34 +- crates/collab_ui/src/contacts_popover.rs | 968 +----------------- .../src/incoming_call_notification.rs | 22 +- crates/picker/src/picker.rs | 25 +- crates/theme/src/theme.rs | 33 +- styles/src/styleTree/app.ts | 4 + styles/src/styleTree/contactFinder.ts | 25 +- styles/src/styleTree/contactsPopover.ts | 1 - 9 files changed, 138 insertions(+), 976 deletions(-) diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 421258114e..da2cf77534 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -1,6 +1,7 @@ mod active_call_popover; mod collab_titlebar_item; mod contact_finder; +mod contact_list; mod contact_notification; mod contacts_popover; mod incoming_call_notification; @@ -18,6 +19,7 @@ use workspace::{AppState, JoinProject, ToggleFollow, Workspace}; pub fn init(app_state: Arc, cx: &mut MutableAppContext) { collab_titlebar_item::init(cx); contact_notification::init(cx); + contact_list::init(cx); contact_finder::init(cx); contacts_popover::init(cx); incoming_call_notification::init(cx); diff --git a/crates/collab_ui/src/contact_finder.rs b/crates/collab_ui/src/contact_finder.rs index 6814b7479f..65ebee2797 100644 --- a/crates/collab_ui/src/contact_finder.rs +++ b/crates/collab_ui/src/contact_finder.rs @@ -1,19 +1,15 @@ use client::{ContactRequestStatus, User, UserStore}; use gpui::{ - actions, elements::*, AnyViewHandle, Entity, ModelHandle, MouseState, MutableAppContext, - RenderContext, Task, View, ViewContext, ViewHandle, + elements::*, AnyViewHandle, Entity, ModelHandle, MouseState, MutableAppContext, RenderContext, + Task, View, ViewContext, ViewHandle, }; use picker::{Picker, PickerDelegate}; use settings::Settings; use std::sync::Arc; use util::TryFutureExt; -use workspace::Workspace; - -actions!(contact_finder, [Toggle]); pub fn init(cx: &mut MutableAppContext) { Picker::::init(cx); - cx.add_action(ContactFinder::toggle); } pub struct ContactFinder { @@ -166,34 +162,16 @@ impl PickerDelegate for ContactFinder { } impl ContactFinder { - fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext) { - workspace.toggle_modal(cx, |workspace, cx| { - let finder = cx.add_view(|cx| Self::new(workspace.user_store().clone(), cx)); - cx.subscribe(&finder, Self::on_event).detach(); - finder - }); - } - pub fn new(user_store: ModelHandle, cx: &mut ViewContext) -> Self { let this = cx.weak_handle(); Self { - picker: cx.add_view(|cx| Picker::new(this, cx)), + picker: cx.add_view(|cx| { + Picker::new(this, cx) + .with_theme(|cx| &cx.global::().theme.contact_finder.picker) + }), potential_contacts: Arc::from([]), user_store, selected_index: 0, } } - - fn on_event( - workspace: &mut Workspace, - _: ViewHandle, - event: &Event, - cx: &mut ViewContext, - ) { - match event { - Event::Dismissed => { - workspace.dismiss_modal(cx); - } - } - } } diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index abc8db658b..07ddc487a4 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -1,120 +1,32 @@ -use std::sync::Arc; - -use crate::contact_finder; -use call::ActiveCall; -use client::{Contact, PeerId, User, UserStore}; -use editor::{Cancel, Editor}; -use fuzzy::{match_strings, StringMatchCandidate}; +use crate::{contact_finder::ContactFinder, contact_list::ContactList}; +use client::UserStore; use gpui::{ - elements::*, impl_actions, impl_internal_actions, keymap, AppContext, ClipboardItem, - CursorStyle, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, - View, ViewContext, ViewHandle, + actions, elements::*, Entity, ModelHandle, MutableAppContext, RenderContext, View, ViewContext, + ViewHandle, }; -use menu::{Confirm, SelectNext, SelectPrev}; use project::Project; -use serde::Deserialize; use settings::Settings; -use theme::IconButton; -impl_actions!(contacts_popover, [RemoveContact, RespondToContactRequest]); -impl_internal_actions!(contacts_popover, [ToggleExpanded, Call]); +actions!(contacts_popover, [ToggleContactFinder]); pub fn init(cx: &mut MutableAppContext) { - cx.add_action(ContactsPopover::remove_contact); - cx.add_action(ContactsPopover::respond_to_contact_request); - cx.add_action(ContactsPopover::clear_filter); - cx.add_action(ContactsPopover::select_next); - cx.add_action(ContactsPopover::select_prev); - cx.add_action(ContactsPopover::confirm); - cx.add_action(ContactsPopover::toggle_expanded); - cx.add_action(ContactsPopover::call); -} - -#[derive(Clone, PartialEq)] -struct ToggleExpanded(Section); - -#[derive(Clone, PartialEq)] -struct Call { - recipient_user_id: u64, - initial_project: Option>, -} - -#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)] -enum Section { - ActiveCall, - Requests, - Online, - Offline, -} - -#[derive(Clone)] -enum ContactEntry { - Header(Section), - CallParticipant { user: Arc, is_pending: bool }, - IncomingRequest(Arc), - OutgoingRequest(Arc), - Contact(Arc), -} - -impl PartialEq for ContactEntry { - fn eq(&self, other: &Self) -> bool { - match self { - ContactEntry::Header(section_1) => { - if let ContactEntry::Header(section_2) = other { - return section_1 == section_2; - } - } - ContactEntry::CallParticipant { user: user_1, .. } => { - if let ContactEntry::CallParticipant { user: user_2, .. } = other { - return user_1.id == user_2.id; - } - } - ContactEntry::IncomingRequest(user_1) => { - if let ContactEntry::IncomingRequest(user_2) = other { - return user_1.id == user_2.id; - } - } - ContactEntry::OutgoingRequest(user_1) => { - if let ContactEntry::OutgoingRequest(user_2) = other { - return user_1.id == user_2.id; - } - } - ContactEntry::Contact(contact_1) => { - if let ContactEntry::Contact(contact_2) = other { - return contact_1.user.id == contact_2.user.id; - } - } - } - false - } -} - -#[derive(Clone, Deserialize, PartialEq)] -pub struct RequestContact(pub u64); - -#[derive(Clone, Deserialize, PartialEq)] -pub struct RemoveContact(pub u64); - -#[derive(Clone, Deserialize, PartialEq)] -pub struct RespondToContactRequest { - pub user_id: u64, - pub accept: bool, + cx.add_action(ContactsPopover::toggle_contact_finder); } pub enum Event { Dismissed, } +enum Child { + ContactList(ViewHandle), + ContactFinder(ViewHandle), +} + pub struct ContactsPopover { - entries: Vec, - match_candidates: Vec, - list_state: ListState, + child: Child, project: ModelHandle, user_store: ModelHandle, - filter_editor: ViewHandle, - collapsed_sections: Vec
, - selection: Option, - _subscriptions: Vec, + _subscription: Option, } impl ContactsPopover { @@ -123,729 +35,44 @@ impl ContactsPopover { user_store: ModelHandle, cx: &mut ViewContext, ) -> Self { - let filter_editor = cx.add_view(|cx| { - let mut editor = Editor::single_line( - Some(|theme| theme.contacts_popover.user_query_editor.clone()), - cx, - ); - editor.set_placeholder_text("Filter contacts", cx); - editor - }); - - cx.subscribe(&filter_editor, |this, _, event, cx| { - if let editor::Event::BufferEdited = event { - let query = this.filter_editor.read(cx).text(cx); - if !query.is_empty() { - this.selection.take(); - } - this.update_entries(cx); - if !query.is_empty() { - this.selection = this - .entries - .iter() - .position(|entry| !matches!(entry, ContactEntry::Header(_))); - } - } - }) - .detach(); - - let list_state = ListState::new(0, Orientation::Top, 1000., cx, move |this, ix, cx| { - let theme = cx.global::().theme.clone(); - let is_selected = this.selection == Some(ix); - - match &this.entries[ix] { - ContactEntry::Header(section) => { - let is_collapsed = this.collapsed_sections.contains(section); - Self::render_header( - *section, - &theme.contacts_popover, - is_selected, - is_collapsed, - cx, - ) - } - ContactEntry::CallParticipant { user, is_pending } => { - Self::render_call_participant( - user, - *is_pending, - is_selected, - &theme.contacts_popover, - ) - } - ContactEntry::IncomingRequest(user) => Self::render_contact_request( - user.clone(), - this.user_store.clone(), - &theme.contacts_popover, - true, - is_selected, - cx, - ), - ContactEntry::OutgoingRequest(user) => Self::render_contact_request( - user.clone(), - this.user_store.clone(), - &theme.contacts_popover, - false, - is_selected, - cx, - ), - ContactEntry::Contact(contact) => Self::render_contact( - contact, - &this.project, - &theme.contacts_popover, - is_selected, - cx, - ), - } - }); - - let active_call = ActiveCall::global(cx); - let mut subscriptions = Vec::new(); - subscriptions.push(cx.observe(&user_store, |this, _, cx| this.update_entries(cx))); - subscriptions.push(cx.observe(&active_call, |this, _, cx| this.update_entries(cx))); - let mut this = Self { - list_state, - selection: None, - collapsed_sections: Default::default(), - entries: Default::default(), - match_candidates: Default::default(), - filter_editor, - _subscriptions: subscriptions, + child: Child::ContactList( + cx.add_view(|cx| ContactList::new(project.clone(), user_store.clone(), cx)), + ), project, user_store, + _subscription: None, }; - this.update_entries(cx); + this.show_contact_list(cx); this } - fn remove_contact(&mut self, request: &RemoveContact, cx: &mut ViewContext) { - self.user_store - .update(cx, |store, cx| store.remove_contact(request.0, cx)) - .detach(); - } - - fn respond_to_contact_request( - &mut self, - action: &RespondToContactRequest, - cx: &mut ViewContext, - ) { - self.user_store - .update(cx, |store, cx| { - store.respond_to_contact_request(action.user_id, action.accept, cx) - }) - .detach(); - } - - fn clear_filter(&mut self, _: &Cancel, cx: &mut ViewContext) { - let did_clear = self.filter_editor.update(cx, |editor, cx| { - if editor.buffer().read(cx).len(cx) > 0 { - editor.set_text("", cx); - true - } else { - false - } - }); - if !did_clear { - cx.emit(Event::Dismissed); + fn toggle_contact_finder(&mut self, _: &ToggleContactFinder, cx: &mut ViewContext) { + match &self.child { + Child::ContactList(_) => self.show_contact_finder(cx), + Child::ContactFinder(_) => self.show_contact_list(cx), } } - fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext) { - if let Some(ix) = self.selection { - if self.entries.len() > ix + 1 { - self.selection = Some(ix + 1); - } - } else if !self.entries.is_empty() { - self.selection = Some(0); - } - cx.notify(); - self.list_state.reset(self.entries.len()); - } - - fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext) { - if let Some(ix) = self.selection { - if ix > 0 { - self.selection = Some(ix - 1); - } else { - self.selection = None; - } - } - cx.notify(); - self.list_state.reset(self.entries.len()); - } - - fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext) { - if let Some(selection) = self.selection { - if let Some(entry) = self.entries.get(selection) { - match entry { - ContactEntry::Header(section) => { - let section = *section; - self.toggle_expanded(&ToggleExpanded(section), cx); - } - ContactEntry::Contact(contact) => { - if contact.online && !contact.busy { - self.call( - &Call { - recipient_user_id: contact.user.id, - initial_project: Some(self.project.clone()), - }, - cx, - ); - } - } - _ => {} - } - } - } - } - - fn toggle_expanded(&mut self, action: &ToggleExpanded, cx: &mut ViewContext) { - let section = action.0; - if let Some(ix) = self.collapsed_sections.iter().position(|s| *s == section) { - self.collapsed_sections.remove(ix); - } else { - self.collapsed_sections.push(section); - } - self.update_entries(cx); - } - - fn update_entries(&mut self, cx: &mut ViewContext) { - let user_store = self.user_store.read(cx); - let query = self.filter_editor.read(cx).text(cx); - let executor = cx.background().clone(); - - let prev_selected_entry = self.selection.and_then(|ix| self.entries.get(ix).cloned()); - self.entries.clear(); - - if let Some(room) = ActiveCall::global(cx).read(cx).room() { - let room = room.read(cx); - let mut call_participants = Vec::new(); - - // Populate the active user. - if let Some(user) = user_store.current_user() { - self.match_candidates.clear(); - self.match_candidates.push(StringMatchCandidate { - id: 0, - string: user.github_login.clone(), - char_bag: user.github_login.chars().collect(), - }); - let matches = executor.block(match_strings( - &self.match_candidates, - &query, - true, - usize::MAX, - &Default::default(), - executor.clone(), - )); - if !matches.is_empty() { - call_participants.push(ContactEntry::CallParticipant { - user, - is_pending: false, - }); - } - } - - // Populate remote participants. - self.match_candidates.clear(); - self.match_candidates - .extend( - room.remote_participants() - .iter() - .map(|(peer_id, participant)| StringMatchCandidate { - id: peer_id.0 as usize, - string: participant.user.github_login.clone(), - char_bag: participant.user.github_login.chars().collect(), - }), - ); - let matches = executor.block(match_strings( - &self.match_candidates, - &query, - true, - usize::MAX, - &Default::default(), - executor.clone(), - )); - call_participants.extend(matches.iter().map(|mat| { - ContactEntry::CallParticipant { - user: room.remote_participants()[&PeerId(mat.candidate_id as u32)] - .user - .clone(), - is_pending: false, - } - })); - - // Populate pending participants. - self.match_candidates.clear(); - self.match_candidates - .extend( - room.pending_participants() - .iter() - .enumerate() - .map(|(id, participant)| StringMatchCandidate { - id, - string: participant.github_login.clone(), - char_bag: participant.github_login.chars().collect(), - }), - ); - let matches = executor.block(match_strings( - &self.match_candidates, - &query, - true, - usize::MAX, - &Default::default(), - executor.clone(), - )); - call_participants.extend(matches.iter().map(|mat| ContactEntry::CallParticipant { - user: room.pending_participants()[mat.candidate_id].clone(), - is_pending: true, - })); - - if !call_participants.is_empty() { - self.entries.push(ContactEntry::Header(Section::ActiveCall)); - if !self.collapsed_sections.contains(&Section::ActiveCall) { - self.entries.extend(call_participants); - } - } - } - - let mut request_entries = Vec::new(); - let incoming = user_store.incoming_contact_requests(); - if !incoming.is_empty() { - self.match_candidates.clear(); - self.match_candidates - .extend( - incoming - .iter() - .enumerate() - .map(|(ix, user)| StringMatchCandidate { - id: ix, - string: user.github_login.clone(), - char_bag: user.github_login.chars().collect(), - }), - ); - let matches = executor.block(match_strings( - &self.match_candidates, - &query, - true, - usize::MAX, - &Default::default(), - executor.clone(), - )); - request_entries.extend( - matches - .iter() - .map(|mat| ContactEntry::IncomingRequest(incoming[mat.candidate_id].clone())), - ); - } - - let outgoing = user_store.outgoing_contact_requests(); - if !outgoing.is_empty() { - self.match_candidates.clear(); - self.match_candidates - .extend( - outgoing - .iter() - .enumerate() - .map(|(ix, user)| StringMatchCandidate { - id: ix, - string: user.github_login.clone(), - char_bag: user.github_login.chars().collect(), - }), - ); - let matches = executor.block(match_strings( - &self.match_candidates, - &query, - true, - usize::MAX, - &Default::default(), - executor.clone(), - )); - request_entries.extend( - matches - .iter() - .map(|mat| ContactEntry::OutgoingRequest(outgoing[mat.candidate_id].clone())), - ); - } - - if !request_entries.is_empty() { - self.entries.push(ContactEntry::Header(Section::Requests)); - if !self.collapsed_sections.contains(&Section::Requests) { - self.entries.append(&mut request_entries); - } - } - - let contacts = user_store.contacts(); - if !contacts.is_empty() { - self.match_candidates.clear(); - self.match_candidates - .extend( - contacts - .iter() - .enumerate() - .map(|(ix, contact)| StringMatchCandidate { - id: ix, - string: contact.user.github_login.clone(), - char_bag: contact.user.github_login.chars().collect(), - }), - ); - - let matches = executor.block(match_strings( - &self.match_candidates, - &query, - true, - usize::MAX, - &Default::default(), - executor.clone(), - )); - - let (mut online_contacts, offline_contacts) = matches - .iter() - .partition::, _>(|mat| contacts[mat.candidate_id].online); - if let Some(room) = ActiveCall::global(cx).read(cx).room() { - let room = room.read(cx); - online_contacts.retain(|contact| { - let contact = &contacts[contact.candidate_id]; - !room.contains_participant(contact.user.id) - }); - } - - for (matches, section) in [ - (online_contacts, Section::Online), - (offline_contacts, Section::Offline), - ] { - if !matches.is_empty() { - self.entries.push(ContactEntry::Header(section)); - if !self.collapsed_sections.contains(§ion) { - for mat in matches { - let contact = &contacts[mat.candidate_id]; - self.entries.push(ContactEntry::Contact(contact.clone())); - } - } - } - } - } - - if let Some(prev_selected_entry) = prev_selected_entry { - self.selection.take(); - for (ix, entry) in self.entries.iter().enumerate() { - if *entry == prev_selected_entry { - self.selection = Some(ix); - break; - } - } - } - - self.list_state.reset(self.entries.len()); + fn show_contact_finder(&mut self, cx: &mut ViewContext) { + let child = cx.add_view(|cx| ContactFinder::new(self.user_store.clone(), cx)); + cx.focus(&child); + self._subscription = Some(cx.subscribe(&child, |this, _, event, cx| match event { + crate::contact_finder::Event::Dismissed => this.show_contact_list(cx), + })); + self.child = Child::ContactFinder(child); cx.notify(); } - fn render_call_participant( - user: &User, - is_pending: bool, - is_selected: bool, - theme: &theme::ContactsPopover, - ) -> ElementBox { - Flex::row() - .with_children(user.avatar.clone().map(|avatar| { - Image::new(avatar) - .with_style(theme.contact_avatar) - .aligned() - .left() - .boxed() - })) - .with_child( - Label::new( - user.github_login.clone(), - theme.contact_username.text.clone(), - ) - .contained() - .with_style(theme.contact_username.container) - .aligned() - .left() - .flex(1., true) - .boxed(), - ) - .with_children(if is_pending { - Some( - Label::new("Calling".to_string(), theme.calling_indicator.text.clone()) - .contained() - .with_style(theme.calling_indicator.container) - .aligned() - .boxed(), - ) - } else { - None - }) - .constrained() - .with_height(theme.row_height) - .contained() - .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) - .boxed() - } - - fn render_header( - section: Section, - theme: &theme::ContactsPopover, - is_selected: bool, - is_collapsed: bool, - cx: &mut RenderContext, - ) -> ElementBox { - enum Header {} - - let header_style = theme.header_row.style_for(Default::default(), is_selected); - let text = match section { - Section::ActiveCall => "Call", - Section::Requests => "Requests", - Section::Online => "Online", - Section::Offline => "Offline", - }; - let icon_size = theme.section_icon_size; - MouseEventHandler::
::new(section as usize, cx, |_, _| { - Flex::row() - .with_child( - Svg::new(if is_collapsed { - "icons/chevron_right_8.svg" - } else { - "icons/chevron_down_8.svg" - }) - .with_color(header_style.text.color) - .constrained() - .with_max_width(icon_size) - .with_max_height(icon_size) - .aligned() - .constrained() - .with_width(icon_size) - .boxed(), - ) - .with_child( - Label::new(text.to_string(), header_style.text.clone()) - .aligned() - .left() - .contained() - .with_margin_left(theme.contact_username.container.margin.left) - .flex(1., true) - .boxed(), - ) - .constrained() - .with_height(theme.row_height) - .contained() - .with_style(header_style.container) - .boxed() - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, cx| { - cx.dispatch_action(ToggleExpanded(section)) - }) - .boxed() - } - - fn render_contact( - contact: &Contact, - project: &ModelHandle, - theme: &theme::ContactsPopover, - is_selected: bool, - cx: &mut RenderContext, - ) -> ElementBox { - let online = contact.online; - let busy = contact.busy; - let user_id = contact.user.id; - let initial_project = project.clone(); - let mut element = - MouseEventHandler::::new(contact.user.id as usize, cx, |_, _| { - Flex::row() - .with_children(contact.user.avatar.clone().map(|avatar| { - let status_badge = if contact.online { - Some( - Empty::new() - .collapsed() - .contained() - .with_style(if contact.busy { - theme.contact_status_busy - } else { - theme.contact_status_free - }) - .aligned() - .boxed(), - ) - } else { - None - }; - Stack::new() - .with_child( - Image::new(avatar) - .with_style(theme.contact_avatar) - .aligned() - .left() - .boxed(), - ) - .with_children(status_badge) - .boxed() - })) - .with_child( - Label::new( - contact.user.github_login.clone(), - theme.contact_username.text.clone(), - ) - .contained() - .with_style(theme.contact_username.container) - .aligned() - .left() - .flex(1., true) - .boxed(), - ) - .constrained() - .with_height(theme.row_height) - .contained() - .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) - .boxed() - }) - .on_click(MouseButton::Left, move |_, cx| { - if online && !busy { - cx.dispatch_action(Call { - recipient_user_id: user_id, - initial_project: Some(initial_project.clone()), - }); - } - }); - - if online { - element = element.with_cursor_style(CursorStyle::PointingHand); - } - - element.boxed() - } - - fn render_contact_request( - user: Arc, - user_store: ModelHandle, - theme: &theme::ContactsPopover, - is_incoming: bool, - is_selected: bool, - cx: &mut RenderContext, - ) -> ElementBox { - enum Decline {} - enum Accept {} - enum Cancel {} - - let mut row = Flex::row() - .with_children(user.avatar.clone().map(|avatar| { - Image::new(avatar) - .with_style(theme.contact_avatar) - .aligned() - .left() - .boxed() - })) - .with_child( - Label::new( - user.github_login.clone(), - theme.contact_username.text.clone(), - ) - .contained() - .with_style(theme.contact_username.container) - .aligned() - .left() - .flex(1., true) - .boxed(), - ); - - let user_id = user.id; - let is_contact_request_pending = user_store.read(cx).is_contact_request_pending(&user); - let button_spacing = theme.contact_button_spacing; - - if is_incoming { - row.add_children([ - MouseEventHandler::::new(user.id as usize, cx, |mouse_state, _| { - let button_style = if is_contact_request_pending { - &theme.disabled_button - } else { - theme.contact_button.style_for(mouse_state, false) - }; - render_icon_button(button_style, "icons/x_mark_8.svg") - .aligned() - .boxed() - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, cx| { - cx.dispatch_action(RespondToContactRequest { - user_id, - accept: false, - }) - }) - .contained() - .with_margin_right(button_spacing) - .boxed(), - MouseEventHandler::::new(user.id as usize, cx, |mouse_state, _| { - let button_style = if is_contact_request_pending { - &theme.disabled_button - } else { - theme.contact_button.style_for(mouse_state, false) - }; - render_icon_button(button_style, "icons/check_8.svg") - .aligned() - .flex_float() - .boxed() - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, cx| { - cx.dispatch_action(RespondToContactRequest { - user_id, - accept: true, - }) - }) - .boxed(), - ]); - } else { - row.add_child( - MouseEventHandler::::new(user.id as usize, cx, |mouse_state, _| { - let button_style = if is_contact_request_pending { - &theme.disabled_button - } else { - theme.contact_button.style_for(mouse_state, false) - }; - render_icon_button(button_style, "icons/x_mark_8.svg") - .aligned() - .flex_float() - .boxed() - }) - .with_padding(Padding::uniform(2.)) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, cx| { - cx.dispatch_action(RemoveContact(user_id)) - }) - .flex_float() - .boxed(), - ); - } - - row.constrained() - .with_height(theme.row_height) - .contained() - .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) - .boxed() - } - - fn call(&mut self, action: &Call, cx: &mut ViewContext) { - let recipient_user_id = action.recipient_user_id; - let initial_project = action.initial_project.clone(); - let window_id = cx.window_id(); - - let active_call = ActiveCall::global(cx); - cx.spawn_weak(|_, mut cx| async move { - active_call - .update(&mut cx, |active_call, cx| { - active_call.invite(recipient_user_id, initial_project.clone(), cx) - }) - .await?; - if cx.update(|cx| cx.window_is_active(window_id)) { - active_call - .update(&mut cx, |call, cx| { - call.set_location(initial_project.as_ref(), cx) - }) - .await?; - } - anyhow::Ok(()) - }) - .detach_and_log_err(cx); + fn show_contact_list(&mut self, cx: &mut ViewContext) { + let child = + cx.add_view(|cx| ContactList::new(self.project.clone(), self.user_store.clone(), cx)); + cx.focus(&child); + self._subscription = Some(cx.subscribe(&child, |_, _, event, cx| match event { + crate::contact_list::Event::Dismissed => cx.emit(Event::Dismissed), + })); + self.child = Child::ContactList(child); + cx.notify(); } } @@ -858,97 +85,14 @@ impl View for ContactsPopover { "ContactsPopover" } - fn keymap_context(&self, _: &AppContext) -> keymap::Context { - let mut cx = Self::default_keymap_context(); - cx.set.insert("menu".into()); - cx - } - fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - enum AddContact {} let theme = cx.global::().theme.clone(); + let child = match &self.child { + Child::ContactList(child) => ChildView::new(child), + Child::ContactFinder(child) => ChildView::new(child), + }; - Flex::column() - .with_child( - Flex::row() - .with_child( - ChildView::new(self.filter_editor.clone()) - .contained() - .with_style(theme.contacts_popover.user_query_editor.container) - .flex(1., true) - .boxed(), - ) - .with_child( - MouseEventHandler::::new(0, cx, |_, _| { - Svg::new("icons/user_plus_16.svg") - .with_color(theme.contacts_popover.add_contact_button.color) - .constrained() - .with_height(16.) - .contained() - .with_style(theme.contacts_popover.add_contact_button.container) - .aligned() - .boxed() - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, |_, cx| { - cx.dispatch_action(contact_finder::Toggle) - }) - .boxed(), - ) - .constrained() - .with_height(theme.contacts_popover.user_query_editor_height) - .boxed(), - ) - .with_child(List::new(self.list_state.clone()).flex(1., false).boxed()) - .with_children( - self.user_store - .read(cx) - .invite_info() - .cloned() - .and_then(|info| { - enum InviteLink {} - - if info.count > 0 { - Some( - MouseEventHandler::::new(0, cx, |state, cx| { - let style = theme - .contacts_popover - .invite_row - .style_for(state, false) - .clone(); - - let copied = cx.read_from_clipboard().map_or(false, |item| { - item.text().as_str() == info.url.as_ref() - }); - - Label::new( - format!( - "{} invite link ({} left)", - if copied { "Copied" } else { "Copy" }, - info.count - ), - style.label.clone(), - ) - .aligned() - .left() - .constrained() - .with_height(theme.contacts_popover.row_height) - .contained() - .with_style(style.container) - .boxed() - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, cx| { - cx.write_to_clipboard(ClipboardItem::new(info.url.to_string())); - cx.notify(); - }) - .boxed(), - ) - } else { - None - } - }), - ) + child .contained() .with_style(theme.contacts_popover.container) .constrained() @@ -958,27 +102,11 @@ impl View for ContactsPopover { } fn on_focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext) { - if !self.filter_editor.is_focused(cx) { - cx.focus(&self.filter_editor); - } - } - - fn on_focus_out(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext) { - if !self.filter_editor.is_focused(cx) { - cx.emit(Event::Dismissed); + if cx.is_self_focused() { + match &self.child { + Child::ContactList(child) => cx.focus(child), + Child::ContactFinder(child) => cx.focus(child), + } } } } - -fn render_icon_button(style: &IconButton, svg_path: &'static str) -> impl Element { - Svg::new(svg_path) - .with_color(style.color) - .constrained() - .with_width(style.icon_width) - .aligned() - .contained() - .with_style(style.container) - .constrained() - .with_width(style.button_width) - .with_height(style.button_width) -} diff --git a/crates/collab_ui/src/incoming_call_notification.rs b/crates/collab_ui/src/incoming_call_notification.rs index 0581859ea9..47fe8cbbfb 100644 --- a/crates/collab_ui/src/incoming_call_notification.rs +++ b/crates/collab_ui/src/incoming_call_notification.rs @@ -82,20 +82,22 @@ impl IncomingCallNotification { } fn render_caller(&self, cx: &mut RenderContext) -> ElementBox { - let theme = &cx.global::().theme.contacts_popover; + let theme = &cx.global::().theme.incoming_call_notification; Flex::row() .with_children( self.call .caller .avatar .clone() - .map(|avatar| Image::new(avatar).with_style(theme.contact_avatar).boxed()), + .map(|avatar| Image::new(avatar).with_style(theme.caller_avatar).boxed()), ) .with_child( Label::new( self.call.caller.github_login.clone(), - theme.contact_username.text.clone(), + theme.caller_username.text.clone(), ) + .contained() + .with_style(theme.caller_username.container) .boxed(), ) .boxed() @@ -108,8 +110,11 @@ impl IncomingCallNotification { Flex::row() .with_child( MouseEventHandler::::new(0, cx, |_, cx| { - let theme = &cx.global::().theme.contacts_popover; - Label::new("Accept".to_string(), theme.contact_username.text.clone()).boxed() + let theme = &cx.global::().theme.incoming_call_notification; + Label::new("Accept".to_string(), theme.accept_button.text.clone()) + .contained() + .with_style(theme.accept_button.container) + .boxed() }) .on_click(MouseButton::Left, |_, cx| { cx.dispatch_action(RespondToCall { accept: true }); @@ -118,8 +123,11 @@ impl IncomingCallNotification { ) .with_child( MouseEventHandler::::new(0, cx, |_, cx| { - let theme = &cx.global::().theme.contacts_popover; - Label::new("Decline".to_string(), theme.contact_username.text.clone()).boxed() + let theme = &cx.global::().theme.incoming_call_notification; + Label::new("Decline".to_string(), theme.decline_button.text.clone()) + .contained() + .with_style(theme.decline_button.container) + .boxed() }) .on_click(MouseButton::Left, |_, cx| { cx.dispatch_action(RespondToCall { accept: false }); diff --git a/crates/picker/src/picker.rs b/crates/picker/src/picker.rs index c2fac6371e..622dc13309 100644 --- a/crates/picker/src/picker.rs +++ b/crates/picker/src/picker.rs @@ -19,6 +19,7 @@ pub struct Picker { query_editor: ViewHandle, list_state: UniformListState, max_size: Vector2F, + theme: Box &theme::Picker>, confirmed: bool, } @@ -51,8 +52,8 @@ impl View for Picker { } fn render(&mut self, cx: &mut RenderContext) -> gpui::ElementBox { - let settings = cx.global::(); - let container_style = settings.theme.picker.container; + let theme = (self.theme)(cx); + let container_style = theme.container; let delegate = self.delegate.clone(); let match_count = if let Some(delegate) = delegate.upgrade(cx.app) { delegate.read(cx).match_count() @@ -64,17 +65,14 @@ impl View for Picker { .with_child( ChildView::new(&self.query_editor) .contained() - .with_style(settings.theme.picker.input_editor.container) + .with_style(theme.input_editor.container) .boxed(), ) .with_child( if match_count == 0 { - Label::new( - "No matches".into(), - settings.theme.picker.empty.label.clone(), - ) - .contained() - .with_style(settings.theme.picker.empty.container) + Label::new("No matches".into(), theme.empty.label.clone()) + .contained() + .with_style(theme.empty.container) } else { UniformList::new( self.list_state.clone(), @@ -147,6 +145,7 @@ impl Picker { list_state: Default::default(), delegate, max_size: vec2f(540., 420.), + theme: Box::new(|cx| &cx.global::().theme.picker), confirmed: false, }; cx.defer(|this, cx| { @@ -163,6 +162,14 @@ impl Picker { self } + pub fn with_theme(mut self, theme: F) -> Self + where + F: 'static + FnMut(&AppContext) -> &theme::Picker, + { + self.theme = Box::new(theme); + self + } + pub fn query(&self, cx: &AppContext) -> String { self.query_editor.read(cx).text(cx) } diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 96d5b07582..268a655c23 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -20,6 +20,7 @@ pub struct Theme { pub context_menu: ContextMenu, pub chat_panel: ChatPanel, pub contacts_popover: ContactsPopover, + pub contact_list: ContactList, pub contact_finder: ContactFinder, pub project_panel: ProjectPanel, pub command_palette: CommandPalette, @@ -31,6 +32,7 @@ pub struct Theme { pub contact_notification: ContactNotification, pub update_notification: UpdateNotification, pub project_shared_notification: ProjectSharedNotification, + pub incoming_call_notification: IncomingCallNotification, pub tooltip: TooltipStyle, pub terminal: TerminalStyle, } @@ -87,6 +89,10 @@ pub struct ContactsPopover { pub container: ContainerStyle, pub height: f32, pub width: f32, +} + +#[derive(Deserialize, Default)] +pub struct ContactList { pub user_query_editor: FieldEditor, pub user_query_editor_height: f32, pub add_contact_button: IconButton, @@ -105,6 +111,16 @@ pub struct ContactsPopover { pub calling_indicator: ContainedText, } +#[derive(Deserialize, Default)] +pub struct ContactFinder { + pub picker: Picker, + pub row_height: f32, + pub contact_avatar: ImageStyle, + pub contact_username: ContainerStyle, + pub contact_button: IconButton, + pub disabled_contact_button: IconButton, +} + #[derive(Clone, Deserialize, Default)] pub struct TabBar { #[serde(flatten)] @@ -353,15 +369,6 @@ pub struct InviteLink { pub icon: Icon, } -#[derive(Deserialize, Default)] -pub struct ContactFinder { - pub row_height: f32, - pub contact_avatar: ImageStyle, - pub contact_username: ContainerStyle, - pub contact_button: IconButton, - pub disabled_contact_button: IconButton, -} - #[derive(Deserialize, Default)] pub struct Icon { #[serde(flatten)] @@ -469,6 +476,14 @@ pub struct ProjectSharedNotification { pub dismiss_button: ContainedText, } +#[derive(Deserialize, Default)] +pub struct IncomingCallNotification { + pub caller_avatar: ImageStyle, + pub caller_username: ContainedText, + pub accept_button: ContainedText, + pub decline_button: ContainedText, +} + #[derive(Clone, Deserialize, Default)] pub struct Editor { pub text_color: Color, diff --git a/styles/src/styleTree/app.ts b/styles/src/styleTree/app.ts index 1b1aa26916..f540074a70 100644 --- a/styles/src/styleTree/app.ts +++ b/styles/src/styleTree/app.ts @@ -16,6 +16,8 @@ import updateNotification from "./updateNotification"; import projectSharedNotification from "./projectSharedNotification"; import tooltip from "./tooltip"; import terminal from "./terminal"; +import contactList from "./contactList"; +import incomingCallNotification from "./incomingCallNotification"; export const panel = { padding: { top: 12, bottom: 12 }, @@ -36,6 +38,7 @@ export default function app(theme: Theme): Object { projectPanel: projectPanel(theme), chatPanel: chatPanel(theme), contactsPopover: contactsPopover(theme), + contactList: contactList(theme), contactFinder: contactFinder(theme), search: search(theme), breadcrumbs: { @@ -47,6 +50,7 @@ export default function app(theme: Theme): Object { contactNotification: contactNotification(theme), updateNotification: updateNotification(theme), projectSharedNotification: projectSharedNotification(theme), + incomingCallNotification: incomingCallNotification(theme), tooltip: tooltip(theme), terminal: terminal(theme), }; diff --git a/styles/src/styleTree/contactFinder.ts b/styles/src/styleTree/contactFinder.ts index e34fac4b2d..bf43a74666 100644 --- a/styles/src/styleTree/contactFinder.ts +++ b/styles/src/styleTree/contactFinder.ts @@ -1,6 +1,6 @@ import Theme from "../themes/common/theme"; import picker from "./picker"; -import { backgroundColor, iconColor } from "./components"; +import { backgroundColor, border, iconColor, player, text } from "./components"; export default function contactFinder(theme: Theme) { const contactButton = { @@ -12,7 +12,28 @@ export default function contactFinder(theme: Theme) { }; return { - ...picker(theme), + picker: { + item: picker(theme).item, + empty: picker(theme).empty, + inputEditor: { + background: backgroundColor(theme, 500), + cornerRadius: 6, + text: text(theme, "mono", "primary"), + placeholderText: text(theme, "mono", "placeholder", { size: "sm" }), + selection: player(theme, 1).selection, + border: border(theme, "secondary"), + padding: { + bottom: 4, + left: 8, + right: 8, + top: 4, + }, + margin: { + left: 12, + right: 12, + } + } + }, rowHeight: 28, contactAvatar: { cornerRadius: 10, diff --git a/styles/src/styleTree/contactsPopover.ts b/styles/src/styleTree/contactsPopover.ts index 8786e81f5c..57af5a6d4d 100644 --- a/styles/src/styleTree/contactsPopover.ts +++ b/styles/src/styleTree/contactsPopover.ts @@ -19,7 +19,6 @@ export default function contactsPopover(theme: Theme) { padding: { top: 6 }, shadow: popoverShadow(theme), border: border(theme, "primary"), - margin: { top: -5 }, width: 250, height: 300, userQueryEditor: { From 79748803a90fb2418cc391ed8b7a3d710393c9cd Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 10 Oct 2022 10:30:51 +0200 Subject: [PATCH 207/314] Add leave button on active call header --- crates/call/src/call.rs | 1 + crates/collab_ui/src/contact_list.rs | 1008 +++++++++++++++++ crates/theme/src/theme.rs | 1 + styles/src/styleTree/contactList.ts | 134 +++ .../src/styleTree/incomingCallNotification.ts | 22 + 5 files changed, 1166 insertions(+) create mode 100644 crates/collab_ui/src/contact_list.rs create mode 100644 styles/src/styleTree/contactList.ts create mode 100644 styles/src/styleTree/incomingCallNotification.ts diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 7015173ce7..99edb33b6e 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -201,6 +201,7 @@ impl ActiveCall { pub fn hang_up(&mut self, cx: &mut ModelContext) -> Result<()> { if let Some((room, _)) = self.room.take() { room.update(cx, |room, cx| room.leave(cx))?; + cx.notify(); } Ok(()) } diff --git a/crates/collab_ui/src/contact_list.rs b/crates/collab_ui/src/contact_list.rs new file mode 100644 index 0000000000..a539f8ffac --- /dev/null +++ b/crates/collab_ui/src/contact_list.rs @@ -0,0 +1,1008 @@ +use std::sync::Arc; + +use crate::contacts_popover; +use call::ActiveCall; +use client::{Contact, PeerId, User, UserStore}; +use editor::{Cancel, Editor}; +use fuzzy::{match_strings, StringMatchCandidate}; +use gpui::{ + elements::*, impl_actions, impl_internal_actions, keymap, AppContext, ClipboardItem, + CursorStyle, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, + View, ViewContext, ViewHandle, +}; +use menu::{Confirm, SelectNext, SelectPrev}; +use project::Project; +use serde::Deserialize; +use settings::Settings; +use theme::IconButton; +use util::ResultExt; + +impl_actions!(contact_list, [RemoveContact, RespondToContactRequest]); +impl_internal_actions!(contact_list, [ToggleExpanded, Call, LeaveCall]); + +pub fn init(cx: &mut MutableAppContext) { + cx.add_action(ContactList::remove_contact); + cx.add_action(ContactList::respond_to_contact_request); + cx.add_action(ContactList::clear_filter); + cx.add_action(ContactList::select_next); + cx.add_action(ContactList::select_prev); + cx.add_action(ContactList::confirm); + cx.add_action(ContactList::toggle_expanded); + cx.add_action(ContactList::call); + cx.add_action(ContactList::leave_call); +} + +#[derive(Clone, PartialEq)] +struct ToggleExpanded(Section); + +#[derive(Clone, PartialEq)] +struct Call { + recipient_user_id: u64, + initial_project: Option>, +} + +#[derive(Copy, Clone, PartialEq)] +struct LeaveCall; + +#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)] +enum Section { + ActiveCall, + Requests, + Online, + Offline, +} + +#[derive(Clone)] +enum ContactEntry { + Header(Section), + CallParticipant { user: Arc, is_pending: bool }, + IncomingRequest(Arc), + OutgoingRequest(Arc), + Contact(Arc), +} + +impl PartialEq for ContactEntry { + fn eq(&self, other: &Self) -> bool { + match self { + ContactEntry::Header(section_1) => { + if let ContactEntry::Header(section_2) = other { + return section_1 == section_2; + } + } + ContactEntry::CallParticipant { user: user_1, .. } => { + if let ContactEntry::CallParticipant { user: user_2, .. } = other { + return user_1.id == user_2.id; + } + } + ContactEntry::IncomingRequest(user_1) => { + if let ContactEntry::IncomingRequest(user_2) = other { + return user_1.id == user_2.id; + } + } + ContactEntry::OutgoingRequest(user_1) => { + if let ContactEntry::OutgoingRequest(user_2) = other { + return user_1.id == user_2.id; + } + } + ContactEntry::Contact(contact_1) => { + if let ContactEntry::Contact(contact_2) = other { + return contact_1.user.id == contact_2.user.id; + } + } + } + false + } +} + +#[derive(Clone, Deserialize, PartialEq)] +pub struct RequestContact(pub u64); + +#[derive(Clone, Deserialize, PartialEq)] +pub struct RemoveContact(pub u64); + +#[derive(Clone, Deserialize, PartialEq)] +pub struct RespondToContactRequest { + pub user_id: u64, + pub accept: bool, +} + +pub enum Event { + Dismissed, +} + +pub struct ContactList { + entries: Vec, + match_candidates: Vec, + list_state: ListState, + project: ModelHandle, + user_store: ModelHandle, + filter_editor: ViewHandle, + collapsed_sections: Vec
, + selection: Option, + _subscriptions: Vec, +} + +impl ContactList { + pub fn new( + project: ModelHandle, + user_store: ModelHandle, + cx: &mut ViewContext, + ) -> Self { + let filter_editor = cx.add_view(|cx| { + let mut editor = Editor::single_line( + Some(|theme| theme.contact_list.user_query_editor.clone()), + cx, + ); + editor.set_placeholder_text("Filter contacts", cx); + editor + }); + + cx.subscribe(&filter_editor, |this, _, event, cx| { + if let editor::Event::BufferEdited = event { + let query = this.filter_editor.read(cx).text(cx); + if !query.is_empty() { + this.selection.take(); + } + this.update_entries(cx); + if !query.is_empty() { + this.selection = this + .entries + .iter() + .position(|entry| !matches!(entry, ContactEntry::Header(_))); + } + } + }) + .detach(); + + let list_state = ListState::new(0, Orientation::Top, 1000., cx, move |this, ix, cx| { + let theme = cx.global::().theme.clone(); + let is_selected = this.selection == Some(ix); + + match &this.entries[ix] { + ContactEntry::Header(section) => { + let is_collapsed = this.collapsed_sections.contains(section); + Self::render_header( + *section, + &theme.contact_list, + is_selected, + is_collapsed, + cx, + ) + } + ContactEntry::CallParticipant { user, is_pending } => { + Self::render_call_participant( + user, + *is_pending, + is_selected, + &theme.contact_list, + ) + } + ContactEntry::IncomingRequest(user) => Self::render_contact_request( + user.clone(), + this.user_store.clone(), + &theme.contact_list, + true, + is_selected, + cx, + ), + ContactEntry::OutgoingRequest(user) => Self::render_contact_request( + user.clone(), + this.user_store.clone(), + &theme.contact_list, + false, + is_selected, + cx, + ), + ContactEntry::Contact(contact) => Self::render_contact( + contact, + &this.project, + &theme.contact_list, + is_selected, + cx, + ), + } + }); + + let active_call = ActiveCall::global(cx); + let mut subscriptions = Vec::new(); + subscriptions.push(cx.observe(&user_store, |this, _, cx| this.update_entries(cx))); + subscriptions.push(cx.observe(&active_call, |this, _, cx| this.update_entries(cx))); + + let mut this = Self { + list_state, + selection: None, + collapsed_sections: Default::default(), + entries: Default::default(), + match_candidates: Default::default(), + filter_editor, + _subscriptions: subscriptions, + project, + user_store, + }; + this.update_entries(cx); + this + } + + fn remove_contact(&mut self, request: &RemoveContact, cx: &mut ViewContext) { + self.user_store + .update(cx, |store, cx| store.remove_contact(request.0, cx)) + .detach(); + } + + fn respond_to_contact_request( + &mut self, + action: &RespondToContactRequest, + cx: &mut ViewContext, + ) { + self.user_store + .update(cx, |store, cx| { + store.respond_to_contact_request(action.user_id, action.accept, cx) + }) + .detach(); + } + + fn clear_filter(&mut self, _: &Cancel, cx: &mut ViewContext) { + let did_clear = self.filter_editor.update(cx, |editor, cx| { + if editor.buffer().read(cx).len(cx) > 0 { + editor.set_text("", cx); + true + } else { + false + } + }); + if !did_clear { + cx.emit(Event::Dismissed); + } + } + + fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext) { + if let Some(ix) = self.selection { + if self.entries.len() > ix + 1 { + self.selection = Some(ix + 1); + } + } else if !self.entries.is_empty() { + self.selection = Some(0); + } + cx.notify(); + self.list_state.reset(self.entries.len()); + } + + fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext) { + if let Some(ix) = self.selection { + if ix > 0 { + self.selection = Some(ix - 1); + } else { + self.selection = None; + } + } + cx.notify(); + self.list_state.reset(self.entries.len()); + } + + fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext) { + if let Some(selection) = self.selection { + if let Some(entry) = self.entries.get(selection) { + match entry { + ContactEntry::Header(section) => { + let section = *section; + self.toggle_expanded(&ToggleExpanded(section), cx); + } + ContactEntry::Contact(contact) => { + if contact.online && !contact.busy { + self.call( + &Call { + recipient_user_id: contact.user.id, + initial_project: Some(self.project.clone()), + }, + cx, + ); + } + } + _ => {} + } + } + } + } + + fn toggle_expanded(&mut self, action: &ToggleExpanded, cx: &mut ViewContext) { + let section = action.0; + if let Some(ix) = self.collapsed_sections.iter().position(|s| *s == section) { + self.collapsed_sections.remove(ix); + } else { + self.collapsed_sections.push(section); + } + self.update_entries(cx); + } + + fn update_entries(&mut self, cx: &mut ViewContext) { + let user_store = self.user_store.read(cx); + let query = self.filter_editor.read(cx).text(cx); + let executor = cx.background().clone(); + + let prev_selected_entry = self.selection.and_then(|ix| self.entries.get(ix).cloned()); + self.entries.clear(); + + if let Some(room) = ActiveCall::global(cx).read(cx).room() { + let room = room.read(cx); + let mut call_participants = Vec::new(); + + // Populate the active user. + if let Some(user) = user_store.current_user() { + self.match_candidates.clear(); + self.match_candidates.push(StringMatchCandidate { + id: 0, + string: user.github_login.clone(), + char_bag: user.github_login.chars().collect(), + }); + let matches = executor.block(match_strings( + &self.match_candidates, + &query, + true, + usize::MAX, + &Default::default(), + executor.clone(), + )); + if !matches.is_empty() { + call_participants.push(ContactEntry::CallParticipant { + user, + is_pending: false, + }); + } + } + + // Populate remote participants. + self.match_candidates.clear(); + self.match_candidates + .extend( + room.remote_participants() + .iter() + .map(|(peer_id, participant)| StringMatchCandidate { + id: peer_id.0 as usize, + string: participant.user.github_login.clone(), + char_bag: participant.user.github_login.chars().collect(), + }), + ); + let matches = executor.block(match_strings( + &self.match_candidates, + &query, + true, + usize::MAX, + &Default::default(), + executor.clone(), + )); + call_participants.extend(matches.iter().map(|mat| { + ContactEntry::CallParticipant { + user: room.remote_participants()[&PeerId(mat.candidate_id as u32)] + .user + .clone(), + is_pending: false, + } + })); + + // Populate pending participants. + self.match_candidates.clear(); + self.match_candidates + .extend( + room.pending_participants() + .iter() + .enumerate() + .map(|(id, participant)| StringMatchCandidate { + id, + string: participant.github_login.clone(), + char_bag: participant.github_login.chars().collect(), + }), + ); + let matches = executor.block(match_strings( + &self.match_candidates, + &query, + true, + usize::MAX, + &Default::default(), + executor.clone(), + )); + call_participants.extend(matches.iter().map(|mat| ContactEntry::CallParticipant { + user: room.pending_participants()[mat.candidate_id].clone(), + is_pending: true, + })); + + if !call_participants.is_empty() { + self.entries.push(ContactEntry::Header(Section::ActiveCall)); + if !self.collapsed_sections.contains(&Section::ActiveCall) { + self.entries.extend(call_participants); + } + } + } + + let mut request_entries = Vec::new(); + let incoming = user_store.incoming_contact_requests(); + if !incoming.is_empty() { + self.match_candidates.clear(); + self.match_candidates + .extend( + incoming + .iter() + .enumerate() + .map(|(ix, user)| StringMatchCandidate { + id: ix, + string: user.github_login.clone(), + char_bag: user.github_login.chars().collect(), + }), + ); + let matches = executor.block(match_strings( + &self.match_candidates, + &query, + true, + usize::MAX, + &Default::default(), + executor.clone(), + )); + request_entries.extend( + matches + .iter() + .map(|mat| ContactEntry::IncomingRequest(incoming[mat.candidate_id].clone())), + ); + } + + let outgoing = user_store.outgoing_contact_requests(); + if !outgoing.is_empty() { + self.match_candidates.clear(); + self.match_candidates + .extend( + outgoing + .iter() + .enumerate() + .map(|(ix, user)| StringMatchCandidate { + id: ix, + string: user.github_login.clone(), + char_bag: user.github_login.chars().collect(), + }), + ); + let matches = executor.block(match_strings( + &self.match_candidates, + &query, + true, + usize::MAX, + &Default::default(), + executor.clone(), + )); + request_entries.extend( + matches + .iter() + .map(|mat| ContactEntry::OutgoingRequest(outgoing[mat.candidate_id].clone())), + ); + } + + if !request_entries.is_empty() { + self.entries.push(ContactEntry::Header(Section::Requests)); + if !self.collapsed_sections.contains(&Section::Requests) { + self.entries.append(&mut request_entries); + } + } + + let contacts = user_store.contacts(); + if !contacts.is_empty() { + self.match_candidates.clear(); + self.match_candidates + .extend( + contacts + .iter() + .enumerate() + .map(|(ix, contact)| StringMatchCandidate { + id: ix, + string: contact.user.github_login.clone(), + char_bag: contact.user.github_login.chars().collect(), + }), + ); + + let matches = executor.block(match_strings( + &self.match_candidates, + &query, + true, + usize::MAX, + &Default::default(), + executor.clone(), + )); + + let (mut online_contacts, offline_contacts) = matches + .iter() + .partition::, _>(|mat| contacts[mat.candidate_id].online); + if let Some(room) = ActiveCall::global(cx).read(cx).room() { + let room = room.read(cx); + online_contacts.retain(|contact| { + let contact = &contacts[contact.candidate_id]; + !room.contains_participant(contact.user.id) + }); + } + + for (matches, section) in [ + (online_contacts, Section::Online), + (offline_contacts, Section::Offline), + ] { + if !matches.is_empty() { + self.entries.push(ContactEntry::Header(section)); + if !self.collapsed_sections.contains(§ion) { + for mat in matches { + let contact = &contacts[mat.candidate_id]; + self.entries.push(ContactEntry::Contact(contact.clone())); + } + } + } + } + } + + if let Some(prev_selected_entry) = prev_selected_entry { + self.selection.take(); + for (ix, entry) in self.entries.iter().enumerate() { + if *entry == prev_selected_entry { + self.selection = Some(ix); + break; + } + } + } + + self.list_state.reset(self.entries.len()); + cx.notify(); + } + + fn render_call_participant( + user: &User, + is_pending: bool, + is_selected: bool, + theme: &theme::ContactList, + ) -> ElementBox { + Flex::row() + .with_children(user.avatar.clone().map(|avatar| { + Image::new(avatar) + .with_style(theme.contact_avatar) + .aligned() + .left() + .boxed() + })) + .with_child( + Label::new( + user.github_login.clone(), + theme.contact_username.text.clone(), + ) + .contained() + .with_style(theme.contact_username.container) + .aligned() + .left() + .flex(1., true) + .boxed(), + ) + .with_children(if is_pending { + Some( + Label::new("Calling".to_string(), theme.calling_indicator.text.clone()) + .contained() + .with_style(theme.calling_indicator.container) + .aligned() + .boxed(), + ) + } else { + None + }) + .constrained() + .with_height(theme.row_height) + .contained() + .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) + .boxed() + } + + fn render_header( + section: Section, + theme: &theme::ContactList, + is_selected: bool, + is_collapsed: bool, + cx: &mut RenderContext, + ) -> ElementBox { + enum Header {} + + let header_style = theme.header_row.style_for(Default::default(), is_selected); + let text = match section { + Section::ActiveCall => "Call", + Section::Requests => "Requests", + Section::Online => "Online", + Section::Offline => "Offline", + }; + let leave_call = if section == Section::ActiveCall { + Some( + MouseEventHandler::::new(0, cx, |state, _| { + let style = theme.leave_call.style_for(state, false); + Label::new("Leave".into(), style.text.clone()) + .contained() + .with_style(style.container) + .boxed() + }) + .on_click(MouseButton::Left, |_, cx| cx.dispatch_action(LeaveCall)) + .aligned() + .boxed(), + ) + } else { + None + }; + + let icon_size = theme.section_icon_size; + MouseEventHandler::
::new(section as usize, cx, |_, _| { + Flex::row() + .with_child( + Svg::new(if is_collapsed { + "icons/chevron_right_8.svg" + } else { + "icons/chevron_down_8.svg" + }) + .with_color(header_style.text.color) + .constrained() + .with_max_width(icon_size) + .with_max_height(icon_size) + .aligned() + .constrained() + .with_width(icon_size) + .boxed(), + ) + .with_child( + Label::new(text.to_string(), header_style.text.clone()) + .aligned() + .left() + .contained() + .with_margin_left(theme.contact_username.container.margin.left) + .flex(1., true) + .boxed(), + ) + .with_children(leave_call) + .constrained() + .with_height(theme.row_height) + .contained() + .with_style(header_style.container) + .boxed() + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, cx| { + cx.dispatch_action(ToggleExpanded(section)) + }) + .boxed() + } + + fn render_contact( + contact: &Contact, + project: &ModelHandle, + theme: &theme::ContactList, + is_selected: bool, + cx: &mut RenderContext, + ) -> ElementBox { + let online = contact.online; + let busy = contact.busy; + let user_id = contact.user.id; + let initial_project = project.clone(); + let mut element = + MouseEventHandler::::new(contact.user.id as usize, cx, |_, _| { + Flex::row() + .with_children(contact.user.avatar.clone().map(|avatar| { + let status_badge = if contact.online { + Some( + Empty::new() + .collapsed() + .contained() + .with_style(if contact.busy { + theme.contact_status_busy + } else { + theme.contact_status_free + }) + .aligned() + .boxed(), + ) + } else { + None + }; + Stack::new() + .with_child( + Image::new(avatar) + .with_style(theme.contact_avatar) + .aligned() + .left() + .boxed(), + ) + .with_children(status_badge) + .boxed() + })) + .with_child( + Label::new( + contact.user.github_login.clone(), + theme.contact_username.text.clone(), + ) + .contained() + .with_style(theme.contact_username.container) + .aligned() + .left() + .flex(1., true) + .boxed(), + ) + .constrained() + .with_height(theme.row_height) + .contained() + .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) + .boxed() + }) + .on_click(MouseButton::Left, move |_, cx| { + if online && !busy { + cx.dispatch_action(Call { + recipient_user_id: user_id, + initial_project: Some(initial_project.clone()), + }); + } + }); + + if online { + element = element.with_cursor_style(CursorStyle::PointingHand); + } + + element.boxed() + } + + fn render_contact_request( + user: Arc, + user_store: ModelHandle, + theme: &theme::ContactList, + is_incoming: bool, + is_selected: bool, + cx: &mut RenderContext, + ) -> ElementBox { + enum Decline {} + enum Accept {} + enum Cancel {} + + let mut row = Flex::row() + .with_children(user.avatar.clone().map(|avatar| { + Image::new(avatar) + .with_style(theme.contact_avatar) + .aligned() + .left() + .boxed() + })) + .with_child( + Label::new( + user.github_login.clone(), + theme.contact_username.text.clone(), + ) + .contained() + .with_style(theme.contact_username.container) + .aligned() + .left() + .flex(1., true) + .boxed(), + ); + + let user_id = user.id; + let is_contact_request_pending = user_store.read(cx).is_contact_request_pending(&user); + let button_spacing = theme.contact_button_spacing; + + if is_incoming { + row.add_children([ + MouseEventHandler::::new(user.id as usize, cx, |mouse_state, _| { + let button_style = if is_contact_request_pending { + &theme.disabled_button + } else { + theme.contact_button.style_for(mouse_state, false) + }; + render_icon_button(button_style, "icons/x_mark_8.svg") + .aligned() + .boxed() + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, cx| { + cx.dispatch_action(RespondToContactRequest { + user_id, + accept: false, + }) + }) + .contained() + .with_margin_right(button_spacing) + .boxed(), + MouseEventHandler::::new(user.id as usize, cx, |mouse_state, _| { + let button_style = if is_contact_request_pending { + &theme.disabled_button + } else { + theme.contact_button.style_for(mouse_state, false) + }; + render_icon_button(button_style, "icons/check_8.svg") + .aligned() + .flex_float() + .boxed() + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, cx| { + cx.dispatch_action(RespondToContactRequest { + user_id, + accept: true, + }) + }) + .boxed(), + ]); + } else { + row.add_child( + MouseEventHandler::::new(user.id as usize, cx, |mouse_state, _| { + let button_style = if is_contact_request_pending { + &theme.disabled_button + } else { + theme.contact_button.style_for(mouse_state, false) + }; + render_icon_button(button_style, "icons/x_mark_8.svg") + .aligned() + .flex_float() + .boxed() + }) + .with_padding(Padding::uniform(2.)) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, cx| { + cx.dispatch_action(RemoveContact(user_id)) + }) + .flex_float() + .boxed(), + ); + } + + row.constrained() + .with_height(theme.row_height) + .contained() + .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) + .boxed() + } + + fn call(&mut self, action: &Call, cx: &mut ViewContext) { + let recipient_user_id = action.recipient_user_id; + let initial_project = action.initial_project.clone(); + let window_id = cx.window_id(); + + let active_call = ActiveCall::global(cx); + cx.spawn_weak(|_, mut cx| async move { + active_call + .update(&mut cx, |active_call, cx| { + active_call.invite(recipient_user_id, initial_project.clone(), cx) + }) + .await?; + if cx.update(|cx| cx.window_is_active(window_id)) { + active_call + .update(&mut cx, |call, cx| { + call.set_location(initial_project.as_ref(), cx) + }) + .await?; + } + anyhow::Ok(()) + }) + .detach_and_log_err(cx); + } + + fn leave_call(&mut self, _: &LeaveCall, cx: &mut ViewContext) { + ActiveCall::global(cx) + .update(cx, |call, cx| call.hang_up(cx)) + .log_err(); + } +} + +impl Entity for ContactList { + type Event = Event; +} + +impl View for ContactList { + fn ui_name() -> &'static str { + "ContactList" + } + + fn keymap_context(&self, _: &AppContext) -> keymap::Context { + let mut cx = Self::default_keymap_context(); + cx.set.insert("menu".into()); + cx + } + + fn render(&mut self, cx: &mut RenderContext) -> ElementBox { + enum AddContact {} + let theme = cx.global::().theme.clone(); + + Flex::column() + .with_child( + Flex::row() + .with_child( + ChildView::new(self.filter_editor.clone()) + .contained() + .with_style(theme.contact_list.user_query_editor.container) + .flex(1., true) + .boxed(), + ) + .with_child( + MouseEventHandler::::new(0, cx, |_, _| { + Svg::new("icons/user_plus_16.svg") + .with_color(theme.contact_list.add_contact_button.color) + .constrained() + .with_height(16.) + .contained() + .with_style(theme.contact_list.add_contact_button.container) + .aligned() + .boxed() + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, |_, cx| { + cx.dispatch_action(contacts_popover::ToggleContactFinder) + }) + .boxed(), + ) + .constrained() + .with_height(theme.contact_list.user_query_editor_height) + .boxed(), + ) + .with_child(List::new(self.list_state.clone()).flex(1., false).boxed()) + .with_children( + self.user_store + .read(cx) + .invite_info() + .cloned() + .and_then(|info| { + enum InviteLink {} + + if info.count > 0 { + Some( + MouseEventHandler::::new(0, cx, |state, cx| { + let style = theme + .contact_list + .invite_row + .style_for(state, false) + .clone(); + + let copied = cx.read_from_clipboard().map_or(false, |item| { + item.text().as_str() == info.url.as_ref() + }); + + Label::new( + format!( + "{} invite link ({} left)", + if copied { "Copied" } else { "Copy" }, + info.count + ), + style.label.clone(), + ) + .aligned() + .left() + .constrained() + .with_height(theme.contact_list.row_height) + .contained() + .with_style(style.container) + .boxed() + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, cx| { + cx.write_to_clipboard(ClipboardItem::new(info.url.to_string())); + cx.notify(); + }) + .boxed(), + ) + } else { + None + } + }), + ) + .boxed() + } + + fn on_focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext) { + if !self.filter_editor.is_focused(cx) { + cx.focus(&self.filter_editor); + } + } + + fn on_focus_out(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext) { + if !self.filter_editor.is_focused(cx) { + cx.emit(Event::Dismissed); + } + } +} + +fn render_icon_button(style: &IconButton, svg_path: &'static str) -> impl Element { + Svg::new(svg_path) + .with_color(style.color) + .constrained() + .with_width(style.icon_width) + .aligned() + .contained() + .with_style(style.container) + .constrained() + .with_width(style.button_width) + .with_height(style.button_width) +} diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 268a655c23..d69720322e 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -97,6 +97,7 @@ pub struct ContactList { pub user_query_editor_height: f32, pub add_contact_button: IconButton, pub header_row: Interactive, + pub leave_call: Interactive, pub contact_row: Interactive, pub row_height: f32, pub contact_avatar: ImageStyle, diff --git a/styles/src/styleTree/contactList.ts b/styles/src/styleTree/contactList.ts new file mode 100644 index 0000000000..f2430d31e6 --- /dev/null +++ b/styles/src/styleTree/contactList.ts @@ -0,0 +1,134 @@ +import Theme from "../themes/common/theme"; +import { backgroundColor, border, borderColor, iconColor, player, text } from "./components"; + +export default function contactList(theme: Theme) { + const nameMargin = 8; + const sidePadding = 12; + + const contactButton = { + background: backgroundColor(theme, 100), + color: iconColor(theme, "primary"), + iconWidth: 8, + buttonWidth: 16, + cornerRadius: 8, + }; + + return { + userQueryEditor: { + background: backgroundColor(theme, 500), + cornerRadius: 6, + text: text(theme, "mono", "primary"), + placeholderText: text(theme, "mono", "placeholder", { size: "sm" }), + selection: player(theme, 1).selection, + border: border(theme, "secondary"), + padding: { + bottom: 4, + left: 8, + right: 8, + top: 4, + }, + margin: { + left: sidePadding, + right: sidePadding, + }, + }, + userQueryEditorHeight: 33, + addContactButton: { + margin: { left: 6, right: 12 }, + color: iconColor(theme, "primary"), + buttonWidth: 16, + iconWidth: 16, + }, + rowHeight: 28, + sectionIconSize: 8, + headerRow: { + ...text(theme, "mono", "secondary", { size: "sm" }), + margin: { top: 14 }, + padding: { + left: sidePadding, + right: sidePadding, + }, + active: { + ...text(theme, "mono", "primary", { size: "sm" }), + background: backgroundColor(theme, 100, "active"), + }, + }, + leaveCall: { + background: backgroundColor(theme, 100), + border: border(theme, "secondary"), + cornerRadius: 6, + margin: { + top: 1, + }, + padding: { + top: 1, + bottom: 1, + left: 7, + right: 7, + }, + ...text(theme, "sans", "secondary", { size: "xs" }), + hover: { + ...text(theme, "sans", "active", { size: "xs" }), + background: backgroundColor(theme, "on300", "hovered"), + border: border(theme, "primary"), + }, + }, + contactRow: { + padding: { + left: sidePadding, + right: sidePadding, + }, + active: { + background: backgroundColor(theme, 100, "active"), + }, + }, + contactAvatar: { + cornerRadius: 10, + width: 18, + }, + contactStatusFree: { + cornerRadius: 4, + padding: 4, + margin: { top: 12, left: 12 }, + background: iconColor(theme, "success"), + }, + contactStatusBusy: { + cornerRadius: 4, + padding: 4, + margin: { top: 12, left: 12 }, + background: iconColor(theme, "warning"), + }, + contactUsername: { + ...text(theme, "mono", "primary", { size: "sm" }), + margin: { + left: nameMargin, + }, + }, + contactButtonSpacing: nameMargin, + contactButton: { + ...contactButton, + hover: { + background: backgroundColor(theme, "on300", "hovered"), + }, + }, + disabledButton: { + ...contactButton, + background: backgroundColor(theme, 100), + color: iconColor(theme, "muted"), + }, + inviteRow: { + padding: { + left: sidePadding, + right: sidePadding, + }, + border: { top: true, width: 1, color: borderColor(theme, "primary") }, + text: text(theme, "sans", "secondary", { size: "sm" }), + hover: { + text: text(theme, "sans", "active", { size: "sm" }), + }, + }, + callingIndicator: { + ...text(theme, "mono", "muted", { size: "xs" }) + } + } +} diff --git a/styles/src/styleTree/incomingCallNotification.ts b/styles/src/styleTree/incomingCallNotification.ts new file mode 100644 index 0000000000..dcae47bff0 --- /dev/null +++ b/styles/src/styleTree/incomingCallNotification.ts @@ -0,0 +1,22 @@ +import Theme from "../themes/common/theme"; +import { text } from "./components"; + +export default function incomingCallNotification(theme: Theme): Object { + const avatarSize = 12; + return { + callerAvatar: { + height: avatarSize, + width: avatarSize, + cornerRadius: 6, + }, + callerUsername: { + ...text(theme, "sans", "primary", { size: "xs" }), + }, + acceptButton: { + ...text(theme, "sans", "primary", { size: "xs" }) + }, + declineButton: { + ...text(theme, "sans", "primary", { size: "xs" }) + }, + }; +} From d7bac3cea6ec08aabccb866db1eb593b376cc1aa Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 10 Oct 2022 11:36:39 +0200 Subject: [PATCH 208/314] Style incoming call notification --- .../src/incoming_call_notification.rs | 85 ++++++++++++++----- crates/gpui/src/platform.rs | 2 + crates/gpui/src/platform/mac/platform.rs | 14 ++- crates/gpui/src/platform/test.rs | 4 + crates/theme/src/theme.rs | 6 ++ .../src/styleTree/incomingCallNotification.ts | 28 ++++-- 6 files changed, 111 insertions(+), 28 deletions(-) diff --git a/crates/collab_ui/src/incoming_call_notification.rs b/crates/collab_ui/src/incoming_call_notification.rs index 47fe8cbbfb..a396f8728a 100644 --- a/crates/collab_ui/src/incoming_call_notification.rs +++ b/crates/collab_ui/src/incoming_call_notification.rs @@ -3,8 +3,8 @@ use futures::StreamExt; use gpui::{ elements::*, geometry::{rect::RectF, vector::vec2f}, - impl_internal_actions, Entity, MouseButton, MutableAppContext, RenderContext, View, - ViewContext, WindowBounds, WindowKind, WindowOptions, + impl_internal_actions, CursorStyle, Entity, MouseButton, MutableAppContext, RenderContext, + View, ViewContext, WindowBounds, WindowKind, WindowOptions, }; use settings::Settings; use util::ResultExt; @@ -24,11 +24,17 @@ pub fn init(cx: &mut MutableAppContext) { } if let Some(incoming_call) = incoming_call { + const PADDING: f32 = 16.; + let screen_size = cx.platform().screen_size(); + let window_size = vec2f(304., 64.); let (window_id, _) = cx.add_window( WindowOptions { - bounds: WindowBounds::Fixed(RectF::new(vec2f(0., 0.), vec2f(300., 400.))), + bounds: WindowBounds::Fixed(RectF::new( + vec2f(screen_size.x() - window_size.x() - PADDING, PADDING), + window_size, + )), titlebar: None, - center: true, + center: false, kind: WindowKind::PopUp, is_movable: false, }, @@ -84,22 +90,40 @@ impl IncomingCallNotification { fn render_caller(&self, cx: &mut RenderContext) -> ElementBox { let theme = &cx.global::().theme.incoming_call_notification; Flex::row() - .with_children( - self.call - .caller - .avatar - .clone() - .map(|avatar| Image::new(avatar).with_style(theme.caller_avatar).boxed()), - ) + .with_children(self.call.caller.avatar.clone().map(|avatar| { + Image::new(avatar) + .with_style(theme.caller_avatar) + .aligned() + .boxed() + })) .with_child( - Label::new( - self.call.caller.github_login.clone(), - theme.caller_username.text.clone(), - ) - .contained() - .with_style(theme.caller_username.container) - .boxed(), + Flex::column() + .with_child( + Label::new( + self.call.caller.github_login.clone(), + theme.caller_username.text.clone(), + ) + .contained() + .with_style(theme.caller_username.container) + .boxed(), + ) + .with_child( + Label::new( + "Incoming Zed call...".into(), + theme.caller_message.text.clone(), + ) + .contained() + .with_style(theme.caller_message.container) + .boxed(), + ) + .contained() + .with_style(theme.caller_metadata) + .aligned() + .boxed(), ) + .contained() + .with_style(theme.caller_container) + .flex(1., true) .boxed() } @@ -107,33 +131,46 @@ impl IncomingCallNotification { enum Accept {} enum Decline {} - Flex::row() + Flex::column() .with_child( MouseEventHandler::::new(0, cx, |_, cx| { let theme = &cx.global::().theme.incoming_call_notification; Label::new("Accept".to_string(), theme.accept_button.text.clone()) + .aligned() .contained() .with_style(theme.accept_button.container) .boxed() }) + .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, |_, cx| { cx.dispatch_action(RespondToCall { accept: true }); }) + .flex(1., true) .boxed(), ) .with_child( MouseEventHandler::::new(0, cx, |_, cx| { let theme = &cx.global::().theme.incoming_call_notification; Label::new("Decline".to_string(), theme.decline_button.text.clone()) + .aligned() .contained() .with_style(theme.decline_button.container) .boxed() }) + .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, |_, cx| { cx.dispatch_action(RespondToCall { accept: false }); }) + .flex(1., true) .boxed(), ) + .constrained() + .with_width( + cx.global::() + .theme + .incoming_call_notification + .button_width, + ) .boxed() } } @@ -148,9 +185,17 @@ impl View for IncomingCallNotification { } fn render(&mut self, cx: &mut RenderContext) -> gpui::ElementBox { - Flex::column() + let background = cx + .global::() + .theme + .incoming_call_notification + .background; + Flex::row() .with_child(self.render_caller(cx)) .with_child(self.render_buttons(cx)) + .contained() + .with_background_color(background) + .expanded() .boxed() } } diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index a50698070c..a0e8cedfe9 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -44,6 +44,8 @@ pub trait Platform: Send + Sync { fn unhide_other_apps(&self); fn quit(&self); + fn screen_size(&self) -> Vector2F; + fn open_window( &self, id: usize, diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 7732da2b3e..cf2aeff466 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -2,7 +2,9 @@ use super::{ event::key_to_native, status_item::StatusItem, BoolExt as _, Dispatcher, FontSystem, Window, }; use crate::{ - executor, keymap, + executor, + geometry::vector::{vec2f, Vector2F}, + keymap, platform::{self, CursorStyle}, Action, ClipboardItem, Event, Menu, MenuItem, }; @@ -12,7 +14,7 @@ use cocoa::{ appkit::{ NSApplication, NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular, NSEventModifierFlags, NSMenu, NSMenuItem, NSModalResponse, NSOpenPanel, NSPasteboard, - NSPasteboardTypeString, NSSavePanel, NSWindow, + NSPasteboardTypeString, NSSavePanel, NSScreen, NSWindow, }, base::{id, nil, selector, YES}, foundation::{ @@ -485,6 +487,14 @@ impl platform::Platform for MacPlatform { } } + fn screen_size(&self) -> Vector2F { + unsafe { + let screen = NSScreen::mainScreen(nil); + let frame = NSScreen::frame(screen); + vec2f(frame.size.width as f32, frame.size.height as f32) + } + } + fn open_window( &self, id: usize, diff --git a/crates/gpui/src/platform/test.rs b/crates/gpui/src/platform/test.rs index 9a458a1dd9..613a2117b9 100644 --- a/crates/gpui/src/platform/test.rs +++ b/crates/gpui/src/platform/test.rs @@ -131,6 +131,10 @@ impl super::Platform for Platform { fn quit(&self) {} + fn screen_size(&self) -> Vector2F { + vec2f(1024., 768.) + } + fn open_window( &self, _: usize, diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index d69720322e..3f9b64ce56 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -479,8 +479,14 @@ pub struct ProjectSharedNotification { #[derive(Deserialize, Default)] pub struct IncomingCallNotification { + #[serde(default)] + pub background: Color, + pub caller_container: ContainerStyle, pub caller_avatar: ImageStyle, + pub caller_metadata: ContainerStyle, pub caller_username: ContainedText, + pub caller_message: ContainedText, + pub button_width: f32, pub accept_button: ContainedText, pub decline_button: ContainedText, } diff --git a/styles/src/styleTree/incomingCallNotification.ts b/styles/src/styleTree/incomingCallNotification.ts index dcae47bff0..d8ea7dbad9 100644 --- a/styles/src/styleTree/incomingCallNotification.ts +++ b/styles/src/styleTree/incomingCallNotification.ts @@ -1,22 +1,38 @@ import Theme from "../themes/common/theme"; -import { text } from "./components"; +import { backgroundColor, borderColor, text } from "./components"; export default function incomingCallNotification(theme: Theme): Object { - const avatarSize = 12; + const avatarSize = 32; return { + background: backgroundColor(theme, 300), + callerContainer: { + padding: 12, + }, callerAvatar: { height: avatarSize, width: avatarSize, - cornerRadius: 6, + cornerRadius: avatarSize / 2, + }, + callerMetadata: { + margin: { left: 10 }, }, callerUsername: { - ...text(theme, "sans", "primary", { size: "xs" }), + ...text(theme, "sans", "active", { size: "sm", weight: "bold" }), + margin: { top: -3 }, }, + callerMessage: { + ...text(theme, "sans", "secondary", { size: "xs" }), + margin: { top: -3 }, + }, + buttonWidth: 96, acceptButton: { - ...text(theme, "sans", "primary", { size: "xs" }) + background: backgroundColor(theme, "ok", "active"), + border: { left: true, bottom: true, width: 1, color: borderColor(theme, "primary") }, + ...text(theme, "sans", "ok", { size: "xs", weight: "extra_bold" }) }, declineButton: { - ...text(theme, "sans", "primary", { size: "xs" }) + border: { left: true, width: 1, color: borderColor(theme, "primary") }, + ...text(theme, "sans", "error", { size: "xs", weight: "extra_bold" }) }, }; } From 25ff5959fb72b7658ba0f080c62ba2238c13fbec Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 10 Oct 2022 12:23:50 +0200 Subject: [PATCH 209/314] Superimpose external location message on active view --- crates/workspace/src/pane_group.rs | 128 ++++++++++++++++------------- styles/src/styleTree/workspace.ts | 6 +- 2 files changed, 78 insertions(+), 56 deletions(-) diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index 9fd27f78b5..694f6a55eb 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -127,68 +127,86 @@ impl Member { Some((collaborator.replica_id, participant)) }); - if let Some((replica_id, leader)) = leader { - let view = match leader.location { - call::ParticipantLocation::Project { - project_id: leader_project_id, - } => { - if Some(leader_project_id) == project.read(cx).remote_id() { - ChildView::new(pane).boxed() - } else { - let leader_user = leader.user.clone(); - let leader_user_id = leader.user.id; - MouseEventHandler::::new( - pane.id(), - cx, - |_, _| { - Label::new( - format!( - "Follow {} on their currently active project", - leader_user.github_login, - ), - theme.workspace.external_location_message.text.clone(), - ) - .contained() - .with_style( - theme.workspace.external_location_message.container, - ) - .boxed() - }, - ) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, cx| { - cx.dispatch_action(JoinProject { - project_id: leader_project_id, - follow_user_id: leader_user_id, - }) - }) - .aligned() - .boxed() - } - } - call::ParticipantLocation::External => Label::new( - format!( - "{} is viewing a window outside of Zed", - leader.user.github_login - ), - theme.workspace.external_location_message.text.clone(), - ) - .contained() - .with_style(theme.workspace.external_location_message.container) - .aligned() - .boxed(), - }; + let mut border = Border::default(); + let prompt = if let Some((replica_id, leader)) = leader { let leader_color = theme.editor.replica_selection_style(replica_id).cursor; - let mut border = Border::all(theme.workspace.leader_border_width, leader_color); + border = Border::all(theme.workspace.leader_border_width, leader_color); border .color .fade_out(1. - theme.workspace.leader_border_opacity); border.overlay = true; - Container::new(view).with_border(border).boxed() + + match leader.location { + call::ParticipantLocation::Project { + project_id: leader_project_id, + } => { + if Some(leader_project_id) == project.read(cx).remote_id() { + None + } else { + let leader_user = leader.user.clone(); + let leader_user_id = leader.user.id; + Some( + MouseEventHandler::::new( + pane.id(), + cx, + |_, _| { + Label::new( + format!( + "Follow {} on their currently active project", + leader_user.github_login, + ), + theme + .workspace + .external_location_message + .text + .clone(), + ) + .contained() + .with_style( + theme.workspace.external_location_message.container, + ) + .boxed() + }, + ) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, cx| { + cx.dispatch_action(JoinProject { + project_id: leader_project_id, + follow_user_id: leader_user_id, + }) + }) + .aligned() + .bottom() + .right() + .boxed(), + ) + } + } + call::ParticipantLocation::External => Some( + Label::new( + format!( + "{} is viewing a window outside of Zed", + leader.user.github_login + ), + theme.workspace.external_location_message.text.clone(), + ) + .contained() + .with_style(theme.workspace.external_location_message.container) + .aligned() + .bottom() + .right() + .boxed(), + ), + } } else { - ChildView::new(pane).boxed() - } + None + }; + + Stack::new() + .with_child(ChildView::new(pane).contained().with_border(border).boxed()) + .with_children(prompt) + .boxed() } Member::Axis(axis) => axis.render(project, theme, follower_states, cx), } diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index c970c38296..4deee046b4 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -48,8 +48,12 @@ export default function workspace(theme: Theme) { ...text(theme, "sans", "primary", { size: "lg" }), }, externalLocationMessage: { + background: backgroundColor(theme, "info"), + border: border(theme, "secondary"), + cornerRadius: 6, padding: 12, - ...text(theme, "sans", "primary", { size: "lg" }), + margin: { bottom: 8, right: 8 }, + ...text(theme, "sans", "secondary", { size: "xs" }), }, leaderBorderOpacity: 0.7, leaderBorderWidth: 2.0, From 9d990ae3292132e9a53be1f7d387179084069c36 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 10 Oct 2022 14:19:40 +0200 Subject: [PATCH 210/314] Show all room participants in titlebar ...and allow following them into external projects. --- crates/collab_ui/src/collab_titlebar_item.rs | 157 +++++++++++------- crates/gpui/src/elements/image.rs | 3 + crates/gpui/src/platform/mac/renderer.rs | 2 + .../gpui/src/platform/mac/shaders/shaders.h | 1 + .../src/platform/mac/shaders/shaders.metal | 11 ++ crates/gpui/src/scene.rs | 1 + styles/src/styleTree/workspace.ts | 13 +- 7 files changed, 121 insertions(+), 67 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index cc82acac5e..c46861b5ab 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -1,6 +1,6 @@ use crate::{contact_notification::ContactNotification, contacts_popover}; use call::{ActiveCall, ParticipantLocation}; -use client::{Authenticate, ContactEventKind, PeerId, UserStore}; +use client::{Authenticate, ContactEventKind, PeerId, User, UserStore}; use clock::ReplicaId; use contacts_popover::ContactsPopover; use gpui::{ @@ -9,13 +9,13 @@ use gpui::{ elements::*, geometry::{rect::RectF, vector::vec2f, PathBuilder}, json::{self, ToJson}, - Border, CursorStyle, Entity, ImageData, ModelHandle, MouseButton, MutableAppContext, - RenderContext, Subscription, View, ViewContext, ViewHandle, WeakViewHandle, + Border, CursorStyle, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, + Subscription, View, ViewContext, ViewHandle, WeakViewHandle, }; use settings::Settings; -use std::{ops::Range, sync::Arc}; +use std::ops::Range; use theme::Theme; -use workspace::{FollowNextCollaborator, ToggleFollow, Workspace}; +use workspace::{FollowNextCollaborator, JoinProject, ToggleFollow, Workspace}; actions!( contacts_titlebar_item, @@ -282,29 +282,27 @@ impl CollabTitlebarItem { let active_call = ActiveCall::global(cx); if let Some(room) = active_call.read(cx).room().cloned() { let project = workspace.read(cx).project().read(cx); - let project_id = project.remote_id(); - let mut collaborators = project - .collaborators() - .values() - .cloned() + let mut participants = room + .read(cx) + .remote_participants() + .iter() + .map(|(peer_id, collaborator)| (*peer_id, collaborator.clone())) .collect::>(); - collaborators.sort_by_key(|collaborator| collaborator.replica_id); - collaborators + participants + .sort_by_key(|(peer_id, _)| Some(project.collaborators().get(peer_id)?.replica_id)); + participants .into_iter() - .filter_map(|collaborator| { - let participant = room - .read(cx) - .remote_participants() - .get(&collaborator.peer_id)?; + .filter_map(|(peer_id, participant)| { + let project = workspace.read(cx).project().read(cx); + let replica_id = project + .collaborators() + .get(&peer_id) + .map(|collaborator| collaborator.replica_id); let user = participant.user.clone(); - let is_active = project_id.map_or(false, |project_id| { - participant.location == ParticipantLocation::Project { project_id } - }); Some(self.render_avatar( - user.avatar.clone()?, - collaborator.replica_id, - Some((collaborator.peer_id, &user.github_login)), - is_active, + &user, + replica_id, + Some((peer_id, &user.github_login, participant.location)), workspace, theme, cx, @@ -325,8 +323,8 @@ impl CollabTitlebarItem { let user = workspace.read(cx).user_store().read(cx).current_user(); let replica_id = workspace.read(cx).project().read(cx).replica_id(); let status = *workspace.read(cx).client().status().borrow(); - if let Some(avatar) = user.and_then(|user| user.avatar.clone()) { - Some(self.render_avatar(avatar, replica_id, None, true, workspace, theme, cx)) + if let Some(user) = user { + Some(self.render_avatar(&user, Some(replica_id), None, workspace, theme, cx)) } else if matches!(status, client::Status::UpgradeRequired) { None } else { @@ -352,72 +350,105 @@ impl CollabTitlebarItem { fn render_avatar( &self, - avatar: Arc, - replica_id: ReplicaId, - peer: Option<(PeerId, &str)>, - is_active: bool, + user: &User, + replica_id: Option, + peer: Option<(PeerId, &str, ParticipantLocation)>, workspace: &ViewHandle, theme: &Theme, cx: &mut RenderContext, ) -> ElementBox { - let replica_color = theme.editor.replica_selection_style(replica_id).cursor; - let is_followed = peer.map_or(false, |(peer_id, _)| { + let is_followed = peer.map_or(false, |(peer_id, _, _)| { workspace.read(cx).is_following(peer_id) }); - let mut avatar_style; - if is_active { - avatar_style = theme.workspace.titlebar.avatar; + let mut avatar_style; + if let Some((_, _, location)) = peer.as_ref() { + if let ParticipantLocation::Project { project_id } = *location { + if Some(project_id) == workspace.read(cx).project().read(cx).remote_id() { + avatar_style = theme.workspace.titlebar.avatar; + } else { + avatar_style = theme.workspace.titlebar.inactive_avatar; + } + } else { + avatar_style = theme.workspace.titlebar.inactive_avatar; + } } else { - avatar_style = theme.workspace.titlebar.inactive_avatar; + avatar_style = theme.workspace.titlebar.avatar; } - if is_followed { - avatar_style.border = Border::all(1.0, replica_color); + let mut replica_color = None; + if let Some(replica_id) = replica_id { + let color = theme.editor.replica_selection_style(replica_id).cursor; + replica_color = Some(color); + if is_followed { + avatar_style.border = Border::all(1.0, color); + } } let content = Stack::new() - .with_child( - Image::new(avatar) + .with_children(user.avatar.as_ref().map(|avatar| { + Image::new(avatar.clone()) .with_style(avatar_style) .constrained() .with_width(theme.workspace.titlebar.avatar_width) .aligned() - .boxed(), - ) - .with_child( + .boxed() + })) + .with_children(replica_color.map(|replica_color| { AvatarRibbon::new(replica_color) .constrained() .with_width(theme.workspace.titlebar.avatar_ribbon.width) .with_height(theme.workspace.titlebar.avatar_ribbon.height) .aligned() .bottom() - .boxed(), - ) + .boxed() + })) .constrained() .with_width(theme.workspace.titlebar.avatar_width) .contained() .with_margin_left(theme.workspace.titlebar.avatar_margin) .boxed(); - if let Some((peer_id, peer_github_login)) = peer { - MouseEventHandler::::new(replica_id.into(), cx, move |_, _| content) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, cx| { - cx.dispatch_action(ToggleFollow(peer_id)) - }) - .with_tooltip::( - peer_id.0 as usize, - if is_followed { - format!("Unfollow {}", peer_github_login) - } else { - format!("Follow {}", peer_github_login) - }, - Some(Box::new(FollowNextCollaborator)), - theme.tooltip.clone(), - cx, - ) - .boxed() + if let Some((peer_id, peer_github_login, location)) = peer { + if let Some(replica_id) = replica_id { + MouseEventHandler::::new(replica_id.into(), cx, move |_, _| content) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, cx| { + cx.dispatch_action(ToggleFollow(peer_id)) + }) + .with_tooltip::( + peer_id.0 as usize, + if is_followed { + format!("Unfollow {}", peer_github_login) + } else { + format!("Follow {}", peer_github_login) + }, + Some(Box::new(FollowNextCollaborator)), + theme.tooltip.clone(), + cx, + ) + .boxed() + } else if let ParticipantLocation::Project { project_id } = location { + let user_id = user.id; + MouseEventHandler::::new(peer_id.0 as usize, cx, move |_, _| content) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, cx| { + cx.dispatch_action(JoinProject { + project_id, + follow_user_id: user_id, + }) + }) + .with_tooltip::( + peer_id.0 as usize, + format!("Follow {} into external project", peer_github_login), + Some(Box::new(FollowNextCollaborator)), + theme.tooltip.clone(), + cx, + ) + .boxed() + } else { + content + } } else { content } diff --git a/crates/gpui/src/elements/image.rs b/crates/gpui/src/elements/image.rs index e0387e39bb..7e231e5e29 100644 --- a/crates/gpui/src/elements/image.rs +++ b/crates/gpui/src/elements/image.rs @@ -27,6 +27,8 @@ pub struct ImageStyle { pub height: Option, #[serde(default)] pub width: Option, + #[serde(default)] + pub grayscale: bool, } impl Image { @@ -74,6 +76,7 @@ impl Element for Image { bounds, border: self.style.border, corner_radius: self.style.corner_radius, + grayscale: self.style.grayscale, data: self.data.clone(), }); } diff --git a/crates/gpui/src/platform/mac/renderer.rs b/crates/gpui/src/platform/mac/renderer.rs index ea094e998c..6a70ff41f0 100644 --- a/crates/gpui/src/platform/mac/renderer.rs +++ b/crates/gpui/src/platform/mac/renderer.rs @@ -747,6 +747,7 @@ impl Renderer { border_left: border_width * (image.border.left as usize as f32), border_color: image.border.color.to_uchar4(), corner_radius, + grayscale: image.grayscale as u8, }); } @@ -769,6 +770,7 @@ impl Renderer { border_left: 0., border_color: Default::default(), corner_radius: 0., + grayscale: false as u8, }); } else { log::warn!("could not render glyph with id {}", image_glyph.id); diff --git a/crates/gpui/src/platform/mac/shaders/shaders.h b/crates/gpui/src/platform/mac/shaders/shaders.h index 29be2c9e1e..6e0ed1a5f1 100644 --- a/crates/gpui/src/platform/mac/shaders/shaders.h +++ b/crates/gpui/src/platform/mac/shaders/shaders.h @@ -90,6 +90,7 @@ typedef struct { float border_left; vector_uchar4 border_color; float corner_radius; + uint8_t grayscale; } GPUIImage; typedef enum { diff --git a/crates/gpui/src/platform/mac/shaders/shaders.metal b/crates/gpui/src/platform/mac/shaders/shaders.metal index 795026e747..397e7647c4 100644 --- a/crates/gpui/src/platform/mac/shaders/shaders.metal +++ b/crates/gpui/src/platform/mac/shaders/shaders.metal @@ -44,6 +44,7 @@ struct QuadFragmentInput { float border_left; float4 border_color; float corner_radius; + uchar grayscale; // only used in image shader }; float4 quad_sdf(QuadFragmentInput input) { @@ -110,6 +111,7 @@ vertex QuadFragmentInput quad_vertex( quad.border_left, coloru_to_colorf(quad.border_color), quad.corner_radius, + 0, }; } @@ -251,6 +253,7 @@ vertex QuadFragmentInput image_vertex( image.border_left, coloru_to_colorf(image.border_color), image.corner_radius, + image.grayscale, }; } @@ -260,6 +263,13 @@ fragment float4 image_fragment( ) { constexpr sampler atlas_sampler(mag_filter::linear, min_filter::linear); input.background_color = atlas.sample(atlas_sampler, input.atlas_position); + if (input.grayscale) { + float grayscale = + 0.2126 * input.background_color.r + + 0.7152 * input.background_color.g + + 0.0722 * input.background_color.b; + input.background_color = float4(grayscale, grayscale, grayscale, input.background_color.a); + } return quad_sdf(input); } @@ -289,6 +299,7 @@ vertex QuadFragmentInput surface_vertex( 0., float4(0.), 0., + 0, }; } diff --git a/crates/gpui/src/scene.rs b/crates/gpui/src/scene.rs index e676147fa9..4ef17a3f8f 100644 --- a/crates/gpui/src/scene.rs +++ b/crates/gpui/src/scene.rs @@ -172,6 +172,7 @@ pub struct Image { pub bounds: RectF, pub border: Border, pub corner_radius: f32, + pub grayscale: bool, pub data: Arc, } diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 4deee046b4..cfbda49056 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -36,6 +36,7 @@ export default function workspace(theme: Theme) { border: border(theme, "primary"), }, }; + const avatarWidth = 18; return { background: backgroundColor(theme, 300), @@ -80,7 +81,7 @@ export default function workspace(theme: Theme) { }, statusBar: statusBar(theme), titlebar: { - avatarWidth: 18, + avatarWidth, avatarMargin: 8, height: 33, background: backgroundColor(theme, 100), @@ -90,15 +91,19 @@ export default function workspace(theme: Theme) { }, title: text(theme, "sans", "primary"), avatar: { - cornerRadius: 10, + cornerRadius: avatarWidth / 2, border: { color: "#00000088", width: 1, }, }, inactiveAvatar: { - cornerRadius: 10, - opacity: 0.65, + cornerRadius: avatarWidth / 2, + border: { + color: "#00000088", + width: 1, + }, + grayscale: true, }, avatarRibbon: { height: 3, From 7cfe435e627f4187683df6f736e7f095af264c25 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 10 Oct 2022 14:37:51 +0200 Subject: [PATCH 211/314] Style project shared notifications --- .../src/incoming_call_notification.rs | 13 ++- .../src/project_shared_notification.rs | 81 +++++++++++++++---- crates/theme/src/theme.rs | 6 ++ .../styleTree/projectSharedNotification.ts | 28 +++++-- 4 files changed, 98 insertions(+), 30 deletions(-) diff --git a/crates/collab_ui/src/incoming_call_notification.rs b/crates/collab_ui/src/incoming_call_notification.rs index a396f8728a..cbb23de2e6 100644 --- a/crates/collab_ui/src/incoming_call_notification.rs +++ b/crates/collab_ui/src/incoming_call_notification.rs @@ -26,7 +26,7 @@ pub fn init(cx: &mut MutableAppContext) { if let Some(incoming_call) = incoming_call { const PADDING: f32 = 16.; let screen_size = cx.platform().screen_size(); - let window_size = vec2f(304., 64.); + let window_size = vec2f(274., 64.); let (window_id, _) = cx.add_window( WindowOptions { bounds: WindowBounds::Fixed(RectF::new( @@ -108,13 +108,10 @@ impl IncomingCallNotification { .boxed(), ) .with_child( - Label::new( - "Incoming Zed call...".into(), - theme.caller_message.text.clone(), - ) - .contained() - .with_style(theme.caller_message.container) - .boxed(), + Label::new("is calling you".into(), theme.caller_message.text.clone()) + .contained() + .with_style(theme.caller_message.container) + .boxed(), ) .contained() .with_style(theme.caller_metadata) diff --git a/crates/collab_ui/src/project_shared_notification.rs b/crates/collab_ui/src/project_shared_notification.rs index e0c1966144..5597ea0a01 100644 --- a/crates/collab_ui/src/project_shared_notification.rs +++ b/crates/collab_ui/src/project_shared_notification.rs @@ -4,8 +4,8 @@ use gpui::{ actions, elements::*, geometry::{rect::RectF, vector::vec2f}, - Entity, MouseButton, MutableAppContext, RenderContext, View, ViewContext, WindowBounds, - WindowKind, WindowOptions, + CursorStyle, Entity, MouseButton, MutableAppContext, RenderContext, View, ViewContext, + WindowBounds, WindowKind, WindowOptions, }; use settings::Settings; use std::sync::Arc; @@ -20,11 +20,17 @@ pub fn init(cx: &mut MutableAppContext) { let active_call = ActiveCall::global(cx); cx.subscribe(&active_call, move |_, event, cx| match event { room::Event::RemoteProjectShared { owner, project_id } => { + const PADDING: f32 = 16.; + let screen_size = cx.platform().screen_size(); + let window_size = vec2f(366., 64.); cx.add_window( WindowOptions { - bounds: WindowBounds::Fixed(RectF::new(vec2f(0., 0.), vec2f(300., 400.))), + bounds: WindowBounds::Fixed(RectF::new( + vec2f(screen_size.x() - window_size.x() - PADDING, PADDING), + window_size, + )), titlebar: None, - center: true, + center: false, kind: WindowKind::PopUp, is_movable: false, }, @@ -59,19 +65,40 @@ impl ProjectSharedNotification { fn render_owner(&self, cx: &mut RenderContext) -> ElementBox { let theme = &cx.global::().theme.project_shared_notification; Flex::row() - .with_children( - self.owner - .avatar - .clone() - .map(|avatar| Image::new(avatar).with_style(theme.owner_avatar).boxed()), - ) + .with_children(self.owner.avatar.clone().map(|avatar| { + Image::new(avatar) + .with_style(theme.owner_avatar) + .aligned() + .boxed() + })) .with_child( - Label::new( - format!("{} has shared a new project", self.owner.github_login), - theme.message.text.clone(), - ) - .boxed(), + Flex::column() + .with_child( + Label::new( + self.owner.github_login.clone(), + theme.owner_username.text.clone(), + ) + .contained() + .with_style(theme.owner_username.container) + .boxed(), + ) + .with_child( + Label::new( + "has shared a project with you".into(), + theme.message.text.clone(), + ) + .contained() + .with_style(theme.message.container) + .boxed(), + ) + .contained() + .with_style(theme.owner_metadata) + .aligned() + .boxed(), ) + .contained() + .with_style(theme.owner_container) + .flex(1., true) .boxed() } @@ -81,36 +108,50 @@ impl ProjectSharedNotification { let project_id = self.project_id; let owner_user_id = self.owner.id; - Flex::row() + + Flex::column() .with_child( MouseEventHandler::::new(0, cx, |_, cx| { let theme = &cx.global::().theme.project_shared_notification; Label::new("Join".to_string(), theme.join_button.text.clone()) + .aligned() .contained() .with_style(theme.join_button.container) .boxed() }) + .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, move |_, cx| { cx.dispatch_action(JoinProject { project_id, follow_user_id: owner_user_id, }); }) + .flex(1., true) .boxed(), ) .with_child( MouseEventHandler::::new(0, cx, |_, cx| { let theme = &cx.global::().theme.project_shared_notification; Label::new("Dismiss".to_string(), theme.dismiss_button.text.clone()) + .aligned() .contained() .with_style(theme.dismiss_button.container) .boxed() }) + .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, |_, cx| { cx.dispatch_action(DismissProject); }) + .flex(1., true) .boxed(), ) + .constrained() + .with_width( + cx.global::() + .theme + .project_shared_notification + .button_width, + ) .boxed() } } @@ -125,9 +166,17 @@ impl View for ProjectSharedNotification { } fn render(&mut self, cx: &mut RenderContext) -> gpui::ElementBox { + let background = cx + .global::() + .theme + .project_shared_notification + .background; Flex::row() .with_child(self.render_owner(cx)) .with_child(self.render_buttons(cx)) + .contained() + .with_background_color(background) + .expanded() .boxed() } } diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 3f9b64ce56..a02bcfb1da 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -471,8 +471,14 @@ pub struct UpdateNotification { #[derive(Deserialize, Default)] pub struct ProjectSharedNotification { + #[serde(default)] + pub background: Color, + pub owner_container: ContainerStyle, pub owner_avatar: ImageStyle, + pub owner_metadata: ContainerStyle, + pub owner_username: ContainedText, pub message: ContainedText, + pub button_width: f32, pub join_button: ContainedText, pub dismiss_button: ContainedText, } diff --git a/styles/src/styleTree/projectSharedNotification.ts b/styles/src/styleTree/projectSharedNotification.ts index bc34265135..7a70f0f0d3 100644 --- a/styles/src/styleTree/projectSharedNotification.ts +++ b/styles/src/styleTree/projectSharedNotification.ts @@ -1,22 +1,38 @@ import Theme from "../themes/common/theme"; -import { text } from "./components"; +import { backgroundColor, borderColor, text } from "./components"; export default function projectSharedNotification(theme: Theme): Object { - const avatarSize = 12; + const avatarSize = 32; return { + background: backgroundColor(theme, 300), + ownerContainer: { + padding: 12, + }, ownerAvatar: { height: avatarSize, width: avatarSize, - cornerRadius: 6, + cornerRadius: avatarSize / 2, + }, + ownerMetadata: { + margin: { left: 10 }, + }, + ownerUsername: { + ...text(theme, "sans", "active", { size: "sm", weight: "bold" }), + margin: { top: -3 }, }, message: { - ...text(theme, "sans", "primary", { size: "xs" }), + ...text(theme, "sans", "secondary", { size: "xs" }), + margin: { top: -3 }, }, + buttonWidth: 96, joinButton: { - ...text(theme, "sans", "primary", { size: "xs" }) + background: backgroundColor(theme, "info", "active"), + border: { left: true, bottom: true, width: 1, color: borderColor(theme, "primary") }, + ...text(theme, "sans", "info", { size: "xs", weight: "extra_bold" }) }, dismissButton: { - ...text(theme, "sans", "primary", { size: "xs" }) + border: { left: true, width: 1, color: borderColor(theme, "primary") }, + ...text(theme, "sans", "secondary", { size: "xs", weight: "extra_bold" }) }, }; } From 3396a98978ecdb6a9e3013e0ac26d587946efa61 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 10 Oct 2022 14:41:18 +0200 Subject: [PATCH 212/314] :lipstick: --- styles/src/styleTree/contactList.ts | 4 +- styles/src/styleTree/contactsPopover.ts | 115 +----------------------- styles/src/themes/common/base16.ts | 1 - styles/src/themes/common/theme.ts | 1 - 4 files changed, 3 insertions(+), 118 deletions(-) diff --git a/styles/src/styleTree/contactList.ts b/styles/src/styleTree/contactList.ts index f2430d31e6..8b8b6024cf 100644 --- a/styles/src/styleTree/contactList.ts +++ b/styles/src/styleTree/contactList.ts @@ -90,13 +90,13 @@ export default function contactList(theme: Theme) { cornerRadius: 4, padding: 4, margin: { top: 12, left: 12 }, - background: iconColor(theme, "success"), + background: iconColor(theme, "ok"), }, contactStatusBusy: { cornerRadius: 4, padding: 4, margin: { top: 12, left: 12 }, - background: iconColor(theme, "warning"), + background: iconColor(theme, "error"), }, contactUsername: { ...text(theme, "mono", "primary", { size: "sm" }), diff --git a/styles/src/styleTree/contactsPopover.ts b/styles/src/styleTree/contactsPopover.ts index 57af5a6d4d..7d699fa26b 100644 --- a/styles/src/styleTree/contactsPopover.ts +++ b/styles/src/styleTree/contactsPopover.ts @@ -1,18 +1,7 @@ import Theme from "../themes/common/theme"; -import { backgroundColor, border, borderColor, iconColor, player, popoverShadow, text } from "./components"; +import { backgroundColor, border, popoverShadow } from "./components"; export default function contactsPopover(theme: Theme) { - const nameMargin = 8; - const sidePadding = 12; - - const contactButton = { - background: backgroundColor(theme, 100), - color: iconColor(theme, "primary"), - iconWidth: 8, - buttonWidth: 16, - cornerRadius: 8, - }; - return { background: backgroundColor(theme, 300, "base"), cornerRadius: 6, @@ -21,107 +10,5 @@ export default function contactsPopover(theme: Theme) { border: border(theme, "primary"), width: 250, height: 300, - userQueryEditor: { - background: backgroundColor(theme, 500), - cornerRadius: 6, - text: text(theme, "mono", "primary"), - placeholderText: text(theme, "mono", "placeholder", { size: "sm" }), - selection: player(theme, 1).selection, - border: border(theme, "secondary"), - padding: { - bottom: 4, - left: 8, - right: 8, - top: 4, - }, - margin: { - left: sidePadding, - right: sidePadding, - }, - }, - userQueryEditorHeight: 32, - addContactButton: { - margin: { left: 6, right: 12 }, - color: iconColor(theme, "primary"), - buttonWidth: 16, - iconWidth: 16, - }, - privateButton: { - iconWidth: 12, - color: iconColor(theme, "primary"), - cornerRadius: 5, - buttonWidth: 12, - }, - rowHeight: 28, - sectionIconSize: 8, - headerRow: { - ...text(theme, "mono", "secondary", { size: "sm" }), - margin: { top: 14 }, - padding: { - left: sidePadding, - right: sidePadding, - }, - active: { - ...text(theme, "mono", "primary", { size: "sm" }), - background: backgroundColor(theme, 100, "active"), - }, - }, - contactRow: { - padding: { - left: sidePadding, - right: sidePadding, - }, - active: { - background: backgroundColor(theme, 100, "active"), - }, - }, - contactAvatar: { - cornerRadius: 10, - width: 18, - }, - contactStatusFree: { - cornerRadius: 4, - padding: 4, - margin: { top: 12, left: 12 }, - background: iconColor(theme, "success"), - }, - contactStatusBusy: { - cornerRadius: 4, - padding: 4, - margin: { top: 12, left: 12 }, - background: iconColor(theme, "warning"), - }, - contactUsername: { - ...text(theme, "mono", "primary", { size: "sm" }), - margin: { - left: nameMargin, - }, - }, - contactButtonSpacing: nameMargin, - contactButton: { - ...contactButton, - hover: { - background: backgroundColor(theme, "on300", "hovered"), - }, - }, - disabledButton: { - ...contactButton, - background: backgroundColor(theme, 100), - color: iconColor(theme, "muted"), - }, - inviteRow: { - padding: { - left: sidePadding, - right: sidePadding, - }, - border: { top: true, width: 1, color: borderColor(theme, "primary") }, - text: text(theme, "sans", "secondary", { size: "sm" }), - hover: { - text: text(theme, "sans", "active", { size: "sm" }), - }, - }, - callingIndicator: { - ...text(theme, "mono", "muted", { size: "xs" }) - } } } diff --git a/styles/src/themes/common/base16.ts b/styles/src/themes/common/base16.ts index 3612988075..7aa72ef137 100644 --- a/styles/src/themes/common/base16.ts +++ b/styles/src/themes/common/base16.ts @@ -137,7 +137,6 @@ export function createTheme( ok: sample(ramps.green, 0.5), error: sample(ramps.red, 0.5), warning: sample(ramps.yellow, 0.5), - success: sample(ramps.green, 0.5), info: sample(ramps.blue, 0.5), onMedia: darkest, }; diff --git a/styles/src/themes/common/theme.ts b/styles/src/themes/common/theme.ts index 18ad8122e0..e01435b846 100644 --- a/styles/src/themes/common/theme.ts +++ b/styles/src/themes/common/theme.ts @@ -123,7 +123,6 @@ export default interface Theme { error: string; warning: string; info: string; - success: string; }; editor: { background: string; From 5f9cedad23d66a8c18ac59c26dd5b9f9cf11db9b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 10 Oct 2022 16:05:09 +0200 Subject: [PATCH 213/314] Add margin to picker in contacts popover --- crates/collab_ui/src/contact_finder.rs | 6 +++++- styles/src/styleTree/contactFinder.ts | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/collab_ui/src/contact_finder.rs b/crates/collab_ui/src/contact_finder.rs index 65ebee2797..25726e381e 100644 --- a/crates/collab_ui/src/contact_finder.rs +++ b/crates/collab_ui/src/contact_finder.rs @@ -121,7 +121,11 @@ impl PickerDelegate for ContactFinder { } else { &theme.contact_finder.contact_button }; - let style = theme.picker.item.style_for(mouse_state, selected); + let style = theme + .contact_finder + .picker + .item + .style_for(mouse_state, selected); Flex::row() .with_children(user.avatar.clone().map(|avatar| { Image::new(avatar) diff --git a/styles/src/styleTree/contactFinder.ts b/styles/src/styleTree/contactFinder.ts index bf43a74666..2feb3d7e35 100644 --- a/styles/src/styleTree/contactFinder.ts +++ b/styles/src/styleTree/contactFinder.ts @@ -3,6 +3,7 @@ import picker from "./picker"; import { backgroundColor, border, iconColor, player, text } from "./components"; export default function contactFinder(theme: Theme) { + const sideMargin = 12; const contactButton = { background: backgroundColor(theme, 100), color: iconColor(theme, "primary"), @@ -13,7 +14,10 @@ export default function contactFinder(theme: Theme) { return { picker: { - item: picker(theme).item, + item: { + ...picker(theme).item, + margin: { left: sideMargin, right: sideMargin } + }, empty: picker(theme).empty, inputEditor: { background: backgroundColor(theme, 500), From d9d99e5e0407f9641463ee38660db556e71e449b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 10 Oct 2022 16:05:22 +0200 Subject: [PATCH 214/314] Fix seed script --- crates/collab/src/bin/seed.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/crates/collab/src/bin/seed.rs b/crates/collab/src/bin/seed.rs index b7b3a96710..cabea7d013 100644 --- a/crates/collab/src/bin/seed.rs +++ b/crates/collab/src/bin/seed.rs @@ -84,7 +84,23 @@ async fn main() { }, ) .await - .expect("failed to insert user"), + .expect("failed to insert user") + .user_id, + ); + } else if admin { + zed_user_ids.push( + db.create_user( + &format!("{}@zed.dev", github_user.login), + admin, + db::NewUserParams { + github_login: github_user.login, + github_user_id: github_user.id, + invite_count: 5, + }, + ) + .await + .expect("failed to insert user") + .user_id, ); } } From 04fcd18c755c7e6f1ec9339fb1328617ad55c517 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 10 Oct 2022 16:30:02 +0200 Subject: [PATCH 215/314] Show contacts popover when clicking on menu bar extra --- crates/collab_ui/src/active_call_popover.rs | 40 -------------------- crates/collab_ui/src/collab_titlebar_item.rs | 3 +- crates/collab_ui/src/collab_ui.rs | 3 +- crates/collab_ui/src/contact_list.rs | 14 +++---- crates/collab_ui/src/contacts_popover.rs | 37 +++++++++++++----- crates/collab_ui/src/menu_bar_extra.rs | 31 ++++++++------- 6 files changed, 56 insertions(+), 72 deletions(-) delete mode 100644 crates/collab_ui/src/active_call_popover.rs diff --git a/crates/collab_ui/src/active_call_popover.rs b/crates/collab_ui/src/active_call_popover.rs deleted file mode 100644 index 01a4e4721d..0000000000 --- a/crates/collab_ui/src/active_call_popover.rs +++ /dev/null @@ -1,40 +0,0 @@ -use gpui::{color::Color, elements::*, Entity, RenderContext, View, ViewContext}; - -pub enum Event { - Deactivated, -} - -pub struct ActiveCallPopover { - _subscription: gpui::Subscription, -} - -impl Entity for ActiveCallPopover { - type Event = Event; -} - -impl View for ActiveCallPopover { - fn ui_name() -> &'static str { - "ActiveCallPopover" - } - - fn render(&mut self, _: &mut RenderContext) -> ElementBox { - Empty::new() - .contained() - .with_background_color(Color::red()) - .boxed() - } -} - -impl ActiveCallPopover { - pub fn new(cx: &mut ViewContext) -> Self { - Self { - _subscription: cx.observe_window_activation(Self::window_activation_changed), - } - } - - fn window_activation_changed(&mut self, is_active: bool, cx: &mut ViewContext) { - if !is_active { - cx.emit(Event::Deactivated); - } - } -} diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index c46861b5ab..bbe27ce3a9 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -163,7 +163,8 @@ impl CollabTitlebarItem { if let Some(workspace) = self.workspace.upgrade(cx) { let project = workspace.read(cx).project().clone(); let user_store = workspace.read(cx).user_store().clone(); - let view = cx.add_view(|cx| ContactsPopover::new(project, user_store, cx)); + let view = cx + .add_view(|cx| ContactsPopover::new(false, Some(project), user_store, cx)); cx.focus(&view); cx.subscribe(&view, |this, _, event, cx| { match event { diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index da2cf77534..221ee6068f 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -1,4 +1,3 @@ -mod active_call_popover; mod collab_titlebar_item; mod contact_finder; mod contact_list; @@ -23,7 +22,7 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { contact_finder::init(cx); contacts_popover::init(cx); incoming_call_notification::init(cx); - menu_bar_extra::init(cx); + menu_bar_extra::init(app_state.user_store.clone(), cx); project_shared_notification::init(cx); cx.add_global_action(move |action: &JoinProject, cx| { diff --git a/crates/collab_ui/src/contact_list.rs b/crates/collab_ui/src/contact_list.rs index a539f8ffac..da48015b4e 100644 --- a/crates/collab_ui/src/contact_list.rs +++ b/crates/collab_ui/src/contact_list.rs @@ -114,7 +114,7 @@ pub struct ContactList { entries: Vec, match_candidates: Vec, list_state: ListState, - project: ModelHandle, + project: Option>, user_store: ModelHandle, filter_editor: ViewHandle, collapsed_sections: Vec
, @@ -124,7 +124,7 @@ pub struct ContactList { impl ContactList { pub fn new( - project: ModelHandle, + project: Option>, user_store: ModelHandle, cx: &mut ViewContext, ) -> Self { @@ -195,7 +195,7 @@ impl ContactList { ), ContactEntry::Contact(contact) => Self::render_contact( contact, - &this.project, + this.project.as_ref(), &theme.contact_list, is_selected, cx, @@ -292,7 +292,7 @@ impl ContactList { self.call( &Call { recipient_user_id: contact.user.id, - initial_project: Some(self.project.clone()), + initial_project: self.project.clone(), }, cx, ); @@ -664,7 +664,7 @@ impl ContactList { fn render_contact( contact: &Contact, - project: &ModelHandle, + project: Option<&ModelHandle>, theme: &theme::ContactList, is_selected: bool, cx: &mut RenderContext, @@ -672,7 +672,7 @@ impl ContactList { let online = contact.online; let busy = contact.busy; let user_id = contact.user.id; - let initial_project = project.clone(); + let initial_project = project.cloned(); let mut element = MouseEventHandler::::new(contact.user.id as usize, cx, |_, _| { Flex::row() @@ -726,7 +726,7 @@ impl ContactList { if online && !busy { cx.dispatch_action(Call { recipient_user_id: user_id, - initial_project: Some(initial_project.clone()), + initial_project: initial_project.clone(), }); } }); diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index 07ddc487a4..dd67cfe724 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -23,30 +23,41 @@ enum Child { } pub struct ContactsPopover { + is_popup: bool, child: Child, - project: ModelHandle, + project: Option>, user_store: ModelHandle, _subscription: Option, + _window_subscription: gpui::Subscription, } impl ContactsPopover { pub fn new( - project: ModelHandle, + is_popup: bool, + project: Option>, user_store: ModelHandle, cx: &mut ViewContext, ) -> Self { let mut this = Self { + is_popup, child: Child::ContactList( cx.add_view(|cx| ContactList::new(project.clone(), user_store.clone(), cx)), ), project, user_store, _subscription: None, + _window_subscription: cx.observe_window_activation(Self::window_activation_changed), }; this.show_contact_list(cx); this } + fn window_activation_changed(&mut self, active: bool, cx: &mut ViewContext) { + if !active { + cx.emit(Event::Dismissed); + } + } + fn toggle_contact_finder(&mut self, _: &ToggleContactFinder, cx: &mut ViewContext) { match &self.child { Child::ContactList(_) => self.show_contact_finder(cx), @@ -92,13 +103,21 @@ impl View for ContactsPopover { Child::ContactFinder(child) => ChildView::new(child), }; - child - .contained() - .with_style(theme.contacts_popover.container) - .constrained() - .with_width(theme.contacts_popover.width) - .with_height(theme.contacts_popover.height) - .boxed() + let mut container_style = theme.contacts_popover.container; + if self.is_popup { + container_style.shadow = Default::default(); + container_style.border = Default::default(); + container_style.corner_radius = Default::default(); + child.contained().with_style(container_style).boxed() + } else { + child + .contained() + .with_style(container_style) + .constrained() + .with_width(theme.contacts_popover.width) + .with_height(theme.contacts_popover.height) + .boxed() + } } fn on_focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext) { diff --git a/crates/collab_ui/src/menu_bar_extra.rs b/crates/collab_ui/src/menu_bar_extra.rs index 814d51b189..1db9ceaabd 100644 --- a/crates/collab_ui/src/menu_bar_extra.rs +++ b/crates/collab_ui/src/menu_bar_extra.rs @@ -1,17 +1,18 @@ -use crate::active_call_popover::{self, ActiveCallPopover}; +use crate::contacts_popover::{self, ContactsPopover}; use call::ActiveCall; +use client::UserStore; use gpui::{ actions, color::Color, elements::*, geometry::{rect::RectF, vector::vec2f}, - Appearance, Entity, MouseButton, MutableAppContext, RenderContext, View, ViewContext, - ViewHandle, WindowKind, + Appearance, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, View, + ViewContext, ViewHandle, WindowKind, }; actions!(menu_bar_extra, [ToggleActiveCallPopover]); -pub fn init(cx: &mut MutableAppContext) { +pub fn init(user_store: ModelHandle, cx: &mut MutableAppContext) { cx.add_action(MenuBarExtra::toggle_active_call_popover); let mut status_bar_item_id = None; @@ -24,7 +25,7 @@ pub fn init(cx: &mut MutableAppContext) { } if has_room { - let (id, _) = cx.add_status_bar_item(|_| MenuBarExtra::new()); + let (id, _) = cx.add_status_bar_item(|_| MenuBarExtra::new(user_store.clone())); status_bar_item_id = Some(id); } } @@ -33,7 +34,8 @@ pub fn init(cx: &mut MutableAppContext) { } struct MenuBarExtra { - popover: Option>, + popover: Option>, + user_store: ModelHandle, } impl Entity for MenuBarExtra { @@ -70,8 +72,11 @@ impl View for MenuBarExtra { } impl MenuBarExtra { - fn new() -> Self { - Self { popover: None } + fn new(user_store: ModelHandle) -> Self { + Self { + popover: None, + user_store, + } } fn toggle_active_call_popover( @@ -85,7 +90,7 @@ impl MenuBarExtra { } None => { let window_bounds = cx.window_bounds(); - let size = vec2f(360., 460.); + let size = vec2f(300., 350.); let origin = window_bounds.lower_left() + vec2f(window_bounds.width() / 2. - size.x() / 2., 0.); let (_, popover) = cx.add_window( @@ -96,7 +101,7 @@ impl MenuBarExtra { kind: WindowKind::PopUp, is_movable: false, }, - |cx| ActiveCallPopover::new(cx), + |cx| ContactsPopover::new(true, None, self.user_store.clone(), cx), ); cx.subscribe(&popover, Self::on_popover_event).detach(); self.popover = Some(popover); @@ -106,12 +111,12 @@ impl MenuBarExtra { fn on_popover_event( &mut self, - popover: ViewHandle, - event: &active_call_popover::Event, + popover: ViewHandle, + event: &contacts_popover::Event, cx: &mut ViewContext, ) { match event { - active_call_popover::Event::Deactivated => { + contacts_popover::Event::Dismissed => { self.popover.take(); cx.remove_window(popover.window_id()); } From 8dc99d42ff643a77ee1de2c087270ab7fd313e8f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 10 Oct 2022 18:21:11 +0200 Subject: [PATCH 216/314] Remove menu bar extra --- assets/icons/zed_22.svg | 4 - crates/collab_ui/src/collab_titlebar_item.rs | 3 +- crates/collab_ui/src/collab_ui.rs | 2 - crates/collab_ui/src/contact_list.rs | 14 +-- crates/collab_ui/src/contacts_popover.rs | 29 ++--- crates/collab_ui/src/menu_bar_extra.rs | 125 ------------------- 6 files changed, 17 insertions(+), 160 deletions(-) delete mode 100644 assets/icons/zed_22.svg delete mode 100644 crates/collab_ui/src/menu_bar_extra.rs diff --git a/assets/icons/zed_22.svg b/assets/icons/zed_22.svg deleted file mode 100644 index 68e7dc8e57..0000000000 --- a/assets/icons/zed_22.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index bbe27ce3a9..c46861b5ab 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -163,8 +163,7 @@ impl CollabTitlebarItem { if let Some(workspace) = self.workspace.upgrade(cx) { let project = workspace.read(cx).project().clone(); let user_store = workspace.read(cx).user_store().clone(); - let view = cx - .add_view(|cx| ContactsPopover::new(false, Some(project), user_store, cx)); + let view = cx.add_view(|cx| ContactsPopover::new(project, user_store, cx)); cx.focus(&view); cx.subscribe(&view, |this, _, event, cx| { match event { diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 221ee6068f..72595c2338 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -4,7 +4,6 @@ mod contact_list; mod contact_notification; mod contacts_popover; mod incoming_call_notification; -mod menu_bar_extra; mod notifications; mod project_shared_notification; @@ -22,7 +21,6 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { contact_finder::init(cx); contacts_popover::init(cx); incoming_call_notification::init(cx); - menu_bar_extra::init(app_state.user_store.clone(), cx); project_shared_notification::init(cx); cx.add_global_action(move |action: &JoinProject, cx| { diff --git a/crates/collab_ui/src/contact_list.rs b/crates/collab_ui/src/contact_list.rs index da48015b4e..a539f8ffac 100644 --- a/crates/collab_ui/src/contact_list.rs +++ b/crates/collab_ui/src/contact_list.rs @@ -114,7 +114,7 @@ pub struct ContactList { entries: Vec, match_candidates: Vec, list_state: ListState, - project: Option>, + project: ModelHandle, user_store: ModelHandle, filter_editor: ViewHandle, collapsed_sections: Vec
, @@ -124,7 +124,7 @@ pub struct ContactList { impl ContactList { pub fn new( - project: Option>, + project: ModelHandle, user_store: ModelHandle, cx: &mut ViewContext, ) -> Self { @@ -195,7 +195,7 @@ impl ContactList { ), ContactEntry::Contact(contact) => Self::render_contact( contact, - this.project.as_ref(), + &this.project, &theme.contact_list, is_selected, cx, @@ -292,7 +292,7 @@ impl ContactList { self.call( &Call { recipient_user_id: contact.user.id, - initial_project: self.project.clone(), + initial_project: Some(self.project.clone()), }, cx, ); @@ -664,7 +664,7 @@ impl ContactList { fn render_contact( contact: &Contact, - project: Option<&ModelHandle>, + project: &ModelHandle, theme: &theme::ContactList, is_selected: bool, cx: &mut RenderContext, @@ -672,7 +672,7 @@ impl ContactList { let online = contact.online; let busy = contact.busy; let user_id = contact.user.id; - let initial_project = project.cloned(); + let initial_project = project.clone(); let mut element = MouseEventHandler::::new(contact.user.id as usize, cx, |_, _| { Flex::row() @@ -726,7 +726,7 @@ impl ContactList { if online && !busy { cx.dispatch_action(Call { recipient_user_id: user_id, - initial_project: initial_project.clone(), + initial_project: Some(initial_project.clone()), }); } }); diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index dd67cfe724..b9b1d254b6 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -23,9 +23,8 @@ enum Child { } pub struct ContactsPopover { - is_popup: bool, child: Child, - project: Option>, + project: ModelHandle, user_store: ModelHandle, _subscription: Option, _window_subscription: gpui::Subscription, @@ -33,13 +32,11 @@ pub struct ContactsPopover { impl ContactsPopover { pub fn new( - is_popup: bool, - project: Option>, + project: ModelHandle, user_store: ModelHandle, cx: &mut ViewContext, ) -> Self { let mut this = Self { - is_popup, child: Child::ContactList( cx.add_view(|cx| ContactList::new(project.clone(), user_store.clone(), cx)), ), @@ -103,21 +100,13 @@ impl View for ContactsPopover { Child::ContactFinder(child) => ChildView::new(child), }; - let mut container_style = theme.contacts_popover.container; - if self.is_popup { - container_style.shadow = Default::default(); - container_style.border = Default::default(); - container_style.corner_radius = Default::default(); - child.contained().with_style(container_style).boxed() - } else { - child - .contained() - .with_style(container_style) - .constrained() - .with_width(theme.contacts_popover.width) - .with_height(theme.contacts_popover.height) - .boxed() - } + child + .contained() + .with_style(theme.contacts_popover.container) + .constrained() + .with_width(theme.contacts_popover.width) + .with_height(theme.contacts_popover.height) + .boxed() } fn on_focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext) { diff --git a/crates/collab_ui/src/menu_bar_extra.rs b/crates/collab_ui/src/menu_bar_extra.rs deleted file mode 100644 index 1db9ceaabd..0000000000 --- a/crates/collab_ui/src/menu_bar_extra.rs +++ /dev/null @@ -1,125 +0,0 @@ -use crate::contacts_popover::{self, ContactsPopover}; -use call::ActiveCall; -use client::UserStore; -use gpui::{ - actions, - color::Color, - elements::*, - geometry::{rect::RectF, vector::vec2f}, - Appearance, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, View, - ViewContext, ViewHandle, WindowKind, -}; - -actions!(menu_bar_extra, [ToggleActiveCallPopover]); - -pub fn init(user_store: ModelHandle, cx: &mut MutableAppContext) { - cx.add_action(MenuBarExtra::toggle_active_call_popover); - - let mut status_bar_item_id = None; - cx.observe(&ActiveCall::global(cx), move |call, cx| { - let had_room = status_bar_item_id.is_some(); - let has_room = call.read(cx).room().is_some(); - if had_room != has_room { - if let Some(status_bar_item_id) = status_bar_item_id.take() { - cx.remove_status_bar_item(status_bar_item_id); - } - - if has_room { - let (id, _) = cx.add_status_bar_item(|_| MenuBarExtra::new(user_store.clone())); - status_bar_item_id = Some(id); - } - } - }) - .detach(); -} - -struct MenuBarExtra { - popover: Option>, - user_store: ModelHandle, -} - -impl Entity for MenuBarExtra { - type Event = (); - - fn release(&mut self, cx: &mut MutableAppContext) { - if let Some(popover) = self.popover.take() { - cx.remove_window(popover.window_id()); - } - } -} - -impl View for MenuBarExtra { - fn ui_name() -> &'static str { - "MenuBarExtra" - } - - fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - let color = match cx.appearance { - Appearance::Light | Appearance::VibrantLight => Color::black(), - Appearance::Dark | Appearance::VibrantDark => Color::white(), - }; - MouseEventHandler::::new(0, cx, |_, _| { - Svg::new("icons/zed_22.svg") - .with_color(color) - .aligned() - .boxed() - }) - .on_click(MouseButton::Left, |_, cx| { - cx.dispatch_action(ToggleActiveCallPopover); - }) - .boxed() - } -} - -impl MenuBarExtra { - fn new(user_store: ModelHandle) -> Self { - Self { - popover: None, - user_store, - } - } - - fn toggle_active_call_popover( - &mut self, - _: &ToggleActiveCallPopover, - cx: &mut ViewContext, - ) { - match self.popover.take() { - Some(popover) => { - cx.remove_window(popover.window_id()); - } - None => { - let window_bounds = cx.window_bounds(); - let size = vec2f(300., 350.); - let origin = window_bounds.lower_left() - + vec2f(window_bounds.width() / 2. - size.x() / 2., 0.); - let (_, popover) = cx.add_window( - gpui::WindowOptions { - bounds: gpui::WindowBounds::Fixed(RectF::new(origin, size)), - titlebar: None, - center: false, - kind: WindowKind::PopUp, - is_movable: false, - }, - |cx| ContactsPopover::new(true, None, self.user_store.clone(), cx), - ); - cx.subscribe(&popover, Self::on_popover_event).detach(); - self.popover = Some(popover); - } - } - } - - fn on_popover_event( - &mut self, - popover: ViewHandle, - event: &contacts_popover::Event, - cx: &mut ViewContext, - ) { - match event { - contacts_popover::Event::Dismissed => { - self.popover.take(); - cx.remove_window(popover.window_id()); - } - } - } -} From 94c68d246e1c009b481043ca15c94ba971f8cc16 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 10 Oct 2022 19:18:05 +0200 Subject: [PATCH 217/314] :memo: Co-Authored-By: Nathan Sobo --- crates/workspace/src/pane_group.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index 694f6a55eb..83ce28fdf0 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -153,7 +153,7 @@ impl Member { |_, _| { Label::new( format!( - "Follow {} on their currently active project", + "Follow {} on their active project", leader_user.github_login, ), theme From 2f96a09c4651de42e5e6151b6aa059e08c3b24c9 Mon Sep 17 00:00:00 2001 From: Julia Date: Mon, 10 Oct 2022 10:29:51 -0400 Subject: [PATCH 218/314] Don't select on copy by default in the terminal Co-Authored-By: Mikayla Maki --- assets/settings/default.json | 3 +++ crates/settings/src/settings.rs | 4 +++- crates/terminal/src/terminal.rs | 18 ++++++++++++++---- crates/terminal/src/terminal_element.rs | 12 ++++++------ 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/assets/settings/default.json b/assets/settings/default.json index fddac662a5..ac5cfb8612 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -148,6 +148,9 @@ // 2. Make the option keys behave as a 'meta' key, e.g. for emacs // "option_to_meta": true, "option_as_meta": false, + // Whether or not selecting text in the terminal will automatically + // copy to the system clipboard. + "copy_on_select": false, // Any key-value pairs added to this list will be added to the terminal's // enviroment. Use `:` to seperate multiple values. "env": { diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index fd04fc0aa6..d661eb0f21 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -129,6 +129,7 @@ pub struct TerminalSettings { pub blinking: Option, pub alternate_scroll: Option, pub option_as_meta: Option, + pub copy_on_select: Option, } #[derive(Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)] @@ -274,7 +275,7 @@ impl Settings { editor_overrides: Default::default(), git: defaults.git.unwrap(), git_overrides: Default::default(), - terminal_defaults: Default::default(), + terminal_defaults: defaults.terminal, terminal_overrides: Default::default(), language_defaults: defaults.languages, language_overrides: Default::default(), @@ -327,6 +328,7 @@ impl Settings { self.editor_overrides = data.editor; self.git_overrides = data.git.unwrap_or_default(); self.terminal_defaults.font_size = data.terminal.font_size; + self.terminal_overrides.copy_on_select = data.terminal.copy_on_select; self.terminal_overrides = data.terminal; self.language_overrides = data.languages; self.lsp = data.lsp; diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 473bbd4f52..0f9d16734b 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -1045,7 +1045,18 @@ impl Terminal { } } - pub fn mouse_up(&mut self, e: &UpRegionEvent, origin: Vector2F) { + pub fn mouse_up(&mut self, e: &UpRegionEvent, origin: Vector2F, cx: &mut ModelContext) { + let settings = cx.global::(); + let copy_on_select = settings + .terminal_overrides + .copy_on_select + .unwrap_or_else(|| { + settings + .terminal_defaults + .copy_on_select + .expect("Should be set in defaults") + }); + let position = e.position.sub(origin); if self.mouse_mode(e.shift) { let point = grid_point( @@ -1057,11 +1068,10 @@ impl Terminal { if let Some(bytes) = mouse_button_report(point, e, false, self.last_content.mode) { self.pty_tx.notify(bytes); } - } else if e.button == MouseButton::Left { - // Seems pretty standard to automatically copy on mouse_up for terminals, - // so let's do that here + } else if e.button == MouseButton::Left && copy_on_select { self.copy(); } + self.selection_phase = SelectionPhase::Ended; self.last_mouse = None; } diff --git a/crates/terminal/src/terminal_element.rs b/crates/terminal/src/terminal_element.rs index e7fd69fe49..23538d0fe3 100644 --- a/crates/terminal/src/terminal_element.rs +++ b/crates/terminal/src/terminal_element.rs @@ -424,8 +424,8 @@ impl TerminalElement { TerminalElement::generic_button_handler( connection, origin, - move |terminal, origin, e, _cx| { - terminal.mouse_up(&e, origin); + move |terminal, origin, e, cx| { + terminal.mouse_up(&e, origin, cx); }, ), ) @@ -492,8 +492,8 @@ impl TerminalElement { TerminalElement::generic_button_handler( connection, origin, - move |terminal, origin, e, _cx| { - terminal.mouse_up(&e, origin); + move |terminal, origin, e, cx| { + terminal.mouse_up(&e, origin, cx); }, ), ) @@ -502,8 +502,8 @@ impl TerminalElement { TerminalElement::generic_button_handler( connection, origin, - move |terminal, origin, e, _cx| { - terminal.mouse_up(&e, origin); + move |terminal, origin, e, cx| { + terminal.mouse_up(&e, origin, cx); }, ), ) From 1d2495d57b12b1c508ea1b86349c19e37b38d704 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 10 Oct 2022 11:38:28 -0700 Subject: [PATCH 219/314] Re-arrange how lines are set --- crates/editor/src/editor.rs | 14 ++++++-------- crates/editor/src/element.rs | 4 ++-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index ee795bb0eb..38ca82b0d2 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -451,7 +451,7 @@ pub struct Editor { leader_replica_id: Option, hover_state: HoverState, link_go_to_definition_state: LinkGoToDefinitionState, - lines: Option, + visible_line_count: Option, _subscriptions: Vec, } @@ -1053,7 +1053,7 @@ impl Editor { leader_replica_id: None, hover_state: Default::default(), link_go_to_definition_state: Default::default(), - lines: None, + visible_line_count: None, _subscriptions: vec![ cx.observe(&buffer, Self::on_buffer_changed), cx.subscribe(&buffer, Self::on_buffer_event), @@ -1188,8 +1188,8 @@ impl Editor { cx.notify(); } - fn set_lines(&mut self, lines: f32) { - self.lines = Some(lines) + fn set_visible_line_count(&mut self, lines: f32) { + self.visible_line_count = Some(lines) } fn set_scroll_top_anchor( @@ -5521,8 +5521,7 @@ impl Editor { } pub fn page_up(&mut self, _: &PageUp, cx: &mut ViewContext) { - log::info!("Editor::page_up"); - let lines = match self.lines { + let lines = match self.visible_line_count { Some(lines) => lines, None => return, }; @@ -5533,8 +5532,7 @@ impl Editor { } pub fn page_down(&mut self, _: &PageDown, cx: &mut ViewContext) { - log::info!("Editor::page_up"); - let lines = match self.lines { + let lines = match self.visible_line_count { Some(lines) => lines, None => return, }; diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index d1ab64d7b7..587133e9dd 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1422,6 +1422,8 @@ impl Element for EditorElement { let em_advance = style.text.em_advance(cx.font_cache); let overscroll = vec2f(em_width, 0.); let snapshot = self.update_view(cx.app, |view, cx| { + view.set_visible_line_count(size.y() / line_height); + let wrap_width = match view.soft_wrap_mode(cx) { SoftWrap::None => Some((MAX_LINE_LEN / 2) as f32 * em_advance), SoftWrap::EditorWidth => { @@ -1495,8 +1497,6 @@ impl Element for EditorElement { let mut highlighted_rows = None; let mut highlighted_ranges = Vec::new(); self.update_view(cx.app, |view, cx| { - view.set_lines(size.y() / line_height); - let display_map = view.display_map.update(cx, |map, cx| map.snapshot(cx)); highlighted_rows = view.highlighted_rows(); From 3ae96f2c6eb0612a6d0ec4729da64ff2f7260a15 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 10 Oct 2022 15:15:43 -0600 Subject: [PATCH 220/314] Don't autoclose brackets when is false --- crates/editor/src/editor.rs | 2 +- crates/editor/src/editor_tests.rs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 38ca82b0d2..d1dfed3c85 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1874,7 +1874,7 @@ impl Editor { let mut bracket_pair = None; let mut is_bracket_pair_start = false; for pair in language.brackets() { - if pair.start.ends_with(text.as_ref()) { + if pair.close && pair.start.ends_with(text.as_ref()) { bracket_pair = Some(pair.clone()); is_bracket_pair_start = true; break; diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 430b958407..3525fa4676 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -3016,6 +3016,11 @@ async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) { 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)); From 425e540c9a236ba495e25b2de527a5fe0ffae4a4 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 10 Oct 2022 15:29:24 -0600 Subject: [PATCH 221/314] Fix tests by providing close: true --- crates/editor/src/editor_tests.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 3525fa4676..063dcf6098 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -3039,16 +3039,19 @@ async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) { 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() }, ], @@ -3074,16 +3077,19 @@ async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) { 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() }, ], From d2494822b006278d3c94c4cd63b676412deff232 Mon Sep 17 00:00:00 2001 From: K Simmons Date: Mon, 10 Oct 2022 14:46:07 -0700 Subject: [PATCH 222/314] Add assertion context manager to TestAppContext and convert existing vim tests to use neovim backed test context --- Cargo.lock | 2 + crates/editor/src/editor_tests.rs | 14 +- .../editor/src/highlight_matching_bracket.rs | 3 +- crates/editor/src/hover_popover.rs | 4 +- crates/editor/src/link_go_to_definition.rs | 2 +- crates/editor/src/mouse_context_menu.rs | 3 +- crates/editor/src/test.rs | 512 +------------- .../src/test/editor_lsp_test_context.rs | 208 ++++++ crates/editor/src/test/editor_test_context.rs | 273 ++++++++ crates/gpui/Cargo.toml | 1 + crates/gpui/src/app.rs | 634 +---------------- crates/gpui/src/app/test_app_context.rs | 655 ++++++++++++++++++ crates/vim/Cargo.toml | 1 + crates/vim/src/insert.rs | 2 +- crates/vim/src/normal.rs | 470 ++++--------- crates/vim/src/normal/change.rs | 2 +- crates/vim/src/normal/delete.rs | 2 +- crates/vim/src/object.rs | 2 +- crates/vim/src/test.rs | 102 +++ .../neovim_backed_binding_test_context.rs | 7 +- .../src/test/neovim_backed_test_context.rs | 158 +++++ crates/vim/src/test/neovim_connection.rs | 383 ++++++++++ .../vim_binding_test_context.rs | 0 .../vim_test_context.rs | 15 +- crates/vim/src/test_contexts.rs | 9 - .../neovim_backed_test_context.rs | 518 -------------- crates/vim/src/vim.rs | 100 +-- crates/vim/src/visual.rs | 178 ++--- crates/vim/test_data/test_b.json | 1 + crates/vim/test_data/test_cc.json | 1 + crates/vim/test_data/test_dd.json | 1 + crates/vim/test_data/test_delete_left.json | 1 + .../test_data/test_delete_to_end_of_line.json | 1 + .../vim/test_data/test_enter_visual_mode.json | 2 +- .../test_insert_first_non_whitespace.json | 1 + .../test_jump_to_first_non_whitespace.json | 1 + crates/vim/test_data/test_o.json | 1 + crates/vim/test_data/test_p.json | 1 + crates/vim/test_data/test_visual_change.json | 1 + .../test_data/test_visual_line_change.json | 1 + crates/vim/test_data/test_x.json | 1 + 41 files changed, 2062 insertions(+), 2212 deletions(-) create mode 100644 crates/editor/src/test/editor_lsp_test_context.rs create mode 100644 crates/editor/src/test/editor_test_context.rs create mode 100644 crates/gpui/src/app/test_app_context.rs create mode 100644 crates/vim/src/test.rs rename crates/vim/src/{test_contexts => test}/neovim_backed_binding_test_context.rs (92%) create mode 100644 crates/vim/src/test/neovim_backed_test_context.rs create mode 100644 crates/vim/src/test/neovim_connection.rs rename crates/vim/src/{test_contexts => test}/vim_binding_test_context.rs (100%) rename crates/vim/src/{test_contexts => test}/vim_test_context.rs (91%) delete mode 100644 crates/vim/src/test_contexts.rs delete mode 100644 crates/vim/src/test_contexts/neovim_backed_test_context.rs create mode 100644 crates/vim/test_data/test_b.json create mode 100644 crates/vim/test_data/test_cc.json create mode 100644 crates/vim/test_data/test_dd.json create mode 100644 crates/vim/test_data/test_delete_left.json create mode 100644 crates/vim/test_data/test_delete_to_end_of_line.json create mode 100644 crates/vim/test_data/test_insert_first_non_whitespace.json create mode 100644 crates/vim/test_data/test_jump_to_first_non_whitespace.json create mode 100644 crates/vim/test_data/test_o.json create mode 100644 crates/vim/test_data/test_p.json create mode 100644 crates/vim/test_data/test_visual_change.json create mode 100644 crates/vim/test_data/test_visual_line_change.json create mode 100644 crates/vim/test_data/test_x.json diff --git a/Cargo.lock b/Cargo.lock index 1da2348ff9..aa9ad80001 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2420,6 +2420,7 @@ dependencies = [ "futures 0.3.24", "gpui_macros", "image", + "itertools", "lazy_static", "log", "media", @@ -6736,6 +6737,7 @@ dependencies = [ "indoc", "itertools", "language", + "lazy_static", "log", "nvim-rs", "parking_lot 0.11.2", diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 430b958407..d2d7a9dfef 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -1,20 +1,22 @@ +use std::{cell::RefCell, rc::Rc, time::Instant}; + +use futures::StreamExt; +use indoc::indoc; +use unindent::Unindent; + use super::*; use crate::test::{ - assert_text_with_selections, build_editor, select_ranges, EditorLspTestContext, - EditorTestContext, + assert_text_with_selections, build_editor, editor_lsp_test_context::EditorLspTestContext, + editor_test_context::EditorTestContext, select_ranges, }; -use futures::StreamExt; use gpui::{ geometry::rect::RectF, platform::{WindowBounds, WindowOptions}, }; -use indoc::indoc; use language::{FakeLspAdapter, LanguageConfig, LanguageRegistry}; use project::FakeFs; use settings::EditorSettings; -use std::{cell::RefCell, rc::Rc, time::Instant}; use text::Point; -use unindent::Unindent; use util::{ assert_set_eq, test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker}, diff --git a/crates/editor/src/highlight_matching_bracket.rs b/crates/editor/src/highlight_matching_bracket.rs index 789393d70b..043b21db21 100644 --- a/crates/editor/src/highlight_matching_bracket.rs +++ b/crates/editor/src/highlight_matching_bracket.rs @@ -32,8 +32,9 @@ pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut ViewCon #[cfg(test)] mod tests { + use crate::test::editor_lsp_test_context::EditorLspTestContext; + use super::*; - use crate::test::EditorLspTestContext; use indoc::indoc; use language::{BracketPair, Language, LanguageConfig}; diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index 250f8427a5..38b28f0630 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -427,13 +427,13 @@ impl DiagnosticPopover { #[cfg(test)] mod tests { - use futures::StreamExt; use indoc::indoc; use language::{Diagnostic, DiagnosticSet}; use project::HoverBlock; + use smol::stream::StreamExt; - use crate::test::EditorLspTestContext; + use crate::test::editor_lsp_test_context::EditorLspTestContext; use super::*; diff --git a/crates/editor/src/link_go_to_definition.rs b/crates/editor/src/link_go_to_definition.rs index 6b23a04b67..c8294ddb43 100644 --- a/crates/editor/src/link_go_to_definition.rs +++ b/crates/editor/src/link_go_to_definition.rs @@ -400,7 +400,7 @@ mod tests { use indoc::indoc; use lsp::request::{GotoDefinition, GotoTypeDefinition}; - use crate::test::EditorLspTestContext; + use crate::test::editor_lsp_test_context::EditorLspTestContext; use super::*; diff --git a/crates/editor/src/mouse_context_menu.rs b/crates/editor/src/mouse_context_menu.rs index 4adc030d99..d9840fd3fa 100644 --- a/crates/editor/src/mouse_context_menu.rs +++ b/crates/editor/src/mouse_context_menu.rs @@ -70,8 +70,9 @@ pub fn deploy_context_menu( #[cfg(test)] mod tests { + use crate::test::editor_lsp_test_context::EditorLspTestContext; + use super::*; - use crate::test::EditorLspTestContext; use indoc::indoc; #[gpui::test] diff --git a/crates/editor/src/test.rs b/crates/editor/src/test.rs index acee1216d1..48652c44b7 100644 --- a/crates/editor/src/test.rs +++ b/crates/editor/src/test.rs @@ -1,34 +1,14 @@ +pub mod editor_lsp_test_context; +pub mod editor_test_context; + use crate::{ display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint}, - multi_buffer::ToPointUtf16, - AnchorRangeExt, Autoscroll, DisplayPoint, Editor, EditorMode, MultiBuffer, ToPoint, + DisplayPoint, Editor, EditorMode, MultiBuffer, }; -use anyhow::Result; -use collections::BTreeMap; -use futures::{Future, StreamExt}; -use gpui::{ - json, keymap::Keystroke, AppContext, ModelContext, ModelHandle, ViewContext, ViewHandle, -}; -use indoc::indoc; -use itertools::Itertools; -use language::{point_to_lsp, Buffer, BufferSnapshot, FakeLspAdapter, Language, LanguageConfig}; -use lsp::{notification, request}; -use parking_lot::RwLock; -use project::Project; -use settings::Settings; -use std::{ - any::TypeId, - ops::{Deref, DerefMut, Range}, - sync::{ - atomic::{AtomicUsize, Ordering}, - Arc, - }, -}; -use util::{ - assert_set_eq, set_eq, - test::{generate_marked_text, marked_text_offsets, marked_text_ranges}, -}; -use workspace::{pane, AppState, Workspace, WorkspaceHandle}; + +use gpui::{ModelHandle, ViewContext}; + +use util::test::{marked_text_offsets, marked_text_ranges}; #[cfg(test)] #[ctor::ctor] @@ -86,479 +66,3 @@ pub(crate) fn build_editor( ) -> Editor { Editor::new(EditorMode::Full, buffer, None, None, cx) } - -pub struct EditorTestContext<'a> { - pub cx: &'a mut gpui::TestAppContext, - pub window_id: usize, - pub editor: ViewHandle, - pub assertion_context: AssertionContextManager, -} - -impl<'a> EditorTestContext<'a> { - pub fn new(cx: &'a mut gpui::TestAppContext) -> EditorTestContext<'a> { - let (window_id, editor) = cx.update(|cx| { - cx.set_global(Settings::test(cx)); - crate::init(cx); - - let (window_id, editor) = cx.add_window(Default::default(), |cx| { - build_editor(MultiBuffer::build_simple("", cx), cx) - }); - - editor.update(cx, |_, cx| cx.focus_self()); - - (window_id, editor) - }); - - Self { - cx, - window_id, - editor, - assertion_context: AssertionContextManager::new(), - } - } - - pub fn add_assertion_context(&self, context: String) -> ContextHandle { - self.assertion_context.add_context(context) - } - - pub fn condition( - &self, - predicate: impl FnMut(&Editor, &AppContext) -> bool, - ) -> impl Future { - self.editor.condition(self.cx, predicate) - } - - pub fn editor(&self, read: F) -> T - where - F: FnOnce(&Editor, &AppContext) -> T, - { - self.editor.read_with(self.cx, read) - } - - pub fn update_editor(&mut self, update: F) -> T - where - F: FnOnce(&mut Editor, &mut ViewContext) -> T, - { - self.editor.update(self.cx, update) - } - - pub fn multibuffer(&self, read: F) -> T - where - F: FnOnce(&MultiBuffer, &AppContext) -> T, - { - self.editor(|editor, cx| read(editor.buffer().read(cx), cx)) - } - - pub fn update_multibuffer(&mut self, update: F) -> T - where - F: FnOnce(&mut MultiBuffer, &mut ModelContext) -> T, - { - self.update_editor(|editor, cx| editor.buffer().update(cx, update)) - } - - pub fn buffer_text(&self) -> String { - self.multibuffer(|buffer, cx| buffer.snapshot(cx).text()) - } - - pub fn buffer(&self, read: F) -> T - where - F: FnOnce(&Buffer, &AppContext) -> T, - { - self.multibuffer(|multibuffer, cx| { - let buffer = multibuffer.as_singleton().unwrap().read(cx); - read(buffer, cx) - }) - } - - pub fn update_buffer(&mut self, update: F) -> T - where - F: FnOnce(&mut Buffer, &mut ModelContext) -> T, - { - self.update_multibuffer(|multibuffer, cx| { - let buffer = multibuffer.as_singleton().unwrap(); - buffer.update(cx, update) - }) - } - - pub fn buffer_snapshot(&self) -> BufferSnapshot { - self.buffer(|buffer, _| buffer.snapshot()) - } - - pub fn simulate_keystroke(&mut self, keystroke_text: &str) { - let keystroke = Keystroke::parse(keystroke_text).unwrap(); - self.cx.dispatch_keystroke(self.window_id, keystroke, false); - } - - pub fn simulate_keystrokes(&mut self, keystroke_texts: [&str; COUNT]) { - for keystroke_text in keystroke_texts.into_iter() { - self.simulate_keystroke(keystroke_text); - } - } - - pub fn ranges(&self, marked_text: &str) -> Vec> { - let (unmarked_text, ranges) = marked_text_ranges(marked_text, false); - assert_eq!(self.buffer_text(), unmarked_text); - ranges - } - - pub fn display_point(&mut self, marked_text: &str) -> DisplayPoint { - let ranges = self.ranges(marked_text); - let snapshot = self - .editor - .update(self.cx, |editor, cx| editor.snapshot(cx)); - ranges[0].start.to_display_point(&snapshot) - } - - // Returns anchors for the current buffer using `«` and `»` - pub fn text_anchor_range(&self, marked_text: &str) -> Range { - let ranges = self.ranges(marked_text); - let snapshot = self.buffer_snapshot(); - snapshot.anchor_before(ranges[0].start)..snapshot.anchor_after(ranges[0].end) - } - - /// Change the editor's text and selections using a string containing - /// embedded range markers that represent the ranges and directions of - /// each selection. - /// - /// See the `util::test::marked_text_ranges` function for more information. - pub fn set_state(&mut self, marked_text: &str) { - let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true); - self.editor.update(self.cx, |editor, cx| { - editor.set_text(unmarked_text, cx); - editor.change_selections(Some(Autoscroll::Fit), cx, |s| { - s.select_ranges(selection_ranges) - }) - }) - } - - /// Make an assertion about the editor's text and the ranges and directions - /// of its selections using a string containing embedded range markers. - /// - /// See the `util::test::marked_text_ranges` function for more information. - pub fn assert_editor_state(&mut self, marked_text: &str) { - let (unmarked_text, expected_selections) = marked_text_ranges(marked_text, true); - let buffer_text = self.buffer_text(); - assert_eq!( - buffer_text, unmarked_text, - "Unmarked text doesn't match buffer text" - ); - self.assert_selections(expected_selections, marked_text.to_string()) - } - - pub fn assert_editor_background_highlights(&mut self, marked_text: &str) { - let expected_ranges = self.ranges(marked_text); - let actual_ranges: Vec> = self.update_editor(|editor, cx| { - let snapshot = editor.snapshot(cx); - editor - .background_highlights - .get(&TypeId::of::()) - .map(|h| h.1.clone()) - .unwrap_or_default() - .into_iter() - .map(|range| range.to_offset(&snapshot.buffer_snapshot)) - .collect() - }); - assert_set_eq!(actual_ranges, expected_ranges); - } - - pub fn assert_editor_text_highlights(&mut self, marked_text: &str) { - let expected_ranges = self.ranges(marked_text); - let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx)); - let actual_ranges: Vec> = snapshot - .highlight_ranges::() - .map(|ranges| ranges.as_ref().clone().1) - .unwrap_or_default() - .into_iter() - .map(|range| range.to_offset(&snapshot.buffer_snapshot)) - .collect(); - assert_set_eq!(actual_ranges, expected_ranges); - } - - pub fn assert_editor_selections(&mut self, expected_selections: Vec>) { - let expected_marked_text = - generate_marked_text(&self.buffer_text(), &expected_selections, true); - self.assert_selections(expected_selections, expected_marked_text) - } - - fn assert_selections( - &mut self, - expected_selections: Vec>, - expected_marked_text: String, - ) { - let actual_selections = self - .editor - .read_with(self.cx, |editor, cx| editor.selections.all::(cx)) - .into_iter() - .map(|s| { - if s.reversed { - s.end..s.start - } else { - s.start..s.end - } - }) - .collect::>(); - let actual_marked_text = - generate_marked_text(&self.buffer_text(), &actual_selections, true); - if expected_selections != actual_selections { - panic!( - indoc! {" - Editor has unexpected selections. - - Expected selections: - {} - - Actual selections: - {} - "}, - expected_marked_text, actual_marked_text, - ); - } - } -} - -impl<'a> Deref for EditorTestContext<'a> { - type Target = gpui::TestAppContext; - - fn deref(&self) -> &Self::Target { - self.cx - } -} - -impl<'a> DerefMut for EditorTestContext<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.cx - } -} - -pub struct EditorLspTestContext<'a> { - pub cx: EditorTestContext<'a>, - pub lsp: lsp::FakeLanguageServer, - pub workspace: ViewHandle, - pub buffer_lsp_url: lsp::Url, -} - -impl<'a> EditorLspTestContext<'a> { - pub async fn new( - mut language: Language, - capabilities: lsp::ServerCapabilities, - cx: &'a mut gpui::TestAppContext, - ) -> EditorLspTestContext<'a> { - use json::json; - - cx.update(|cx| { - crate::init(cx); - pane::init(cx); - }); - - let params = cx.update(AppState::test); - - let file_name = format!( - "file.{}", - language - .path_suffixes() - .first() - .unwrap_or(&"txt".to_string()) - ); - - let mut fake_servers = language - .set_fake_lsp_adapter(Arc::new(FakeLspAdapter { - capabilities, - ..Default::default() - })) - .await; - - let project = Project::test(params.fs.clone(), [], cx).await; - project.update(cx, |project, _| project.languages().add(Arc::new(language))); - - params - .fs - .as_fake() - .insert_tree("/root", json!({ "dir": { file_name: "" }})) - .await; - - let (window_id, workspace) = - cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx)); - project - .update(cx, |project, cx| { - project.find_or_create_local_worktree("/root", true, cx) - }) - .await - .unwrap(); - cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx)) - .await; - - let file = cx.read(|cx| workspace.file_project_paths(cx)[0].clone()); - let item = workspace - .update(cx, |workspace, cx| workspace.open_path(file, true, cx)) - .await - .expect("Could not open test file"); - - let editor = cx.update(|cx| { - item.act_as::(cx) - .expect("Opened test file wasn't an editor") - }); - editor.update(cx, |_, cx| cx.focus_self()); - - let lsp = fake_servers.next().await.unwrap(); - - Self { - cx: EditorTestContext { - cx, - window_id, - editor, - assertion_context: AssertionContextManager::new(), - }, - lsp, - workspace, - buffer_lsp_url: lsp::Url::from_file_path("/root/dir/file.rs").unwrap(), - } - } - - pub async fn new_rust( - capabilities: lsp::ServerCapabilities, - cx: &'a mut gpui::TestAppContext, - ) -> EditorLspTestContext<'a> { - let language = Language::new( - LanguageConfig { - name: "Rust".into(), - path_suffixes: vec!["rs".to_string()], - ..Default::default() - }, - Some(tree_sitter_rust::language()), - ); - - Self::new(language, capabilities, cx).await - } - - // Constructs lsp range using a marked string with '[', ']' range delimiters - pub fn lsp_range(&mut self, marked_text: &str) -> lsp::Range { - let ranges = self.ranges(marked_text); - self.to_lsp_range(ranges[0].clone()) - } - - pub fn to_lsp_range(&mut self, range: Range) -> lsp::Range { - let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx)); - let start_point = range.start.to_point(&snapshot.buffer_snapshot); - let end_point = range.end.to_point(&snapshot.buffer_snapshot); - - self.editor(|editor, cx| { - let buffer = editor.buffer().read(cx); - let start = point_to_lsp( - buffer - .point_to_buffer_offset(start_point, cx) - .unwrap() - .1 - .to_point_utf16(&buffer.read(cx)), - ); - let end = point_to_lsp( - buffer - .point_to_buffer_offset(end_point, cx) - .unwrap() - .1 - .to_point_utf16(&buffer.read(cx)), - ); - - lsp::Range { start, end } - }) - } - - pub fn to_lsp(&mut self, offset: usize) -> lsp::Position { - let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx)); - let point = offset.to_point(&snapshot.buffer_snapshot); - - self.editor(|editor, cx| { - let buffer = editor.buffer().read(cx); - point_to_lsp( - buffer - .point_to_buffer_offset(point, cx) - .unwrap() - .1 - .to_point_utf16(&buffer.read(cx)), - ) - }) - } - - pub fn update_workspace(&mut self, update: F) -> T - where - F: FnOnce(&mut Workspace, &mut ViewContext) -> T, - { - self.workspace.update(self.cx.cx, update) - } - - pub fn handle_request( - &self, - mut handler: F, - ) -> futures::channel::mpsc::UnboundedReceiver<()> - where - T: 'static + request::Request, - T::Params: 'static + Send, - F: 'static + Send + FnMut(lsp::Url, T::Params, gpui::AsyncAppContext) -> Fut, - Fut: 'static + Send + Future>, - { - let url = self.buffer_lsp_url.clone(); - self.lsp.handle_request::(move |params, cx| { - let url = url.clone(); - handler(url, params, cx) - }) - } - - pub fn notify(&self, params: T::Params) { - self.lsp.notify::(params); - } -} - -impl<'a> Deref for EditorLspTestContext<'a> { - type Target = EditorTestContext<'a>; - - fn deref(&self) -> &Self::Target { - &self.cx - } -} - -impl<'a> DerefMut for EditorLspTestContext<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.cx - } -} - -#[derive(Clone)] -pub struct AssertionContextManager { - id: Arc, - contexts: Arc>>, -} - -impl AssertionContextManager { - pub fn new() -> Self { - Self { - id: Arc::new(AtomicUsize::new(0)), - contexts: Arc::new(RwLock::new(BTreeMap::new())), - } - } - - pub fn add_context(&self, context: String) -> ContextHandle { - let id = self.id.fetch_add(1, Ordering::Relaxed); - let mut contexts = self.contexts.write(); - contexts.insert(id, context); - ContextHandle { - id, - manager: self.clone(), - } - } - - pub fn context(&self) -> String { - let contexts = self.contexts.read(); - format!("\n{}\n", contexts.values().join("\n")) - } -} - -pub struct ContextHandle { - id: usize, - manager: AssertionContextManager, -} - -impl Drop for ContextHandle { - fn drop(&mut self) { - let mut contexts = self.manager.contexts.write(); - contexts.remove(&self.id); - } -} diff --git a/crates/editor/src/test/editor_lsp_test_context.rs b/crates/editor/src/test/editor_lsp_test_context.rs new file mode 100644 index 0000000000..b4a4cd7ab8 --- /dev/null +++ b/crates/editor/src/test/editor_lsp_test_context.rs @@ -0,0 +1,208 @@ +use std::{ + ops::{Deref, DerefMut, Range}, + sync::Arc, +}; + +use anyhow::Result; + +use futures::Future; +use gpui::{json, ViewContext, ViewHandle}; +use language::{point_to_lsp, FakeLspAdapter, Language, LanguageConfig}; +use lsp::{notification, request}; +use project::Project; +use smol::stream::StreamExt; +use workspace::{pane, AppState, Workspace, WorkspaceHandle}; + +use crate::{multi_buffer::ToPointUtf16, Editor, ToPoint}; + +use super::editor_test_context::EditorTestContext; + +pub struct EditorLspTestContext<'a> { + pub cx: EditorTestContext<'a>, + pub lsp: lsp::FakeLanguageServer, + pub workspace: ViewHandle, + pub buffer_lsp_url: lsp::Url, +} + +impl<'a> EditorLspTestContext<'a> { + pub async fn new( + mut language: Language, + capabilities: lsp::ServerCapabilities, + cx: &'a mut gpui::TestAppContext, + ) -> EditorLspTestContext<'a> { + use json::json; + + cx.update(|cx| { + crate::init(cx); + pane::init(cx); + }); + + let params = cx.update(AppState::test); + + let file_name = format!( + "file.{}", + language + .path_suffixes() + .first() + .unwrap_or(&"txt".to_string()) + ); + + let mut fake_servers = language + .set_fake_lsp_adapter(Arc::new(FakeLspAdapter { + capabilities, + ..Default::default() + })) + .await; + + let project = Project::test(params.fs.clone(), [], cx).await; + project.update(cx, |project, _| project.languages().add(Arc::new(language))); + + params + .fs + .as_fake() + .insert_tree("/root", json!({ "dir": { file_name: "" }})) + .await; + + let (window_id, workspace) = + cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx)); + project + .update(cx, |project, cx| { + project.find_or_create_local_worktree("/root", true, cx) + }) + .await + .unwrap(); + cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx)) + .await; + + let file = cx.read(|cx| workspace.file_project_paths(cx)[0].clone()); + let item = workspace + .update(cx, |workspace, cx| workspace.open_path(file, true, cx)) + .await + .expect("Could not open test file"); + + let editor = cx.update(|cx| { + item.act_as::(cx) + .expect("Opened test file wasn't an editor") + }); + editor.update(cx, |_, cx| cx.focus_self()); + + let lsp = fake_servers.next().await.unwrap(); + + Self { + cx: EditorTestContext { + cx, + window_id, + editor, + }, + lsp, + workspace, + buffer_lsp_url: lsp::Url::from_file_path("/root/dir/file.rs").unwrap(), + } + } + + pub async fn new_rust( + capabilities: lsp::ServerCapabilities, + cx: &'a mut gpui::TestAppContext, + ) -> EditorLspTestContext<'a> { + let language = Language::new( + LanguageConfig { + name: "Rust".into(), + path_suffixes: vec!["rs".to_string()], + ..Default::default() + }, + Some(tree_sitter_rust::language()), + ); + + Self::new(language, capabilities, cx).await + } + + // Constructs lsp range using a marked string with '[', ']' range delimiters + pub fn lsp_range(&mut self, marked_text: &str) -> lsp::Range { + let ranges = self.ranges(marked_text); + self.to_lsp_range(ranges[0].clone()) + } + + pub fn to_lsp_range(&mut self, range: Range) -> lsp::Range { + let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx)); + let start_point = range.start.to_point(&snapshot.buffer_snapshot); + let end_point = range.end.to_point(&snapshot.buffer_snapshot); + + self.editor(|editor, cx| { + let buffer = editor.buffer().read(cx); + let start = point_to_lsp( + buffer + .point_to_buffer_offset(start_point, cx) + .unwrap() + .1 + .to_point_utf16(&buffer.read(cx)), + ); + let end = point_to_lsp( + buffer + .point_to_buffer_offset(end_point, cx) + .unwrap() + .1 + .to_point_utf16(&buffer.read(cx)), + ); + + lsp::Range { start, end } + }) + } + + pub fn to_lsp(&mut self, offset: usize) -> lsp::Position { + let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx)); + let point = offset.to_point(&snapshot.buffer_snapshot); + + self.editor(|editor, cx| { + let buffer = editor.buffer().read(cx); + point_to_lsp( + buffer + .point_to_buffer_offset(point, cx) + .unwrap() + .1 + .to_point_utf16(&buffer.read(cx)), + ) + }) + } + + pub fn update_workspace(&mut self, update: F) -> T + where + F: FnOnce(&mut Workspace, &mut ViewContext) -> T, + { + self.workspace.update(self.cx.cx, update) + } + + pub fn handle_request( + &self, + mut handler: F, + ) -> futures::channel::mpsc::UnboundedReceiver<()> + where + T: 'static + request::Request, + T::Params: 'static + Send, + F: 'static + Send + FnMut(lsp::Url, T::Params, gpui::AsyncAppContext) -> Fut, + Fut: 'static + Send + Future>, + { + let url = self.buffer_lsp_url.clone(); + self.lsp.handle_request::(move |params, cx| { + let url = url.clone(); + handler(url, params, cx) + }) + } + + pub fn notify(&self, params: T::Params) { + self.lsp.notify::(params); + } +} + +impl<'a> Deref for EditorLspTestContext<'a> { + type Target = EditorTestContext<'a>; + + fn deref(&self) -> &Self::Target { + &self.cx + } +} + +impl<'a> DerefMut for EditorLspTestContext<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.cx + } +} diff --git a/crates/editor/src/test/editor_test_context.rs b/crates/editor/src/test/editor_test_context.rs new file mode 100644 index 0000000000..73dc6bfd6e --- /dev/null +++ b/crates/editor/src/test/editor_test_context.rs @@ -0,0 +1,273 @@ +use std::{ + any::TypeId, + ops::{Deref, DerefMut, Range}, +}; + +use futures::Future; +use indoc::indoc; + +use crate::{ + display_map::ToDisplayPoint, AnchorRangeExt, Autoscroll, DisplayPoint, Editor, MultiBuffer, +}; +use gpui::{keymap::Keystroke, AppContext, ContextHandle, ModelContext, ViewContext, ViewHandle}; +use language::{Buffer, BufferSnapshot}; +use settings::Settings; +use util::{ + assert_set_eq, + test::{generate_marked_text, marked_text_ranges}, +}; + +use super::build_editor; + +pub struct EditorTestContext<'a> { + pub cx: &'a mut gpui::TestAppContext, + pub window_id: usize, + pub editor: ViewHandle, +} + +impl<'a> EditorTestContext<'a> { + pub fn new(cx: &'a mut gpui::TestAppContext) -> EditorTestContext<'a> { + let (window_id, editor) = cx.update(|cx| { + cx.set_global(Settings::test(cx)); + crate::init(cx); + + let (window_id, editor) = cx.add_window(Default::default(), |cx| { + build_editor(MultiBuffer::build_simple("", cx), cx) + }); + + editor.update(cx, |_, cx| cx.focus_self()); + + (window_id, editor) + }); + + Self { + cx, + window_id, + editor, + } + } + + pub fn condition( + &self, + predicate: impl FnMut(&Editor, &AppContext) -> bool, + ) -> impl Future { + self.editor.condition(self.cx, predicate) + } + + pub fn editor(&self, read: F) -> T + where + F: FnOnce(&Editor, &AppContext) -> T, + { + self.editor.read_with(self.cx, read) + } + + pub fn update_editor(&mut self, update: F) -> T + where + F: FnOnce(&mut Editor, &mut ViewContext) -> T, + { + self.editor.update(self.cx, update) + } + + pub fn multibuffer(&self, read: F) -> T + where + F: FnOnce(&MultiBuffer, &AppContext) -> T, + { + self.editor(|editor, cx| read(editor.buffer().read(cx), cx)) + } + + pub fn update_multibuffer(&mut self, update: F) -> T + where + F: FnOnce(&mut MultiBuffer, &mut ModelContext) -> T, + { + self.update_editor(|editor, cx| editor.buffer().update(cx, update)) + } + + pub fn buffer_text(&self) -> String { + self.multibuffer(|buffer, cx| buffer.snapshot(cx).text()) + } + + pub fn buffer(&self, read: F) -> T + where + F: FnOnce(&Buffer, &AppContext) -> T, + { + self.multibuffer(|multibuffer, cx| { + let buffer = multibuffer.as_singleton().unwrap().read(cx); + read(buffer, cx) + }) + } + + pub fn update_buffer(&mut self, update: F) -> T + where + F: FnOnce(&mut Buffer, &mut ModelContext) -> T, + { + self.update_multibuffer(|multibuffer, cx| { + let buffer = multibuffer.as_singleton().unwrap(); + buffer.update(cx, update) + }) + } + + pub fn buffer_snapshot(&self) -> BufferSnapshot { + self.buffer(|buffer, _| buffer.snapshot()) + } + + pub fn simulate_keystroke(&mut self, keystroke_text: &str) -> ContextHandle { + let keystroke_under_test_handle = + self.add_assertion_context(format!("Simulated Keystroke: {:?}", keystroke_text)); + let keystroke = Keystroke::parse(keystroke_text).unwrap(); + self.cx.dispatch_keystroke(self.window_id, keystroke, false); + keystroke_under_test_handle + } + + pub fn simulate_keystrokes( + &mut self, + keystroke_texts: [&str; COUNT], + ) -> ContextHandle { + let keystrokes_under_test_handle = + self.add_assertion_context(format!("Simulated Keystrokes: {:?}", keystroke_texts)); + for keystroke_text in keystroke_texts.into_iter() { + self.simulate_keystroke(keystroke_text); + } + keystrokes_under_test_handle + } + + pub fn ranges(&self, marked_text: &str) -> Vec> { + let (unmarked_text, ranges) = marked_text_ranges(marked_text, false); + assert_eq!(self.buffer_text(), unmarked_text); + ranges + } + + pub fn display_point(&mut self, marked_text: &str) -> DisplayPoint { + let ranges = self.ranges(marked_text); + let snapshot = self + .editor + .update(self.cx, |editor, cx| editor.snapshot(cx)); + ranges[0].start.to_display_point(&snapshot) + } + + // Returns anchors for the current buffer using `«` and `»` + pub fn text_anchor_range(&self, marked_text: &str) -> Range { + let ranges = self.ranges(marked_text); + let snapshot = self.buffer_snapshot(); + snapshot.anchor_before(ranges[0].start)..snapshot.anchor_after(ranges[0].end) + } + + /// Change the editor's text and selections using a string containing + /// embedded range markers that represent the ranges and directions of + /// each selection. + /// + /// See the `util::test::marked_text_ranges` function for more information. + pub fn set_state(&mut self, marked_text: &str) -> ContextHandle { + let _state_context = self.add_assertion_context(format!( + "Editor State: \"{}\"", + marked_text.escape_debug().to_string() + )); + let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true); + self.editor.update(self.cx, |editor, cx| { + editor.set_text(unmarked_text, cx); + editor.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.select_ranges(selection_ranges) + }) + }); + _state_context + } + + /// Make an assertion about the editor's text and the ranges and directions + /// of its selections using a string containing embedded range markers. + /// + /// See the `util::test::marked_text_ranges` function for more information. + pub fn assert_editor_state(&mut self, marked_text: &str) { + let (unmarked_text, expected_selections) = marked_text_ranges(marked_text, true); + let buffer_text = self.buffer_text(); + assert_eq!( + buffer_text, unmarked_text, + "Unmarked text doesn't match buffer text" + ); + self.assert_selections(expected_selections, marked_text.to_string()) + } + + pub fn assert_editor_background_highlights(&mut self, marked_text: &str) { + let expected_ranges = self.ranges(marked_text); + let actual_ranges: Vec> = self.update_editor(|editor, cx| { + let snapshot = editor.snapshot(cx); + editor + .background_highlights + .get(&TypeId::of::()) + .map(|h| h.1.clone()) + .unwrap_or_default() + .into_iter() + .map(|range| range.to_offset(&snapshot.buffer_snapshot)) + .collect() + }); + assert_set_eq!(actual_ranges, expected_ranges); + } + + pub fn assert_editor_text_highlights(&mut self, marked_text: &str) { + let expected_ranges = self.ranges(marked_text); + let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx)); + let actual_ranges: Vec> = snapshot + .highlight_ranges::() + .map(|ranges| ranges.as_ref().clone().1) + .unwrap_or_default() + .into_iter() + .map(|range| range.to_offset(&snapshot.buffer_snapshot)) + .collect(); + assert_set_eq!(actual_ranges, expected_ranges); + } + + pub fn assert_editor_selections(&mut self, expected_selections: Vec>) { + let expected_marked_text = + generate_marked_text(&self.buffer_text(), &expected_selections, true); + self.assert_selections(expected_selections, expected_marked_text) + } + + fn assert_selections( + &mut self, + expected_selections: Vec>, + expected_marked_text: String, + ) { + let actual_selections = self + .editor + .read_with(self.cx, |editor, cx| editor.selections.all::(cx)) + .into_iter() + .map(|s| { + if s.reversed { + s.end..s.start + } else { + s.start..s.end + } + }) + .collect::>(); + let actual_marked_text = + generate_marked_text(&self.buffer_text(), &actual_selections, true); + if expected_selections != actual_selections { + panic!( + indoc! {" + {}Editor has unexpected selections. + + Expected selections: + {} + + Actual selections: + {} + "}, + self.assertion_context(), + expected_marked_text, + actual_marked_text, + ); + } + } +} + +impl<'a> Deref for EditorTestContext<'a> { + type Target = gpui::TestAppContext; + + fn deref(&self) -> &Self::Target { + self.cx + } +} + +impl<'a> DerefMut for EditorTestContext<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.cx + } +} diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 51bc416e19..54fe5e46a2 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -25,6 +25,7 @@ env_logger = { version = "0.9", optional = true } etagere = "0.2" futures = "0.3" image = "0.23" +itertools = "0.10" lazy_static = "1.4.0" log = { version = "0.4.16", features = ["kv_unstable_serde"] } num_cpus = "1.13" diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 1da9f35dc8..16c0a3bae4 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1,28 +1,8 @@ pub mod action; mod callback_collection; +#[cfg(any(test, feature = "test-support"))] +pub mod test_app_context; -use crate::{ - elements::ElementBox, - executor::{self, Task}, - geometry::rect::RectF, - keymap::{self, Binding, Keystroke}, - platform::{self, KeyDownEvent, Platform, PromptLevel, WindowOptions}, - presenter::Presenter, - util::post_inc, - Appearance, AssetCache, AssetSource, ClipboardItem, FontCache, InputHandler, MouseButton, - MouseRegionId, PathPromptOptions, TextLayoutCache, -}; -pub use action::*; -use anyhow::{anyhow, Context, Result}; -use callback_collection::CallbackCollection; -use collections::{btree_map, hash_map::Entry, BTreeMap, HashMap, HashSet, VecDeque}; -use keymap::MatchResult; -use lazy_static::lazy_static; -use parking_lot::Mutex; -use platform::Event; -use postage::oneshot; -use smallvec::SmallVec; -use smol::prelude::*; use std::{ any::{type_name, Any, TypeId}, cell::RefCell, @@ -38,7 +18,32 @@ use std::{ time::Duration, }; -use self::callback_collection::Mapping; +use anyhow::{anyhow, Context, Result}; +use lazy_static::lazy_static; +use parking_lot::Mutex; +use postage::oneshot; +use smallvec::SmallVec; +use smol::prelude::*; + +pub use action::*; +use callback_collection::{CallbackCollection, Mapping}; +use collections::{btree_map, hash_map::Entry, BTreeMap, HashMap, HashSet, VecDeque}; +use keymap::MatchResult; +use platform::Event; +#[cfg(any(test, feature = "test-support"))] +pub use test_app_context::{ContextHandle, TestAppContext}; + +use crate::{ + elements::ElementBox, + executor::{self, Task}, + geometry::rect::RectF, + keymap::{self, Binding, Keystroke}, + platform::{self, KeyDownEvent, Platform, PromptLevel, WindowOptions}, + presenter::Presenter, + util::post_inc, + Appearance, AssetCache, AssetSource, ClipboardItem, FontCache, InputHandler, MouseButton, + MouseRegionId, PathPromptOptions, TextLayoutCache, +}; pub trait Entity: 'static { type Event; @@ -177,14 +182,6 @@ pub struct App(Rc>); #[derive(Clone)] pub struct AsyncAppContext(Rc>); -#[cfg(any(test, feature = "test-support"))] -pub struct TestAppContext { - cx: Rc>, - foreground_platform: Rc, - condition_duration: Option, - pub function_name: String, -} - pub struct WindowInputHandler { app: Rc>, window_id: usize, @@ -428,329 +425,6 @@ impl InputHandler for WindowInputHandler { } } -#[cfg(any(test, feature = "test-support"))] -impl TestAppContext { - pub fn new( - foreground_platform: Rc, - platform: Arc, - foreground: Rc, - background: Arc, - font_cache: Arc, - leak_detector: Arc>, - first_entity_id: usize, - function_name: String, - ) -> Self { - let mut cx = MutableAppContext::new( - foreground, - background, - platform, - foreground_platform.clone(), - font_cache, - RefCounts { - #[cfg(any(test, feature = "test-support"))] - leak_detector, - ..Default::default() - }, - (), - ); - cx.next_entity_id = first_entity_id; - let cx = TestAppContext { - cx: Rc::new(RefCell::new(cx)), - foreground_platform, - condition_duration: None, - function_name, - }; - cx.cx.borrow_mut().weak_self = Some(Rc::downgrade(&cx.cx)); - cx - } - - pub fn dispatch_action(&self, window_id: usize, action: A) { - let mut cx = self.cx.borrow_mut(); - if let Some(view_id) = cx.focused_view_id(window_id) { - cx.handle_dispatch_action_from_effect(window_id, Some(view_id), &action); - } - } - - pub fn dispatch_global_action(&self, action: A) { - self.cx.borrow_mut().dispatch_global_action(action); - } - - pub fn dispatch_keystroke(&mut self, window_id: usize, keystroke: Keystroke, is_held: bool) { - let handled = self.cx.borrow_mut().update(|cx| { - let presenter = cx - .presenters_and_platform_windows - .get(&window_id) - .unwrap() - .0 - .clone(); - - if cx.dispatch_keystroke(window_id, &keystroke) { - return true; - } - - if presenter.borrow_mut().dispatch_event( - Event::KeyDown(KeyDownEvent { - keystroke: keystroke.clone(), - is_held, - }), - false, - cx, - ) { - return true; - } - - false - }); - - if !handled && !keystroke.cmd && !keystroke.ctrl { - WindowInputHandler { - app: self.cx.clone(), - window_id, - } - .replace_text_in_range(None, &keystroke.key) - } - } - - pub fn add_model(&mut self, build_model: F) -> ModelHandle - where - T: Entity, - F: FnOnce(&mut ModelContext) -> T, - { - self.cx.borrow_mut().add_model(build_model) - } - - pub fn add_window(&mut self, build_root_view: F) -> (usize, ViewHandle) - where - T: View, - F: FnOnce(&mut ViewContext) -> T, - { - let (window_id, view) = self - .cx - .borrow_mut() - .add_window(Default::default(), build_root_view); - self.simulate_window_activation(Some(window_id)); - (window_id, view) - } - - pub fn add_view( - &mut self, - parent_handle: impl Into, - build_view: F, - ) -> ViewHandle - where - T: View, - F: FnOnce(&mut ViewContext) -> T, - { - self.cx.borrow_mut().add_view(parent_handle, build_view) - } - - pub fn window_ids(&self) -> Vec { - self.cx.borrow().window_ids().collect() - } - - pub fn root_view(&self, window_id: usize) -> Option> { - self.cx.borrow().root_view(window_id) - } - - pub fn read T>(&self, callback: F) -> T { - callback(self.cx.borrow().as_ref()) - } - - pub fn update T>(&mut self, callback: F) -> T { - let mut state = self.cx.borrow_mut(); - // Don't increment pending flushes in order for effects to be flushed before the callback - // completes, which is helpful in tests. - let result = callback(&mut *state); - // Flush effects after the callback just in case there are any. This can happen in edge - // cases such as the closure dropping handles. - state.flush_effects(); - result - } - - pub fn render(&mut self, handle: &ViewHandle, f: F) -> T - where - F: FnOnce(&mut V, &mut RenderContext) -> T, - V: View, - { - handle.update(&mut *self.cx.borrow_mut(), |view, cx| { - let mut render_cx = RenderContext { - app: cx, - window_id: handle.window_id(), - view_id: handle.id(), - view_type: PhantomData, - titlebar_height: 0., - hovered_region_ids: Default::default(), - clicked_region_ids: None, - refreshing: false, - appearance: Appearance::Light, - }; - f(view, &mut render_cx) - }) - } - - pub fn to_async(&self) -> AsyncAppContext { - AsyncAppContext(self.cx.clone()) - } - - pub fn font_cache(&self) -> Arc { - self.cx.borrow().cx.font_cache.clone() - } - - pub fn foreground_platform(&self) -> Rc { - self.foreground_platform.clone() - } - - pub fn platform(&self) -> Arc { - self.cx.borrow().cx.platform.clone() - } - - pub fn foreground(&self) -> Rc { - self.cx.borrow().foreground().clone() - } - - pub fn background(&self) -> Arc { - self.cx.borrow().background().clone() - } - - pub fn spawn(&self, f: F) -> Task - where - F: FnOnce(AsyncAppContext) -> Fut, - Fut: 'static + Future, - T: 'static, - { - let foreground = self.foreground(); - let future = f(self.to_async()); - let cx = self.to_async(); - foreground.spawn(async move { - let result = future.await; - cx.0.borrow_mut().flush_effects(); - result - }) - } - - pub fn simulate_new_path_selection(&self, result: impl FnOnce(PathBuf) -> Option) { - self.foreground_platform.simulate_new_path_selection(result); - } - - pub fn did_prompt_for_new_path(&self) -> bool { - self.foreground_platform.as_ref().did_prompt_for_new_path() - } - - pub fn simulate_prompt_answer(&self, window_id: usize, answer: usize) { - use postage::prelude::Sink as _; - - let mut done_tx = self - .window_mut(window_id) - .pending_prompts - .borrow_mut() - .pop_front() - .expect("prompt was not called"); - let _ = done_tx.try_send(answer); - } - - pub fn has_pending_prompt(&self, window_id: usize) -> bool { - let window = self.window_mut(window_id); - let prompts = window.pending_prompts.borrow_mut(); - !prompts.is_empty() - } - - pub fn current_window_title(&self, window_id: usize) -> Option { - self.window_mut(window_id).title.clone() - } - - pub fn simulate_window_close(&self, window_id: usize) -> bool { - let handler = self.window_mut(window_id).should_close_handler.take(); - if let Some(mut handler) = handler { - let should_close = handler(); - self.window_mut(window_id).should_close_handler = Some(handler); - should_close - } else { - false - } - } - - pub fn simulate_window_activation(&self, to_activate: Option) { - let mut handlers = BTreeMap::new(); - { - let mut cx = self.cx.borrow_mut(); - for (window_id, (_, window)) in &mut cx.presenters_and_platform_windows { - let window = window - .as_any_mut() - .downcast_mut::() - .unwrap(); - handlers.insert( - *window_id, - mem::take(&mut window.active_status_change_handlers), - ); - } - }; - let mut handlers = handlers.into_iter().collect::>(); - handlers.sort_unstable_by_key(|(window_id, _)| Some(*window_id) == to_activate); - - for (window_id, mut window_handlers) in handlers { - for window_handler in &mut window_handlers { - window_handler(Some(window_id) == to_activate); - } - - self.window_mut(window_id) - .active_status_change_handlers - .extend(window_handlers); - } - } - - pub fn is_window_edited(&self, window_id: usize) -> bool { - self.window_mut(window_id).edited - } - - pub fn leak_detector(&self) -> Arc> { - self.cx.borrow().leak_detector() - } - - pub fn assert_dropped(&self, handle: impl WeakHandle) { - self.cx - .borrow() - .leak_detector() - .lock() - .assert_dropped(handle.id()) - } - - fn window_mut(&self, window_id: usize) -> std::cell::RefMut { - std::cell::RefMut::map(self.cx.borrow_mut(), |state| { - let (_, window) = state - .presenters_and_platform_windows - .get_mut(&window_id) - .unwrap(); - let test_window = window - .as_any_mut() - .downcast_mut::() - .unwrap(); - test_window - }) - } - - pub fn set_condition_duration(&mut self, duration: Option) { - self.condition_duration = duration; - } - - pub fn condition_duration(&self) -> Duration { - self.condition_duration.unwrap_or_else(|| { - if std::env::var("CI").is_ok() { - Duration::from_secs(2) - } else { - Duration::from_millis(500) - } - }) - } - - pub fn assert_clipboard_content(&mut self, expected_content: Option<&str>) { - self.update(|cx| { - let actual_content = cx.read_from_clipboard().map(|item| item.text().to_owned()); - let expected_content = expected_content.map(|content| content.to_owned()); - assert_eq!(actual_content, expected_content); - }) - } -} - impl AsyncAppContext { pub fn spawn(&self, f: F) -> Task where @@ -879,60 +553,6 @@ impl ReadViewWith for AsyncAppContext { } } -#[cfg(any(test, feature = "test-support"))] -impl UpdateModel for TestAppContext { - fn update_model( - &mut self, - handle: &ModelHandle, - update: &mut dyn FnMut(&mut T, &mut ModelContext) -> O, - ) -> O { - self.cx.borrow_mut().update_model(handle, update) - } -} - -#[cfg(any(test, feature = "test-support"))] -impl ReadModelWith for TestAppContext { - fn read_model_with( - &self, - handle: &ModelHandle, - read: &mut dyn FnMut(&E, &AppContext) -> T, - ) -> T { - let cx = self.cx.borrow(); - let cx = cx.as_ref(); - read(handle.read(cx), cx) - } -} - -#[cfg(any(test, feature = "test-support"))] -impl UpdateView for TestAppContext { - fn update_view( - &mut self, - handle: &ViewHandle, - update: &mut dyn FnMut(&mut T, &mut ViewContext) -> S, - ) -> S - where - T: View, - { - self.cx.borrow_mut().update_view(handle, update) - } -} - -#[cfg(any(test, feature = "test-support"))] -impl ReadViewWith for TestAppContext { - fn read_view_with( - &self, - handle: &ViewHandle, - read: &mut dyn FnMut(&V, &AppContext) -> T, - ) -> T - where - V: View, - { - let cx = self.cx.borrow(); - let cx = cx.as_ref(); - read(handle.read(cx), cx) - } -} - type ActionCallback = dyn FnMut(&mut dyn AnyView, &dyn Action, &mut MutableAppContext, usize, usize); type GlobalActionCallback = dyn FnMut(&dyn Action, &mut MutableAppContext); @@ -4412,117 +4032,6 @@ impl ModelHandle { update(model, cx) }) } - - #[cfg(any(test, feature = "test-support"))] - pub fn next_notification(&self, cx: &TestAppContext) -> impl Future { - let (tx, mut rx) = futures::channel::mpsc::unbounded(); - let mut cx = cx.cx.borrow_mut(); - let subscription = cx.observe(self, move |_, _| { - tx.unbounded_send(()).ok(); - }); - - let duration = if std::env::var("CI").is_ok() { - Duration::from_secs(5) - } else { - Duration::from_secs(1) - }; - - async move { - let notification = crate::util::timeout(duration, rx.next()) - .await - .expect("next notification timed out"); - drop(subscription); - notification.expect("model dropped while test was waiting for its next notification") - } - } - - #[cfg(any(test, feature = "test-support"))] - pub fn next_event(&self, cx: &TestAppContext) -> impl Future - where - T::Event: Clone, - { - let (tx, mut rx) = futures::channel::mpsc::unbounded(); - let mut cx = cx.cx.borrow_mut(); - let subscription = cx.subscribe(self, move |_, event, _| { - tx.unbounded_send(event.clone()).ok(); - }); - - let duration = if std::env::var("CI").is_ok() { - Duration::from_secs(5) - } else { - Duration::from_secs(1) - }; - - cx.foreground.start_waiting(); - async move { - let event = crate::util::timeout(duration, rx.next()) - .await - .expect("next event timed out"); - drop(subscription); - event.expect("model dropped while test was waiting for its next event") - } - } - - #[cfg(any(test, feature = "test-support"))] - pub fn condition( - &self, - cx: &TestAppContext, - mut predicate: impl FnMut(&T, &AppContext) -> bool, - ) -> impl Future { - let (tx, mut rx) = futures::channel::mpsc::unbounded(); - - let mut cx = cx.cx.borrow_mut(); - let subscriptions = ( - cx.observe(self, { - let tx = tx.clone(); - move |_, _| { - tx.unbounded_send(()).ok(); - } - }), - cx.subscribe(self, { - move |_, _, _| { - tx.unbounded_send(()).ok(); - } - }), - ); - - let cx = cx.weak_self.as_ref().unwrap().upgrade().unwrap(); - let handle = self.downgrade(); - let duration = if std::env::var("CI").is_ok() { - Duration::from_secs(5) - } else { - Duration::from_secs(1) - }; - - async move { - crate::util::timeout(duration, async move { - loop { - { - let cx = cx.borrow(); - let cx = cx.as_ref(); - if predicate( - handle - .upgrade(cx) - .expect("model dropped with pending condition") - .read(cx), - cx, - ) { - break; - } - } - - cx.borrow().foreground().start_waiting(); - rx.next() - .await - .expect("model dropped with pending condition"); - cx.borrow().foreground().finish_waiting(); - } - }) - .await - .expect("condition timed out"); - drop(subscriptions); - } - } } impl Clone for ModelHandle { @@ -4749,93 +4258,6 @@ impl ViewHandle { cx.focused_view_id(self.window_id) .map_or(false, |focused_id| focused_id == self.view_id) } - - #[cfg(any(test, feature = "test-support"))] - pub fn next_notification(&self, cx: &TestAppContext) -> impl Future { - use postage::prelude::{Sink as _, Stream as _}; - - let (mut tx, mut rx) = postage::mpsc::channel(1); - let mut cx = cx.cx.borrow_mut(); - let subscription = cx.observe(self, move |_, _| { - tx.try_send(()).ok(); - }); - - let duration = if std::env::var("CI").is_ok() { - Duration::from_secs(5) - } else { - Duration::from_secs(1) - }; - - async move { - let notification = crate::util::timeout(duration, rx.recv()) - .await - .expect("next notification timed out"); - drop(subscription); - notification.expect("model dropped while test was waiting for its next notification") - } - } - - #[cfg(any(test, feature = "test-support"))] - pub fn condition( - &self, - cx: &TestAppContext, - mut predicate: impl FnMut(&T, &AppContext) -> bool, - ) -> impl Future { - use postage::prelude::{Sink as _, Stream as _}; - - let (tx, mut rx) = postage::mpsc::channel(1024); - let timeout_duration = cx.condition_duration(); - - let mut cx = cx.cx.borrow_mut(); - let subscriptions = self.update(&mut *cx, |_, cx| { - ( - cx.observe(self, { - let mut tx = tx.clone(); - move |_, _, _| { - tx.blocking_send(()).ok(); - } - }), - cx.subscribe(self, { - let mut tx = tx.clone(); - move |_, _, _, _| { - tx.blocking_send(()).ok(); - } - }), - ) - }); - - let cx = cx.weak_self.as_ref().unwrap().upgrade().unwrap(); - let handle = self.downgrade(); - - async move { - crate::util::timeout(timeout_duration, async move { - loop { - { - let cx = cx.borrow(); - let cx = cx.as_ref(); - if predicate( - handle - .upgrade(cx) - .expect("view dropped with pending condition") - .read(cx), - cx, - ) { - break; - } - } - - cx.borrow().foreground().start_waiting(); - rx.recv() - .await - .expect("view dropped with pending condition"); - cx.borrow().foreground().finish_waiting(); - } - }) - .await - .expect("condition timed out"); - drop(subscriptions); - } - } } impl Clone for ViewHandle { diff --git a/crates/gpui/src/app/test_app_context.rs b/crates/gpui/src/app/test_app_context.rs new file mode 100644 index 0000000000..477c316f71 --- /dev/null +++ b/crates/gpui/src/app/test_app_context.rs @@ -0,0 +1,655 @@ +use std::{ + cell::RefCell, + marker::PhantomData, + mem, + path::PathBuf, + rc::Rc, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }, + time::Duration, +}; + +use futures::Future; +use itertools::Itertools; +use parking_lot::{Mutex, RwLock}; +use smol::stream::StreamExt; + +use crate::{ + executor, keymap::Keystroke, platform, Action, AnyViewHandle, AppContext, Appearance, Entity, + Event, FontCache, InputHandler, KeyDownEvent, LeakDetector, ModelContext, ModelHandle, + MutableAppContext, Platform, ReadModelWith, ReadViewWith, RenderContext, Task, UpdateModel, + UpdateView, View, ViewContext, ViewHandle, WeakHandle, WindowInputHandler, +}; +use collections::BTreeMap; + +use super::{AsyncAppContext, RefCounts}; + +pub struct TestAppContext { + cx: Rc>, + foreground_platform: Rc, + condition_duration: Option, + pub function_name: String, + assertion_context: AssertionContextManager, +} + +impl TestAppContext { + pub fn new( + foreground_platform: Rc, + platform: Arc, + foreground: Rc, + background: Arc, + font_cache: Arc, + leak_detector: Arc>, + first_entity_id: usize, + function_name: String, + ) -> Self { + let mut cx = MutableAppContext::new( + foreground, + background, + platform, + foreground_platform.clone(), + font_cache, + RefCounts { + #[cfg(any(test, feature = "test-support"))] + leak_detector, + ..Default::default() + }, + (), + ); + cx.next_entity_id = first_entity_id; + let cx = TestAppContext { + cx: Rc::new(RefCell::new(cx)), + foreground_platform, + condition_duration: None, + function_name, + assertion_context: AssertionContextManager::new(), + }; + cx.cx.borrow_mut().weak_self = Some(Rc::downgrade(&cx.cx)); + cx + } + + pub fn dispatch_action(&self, window_id: usize, action: A) { + let mut cx = self.cx.borrow_mut(); + if let Some(view_id) = cx.focused_view_id(window_id) { + cx.handle_dispatch_action_from_effect(window_id, Some(view_id), &action); + } + } + + pub fn dispatch_global_action(&self, action: A) { + self.cx.borrow_mut().dispatch_global_action(action); + } + + pub fn dispatch_keystroke(&mut self, window_id: usize, keystroke: Keystroke, is_held: bool) { + let handled = self.cx.borrow_mut().update(|cx| { + let presenter = cx + .presenters_and_platform_windows + .get(&window_id) + .unwrap() + .0 + .clone(); + + if cx.dispatch_keystroke(window_id, &keystroke) { + return true; + } + + if presenter.borrow_mut().dispatch_event( + Event::KeyDown(KeyDownEvent { + keystroke: keystroke.clone(), + is_held, + }), + false, + cx, + ) { + return true; + } + + false + }); + + if !handled && !keystroke.cmd && !keystroke.ctrl { + WindowInputHandler { + app: self.cx.clone(), + window_id, + } + .replace_text_in_range(None, &keystroke.key) + } + } + + pub fn add_model(&mut self, build_model: F) -> ModelHandle + where + T: Entity, + F: FnOnce(&mut ModelContext) -> T, + { + self.cx.borrow_mut().add_model(build_model) + } + + pub fn add_window(&mut self, build_root_view: F) -> (usize, ViewHandle) + where + T: View, + F: FnOnce(&mut ViewContext) -> T, + { + let (window_id, view) = self + .cx + .borrow_mut() + .add_window(Default::default(), build_root_view); + self.simulate_window_activation(Some(window_id)); + (window_id, view) + } + + pub fn add_view( + &mut self, + parent_handle: impl Into, + build_view: F, + ) -> ViewHandle + where + T: View, + F: FnOnce(&mut ViewContext) -> T, + { + self.cx.borrow_mut().add_view(parent_handle, build_view) + } + + pub fn window_ids(&self) -> Vec { + self.cx.borrow().window_ids().collect() + } + + pub fn root_view(&self, window_id: usize) -> Option> { + self.cx.borrow().root_view(window_id) + } + + pub fn read T>(&self, callback: F) -> T { + callback(self.cx.borrow().as_ref()) + } + + pub fn update T>(&mut self, callback: F) -> T { + let mut state = self.cx.borrow_mut(); + // Don't increment pending flushes in order for effects to be flushed before the callback + // completes, which is helpful in tests. + let result = callback(&mut *state); + // Flush effects after the callback just in case there are any. This can happen in edge + // cases such as the closure dropping handles. + state.flush_effects(); + result + } + + pub fn render(&mut self, handle: &ViewHandle, f: F) -> T + where + F: FnOnce(&mut V, &mut RenderContext) -> T, + V: View, + { + handle.update(&mut *self.cx.borrow_mut(), |view, cx| { + let mut render_cx = RenderContext { + app: cx, + window_id: handle.window_id(), + view_id: handle.id(), + view_type: PhantomData, + titlebar_height: 0., + hovered_region_ids: Default::default(), + clicked_region_ids: None, + refreshing: false, + appearance: Appearance::Light, + }; + f(view, &mut render_cx) + }) + } + + pub fn to_async(&self) -> AsyncAppContext { + AsyncAppContext(self.cx.clone()) + } + + pub fn font_cache(&self) -> Arc { + self.cx.borrow().cx.font_cache.clone() + } + + pub fn foreground_platform(&self) -> Rc { + self.foreground_platform.clone() + } + + pub fn platform(&self) -> Arc { + self.cx.borrow().cx.platform.clone() + } + + pub fn foreground(&self) -> Rc { + self.cx.borrow().foreground().clone() + } + + pub fn background(&self) -> Arc { + self.cx.borrow().background().clone() + } + + pub fn spawn(&self, f: F) -> Task + where + F: FnOnce(AsyncAppContext) -> Fut, + Fut: 'static + Future, + T: 'static, + { + let foreground = self.foreground(); + let future = f(self.to_async()); + let cx = self.to_async(); + foreground.spawn(async move { + let result = future.await; + cx.0.borrow_mut().flush_effects(); + result + }) + } + + pub fn simulate_new_path_selection(&self, result: impl FnOnce(PathBuf) -> Option) { + self.foreground_platform.simulate_new_path_selection(result); + } + + pub fn did_prompt_for_new_path(&self) -> bool { + self.foreground_platform.as_ref().did_prompt_for_new_path() + } + + pub fn simulate_prompt_answer(&self, window_id: usize, answer: usize) { + use postage::prelude::Sink as _; + + let mut done_tx = self + .window_mut(window_id) + .pending_prompts + .borrow_mut() + .pop_front() + .expect("prompt was not called"); + let _ = done_tx.try_send(answer); + } + + pub fn has_pending_prompt(&self, window_id: usize) -> bool { + let window = self.window_mut(window_id); + let prompts = window.pending_prompts.borrow_mut(); + !prompts.is_empty() + } + + pub fn current_window_title(&self, window_id: usize) -> Option { + self.window_mut(window_id).title.clone() + } + + pub fn simulate_window_close(&self, window_id: usize) -> bool { + let handler = self.window_mut(window_id).should_close_handler.take(); + if let Some(mut handler) = handler { + let should_close = handler(); + self.window_mut(window_id).should_close_handler = Some(handler); + should_close + } else { + false + } + } + + pub fn simulate_window_activation(&self, to_activate: Option) { + let mut handlers = BTreeMap::new(); + { + let mut cx = self.cx.borrow_mut(); + for (window_id, (_, window)) in &mut cx.presenters_and_platform_windows { + let window = window + .as_any_mut() + .downcast_mut::() + .unwrap(); + handlers.insert( + *window_id, + mem::take(&mut window.active_status_change_handlers), + ); + } + }; + let mut handlers = handlers.into_iter().collect::>(); + handlers.sort_unstable_by_key(|(window_id, _)| Some(*window_id) == to_activate); + + for (window_id, mut window_handlers) in handlers { + for window_handler in &mut window_handlers { + window_handler(Some(window_id) == to_activate); + } + + self.window_mut(window_id) + .active_status_change_handlers + .extend(window_handlers); + } + } + + pub fn is_window_edited(&self, window_id: usize) -> bool { + self.window_mut(window_id).edited + } + + pub fn leak_detector(&self) -> Arc> { + self.cx.borrow().leak_detector() + } + + pub fn assert_dropped(&self, handle: impl WeakHandle) { + self.cx + .borrow() + .leak_detector() + .lock() + .assert_dropped(handle.id()) + } + + fn window_mut(&self, window_id: usize) -> std::cell::RefMut { + std::cell::RefMut::map(self.cx.borrow_mut(), |state| { + let (_, window) = state + .presenters_and_platform_windows + .get_mut(&window_id) + .unwrap(); + let test_window = window + .as_any_mut() + .downcast_mut::() + .unwrap(); + test_window + }) + } + + pub fn set_condition_duration(&mut self, duration: Option) { + self.condition_duration = duration; + } + + pub fn condition_duration(&self) -> Duration { + self.condition_duration.unwrap_or_else(|| { + if std::env::var("CI").is_ok() { + Duration::from_secs(2) + } else { + Duration::from_millis(500) + } + }) + } + + pub fn assert_clipboard_content(&mut self, expected_content: Option<&str>) { + self.update(|cx| { + let actual_content = cx.read_from_clipboard().map(|item| item.text().to_owned()); + let expected_content = expected_content.map(|content| content.to_owned()); + assert_eq!(actual_content, expected_content); + }) + } + + pub fn add_assertion_context(&self, context: String) -> ContextHandle { + self.assertion_context.add_context(context) + } + + pub fn assertion_context(&self) -> String { + self.assertion_context.context() + } +} + +impl UpdateModel for TestAppContext { + fn update_model( + &mut self, + handle: &ModelHandle, + update: &mut dyn FnMut(&mut T, &mut ModelContext) -> O, + ) -> O { + self.cx.borrow_mut().update_model(handle, update) + } +} + +impl ReadModelWith for TestAppContext { + fn read_model_with( + &self, + handle: &ModelHandle, + read: &mut dyn FnMut(&E, &AppContext) -> T, + ) -> T { + let cx = self.cx.borrow(); + let cx = cx.as_ref(); + read(handle.read(cx), cx) + } +} + +impl UpdateView for TestAppContext { + fn update_view( + &mut self, + handle: &ViewHandle, + update: &mut dyn FnMut(&mut T, &mut ViewContext) -> S, + ) -> S + where + T: View, + { + self.cx.borrow_mut().update_view(handle, update) + } +} + +impl ReadViewWith for TestAppContext { + fn read_view_with( + &self, + handle: &ViewHandle, + read: &mut dyn FnMut(&V, &AppContext) -> T, + ) -> T + where + V: View, + { + let cx = self.cx.borrow(); + let cx = cx.as_ref(); + read(handle.read(cx), cx) + } +} + +impl ModelHandle { + pub fn next_notification(&self, cx: &TestAppContext) -> impl Future { + let (tx, mut rx) = futures::channel::mpsc::unbounded(); + let mut cx = cx.cx.borrow_mut(); + let subscription = cx.observe(self, move |_, _| { + tx.unbounded_send(()).ok(); + }); + + let duration = if std::env::var("CI").is_ok() { + Duration::from_secs(5) + } else { + Duration::from_secs(1) + }; + + async move { + let notification = crate::util::timeout(duration, rx.next()) + .await + .expect("next notification timed out"); + drop(subscription); + notification.expect("model dropped while test was waiting for its next notification") + } + } + + pub fn next_event(&self, cx: &TestAppContext) -> impl Future + where + T::Event: Clone, + { + let (tx, mut rx) = futures::channel::mpsc::unbounded(); + let mut cx = cx.cx.borrow_mut(); + let subscription = cx.subscribe(self, move |_, event, _| { + tx.unbounded_send(event.clone()).ok(); + }); + + let duration = if std::env::var("CI").is_ok() { + Duration::from_secs(5) + } else { + Duration::from_secs(1) + }; + + cx.foreground.start_waiting(); + async move { + let event = crate::util::timeout(duration, rx.next()) + .await + .expect("next event timed out"); + drop(subscription); + event.expect("model dropped while test was waiting for its next event") + } + } + + pub fn condition( + &self, + cx: &TestAppContext, + mut predicate: impl FnMut(&T, &AppContext) -> bool, + ) -> impl Future { + let (tx, mut rx) = futures::channel::mpsc::unbounded(); + + let mut cx = cx.cx.borrow_mut(); + let subscriptions = ( + cx.observe(self, { + let tx = tx.clone(); + move |_, _| { + tx.unbounded_send(()).ok(); + } + }), + cx.subscribe(self, { + move |_, _, _| { + tx.unbounded_send(()).ok(); + } + }), + ); + + let cx = cx.weak_self.as_ref().unwrap().upgrade().unwrap(); + let handle = self.downgrade(); + let duration = if std::env::var("CI").is_ok() { + Duration::from_secs(5) + } else { + Duration::from_secs(1) + }; + + async move { + crate::util::timeout(duration, async move { + loop { + { + let cx = cx.borrow(); + let cx = cx.as_ref(); + if predicate( + handle + .upgrade(cx) + .expect("model dropped with pending condition") + .read(cx), + cx, + ) { + break; + } + } + + cx.borrow().foreground().start_waiting(); + rx.next() + .await + .expect("model dropped with pending condition"); + cx.borrow().foreground().finish_waiting(); + } + }) + .await + .expect("condition timed out"); + drop(subscriptions); + } + } +} + +impl ViewHandle { + pub fn next_notification(&self, cx: &TestAppContext) -> impl Future { + use postage::prelude::{Sink as _, Stream as _}; + + let (mut tx, mut rx) = postage::mpsc::channel(1); + let mut cx = cx.cx.borrow_mut(); + let subscription = cx.observe(self, move |_, _| { + tx.try_send(()).ok(); + }); + + let duration = if std::env::var("CI").is_ok() { + Duration::from_secs(5) + } else { + Duration::from_secs(1) + }; + + async move { + let notification = crate::util::timeout(duration, rx.recv()) + .await + .expect("next notification timed out"); + drop(subscription); + notification.expect("model dropped while test was waiting for its next notification") + } + } + + pub fn condition( + &self, + cx: &TestAppContext, + mut predicate: impl FnMut(&T, &AppContext) -> bool, + ) -> impl Future { + use postage::prelude::{Sink as _, Stream as _}; + + let (tx, mut rx) = postage::mpsc::channel(1024); + let timeout_duration = cx.condition_duration(); + + let mut cx = cx.cx.borrow_mut(); + let subscriptions = self.update(&mut *cx, |_, cx| { + ( + cx.observe(self, { + let mut tx = tx.clone(); + move |_, _, _| { + tx.blocking_send(()).ok(); + } + }), + cx.subscribe(self, { + let mut tx = tx.clone(); + move |_, _, _, _| { + tx.blocking_send(()).ok(); + } + }), + ) + }); + + let cx = cx.weak_self.as_ref().unwrap().upgrade().unwrap(); + let handle = self.downgrade(); + + async move { + crate::util::timeout(timeout_duration, async move { + loop { + { + let cx = cx.borrow(); + let cx = cx.as_ref(); + if predicate( + handle + .upgrade(cx) + .expect("view dropped with pending condition") + .read(cx), + cx, + ) { + break; + } + } + + cx.borrow().foreground().start_waiting(); + rx.recv() + .await + .expect("view dropped with pending condition"); + cx.borrow().foreground().finish_waiting(); + } + }) + .await + .expect("condition timed out"); + drop(subscriptions); + } + } +} + +#[derive(Clone)] +pub struct AssertionContextManager { + id: Arc, + contexts: Arc>>, +} + +impl AssertionContextManager { + pub fn new() -> Self { + Self { + id: Arc::new(AtomicUsize::new(0)), + contexts: Arc::new(RwLock::new(BTreeMap::new())), + } + } + + pub fn add_context(&self, context: String) -> ContextHandle { + let id = self.id.fetch_add(1, Ordering::Relaxed); + let mut contexts = self.contexts.write(); + contexts.insert(id, context); + ContextHandle { + id, + manager: self.clone(), + } + } + + pub fn context(&self) -> String { + let contexts = self.contexts.read(); + format!("\n{}\n", contexts.values().join("\n")) + } +} + +pub struct ContextHandle { + id: usize, + manager: AssertionContextManager, +} + +impl Drop for ContextHandle { + fn drop(&mut self) { + let mut contexts = self.manager.contexts.write(); + contexts.remove(&self.id); + } +} diff --git a/crates/vim/Cargo.toml b/crates/vim/Cargo.toml index 3acd3f3a90..44f2a8cb16 100644 --- a/crates/vim/Cargo.toml +++ b/crates/vim/Cargo.toml @@ -34,6 +34,7 @@ workspace = { path = "../workspace" } [dev-dependencies] indoc = "1.0.4" parking_lot = "0.11.1" +lazy_static = "1.4" editor = { path = "../editor", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } diff --git a/crates/vim/src/insert.rs b/crates/vim/src/insert.rs index 1b9b299bf3..05cd2af1d9 100644 --- a/crates/vim/src/insert.rs +++ b/crates/vim/src/insert.rs @@ -26,7 +26,7 @@ fn normal_before(_: &mut Workspace, _: &NormalBefore, cx: &mut ViewContext) { clipboard_text = Cow::Owned(newline_separated_text); } - let mut new_selections = Vec::new(); + // If the pasted text is a single line, the cursor should be placed after + // the newly pasted text. This is easiest done with an anchor after the + // insertion, and then with a fixup to move the selection back one position. + // However if the pasted text is linewise, the cursor should be placed at the start + // of the new text on the following line. This is easiest done with a manually adjusted + // point. + // This enum lets us represent both cases + enum NewPosition { + Inside(Point), + After(Anchor), + } + let mut new_selections: HashMap = Default::default(); editor.buffer().update(cx, |buffer, cx| { let snapshot = buffer.snapshot(cx); let mut start_offset = 0; @@ -288,8 +301,10 @@ fn paste(_: &mut Workspace, _: &Paste, cx: &mut ViewContext) { edits.push((point..point, "\n")); } // Drop selection at the start of the next line - let selection_point = Point::new(point.row + 1, 0); - new_selections.push(selection.map(|_| selection_point)); + new_selections.insert( + selection.id, + NewPosition::Inside(Point::new(point.row + 1, 0)), + ); point } else { let mut point = selection.end; @@ -299,7 +314,14 @@ fn paste(_: &mut Workspace, _: &Paste, cx: &mut ViewContext) { .clip_point(point, Bias::Right) .to_point(&display_map); - new_selections.push(selection.map(|_| point)); + new_selections.insert( + selection.id, + if to_insert.contains('\n') { + NewPosition::Inside(point) + } else { + NewPosition::After(snapshot.anchor_after(point)) + }, + ); point }; @@ -317,7 +339,25 @@ fn paste(_: &mut Workspace, _: &Paste, cx: &mut ViewContext) { }); editor.change_selections(Some(Autoscroll::Fit), cx, |s| { - s.select(new_selections) + s.move_with(|map, selection| { + if let Some(new_position) = new_selections.get(&selection.id) { + match new_position { + NewPosition::Inside(new_point) => { + selection.collapse_to( + new_point.to_display_point(map), + SelectionGoal::None, + ); + } + NewPosition::After(after_point) => { + let mut new_point = after_point.to_display_point(map); + *new_point.column_mut() = + new_point.column().saturating_sub(1); + new_point = map.clip_point(new_point, Bias::Left); + selection.collapse_to(new_point, SelectionGoal::None); + } + } + } + }); }); } else { editor.insert(&clipboard_text, cx); @@ -332,14 +372,13 @@ fn paste(_: &mut Workspace, _: &Paste, cx: &mut ViewContext) { #[cfg(test)] mod test { use indoc::indoc; - use util::test::marked_text_offsets; use crate::{ state::{ Mode::{self, *}, Namespace, Operator, }, - test_contexts::{NeovimBackedTestContext, VimTestContext}, + test::{NeovimBackedTestContext, VimTestContext}, }; #[gpui::test] @@ -476,48 +515,22 @@ mod test { #[gpui::test] async fn test_b(cx: &mut gpui::TestAppContext) { - let mut cx = VimTestContext::new(cx, true).await; - let (_, cursor_offsets) = marked_text_offsets(indoc! {" - ˇˇThe ˇquickˇ-ˇbrown + let mut cx = NeovimBackedTestContext::new(cx).await.binding(["b"]); + cx.assert_all(indoc! {" + ˇThe ˇquickˇ-ˇbrown ˇ ˇ ˇfox_jumps ˇover - ˇthe"}); - cx.set_state( - indoc! {" - The quick-brown - - - fox_jumps over - thˇe"}, - Mode::Normal, - ); - - for cursor_offset in cursor_offsets.into_iter().rev() { - cx.simulate_keystroke("b"); - cx.assert_editor_selections(vec![cursor_offset..cursor_offset]); - } - - // Reset and test ignoring punctuation - let (_, cursor_offsets) = marked_text_offsets(indoc! {" - ˇˇThe ˇquick-brown + ˇthe"}) + .await; + let mut cx = cx.binding(["shift-b"]); + cx.assert_all(indoc! {" + ˇThe ˇquickˇ-ˇbrown ˇ ˇ ˇfox_jumps ˇover - ˇthe"}); - cx.set_state( - indoc! {" - The quick-brown - - - fox_jumps over - thˇe"}, - Mode::Normal, - ); - for cursor_offset in cursor_offsets.into_iter().rev() { - cx.simulate_keystroke("shift-b"); - cx.assert_editor_selections(vec![cursor_offset..cursor_offset]); - } + ˇthe"}) + .await; } #[gpui::test] @@ -571,199 +584,98 @@ mod test { #[gpui::test] async fn test_jump_to_first_non_whitespace(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["^"]); - cx.assert("The qˇuick", "ˇThe quick"); - cx.assert(" The qˇuick", " ˇThe quick"); - cx.assert("ˇ", "ˇ"); - cx.assert( - indoc! {" + let mut cx = NeovimBackedTestContext::new(cx).await.binding(["^"]); + cx.assert("The qˇuick").await; + cx.assert(" The qˇuick").await; + cx.assert("ˇ").await; + cx.assert(indoc! {" The qˇuick - brown fox"}, - indoc! {" - ˇThe quick - brown fox"}, - ); - cx.assert( - indoc! {" + brown fox"}) + .await; + cx.assert(indoc! {" ˇ - The quick"}, - indoc! {" - ˇ - The quick"}, - ); + The quick"}) + .await; // Indoc disallows trailing whitspace. - cx.assert(" ˇ \nThe quick", " ˇ \nThe quick"); + cx.assert(" ˇ \nThe quick").await; } #[gpui::test] async fn test_insert_first_non_whitespace(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["shift-i"]).mode_after(Mode::Insert); - cx.assert("The qˇuick", "ˇThe quick"); - cx.assert(" The qˇuick", " ˇThe quick"); - cx.assert("ˇ", "ˇ"); - cx.assert( - indoc! {" + let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-i"]); + cx.assert("The qˇuick").await; + cx.assert(" The qˇuick").await; + cx.assert("ˇ").await; + cx.assert(indoc! {" The qˇuick - brown fox"}, - indoc! {" - ˇThe quick - brown fox"}, - ); - cx.assert( - indoc! {" + brown fox"}) + .await; + cx.assert(indoc! {" ˇ - The quick"}, - indoc! {" - ˇ - The quick"}, - ); + The quick"}) + .await; } #[gpui::test] async fn test_delete_to_end_of_line(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["shift-d"]); - cx.assert( - indoc! {" + let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-d"]); + cx.assert(indoc! {" The qˇuick - brown fox"}, - indoc! {" - The ˇq - brown fox"}, - ); - cx.assert( - indoc! {" + brown fox"}) + .await; + cx.assert(indoc! {" The quick ˇ - brown fox"}, - indoc! {" - The quick - ˇ - brown fox"}, - ); + brown fox"}) + .await; } #[gpui::test] async fn test_x(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["x"]); - cx.assert("ˇTest", "ˇest"); - cx.assert("Teˇst", "Teˇt"); - cx.assert("Tesˇt", "Teˇs"); - cx.assert( - indoc! {" + let mut cx = NeovimBackedTestContext::new(cx).await.binding(["x"]); + cx.assert_all("ˇTeˇsˇt").await; + cx.assert(indoc! {" Tesˇt - test"}, - indoc! {" - Teˇs - test"}, - ); + test"}) + .await; } #[gpui::test] async fn test_delete_left(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["shift-x"]); - cx.assert("Teˇst", "Tˇst"); - cx.assert("Tˇest", "ˇest"); - cx.assert("ˇTest", "ˇTest"); - cx.assert( - indoc! {" + let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-x"]); + cx.assert_all("ˇTˇeˇsˇt").await; + cx.assert(indoc! {" Test - ˇtest"}, - indoc! {" - Test - ˇtest"}, - ); + ˇtest"}) + .await; } #[gpui::test] async fn test_o(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["o"]).mode_after(Mode::Insert); - - cx.assert( - "ˇ", - indoc! {" - - ˇ"}, - ); - cx.assert( - "The ˇquick", - indoc! {" - The quick - ˇ"}, - ); - cx.assert( - indoc! {" - The quick - brown ˇfox - jumps over"}, - indoc! {" - The quick - brown fox - ˇ - jumps over"}, - ); - cx.assert( - indoc! {" - The quick - brown fox - jumps ˇover"}, - indoc! {" - The quick - brown fox - jumps over - ˇ"}, - ); - cx.assert( - indoc! {" + let mut cx = NeovimBackedTestContext::new(cx).await.binding(["o"]); + cx.assert("ˇ").await; + cx.assert("The ˇquick").await; + cx.assert_all(indoc! {" The qˇuick - brown fox - jumps over"}, - indoc! {" + brown ˇfox + jumps ˇover"}) + .await; + cx.assert(indoc! {" The quick ˇ - brown fox - jumps over"}, - ); - cx.assert( - indoc! {" - The quick - ˇ - brown fox"}, - indoc! {" - The quick - - ˇ - brown fox"}, - ); - cx.assert( - indoc! {" + brown fox"}) + .await; + cx.assert(indoc! {" fn test() { println!(ˇ); } - "}, - indoc! {" - fn test() { - println!(); - ˇ - } - "}, - ); - cx.assert( - indoc! {" + "}) + .await; + cx.assert(indoc! {" fn test(ˇ) { println!(); - }"}, - indoc! {" - fn test() { - ˇ - println!(); - }"}, - ); + }"}) + .await; } #[gpui::test] @@ -812,146 +724,66 @@ mod test { #[gpui::test] async fn test_dd(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["d", "d"]); - - cx.assert("ˇ", "ˇ"); - cx.assert("The ˇquick", "ˇ"); - cx.assert( - indoc! {" - The quick - brown ˇfox - jumps over"}, - indoc! {" - The quick - jumps ˇover"}, - ); - cx.assert( - indoc! {" - The quick - brown fox - jumps ˇover"}, - indoc! {" - The quick - brown ˇfox"}, - ); - cx.assert( - indoc! {" + let mut cx = NeovimBackedTestContext::new(cx).await.binding(["d", "d"]); + cx.assert("ˇ").await; + cx.assert("The ˇquick").await; + cx.assert_all(indoc! {" The qˇuick - brown fox - jumps over"}, - indoc! {" - brownˇ fox - jumps over"}, - ); - cx.assert( - indoc! {" + brown ˇfox + jumps ˇover"}) + .await; + cx.assert(indoc! {" The quick ˇ - brown fox"}, - indoc! {" - The quick - ˇbrown fox"}, - ); + brown fox"}) + .await; } #[gpui::test] async fn test_cc(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["c", "c"]).mode_after(Mode::Insert); - - cx.assert("ˇ", "ˇ"); - cx.assert("The ˇquick", "ˇ"); - cx.assert( - indoc! {" - The quick + let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "c"]); + cx.assert("ˇ").await; + cx.assert("The ˇquick").await; + cx.assert_all(indoc! {" + The quˇick brown ˇfox - jumps over"}, - indoc! {" + jumps ˇover"}) + .await; + cx.assert(indoc! {" The quick ˇ - jumps over"}, - ); - cx.assert( - indoc! {" - The quick - brown fox - jumps ˇover"}, - indoc! {" - The quick - brown fox - ˇ"}, - ); - cx.assert( - indoc! {" - The qˇuick - brown fox - jumps over"}, - indoc! {" - ˇ - brown fox - jumps over"}, - ); - cx.assert( - indoc! {" - The quick - ˇ - brown fox"}, - indoc! {" - The quick - ˇ - brown fox"}, - ); + brown fox"}) + .await; } #[gpui::test] async fn test_p(cx: &mut gpui::TestAppContext) { - let mut cx = VimTestContext::new(cx, true).await; - cx.set_state( - indoc! {" + let mut cx = NeovimBackedTestContext::new(cx).await; + cx.set_shared_state(indoc! {" The quick brown fox juˇmps over - the lazy dog"}, - Mode::Normal, - ); + the lazy dog"}) + .await; - cx.simulate_keystrokes(["d", "d"]); - cx.assert_editor_state(indoc! {" - The quick brown - the laˇzy dog"}); + cx.simulate_shared_keystrokes(["d", "d"]).await; + cx.assert_state_matches().await; - cx.simulate_keystroke("p"); - cx.assert_state( - indoc! {" + cx.simulate_shared_keystroke("p").await; + cx.assert_state_matches().await; + + cx.set_shared_state(indoc! {" The quick brown - the lazy dog - ˇfox jumps over"}, - Mode::Normal, - ); - - cx.set_state( - indoc! {" - The quick brown - fox «jumpˇ»s over - the lazy dog"}, - Mode::Visual { line: false }, - ); - cx.simulate_keystroke("y"); - cx.set_state( - indoc! {" + fox ˇjumps over + the lazy dog"}) + .await; + cx.simulate_shared_keystrokes(["v", "w", "y"]).await; + cx.set_shared_state(indoc! {" The quick brown fox jumps oveˇr - the lazy dog"}, - Mode::Normal, - ); - cx.simulate_keystroke("p"); - cx.assert_state( - indoc! {" - The quick brown - fox jumps overˇjumps - the lazy dog"}, - Mode::Normal, - ); + the lazy dog"}) + .await; + cx.simulate_shared_keystroke("p").await; + cx.assert_state_matches().await; } #[gpui::test] diff --git a/crates/vim/src/normal/change.rs b/crates/vim/src/normal/change.rs index 924fc9d708..cc62ce8db0 100644 --- a/crates/vim/src/normal/change.rs +++ b/crates/vim/src/normal/change.rs @@ -79,7 +79,7 @@ mod test { use crate::{ state::Mode, - test_contexts::{NeovimBackedTestContext, VimTestContext}, + test::{NeovimBackedTestContext, VimTestContext}, }; #[gpui::test] diff --git a/crates/vim/src/normal/delete.rs b/crates/vim/src/normal/delete.rs index b0fd3ea5a1..1465e3e377 100644 --- a/crates/vim/src/normal/delete.rs +++ b/crates/vim/src/normal/delete.rs @@ -96,7 +96,7 @@ pub fn delete_object(vim: &mut Vim, object: Object, around: bool, cx: &mut Mutab mod test { use indoc::indoc; - use crate::{state::Mode, test_contexts::VimTestContext}; + use crate::{state::Mode, test::VimTestContext}; #[gpui::test] async fn test_delete_h(cx: &mut gpui::TestAppContext) { diff --git a/crates/vim/src/object.rs b/crates/vim/src/object.rs index b55545682f..a0f9b4a6da 100644 --- a/crates/vim/src/object.rs +++ b/crates/vim/src/object.rs @@ -310,7 +310,7 @@ fn expand_to_include_whitespace( mod test { use indoc::indoc; - use crate::test_contexts::NeovimBackedTestContext; + use crate::test::NeovimBackedTestContext; const WORD_LOCATIONS: &'static str = indoc! {" The quick ˇbrowˇnˇ diff --git a/crates/vim/src/test.rs b/crates/vim/src/test.rs new file mode 100644 index 0000000000..63bb7996bf --- /dev/null +++ b/crates/vim/src/test.rs @@ -0,0 +1,102 @@ +mod neovim_backed_binding_test_context; +mod neovim_backed_test_context; +mod neovim_connection; +mod vim_binding_test_context; +mod vim_test_context; + +pub use neovim_backed_binding_test_context::*; +pub use neovim_backed_test_context::*; +pub use vim_binding_test_context::*; +pub use vim_test_context::*; + +use indoc::indoc; +use search::BufferSearchBar; + +use crate::state::Mode; + +#[gpui::test] +async fn test_initially_disabled(cx: &mut gpui::TestAppContext) { + let mut cx = VimTestContext::new(cx, false).await; + cx.simulate_keystrokes(["h", "j", "k", "l"]); + cx.assert_editor_state("hjklˇ"); +} + +#[gpui::test] +async fn test_neovim(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.simulate_shared_keystroke("i").await; + cx.simulate_shared_keystrokes([ + "shift-T", "e", "s", "t", " ", "t", "e", "s", "t", "escape", "0", "d", "w", + ]) + .await; + cx.assert_state_matches().await; + cx.assert_editor_state("ˇtest"); +} + +#[gpui::test] +async fn test_toggle_through_settings(cx: &mut gpui::TestAppContext) { + let mut cx = VimTestContext::new(cx, true).await; + + cx.simulate_keystroke("i"); + assert_eq!(cx.mode(), Mode::Insert); + + // Editor acts as though vim is disabled + cx.disable_vim(); + cx.simulate_keystrokes(["h", "j", "k", "l"]); + cx.assert_editor_state("hjklˇ"); + + // Selections aren't changed if editor is blurred but vim-mode is still disabled. + cx.set_state("«hjklˇ»", Mode::Normal); + cx.assert_editor_state("«hjklˇ»"); + cx.update_editor(|_, cx| cx.blur()); + cx.assert_editor_state("«hjklˇ»"); + cx.update_editor(|_, cx| cx.focus_self()); + cx.assert_editor_state("«hjklˇ»"); + + // Enabling dynamically sets vim mode again and restores normal mode + cx.enable_vim(); + assert_eq!(cx.mode(), Mode::Normal); + cx.simulate_keystrokes(["h", "h", "h", "l"]); + assert_eq!(cx.buffer_text(), "hjkl".to_owned()); + cx.assert_editor_state("hˇjkl"); + cx.simulate_keystrokes(["i", "T", "e", "s", "t"]); + cx.assert_editor_state("hTestˇjkl"); + + // Disabling and enabling resets to normal mode + assert_eq!(cx.mode(), Mode::Insert); + cx.disable_vim(); + cx.enable_vim(); + assert_eq!(cx.mode(), Mode::Normal); +} + +#[gpui::test] +async fn test_buffer_search(cx: &mut gpui::TestAppContext) { + let mut cx = VimTestContext::new(cx, true).await; + + cx.set_state( + indoc! {" + The quick brown + fox juˇmps over + the lazy dog"}, + Mode::Normal, + ); + cx.simulate_keystroke("/"); + + // We now use a weird insert mode with selection when jumping to a single line editor + assert_eq!(cx.mode(), Mode::Insert); + + let search_bar = cx.workspace(|workspace, cx| { + workspace + .active_pane() + .read(cx) + .toolbar() + .read(cx) + .item_of_type::() + .expect("Buffer search bar should be deployed") + }); + + search_bar.read_with(cx.cx, |bar, cx| { + assert_eq!(bar.query_editor.read(cx).text(cx), "jumps"); + }) +} diff --git a/crates/vim/src/test_contexts/neovim_backed_binding_test_context.rs b/crates/vim/src/test/neovim_backed_binding_test_context.rs similarity index 92% rename from crates/vim/src/test_contexts/neovim_backed_binding_test_context.rs rename to crates/vim/src/test/neovim_backed_binding_test_context.rs index 3f6b8f99f8..a768aff59d 100644 --- a/crates/vim/src/test_contexts/neovim_backed_binding_test_context.rs +++ b/crates/vim/src/test/neovim_backed_binding_test_context.rs @@ -1,5 +1,7 @@ use std::ops::{Deref, DerefMut}; +use gpui::ContextHandle; + use crate::state::Mode; use super::NeovimBackedTestContext; @@ -31,7 +33,10 @@ impl<'a, const COUNT: usize> NeovimBackedBindingTestContext<'a, COUNT> { self.consume().binding(keystrokes) } - pub async fn assert(&mut self, marked_positions: &str) { + pub async fn assert( + &mut self, + marked_positions: &str, + ) -> Option<(ContextHandle, ContextHandle)> { self.cx .assert_binding_matches(self.keystrokes_under_test, marked_positions) .await diff --git a/crates/vim/src/test/neovim_backed_test_context.rs b/crates/vim/src/test/neovim_backed_test_context.rs new file mode 100644 index 0000000000..bb8ba26b74 --- /dev/null +++ b/crates/vim/src/test/neovim_backed_test_context.rs @@ -0,0 +1,158 @@ +use std::ops::{Deref, DerefMut}; + +use collections::{HashMap, HashSet}; +use gpui::ContextHandle; +use language::OffsetRangeExt; +use util::test::marked_text_offsets; + +use super::{neovim_connection::NeovimConnection, NeovimBackedBindingTestContext, VimTestContext}; +use crate::state::Mode; + +pub struct NeovimBackedTestContext<'a> { + cx: VimTestContext<'a>, + // Lookup for exempted assertions. Keyed by the insertion text, and with a value indicating which + // bindings are exempted. If None, all bindings are ignored for that insertion text. + exemptions: HashMap>>, + neovim: NeovimConnection, +} + +impl<'a> NeovimBackedTestContext<'a> { + pub async fn new(cx: &'a mut gpui::TestAppContext) -> NeovimBackedTestContext<'a> { + let function_name = cx.function_name.clone(); + let cx = VimTestContext::new(cx, true).await; + Self { + cx, + exemptions: Default::default(), + neovim: NeovimConnection::new(function_name).await, + } + } + + pub fn add_initial_state_exemption(&mut self, initial_state: &str) { + let initial_state = initial_state.to_string(); + // None represents all keybindings being exempted for that initial state + self.exemptions.insert(initial_state, None); + } + + pub async fn simulate_shared_keystroke(&mut self, keystroke_text: &str) -> ContextHandle { + self.neovim.send_keystroke(keystroke_text).await; + self.simulate_keystroke(keystroke_text) + } + + pub async fn simulate_shared_keystrokes( + &mut self, + keystroke_texts: [&str; COUNT], + ) -> ContextHandle { + for keystroke_text in keystroke_texts.into_iter() { + self.neovim.send_keystroke(keystroke_text).await; + } + self.simulate_keystrokes(keystroke_texts) + } + + pub async fn set_shared_state(&mut self, marked_text: &str) -> ContextHandle { + let context_handle = self.set_state(marked_text, Mode::Normal); + + let selection = self.editor(|editor, cx| editor.selections.newest::(cx)); + let text = self.buffer_text(); + self.neovim.set_state(selection, &text).await; + + context_handle + } + + pub async fn assert_state_matches(&mut self) { + assert_eq!( + self.neovim.text().await, + self.buffer_text(), + "{}", + self.assertion_context() + ); + + let mut neovim_selection = self.neovim.selection().await; + // Zed selections adjust themselves to make the end point visually make sense + if neovim_selection.start > neovim_selection.end { + neovim_selection.start.column += 1; + } + let neovim_selection = neovim_selection.to_offset(&self.buffer_snapshot()); + self.assert_editor_selections(vec![neovim_selection]); + + if let Some(neovim_mode) = self.neovim.mode().await { + assert_eq!(neovim_mode, self.mode(), "{}", self.assertion_context(),); + } + } + + pub async fn assert_binding_matches( + &mut self, + keystrokes: [&str; COUNT], + initial_state: &str, + ) -> Option<(ContextHandle, ContextHandle)> { + if let Some(possible_exempted_keystrokes) = self.exemptions.get(initial_state) { + match possible_exempted_keystrokes { + Some(exempted_keystrokes) => { + if exempted_keystrokes.contains(&format!("{keystrokes:?}")) { + // This keystroke was exempted for this insertion text + return None; + } + } + None => { + // All keystrokes for this insertion text are exempted + return None; + } + } + } + + let _state_context = self.set_shared_state(initial_state).await; + let _keystroke_context = self.simulate_shared_keystrokes(keystrokes).await; + self.assert_state_matches().await; + Some((_state_context, _keystroke_context)) + } + + pub async fn assert_binding_matches_all( + &mut self, + keystrokes: [&str; COUNT], + marked_positions: &str, + ) { + let (unmarked_text, cursor_offsets) = marked_text_offsets(marked_positions); + + for cursor_offset in cursor_offsets.iter() { + let mut marked_text = unmarked_text.clone(); + marked_text.insert(*cursor_offset, 'ˇ'); + + self.assert_binding_matches(keystrokes, &marked_text).await; + } + } + + pub fn binding( + self, + keystrokes: [&'static str; COUNT], + ) -> NeovimBackedBindingTestContext<'a, COUNT> { + NeovimBackedBindingTestContext::new(keystrokes, self) + } +} + +impl<'a> Deref for NeovimBackedTestContext<'a> { + type Target = VimTestContext<'a>; + + fn deref(&self) -> &Self::Target { + &self.cx + } +} + +impl<'a> DerefMut for NeovimBackedTestContext<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.cx + } +} + +#[cfg(test)] +mod test { + use gpui::TestAppContext; + + use crate::test::NeovimBackedTestContext; + + #[gpui::test] + async fn neovim_backed_test_context_works(cx: &mut TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + cx.assert_state_matches().await; + cx.set_shared_state("This is a tesˇt").await; + cx.assert_state_matches().await; + } +} diff --git a/crates/vim/src/test/neovim_connection.rs b/crates/vim/src/test/neovim_connection.rs new file mode 100644 index 0000000000..ff4e10cfe5 --- /dev/null +++ b/crates/vim/src/test/neovim_connection.rs @@ -0,0 +1,383 @@ +#[cfg(feature = "neovim")] +use std::ops::{Deref, DerefMut}; +use std::{ops::Range, path::PathBuf}; + +#[cfg(feature = "neovim")] +use async_compat::Compat; +#[cfg(feature = "neovim")] +use async_trait::async_trait; +#[cfg(feature = "neovim")] +use gpui::keymap::Keystroke; +use language::{Point, Selection}; +#[cfg(feature = "neovim")] +use lazy_static::lazy_static; +#[cfg(feature = "neovim")] +use nvim_rs::{ + create::tokio::new_child_cmd, error::LoopError, Handler, Neovim, UiAttachOptions, Value, +}; +#[cfg(feature = "neovim")] +use parking_lot::ReentrantMutex; +use serde::{Deserialize, Serialize}; +#[cfg(feature = "neovim")] +use tokio::{ + process::{Child, ChildStdin, Command}, + task::JoinHandle, +}; + +use crate::state::Mode; +use collections::VecDeque; + +// Neovim doesn't like to be started simultaneously from multiple threads. We use thsi lock +// to ensure we are only constructing one neovim connection at a time. +#[cfg(feature = "neovim")] +lazy_static! { + static ref NEOVIM_LOCK: ReentrantMutex<()> = ReentrantMutex::new(()); +} + +#[derive(Serialize, Deserialize)] +pub enum NeovimData { + Text(String), + Selection { start: (u32, u32), end: (u32, u32) }, + Mode(Option), +} + +pub struct NeovimConnection { + data: VecDeque, + #[cfg(feature = "neovim")] + test_case_id: String, + #[cfg(feature = "neovim")] + nvim: Neovim>, + #[cfg(feature = "neovim")] + _join_handle: JoinHandle>>, + #[cfg(feature = "neovim")] + _child: Child, +} + +impl NeovimConnection { + pub async fn new(test_case_id: String) -> Self { + #[cfg(feature = "neovim")] + let handler = NvimHandler {}; + #[cfg(feature = "neovim")] + let (nvim, join_handle, child) = Compat::new(async { + // Ensure we don't create neovim connections in parallel + let _lock = NEOVIM_LOCK.lock(); + let (nvim, join_handle, child) = new_child_cmd( + &mut Command::new("nvim").arg("--embed").arg("--clean"), + handler, + ) + .await + .expect("Could not connect to neovim process"); + + nvim.ui_attach(100, 100, &UiAttachOptions::default()) + .await + .expect("Could not attach to ui"); + + // Makes system act a little more like zed in terms of indentation + nvim.set_option("smartindent", nvim_rs::Value::Boolean(true)) + .await + .expect("Could not set smartindent on startup"); + + (nvim, join_handle, child) + }) + .await; + + Self { + #[cfg(feature = "neovim")] + data: Default::default(), + #[cfg(not(feature = "neovim"))] + data: Self::read_test_data(&test_case_id), + #[cfg(feature = "neovim")] + test_case_id, + #[cfg(feature = "neovim")] + nvim, + #[cfg(feature = "neovim")] + _join_handle: join_handle, + #[cfg(feature = "neovim")] + _child: child, + } + } + + // Sends a keystroke to the neovim process. + #[cfg(feature = "neovim")] + pub async fn send_keystroke(&mut self, keystroke_text: &str) { + let keystroke = Keystroke::parse(keystroke_text).unwrap(); + let special = keystroke.shift + || keystroke.ctrl + || keystroke.alt + || keystroke.cmd + || keystroke.key.len() > 1; + let start = if special { "<" } else { "" }; + let shift = if keystroke.shift { "S-" } else { "" }; + let ctrl = if keystroke.ctrl { "C-" } else { "" }; + let alt = if keystroke.alt { "M-" } else { "" }; + let cmd = if keystroke.cmd { "D-" } else { "" }; + let end = if special { ">" } else { "" }; + + let key = format!("{start}{shift}{ctrl}{alt}{cmd}{}{end}", keystroke.key); + + self.nvim + .input(&key) + .await + .expect("Could not input keystroke"); + } + + // If not running with a live neovim connection, this is a no-op + #[cfg(not(feature = "neovim"))] + pub async fn send_keystroke(&mut self, _keystroke_text: &str) {} + + #[cfg(feature = "neovim")] + pub async fn set_state(&mut self, selection: Selection, text: &str) { + let nvim_buffer = self + .nvim + .get_current_buf() + .await + .expect("Could not get neovim buffer"); + let lines = text + .split('\n') + .map(|line| line.to_string()) + .collect::>(); + + nvim_buffer + .set_lines(0, -1, false, lines) + .await + .expect("Could not set nvim buffer text"); + + self.nvim + .input("") + .await + .expect("Could not send escape to nvim"); + self.nvim + .input("") + .await + .expect("Could not send escape to nvim"); + + let nvim_window = self + .nvim + .get_current_win() + .await + .expect("Could not get neovim window"); + + if !selection.is_empty() { + panic!("Setting neovim state with non empty selection not yet supported"); + } + let cursor = selection.head(); + nvim_window + .set_cursor((cursor.row as i64 + 1, cursor.column as i64)) + .await + .expect("Could not set nvim cursor position"); + } + + #[cfg(not(feature = "neovim"))] + pub async fn set_state(&mut self, _selection: Selection, _text: &str) {} + + #[cfg(feature = "neovim")] + pub async fn text(&mut self) -> String { + let nvim_buffer = self + .nvim + .get_current_buf() + .await + .expect("Could not get neovim buffer"); + let text = nvim_buffer + .get_lines(0, -1, false) + .await + .expect("Could not get buffer text") + .join("\n"); + + self.data.push_back(NeovimData::Text(text.clone())); + + text + } + + #[cfg(not(feature = "neovim"))] + pub async fn text(&mut self) -> String { + if let Some(NeovimData::Text(text)) = self.data.pop_front() { + text + } else { + panic!("Invalid test data. Is test deterministic? Try running with '--features neovim' to regenerate"); + } + } + + #[cfg(feature = "neovim")] + pub async fn selection(&mut self) -> Range { + let cursor_row: u32 = self + .nvim + .command_output("echo line('.')") + .await + .unwrap() + .parse::() + .unwrap() + - 1; // Neovim rows start at 1 + let cursor_col: u32 = self + .nvim + .command_output("echo col('.')") + .await + .unwrap() + .parse::() + .unwrap() + - 1; // Neovim columns start at 1 + + let (start, end) = if let Some(Mode::Visual { .. }) = self.mode().await { + self.nvim + .input("") + .await + .expect("Could not exit visual mode"); + let nvim_buffer = self + .nvim + .get_current_buf() + .await + .expect("Could not get neovim buffer"); + let (start_row, start_col) = nvim_buffer + .get_mark("<") + .await + .expect("Could not get selection start"); + let (end_row, end_col) = nvim_buffer + .get_mark(">") + .await + .expect("Could not get selection end"); + self.nvim + .input("gv") + .await + .expect("Could not reselect visual selection"); + + if cursor_row == start_row as u32 - 1 && cursor_col == start_col as u32 { + ( + (end_row as u32 - 1, end_col as u32), + (start_row as u32 - 1, start_col as u32), + ) + } else { + ( + (start_row as u32 - 1, start_col as u32), + (end_row as u32 - 1, end_col as u32), + ) + } + } else { + ((cursor_row, cursor_col), (cursor_row, cursor_col)) + }; + + self.data.push_back(NeovimData::Selection { start, end }); + + Point::new(start.0, start.1)..Point::new(end.0, end.1) + } + + #[cfg(not(feature = "neovim"))] + pub async fn selection(&mut self) -> Range { + // Selection code fetches the mode. This emulates that. + let _mode = self.mode().await; + if let Some(NeovimData::Selection { start, end }) = self.data.pop_front() { + Point::new(start.0, start.1)..Point::new(end.0, end.1) + } else { + panic!("Invalid test data. Is test deterministic? Try running with '--features neovim' to regenerate"); + } + } + + #[cfg(feature = "neovim")] + pub async fn mode(&mut self) -> Option { + let nvim_mode_text = self + .nvim + .get_mode() + .await + .expect("Could not get mode") + .into_iter() + .find_map(|(key, value)| { + if key.as_str() == Some("mode") { + Some(value.as_str().unwrap().to_owned()) + } else { + None + } + }) + .expect("Could not find mode value"); + + let mode = match nvim_mode_text.as_ref() { + "i" => Some(Mode::Insert), + "n" => Some(Mode::Normal), + "v" => Some(Mode::Visual { line: false }), + "V" => Some(Mode::Visual { line: true }), + _ => None, + }; + + self.data.push_back(NeovimData::Mode(mode.clone())); + + mode + } + + #[cfg(not(feature = "neovim"))] + pub async fn mode(&mut self) -> Option { + if let Some(NeovimData::Mode(mode)) = self.data.pop_front() { + mode + } else { + panic!("Invalid test data. Is test deterministic? Try running with '--features neovim' to regenerate"); + } + } + + fn test_data_path(test_case_id: &str) -> PathBuf { + let mut data_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + data_path.push("test_data"); + data_path.push(format!("{}.json", test_case_id)); + data_path + } + + #[cfg(not(feature = "neovim"))] + fn read_test_data(test_case_id: &str) -> VecDeque { + let path = Self::test_data_path(test_case_id); + let json = std::fs::read_to_string(path).expect( + "Could not read test data. Is it generated? Try running test with '--features neovim'", + ); + + serde_json::from_str(&json) + .expect("Test data corrupted. Try regenerating it with '--features neovim'") + } +} + +#[cfg(feature = "neovim")] +impl Deref for NeovimConnection { + type Target = Neovim>; + + fn deref(&self) -> &Self::Target { + &self.nvim + } +} + +#[cfg(feature = "neovim")] +impl DerefMut for NeovimConnection { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.nvim + } +} + +#[cfg(feature = "neovim")] +impl Drop for NeovimConnection { + fn drop(&mut self) { + let path = Self::test_data_path(&self.test_case_id); + std::fs::create_dir_all(path.parent().unwrap()) + .expect("Could not create test data directory"); + let json = serde_json::to_string(&self.data).expect("Could not serialize test data"); + std::fs::write(path, json).expect("Could not write out test data"); + } +} + +#[cfg(feature = "neovim")] +#[derive(Clone)] +struct NvimHandler {} + +#[cfg(feature = "neovim")] +#[async_trait] +impl Handler for NvimHandler { + type Writer = nvim_rs::compat::tokio::Compat; + + async fn handle_request( + &self, + _event_name: String, + _arguments: Vec, + _neovim: Neovim, + ) -> Result { + unimplemented!(); + } + + async fn handle_notify( + &self, + _event_name: String, + _arguments: Vec, + _neovim: Neovim, + ) { + } +} diff --git a/crates/vim/src/test_contexts/vim_binding_test_context.rs b/crates/vim/src/test/vim_binding_test_context.rs similarity index 100% rename from crates/vim/src/test_contexts/vim_binding_test_context.rs rename to crates/vim/src/test/vim_binding_test_context.rs diff --git a/crates/vim/src/test_contexts/vim_test_context.rs b/crates/vim/src/test/vim_test_context.rs similarity index 91% rename from crates/vim/src/test_contexts/vim_test_context.rs rename to crates/vim/src/test/vim_test_context.rs index 711e9d610c..2fb446d127 100644 --- a/crates/vim/src/test_contexts/vim_test_context.rs +++ b/crates/vim/src/test/vim_test_context.rs @@ -1,7 +1,7 @@ use std::ops::{Deref, DerefMut}; -use editor::test::{AssertionContextManager, EditorTestContext}; -use gpui::{json::json, AppContext, ViewHandle}; +use editor::test::editor_test_context::EditorTestContext; +use gpui::{json::json, AppContext, ContextHandle, ViewHandle}; use project::Project; use search::{BufferSearchBar, ProjectSearchBar}; use workspace::{pane, AppState, WorkspaceHandle}; @@ -82,7 +82,6 @@ impl<'a> VimTestContext<'a> { cx, window_id, editor, - assertion_context: AssertionContextManager::new(), }, workspace, } @@ -120,18 +119,18 @@ impl<'a> VimTestContext<'a> { .read(|cx| cx.global::().state.operator_stack.last().copied()) } - pub fn set_state(&mut self, text: &str, mode: Mode) { + pub fn set_state(&mut self, text: &str, mode: Mode) -> ContextHandle { self.cx.update(|cx| { Vim::update(cx, |vim, cx| { vim.switch_mode(mode, false, cx); }) }); - self.cx.set_state(text); + self.cx.set_state(text) } pub fn assert_state(&mut self, text: &str, mode: Mode) { self.assert_editor_state(text); - assert_eq!(self.mode(), mode); + assert_eq!(self.mode(), mode, "{}", self.assertion_context()); } pub fn assert_binding( @@ -145,8 +144,8 @@ impl<'a> VimTestContext<'a> { self.set_state(initial_state, initial_mode); self.cx.simulate_keystrokes(keystrokes); self.cx.assert_editor_state(state_after); - assert_eq!(self.mode(), mode_after); - assert_eq!(self.active_operator(), None); + assert_eq!(self.mode(), mode_after, "{}", self.assertion_context()); + assert_eq!(self.active_operator(), None, "{}", self.assertion_context()); } pub fn binding( diff --git a/crates/vim/src/test_contexts.rs b/crates/vim/src/test_contexts.rs deleted file mode 100644 index 1a65be251b..0000000000 --- a/crates/vim/src/test_contexts.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod neovim_backed_binding_test_context; -mod neovim_backed_test_context; -mod vim_binding_test_context; -mod vim_test_context; - -pub use neovim_backed_binding_test_context::*; -pub use neovim_backed_test_context::*; -pub use vim_binding_test_context::*; -pub use vim_test_context::*; diff --git a/crates/vim/src/test_contexts/neovim_backed_test_context.rs b/crates/vim/src/test_contexts/neovim_backed_test_context.rs deleted file mode 100644 index aa52f0c40b..0000000000 --- a/crates/vim/src/test_contexts/neovim_backed_test_context.rs +++ /dev/null @@ -1,518 +0,0 @@ -use std::{ - ops::{Deref, DerefMut, Range}, - path::PathBuf, -}; - -use collections::{HashMap, HashSet, VecDeque}; -use editor::DisplayPoint; -use gpui::keymap::Keystroke; - -#[cfg(feature = "neovim")] -use async_compat::Compat; -#[cfg(feature = "neovim")] -use async_trait::async_trait; -#[cfg(feature = "neovim")] -use nvim_rs::{ - create::tokio::new_child_cmd, error::LoopError, Handler, Neovim, UiAttachOptions, Value, -}; -use serde::{Deserialize, Serialize}; -#[cfg(feature = "neovim")] -use tokio::{ - process::{Child, ChildStdin, Command}, - task::JoinHandle, -}; -use util::test::marked_text_offsets; - -use crate::state::Mode; - -use super::{NeovimBackedBindingTestContext, VimTestContext}; - -pub struct NeovimBackedTestContext<'a> { - cx: VimTestContext<'a>, - // Lookup for exempted assertions. Keyed by the insertion text, and with a value indicating which - // bindings are exempted. If None, all bindings are ignored for that insertion text. - exemptions: HashMap>>, - neovim: NeovimConnection, -} - -impl<'a> NeovimBackedTestContext<'a> { - pub async fn new(cx: &'a mut gpui::TestAppContext) -> NeovimBackedTestContext<'a> { - let function_name = cx.function_name.clone(); - let cx = VimTestContext::new(cx, true).await; - Self { - cx, - exemptions: Default::default(), - neovim: NeovimConnection::new(function_name).await, - } - } - - pub fn add_initial_state_exemption(&mut self, initial_state: &str) { - let initial_state = initial_state.to_string(); - // None represents all keybindings being exempted for that initial state - self.exemptions.insert(initial_state, None); - } - - pub async fn simulate_shared_keystroke(&mut self, keystroke_text: &str) { - let keystroke = Keystroke::parse(keystroke_text).unwrap(); - - #[cfg(feature = "neovim")] - { - let special = keystroke.shift - || keystroke.ctrl - || keystroke.alt - || keystroke.cmd - || keystroke.key.len() > 1; - let start = if special { "<" } else { "" }; - let shift = if keystroke.shift { "S-" } else { "" }; - let ctrl = if keystroke.ctrl { "C-" } else { "" }; - let alt = if keystroke.alt { "M-" } else { "" }; - let cmd = if keystroke.cmd { "D-" } else { "" }; - let end = if special { ">" } else { "" }; - - let key = format!("{start}{shift}{ctrl}{alt}{cmd}{}{end}", keystroke.key); - - self.neovim - .input(&key) - .await - .expect("Could not input keystroke"); - } - - let window_id = self.window_id; - self.cx.dispatch_keystroke(window_id, keystroke, false); - } - - pub async fn simulate_shared_keystrokes( - &mut self, - keystroke_texts: [&str; COUNT], - ) { - for keystroke_text in keystroke_texts.into_iter() { - self.simulate_shared_keystroke(keystroke_text).await; - } - } - - pub async fn set_shared_state(&mut self, marked_text: &str) { - self.set_state(marked_text, Mode::Normal); - - #[cfg(feature = "neovim")] - { - let cursor_point = - self.editor(|editor, cx| editor.selections.newest::(cx)); - let nvim_buffer = self - .neovim - .get_current_buf() - .await - .expect("Could not get neovim buffer"); - let lines = self - .buffer_text() - .split('\n') - .map(|line| line.to_string()) - .collect::>(); - - nvim_buffer - .set_lines(0, -1, false, lines) - .await - .expect("Could not set nvim buffer text"); - - self.neovim - .input("") - .await - .expect("Could not send escape to nvim"); - self.neovim - .input("") - .await - .expect("Could not send escape to nvim"); - - let nvim_window = self - .neovim - .get_current_win() - .await - .expect("Could not get neovim window"); - nvim_window - .set_cursor(( - cursor_point.head().row as i64 + 1, - cursor_point.head().column as i64, - )) - .await - .expect("Could not set nvim cursor position"); - } - } - - pub async fn assert_state_matches(&mut self) { - assert_eq!( - self.neovim.text().await, - self.buffer_text(), - "{}", - self.assertion_context.context() - ); - - let zed_selection = self.update_editor(|editor, cx| editor.selections.newest_display(cx)); - let mut zed_selection_range = zed_selection.range(); - // Zed selections adjust themselves to make the end point visually make sense - if zed_selection.reversed { - *zed_selection_range.end.column_mut() = - zed_selection_range.end.column().saturating_sub(1); - } - let neovim_selection = self.neovim.selection().await; - assert_eq!( - neovim_selection, - zed_selection_range, - "{}", - self.assertion_context.context() - ); - - if let Some(neovim_mode) = self.neovim.mode().await { - assert_eq!( - neovim_mode, - self.mode(), - "{}", - self.assertion_context.context() - ); - } - } - - pub async fn assert_binding_matches( - &mut self, - keystrokes: [&str; COUNT], - initial_state: &str, - ) { - if let Some(possible_exempted_keystrokes) = self.exemptions.get(initial_state) { - match possible_exempted_keystrokes { - Some(exempted_keystrokes) => { - if exempted_keystrokes.contains(&format!("{keystrokes:?}")) { - // This keystroke was exempted for this insertion text - return; - } - } - None => { - // All keystrokes for this insertion text are exempted - return; - } - } - } - - let _keybinding_context_handle = - self.add_assertion_context(format!("Key Binding Under Test: {:?}", keystrokes)); - let _initial_state_context_handle = self.add_assertion_context(format!( - "Initial State: \"{}\"", - initial_state.escape_debug().to_string() - )); - self.set_shared_state(initial_state).await; - self.simulate_shared_keystrokes(keystrokes).await; - self.assert_state_matches().await; - } - - pub async fn assert_binding_matches_all( - &mut self, - keystrokes: [&str; COUNT], - marked_positions: &str, - ) { - let (unmarked_text, cursor_offsets) = marked_text_offsets(marked_positions); - - for cursor_offset in cursor_offsets.iter() { - let mut marked_text = unmarked_text.clone(); - marked_text.insert(*cursor_offset, 'ˇ'); - - self.assert_binding_matches(keystrokes, &marked_text).await; - } - } - - pub fn binding( - self, - keystrokes: [&'static str; COUNT], - ) -> NeovimBackedBindingTestContext<'a, COUNT> { - NeovimBackedBindingTestContext::new(keystrokes, self) - } -} - -impl<'a> Deref for NeovimBackedTestContext<'a> { - type Target = VimTestContext<'a>; - - fn deref(&self) -> &Self::Target { - &self.cx - } -} - -impl<'a> DerefMut for NeovimBackedTestContext<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.cx - } -} - -#[derive(Serialize, Deserialize)] -pub enum NeovimData { - Text(String), - Selection { start: (u32, u32), end: (u32, u32) }, - Mode(Option), -} - -struct NeovimConnection { - data: VecDeque, - #[cfg(feature = "neovim")] - test_case_id: String, - #[cfg(feature = "neovim")] - nvim: Neovim>, - #[cfg(feature = "neovim")] - _join_handle: JoinHandle>>, - #[cfg(feature = "neovim")] - _child: Child, -} - -impl NeovimConnection { - async fn new(test_case_id: String) -> Self { - #[cfg(feature = "neovim")] - let handler = NvimHandler {}; - #[cfg(feature = "neovim")] - let (nvim, join_handle, child) = Compat::new(async { - let (nvim, join_handle, child) = new_child_cmd( - &mut Command::new("nvim").arg("--embed").arg("--clean"), - handler, - ) - .await - .expect("Could not connect to neovim process"); - - nvim.ui_attach(100, 100, &UiAttachOptions::default()) - .await - .expect("Could not attach to ui"); - - // Makes system act a little more like zed in terms of indentation - nvim.set_option("smartindent", nvim_rs::Value::Boolean(true)) - .await - .expect("Could not set smartindent on startup"); - - (nvim, join_handle, child) - }) - .await; - - Self { - #[cfg(feature = "neovim")] - data: Default::default(), - #[cfg(not(feature = "neovim"))] - data: Self::read_test_data(&test_case_id), - #[cfg(feature = "neovim")] - test_case_id, - #[cfg(feature = "neovim")] - nvim, - #[cfg(feature = "neovim")] - _join_handle: join_handle, - #[cfg(feature = "neovim")] - _child: child, - } - } - - #[cfg(feature = "neovim")] - pub async fn text(&mut self) -> String { - let nvim_buffer = self - .nvim - .get_current_buf() - .await - .expect("Could not get neovim buffer"); - let text = nvim_buffer - .get_lines(0, -1, false) - .await - .expect("Could not get buffer text") - .join("\n"); - - self.data.push_back(NeovimData::Text(text.clone())); - - text - } - - #[cfg(not(feature = "neovim"))] - pub async fn text(&mut self) -> String { - if let Some(NeovimData::Text(text)) = self.data.pop_front() { - text - } else { - panic!("Invalid test data. Is test deterministic? Try running with '--features neovim' to regenerate"); - } - } - - #[cfg(feature = "neovim")] - pub async fn selection(&mut self) -> Range { - let (start, end) = if let Some(Mode::Visual { .. }) = self.mode().await { - self.nvim - .input("") - .await - .expect("Could not exit visual mode"); - let nvim_buffer = self - .nvim - .get_current_buf() - .await - .expect("Could not get neovim buffer"); - let (start_row, start_col) = nvim_buffer - .get_mark("<") - .await - .expect("Could not get selection start"); - let (end_row, end_col) = nvim_buffer - .get_mark(">") - .await - .expect("Could not get selection end"); - self.nvim - .input("gv") - .await - .expect("Could not reselect visual selection"); - - ( - (start_row as u32 - 1, start_col as u32), - (end_row as u32 - 1, end_col as u32), - ) - } else { - let nvim_row: u32 = self - .nvim - .command_output("echo line('.')") - .await - .unwrap() - .parse::() - .unwrap() - - 1; // Neovim rows start at 1 - let nvim_column: u32 = self - .nvim - .command_output("echo col('.')") - .await - .unwrap() - .parse::() - .unwrap() - - 1; // Neovim columns start at 1 - - ((nvim_row, nvim_column), (nvim_row, nvim_column)) - }; - - self.data.push_back(NeovimData::Selection { start, end }); - - DisplayPoint::new(start.0, start.1)..DisplayPoint::new(end.0, end.1) - } - - #[cfg(not(feature = "neovim"))] - pub async fn selection(&mut self) -> Range { - if let Some(NeovimData::Selection { start, end }) = self.data.pop_front() { - DisplayPoint::new(start.0, start.1)..DisplayPoint::new(end.0, end.1) - } else { - panic!("Invalid test data. Is test deterministic? Try running with '--features neovim' to regenerate"); - } - } - - #[cfg(feature = "neovim")] - pub async fn mode(&mut self) -> Option { - let nvim_mode_text = self - .nvim - .get_mode() - .await - .expect("Could not get mode") - .into_iter() - .find_map(|(key, value)| { - if key.as_str() == Some("mode") { - Some(value.as_str().unwrap().to_owned()) - } else { - None - } - }) - .expect("Could not find mode value"); - - let mode = match nvim_mode_text.as_ref() { - "i" => Some(Mode::Insert), - "n" => Some(Mode::Normal), - "v" => Some(Mode::Visual { line: false }), - "V" => Some(Mode::Visual { line: true }), - _ => None, - }; - - self.data.push_back(NeovimData::Mode(mode.clone())); - - mode - } - - #[cfg(not(feature = "neovim"))] - pub async fn mode(&mut self) -> Option { - if let Some(NeovimData::Mode(mode)) = self.data.pop_front() { - mode - } else { - panic!("Invalid test data. Is test deterministic? Try running with '--features neovim' to regenerate"); - } - } - - fn test_data_path(test_case_id: &str) -> PathBuf { - let mut data_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - data_path.push("test_data"); - data_path.push(format!("{}.json", test_case_id)); - data_path - } - - #[cfg(not(feature = "neovim"))] - fn read_test_data(test_case_id: &str) -> VecDeque { - let path = Self::test_data_path(test_case_id); - let json = std::fs::read_to_string(path).expect( - "Could not read test data. Is it generated? Try running test with '--features neovim'", - ); - - serde_json::from_str(&json) - .expect("Test data corrupted. Try regenerating it with '--features neovim'") - } -} - -#[cfg(feature = "neovim")] -impl Deref for NeovimConnection { - type Target = Neovim>; - - fn deref(&self) -> &Self::Target { - &self.nvim - } -} - -#[cfg(feature = "neovim")] -impl DerefMut for NeovimConnection { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.nvim - } -} - -#[cfg(feature = "neovim")] -impl Drop for NeovimConnection { - fn drop(&mut self) { - let path = Self::test_data_path(&self.test_case_id); - std::fs::create_dir_all(path.parent().unwrap()) - .expect("Could not create test data directory"); - let json = serde_json::to_string(&self.data).expect("Could not serialize test data"); - std::fs::write(path, json).expect("Could not write out test data"); - } -} - -#[cfg(feature = "neovim")] -#[derive(Clone)] -struct NvimHandler {} - -#[cfg(feature = "neovim")] -#[async_trait] -impl Handler for NvimHandler { - type Writer = nvim_rs::compat::tokio::Compat; - - async fn handle_request( - &self, - _event_name: String, - _arguments: Vec, - _neovim: Neovim, - ) -> Result { - unimplemented!(); - } - - async fn handle_notify( - &self, - _event_name: String, - _arguments: Vec, - _neovim: Neovim, - ) { - } -} - -#[cfg(test)] -mod test { - use gpui::TestAppContext; - - use crate::test_contexts::NeovimBackedTestContext; - - #[gpui::test] - async fn neovim_backed_test_context_works(cx: &mut TestAppContext) { - let mut cx = NeovimBackedTestContext::new(cx).await; - cx.assert_state_matches().await; - cx.set_shared_state("This is a tesˇt").await; - cx.assert_state_matches().await; - } -} diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index 5c9f23b41f..81bafcf3e2 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -1,5 +1,5 @@ #[cfg(test)] -mod test_contexts; +mod test; mod editor_events; mod insert; @@ -231,101 +231,3 @@ impl Vim { } } } - -#[cfg(test)] -mod test { - use indoc::indoc; - use search::BufferSearchBar; - - use crate::{ - state::Mode, - test_contexts::{NeovimBackedTestContext, VimTestContext}, - }; - - #[gpui::test] - async fn test_initially_disabled(cx: &mut gpui::TestAppContext) { - let mut cx = VimTestContext::new(cx, false).await; - cx.simulate_keystrokes(["h", "j", "k", "l"]); - cx.assert_editor_state("hjklˇ"); - } - - #[gpui::test] - async fn test_neovim(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new(cx).await; - - cx.simulate_shared_keystroke("i").await; - cx.simulate_shared_keystrokes([ - "shift-T", "e", "s", "t", " ", "t", "e", "s", "t", "escape", "0", "d", "w", - ]) - .await; - cx.assert_state_matches().await; - cx.assert_editor_state("ˇtest"); - } - - #[gpui::test] - async fn test_toggle_through_settings(cx: &mut gpui::TestAppContext) { - let mut cx = VimTestContext::new(cx, true).await; - - cx.simulate_keystroke("i"); - assert_eq!(cx.mode(), Mode::Insert); - - // Editor acts as though vim is disabled - cx.disable_vim(); - cx.simulate_keystrokes(["h", "j", "k", "l"]); - cx.assert_editor_state("hjklˇ"); - - // Selections aren't changed if editor is blurred but vim-mode is still disabled. - cx.set_state("«hjklˇ»", Mode::Normal); - cx.assert_editor_state("«hjklˇ»"); - cx.update_editor(|_, cx| cx.blur()); - cx.assert_editor_state("«hjklˇ»"); - cx.update_editor(|_, cx| cx.focus_self()); - cx.assert_editor_state("«hjklˇ»"); - - // Enabling dynamically sets vim mode again and restores normal mode - cx.enable_vim(); - assert_eq!(cx.mode(), Mode::Normal); - cx.simulate_keystrokes(["h", "h", "h", "l"]); - assert_eq!(cx.buffer_text(), "hjkl".to_owned()); - cx.assert_editor_state("hˇjkl"); - cx.simulate_keystrokes(["i", "T", "e", "s", "t"]); - cx.assert_editor_state("hTestˇjkl"); - - // Disabling and enabling resets to normal mode - assert_eq!(cx.mode(), Mode::Insert); - cx.disable_vim(); - cx.enable_vim(); - assert_eq!(cx.mode(), Mode::Normal); - } - - #[gpui::test] - async fn test_buffer_search(cx: &mut gpui::TestAppContext) { - let mut cx = VimTestContext::new(cx, true).await; - - cx.set_state( - indoc! {" - The quick brown - fox juˇmps over - the lazy dog"}, - Mode::Normal, - ); - cx.simulate_keystroke("/"); - - // We now use a weird insert mode with selection when jumping to a single line editor - assert_eq!(cx.mode(), Mode::Insert); - - let search_bar = cx.workspace(|workspace, cx| { - workspace - .active_pane() - .read(cx) - .toolbar() - .read(cx) - .item_of_type::() - .expect("Buffer search bar should be deployed") - }); - - search_bar.read_with(cx.cx, |bar, cx| { - assert_eq!(bar.query_editor.read(cx).text(cx), "jumps"); - }) - } -} diff --git a/crates/vim/src/visual.rs b/crates/vim/src/visual.rs index eb222346ce..63d914d570 100644 --- a/crates/vim/src/visual.rs +++ b/crates/vim/src/visual.rs @@ -282,7 +282,7 @@ mod test { use crate::{ state::Mode, - test_contexts::{NeovimBackedTestContext, VimTestContext}, + test::{NeovimBackedTestContext, VimTestContext}, }; #[gpui::test] @@ -305,20 +305,23 @@ mod test { #[gpui::test] async fn test_visual_delete(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new(cx) - .await - .binding(["v", "w", "x"]); - cx.assert("The quick ˇbrown").await; - let mut cx = cx.binding(["v", "w", "j", "x"]); - cx.assert(indoc! {" + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.assert_binding_matches(["v", "w", "x"], "The quick ˇbrown") + .await; + cx.assert_binding_matches( + ["v", "w", "j", "x"], + indoc! {" The ˇquick brown fox jumps over - the lazy dog"}) - .await; + the lazy dog"}, + ) + .await; // Test pasting code copied on delete cx.simulate_shared_keystrokes(["j", "p"]).await; cx.assert_state_matches().await; + let mut cx = cx.binding(["v", "w", "j", "x"]); cx.assert_all(indoc! {" The ˇquick brown fox jumps over @@ -370,147 +373,58 @@ mod test { #[gpui::test] async fn test_visual_change(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["v", "w", "c"]).mode_after(Mode::Insert); - cx.assert("The quick ˇbrown", "The quick ˇ"); - let mut cx = cx.binding(["v", "w", "j", "c"]).mode_after(Mode::Insert); - cx.assert( - indoc! {" + let mut cx = NeovimBackedTestContext::new(cx) + .await + .binding(["v", "w", "c"]); + cx.assert("The quick ˇbrown").await; + let mut cx = cx.binding(["v", "w", "j", "c"]); + cx.assert_all(indoc! {" The ˇquick brown - fox jumps over - the lazy dog"}, - indoc! {" - The ˇver - the lazy dog"}, - ); - cx.assert( - indoc! {" - The quick brown - fox jumps over - the ˇlazy dog"}, - indoc! {" - The quick brown - fox jumps over - the ˇog"}, - ); - cx.assert( - indoc! {" - The quick brown fox jumps ˇover - the lazy dog"}, - indoc! {" - The quick brown - fox jumps ˇhe lazy dog"}, - ); - let mut cx = cx.binding(["v", "b", "k", "c"]).mode_after(Mode::Insert); - cx.assert( - indoc! {" + the ˇlazy dog"}) + .await; + let mut cx = cx.binding(["v", "b", "k", "c"]); + cx.assert_all(indoc! {" The ˇquick brown - fox jumps over - the lazy dog"}, - indoc! {" - ˇuick brown - fox jumps over - the lazy dog"}, - ); - cx.assert( - indoc! {" - The quick brown - fox jumps over - the ˇlazy dog"}, - indoc! {" - The quick brown - ˇazy dog"}, - ); - cx.assert( - indoc! {" - The quick brown fox jumps ˇover - the lazy dog"}, - indoc! {" - The ˇver - the lazy dog"}, - ); + the ˇlazy dog"}) + .await; } #[gpui::test] async fn test_visual_line_change(cx: &mut gpui::TestAppContext) { - let cx = VimTestContext::new(cx, true).await; - let mut cx = cx.binding(["shift-v", "c"]).mode_after(Mode::Insert); - cx.assert( - indoc! {" + let mut cx = NeovimBackedTestContext::new(cx) + .await + .binding(["shift-v", "c"]); + cx.assert(indoc! {" The quˇick brown fox jumps over - the lazy dog"}, - indoc! {" - ˇ - fox jumps over - the lazy dog"}, - ); + the lazy dog"}) + .await; // Test pasting code copied on change - cx.simulate_keystrokes(["escape", "j", "p"]); - cx.assert_editor_state(indoc! {" - - fox jumps over - ˇThe quick brown - the lazy dog"}); + cx.simulate_shared_keystrokes(["escape", "j", "p"]).await; + cx.assert_state_matches().await; - cx.assert( - indoc! {" + cx.assert_all(indoc! {" The quick brown fox juˇmps over - the lazy dog"}, - indoc! {" - The quick brown - ˇ - the lazy dog"}, - ); - cx.assert( - indoc! {" - The quick brown - fox jumps over - the laˇzy dog"}, - indoc! {" - The quick brown - fox jumps over - ˇ"}, - ); - let mut cx = cx.binding(["shift-v", "j", "c"]).mode_after(Mode::Insert); - cx.assert( - indoc! {" + the laˇzy dog"}) + .await; + let mut cx = cx.binding(["shift-v", "j", "c"]); + cx.assert(indoc! {" The quˇick brown fox jumps over - the lazy dog"}, - indoc! {" - ˇ - the lazy dog"}, - ); + the lazy dog"}) + .await; // Test pasting code copied on delete - cx.simulate_keystrokes(["escape", "j", "p"]); - cx.assert_editor_state(indoc! {" - - the lazy dog - ˇThe quick brown - fox jumps over"}); - cx.assert( - indoc! {" + cx.simulate_shared_keystrokes(["escape", "j", "p"]).await; + cx.assert_state_matches().await; + + cx.assert_all(indoc! {" The quick brown fox juˇmps over - the lazy dog"}, - indoc! {" - The quick brown - ˇ"}, - ); - cx.assert( - indoc! {" - The quick brown - fox jumps over - the laˇzy dog"}, - indoc! {" - The quick brown - fox jumps over - ˇ"}, - ); + the laˇzy dog"}) + .await; } #[gpui::test] @@ -619,7 +533,7 @@ mod test { cx.assert_state( indoc! {" The quick brown - fox jumpsˇjumps over + fox jumpsjumpˇs over the lazy dog"}, Mode::Normal, ); diff --git a/crates/vim/test_data/test_b.json b/crates/vim/test_data/test_b.json new file mode 100644 index 0000000000..635edf536b --- /dev/null +++ b/crates/vim/test_data/test_b.json @@ -0,0 +1 @@ +[{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[3,10],"end":[3,10]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Normal"},{"Text":"The quick-brown\n\n\nfox_jumps over\nthe"},{"Mode":"Normal"},{"Selection":{"start":[3,10],"end":[3,10]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_cc.json b/crates/vim/test_data/test_cc.json new file mode 100644 index 0000000000..67492d827e --- /dev/null +++ b/crates/vim/test_data/test_cc.json @@ -0,0 +1 @@ +[{"Text":""},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":""},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nbrown fox\njumps over"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick\n\njumps over"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick\nbrown fox\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick\n\nbrown fox"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_dd.json b/crates/vim/test_data/test_dd.json new file mode 100644 index 0000000000..fa86b9d3b5 --- /dev/null +++ b/crates/vim/test_data/test_dd.json @@ -0,0 +1 @@ +[{"Text":""},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":""},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"brown fox\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Normal"},{"Text":"The quick\njumps over"},{"Mode":"Normal"},{"Selection":{"start":[1,6],"end":[1,6]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox"},{"Mode":"Normal"},{"Selection":{"start":[1,6],"end":[1,6]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_left.json b/crates/vim/test_data/test_delete_left.json new file mode 100644 index 0000000000..06e24f34f7 --- /dev/null +++ b/crates/vim/test_data/test_delete_left.json @@ -0,0 +1 @@ +[{"Text":"Test"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"est"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Tst"},{"Mode":"Normal"},{"Selection":{"start":[0,1],"end":[0,1]}},{"Mode":"Normal"},{"Text":"Tet"},{"Mode":"Normal"},{"Selection":{"start":[0,2],"end":[0,2]}},{"Mode":"Normal"},{"Text":"Test\ntest"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_to_end_of_line.json b/crates/vim/test_data/test_delete_to_end_of_line.json new file mode 100644 index 0000000000..591dac4200 --- /dev/null +++ b/crates/vim/test_data/test_delete_to_end_of_line.json @@ -0,0 +1 @@ +[{"Text":"The q\nbrown fox"},{"Mode":"Normal"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Normal"},{"Text":"The quick\n\nbrown fox"},{"Mode":"Normal"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_enter_visual_mode.json b/crates/vim/test_data/test_enter_visual_mode.json index 43d1e0559a..b13aa23589 100644 --- a/crates/vim/test_data/test_enter_visual_mode.json +++ b/crates/vim/test_data/test_enter_visual_mode.json @@ -1 +1 @@ -[{"Text":"The quick brown\nfox jumps over\nthe lazy dog"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,4],"end":[1,10]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown\nfox jumps over\nthe lazy dog"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[1,10],"end":[2,0]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown\nfox jumps over\nthe lazy dog"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[2,4],"end":[2,9]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown\nfox jumps over\nthe lazy dog"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,0],"end":[0,4]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown\nfox jumps over\nthe lazy dog"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,4],"end":[1,10]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown\nfox jumps over\nthe lazy dog"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[1,0],"end":[2,4]}},{"Mode":{"Visual":{"line":false}}}] \ No newline at end of file +[{"Text":"The quick brown\nfox jumps over\nthe lazy dog"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,4],"end":[1,10]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown\nfox jumps over\nthe lazy dog"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[1,10],"end":[2,0]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown\nfox jumps over\nthe lazy dog"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[2,4],"end":[2,9]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown\nfox jumps over\nthe lazy dog"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,4],"end":[0,0]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown\nfox jumps over\nthe lazy dog"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[1,10],"end":[0,4]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown\nfox jumps over\nthe lazy dog"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[2,4],"end":[1,0]}},{"Mode":{"Visual":{"line":false}}}] \ No newline at end of file diff --git a/crates/vim/test_data/test_insert_first_non_whitespace.json b/crates/vim/test_data/test_insert_first_non_whitespace.json new file mode 100644 index 0000000000..b64ea3c808 --- /dev/null +++ b/crates/vim/test_data/test_insert_first_non_whitespace.json @@ -0,0 +1 @@ +[{"Text":"The quick"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" The quick"},{"Mode":"Insert"},{"Selection":{"start":[0,1],"end":[0,1]}},{"Mode":"Insert"},{"Text":""},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick\nbrown fox"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nThe quick"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_jump_to_first_non_whitespace.json b/crates/vim/test_data/test_jump_to_first_non_whitespace.json new file mode 100644 index 0000000000..123b752860 --- /dev/null +++ b/crates/vim/test_data/test_jump_to_first_non_whitespace.json @@ -0,0 +1 @@ +[{"Text":"The quick"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" The quick"},{"Mode":"Normal"},{"Selection":{"start":[0,1],"end":[0,1]}},{"Mode":"Normal"},{"Text":""},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick\nbrown fox"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"\nThe quick"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" \nThe quick"},{"Mode":"Normal"},{"Selection":{"start":[0,3],"end":[0,3]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_o.json b/crates/vim/test_data/test_o.json new file mode 100644 index 0000000000..08bea7cae8 --- /dev/null +++ b/crates/vim/test_data/test_o.json @@ -0,0 +1 @@ +[{"Text":"\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick\n\nbrown fox\njumps over"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick\nbrown fox\n\njumps over"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"The quick\nbrown fox\njumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":"The quick\n\n\nbrown fox"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"fn test() {\n println!();\n \n}\n"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"fn test() {\n\n println!();\n}"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_p.json b/crates/vim/test_data/test_p.json new file mode 100644 index 0000000000..2cf45ea2f7 --- /dev/null +++ b/crates/vim/test_data/test_p.json @@ -0,0 +1 @@ +[{"Text":"The quick brown\nthe lazy dog"},{"Mode":"Normal"},{"Selection":{"start":[1,6],"end":[1,6]}},{"Mode":"Normal"},{"Text":"The quick brown\nthe lazy dog\nfox jumps over"},{"Mode":"Normal"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps overjumps o\nthe lazy dog"},{"Mode":"Normal"},{"Selection":{"start":[1,20],"end":[1,20]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_visual_change.json b/crates/vim/test_data/test_visual_change.json new file mode 100644 index 0000000000..c7f6df4445 --- /dev/null +++ b/crates/vim/test_data/test_visual_change.json @@ -0,0 +1 @@ +[{"Text":"The quick "},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The ver\nthe lazy dog"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The quick brown\nfox jumps he lazy dog"},{"Mode":"Insert"},{"Selection":{"start":[1,10],"end":[1,10]}},{"Mode":"Insert"},{"Text":"The quick brown\nfox jumps over\nthe og"},{"Mode":"Insert"},{"Selection":{"start":[2,4],"end":[2,4]}},{"Mode":"Insert"},{"Text":"uick brown\nfox jumps over\nthe lazy dog"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The ver\nthe lazy dog"},{"Mode":"Insert"},{"Selection":{"start":[0,4],"end":[0,4]}},{"Mode":"Insert"},{"Text":"The quick brown\nazy dog"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_visual_line_change.json b/crates/vim/test_data/test_visual_line_change.json new file mode 100644 index 0000000000..8c00d1bb1f --- /dev/null +++ b/crates/vim/test_data/test_visual_line_change.json @@ -0,0 +1 @@ +[{"Text":"\nfox jumps over\nthe lazy dog"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nfox jumps over\nThe quick brown\nthe lazy dog"},{"Mode":"Normal"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n\nthe lazy dog"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\nfox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"},{"Text":"\nthe lazy dog"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"\nthe lazy dog\nThe quick brown\nfox jumps over"},{"Mode":"Normal"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Normal"},{"Text":"The quick brown\n"},{"Mode":"Insert"},{"Selection":{"start":[1,0],"end":[1,0]}},{"Mode":"Insert"},{"Text":"The quick brown\nfox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[2,0],"end":[2,0]}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_x.json b/crates/vim/test_data/test_x.json new file mode 100644 index 0000000000..ca85e2842b --- /dev/null +++ b/crates/vim/test_data/test_x.json @@ -0,0 +1 @@ +[{"Text":"est"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Tet"},{"Mode":"Normal"},{"Selection":{"start":[0,2],"end":[0,2]}},{"Mode":"Normal"},{"Text":"Tes"},{"Mode":"Normal"},{"Selection":{"start":[0,2],"end":[0,2]}},{"Mode":"Normal"},{"Text":"Tes\ntest"},{"Mode":"Normal"},{"Selection":{"start":[0,2],"end":[0,2]}},{"Mode":"Normal"}] \ No newline at end of file From 6a237deb213355a9ff10cca37064bfa87ce91c01 Mon Sep 17 00:00:00 2001 From: K Simmons Date: Mon, 10 Oct 2022 15:32:12 -0700 Subject: [PATCH 223/314] Add some tests for portions of visual text objects. Note: they are slightly broken currently as described in the tests --- crates/vim/src/object.rs | 118 ++++++++++-------- crates/vim/src/test.rs | 1 + crates/vim/src/visual.rs | 37 +++++- .../test_change_around_sentence.json | 1 - .../test_data/test_change_around_word.json | 1 - ....json => test_change_sentence_object.json} | 2 +- ...word.json => test_change_word_object.json} | 2 +- .../test_delete_around_sentence.json | 1 - .../test_data/test_delete_around_word.json | 1 - ....json => test_delete_sentence_object.json} | 2 +- ...word.json => test_delete_word_object.json} | 2 +- crates/vim/test_data/test_neovim.json | 2 +- .../test_visual_sentence_object.json | 1 + .../test_data/test_visual_word_object.json | 1 + 14 files changed, 111 insertions(+), 61 deletions(-) delete mode 100644 crates/vim/test_data/test_change_around_sentence.json delete mode 100644 crates/vim/test_data/test_change_around_word.json rename crates/vim/test_data/{test_change_in_sentence.json => test_change_sentence_object.json} (51%) rename crates/vim/test_data/{test_change_in_word.json => test_change_word_object.json} (50%) delete mode 100644 crates/vim/test_data/test_delete_around_sentence.json delete mode 100644 crates/vim/test_data/test_delete_around_word.json rename crates/vim/test_data/{test_delete_in_sentence.json => test_delete_sentence_object.json} (51%) rename crates/vim/test_data/{test_delete_in_word.json => test_delete_word_object.json} (50%) create mode 100644 crates/vim/test_data/test_visual_sentence_object.json create mode 100644 crates/vim/test_data/test_visual_word_object.json diff --git a/crates/vim/src/object.rs b/crates/vim/src/object.rs index a0f9b4a6da..5adc8397fa 100644 --- a/crates/vim/src/object.rs +++ b/crates/vim/src/object.rs @@ -44,7 +44,7 @@ fn object(object: Object, cx: &mut MutableAppContext) { } impl Object { - pub fn object_range( + pub fn range( self, map: &DisplaySnapshot, relative_to: DisplayPoint, @@ -68,7 +68,7 @@ impl Object { selection: &mut Selection, around: bool, ) { - let range = self.object_range(map, selection.head(), around); + let range = self.range(map, selection.head(), around); selection.start = range.start; selection.end = range.end; } @@ -328,43 +328,58 @@ mod test { "}; #[gpui::test] - async fn test_change_in_word(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new(cx) - .await - .binding(["c", "i", "w"]); - cx.assert_all(WORD_LOCATIONS).await; - let mut cx = cx.consume().binding(["c", "i", "shift-w"]); - cx.assert_all(WORD_LOCATIONS).await; + async fn test_change_word_object(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.assert_binding_matches_all(["c", "i", "w"], WORD_LOCATIONS) + .await; + cx.assert_binding_matches_all(["c", "i", "shift-w"], WORD_LOCATIONS) + .await; + cx.assert_binding_matches_all(["c", "a", "w"], WORD_LOCATIONS) + .await; + cx.assert_binding_matches_all(["c", "a", "shift-w"], WORD_LOCATIONS) + .await; } #[gpui::test] - async fn test_delete_in_word(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new(cx) - .await - .binding(["d", "i", "w"]); - cx.assert_all(WORD_LOCATIONS).await; - let mut cx = cx.consume().binding(["d", "i", "shift-w"]); - cx.assert_all(WORD_LOCATIONS).await; + async fn test_delete_word_object(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.assert_binding_matches_all(["d", "i", "w"], WORD_LOCATIONS) + .await; + cx.assert_binding_matches_all(["d", "i", "shift-w"], WORD_LOCATIONS) + .await; + cx.assert_binding_matches_all(["d", "a", "w"], WORD_LOCATIONS) + .await; + cx.assert_binding_matches_all(["d", "a", "shift-w"], WORD_LOCATIONS) + .await; } #[gpui::test] - async fn test_change_around_word(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new(cx) - .await - .binding(["c", "a", "w"]); - cx.assert_all(WORD_LOCATIONS).await; - let mut cx = cx.consume().binding(["c", "a", "shift-w"]); - cx.assert_all(WORD_LOCATIONS).await; - } + async fn test_visual_word_object(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; - #[gpui::test] - async fn test_delete_around_word(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new(cx) - .await - .binding(["d", "a", "w"]); - cx.assert_all(WORD_LOCATIONS).await; - let mut cx = cx.consume().binding(["d", "a", "shift-w"]); - cx.assert_all(WORD_LOCATIONS).await; + cx.assert_binding_matches_all(["v", "i", "w"], WORD_LOCATIONS) + .await; + // Visual text objects are slightly broken when used with non empty selections + // cx.assert_binding_matches_all(["v", "h", "i", "w"], WORD_LOCATIONS) + // .await; + // cx.assert_binding_matches_all(["v", "l", "i", "w"], WORD_LOCATIONS) + // .await; + cx.assert_binding_matches_all(["v", "i", "shift-w"], WORD_LOCATIONS) + .await; + + // Visual text objects are slightly broken when used with non empty selections + // cx.assert_binding_matches_all(["v", "i", "h", "shift-w"], WORD_LOCATIONS) + // .await; + // cx.assert_binding_matches_all(["v", "i", "l", "shift-w"], WORD_LOCATIONS) + // .await; + + // Visual around words is somewhat broken right now when it comes to newlines + // cx.assert_binding_matches_all(["v", "a", "w"], WORD_LOCATIONS) + // .await; + // cx.assert_binding_matches_all(["v", "a", "shift-w"], WORD_LOCATIONS) + // .await; } const SENTENCE_EXAMPLES: &[&'static str] = &[ @@ -390,32 +405,35 @@ mod test { ]; #[gpui::test] - async fn test_change_in_sentence(cx: &mut gpui::TestAppContext) { + async fn test_change_sentence_object(cx: &mut gpui::TestAppContext) { let mut cx = NeovimBackedTestContext::new(cx) .await .binding(["c", "i", "s"]); for sentence_example in SENTENCE_EXAMPLES { cx.assert_all(sentence_example).await; } + + let mut cx = cx.binding(["c", "a", "s"]); + // Resulting position is slightly incorrect for unintuitive reasons. + cx.add_initial_state_exemption("The quick brown?ˇ Fox Jumps! Over the lazy."); + // Changing around the sentence at the end of the line doesn't remove whitespace.' + cx.add_initial_state_exemption("The quick brown.)]\'\" Brown fox jumps.ˇ "); + + for sentence_example in SENTENCE_EXAMPLES { + cx.assert_all(sentence_example).await; + } } #[gpui::test] - async fn test_delete_in_sentence(cx: &mut gpui::TestAppContext) { + async fn test_delete_sentence_object(cx: &mut gpui::TestAppContext) { let mut cx = NeovimBackedTestContext::new(cx) .await .binding(["d", "i", "s"]); - for sentence_example in SENTENCE_EXAMPLES { cx.assert_all(sentence_example).await; } - } - - #[gpui::test] - async fn test_change_around_sentence(cx: &mut gpui::TestAppContext) { - let mut cx = NeovimBackedTestContext::new(cx) - .await - .binding(["c", "a", "s"]); + let mut cx = cx.binding(["d", "a", "s"]); // Resulting position is slightly incorrect for unintuitive reasons. cx.add_initial_state_exemption("The quick brown?ˇ Fox Jumps! Over the lazy."); // Changing around the sentence at the end of the line doesn't remove whitespace.' @@ -427,18 +445,18 @@ mod test { } #[gpui::test] - async fn test_delete_around_sentence(cx: &mut gpui::TestAppContext) { + async fn test_visual_sentence_object(cx: &mut gpui::TestAppContext) { let mut cx = NeovimBackedTestContext::new(cx) .await - .binding(["d", "a", "s"]); - - // Resulting position is slightly incorrect for unintuitive reasons. - cx.add_initial_state_exemption("The quick brown?ˇ Fox Jumps! Over the lazy."); - // Changing around the sentence at the end of the line doesn't remove whitespace.' - cx.add_initial_state_exemption("The quick brown.)]\'\" Brown fox jumps.ˇ "); - + .binding(["v", "i", "s"]); for sentence_example in SENTENCE_EXAMPLES { cx.assert_all(sentence_example).await; } + + // Visual around sentences is somewhat broken right now when it comes to newlines + // let mut cx = cx.binding(["d", "a", "s"]); + // for sentence_example in SENTENCE_EXAMPLES { + // cx.assert_all(sentence_example).await; + // } } } diff --git a/crates/vim/src/test.rs b/crates/vim/src/test.rs index 63bb7996bf..e320962cfa 100644 --- a/crates/vim/src/test.rs +++ b/crates/vim/src/test.rs @@ -26,6 +26,7 @@ async fn test_neovim(cx: &mut gpui::TestAppContext) { let mut cx = NeovimBackedTestContext::new(cx).await; cx.simulate_shared_keystroke("i").await; + cx.assert_state_matches().await; cx.simulate_shared_keystrokes([ "shift-T", "e", "s", "t", " ", "t", "e", "s", "t", "escape", "0", "d", "w", ]) diff --git a/crates/vim/src/visual.rs b/crates/vim/src/visual.rs index 63d914d570..4fa195a2fa 100644 --- a/crates/vim/src/visual.rs +++ b/crates/vim/src/visual.rs @@ -6,7 +6,13 @@ use gpui::{actions, MutableAppContext, ViewContext}; use language::{AutoindentMode, SelectionGoal}; use workspace::Workspace; -use crate::{motion::Motion, object::Object, state::Mode, utils::copy_selections_content, Vim}; +use crate::{ + motion::Motion, + object::Object, + state::{Mode, Operator}, + utils::copy_selections_content, + Vim, +}; actions!(vim, [VisualDelete, VisualChange, VisualYank, VisualPaste]); @@ -47,7 +53,34 @@ pub fn visual_motion(motion: Motion, times: usize, cx: &mut MutableAppContext) { }); } -pub fn visual_object(_object: Object, _cx: &mut MutableAppContext) {} +pub fn visual_object(object: Object, cx: &mut MutableAppContext) { + Vim::update(cx, |vim, cx| { + if let Operator::Object { around } = vim.pop_operator(cx) { + vim.update_active_editor(cx, |editor, cx| { + editor.change_selections(Some(Autoscroll::Fit), cx, |s| { + s.move_with(|map, selection| { + let head = selection.head(); + let mut range = object.range(map, head, around); + if !range.is_empty() { + if let Some((_, end)) = map.reverse_chars_at(range.end).next() { + range.end = end; + } + + if selection.is_empty() { + selection.start = range.start; + selection.end = range.end; + } else if selection.reversed { + selection.start = range.start; + } else { + selection.end = range.end; + } + } + }) + }); + }); + } + }); +} pub fn change(_: &mut Workspace, _: &VisualChange, cx: &mut ViewContext) { Vim::update(cx, |vim, cx| { diff --git a/crates/vim/test_data/test_change_around_sentence.json b/crates/vim/test_data/test_change_around_sentence.json deleted file mode 100644 index 187e766f11..0000000000 --- a/crates/vim/test_data/test_change_around_sentence.json +++ /dev/null @@ -1 +0,0 @@ -[{"Text":"Fox Jumps! Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Fox Jumps! Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Fox Jumps! Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Insert"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Insert"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Insert"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Insert"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Mode":"Insert"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Mode":"Insert"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Mode":"Insert"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" "},{"Mode":"Insert"},{"Selection":{"start":[0,21],"end":[0,21]}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" "},{"Mode":"Insert"},{"Selection":{"start":[0,21],"end":[0,21]}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_change_around_word.json b/crates/vim/test_data/test_change_around_word.json deleted file mode 100644 index 3463c5c6d6..0000000000 --- a/crates/vim/test_data/test_change_around_word.json +++ /dev/null @@ -1 +0,0 @@ -[{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,15],"end":[0,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,9],"end":[1,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThequick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,3],"end":[6,3]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,10],"end":[6,10]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,15],"end":[6,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,6],"end":[9,6]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[10,12],"end":[10,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n"},{"Mode":"Insert"},{"Selection":{"start":[11,0],"end":[11,0]}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,15],"end":[0,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,9],"end":[1,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,10],"end":[6,10]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,15],"end":[6,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,2],"end":[9,2]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[10,12],"end":[10,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n"},{"Mode":"Insert"},{"Selection":{"start":[11,0],"end":[11,0]}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_sentence.json b/crates/vim/test_data/test_change_sentence_object.json similarity index 51% rename from crates/vim/test_data/test_change_in_sentence.json rename to crates/vim/test_data/test_change_sentence_object.json index 5058d5b569..7827bc8a28 100644 --- a/crates/vim/test_data/test_change_in_sentence.json +++ b/crates/vim/test_data/test_change_sentence_object.json @@ -1 +1 @@ -[{"Text":" Fox Jumps! Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" Fox Jumps! Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" Fox Jumps! Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown?Fox Jumps! Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,16],"end":[0,16]}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps! "},{"Mode":"Insert"},{"Selection":{"start":[0,28],"end":[0,28]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps! "},{"Mode":"Insert"},{"Selection":{"start":[0,28],"end":[0,28]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps! "},{"Mode":"Insert"},{"Selection":{"start":[0,28],"end":[0,28]}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. \n"},{"Mode":"Insert"},{"Selection":{"start":[2,14],"end":[2,14]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. \n"},{"Mode":"Insert"},{"Selection":{"start":[2,14],"end":[2,14]}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" "},{"Mode":"Insert"},{"Selection":{"start":[0,21],"end":[0,21]}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" "},{"Mode":"Insert"},{"Selection":{"start":[0,21],"end":[0,21]}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" Brown fox jumps."},{"Mode":"Insert"},{"Selection":{"start":[0,37],"end":[0,37]}},{"Mode":"Insert"}] \ No newline at end of file +[{"Text":" Fox Jumps! Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" Fox Jumps! Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" Fox Jumps! Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown?Fox Jumps! Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,16],"end":[0,16]}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps! "},{"Mode":"Insert"},{"Selection":{"start":[0,28],"end":[0,28]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps! "},{"Mode":"Insert"},{"Selection":{"start":[0,28],"end":[0,28]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps! "},{"Mode":"Insert"},{"Selection":{"start":[0,28],"end":[0,28]}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. \n"},{"Mode":"Insert"},{"Selection":{"start":[2,14],"end":[2,14]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. \n"},{"Mode":"Insert"},{"Selection":{"start":[2,14],"end":[2,14]}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":" Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" "},{"Mode":"Insert"},{"Selection":{"start":[0,21],"end":[0,21]}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" "},{"Mode":"Insert"},{"Selection":{"start":[0,21],"end":[0,21]}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" Brown fox jumps."},{"Mode":"Insert"},{"Selection":{"start":[0,37],"end":[0,37]}},{"Mode":"Insert"},{"Text":"Fox Jumps! Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Fox Jumps! Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Fox Jumps! Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Insert"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Insert"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Insert"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Insert"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Insert"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Insert"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Insert"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Mode":"Insert"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Mode":"Insert"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Mode":"Insert"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"Brown fox jumps. "},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" "},{"Mode":"Insert"},{"Selection":{"start":[0,21],"end":[0,21]}},{"Mode":"Insert"},{"Text":"The quick brown.)]'\" "},{"Mode":"Insert"},{"Selection":{"start":[0,21],"end":[0,21]}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_change_in_word.json b/crates/vim/test_data/test_change_word_object.json similarity index 50% rename from crates/vim/test_data/test_change_in_word.json rename to crates/vim/test_data/test_change_word_object.json index ef65a09291..e96efcaa7c 100644 --- a/crates/vim/test_data/test_change_in_word.json +++ b/crates/vim/test_data/test_change_word_object.json @@ -1 +1 @@ -[{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown\nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,15],"end":[0,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumpsover\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,9],"end":[1,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThequick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,3],"end":[6,3]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe- brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe- brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quickbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,10],"end":[6,10]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown\n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,15],"end":[6,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n\n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n\n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \nfox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox- over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,6],"end":[9,6]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n\n"},{"Mode":"Insert"},{"Selection":{"start":[10,12],"end":[10,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[11,0],"end":[11,0]}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown\nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,15],"end":[0,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumpsover\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,9],"end":[1,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quickbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,10],"end":[6,10]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown\n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,15],"end":[6,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n\n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n\n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \nfox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,2],"end":[9,2]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n\n"},{"Mode":"Insert"},{"Selection":{"start":[10,12],"end":[10,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[11,0],"end":[11,0]}},{"Mode":"Insert"}] \ No newline at end of file +[{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown\nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,15],"end":[0,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumpsover\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,9],"end":[1,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThequick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,3],"end":[6,3]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe- brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe- brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quickbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,10],"end":[6,10]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown\n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,15],"end":[6,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n\n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n\n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \nfox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox- over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,6],"end":[9,6]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n\n"},{"Mode":"Insert"},{"Selection":{"start":[10,12],"end":[10,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[11,0],"end":[11,0]}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown\nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,15],"end":[0,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumpsover\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,9],"end":[1,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quickbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,10],"end":[6,10]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown\n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,15],"end":[6,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n\n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n\n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \nfox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,2],"end":[9,2]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n\n"},{"Mode":"Insert"},{"Selection":{"start":[10,12],"end":[10,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[11,0],"end":[11,0]}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,15],"end":[0,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,9],"end":[1,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThequick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,3],"end":[6,3]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,10],"end":[6,10]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,15],"end":[6,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,6],"end":[9,6]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[10,12],"end":[10,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n"},{"Mode":"Insert"},{"Selection":{"start":[11,0],"end":[11,0]}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"The quick brown jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[0,15],"end":[0,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[1,9],"end":[1,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,10],"end":[6,10]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[6,15],"end":[6,15]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Mode":"Insert"},{"Selection":{"start":[9,2],"end":[9,2]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n"},{"Mode":"Insert"},{"Selection":{"start":[10,12],"end":[10,12]}},{"Mode":"Insert"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n"},{"Mode":"Insert"},{"Selection":{"start":[11,0],"end":[11,0]}},{"Mode":"Insert"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_sentence.json b/crates/vim/test_data/test_delete_around_sentence.json deleted file mode 100644 index b18c6ffa76..0000000000 --- a/crates/vim/test_data/test_delete_around_sentence.json +++ /dev/null @@ -1 +0,0 @@ -[{"Text":"Fox Jumps! Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Fox Jumps! Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Fox Jumps! Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Normal"},{"Selection":{"start":[0,26],"end":[0,26]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Normal"},{"Selection":{"start":[0,26],"end":[0,26]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Normal"},{"Selection":{"start":[0,26],"end":[0,26]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Normal"},{"Selection":{"start":[0,26],"end":[0,26]}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Mode":"Normal"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Mode":"Normal"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Mode":"Normal"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" "},{"Mode":"Normal"},{"Selection":{"start":[0,20],"end":[0,20]}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" "},{"Mode":"Normal"},{"Selection":{"start":[0,20],"end":[0,20]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_around_word.json b/crates/vim/test_data/test_delete_around_word.json deleted file mode 100644 index aec3fdd39a..0000000000 --- a/crates/vim/test_data/test_delete_around_word.json +++ /dev/null @@ -1 +0,0 @@ -[{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Normal"},{"Text":"The quick brown jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,15],"end":[0,15]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,8],"end":[1,8]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThequick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,3],"end":[6,3]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,15],"end":[6,15]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,6],"end":[9,6]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[10,11],"end":[10,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog "},{"Mode":"Normal"},{"Selection":{"start":[10,0],"end":[10,0]}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Normal"},{"Text":"The quick brown jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,15],"end":[0,15]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,8],"end":[1,8]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,15],"end":[6,15]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,2],"end":[9,2]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[10,11],"end":[10,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog "},{"Mode":"Normal"},{"Selection":{"start":[10,0],"end":[10,0]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_sentence.json b/crates/vim/test_data/test_delete_sentence_object.json similarity index 51% rename from crates/vim/test_data/test_delete_in_sentence.json rename to crates/vim/test_data/test_delete_sentence_object.json index 31891d19cb..1dfae9eca4 100644 --- a/crates/vim/test_data/test_delete_in_sentence.json +++ b/crates/vim/test_data/test_delete_sentence_object.json @@ -1 +1 @@ -[{"Text":" Fox Jumps! Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" Fox Jumps! Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" Fox Jumps! Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick brown?Fox Jumps! Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,16],"end":[0,16]}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps! "},{"Mode":"Normal"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps! "},{"Mode":"Normal"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps! "},{"Mode":"Normal"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. \n"},{"Mode":"Normal"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. \n"},{"Mode":"Normal"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" "},{"Mode":"Normal"},{"Selection":{"start":[0,21],"end":[0,21]}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" "},{"Mode":"Normal"},{"Selection":{"start":[0,21],"end":[0,21]}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" Brown fox jumps."},{"Mode":"Normal"},{"Selection":{"start":[0,36],"end":[0,36]}},{"Mode":"Normal"}] \ No newline at end of file +[{"Text":" Fox Jumps! Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" Fox Jumps! Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" Fox Jumps! Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick brown?Fox Jumps! Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,16],"end":[0,16]}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps! "},{"Mode":"Normal"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps! "},{"Mode":"Normal"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps! "},{"Mode":"Normal"},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. \n"},{"Mode":"Normal"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. \n"},{"Mode":"Normal"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":" Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" "},{"Mode":"Normal"},{"Selection":{"start":[0,21],"end":[0,21]}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" "},{"Mode":"Normal"},{"Selection":{"start":[0,21],"end":[0,21]}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" Brown fox jumps."},{"Mode":"Normal"},{"Selection":{"start":[0,36],"end":[0,36]}},{"Mode":"Normal"},{"Text":"Fox Jumps! Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Fox Jumps! Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Fox Jumps! Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Normal"},{"Text":"The quick brown? Over the lazy."},{"Mode":"Normal"},{"Selection":{"start":[0,17],"end":[0,17]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Normal"},{"Selection":{"start":[0,26],"end":[0,26]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Normal"},{"Selection":{"start":[0,26],"end":[0,26]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Normal"},{"Selection":{"start":[0,26],"end":[0,26]}},{"Mode":"Normal"},{"Text":"The quick brown? Fox Jumps!"},{"Mode":"Normal"},{"Selection":{"start":[0,26],"end":[0,26]}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick \nbrown fox jumps over\n"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Mode":"Normal"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Mode":"Normal"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog.\n"},{"Mode":"Normal"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Brown fox jumps. "},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" "},{"Mode":"Normal"},{"Selection":{"start":[0,20],"end":[0,20]}},{"Mode":"Normal"},{"Text":"The quick brown.)]'\" "},{"Mode":"Normal"},{"Selection":{"start":[0,20],"end":[0,20]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_delete_in_word.json b/crates/vim/test_data/test_delete_word_object.json similarity index 50% rename from crates/vim/test_data/test_delete_in_word.json rename to crates/vim/test_data/test_delete_word_object.json index c15c6ce208..7cd6ffbafc 100644 --- a/crates/vim/test_data/test_delete_in_word.json +++ b/crates/vim/test_data/test_delete_word_object.json @@ -1 +1 @@ -[{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,14],"end":[0,14]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumpsover\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,9],"end":[1,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThequick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,3],"end":[6,3]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe- brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe- brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quickbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,10],"end":[6,10]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown\n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,14],"end":[6,14]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n\n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n\n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \nfox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox- over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,6],"end":[9,6]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n\n"},{"Mode":"Normal"},{"Selection":{"start":[10,11],"end":[10,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[11,0],"end":[11,0]}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,14],"end":[0,14]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumpsover\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,9],"end":[1,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quickbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,10],"end":[6,10]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown\n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,14],"end":[6,14]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n\n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n\n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \nfox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,2],"end":[9,2]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n\n"},{"Mode":"Normal"},{"Selection":{"start":[10,11],"end":[10,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[11,0],"end":[11,0]}},{"Mode":"Normal"}] \ No newline at end of file +[{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,14],"end":[0,14]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumpsover\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,9],"end":[1,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThequick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,3],"end":[6,3]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe- brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe- brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quickbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,10],"end":[6,10]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown\n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,14],"end":[6,14]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n\n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n\n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \nfox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox- over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,6],"end":[9,6]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n\n"},{"Mode":"Normal"},{"Selection":{"start":[10,11],"end":[10,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[11,0],"end":[11,0]}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Normal"},{"Text":"The quick brown\nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,14],"end":[0,14]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumpsover\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,9],"end":[1,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quickbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,10],"end":[6,10]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown\n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,14],"end":[6,14]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n\n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n\n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \nfox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,2],"end":[9,2]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n\n"},{"Mode":"Normal"},{"Selection":{"start":[10,11],"end":[10,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[11,0],"end":[11,0]}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Normal"},{"Text":"The quick brown jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,15],"end":[0,15]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,8],"end":[1,8]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\n-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThequick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,3],"end":[6,3]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,4],"end":[6,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,15],"end":[6,15]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,6],"end":[9,6]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[10,11],"end":[10,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog "},{"Mode":"Normal"},{"Selection":{"start":[10,0],"end":[10,0]}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Normal"},{"Text":"The quick \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,9],"end":[0,9]}},{"Mode":"Normal"},{"Text":"The quick brown jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[0,15],"end":[0,15]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,4],"end":[1,4]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[1,8],"end":[1,8]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nbrown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,0],"end":[6,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[6,15],"end":[6,15]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[7,0],"end":[7,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[8,0],"end":[8,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,0],"end":[9,0]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n over\nthe lazy dog \n\n"},{"Mode":"Normal"},{"Selection":{"start":[9,2],"end":[9,2]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog\n"},{"Mode":"Normal"},{"Selection":{"start":[10,11],"end":[10,11]}},{"Mode":"Normal"},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog "},{"Mode":"Normal"},{"Selection":{"start":[10,0],"end":[10,0]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_neovim.json b/crates/vim/test_data/test_neovim.json index eba763f676..244e909ba4 100644 --- a/crates/vim/test_data/test_neovim.json +++ b/crates/vim/test_data/test_neovim.json @@ -1 +1 @@ -[{"Text":"test"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"}] \ No newline at end of file +[{"Text":""},{"Mode":"Insert"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Insert"},{"Text":"test"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"}] \ No newline at end of file diff --git a/crates/vim/test_data/test_visual_sentence_object.json b/crates/vim/test_data/test_visual_sentence_object.json new file mode 100644 index 0000000000..c88f314333 --- /dev/null +++ b/crates/vim/test_data/test_visual_sentence_object.json @@ -0,0 +1 @@ +[{"Text":"The quick brown? Fox Jumps! Over the lazy."},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,0],"end":[0,15]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown? Fox Jumps! Over the lazy."},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,0],"end":[0,15]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown? Fox Jumps! Over the lazy."},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,0],"end":[0,15]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown? Fox Jumps! Over the lazy."},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,16],"end":[0,16]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown? Fox Jumps! Over the lazy."},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,17],"end":[0,26]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown? Fox Jumps! Over the lazy."},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,17],"end":[0,26]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown? Fox Jumps! Over the lazy."},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,17],"end":[0,26]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown? Fox Jumps! Over the lazy."},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,27],"end":[0,27]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown? Fox Jumps! Over the lazy."},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,28],"end":[0,41]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown? Fox Jumps! Over the lazy."},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,28],"end":[0,41]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown? Fox Jumps! Over the lazy."},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,28],"end":[0,41]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. The quick \nbrown fox jumps over\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,0],"end":[2,12]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. The quick \nbrown fox jumps over\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,0],"end":[2,12]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. The quick \nbrown fox jumps over\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,0],"end":[2,12]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. The quick \nbrown fox jumps over\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,0],"end":[2,12]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. The quick \nbrown fox jumps over\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,0],"end":[2,12]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. The quick \nbrown fox jumps over\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. The quick \nbrown fox jumps over\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[2,14],"end":[3,19]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog. The quick \nbrown fox jumps over\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[2,14],"end":[3,19]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown.)]'\" Brown fox jumps. "},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,0],"end":[0,19]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown.)]'\" Brown fox jumps. "},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,0],"end":[0,19]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown.)]'\" Brown fox jumps. "},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,0],"end":[0,19]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown.)]'\" Brown fox jumps. "},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,0],"end":[0,19]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown.)]'\" Brown fox jumps. "},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,0],"end":[0,19]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown.)]'\" Brown fox jumps. "},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,0],"end":[0,19]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown.)]'\" Brown fox jumps. "},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,21],"end":[0,36]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown.)]'\" Brown fox jumps. "},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,21],"end":[0,36]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown.)]'\" Brown fox jumps. "},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,37],"end":[0,37]}},{"Mode":{"Visual":{"line":false}}}] \ No newline at end of file diff --git a/crates/vim/test_data/test_visual_word_object.json b/crates/vim/test_data/test_visual_word_object.json new file mode 100644 index 0000000000..751636a5a3 --- /dev/null +++ b/crates/vim/test_data/test_visual_word_object.json @@ -0,0 +1 @@ +[{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,10],"end":[0,14]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,10],"end":[0,14]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,15],"end":[0,17]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[1,4],"end":[1,8]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[1,4],"end":[1,8]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[1,9],"end":[1,9]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[2,12],"end":[2,13]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[6,0],"end":[6,2]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[6,3],"end":[6,3]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[6,4],"end":[6,8]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[6,4],"end":[6,8]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[6,10],"end":[6,14]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[6,15],"end":[6,15]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[7,0],"end":[7,1]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[8,0],"end":[8,1]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[9,0],"end":[9,1]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[9,6],"end":[9,10]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[10,12],"end":[10,12]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[11,0],"end":[11,0]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,10],"end":[0,14]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,10],"end":[0,14]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[0,15],"end":[0,17]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[1,4],"end":[1,8]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[1,4],"end":[1,8]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[1,9],"end":[1,9]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[2,12],"end":[2,13]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[3,0],"end":[3,0]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[4,0],"end":[4,0]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[5,0],"end":[5,0]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[6,0],"end":[6,8]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[6,0],"end":[6,8]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[6,0],"end":[6,8]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[6,0],"end":[6,8]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[6,9],"end":[6,9]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[6,10],"end":[6,14]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[6,15],"end":[6,15]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[7,0],"end":[7,1]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[8,0],"end":[8,1]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[9,0],"end":[9,1]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[9,2],"end":[9,10]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[10,12],"end":[10,12]}},{"Mode":{"Visual":{"line":false}}},{"Text":"The quick brown \nfox jumps over\nthe lazy dog \n\n\n\nThe-quick brown \n \n \n fox-jumps over\nthe lazy dog \n\n"},{"Mode":{"Visual":{"line":false}}},{"Selection":{"start":[11,0],"end":[11,0]}},{"Mode":{"Visual":{"line":false}}}] \ No newline at end of file From 0d31ea7cf2cb62e10131b697d1d42a6389384d06 Mon Sep 17 00:00:00 2001 From: K Simmons Date: Mon, 10 Oct 2022 15:34:40 -0700 Subject: [PATCH 224/314] fix minor issue where undo is not available in visual mode --- assets/keymaps/vim.json | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index 75d0eae5ff..1c2f8ac24d 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -204,6 +204,7 @@ { "context": "Editor && vim_mode == visual", "bindings": { + "u": "editor::Undo", "c": "vim::VisualChange", "d": "vim::VisualDelete", "x": "vim::VisualDelete", From eedcc585afd921779c3ccc1aef24fc258647c0b9 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 10 Oct 2022 16:12:42 -0700 Subject: [PATCH 225/314] Add scrollbars to editors --- crates/editor/src/element.rs | 118 ++++++++++++++++++++++++++++++--- crates/theme/src/theme.rs | 8 +++ styles/src/styleTree/editor.ts | 14 ++++ 3 files changed, 130 insertions(+), 10 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index acf2e5887c..a15f5d0013 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -43,7 +43,7 @@ use std::{ cmp::{self, Ordering}, fmt::Write, iter, - ops::Range, + ops::{DerefMut, Range}, sync::Arc, }; use theme::DiffStyle; @@ -909,6 +909,96 @@ impl EditorElement { cx.scene.pop_layer(); } + fn paint_scrollbar(&mut self, bounds: RectF, layout: &mut LayoutState, cx: &mut PaintContext) { + enum ScrollbarMouseHandlers {} + let row_range = if let Some(row_range) = &layout.scrollbar_row_range { + row_range + } else { + return; + }; + + let style = &self.style.theme.scrollbar; + let top = bounds.min_y(); + let bottom = bounds.max_y(); + let height = bounds.height(); + + let max_row = layout.max_row + row_range.len() as u32; + let scrollbar_start = row_range.start as f32 / max_row as f32; + let scrollbar_end = row_range.end as f32 / max_row as f32; + + let thumb_top = top + scrollbar_start * height; + let thumb_bottom = top + scrollbar_end * height; + let right = bounds.max_x(); + let left = right - style.width; + let track_bounds = RectF::from_points(vec2f(left, top), vec2f(right, bottom)); + let thumb_bounds = RectF::from_points(vec2f(left, thumb_top), vec2f(right, thumb_bottom)); + + cx.scene.push_quad(Quad { + bounds: track_bounds, + border: style.track.border, + background: style.track.background_color, + ..Default::default() + }); + cx.scene.push_quad(Quad { + bounds: thumb_bounds, + border: style.thumb.border, + background: style.thumb.background_color, + corner_radius: style.thumb.corner_radius, + }); + cx.scene.push_cursor_region(CursorRegion { + bounds: track_bounds, + style: CursorStyle::Arrow, + }); + + let view = self.view.clone(); + cx.scene.push_mouse_region( + MouseRegion::new::( + self.view.id(), + self.view.id(), + track_bounds, + ) + .on_down(MouseButton::Left, { + let view = view.clone(); + let row_range_len = row_range.len(); + move |e, cx| { + let y = e.position.y(); + if y < thumb_top || thumb_bottom < y { + if let Some(view) = view.upgrade(cx.deref_mut()) { + view.update(cx.deref_mut(), |view, cx| { + let center_row = + ((y - top) * max_row as f32 / height).round() as u32; + let top_row = center_row.saturating_sub(row_range_len as u32 / 2); + let mut position = view.scroll_position(cx); + position.set_y(top_row as f32); + view.set_scroll_position(position, cx); + }); + } + } + } + }) + .on_drag(MouseButton::Left, { + let view = view.clone(); + move |e, cx| { + let y = e.prev_mouse_position.y(); + let new_y = e.position.y(); + if thumb_top < y && y < thumb_bottom { + if let Some(view) = view.upgrade(cx.deref_mut()) { + view.update(cx.deref_mut(), |view, cx| { + let mut position = view.scroll_position(cx); + position + .set_y(position.y() + (new_y - y) * (max_row as f32) / height); + if position.y() < 0.0 { + position.set_y(0.); + } + view.set_scroll_position(position, cx); + }); + } + } + } + }), + ); + } + #[allow(clippy::too_many_arguments)] fn paint_highlighted_range( &self, @@ -1467,13 +1557,11 @@ impl Element for EditorElement { // The scroll position is a fractional point, the whole number of which represents // the top of the window in terms of display rows. let start_row = scroll_position.y() as u32; - let scroll_top = scroll_position.y() * line_height; + let visible_row_count = (size.y() / line_height).ceil() as u32; + let max_row = snapshot.max_point().row(); // Add 1 to ensure selections bleed off screen - let end_row = 1 + cmp::min( - ((scroll_top + size.y()) / line_height).ceil() as u32, - snapshot.max_point().row(), - ); + let end_row = 1 + cmp::min(start_row + visible_row_count, max_row); let start_anchor = if start_row == 0 { Anchor::min() @@ -1482,7 +1570,7 @@ impl Element for EditorElement { .buffer_snapshot .anchor_before(DisplayPoint::new(start_row, 0).to_offset(&snapshot, Bias::Left)) }; - let end_anchor = if end_row > snapshot.max_point().row() { + let end_anchor = if end_row > max_row { Anchor::max() } else { snapshot @@ -1564,6 +1652,12 @@ impl Element for EditorElement { .git_diff_hunks_in_range(start_row..end_row) .collect(); + let scrollbar_row_range = if snapshot.mode == EditorMode::Full { + Some(start_row..(start_row + visible_row_count)) + } else { + None + }; + let mut max_visible_line_width = 0.0; let line_layouts = self.layout_lines(start_row..end_row, &snapshot, cx); for line in &line_layouts { @@ -1597,7 +1691,6 @@ impl Element for EditorElement { cx, ); - let max_row = snapshot.max_point().row(); let scroll_max = vec2f( ((scroll_width - text_size.x()) / em_width).max(0.0), max_row.saturating_sub(1) as f32, @@ -1707,6 +1800,8 @@ impl Element for EditorElement { gutter_size, gutter_padding, text_size, + scrollbar_row_range, + max_row, gutter_margin, active_rows, highlighted_rows, @@ -1753,11 +1848,12 @@ impl Element for EditorElement { } self.paint_text(text_bounds, visible_bounds, layout, cx); + cx.scene.push_layer(Some(bounds)); if !layout.blocks.is_empty() { - cx.scene.push_layer(Some(bounds)); self.paint_blocks(bounds, visible_bounds, layout, cx); - cx.scene.pop_layer(); } + self.paint_scrollbar(bounds, layout, cx); + cx.scene.pop_layer(); cx.scene.pop_layer(); } @@ -1849,6 +1945,8 @@ pub struct LayoutState { blocks: Vec, highlighted_ranges: Vec<(Range, Color)>, selections: Vec<(ReplicaId, Vec)>, + scrollbar_row_range: Option>, + max_row: u32, context_menu: Option<(DisplayPoint, ElementBox)>, diff_hunks: Vec>, code_actions_indicator: Option<(u32, ElementBox)>, diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index d8c8296481..7bd7913644 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -510,6 +510,14 @@ pub struct Editor { pub link_definition: HighlightStyle, pub composition_mark: HighlightStyle, pub jump_icon: Interactive, + pub scrollbar: Scrollbar, +} + +#[derive(Clone, Deserialize, Default)] +pub struct Scrollbar { + pub track: ContainerStyle, + pub thumb: ContainerStyle, + pub width: f32, } #[derive(Clone, Deserialize, Default)] diff --git a/styles/src/styleTree/editor.ts b/styles/src/styleTree/editor.ts index 04a5bafbd5..610f828ec8 100644 --- a/styles/src/styleTree/editor.ts +++ b/styles/src/styleTree/editor.ts @@ -1,4 +1,5 @@ import Theme from "../themes/common/theme"; +import { withOpacity } from "../utils/color"; import { backgroundColor, border, @@ -170,6 +171,19 @@ export default function editor(theme: Theme) { background: backgroundColor(theme, "on500"), }, }, + scrollbar: { + width: 12, + track: { + border: { + left: true, + width: 1, + color: borderColor(theme, "secondary"), + }, + }, + thumb: { + background: borderColor(theme, "secondary"), + } + }, compositionMark: { underline: { thickness: 1.0, From b8c2acf0f2b87ee49c5f2a3e4404fb3aa62d1644 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 10 Oct 2022 17:56:03 -0600 Subject: [PATCH 226/314] Show worktree root names when sharing additional projects on a call Co-Authored-By: Antonio Scandurra --- crates/call/src/participant.rs | 2 +- crates/call/src/room.rs | 41 ++++++++---- crates/collab/src/integration_tests.rs | 2 + crates/collab/src/rpc.rs | 11 +++- crates/collab/src/rpc/store.rs | 64 +++++++++++++++---- .../src/project_shared_notification.rs | 49 ++++++++++++-- crates/rpc/proto/zed.proto | 8 ++- crates/theme/src/theme.rs | 3 + .../styleTree/projectSharedNotification.ts | 8 ++- 9 files changed, 153 insertions(+), 35 deletions(-) diff --git a/crates/call/src/participant.rs b/crates/call/src/participant.rs index db7a84e584..b124d920a3 100644 --- a/crates/call/src/participant.rs +++ b/crates/call/src/participant.rs @@ -23,6 +23,6 @@ impl ParticipantLocation { #[derive(Clone, Debug)] pub struct RemoteParticipant { pub user: Arc, - pub project_ids: Vec, + pub projects: Vec, pub location: ParticipantLocation, } diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 9cad1ff211..fb9ba09d81 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -13,7 +13,11 @@ use util::ResultExt; #[derive(Clone, Debug, PartialEq, Eq)] pub enum Event { - RemoteProjectShared { owner: Arc, project_id: u64 }, + RemoteProjectShared { + owner: Arc, + project_id: u64, + worktree_root_names: Vec, + }, } pub struct Room { @@ -219,16 +223,19 @@ impl Room { let peer_id = PeerId(participant.peer_id); this.participant_user_ids.insert(participant.user_id); - let existing_project_ids = this + let existing_projects = this .remote_participants .get(&peer_id) - .map(|existing| existing.project_ids.clone()) - .unwrap_or_default(); - for project_id in &participant.project_ids { - if !existing_project_ids.contains(project_id) { + .into_iter() + .flat_map(|existing| &existing.projects) + .map(|project| project.id) + .collect::>(); + for project in &participant.projects { + if !existing_projects.contains(&project.id) { cx.emit(Event::RemoteProjectShared { owner: user.clone(), - project_id: *project_id, + project_id: project.id, + worktree_root_names: project.worktree_root_names.clone(), }); } } @@ -237,7 +244,7 @@ impl Room { peer_id, RemoteParticipant { user: user.clone(), - project_ids: participant.project_ids, + projects: participant.projects, location: ParticipantLocation::from_proto(participant.location) .unwrap_or(ParticipantLocation::External), }, @@ -334,9 +341,21 @@ impl Room { return Task::ready(Ok(project_id)); } - let request = self - .client - .request(proto::ShareProject { room_id: self.id() }); + let request = self.client.request(proto::ShareProject { + room_id: self.id(), + worktrees: project + .read(cx) + .worktrees(cx) + .map(|worktree| { + let worktree = worktree.read(cx); + proto::WorktreeMetadata { + id: worktree.id().to_proto(), + root_name: worktree.root_name().into(), + visible: worktree.is_visible(), + } + }) + .collect(), + }); cx.spawn_weak(|_, mut cx| async move { let response = request.await?; project diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 87ae24bb3d..06e009d08b 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -819,6 +819,7 @@ async fn test_active_call_events( avatar: None, }), project_id: project_a_id, + worktree_root_names: vec!["a".to_string()], }] ); @@ -836,6 +837,7 @@ async fn test_active_call_events( avatar: None, }), project_id: project_b_id, + worktree_root_names: vec!["b".to_string()] }] ); assert_eq!(mem::take(&mut *events_b.borrow_mut()), vec![]); diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 05bcd6875d..11219fb8b8 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -827,7 +827,12 @@ impl Server { .user_id_for_connection(request.sender_id)?; let project_id = self.app_state.db.register_project(user_id).await?; let mut store = self.store().await; - let room = store.share_project(request.payload.room_id, project_id, request.sender_id)?; + let room = store.share_project( + request.payload.room_id, + project_id, + request.payload.worktrees, + request.sender_id, + )?; response.send(proto::ShareProjectResponse { project_id: project_id.to_proto(), })?; @@ -1036,11 +1041,13 @@ impl Server { let guest_connection_ids = state .read_project(project_id, request.sender_id)? .guest_connection_ids(); - state.update_project(project_id, &request.payload.worktrees, request.sender_id)?; + let room = + state.update_project(project_id, &request.payload.worktrees, request.sender_id)?; broadcast(request.sender_id, guest_connection_ids, |connection_id| { self.peer .forward_send(request.sender_id, connection_id, request.payload.clone()) }); + self.room_updated(room); }; Ok(()) diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index 5b23ac92d5..522438255a 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -383,7 +383,7 @@ impl Store { room.participants.push(proto::Participant { user_id: connection.user_id.to_proto(), peer_id: creator_connection_id.0, - project_ids: Default::default(), + projects: Default::default(), location: Some(proto::ParticipantLocation { variant: Some(proto::participant_location::Variant::External( proto::participant_location::External {}, @@ -441,7 +441,7 @@ impl Store { room.participants.push(proto::Participant { user_id: user_id.to_proto(), peer_id: connection_id.0, - project_ids: Default::default(), + projects: Default::default(), location: Some(proto::ParticipantLocation { variant: Some(proto::participant_location::Variant::External( proto::participant_location::External {}, @@ -689,7 +689,8 @@ impl Store { anyhow::ensure!( room.participants .iter() - .any(|participant| participant.project_ids.contains(&project.id)), + .flat_map(|participant| &participant.projects) + .any(|participant_project| participant_project.id == project.id), "no such project" ); } @@ -708,6 +709,7 @@ impl Store { &mut self, room_id: RoomId, project_id: ProjectId, + worktrees: Vec, host_connection_id: ConnectionId, ) -> Result<&proto::Room> { let connection = self @@ -724,7 +726,14 @@ impl Store { .iter_mut() .find(|participant| participant.peer_id == host_connection_id.0) .ok_or_else(|| anyhow!("no such room"))?; - participant.project_ids.push(project_id.to_proto()); + participant.projects.push(proto::ParticipantProject { + id: project_id.to_proto(), + worktree_root_names: worktrees + .iter() + .filter(|worktree| worktree.visible) + .map(|worktree| worktree.root_name.clone()) + .collect(), + }); connection.projects.insert(project_id); self.projects.insert( @@ -741,7 +750,19 @@ impl Store { }, guests: Default::default(), active_replica_ids: Default::default(), - worktrees: Default::default(), + worktrees: worktrees + .into_iter() + .map(|worktree| { + ( + worktree.id, + Worktree { + root_name: worktree.root_name, + visible: worktree.visible, + ..Default::default() + }, + ) + }) + .collect(), language_servers: Default::default(), }, ); @@ -779,8 +800,8 @@ impl Store { .find(|participant| participant.peer_id == connection_id.0) .ok_or_else(|| anyhow!("no such room"))?; participant - .project_ids - .retain(|id| *id != project_id.to_proto()); + .projects + .retain(|project| project.id != project_id.to_proto()); Ok((room, project)) } else { @@ -796,7 +817,7 @@ impl Store { project_id: ProjectId, worktrees: &[proto::WorktreeMetadata], connection_id: ConnectionId, - ) -> Result<()> { + ) -> Result<&proto::Room> { let project = self .projects .get_mut(&project_id) @@ -818,7 +839,23 @@ impl Store { } } - Ok(()) + let room = self + .rooms + .get_mut(&project.room_id) + .ok_or_else(|| anyhow!("no such room"))?; + let participant_project = room + .participants + .iter_mut() + .flat_map(|participant| &mut participant.projects) + .find(|project| project.id == project_id.to_proto()) + .ok_or_else(|| anyhow!("no such project"))?; + participant_project.worktree_root_names = worktrees + .iter() + .filter(|worktree| worktree.visible) + .map(|worktree| worktree.root_name.clone()) + .collect(); + + Ok(room) } else { Err(anyhow!("no such project"))? } @@ -1132,8 +1169,8 @@ impl Store { "room contains participant that has disconnected" ); - for project_id in &participant.project_ids { - let project = &self.projects[&ProjectId::from_proto(*project_id)]; + for participant_project in &participant.projects { + let project = &self.projects[&ProjectId::from_proto(participant_project.id)]; assert_eq!( project.room_id, *room_id, "project was shared on a different room" @@ -1173,8 +1210,9 @@ impl Store { .unwrap(); assert!( room_participant - .project_ids - .contains(&project_id.to_proto()), + .projects + .iter() + .any(|project| project.id == project_id.to_proto()), "project was not shared in room" ); } diff --git a/crates/collab_ui/src/project_shared_notification.rs b/crates/collab_ui/src/project_shared_notification.rs index 5597ea0a01..e5ff27060a 100644 --- a/crates/collab_ui/src/project_shared_notification.rs +++ b/crates/collab_ui/src/project_shared_notification.rs @@ -19,10 +19,16 @@ pub fn init(cx: &mut MutableAppContext) { let active_call = ActiveCall::global(cx); cx.subscribe(&active_call, move |_, event, cx| match event { - room::Event::RemoteProjectShared { owner, project_id } => { + room::Event::RemoteProjectShared { + owner, + project_id, + worktree_root_names, + } => { const PADDING: f32 = 16.; let screen_size = cx.platform().screen_size(); - let window_size = vec2f(366., 64.); + + let theme = &cx.global::().theme.project_shared_notification; + let window_size = vec2f(theme.window_width, theme.window_height); cx.add_window( WindowOptions { bounds: WindowBounds::Fixed(RectF::new( @@ -34,7 +40,13 @@ pub fn init(cx: &mut MutableAppContext) { kind: WindowKind::PopUp, is_movable: false, }, - |_| ProjectSharedNotification::new(*project_id, owner.clone()), + |_| { + ProjectSharedNotification::new( + owner.clone(), + *project_id, + worktree_root_names.clone(), + ) + }, ); } }) @@ -43,12 +55,17 @@ pub fn init(cx: &mut MutableAppContext) { pub struct ProjectSharedNotification { project_id: u64, + worktree_root_names: Vec, owner: Arc, } impl ProjectSharedNotification { - fn new(project_id: u64, owner: Arc) -> Self { - Self { project_id, owner } + fn new(owner: Arc, project_id: u64, worktree_root_names: Vec) -> Self { + Self { + project_id, + worktree_root_names, + owner, + } } fn join(&mut self, _: &JoinProject, cx: &mut ViewContext) { @@ -84,13 +101,33 @@ impl ProjectSharedNotification { ) .with_child( Label::new( - "has shared a project with you".into(), + format!( + "shared a project in Zed{}", + if self.worktree_root_names.is_empty() { + "" + } else { + ":" + } + ), theme.message.text.clone(), ) .contained() .with_style(theme.message.container) .boxed(), ) + .with_children(if self.worktree_root_names.is_empty() { + None + } else { + Some( + Label::new( + self.worktree_root_names.join(", "), + theme.worktree_roots.text.clone(), + ) + .contained() + .with_style(theme.worktree_roots.container) + .boxed(), + ) + }) .contained() .with_style(theme.owner_metadata) .aligned() diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index f9a9fbd34e..b1897653c9 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -163,10 +163,15 @@ message Room { message Participant { uint64 user_id = 1; uint32 peer_id = 2; - repeated uint64 project_ids = 3; + repeated ParticipantProject projects = 3; ParticipantLocation location = 4; } +message ParticipantProject { + uint64 id = 1; + repeated string worktree_root_names = 2; +} + message ParticipantLocation { oneof variant { Project project = 1; @@ -215,6 +220,7 @@ message RoomUpdated { message ShareProject { uint64 room_id = 1; + repeated WorktreeMetadata worktrees = 2; } message ShareProjectResponse { diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 2f19304829..50e2462c15 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -471,6 +471,8 @@ pub struct UpdateNotification { #[derive(Deserialize, Default)] pub struct ProjectSharedNotification { + pub window_height: f32, + pub window_width: f32, #[serde(default)] pub background: Color, pub owner_container: ContainerStyle, @@ -478,6 +480,7 @@ pub struct ProjectSharedNotification { pub owner_metadata: ContainerStyle, pub owner_username: ContainedText, pub message: ContainedText, + pub worktree_roots: ContainedText, pub button_width: f32, pub join_button: ContainedText, pub dismiss_button: ContainedText, diff --git a/styles/src/styleTree/projectSharedNotification.ts b/styles/src/styleTree/projectSharedNotification.ts index 7a70f0f0d3..abe77e7d56 100644 --- a/styles/src/styleTree/projectSharedNotification.ts +++ b/styles/src/styleTree/projectSharedNotification.ts @@ -2,8 +2,10 @@ import Theme from "../themes/common/theme"; import { backgroundColor, borderColor, text } from "./components"; export default function projectSharedNotification(theme: Theme): Object { - const avatarSize = 32; + const avatarSize = 48; return { + windowHeight: 72, + windowWidth: 360, background: backgroundColor(theme, 300), ownerContainer: { padding: 12, @@ -24,6 +26,10 @@ export default function projectSharedNotification(theme: Theme): Object { ...text(theme, "sans", "secondary", { size: "xs" }), margin: { top: -3 }, }, + worktreeRoots: { + ...text(theme, "sans", "secondary", { size: "xs", weight: "bold" }), + margin: { top: -3 }, + }, buttonWidth: 96, joinButton: { background: backgroundColor(theme, "info", "active"), From 6dcf63832213b9f3a64e87756cf23563e82656de Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 10 Oct 2022 17:06:48 -0700 Subject: [PATCH 227/314] Represent scrollbar range with f32s --- crates/editor/src/element.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index a15f5d0013..300edc02a6 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -922,7 +922,7 @@ impl EditorElement { let bottom = bounds.max_y(); let height = bounds.height(); - let max_row = layout.max_row + row_range.len() as u32; + let max_row = layout.max_row + ((row_range.end - row_range.start) as u32); let scrollbar_start = row_range.start as f32 / max_row as f32; let scrollbar_end = row_range.end as f32 / max_row as f32; @@ -959,7 +959,7 @@ impl EditorElement { ) .on_down(MouseButton::Left, { let view = view.clone(); - let row_range_len = row_range.len(); + let row_range_len = row_range.end - row_range.start; move |e, cx| { let y = e.position.y(); if y < thumb_top || thumb_bottom < y { @@ -1653,7 +1653,7 @@ impl Element for EditorElement { .collect(); let scrollbar_row_range = if snapshot.mode == EditorMode::Full { - Some(start_row..(start_row + visible_row_count)) + Some(scroll_position.y()..(scroll_position.y() + visible_row_count as f32)) } else { None }; @@ -1945,7 +1945,7 @@ pub struct LayoutState { blocks: Vec, highlighted_ranges: Vec<(Range, Color)>, selections: Vec<(ReplicaId, Vec)>, - scrollbar_row_range: Option>, + scrollbar_row_range: Option>, max_row: u32, context_menu: Option<(DisplayPoint, ElementBox)>, diff_hunks: Vec>, From e0b6b0df2aab8edd5502d7e7bc10df9ca4cbf3be Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 10 Oct 2022 18:12:00 -0600 Subject: [PATCH 228/314] Rename Join button to Open, rework message slightly --- crates/collab_ui/src/project_shared_notification.rs | 10 +++++----- crates/theme/src/theme.rs | 2 +- styles/src/styleTree/projectSharedNotification.ts | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/collab_ui/src/project_shared_notification.rs b/crates/collab_ui/src/project_shared_notification.rs index e5ff27060a..22383feb20 100644 --- a/crates/collab_ui/src/project_shared_notification.rs +++ b/crates/collab_ui/src/project_shared_notification.rs @@ -102,7 +102,7 @@ impl ProjectSharedNotification { .with_child( Label::new( format!( - "shared a project in Zed{}", + "is sharing a project in Zed{}", if self.worktree_root_names.is_empty() { "" } else { @@ -140,7 +140,7 @@ impl ProjectSharedNotification { } fn render_buttons(&self, cx: &mut RenderContext) -> ElementBox { - enum Join {} + enum Open {} enum Dismiss {} let project_id = self.project_id; @@ -148,12 +148,12 @@ impl ProjectSharedNotification { Flex::column() .with_child( - MouseEventHandler::::new(0, cx, |_, cx| { + MouseEventHandler::::new(0, cx, |_, cx| { let theme = &cx.global::().theme.project_shared_notification; - Label::new("Join".to_string(), theme.join_button.text.clone()) + Label::new("Open".to_string(), theme.open_button.text.clone()) .aligned() .contained() - .with_style(theme.join_button.container) + .with_style(theme.open_button.container) .boxed() }) .with_cursor_style(CursorStyle::PointingHand) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 50e2462c15..7f11ae6e03 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -482,7 +482,7 @@ pub struct ProjectSharedNotification { pub message: ContainedText, pub worktree_roots: ContainedText, pub button_width: f32, - pub join_button: ContainedText, + pub open_button: ContainedText, pub dismiss_button: ContainedText, } diff --git a/styles/src/styleTree/projectSharedNotification.ts b/styles/src/styleTree/projectSharedNotification.ts index abe77e7d56..f6ebf4781a 100644 --- a/styles/src/styleTree/projectSharedNotification.ts +++ b/styles/src/styleTree/projectSharedNotification.ts @@ -5,7 +5,7 @@ export default function projectSharedNotification(theme: Theme): Object { const avatarSize = 48; return { windowHeight: 72, - windowWidth: 360, + windowWidth: 380, background: backgroundColor(theme, 300), ownerContainer: { padding: 12, @@ -31,7 +31,7 @@ export default function projectSharedNotification(theme: Theme): Object { margin: { top: -3 }, }, buttonWidth: 96, - joinButton: { + openButton: { background: backgroundColor(theme, "info", "active"), border: { left: true, bottom: true, width: 1, color: borderColor(theme, "primary") }, ...text(theme, "sans", "info", { size: "xs", weight: "extra_bold" }) From 7b084199be8d81f5771a392e8f5836f33bfa0c89 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 10 Oct 2022 17:54:29 -0700 Subject: [PATCH 229/314] Auto-hide scrollbars --- crates/editor/src/editor.rs | 28 ++++++++ crates/editor/src/element.rs | 130 ++++++++++++++++++++--------------- 2 files changed, 101 insertions(+), 57 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 1abe65d482..3422e599f8 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -76,6 +76,7 @@ use util::{post_inc, ResultExt, TryFutureExt}; use workspace::{ItemNavHistory, Workspace}; const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500); +const SCROLLBAR_SHOW_INTERVAL: Duration = Duration::from_secs(1); const MAX_LINE_LEN: usize = 1024; const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10; const MAX_SELECTION_HISTORY_LEN: usize = 1024; @@ -427,6 +428,8 @@ pub struct Editor { focused: bool, show_local_cursors: bool, show_local_selections: bool, + show_scrollbars: bool, + hide_scrollbar_task: Option>, blink_epoch: usize, blinking_paused: bool, mode: EditorMode, @@ -1028,6 +1031,8 @@ impl Editor { focused: false, show_local_cursors: false, show_local_selections: true, + show_scrollbars: true, + hide_scrollbar_task: None, blink_epoch: 0, blinking_paused: false, mode, @@ -1059,6 +1064,7 @@ impl Editor { ], }; this.end_selection(cx); + this.make_scrollbar_visible(cx); let editor_created_event = EditorCreated(cx.handle()); cx.emit_global(editor_created_event); @@ -1179,6 +1185,7 @@ impl Editor { self.scroll_top_anchor = anchor; } + self.make_scrollbar_visible(cx); self.autoscroll_request.take(); hide_hover(self, cx); @@ -5932,6 +5939,27 @@ impl Editor { self.show_local_cursors && self.focused } + pub fn show_scrollbars(&self) -> bool { + self.show_scrollbars + } + + fn make_scrollbar_visible(&mut self, cx: &mut ViewContext) { + if !self.show_scrollbars { + self.show_scrollbars = true; + cx.notify(); + } + + self.hide_scrollbar_task = Some(cx.spawn_weak(|this, mut cx| async move { + Timer::after(SCROLLBAR_SHOW_INTERVAL).await; + if let Some(this) = this.upgrade(&cx) { + this.update(&mut cx, |this, cx| { + this.show_scrollbars = false; + cx.notify(); + }); + } + })); + } + fn on_buffer_changed(&mut self, _: ModelHandle, cx: &mut ViewContext) { cx.notify(); } diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 300edc02a6..66dbe50864 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -454,7 +454,6 @@ impl EditorElement { let bounds = gutter_bounds.union_rect(text_bounds); let scroll_top = layout.position_map.snapshot.scroll_position().y() * layout.position_map.line_height; - let editor = self.view(cx.app); cx.scene.push_quad(Quad { bounds: gutter_bounds, background: Some(self.style.gutter_background), @@ -468,7 +467,7 @@ impl EditorElement { corner_radius: 0., }); - if let EditorMode::Full = editor.mode { + if let EditorMode::Full = layout.mode { let mut active_rows = layout.active_rows.iter().peekable(); while let Some((start_row, contains_non_empty_selection)) = active_rows.next() { let mut end_row = *start_row; @@ -911,25 +910,24 @@ impl EditorElement { fn paint_scrollbar(&mut self, bounds: RectF, layout: &mut LayoutState, cx: &mut PaintContext) { enum ScrollbarMouseHandlers {} - let row_range = if let Some(row_range) = &layout.scrollbar_row_range { - row_range - } else { + if layout.mode != EditorMode::Full { return; - }; + } + let view = self.view.clone(); let style = &self.style.theme.scrollbar; let top = bounds.min_y(); let bottom = bounds.max_y(); + let right = bounds.max_x(); + let left = right - style.width; let height = bounds.height(); - + let row_range = &layout.scrollbar_row_range; let max_row = layout.max_row + ((row_range.end - row_range.start) as u32); let scrollbar_start = row_range.start as f32 / max_row as f32; let scrollbar_end = row_range.end as f32 / max_row as f32; - let thumb_top = top + scrollbar_start * height; let thumb_bottom = top + scrollbar_end * height; - let right = bounds.max_x(); - let left = right - style.width; + let track_bounds = RectF::from_points(vec2f(left, top), vec2f(right, bottom)); let thumb_bounds = RectF::from_points(vec2f(left, thumb_top), vec2f(right, thumb_bottom)); @@ -939,63 +937,75 @@ impl EditorElement { background: style.track.background_color, ..Default::default() }); - cx.scene.push_quad(Quad { - bounds: thumb_bounds, - border: style.thumb.border, - background: style.thumb.background_color, - corner_radius: style.thumb.corner_radius, - }); + if layout.show_scrollbars { + cx.scene.push_quad(Quad { + bounds: thumb_bounds, + border: style.thumb.border, + background: style.thumb.background_color, + corner_radius: style.thumb.corner_radius, + }); + } + cx.scene.push_cursor_region(CursorRegion { bounds: track_bounds, style: CursorStyle::Arrow, }); - - let view = self.view.clone(); cx.scene.push_mouse_region( - MouseRegion::new::( - self.view.id(), - self.view.id(), - track_bounds, - ) - .on_down(MouseButton::Left, { - let view = view.clone(); - let row_range_len = row_range.end - row_range.start; - move |e, cx| { - let y = e.position.y(); - if y < thumb_top || thumb_bottom < y { + MouseRegion::new::(view.id(), view.id(), track_bounds) + .on_move({ + let view = view.clone(); + move |_, cx| { if let Some(view) = view.upgrade(cx.deref_mut()) { view.update(cx.deref_mut(), |view, cx| { - let center_row = - ((y - top) * max_row as f32 / height).round() as u32; - let top_row = center_row.saturating_sub(row_range_len as u32 / 2); - let mut position = view.scroll_position(cx); - position.set_y(top_row as f32); - view.set_scroll_position(position, cx); + view.make_scrollbar_visible(cx); }); } } - } - }) - .on_drag(MouseButton::Left, { - let view = view.clone(); - move |e, cx| { - let y = e.prev_mouse_position.y(); - let new_y = e.position.y(); - if thumb_top < y && y < thumb_bottom { + }) + .on_down(MouseButton::Left, { + let view = view.clone(); + let row_range = row_range.clone(); + move |e, cx| { + let y = e.position.y(); if let Some(view) = view.upgrade(cx.deref_mut()) { view.update(cx.deref_mut(), |view, cx| { - let mut position = view.scroll_position(cx); - position - .set_y(position.y() + (new_y - y) * (max_row as f32) / height); - if position.y() < 0.0 { - position.set_y(0.); + if y < thumb_top || thumb_bottom < y { + let center_row = + ((y - top) * max_row as f32 / height).round() as u32; + let top_row = center_row.saturating_sub( + (row_range.end - row_range.start) as u32 / 2, + ); + let mut position = view.scroll_position(cx); + position.set_y(top_row as f32); + view.set_scroll_position(position, cx); + } else { + view.make_scrollbar_visible(cx); } - view.set_scroll_position(position, cx); }); } } - } - }), + }) + .on_drag(MouseButton::Left, { + let view = view.clone(); + move |e, cx| { + let y = e.prev_mouse_position.y(); + let new_y = e.position.y(); + if thumb_top < y && y < thumb_bottom { + if let Some(view) = view.upgrade(cx.deref_mut()) { + view.update(cx.deref_mut(), |view, cx| { + let mut position = view.scroll_position(cx); + position.set_y( + position.y() + (new_y - y) * (max_row as f32) / height, + ); + if position.y() < 0.0 { + position.set_y(0.); + } + view.set_scroll_position(position, cx); + }); + } + } + } + }), ); } @@ -1582,6 +1592,7 @@ impl Element for EditorElement { let mut active_rows = BTreeMap::new(); let mut highlighted_rows = None; let mut highlighted_ranges = Vec::new(); + let mut show_scrollbars = false; self.update_view(cx.app, |view, cx| { let display_map = view.display_map.update(cx, |map, cx| map.snapshot(cx)); @@ -1642,6 +1653,8 @@ impl Element for EditorElement { .collect(), )); } + + show_scrollbars = view.show_scrollbars(); }); let line_number_layouts = @@ -1652,11 +1665,8 @@ impl Element for EditorElement { .git_diff_hunks_in_range(start_row..end_row) .collect(); - let scrollbar_row_range = if snapshot.mode == EditorMode::Full { - Some(scroll_position.y()..(scroll_position.y() + visible_row_count as f32)) - } else { - None - }; + let scrollbar_row_range = + scroll_position.y()..(scroll_position.y() + visible_row_count as f32); let mut max_visible_line_width = 0.0; let line_layouts = self.layout_lines(start_row..end_row, &snapshot, cx); @@ -1720,6 +1730,7 @@ impl Element for EditorElement { let mut context_menu = None; let mut code_actions_indicator = None; let mut hover = None; + let mut mode = EditorMode::Full; cx.render(&self.view.upgrade(cx).unwrap(), |view, cx| { let newest_selection_head = view .selections @@ -1741,6 +1752,7 @@ impl Element for EditorElement { let visible_rows = start_row..start_row + line_layouts.len() as u32; hover = view.hover_state.render(&snapshot, &style, visible_rows, cx); + mode = view.mode; }); if let Some((_, context_menu)) = context_menu.as_mut() { @@ -1788,6 +1800,7 @@ impl Element for EditorElement { ( size, LayoutState { + mode, position_map: Arc::new(PositionMap { size, scroll_max, @@ -1801,6 +1814,7 @@ impl Element for EditorElement { gutter_padding, text_size, scrollbar_row_range, + show_scrollbars, max_row, gutter_margin, active_rows, @@ -1939,13 +1953,15 @@ pub struct LayoutState { gutter_padding: f32, gutter_margin: f32, text_size: Vector2F, + mode: EditorMode, active_rows: BTreeMap, highlighted_rows: Option>, line_number_layouts: Vec>, blocks: Vec, highlighted_ranges: Vec<(Range, Color)>, selections: Vec<(ReplicaId, Vec)>, - scrollbar_row_range: Option>, + scrollbar_row_range: Range, + show_scrollbars: bool, max_row: u32, context_menu: Option<(DisplayPoint, ElementBox)>, diff_hunks: Vec>, From b229bc69b986108630ebbbb52e1dda949bfc5a24 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 10 Oct 2022 17:54:40 -0700 Subject: [PATCH 230/314] Tweak scrollbar styling --- styles/src/styleTree/editor.ts | 8 ++++++-- styles/src/themes/common/base16.ts | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/styles/src/styleTree/editor.ts b/styles/src/styleTree/editor.ts index 610f828ec8..a31b3d8654 100644 --- a/styles/src/styleTree/editor.ts +++ b/styles/src/styleTree/editor.ts @@ -172,7 +172,7 @@ export default function editor(theme: Theme) { }, }, scrollbar: { - width: 12, + width: 14, track: { border: { left: true, @@ -181,7 +181,11 @@ export default function editor(theme: Theme) { }, }, thumb: { - background: borderColor(theme, "secondary"), + background: withOpacity(borderColor(theme, "secondary"), 0.5), + border: { + width: 1, + color: withOpacity(borderColor(theme, 'muted'), 0.5), + } } }, compositionMark: { diff --git a/styles/src/themes/common/base16.ts b/styles/src/themes/common/base16.ts index cd6d46a771..1c4a5e4076 100644 --- a/styles/src/themes/common/base16.ts +++ b/styles/src/themes/common/base16.ts @@ -123,7 +123,7 @@ export function createTheme( const borderColor = { primary: sample(ramps.neutral, isLight ? 1.5 : 0), secondary: sample(ramps.neutral, isLight ? 1.25 : 1), - muted: sample(ramps.neutral, isLight ? 1 : 3), + muted: sample(ramps.neutral, isLight ? 1.25 : 3), active: sample(ramps.neutral, isLight ? 4 : 3), onMedia: withOpacity(darkest, 0.1), ok: sample(ramps.green, 0.3), From bf488f2027a432d7cdd67771ae4bca0ba3393ed9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 11 Oct 2022 10:59:36 +0200 Subject: [PATCH 231/314] Show project root names when displaying incoming call notification --- crates/call/src/call.rs | 4 +- crates/collab/src/integration_tests.rs | 6 ++- crates/collab/src/rpc/store.rs | 35 +++++++++----- .../src/incoming_call_notification.rs | 46 ++++++++++++++++--- crates/rpc/proto/zed.proto | 2 +- crates/theme/src/theme.rs | 3 ++ .../src/styleTree/incomingCallNotification.ts | 6 +++ .../styleTree/projectSharedNotification.ts | 2 +- 8 files changed, 81 insertions(+), 23 deletions(-) diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 99edb33b6e..6b06d04375 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -23,7 +23,7 @@ pub struct IncomingCall { pub room_id: u64, pub caller: Arc, pub participants: Vec>, - pub initial_project_id: Option, + pub initial_project: Option, } pub struct ActiveCall { @@ -78,7 +78,7 @@ impl ActiveCall { user_store.get_user(envelope.payload.caller_user_id, cx) }) .await?, - initial_project_id: envelope.payload.initial_project_id, + initial_project: envelope.payload.initial_project, }; this.update(&mut cx, |this, _| { *this.incoming_call.0.borrow_mut() = Some(call); diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 06e009d08b..ce9d01e3d3 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -541,13 +541,15 @@ async fn test_share_project( deterministic.run_until_parked(); let call = incoming_call_b.borrow().clone().unwrap(); assert_eq!(call.caller.github_login, "user_a"); - let project_id = call.initial_project_id.unwrap(); + let initial_project = call.initial_project.unwrap(); active_call_b .update(cx_b, |call, cx| call.accept_incoming(cx)) .await .unwrap(); let client_b_peer_id = client_b.peer_id; - let project_b = client_b.build_remote_project(project_id, cx_b).await; + let project_b = client_b + .build_remote_project(initial_project.id, cx_b) + .await; let replica_id_b = project_b.read_with(cx_b, |project, _| project.replica_id()); deterministic.run_until_parked(); diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index 522438255a..12f50db48e 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -176,9 +176,9 @@ impl Store { .iter() .map(|participant| participant.user_id) .collect(), - initial_project_id: active_call + initial_project: active_call .initial_project_id - .map(|project_id| project_id.to_proto()), + .and_then(|id| Self::build_participant_project(id, &self.projects)), }) } } else { @@ -572,7 +572,8 @@ impl Store { .iter() .map(|participant| participant.user_id) .collect(), - initial_project_id: initial_project_id.map(|project_id| project_id.to_proto()), + initial_project: initial_project_id + .and_then(|id| Self::build_participant_project(id, &self.projects)), }, )) } @@ -726,14 +727,6 @@ impl Store { .iter_mut() .find(|participant| participant.peer_id == host_connection_id.0) .ok_or_else(|| anyhow!("no such room"))?; - participant.projects.push(proto::ParticipantProject { - id: project_id.to_proto(), - worktree_root_names: worktrees - .iter() - .filter(|worktree| worktree.visible) - .map(|worktree| worktree.root_name.clone()) - .collect(), - }); connection.projects.insert(project_id); self.projects.insert( @@ -767,6 +760,10 @@ impl Store { }, ); + participant + .projects + .extend(Self::build_participant_project(project_id, &self.projects)); + Ok(room) } @@ -1011,6 +1008,22 @@ impl Store { Ok(connection_ids) } + fn build_participant_project( + project_id: ProjectId, + projects: &BTreeMap, + ) -> Option { + Some(proto::ParticipantProject { + id: project_id.to_proto(), + worktree_root_names: projects + .get(&project_id)? + .worktrees + .values() + .filter(|worktree| worktree.visible) + .map(|worktree| worktree.root_name.clone()) + .collect(), + }) + } + pub fn project_connection_ids( &self, project_id: ProjectId, diff --git a/crates/collab_ui/src/incoming_call_notification.rs b/crates/collab_ui/src/incoming_call_notification.rs index cbb23de2e6..ff359b9d9e 100644 --- a/crates/collab_ui/src/incoming_call_notification.rs +++ b/crates/collab_ui/src/incoming_call_notification.rs @@ -1,4 +1,5 @@ use call::{ActiveCall, IncomingCall}; +use client::proto; use futures::StreamExt; use gpui::{ elements::*, @@ -26,7 +27,11 @@ pub fn init(cx: &mut MutableAppContext) { if let Some(incoming_call) = incoming_call { const PADDING: f32 = 16.; let screen_size = cx.platform().screen_size(); - let window_size = vec2f(274., 64.); + + let window_size = cx.read(|cx| { + let theme = &cx.global::().theme.incoming_call_notification; + vec2f(theme.window_width, theme.window_height) + }); let (window_id, _) = cx.add_window( WindowOptions { bounds: WindowBounds::Fixed(RectF::new( @@ -66,7 +71,7 @@ impl IncomingCallNotification { if action.accept { let join = active_call.update(cx, |active_call, cx| active_call.accept_incoming(cx)); let caller_user_id = self.call.caller.id; - let initial_project_id = self.call.initial_project_id; + let initial_project_id = self.call.initial_project.as_ref().map(|project| project.id); cx.spawn_weak(|_, mut cx| async move { join.await?; if let Some(project_id) = initial_project_id { @@ -89,6 +94,12 @@ impl IncomingCallNotification { fn render_caller(&self, cx: &mut RenderContext) -> ElementBox { let theme = &cx.global::().theme.incoming_call_notification; + let default_project = proto::ParticipantProject::default(); + let initial_project = self + .call + .initial_project + .as_ref() + .unwrap_or(&default_project); Flex::row() .with_children(self.call.caller.avatar.clone().map(|avatar| { Image::new(avatar) @@ -108,11 +119,34 @@ impl IncomingCallNotification { .boxed(), ) .with_child( - Label::new("is calling you".into(), theme.caller_message.text.clone()) - .contained() - .with_style(theme.caller_message.container) - .boxed(), + Label::new( + format!( + "is sharing a project in Zed{}", + if initial_project.worktree_root_names.is_empty() { + "" + } else { + ":" + } + ), + theme.caller_message.text.clone(), + ) + .contained() + .with_style(theme.caller_message.container) + .boxed(), ) + .with_children(if initial_project.worktree_root_names.is_empty() { + None + } else { + Some( + Label::new( + initial_project.worktree_root_names.join(", "), + theme.worktree_roots.text.clone(), + ) + .contained() + .with_style(theme.worktree_roots.container) + .boxed(), + ) + }) .contained() .with_style(theme.caller_metadata) .aligned() diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index b1897653c9..2a358113e9 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -195,7 +195,7 @@ message IncomingCall { uint64 room_id = 1; uint64 caller_user_id = 2; repeated uint64 participant_user_ids = 3; - optional uint64 initial_project_id = 4; + optional ParticipantProject initial_project = 4; } message CallCanceled {} diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 7f11ae6e03..9a836864bf 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -488,6 +488,8 @@ pub struct ProjectSharedNotification { #[derive(Deserialize, Default)] pub struct IncomingCallNotification { + pub window_height: f32, + pub window_width: f32, #[serde(default)] pub background: Color, pub caller_container: ContainerStyle, @@ -495,6 +497,7 @@ pub struct IncomingCallNotification { pub caller_metadata: ContainerStyle, pub caller_username: ContainedText, pub caller_message: ContainedText, + pub worktree_roots: ContainedText, pub button_width: f32, pub accept_button: ContainedText, pub decline_button: ContainedText, diff --git a/styles/src/styleTree/incomingCallNotification.ts b/styles/src/styleTree/incomingCallNotification.ts index d8ea7dbad9..30b8ae90ca 100644 --- a/styles/src/styleTree/incomingCallNotification.ts +++ b/styles/src/styleTree/incomingCallNotification.ts @@ -4,6 +4,8 @@ import { backgroundColor, borderColor, text } from "./components"; export default function incomingCallNotification(theme: Theme): Object { const avatarSize = 32; return { + windowHeight: 74, + windowWidth: 380, background: backgroundColor(theme, 300), callerContainer: { padding: 12, @@ -24,6 +26,10 @@ export default function incomingCallNotification(theme: Theme): Object { ...text(theme, "sans", "secondary", { size: "xs" }), margin: { top: -3 }, }, + worktreeRoots: { + ...text(theme, "sans", "secondary", { size: "xs", weight: "bold" }), + margin: { top: -3 }, + }, buttonWidth: 96, acceptButton: { background: backgroundColor(theme, "ok", "active"), diff --git a/styles/src/styleTree/projectSharedNotification.ts b/styles/src/styleTree/projectSharedNotification.ts index f6ebf4781a..e3cb29bbf1 100644 --- a/styles/src/styleTree/projectSharedNotification.ts +++ b/styles/src/styleTree/projectSharedNotification.ts @@ -4,7 +4,7 @@ import { backgroundColor, borderColor, text } from "./components"; export default function projectSharedNotification(theme: Theme): Object { const avatarSize = 48; return { - windowHeight: 72, + windowHeight: 74, windowWidth: 380, background: backgroundColor(theme, 300), ownerContainer: { From bf0a04ab50f8a11071e67ddb0d2db4085d91d6cc Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 11 Oct 2022 11:01:38 +0200 Subject: [PATCH 232/314] Dismiss popover when contact finder is unfocused --- crates/collab_ui/src/contacts_popover.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index b9b1d254b6..0758cf735d 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -27,7 +27,6 @@ pub struct ContactsPopover { project: ModelHandle, user_store: ModelHandle, _subscription: Option, - _window_subscription: gpui::Subscription, } impl ContactsPopover { @@ -43,18 +42,11 @@ impl ContactsPopover { project, user_store, _subscription: None, - _window_subscription: cx.observe_window_activation(Self::window_activation_changed), }; this.show_contact_list(cx); this } - fn window_activation_changed(&mut self, active: bool, cx: &mut ViewContext) { - if !active { - cx.emit(Event::Dismissed); - } - } - fn toggle_contact_finder(&mut self, _: &ToggleContactFinder, cx: &mut ViewContext) { match &self.child { Child::ContactList(_) => self.show_contact_finder(cx), @@ -65,8 +57,8 @@ impl ContactsPopover { fn show_contact_finder(&mut self, cx: &mut ViewContext) { let child = cx.add_view(|cx| ContactFinder::new(self.user_store.clone(), cx)); cx.focus(&child); - self._subscription = Some(cx.subscribe(&child, |this, _, event, cx| match event { - crate::contact_finder::Event::Dismissed => this.show_contact_list(cx), + self._subscription = Some(cx.subscribe(&child, |_, _, event, cx| match event { + crate::contact_finder::Event::Dismissed => cx.emit(Event::Dismissed), })); self.child = Child::ContactFinder(child); cx.notify(); From 9ec62d4c1fdb7636a4f0b61aafb4fae44e6cf1dd Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 11 Oct 2022 11:03:49 +0200 Subject: [PATCH 233/314] Foreground app when accepting calls and project shares --- crates/collab_ui/src/collab_ui.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 72595c2338..4b7e3dae01 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -59,6 +59,7 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { }; cx.activate_window(workspace.window_id()); + cx.platform().activate(true); workspace.update(&mut cx, |workspace, cx| { if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { From 1d4bdfc4a18ba8073ba5bffb94cad1b4942eb95d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 11 Oct 2022 11:28:27 +0200 Subject: [PATCH 234/314] Cancel calls automatically when caller hangs up or disconnects --- crates/collab/src/integration_tests.rs | 34 ++++++++++++ crates/collab/src/rpc.rs | 40 +++++++++----- crates/collab/src/rpc/store.rs | 76 +++++++++++++------------- 3 files changed, 97 insertions(+), 53 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index ce9d01e3d3..42c95c4b2e 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -492,6 +492,40 @@ async fn test_calls_on_multiple_connections( deterministic.run_until_parked(); assert!(incoming_call_b1.next().await.unwrap().is_none()); assert!(incoming_call_b2.next().await.unwrap().is_none()); + + // User A calls user B again. + active_call_a + .update(cx_a, |call, cx| { + call.invite(client_b1.user_id().unwrap(), None, cx) + }) + .await + .unwrap(); + deterministic.run_until_parked(); + assert!(incoming_call_b1.next().await.unwrap().is_some()); + assert!(incoming_call_b2.next().await.unwrap().is_some()); + + // User A hangs up, causing both connections to stop ringing. + active_call_a.update(cx_a, |call, cx| call.hang_up(cx).unwrap()); + deterministic.run_until_parked(); + assert!(incoming_call_b1.next().await.unwrap().is_none()); + assert!(incoming_call_b2.next().await.unwrap().is_none()); + + // User A calls user B again. + active_call_a + .update(cx_a, |call, cx| { + call.invite(client_b1.user_id().unwrap(), None, cx) + }) + .await + .unwrap(); + deterministic.run_until_parked(); + assert!(incoming_call_b1.next().await.unwrap().is_some()); + assert!(incoming_call_b2.next().await.unwrap().is_some()); + + // User A disconnects up, causing both connections to stop ringing. + server.disconnect_client(client_a.current_user_id(cx_a)); + cx_a.foreground().advance_clock(rpc::RECEIVE_TIMEOUT); + assert!(incoming_call_b1.next().await.unwrap().is_none()); + assert!(incoming_call_b2.next().await.unwrap().is_none()); } #[gpui::test(iterations = 10)] diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 11219fb8b8..febdfe4434 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -479,30 +479,34 @@ impl Server { let mut store = self.store().await; let removed_connection = store.remove_connection(connection_id)?; - for (project_id, project) in removed_connection.hosted_projects { - projects_to_unshare.push(project_id); + for project in removed_connection.hosted_projects { + projects_to_unshare.push(project.id); broadcast(connection_id, project.guests.keys().copied(), |conn_id| { self.peer.send( conn_id, proto::UnshareProject { - project_id: project_id.to_proto(), + project_id: project.id.to_proto(), }, ) }); } - for project_id in removed_connection.guest_project_ids { - if let Some(project) = store.project(project_id).trace_err() { - broadcast(connection_id, project.connection_ids(), |conn_id| { - self.peer.send( - conn_id, - proto::RemoveProjectCollaborator { - project_id: project_id.to_proto(), - peer_id: connection_id.0, - }, - ) - }); - } + for project in removed_connection.guest_projects { + broadcast(connection_id, project.connection_ids, |conn_id| { + self.peer.send( + conn_id, + proto::RemoveProjectCollaborator { + project_id: project.id.to_proto(), + peer_id: connection_id.0, + }, + ) + }); + } + + for connection_id in removed_connection.canceled_call_connection_ids { + self.peer + .send(connection_id, proto::CallCanceled {}) + .trace_err(); } if let Some(room) = removed_connection @@ -666,6 +670,12 @@ impl Server { } } + for connection_id in left_room.canceled_call_connection_ids { + self.peer + .send(connection_id, proto::CallCanceled {}) + .trace_err(); + } + if let Some(room) = left_room.room { self.room_updated(room); } diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index 12f50db48e..9b2661abca 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -86,10 +86,11 @@ pub type ReplicaId = u16; #[derive(Default)] pub struct RemovedConnectionState { pub user_id: UserId, - pub hosted_projects: HashMap, - pub guest_project_ids: HashSet, + pub hosted_projects: Vec, + pub guest_projects: Vec, pub contact_ids: HashSet, pub room_id: Option, + pub canceled_call_connection_ids: Vec, } pub struct LeftProject { @@ -104,6 +105,7 @@ pub struct LeftRoom<'a> { pub room: Option<&'a proto::Room>, pub unshared_projects: Vec, pub left_projects: Vec, + pub canceled_call_connection_ids: Vec, } #[derive(Copy, Clone)] @@ -197,7 +199,6 @@ impl Store { .ok_or_else(|| anyhow!("no such connection"))?; let user_id = connection.user_id; - let connection_projects = mem::take(&mut connection.projects); let connection_channels = mem::take(&mut connection.channels); let mut result = RemovedConnectionState { @@ -210,48 +211,21 @@ impl Store { self.leave_channel(connection_id, channel_id); } - // Unshare and leave all projects. - for project_id in connection_projects { - if let Ok((_, project)) = self.unshare_project(project_id, connection_id) { - result.hosted_projects.insert(project_id, project); - } else if self.leave_project(project_id, connection_id).is_ok() { - result.guest_project_ids.insert(project_id); - } + let connected_user = self.connected_users.get(&user_id).unwrap(); + if let Some(active_call) = connected_user.active_call.as_ref() { + let room_id = active_call.room_id; + let left_room = self.leave_room(room_id, connection_id)?; + result.hosted_projects = left_room.unshared_projects; + result.guest_projects = left_room.left_projects; + result.room_id = Some(room_id); + result.canceled_call_connection_ids = left_room.canceled_call_connection_ids; } let connected_user = self.connected_users.get_mut(&user_id).unwrap(); connected_user.connection_ids.remove(&connection_id); - if let Some(active_call) = connected_user.active_call.as_ref() { - let room_id = active_call.room_id; - if let Some(room) = self.rooms.get_mut(&room_id) { - let prev_participant_count = room.participants.len(); - room.participants - .retain(|participant| participant.peer_id != connection_id.0); - if prev_participant_count == room.participants.len() { - if connected_user.connection_ids.is_empty() { - room.pending_participant_user_ids - .retain(|pending_user_id| *pending_user_id != user_id.to_proto()); - result.room_id = Some(room_id); - connected_user.active_call = None; - } - } else { - result.room_id = Some(room_id); - connected_user.active_call = None; - } - - if room.participants.is_empty() && room.pending_participant_user_ids.is_empty() { - self.rooms.remove(&room_id); - } - } else { - tracing::error!("disconnected user claims to be in a room that does not exist"); - connected_user.active_call = None; - } - } - if connected_user.connection_ids.is_empty() { self.connected_users.remove(&user_id); } - self.connections.remove(&connection_id).unwrap(); Ok(result) @@ -491,6 +465,31 @@ impl Store { .ok_or_else(|| anyhow!("no such room"))?; room.participants .retain(|participant| participant.peer_id != connection_id.0); + + let mut canceled_call_connection_ids = Vec::new(); + room.pending_participant_user_ids + .retain(|pending_participant_user_id| { + if let Some(connected_user) = self + .connected_users + .get_mut(&UserId::from_proto(*pending_participant_user_id)) + { + if let Some(call) = connected_user.active_call.as_ref() { + if call.caller_user_id == user_id { + connected_user.active_call.take(); + canceled_call_connection_ids + .extend(connected_user.connection_ids.iter().copied()); + false + } else { + true + } + } else { + true + } + } else { + true + } + }); + if room.participants.is_empty() && room.pending_participant_user_ids.is_empty() { self.rooms.remove(&room_id); } @@ -499,6 +498,7 @@ impl Store { room: self.rooms.get(&room_id), unshared_projects, left_projects, + canceled_call_connection_ids, }) } From 0a306808dacbe65672e243d5474128d561fda97a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 11 Oct 2022 11:44:31 +0200 Subject: [PATCH 235/314] Dismiss project shared notifications when a project was unshared --- crates/call/src/room.rs | 32 +++++++++++++++++-- .../src/project_shared_notification.rs | 15 ++++++++- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index fb9ba09d81..ebe2d4284b 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -18,6 +18,10 @@ pub enum Event { project_id: u64, worktree_root_names: Vec, }, + RemoteProjectUnshared { + project_id: u64, + }, + Left, } pub struct Room { @@ -148,6 +152,7 @@ impl Room { } cx.notify(); + cx.emit(Event::Left); self.status = RoomStatus::Offline; self.remote_participants.clear(); self.pending_participants.clear(); @@ -223,15 +228,21 @@ impl Room { let peer_id = PeerId(participant.peer_id); this.participant_user_ids.insert(participant.user_id); - let existing_projects = this + let old_projects = this .remote_participants .get(&peer_id) .into_iter() .flat_map(|existing| &existing.projects) .map(|project| project.id) .collect::>(); + let new_projects = participant + .projects + .iter() + .map(|project| project.id) + .collect::>(); + for project in &participant.projects { - if !existing_projects.contains(&project.id) { + if !old_projects.contains(&project.id) { cx.emit(Event::RemoteProjectShared { owner: user.clone(), project_id: project.id, @@ -240,6 +251,12 @@ impl Room { } } + for unshared_project_id in old_projects.difference(&new_projects) { + cx.emit(Event::RemoteProjectUnshared { + project_id: *unshared_project_id, + }); + } + this.remote_participants.insert( peer_id, RemoteParticipant { @@ -252,7 +269,16 @@ impl Room { } this.remote_participants.retain(|_, participant| { - this.participant_user_ids.contains(&participant.user.id) + if this.participant_user_ids.contains(&participant.user.id) { + true + } else { + for project in &participant.projects { + cx.emit(Event::RemoteProjectUnshared { + project_id: project.id, + }); + } + false + } }); cx.notify(); diff --git a/crates/collab_ui/src/project_shared_notification.rs b/crates/collab_ui/src/project_shared_notification.rs index 22383feb20..a17e11b079 100644 --- a/crates/collab_ui/src/project_shared_notification.rs +++ b/crates/collab_ui/src/project_shared_notification.rs @@ -1,5 +1,6 @@ use call::{room, ActiveCall}; use client::User; +use collections::HashMap; use gpui::{ actions, elements::*, @@ -18,6 +19,7 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action(ProjectSharedNotification::dismiss); let active_call = ActiveCall::global(cx); + let mut notification_windows = HashMap::default(); cx.subscribe(&active_call, move |_, event, cx| match event { room::Event::RemoteProjectShared { owner, @@ -29,7 +31,7 @@ pub fn init(cx: &mut MutableAppContext) { let theme = &cx.global::().theme.project_shared_notification; let window_size = vec2f(theme.window_width, theme.window_height); - cx.add_window( + let (window_id, _) = cx.add_window( WindowOptions { bounds: WindowBounds::Fixed(RectF::new( vec2f(screen_size.x() - window_size.x() - PADDING, PADDING), @@ -48,6 +50,17 @@ pub fn init(cx: &mut MutableAppContext) { ) }, ); + notification_windows.insert(*project_id, window_id); + } + room::Event::RemoteProjectUnshared { project_id } => { + if let Some(window_id) = notification_windows.remove(&project_id) { + cx.remove_window(window_id); + } + } + room::Event::Left => { + for (_, window_id) in notification_windows.drain() { + cx.remove_window(window_id); + } } }) .detach(); From 8e7f96cebc027960ed92a3e7051401d77732415a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 11 Oct 2022 11:54:32 +0200 Subject: [PATCH 236/314] Update contacts when automatically canceling calls --- crates/collab/src/integration_tests.rs | 72 ++++++++++++++++++++++++++ crates/collab/src/rpc.rs | 30 +++++++---- 2 files changed, 91 insertions(+), 11 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 42c95c4b2e..27fb1a9bfb 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -4549,6 +4549,78 @@ async fn test_contacts( ] ); + active_call_a.update(cx_a, |call, cx| call.hang_up(cx).unwrap()); + deterministic.run_until_parked(); + assert_eq!( + contacts(&client_a, cx_a), + [ + ("user_b".to_string(), "online", "free"), + ("user_c".to_string(), "online", "free") + ] + ); + assert_eq!( + contacts(&client_b, cx_b), + [ + ("user_a".to_string(), "online", "free"), + ("user_c".to_string(), "online", "free") + ] + ); + assert_eq!( + contacts(&client_c, cx_c), + [ + ("user_a".to_string(), "online", "free"), + ("user_b".to_string(), "online", "free") + ] + ); + + active_call_a + .update(cx_a, |call, cx| { + call.invite(client_b.user_id().unwrap(), None, cx) + }) + .await + .unwrap(); + deterministic.run_until_parked(); + assert_eq!( + contacts(&client_a, cx_a), + [ + ("user_b".to_string(), "online", "busy"), + ("user_c".to_string(), "online", "free") + ] + ); + assert_eq!( + contacts(&client_b, cx_b), + [ + ("user_a".to_string(), "online", "busy"), + ("user_c".to_string(), "online", "free") + ] + ); + assert_eq!( + contacts(&client_c, cx_c), + [ + ("user_a".to_string(), "online", "busy"), + ("user_b".to_string(), "online", "busy") + ] + ); + + server.forbid_connections(); + server.disconnect_client(client_a.current_user_id(cx_a)); + deterministic.advance_clock(rpc::RECEIVE_TIMEOUT); + assert_eq!(contacts(&client_a, cx_a), []); + assert_eq!( + contacts(&client_b, cx_b), + [ + ("user_a".to_string(), "offline", "free"), + ("user_c".to_string(), "online", "free") + ] + ); + assert_eq!( + contacts(&client_c, cx_c), + [ + ("user_a".to_string(), "offline", "free"), + ("user_b".to_string(), "online", "free") + ] + ); + #[allow(clippy::type_complexity)] fn contacts( client: &TestClient, diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index febdfe4434..84449e79d5 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -22,7 +22,7 @@ use axum::{ routing::get, Extension, Router, TypedHeader, }; -use collections::HashMap; +use collections::{HashMap, HashSet}; use futures::{ channel::mpsc, future::{self, BoxFuture}, @@ -474,7 +474,7 @@ impl Server { self.peer.disconnect(connection_id); let mut projects_to_unshare = Vec::new(); - let removed_user_id; + let mut contacts_to_update = HashSet::default(); { let mut store = self.store().await; let removed_connection = store.remove_connection(connection_id)?; @@ -507,6 +507,7 @@ impl Server { self.peer .send(connection_id, proto::CallCanceled {}) .trace_err(); + contacts_to_update.extend(store.user_id_for_connection(connection_id).ok()); } if let Some(room) = removed_connection @@ -516,10 +517,12 @@ impl Server { self.room_updated(room); } - removed_user_id = removed_connection.user_id; + contacts_to_update.insert(removed_connection.user_id); }; - self.update_user_contacts(removed_user_id).await.trace_err(); + for user_id in contacts_to_update { + self.update_user_contacts(user_id).await.trace_err(); + } for project_id in projects_to_unshare { self.app_state @@ -632,11 +635,12 @@ impl Server { } async fn leave_room(self: Arc, message: TypedEnvelope) -> Result<()> { - let user_id; + let mut contacts_to_update = HashSet::default(); { let mut store = self.store().await; - user_id = store.user_id_for_connection(message.sender_id)?; + let user_id = store.user_id_for_connection(message.sender_id)?; let left_room = store.leave_room(message.payload.id, message.sender_id)?; + contacts_to_update.insert(user_id); for project in left_room.unshared_projects { for connection_id in project.connection_ids() { @@ -670,17 +674,21 @@ impl Server { } } + if let Some(room) = left_room.room { + self.room_updated(room); + } + for connection_id in left_room.canceled_call_connection_ids { self.peer .send(connection_id, proto::CallCanceled {}) .trace_err(); - } - - if let Some(room) = left_room.room { - self.room_updated(room); + contacts_to_update.extend(store.user_id_for_connection(connection_id).ok()); } } - self.update_user_contacts(user_id).await?; + + for user_id in contacts_to_update { + self.update_user_contacts(user_id).await?; + } Ok(()) } From feb17c29ec1e9fd041918ddff34c6f8c4b95c3d9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 11 Oct 2022 12:23:15 +0200 Subject: [PATCH 237/314] Show participant projects in contacts popover --- crates/call/src/participant.rs | 5 + crates/call/src/room.rs | 25 +++- crates/collab_ui/src/contact_list.rs | 202 +++++++++++++++++++++++++-- crates/theme/src/theme.rs | 15 ++ styles/src/styleTree/contactList.ts | 51 ++++++- 5 files changed, 279 insertions(+), 19 deletions(-) diff --git a/crates/call/src/participant.rs b/crates/call/src/participant.rs index b124d920a3..7e031f907f 100644 --- a/crates/call/src/participant.rs +++ b/crates/call/src/participant.rs @@ -20,6 +20,11 @@ impl ParticipantLocation { } } +#[derive(Clone, Debug, Default)] +pub struct LocalParticipant { + pub projects: Vec, +} + #[derive(Clone, Debug)] pub struct RemoteParticipant { pub user: Arc, diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index ebe2d4284b..a3b49c2a4f 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -1,5 +1,5 @@ use crate::{ - participant::{ParticipantLocation, RemoteParticipant}, + participant::{LocalParticipant, ParticipantLocation, RemoteParticipant}, IncomingCall, }; use anyhow::{anyhow, Result}; @@ -27,6 +27,7 @@ pub enum Event { pub struct Room { id: u64, status: RoomStatus, + local_participant: LocalParticipant, remote_participants: BTreeMap, pending_participants: Vec>, participant_user_ids: HashSet, @@ -72,6 +73,7 @@ impl Room { id, status: RoomStatus::Online, participant_user_ids: Default::default(), + local_participant: Default::default(), remote_participants: Default::default(), pending_participants: Default::default(), pending_call_count: 0, @@ -170,6 +172,10 @@ impl Room { self.status } + pub fn local_participant(&self) -> &LocalParticipant { + &self.local_participant + } + pub fn remote_participants(&self) -> &BTreeMap { &self.remote_participants } @@ -201,8 +207,11 @@ impl Room { cx: &mut ModelContext, ) -> Result<()> { // Filter ourselves out from the room's participants. - room.participants - .retain(|participant| Some(participant.user_id) != self.client.user_id()); + let local_participant_ix = room + .participants + .iter() + .position(|participant| Some(participant.user_id) == self.client.user_id()); + let local_participant = local_participant_ix.map(|ix| room.participants.swap_remove(ix)); let remote_participant_user_ids = room .participants @@ -223,6 +232,12 @@ impl Room { this.update(&mut cx, |this, cx| { this.participant_user_ids.clear(); + if let Some(participant) = local_participant { + this.local_participant.projects = participant.projects; + } else { + this.local_participant.projects.clear(); + } + if let Some(participants) = remote_participants.log_err() { for (participant, user) in room.participants.into_iter().zip(participants) { let peer_id = PeerId(participant.peer_id); @@ -280,8 +295,6 @@ impl Room { false } }); - - cx.notify(); } if let Some(pending_participants) = pending_participants.log_err() { @@ -289,7 +302,6 @@ impl Room { for participant in &this.pending_participants { this.participant_user_ids.insert(participant.id); } - cx.notify(); } this.pending_room_update.take(); @@ -298,6 +310,7 @@ impl Room { } this.check_invariants(); + cx.notify(); }); })); diff --git a/crates/collab_ui/src/contact_list.rs b/crates/collab_ui/src/contact_list.rs index a539f8ffac..357b3c65e0 100644 --- a/crates/collab_ui/src/contact_list.rs +++ b/crates/collab_ui/src/contact_list.rs @@ -6,9 +6,11 @@ use client::{Contact, PeerId, User, UserStore}; use editor::{Cancel, Editor}; use fuzzy::{match_strings, StringMatchCandidate}; use gpui::{ - elements::*, impl_actions, impl_internal_actions, keymap, AppContext, ClipboardItem, - CursorStyle, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, - View, ViewContext, ViewHandle, + elements::*, + geometry::{rect::RectF, vector::vec2f}, + impl_actions, impl_internal_actions, keymap, AppContext, ClipboardItem, CursorStyle, Entity, + ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, View, ViewContext, + ViewHandle, }; use menu::{Confirm, SelectNext, SelectPrev}; use project::Project; @@ -16,6 +18,7 @@ use serde::Deserialize; use settings::Settings; use theme::IconButton; use util::ResultExt; +use workspace::JoinProject; impl_actions!(contact_list, [RemoveContact, RespondToContactRequest]); impl_internal_actions!(contact_list, [ToggleExpanded, Call, LeaveCall]); @@ -55,7 +58,17 @@ enum Section { #[derive(Clone)] enum ContactEntry { Header(Section), - CallParticipant { user: Arc, is_pending: bool }, + CallParticipant { + user: Arc, + is_pending: bool, + }, + ParticipantProject { + project_id: u64, + worktree_root_names: Vec, + host_user_id: u64, + is_host: bool, + is_last: bool, + }, IncomingRequest(Arc), OutgoingRequest(Arc), Contact(Arc), @@ -74,6 +87,18 @@ impl PartialEq for ContactEntry { return user_1.id == user_2.id; } } + ContactEntry::ParticipantProject { + project_id: project_id_1, + .. + } => { + if let ContactEntry::ParticipantProject { + project_id: project_id_2, + .. + } = other + { + return project_id_1 == project_id_2; + } + } ContactEntry::IncomingRequest(user_1) => { if let ContactEntry::IncomingRequest(user_2) = other { return user_1.id == user_2.id; @@ -177,6 +202,22 @@ impl ContactList { &theme.contact_list, ) } + ContactEntry::ParticipantProject { + project_id, + worktree_root_names, + host_user_id, + is_host, + is_last, + } => Self::render_participant_project( + *project_id, + worktree_root_names, + *host_user_id, + *is_host, + *is_last, + is_selected, + &theme.contact_list, + cx, + ), ContactEntry::IncomingRequest(user) => Self::render_contact_request( user.clone(), this.user_store.clone(), @@ -298,6 +339,19 @@ impl ContactList { ); } } + ContactEntry::ParticipantProject { + project_id, + host_user_id, + is_host, + .. + } => { + if !is_host { + cx.dispatch_global_action(JoinProject { + project_id: *project_id, + follow_user_id: *host_user_id, + }); + } + } _ => {} } } @@ -324,7 +378,7 @@ impl ContactList { if let Some(room) = ActiveCall::global(cx).read(cx).room() { let room = room.read(cx); - let mut call_participants = Vec::new(); + let mut participant_entries = Vec::new(); // Populate the active user. if let Some(user) = user_store.current_user() { @@ -343,10 +397,21 @@ impl ContactList { executor.clone(), )); if !matches.is_empty() { - call_participants.push(ContactEntry::CallParticipant { + let user_id = user.id; + participant_entries.push(ContactEntry::CallParticipant { user, is_pending: false, }); + let mut projects = room.local_participant().projects.iter().peekable(); + while let Some(project) = projects.next() { + participant_entries.push(ContactEntry::ParticipantProject { + project_id: project.id, + worktree_root_names: project.worktree_root_names.clone(), + host_user_id: user_id, + is_host: true, + is_last: projects.peek().is_none(), + }); + } } } @@ -370,14 +435,25 @@ impl ContactList { &Default::default(), executor.clone(), )); - call_participants.extend(matches.iter().map(|mat| { - ContactEntry::CallParticipant { + for mat in matches { + let participant = &room.remote_participants()[&PeerId(mat.candidate_id as u32)]; + participant_entries.push(ContactEntry::CallParticipant { user: room.remote_participants()[&PeerId(mat.candidate_id as u32)] .user .clone(), is_pending: false, + }); + let mut projects = participant.projects.iter().peekable(); + while let Some(project) = projects.next() { + participant_entries.push(ContactEntry::ParticipantProject { + project_id: project.id, + worktree_root_names: project.worktree_root_names.clone(), + host_user_id: participant.user.id, + is_host: false, + is_last: projects.peek().is_none(), + }); } - })); + } // Populate pending participants. self.match_candidates.clear(); @@ -400,15 +476,15 @@ impl ContactList { &Default::default(), executor.clone(), )); - call_participants.extend(matches.iter().map(|mat| ContactEntry::CallParticipant { + participant_entries.extend(matches.iter().map(|mat| ContactEntry::CallParticipant { user: room.pending_participants()[mat.candidate_id].clone(), is_pending: true, })); - if !call_participants.is_empty() { + if !participant_entries.is_empty() { self.entries.push(ContactEntry::Header(Section::ActiveCall)); if !self.collapsed_sections.contains(&Section::ActiveCall) { - self.entries.extend(call_participants); + self.entries.extend(participant_entries); } } } @@ -588,6 +664,108 @@ impl ContactList { .boxed() } + fn render_participant_project( + project_id: u64, + worktree_root_names: &[String], + host_user_id: u64, + is_host: bool, + is_last: bool, + is_selected: bool, + theme: &theme::ContactList, + cx: &mut RenderContext, + ) -> ElementBox { + let font_cache = cx.font_cache(); + let host_avatar_height = theme + .contact_avatar + .width + .or(theme.contact_avatar.height) + .unwrap_or(0.); + let row = &theme.project_row.default; + let tree_branch = theme.tree_branch; + let line_height = row.name.text.line_height(font_cache); + let cap_height = row.name.text.cap_height(font_cache); + let baseline_offset = + row.name.text.baseline_offset(font_cache) + (theme.row_height - line_height) / 2.; + let project_name = if worktree_root_names.is_empty() { + "untitled".to_string() + } else { + worktree_root_names.join(", ") + }; + + MouseEventHandler::::new(project_id as usize, cx, |mouse_state, _| { + let tree_branch = *tree_branch.style_for(mouse_state, is_selected); + let row = theme.project_row.style_for(mouse_state, is_selected); + + Flex::row() + .with_child( + Stack::new() + .with_child( + Canvas::new(move |bounds, _, cx| { + let start_x = bounds.min_x() + (bounds.width() / 2.) + - (tree_branch.width / 2.); + let end_x = bounds.max_x(); + let start_y = bounds.min_y(); + let end_y = bounds.min_y() + baseline_offset - (cap_height / 2.); + + cx.scene.push_quad(gpui::Quad { + bounds: RectF::from_points( + vec2f(start_x, start_y), + vec2f( + start_x + tree_branch.width, + if is_last { end_y } else { bounds.max_y() }, + ), + ), + background: Some(tree_branch.color), + border: gpui::Border::default(), + corner_radius: 0., + }); + cx.scene.push_quad(gpui::Quad { + bounds: RectF::from_points( + vec2f(start_x, end_y), + vec2f(end_x, end_y + tree_branch.width), + ), + background: Some(tree_branch.color), + border: gpui::Border::default(), + corner_radius: 0., + }); + }) + .boxed(), + ) + .constrained() + .with_width(host_avatar_height) + .boxed(), + ) + .with_child( + Label::new(project_name, row.name.text.clone()) + .aligned() + .left() + .contained() + .with_style(row.name.container) + .flex(1., false) + .boxed(), + ) + .constrained() + .with_height(theme.row_height) + .contained() + .with_style(row.container) + .boxed() + }) + .with_cursor_style(if !is_host { + CursorStyle::PointingHand + } else { + CursorStyle::Arrow + }) + .on_click(MouseButton::Left, move |_, cx| { + if !is_host { + cx.dispatch_global_action(JoinProject { + project_id, + follow_user_id: host_user_id, + }); + } + }) + .boxed() + } + fn render_header( section: Section, theme: &theme::ContactList, diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 9a836864bf..503645d6bc 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -100,6 +100,8 @@ pub struct ContactList { pub leave_call: Interactive, pub contact_row: Interactive, pub row_height: f32, + pub project_row: Interactive, + pub tree_branch: Interactive, pub contact_avatar: ImageStyle, pub contact_status_free: ContainerStyle, pub contact_status_busy: ContainerStyle, @@ -112,6 +114,19 @@ pub struct ContactList { pub calling_indicator: ContainedText, } +#[derive(Deserialize, Default)] +pub struct ProjectRow { + #[serde(flatten)] + pub container: ContainerStyle, + pub name: ContainedText, +} + +#[derive(Deserialize, Default, Clone, Copy)] +pub struct TreeBranch { + pub width: f32, + pub color: Color, +} + #[derive(Deserialize, Default)] pub struct ContactFinder { pub picker: Picker, diff --git a/styles/src/styleTree/contactList.ts b/styles/src/styleTree/contactList.ts index 8b8b6024cf..633d634196 100644 --- a/styles/src/styleTree/contactList.ts +++ b/styles/src/styleTree/contactList.ts @@ -12,6 +12,31 @@ export default function contactList(theme: Theme) { buttonWidth: 16, cornerRadius: 8, }; + const projectRow = { + guestAvatarSpacing: 4, + height: 24, + guestAvatar: { + cornerRadius: 8, + width: 14, + }, + name: { + ...text(theme, "mono", "placeholder", { size: "sm" }), + margin: { + left: nameMargin, + right: 6, + }, + }, + guests: { + margin: { + left: nameMargin, + right: nameMargin, + }, + }, + padding: { + left: sidePadding, + right: sidePadding, + }, + }; return { userQueryEditor: { @@ -129,6 +154,30 @@ export default function contactList(theme: Theme) { }, callingIndicator: { ...text(theme, "mono", "muted", { size: "xs" }) - } + }, + treeBranch: { + color: borderColor(theme, "active"), + width: 1, + hover: { + color: borderColor(theme, "active"), + }, + active: { + color: borderColor(theme, "active"), + }, + }, + projectRow: { + ...projectRow, + background: backgroundColor(theme, 300), + name: { + ...projectRow.name, + ...text(theme, "mono", "secondary", { size: "sm" }), + }, + hover: { + background: backgroundColor(theme, 300, "hovered"), + }, + active: { + background: backgroundColor(theme, 300, "active"), + }, + }, } } From 29c3b81a0a6d8fd1994d827ed979a5ee5f83809d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 11 Oct 2022 14:52:47 +0200 Subject: [PATCH 238/314] Show prompt when closing last window while there's an active call Co-Authored-By: Nathan Sobo --- crates/gpui/src/app.rs | 10 ++++ crates/workspace/src/workspace.rs | 46 ++++++++++++++++--- crates/zed/src/zed.rs | 4 +- .../src/styleTree/incomingCallNotification.ts | 2 +- 4 files changed, 54 insertions(+), 8 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index f55dd2b464..765501368c 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -794,6 +794,16 @@ impl AsyncAppContext { self.update(|cx| cx.activate_window(window_id)) } + pub fn prompt( + &mut self, + window_id: usize, + level: PromptLevel, + msg: &str, + answers: &[&str], + ) -> oneshot::Receiver { + self.update(|cx| cx.prompt(window_id, level, msg, answers)) + } + pub fn platform(&self) -> Arc { self.0.borrow().platform() } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index db9ac3a0c6..6472a6f697 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -17,7 +17,7 @@ use client::{proto, Client, PeerId, TypedEnvelope, UserStore}; use collections::{hash_map, HashMap, HashSet}; use dock::{DefaultItemFactory, Dock, ToggleDockButton}; use drag_and_drop::DragAndDrop; -use futures::{channel::oneshot, FutureExt}; +use futures::{channel::oneshot, FutureExt, StreamExt}; use gpui::{ actions, elements::*, @@ -1231,7 +1231,7 @@ impl Workspace { _: &CloseWindow, cx: &mut ViewContext, ) -> Option>> { - let prepare = self.prepare_to_close(cx); + let prepare = self.prepare_to_close(false, cx); Some(cx.spawn(|this, mut cx| async move { if prepare.await? { this.update(&mut cx, |_, cx| { @@ -1243,8 +1243,42 @@ impl Workspace { })) } - pub fn prepare_to_close(&mut self, cx: &mut ViewContext) -> Task> { - self.save_all_internal(true, cx) + pub fn prepare_to_close( + &mut self, + quitting: bool, + cx: &mut ViewContext, + ) -> Task> { + let active_call = ActiveCall::global(cx); + let window_id = cx.window_id(); + let workspace_count = cx + .window_ids() + .flat_map(|window_id| cx.root_view::(window_id)) + .count(); + cx.spawn(|this, mut cx| async move { + if !quitting + && workspace_count == 1 + && active_call.read_with(&cx, |call, _| call.room().is_some()) + { + let answer = cx + .prompt( + window_id, + PromptLevel::Warning, + "Do you want to leave the current call?", + &["Close window and hang up", "Cancel"], + ) + .next() + .await; + if answer == Some(1) { + return anyhow::Ok(false); + } else { + active_call.update(&mut cx, |call, cx| call.hang_up(cx))?; + } + } + + Ok(this + .update(&mut cx, |this, cx| this.save_all_internal(true, cx)) + .await?) + }) } fn save_all(&mut self, _: &SaveAll, cx: &mut ViewContext) -> Option>> { @@ -2944,7 +2978,7 @@ mod tests { // When there are no dirty items, there's nothing to do. let item1 = cx.add_view(&workspace, |_| TestItem::new()); workspace.update(cx, |w, cx| w.add_item(Box::new(item1.clone()), cx)); - let task = workspace.update(cx, |w, cx| w.prepare_to_close(cx)); + let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx)); assert!(task.await.unwrap()); // When there are dirty untitled items, prompt to save each one. If the user @@ -2964,7 +2998,7 @@ mod tests { w.add_item(Box::new(item2.clone()), cx); w.add_item(Box::new(item3.clone()), cx); }); - let task = workspace.update(cx, |w, cx| w.prepare_to_close(cx)); + let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx)); cx.foreground().run_until_parked(); cx.simulate_prompt_answer(window_id, 2 /* cancel */); cx.foreground().run_until_parked(); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index be3d0cfb88..d032e661d7 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -344,7 +344,9 @@ fn quit(_: &Quit, cx: &mut gpui::MutableAppContext) { // If the user cancels any save prompt, then keep the app open. for workspace in workspaces { if !workspace - .update(&mut cx, |workspace, cx| workspace.prepare_to_close(cx)) + .update(&mut cx, |workspace, cx| { + workspace.prepare_to_close(true, cx) + }) .await? { return Ok(()); diff --git a/styles/src/styleTree/incomingCallNotification.ts b/styles/src/styleTree/incomingCallNotification.ts index 30b8ae90ca..55f5cc80fd 100644 --- a/styles/src/styleTree/incomingCallNotification.ts +++ b/styles/src/styleTree/incomingCallNotification.ts @@ -2,7 +2,7 @@ import Theme from "../themes/common/theme"; import { backgroundColor, borderColor, text } from "./components"; export default function incomingCallNotification(theme: Theme): Object { - const avatarSize = 32; + const avatarSize = 48; return { windowHeight: 74, windowWidth: 380, From 4504b36c8f4f9257ff19fc3f6d7172002071cf31 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 11 Oct 2022 15:24:31 +0200 Subject: [PATCH 239/314] Show a different message when participant is active on unshared project Co-Authored-By: Nathan Sobo --- crates/call/src/participant.rs | 19 +++++--- crates/call/src/room.rs | 26 +++++++++-- crates/collab/src/integration_tests.rs | 47 +++++++++++--------- crates/collab/src/rpc/store.rs | 2 +- crates/collab_ui/src/collab_titlebar_item.rs | 25 +++-------- crates/gpui/src/app.rs | 6 +++ crates/rpc/proto/zed.proto | 9 ++-- crates/workspace/src/pane_group.rs | 17 ++++++- 8 files changed, 97 insertions(+), 54 deletions(-) diff --git a/crates/call/src/participant.rs b/crates/call/src/participant.rs index 7e031f907f..a5be5b4af2 100644 --- a/crates/call/src/participant.rs +++ b/crates/call/src/participant.rs @@ -1,28 +1,37 @@ use anyhow::{anyhow, Result}; use client::{proto, User}; +use gpui::WeakModelHandle; +use project::Project; use std::sync::Arc; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum ParticipantLocation { - Project { project_id: u64 }, + SharedProject { project_id: u64 }, + UnsharedProject, External, } impl ParticipantLocation { pub fn from_proto(location: Option) -> Result { match location.and_then(|l| l.variant) { - Some(proto::participant_location::Variant::Project(project)) => Ok(Self::Project { - project_id: project.id, - }), + Some(proto::participant_location::Variant::SharedProject(project)) => { + Ok(Self::SharedProject { + project_id: project.id, + }) + } + Some(proto::participant_location::Variant::UnsharedProject(_)) => { + Ok(Self::UnsharedProject) + } Some(proto::participant_location::Variant::External(_)) => Ok(Self::External), None => Err(anyhow!("participant location was not provided")), } } } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Default)] pub struct LocalParticipant { pub projects: Vec, + pub active_project: Option>, } #[derive(Clone, Debug)] diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index a3b49c2a4f..572f512d1c 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -395,13 +395,26 @@ impl Room { }) .collect(), }); - cx.spawn_weak(|_, mut cx| async move { + cx.spawn(|this, mut cx| async move { let response = request.await?; + project .update(&mut cx, |project, cx| { project.shared(response.project_id, cx) }) .await?; + + // If the user's location is in this project, it changes from UnsharedProject to SharedProject. + this.update(&mut cx, |this, cx| { + let active_project = this.local_participant.active_project.as_ref(); + if active_project.map_or(false, |location| *location == project) { + this.set_location(Some(&project), cx) + } else { + Task::ready(Ok(())) + } + }) + .await?; + Ok(response.project_id) }) } @@ -418,17 +431,22 @@ impl Room { let client = self.client.clone(); let room_id = self.id; let location = if let Some(project) = project { + self.local_participant.active_project = Some(project.downgrade()); if let Some(project_id) = project.read(cx).remote_id() { - proto::participant_location::Variant::Project( - proto::participant_location::Project { id: project_id }, + proto::participant_location::Variant::SharedProject( + proto::participant_location::SharedProject { id: project_id }, ) } else { - return Task::ready(Err(anyhow!("project is not shared"))); + proto::participant_location::Variant::UnsharedProject( + proto::participant_location::UnsharedProject {}, + ) } } else { + self.local_participant.active_project = None; proto::participant_location::Variant::External(proto::participant_location::External {}) }; + cx.notify(); cx.foreground().spawn(async move { client .request(proto::UpdateParticipantLocation { diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 27fb1a9bfb..29d1c1b833 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -946,6 +946,22 @@ async fn test_room_location( } }); + room_a + .update(cx_a, |room, cx| room.set_location(Some(&project_a), cx)) + .await + .unwrap(); + deterministic.run_until_parked(); + assert!(a_notified.take()); + assert_eq!( + participant_locations(&room_a, cx_a), + vec![("user_b".to_string(), ParticipantLocation::External)] + ); + assert!(b_notified.take()); + assert_eq!( + participant_locations(&room_b, cx_b), + vec![("user_a".to_string(), ParticipantLocation::UnsharedProject)] + ); + let project_a_id = active_call_a .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await @@ -959,7 +975,12 @@ async fn test_room_location( assert!(b_notified.take()); assert_eq!( participant_locations(&room_b, cx_b), - vec![("user_a".to_string(), ParticipantLocation::External)] + vec![( + "user_a".to_string(), + ParticipantLocation::SharedProject { + project_id: project_a_id + } + )] ); let project_b_id = active_call_b @@ -973,27 +994,11 @@ async fn test_room_location( vec![("user_b".to_string(), ParticipantLocation::External)] ); assert!(b_notified.take()); - assert_eq!( - participant_locations(&room_b, cx_b), - vec![("user_a".to_string(), ParticipantLocation::External)] - ); - - room_a - .update(cx_a, |room, cx| room.set_location(Some(&project_a), cx)) - .await - .unwrap(); - deterministic.run_until_parked(); - assert!(a_notified.take()); - assert_eq!( - participant_locations(&room_a, cx_a), - vec![("user_b".to_string(), ParticipantLocation::External)] - ); - assert!(b_notified.take()); assert_eq!( participant_locations(&room_b, cx_b), vec![( "user_a".to_string(), - ParticipantLocation::Project { + ParticipantLocation::SharedProject { project_id: project_a_id } )] @@ -1009,7 +1014,7 @@ async fn test_room_location( participant_locations(&room_a, cx_a), vec![( "user_b".to_string(), - ParticipantLocation::Project { + ParticipantLocation::SharedProject { project_id: project_b_id } )] @@ -1019,7 +1024,7 @@ async fn test_room_location( participant_locations(&room_b, cx_b), vec![( "user_a".to_string(), - ParticipantLocation::Project { + ParticipantLocation::SharedProject { project_id: project_a_id } )] @@ -1040,7 +1045,7 @@ async fn test_room_location( participant_locations(&room_b, cx_b), vec![( "user_a".to_string(), - ParticipantLocation::Project { + ParticipantLocation::SharedProject { project_id: project_a_id } )] diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index 9b2661abca..cc34094782 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -684,7 +684,7 @@ impl Store { .rooms .get_mut(&room_id) .ok_or_else(|| anyhow!("no such room"))?; - if let Some(proto::participant_location::Variant::Project(project)) = + if let Some(proto::participant_location::Variant::SharedProject(project)) = location.variant.as_ref() { anyhow::ensure!( diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index c46861b5ab..a2d7249b57 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -121,14 +121,11 @@ impl CollabTitlebarItem { let room = ActiveCall::global(cx).read(cx).room().cloned(); if let Some((workspace, room)) = workspace.zip(room) { let workspace = workspace.read(cx); - let project = if !active { - None - } else if workspace.project().read(cx).remote_id().is_some() { + let project = if active { Some(workspace.project().clone()) } else { None }; - room.update(cx, |room, cx| { room.set_location(project.as_ref(), cx) .detach_and_log_err(cx); @@ -139,20 +136,10 @@ impl CollabTitlebarItem { fn share_project(&mut self, _: &ShareProject, cx: &mut ViewContext) { if let Some(workspace) = self.workspace.upgrade(cx) { let active_call = ActiveCall::global(cx); - - let window_id = cx.window_id(); let project = workspace.read(cx).project().clone(); - let share = active_call.update(cx, |call, cx| call.share_project(project.clone(), cx)); - cx.spawn_weak(|_, mut cx| async move { - share.await?; - if cx.update(|cx| cx.window_is_active(window_id)) { - active_call.update(&mut cx, |call, cx| { - call.set_location(Some(&project), cx).detach_and_log_err(cx); - }); - } - anyhow::Ok(()) - }) - .detach_and_log_err(cx); + active_call + .update(cx, |call, cx| call.share_project(project, cx)) + .detach_and_log_err(cx); } } @@ -363,7 +350,7 @@ impl CollabTitlebarItem { let mut avatar_style; if let Some((_, _, location)) = peer.as_ref() { - if let ParticipantLocation::Project { project_id } = *location { + if let ParticipantLocation::SharedProject { project_id } = *location { if Some(project_id) == workspace.read(cx).project().read(cx).remote_id() { avatar_style = theme.workspace.titlebar.avatar; } else { @@ -428,7 +415,7 @@ impl CollabTitlebarItem { cx, ) .boxed() - } else if let ParticipantLocation::Project { project_id } = location { + } else if let ParticipantLocation::SharedProject { project_id } = location { let user_id = user.id; MouseEventHandler::::new(peer_id.0 as usize, cx, move |_, _| content) .with_cursor_style(CursorStyle::PointingHand) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 765501368c..ed351cdefe 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -4687,6 +4687,12 @@ impl PartialEq for WeakModelHandle { impl Eq for WeakModelHandle {} +impl PartialEq> for WeakModelHandle { + fn eq(&self, other: &ModelHandle) -> bool { + self.model_id == other.model_id + } +} + impl Clone for WeakModelHandle { fn clone(&self) -> Self { Self { diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 2a358113e9..283b11fd78 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -174,14 +174,17 @@ message ParticipantProject { message ParticipantLocation { oneof variant { - Project project = 1; - External external = 2; + SharedProject shared_project = 1; + UnsharedProject unshared_project = 2; + External external = 3; } - message Project { + message SharedProject { uint64 id = 1; } + message UnsharedProject {} + message External {} } diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index 83ce28fdf0..707526c1d6 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -138,7 +138,7 @@ impl Member { border.overlay = true; match leader.location { - call::ParticipantLocation::Project { + call::ParticipantLocation::SharedProject { project_id: leader_project_id, } => { if Some(leader_project_id) == project.read(cx).remote_id() { @@ -183,6 +183,21 @@ impl Member { ) } } + call::ParticipantLocation::UnsharedProject => Some( + Label::new( + format!( + "{} is viewing an unshared Zed project", + leader.user.github_login + ), + theme.workspace.external_location_message.text.clone(), + ) + .contained() + .with_style(theme.workspace.external_location_message.container) + .aligned() + .bottom() + .right() + .boxed(), + ), call::ParticipantLocation::External => Some( Label::new( format!( From eb711cde5381681df11f1ea14411209252a6cf50 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 11 Oct 2022 16:52:20 +0200 Subject: [PATCH 240/314] Polish styling of contacts popover Co-Authored-By: Nathan Sobo --- crates/collab_ui/src/contact_list.rs | 13 +++++-------- styles/src/styleTree/contactFinder.ts | 6 +++--- styles/src/styleTree/contactList.ts | 8 +++----- styles/src/styleTree/contactsPopover.ts | 4 ++-- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/crates/collab_ui/src/contact_list.rs b/crates/collab_ui/src/contact_list.rs index 357b3c65e0..ba7083d604 100644 --- a/crates/collab_ui/src/contact_list.rs +++ b/crates/collab_ui/src/contact_list.rs @@ -1087,14 +1087,11 @@ impl View for ContactList { ) .with_child( MouseEventHandler::::new(0, cx, |_, _| { - Svg::new("icons/user_plus_16.svg") - .with_color(theme.contact_list.add_contact_button.color) - .constrained() - .with_height(16.) - .contained() - .with_style(theme.contact_list.add_contact_button.container) - .aligned() - .boxed() + render_icon_button( + &theme.contact_list.add_contact_button, + "icons/user_plus_16.svg", + ) + .boxed() }) .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, |_, cx| { diff --git a/styles/src/styleTree/contactFinder.ts b/styles/src/styleTree/contactFinder.ts index 2feb3d7e35..103d669df1 100644 --- a/styles/src/styleTree/contactFinder.ts +++ b/styles/src/styleTree/contactFinder.ts @@ -3,7 +3,7 @@ import picker from "./picker"; import { backgroundColor, border, iconColor, player, text } from "./components"; export default function contactFinder(theme: Theme) { - const sideMargin = 12; + const sideMargin = 6; const contactButton = { background: backgroundColor(theme, 100), color: iconColor(theme, "primary"), @@ -33,8 +33,8 @@ export default function contactFinder(theme: Theme) { top: 4, }, margin: { - left: 12, - right: 12, + left: sideMargin, + right: sideMargin, } } }, diff --git a/styles/src/styleTree/contactList.ts b/styles/src/styleTree/contactList.ts index 633d634196..52d5a25c44 100644 --- a/styles/src/styleTree/contactList.ts +++ b/styles/src/styleTree/contactList.ts @@ -53,22 +53,20 @@ export default function contactList(theme: Theme) { top: 4, }, margin: { - left: sidePadding, - right: sidePadding, + left: 6 }, }, userQueryEditorHeight: 33, addContactButton: { - margin: { left: 6, right: 12 }, color: iconColor(theme, "primary"), - buttonWidth: 16, + buttonWidth: 28, iconWidth: 16, }, rowHeight: 28, sectionIconSize: 8, headerRow: { ...text(theme, "mono", "secondary", { size: "sm" }), - margin: { top: 14 }, + margin: { top: 6 }, padding: { left: sidePadding, right: sidePadding, diff --git a/styles/src/styleTree/contactsPopover.ts b/styles/src/styleTree/contactsPopover.ts index 7d699fa26b..957f3d6c8d 100644 --- a/styles/src/styleTree/contactsPopover.ts +++ b/styles/src/styleTree/contactsPopover.ts @@ -8,7 +8,7 @@ export default function contactsPopover(theme: Theme) { padding: { top: 6 }, shadow: popoverShadow(theme), border: border(theme, "primary"), - width: 250, - height: 300, + width: 300, + height: 400, } } From 45d118f96f17b550e3a77dfb0108eb16081bcd5d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 11 Oct 2022 17:05:13 +0200 Subject: [PATCH 241/314] Decide whether to clip to visible bounds on a per-element basis Co-Authored-By: Nathan Sobo --- crates/editor/src/element.rs | 3 ++- crates/gpui/src/elements.rs | 6 ------ crates/gpui/src/elements/flex.rs | 5 +++-- crates/gpui/src/elements/list.rs | 3 ++- crates/gpui/src/elements/mouse_event_handler.rs | 1 + crates/gpui/src/elements/overlay.rs | 6 +++++- crates/gpui/src/elements/uniform_list.rs | 4 +++- crates/terminal/src/terminal_element.rs | 2 ++ 8 files changed, 18 insertions(+), 12 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index acf2e5887c..3b6033cefa 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1729,7 +1729,8 @@ impl Element for EditorElement { layout: &mut Self::LayoutState, cx: &mut PaintContext, ) -> Self::PaintState { - cx.scene.push_layer(Some(bounds)); + let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); + cx.scene.push_layer(Some(visible_bounds)); let gutter_bounds = RectF::new(bounds.origin(), layout.gutter_size); let text_bounds = RectF::new( diff --git a/crates/gpui/src/elements.rs b/crates/gpui/src/elements.rs index b22f0b3250..59269f8af6 100644 --- a/crates/gpui/src/elements.rs +++ b/crates/gpui/src/elements.rs @@ -271,9 +271,6 @@ impl AnyElement for Lifecycle { mut layout, } => { let bounds = RectF::new(origin, size); - let visible_bounds = visible_bounds - .intersection(bounds) - .unwrap_or_else(|| RectF::new(bounds.origin(), Vector2F::default())); let paint = element.paint(bounds, visible_bounds, &mut layout, cx); Lifecycle::PostPaint { element, @@ -292,9 +289,6 @@ impl AnyElement for Lifecycle { .. } => { let bounds = RectF::new(origin, bounds.size()); - let visible_bounds = visible_bounds - .intersection(bounds) - .unwrap_or_else(|| RectF::new(bounds.origin(), Vector2F::default())); let paint = element.paint(bounds, visible_bounds, &mut layout, cx); Lifecycle::PostPaint { element, diff --git a/crates/gpui/src/elements/flex.rs b/crates/gpui/src/elements/flex.rs index 227f946ac6..fd37b001fe 100644 --- a/crates/gpui/src/elements/flex.rs +++ b/crates/gpui/src/elements/flex.rs @@ -241,11 +241,12 @@ impl Element for Flex { remaining_space: &mut Self::LayoutState, cx: &mut PaintContext, ) -> Self::PaintState { - let mut remaining_space = *remaining_space; + let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); + let mut remaining_space = *remaining_space; let overflowing = remaining_space < 0.; if overflowing { - cx.scene.push_layer(Some(bounds)); + cx.scene.push_layer(Some(visible_bounds)); } if let Some(scroll_state) = &self.scroll_state { diff --git a/crates/gpui/src/elements/list.rs b/crates/gpui/src/elements/list.rs index d752a52a16..a6c76cf643 100644 --- a/crates/gpui/src/elements/list.rs +++ b/crates/gpui/src/elements/list.rs @@ -261,7 +261,8 @@ impl Element for List { scroll_top: &mut ListOffset, cx: &mut PaintContext, ) { - cx.scene.push_layer(Some(bounds)); + let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default(); + cx.scene.push_layer(Some(visible_bounds)); cx.scene .push_mouse_region(MouseRegion::new::(10, 0, bounds).on_scroll({ diff --git a/crates/gpui/src/elements/mouse_event_handler.rs b/crates/gpui/src/elements/mouse_event_handler.rs index 5b3b9b13f6..e809c0080f 100644 --- a/crates/gpui/src/elements/mouse_event_handler.rs +++ b/crates/gpui/src/elements/mouse_event_handler.rs @@ -169,6 +169,7 @@ impl Element for MouseEventHandler { _: &mut Self::LayoutState, cx: &mut PaintContext, ) -> Self::PaintState { + let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default(); let hit_bounds = self.hit_bounds(visible_bounds); if let Some(style) = self.cursor_style { cx.scene.push_cursor_region(CursorRegion { diff --git a/crates/gpui/src/elements/overlay.rs b/crates/gpui/src/elements/overlay.rs index d47a39e958..07442d1140 100644 --- a/crates/gpui/src/elements/overlay.rs +++ b/crates/gpui/src/elements/overlay.rs @@ -217,7 +217,11 @@ impl Element for Overlay { )); } - self.child.paint(bounds.origin(), bounds, cx); + self.child.paint( + bounds.origin(), + RectF::new(Vector2F::zero(), cx.window_size), + cx, + ); cx.scene.pop_stacking_context(); } diff --git a/crates/gpui/src/elements/uniform_list.rs b/crates/gpui/src/elements/uniform_list.rs index 6bc35c0692..c9cdbc1b2c 100644 --- a/crates/gpui/src/elements/uniform_list.rs +++ b/crates/gpui/src/elements/uniform_list.rs @@ -284,7 +284,9 @@ impl Element for UniformList { layout: &mut Self::LayoutState, cx: &mut PaintContext, ) -> Self::PaintState { - cx.scene.push_layer(Some(bounds)); + let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default(); + + cx.scene.push_layer(Some(visible_bounds)); cx.scene.push_mouse_region( MouseRegion::new::(self.view_id, 0, visible_bounds).on_scroll({ diff --git a/crates/terminal/src/terminal_element.rs b/crates/terminal/src/terminal_element.rs index 0f037863af..099de23c6d 100644 --- a/crates/terminal/src/terminal_element.rs +++ b/crates/terminal/src/terminal_element.rs @@ -726,6 +726,8 @@ impl Element for TerminalElement { layout: &mut Self::LayoutState, cx: &mut gpui::PaintContext, ) -> Self::PaintState { + let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); + //Setup element stuff let clip_bounds = Some(visible_bounds); From ee2587d3e57225c41650370c377ed661a353fba7 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 11 Oct 2022 17:09:54 +0200 Subject: [PATCH 242/314] Fix integration tests --- crates/collab/src/integration_tests.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 29d1c1b833..bdd240fda8 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -801,18 +801,11 @@ async fn test_host_disconnect( assert!(!cx_b.is_window_edited(workspace_b.window_id())); // Ensure client B is not prompted to save edits when closing window after disconnecting. - workspace_b - .update(cx_b, |workspace, cx| { - workspace.close(&Default::default(), cx) - }) - .unwrap() + let can_close = workspace_b + .update(cx_b, |workspace, cx| workspace.prepare_to_close(true, cx)) .await .unwrap(); - assert_eq!(cx_b.window_ids().len(), 0); - cx_b.update(|_| { - drop(workspace_b); - drop(project_b); - }); + assert!(can_close); } #[gpui::test(iterations = 10)] From 4c07a0782b25d9fd65c6918c40bc1e7dd8501d8b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 11 Oct 2022 17:25:35 +0200 Subject: [PATCH 243/314] Allow active call to be optional on workspace This prepares us for a future where the workspace is unaware of the active call and doesn't require all tests to invoke `call::init`. --- crates/workspace/src/pane_group.rs | 12 ++++--- crates/workspace/src/workspace.rs | 53 +++++++++++++++++++----------- crates/zed/src/zed.rs | 1 + 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index 707526c1d6..f09c31741e 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -60,9 +60,11 @@ impl PaneGroup { project: &ModelHandle, theme: &Theme, follower_states: &FollowerStatesByLeader, + active_call: Option<&ModelHandle>, cx: &mut RenderContext, ) -> ElementBox { - self.root.render(project, theme, follower_states, cx) + self.root + .render(project, theme, follower_states, active_call, cx) } pub(crate) fn panes(&self) -> Vec<&ViewHandle> { @@ -105,6 +107,7 @@ impl Member { project: &ModelHandle, theme: &Theme, follower_states: &FollowerStatesByLeader, + active_call: Option<&ModelHandle>, cx: &mut RenderContext, ) -> ElementBox { enum FollowIntoExternalProject {} @@ -121,7 +124,7 @@ impl Member { } }) .and_then(|leader_id| { - let room = ActiveCall::global(cx).read(cx).room()?.read(cx); + let room = active_call?.read(cx).room()?.read(cx); let collaborator = project.read(cx).collaborators().get(leader_id)?; let participant = room.remote_participants().get(&leader_id)?; Some((collaborator.replica_id, participant)) @@ -223,7 +226,7 @@ impl Member { .with_children(prompt) .boxed() } - Member::Axis(axis) => axis.render(project, theme, follower_states, cx), + Member::Axis(axis) => axis.render(project, theme, follower_states, active_call, cx), } } @@ -328,12 +331,13 @@ impl PaneAxis { project: &ModelHandle, theme: &Theme, follower_state: &FollowerStatesByLeader, + active_call: Option<&ModelHandle>, cx: &mut RenderContext, ) -> ElementBox { let last_member_ix = self.members.len() - 1; Flex::new(self.axis) .with_children(self.members.iter().enumerate().map(|(ix, member)| { - let mut member = member.render(project, theme, follower_state, cx); + let mut member = member.render(project, theme, follower_state, active_call, cx); if ix < last_member_ix { let mut border = theme.workspace.pane_divider; border.left = false; diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 6472a6f697..705823003f 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -980,8 +980,9 @@ pub struct Workspace { follower_states_by_leader: FollowerStatesByLeader, last_leaders_by_pane: HashMap, PeerId>, window_edited: bool, + active_call: Option>, _observe_current_user: Task<()>, - _active_call_observation: gpui::Subscription, + _active_call_observation: Option, } #[derive(Default)] @@ -1090,6 +1091,14 @@ impl Workspace { drag_and_drop.register_container(weak_handle.clone()); }); + let mut active_call = None; + let mut active_call_observation = None; + if cx.has_global::>() { + let call = cx.global::>().clone(); + active_call_observation = Some(cx.observe(&call, |_, _, cx| cx.notify())); + active_call = Some(call); + } + let mut this = Workspace { modal: None, weak_self: weak_handle, @@ -1116,8 +1125,9 @@ impl Workspace { follower_states_by_leader: Default::default(), last_leaders_by_pane: Default::default(), window_edited: false, + active_call, _observe_current_user, - _active_call_observation: cx.observe(&ActiveCall::global(cx), |_, _, cx| cx.notify()), + _active_call_observation: active_call_observation, }; this.project_remote_id_changed(this.project.read(cx).remote_id(), cx); cx.defer(|this, cx| this.update_window_title(cx)); @@ -1248,30 +1258,32 @@ impl Workspace { quitting: bool, cx: &mut ViewContext, ) -> Task> { - let active_call = ActiveCall::global(cx); + let active_call = self.active_call.clone(); let window_id = cx.window_id(); let workspace_count = cx .window_ids() .flat_map(|window_id| cx.root_view::(window_id)) .count(); cx.spawn(|this, mut cx| async move { - if !quitting - && workspace_count == 1 - && active_call.read_with(&cx, |call, _| call.room().is_some()) - { - let answer = cx - .prompt( - window_id, - PromptLevel::Warning, - "Do you want to leave the current call?", - &["Close window and hang up", "Cancel"], - ) - .next() - .await; - if answer == Some(1) { - return anyhow::Ok(false); - } else { - active_call.update(&mut cx, |call, cx| call.hang_up(cx))?; + if let Some(active_call) = active_call { + if !quitting + && workspace_count == 1 + && active_call.read_with(&cx, |call, _| call.room().is_some()) + { + let answer = cx + .prompt( + window_id, + PromptLevel::Warning, + "Do you want to leave the current call?", + &["Close window and hang up", "Cancel"], + ) + .next() + .await; + if answer == Some(1) { + return anyhow::Ok(false); + } else { + active_call.update(&mut cx, |call, cx| call.hang_up(cx))?; + } } } @@ -2571,6 +2583,7 @@ impl View for Workspace { &project, &theme, &self.follower_states_by_leader, + self.active_call.as_ref(), cx, )) .flex(1., true) diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index d032e661d7..f6f3a34242 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1755,6 +1755,7 @@ mod tests { let state = Arc::get_mut(&mut app_state).unwrap(); state.initialize_workspace = initialize_workspace; state.build_window_options = build_window_options; + call::init(app_state.client.clone(), app_state.user_store.clone(), cx); workspace::init(app_state.clone(), cx); editor::init(cx); pane::init(cx); From f83de0a91cfba22609f0784fdff1e13b2a7f1355 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 11 Oct 2022 17:30:17 +0200 Subject: [PATCH 244/314] Respect contacts popover size --- crates/collab_ui/src/collab_titlebar_item.rs | 1 + styles/src/styleTree/contactsPopover.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index a2d7249b57..9faea76a10 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -226,6 +226,7 @@ impl CollabTitlebarItem { ChildView::new(popover) .contained() .with_margin_top(titlebar.height) + .with_margin_left(titlebar.toggle_contacts_button.default.button_width) .with_margin_right(-titlebar.toggle_contacts_button.default.button_width) .boxed(), ) diff --git a/styles/src/styleTree/contactsPopover.ts b/styles/src/styleTree/contactsPopover.ts index 957f3d6c8d..2e70b3daea 100644 --- a/styles/src/styleTree/contactsPopover.ts +++ b/styles/src/styleTree/contactsPopover.ts @@ -6,6 +6,7 @@ export default function contactsPopover(theme: Theme) { background: backgroundColor(theme, 300, "base"), cornerRadius: 6, padding: { top: 6 }, + margin: { top: -6 }, shadow: popoverShadow(theme), border: border(theme, "primary"), width: 300, From e2700ff8c6d9a3f69520d2c3f65e5f204348aa45 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 10 Oct 2022 18:12:31 -0700 Subject: [PATCH 245/314] Enable/disable scrollbar auto-hide based on OS setting --- crates/editor/src/editor.rs | 29 ++++++++++++++++-------- crates/gpui/src/platform.rs | 1 + crates/gpui/src/platform/mac/platform.rs | 10 ++++++++ crates/gpui/src/platform/test.rs | 4 ++++ 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 3422e599f8..2b1d99a24c 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -239,6 +239,9 @@ pub enum Direction { Next, } +#[derive(Default)] +struct ScrollbarAutoHide(bool); + pub fn init(cx: &mut MutableAppContext) { cx.add_action(Editor::new_file); cx.add_action(|this: &mut Editor, action: &Scroll, cx| this.set_scroll_position(action.0, cx)); @@ -327,6 +330,10 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_async_action(Editor::confirm_rename); cx.add_async_action(Editor::find_all_references); + cx.set_global(ScrollbarAutoHide( + cx.platform().should_auto_hide_scrollbars(), + )); + hover_popover::init(cx); link_go_to_definition::init(cx); mouse_context_menu::init(cx); @@ -5949,15 +5956,19 @@ impl Editor { cx.notify(); } - self.hide_scrollbar_task = Some(cx.spawn_weak(|this, mut cx| async move { - Timer::after(SCROLLBAR_SHOW_INTERVAL).await; - if let Some(this) = this.upgrade(&cx) { - this.update(&mut cx, |this, cx| { - this.show_scrollbars = false; - cx.notify(); - }); - } - })); + if cx.default_global::().0 { + self.hide_scrollbar_task = Some(cx.spawn_weak(|this, mut cx| async move { + Timer::after(SCROLLBAR_SHOW_INTERVAL).await; + if let Some(this) = this.upgrade(&cx) { + this.update(&mut cx, |this, cx| { + this.show_scrollbars = false; + cx.notify(); + }); + } + })); + } else { + self.hide_scrollbar_task = None; + } } fn on_buffer_changed(&mut self, _: ModelHandle, cx: &mut ViewContext) { diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 8997bde527..70d6f1e7d1 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -63,6 +63,7 @@ pub trait Platform: Send + Sync { fn delete_credentials(&self, url: &str) -> Result<()>; fn set_cursor_style(&self, style: CursorStyle); + fn should_auto_hide_scrollbars(&self) -> bool; fn local_timezone(&self) -> UtcOffset; diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 628ddde13c..619ba0f3bf 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -699,6 +699,16 @@ impl platform::Platform for MacPlatform { } } + fn should_auto_hide_scrollbars(&self) -> bool { + #[allow(non_upper_case_globals)] + const NSScrollerStyleOverlay: NSInteger = 1; + + unsafe { + let style: NSInteger = msg_send![class!(NSScroller), preferredScrollerStyle]; + style == NSScrollerStyleOverlay + } + } + fn local_timezone(&self) -> UtcOffset { unsafe { let local_timezone: id = msg_send![class!(NSTimeZone), localTimeZone]; diff --git a/crates/gpui/src/platform/test.rs b/crates/gpui/src/platform/test.rs index 58ef1ffaf2..0dbe011d7b 100644 --- a/crates/gpui/src/platform/test.rs +++ b/crates/gpui/src/platform/test.rs @@ -177,6 +177,10 @@ impl super::Platform for Platform { *self.cursor.lock() = style; } + fn should_auto_hide_scrollbars(&self) -> bool { + false + } + fn local_timezone(&self) -> UtcOffset { UtcOffset::UTC } From ba6c5441c0eb1edfccc29e453f33d68cb6f3b965 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 11 Oct 2022 18:22:00 +0200 Subject: [PATCH 246/314] Always show invite link in contacts popover --- crates/collab_ui/src/contact_list.rs | 54 +---------------------- crates/collab_ui/src/contacts_popover.rs | 56 ++++++++++++++++++++++-- crates/theme/src/theme.rs | 3 +- styles/src/styleTree/contactList.ts | 11 ----- styles/src/styleTree/contactsPopover.ts | 15 ++++++- 5 files changed, 71 insertions(+), 68 deletions(-) diff --git a/crates/collab_ui/src/contact_list.rs b/crates/collab_ui/src/contact_list.rs index ba7083d604..c2736ea9d3 100644 --- a/crates/collab_ui/src/contact_list.rs +++ b/crates/collab_ui/src/contact_list.rs @@ -8,9 +8,8 @@ use fuzzy::{match_strings, StringMatchCandidate}; use gpui::{ elements::*, geometry::{rect::RectF, vector::vec2f}, - impl_actions, impl_internal_actions, keymap, AppContext, ClipboardItem, CursorStyle, Entity, - ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, View, ViewContext, - ViewHandle, + impl_actions, impl_internal_actions, keymap, AppContext, CursorStyle, Entity, ModelHandle, + MouseButton, MutableAppContext, RenderContext, Subscription, View, ViewContext, ViewHandle, }; use menu::{Confirm, SelectNext, SelectPrev}; use project::Project; @@ -1104,55 +1103,6 @@ impl View for ContactList { .boxed(), ) .with_child(List::new(self.list_state.clone()).flex(1., false).boxed()) - .with_children( - self.user_store - .read(cx) - .invite_info() - .cloned() - .and_then(|info| { - enum InviteLink {} - - if info.count > 0 { - Some( - MouseEventHandler::::new(0, cx, |state, cx| { - let style = theme - .contact_list - .invite_row - .style_for(state, false) - .clone(); - - let copied = cx.read_from_clipboard().map_or(false, |item| { - item.text().as_str() == info.url.as_ref() - }); - - Label::new( - format!( - "{} invite link ({} left)", - if copied { "Copied" } else { "Copy" }, - info.count - ), - style.label.clone(), - ) - .aligned() - .left() - .constrained() - .with_height(theme.contact_list.row_height) - .contained() - .with_style(style.container) - .boxed() - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, cx| { - cx.write_to_clipboard(ClipboardItem::new(info.url.to_string())); - cx.notify(); - }) - .boxed(), - ) - } else { - None - } - }), - ) .boxed() } diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index 0758cf735d..deedac9f98 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -1,8 +1,8 @@ use crate::{contact_finder::ContactFinder, contact_list::ContactList}; use client::UserStore; use gpui::{ - actions, elements::*, Entity, ModelHandle, MutableAppContext, RenderContext, View, ViewContext, - ViewHandle, + actions, elements::*, ClipboardItem, CursorStyle, Entity, ModelHandle, MouseButton, + MutableAppContext, RenderContext, View, ViewContext, ViewHandle, }; use project::Project; use settings::Settings; @@ -92,7 +92,57 @@ impl View for ContactsPopover { Child::ContactFinder(child) => ChildView::new(child), }; - child + Flex::column() + .with_child(child.flex(1., true).boxed()) + .with_children( + self.user_store + .read(cx) + .invite_info() + .cloned() + .and_then(|info| { + enum InviteLink {} + + if info.count > 0 { + Some( + MouseEventHandler::::new(0, cx, |state, cx| { + let style = theme + .contacts_popover + .invite_row + .style_for(state, false) + .clone(); + + let copied = cx.read_from_clipboard().map_or(false, |item| { + item.text().as_str() == info.url.as_ref() + }); + + Label::new( + format!( + "{} invite link ({} left)", + if copied { "Copied" } else { "Copy" }, + info.count + ), + style.label.clone(), + ) + .aligned() + .left() + .constrained() + .with_height(theme.contacts_popover.invite_row_height) + .contained() + .with_style(style.container) + .boxed() + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, cx| { + cx.write_to_clipboard(ClipboardItem::new(info.url.to_string())); + cx.notify(); + }) + .boxed(), + ) + } else { + None + } + }), + ) .contained() .with_style(theme.contacts_popover.container) .constrained() diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 503645d6bc..37ec279d02 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -89,6 +89,8 @@ pub struct ContactsPopover { pub container: ContainerStyle, pub height: f32, pub width: f32, + pub invite_row_height: f32, + pub invite_row: Interactive, } #[derive(Deserialize, Default)] @@ -110,7 +112,6 @@ pub struct ContactList { pub contact_button_spacing: f32, pub disabled_button: IconButton, pub section_icon_size: f32, - pub invite_row: Interactive, pub calling_indicator: ContainedText, } diff --git a/styles/src/styleTree/contactList.ts b/styles/src/styleTree/contactList.ts index 52d5a25c44..ecf0eaa0c7 100644 --- a/styles/src/styleTree/contactList.ts +++ b/styles/src/styleTree/contactList.ts @@ -139,17 +139,6 @@ export default function contactList(theme: Theme) { background: backgroundColor(theme, 100), color: iconColor(theme, "muted"), }, - inviteRow: { - padding: { - left: sidePadding, - right: sidePadding, - }, - border: { top: true, width: 1, color: borderColor(theme, "primary") }, - text: text(theme, "sans", "secondary", { size: "sm" }), - hover: { - text: text(theme, "sans", "active", { size: "sm" }), - }, - }, callingIndicator: { ...text(theme, "mono", "muted", { size: "xs" }) }, diff --git a/styles/src/styleTree/contactsPopover.ts b/styles/src/styleTree/contactsPopover.ts index 2e70b3daea..0f82c7c175 100644 --- a/styles/src/styleTree/contactsPopover.ts +++ b/styles/src/styleTree/contactsPopover.ts @@ -1,7 +1,8 @@ import Theme from "../themes/common/theme"; -import { backgroundColor, border, popoverShadow } from "./components"; +import { backgroundColor, border, borderColor, popoverShadow, text } from "./components"; export default function contactsPopover(theme: Theme) { + const sidePadding = 12; return { background: backgroundColor(theme, 300, "base"), cornerRadius: 6, @@ -11,5 +12,17 @@ export default function contactsPopover(theme: Theme) { border: border(theme, "primary"), width: 300, height: 400, + inviteRowHeight: 28, + inviteRow: { + padding: { + left: sidePadding, + right: sidePadding, + }, + border: { top: true, width: 1, color: borderColor(theme, "primary") }, + text: text(theme, "sans", "secondary", { size: "sm" }), + hover: { + text: text(theme, "sans", "active", { size: "sm" }), + }, + }, } } From 67a32de7d427dc48d7a87115022694a1ed34e1ee Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 11 Oct 2022 09:26:19 -0700 Subject: [PATCH 247/314] Hide the scrollbar track, not just the thumb --- crates/editor/src/element.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 66dbe50864..04c67311de 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -931,13 +931,13 @@ impl EditorElement { let track_bounds = RectF::from_points(vec2f(left, top), vec2f(right, bottom)); let thumb_bounds = RectF::from_points(vec2f(left, thumb_top), vec2f(right, thumb_bottom)); - cx.scene.push_quad(Quad { - bounds: track_bounds, - border: style.track.border, - background: style.track.background_color, - ..Default::default() - }); if layout.show_scrollbars { + cx.scene.push_quad(Quad { + bounds: track_bounds, + border: style.track.border, + background: style.track.background_color, + ..Default::default() + }); cx.scene.push_quad(Quad { bounds: thumb_bounds, border: style.thumb.border, From d93e75bf5f119bbb6cc0c6ed08e1ae42398e4b16 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 11 Oct 2022 09:26:31 -0700 Subject: [PATCH 248/314] Make scrollbars a little bit narrower --- styles/src/styleTree/editor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/styles/src/styleTree/editor.ts b/styles/src/styleTree/editor.ts index a31b3d8654..334931a981 100644 --- a/styles/src/styleTree/editor.ts +++ b/styles/src/styleTree/editor.ts @@ -172,7 +172,7 @@ export default function editor(theme: Theme) { }, }, scrollbar: { - width: 14, + width: 12, track: { border: { left: true, From f4306d977f87bfe918524a18ade3e04469d0fc74 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 11 Oct 2022 09:28:17 -0700 Subject: [PATCH 249/314] Refresh scrollbar auto-hide setting when opening a new editor --- crates/editor/src/editor.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 2b1d99a24c..8df8a29360 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -330,10 +330,6 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_async_action(Editor::confirm_rename); cx.add_async_action(Editor::find_all_references); - cx.set_global(ScrollbarAutoHide( - cx.platform().should_auto_hide_scrollbars(), - )); - hover_popover::init(cx); link_go_to_definition::init(cx); mouse_context_menu::init(cx); @@ -1076,6 +1072,11 @@ impl Editor { let editor_created_event = EditorCreated(cx.handle()); cx.emit_global(editor_created_event); + if mode == EditorMode::Full { + let should_auto_hide_scrollbars = cx.platform().should_auto_hide_scrollbars(); + cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars)); + } + this.report_event("open editor", cx); this } From f26695ea8cd8f8211cd989abdd005e5fc3d6c829 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 11 Oct 2022 18:34:04 +0200 Subject: [PATCH 250/314] :lipstick: --- crates/collab_ui/src/contact_list.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/crates/collab_ui/src/contact_list.rs b/crates/collab_ui/src/contact_list.rs index c2736ea9d3..7b773240cf 100644 --- a/crates/collab_ui/src/contact_list.rs +++ b/crates/collab_ui/src/contact_list.rs @@ -776,8 +776,8 @@ impl ContactList { let header_style = theme.header_row.style_for(Default::default(), is_selected); let text = match section { - Section::ActiveCall => "Call", - Section::Requests => "Requests", + Section::ActiveCall => "Collaborators", + Section::Requests => "Contact Requests", Section::Online => "Online", Section::Offline => "Offline", }; @@ -785,7 +785,7 @@ impl ContactList { Some( MouseEventHandler::::new(0, cx, |state, _| { let style = theme.leave_call.style_for(state, false); - Label::new("Leave".into(), style.text.clone()) + Label::new("Leave Session".into(), style.text.clone()) .contained() .with_style(style.container) .boxed() @@ -1096,6 +1096,13 @@ impl View for ContactList { .on_click(MouseButton::Left, |_, cx| { cx.dispatch_action(contacts_popover::ToggleContactFinder) }) + .with_tooltip::( + 0, + "Add contact".into(), + None, + theme.tooltip.clone(), + cx, + ) .boxed(), ) .constrained() From 1f0a9ce418c822115cc0f0f0439d033b2073b0fd Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 11 Oct 2022 10:17:20 -0700 Subject: [PATCH 251/314] Proceed with share while project state uploads Co-authored-by: Antonio Scandurra --- crates/call/src/room.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 572f512d1c..5003479214 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -398,11 +398,11 @@ impl Room { cx.spawn(|this, mut cx| async move { let response = request.await?; - project - .update(&mut cx, |project, cx| { - project.shared(response.project_id, cx) - }) - .await?; + project.update(&mut cx, |project, cx| { + project + .shared(response.project_id, cx) + .detach_and_log_err(cx) + }); // If the user's location is in this project, it changes from UnsharedProject to SharedProject. this.update(&mut cx, |this, cx| { From 0e695eaae864797710821f909a6be5051d97ad6e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 11 Oct 2022 12:15:54 -0700 Subject: [PATCH 252/314] Wait for project sharing to complete in LSP status integration test --- crates/collab/src/integration_tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index bdd240fda8..6e08e7bef5 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -3874,6 +3874,7 @@ async fn test_language_server_statuses( .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); + deterministic.run_until_parked(); let project_b = client_b.build_remote_project(project_id, cx_b).await; project_b.read_with(cx_b, |project, _| { let status = project.language_server_statuses().next().unwrap(); From 65b8c512fed0d21ae55e35b02208a11dc2144018 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 11 Oct 2022 12:37:00 -0700 Subject: [PATCH 253/314] Allow opening other local projects via contacts list --- crates/collab_ui/src/contact_list.rs | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/crates/collab_ui/src/contact_list.rs b/crates/collab_ui/src/contact_list.rs index 7b773240cf..c04f0fe72d 100644 --- a/crates/collab_ui/src/contact_list.rs +++ b/crates/collab_ui/src/contact_list.rs @@ -65,7 +65,6 @@ enum ContactEntry { project_id: u64, worktree_root_names: Vec, host_user_id: u64, - is_host: bool, is_last: bool, }, IncomingRequest(Arc), @@ -181,6 +180,7 @@ impl ContactList { let list_state = ListState::new(0, Orientation::Top, 1000., cx, move |this, ix, cx| { let theme = cx.global::().theme.clone(); let is_selected = this.selection == Some(ix); + let current_project_id = this.project.read(cx).remote_id(); match &this.entries[ix] { ContactEntry::Header(section) => { @@ -205,13 +205,12 @@ impl ContactList { project_id, worktree_root_names, host_user_id, - is_host, is_last, } => Self::render_participant_project( *project_id, worktree_root_names, *host_user_id, - *is_host, + Some(*project_id) == current_project_id, *is_last, is_selected, &theme.contact_list, @@ -341,15 +340,12 @@ impl ContactList { ContactEntry::ParticipantProject { project_id, host_user_id, - is_host, .. } => { - if !is_host { - cx.dispatch_global_action(JoinProject { - project_id: *project_id, - follow_user_id: *host_user_id, - }); - } + cx.dispatch_global_action(JoinProject { + project_id: *project_id, + follow_user_id: *host_user_id, + }); } _ => {} } @@ -407,7 +403,6 @@ impl ContactList { project_id: project.id, worktree_root_names: project.worktree_root_names.clone(), host_user_id: user_id, - is_host: true, is_last: projects.peek().is_none(), }); } @@ -448,7 +443,6 @@ impl ContactList { project_id: project.id, worktree_root_names: project.worktree_root_names.clone(), host_user_id: participant.user.id, - is_host: false, is_last: projects.peek().is_none(), }); } @@ -667,7 +661,7 @@ impl ContactList { project_id: u64, worktree_root_names: &[String], host_user_id: u64, - is_host: bool, + is_current: bool, is_last: bool, is_selected: bool, theme: &theme::ContactList, @@ -749,13 +743,13 @@ impl ContactList { .with_style(row.container) .boxed() }) - .with_cursor_style(if !is_host { + .with_cursor_style(if !is_current { CursorStyle::PointingHand } else { CursorStyle::Arrow }) .on_click(MouseButton::Left, move |_, cx| { - if !is_host { + if !is_current { cx.dispatch_global_action(JoinProject { project_id, follow_user_id: host_user_id, From cc8ae45012cf3dee93362e6492613629ac436d4d Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 10 Oct 2022 17:10:42 -0700 Subject: [PATCH 254/314] Added theme writing code, really bad race condition --- Cargo.lock | 14 +- Cargo.toml | 5 + constellation.txt | 10 + crates/editor/Cargo.toml | 2 +- crates/paths/Cargo.toml | 12 ++ crates/{zed => paths}/src/paths.rs | 0 crates/settings/Cargo.toml | 10 +- crates/settings/src/settings.rs | 194 +++++++++++++++++++- crates/theme_selector/Cargo.toml | 2 + crates/theme_selector/src/theme_selector.rs | 38 +++- crates/zed/Cargo.toml | 2 +- crates/zed/src/main.rs | 39 ++-- crates/zed/src/zed.rs | 1 - 13 files changed, 297 insertions(+), 32 deletions(-) create mode 100644 constellation.txt create mode 100644 crates/paths/Cargo.toml rename crates/{zed => paths}/src/paths.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 0448538c35..177b64c2ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3738,6 +3738,14 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "paths" +version = "0.1.0" +dependencies = [ + "dirs 3.0.2", + "lazy_static", +] + [[package]] name = "pbkdf2" version = "0.8.0" @@ -5021,6 +5029,9 @@ dependencies = [ "serde_path_to_error", "theme", "toml", + "tree-sitter", + "tree-sitter-json 0.19.0", + "unindent", "util", ] @@ -5626,6 +5637,7 @@ dependencies = [ "gpui", "log", "parking_lot 0.11.2", + "paths", "picker", "postage", "settings", @@ -7251,7 +7263,6 @@ dependencies = [ "context_menu", "ctor", "diagnostics", - "dirs 3.0.2", "easy-parallel", "editor", "env_logger", @@ -7274,6 +7285,7 @@ dependencies = [ "num_cpus", "outline", "parking_lot 0.11.2", + "paths", "plugin_runtime", "postage", "project", diff --git a/Cargo.toml b/Cargo.toml index 31a9118a1a..b64df300c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,10 @@ members = ["crates/*"] default-members = ["crates/zed"] resolver = "2" +[workspace.dependencies] +serde = { version = "1.0", features = ["derive", "rc"] } +serde_json = { version = "1.0", features = ["preserve_order", "raw_value"] } + [patch.crates-io] tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "366210ae925d7ea0891bc7a0c738f60c77c04d7b" } async-task = { git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e" } @@ -21,3 +25,4 @@ split-debuginfo = "unpacked" [profile.release] debug = true + diff --git a/constellation.txt b/constellation.txt new file mode 100644 index 0000000000..b8b8f77e03 --- /dev/null +++ b/constellation.txt @@ -0,0 +1,10 @@ +Burn down pain points constellation + +--------------------- +| | +| * | Mikayla +| * | Julia +| | +| * | Kay +| | +--------------------- \ No newline at end of file diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index e8695e5ddc..db634376d0 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -48,7 +48,7 @@ ordered-float = "2.1.1" parking_lot = "0.11" postage = { version = "0.4", features = ["futures-traits"] } rand = { version = "0.8.3", optional = true } -serde = { version = "1.0", features = ["derive", "rc"] } +serde = { workspace = true } smallvec = { version = "1.6", features = ["union"] } smol = "1.2" tree-sitter-rust = { version = "*", optional = true } diff --git a/crates/paths/Cargo.toml b/crates/paths/Cargo.toml new file mode 100644 index 0000000000..3da33cce1b --- /dev/null +++ b/crates/paths/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "paths" +version = "0.1.0" +edition = "2021" + +[lib] +path = "src/paths.rs" + +[dependencies] +lazy_static = "1.4.0" +dirs = "3.0" + diff --git a/crates/zed/src/paths.rs b/crates/paths/src/paths.rs similarity index 100% rename from crates/zed/src/paths.rs rename to crates/paths/src/paths.rs diff --git a/crates/settings/Cargo.toml b/crates/settings/Cargo.toml index 78440a2418..e8fb58cd61 100644 --- a/crates/settings/Cargo.toml +++ b/crates/settings/Cargo.toml @@ -19,7 +19,13 @@ util = { path = "../util" } anyhow = "1.0.38" json_comments = "0.2" schemars = "0.8" -serde = { version = "1.0", features = ["derive", "rc"] } -serde_json = { version = "1.0", features = ["preserve_order"] } +serde = { workspace = true } +serde_json = { workspace = true } serde_path_to_error = "0.1.4" toml = "0.5" +tree-sitter = "*" +tree-sitter-json = "*" + +[dev-dependencies] +unindent = "0.1" +gpui = { path = "../gpui", features = ["test-support"] } diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index d661eb0f21..921b36051f 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -12,8 +12,9 @@ use schemars::{ }; use serde::{de::DeserializeOwned, Deserialize}; use serde_json::Value; -use std::{collections::HashMap, num::NonZeroU32, str, sync::Arc}; +use std::{collections::HashMap, fmt::Write as _, num::NonZeroU32, str, sync::Arc}; use theme::{Theme, ThemeRegistry}; +use tree_sitter::Query; use util::ResultExt as _; pub use keymap_file::{keymap_file_json_schema, KeymapFileContent}; @@ -501,6 +502,92 @@ pub fn settings_file_json_schema( serde_json::to_value(root_schema).unwrap() } +pub fn write_theme(mut settings_content: String, new_val: &str) -> String { + let mut parser = tree_sitter::Parser::new(); + parser.set_language(tree_sitter_json::language()).unwrap(); + let tree = parser.parse(&settings_content, None).unwrap(); + + let mut cursor = tree_sitter::QueryCursor::new(); + + let query = Query::new( + tree_sitter_json::language(), + " + (document + (object + (pair + key: (string) @key + value: (_) @value))) + ", + ) + .unwrap(); + + let mut first_key_start = None; + let mut existing_value_range = None; + let matches = cursor.matches(&query, tree.root_node(), settings_content.as_bytes()); + for mat in matches { + if mat.captures.len() != 2 { + continue; + } + + let key = mat.captures[0]; + let value = mat.captures[1]; + + first_key_start.get_or_insert_with(|| key.node.start_byte()); + + if let Some(key_text) = settings_content.get(key.node.byte_range()) { + if key_text == "\"theme\"" { + existing_value_range = Some(value.node.byte_range()); + break; + } + } + } + + match (first_key_start, existing_value_range) { + (None, None) => { + // No document, create a new object and overwrite + settings_content.clear(); + write!(settings_content, "{{\n \"theme\": \"{new_val}\"\n}}\n").unwrap(); + } + + (_, Some(existing_value_range)) => { + // Existing theme key, overwrite + settings_content.replace_range(existing_value_range, &format!("\"{new_val}\"")); + } + + (Some(first_key_start), None) => { + // No existing theme key, but other settings. Prepend new theme settings and + // match style of first key + let mut row = 0; + let mut column = 0; + for (ix, char) in settings_content.char_indices() { + if ix == first_key_start { + break; + } + if char == '\n' { + row += 1; + column = 0; + } else { + column += char.len_utf8(); + } + } + + let content = format!(r#""theme": "{new_val}","#); + settings_content.insert_str(first_key_start, &content); + + if row > 0 { + settings_content.insert_str( + first_key_start + content.len(), + &format!("\n{:width$}", ' ', width = column), + ) + } else { + settings_content.insert_str(first_key_start + content.len(), " ") + } + } + } + + settings_content +} + fn merge(target: &mut T, value: Option) { if let Some(value) = value { *target = value; @@ -512,3 +599,108 @@ pub fn parse_json_with_comments(content: &str) -> Result json_comments::CommentSettings::c_style().strip_comments(content.as_bytes()), )?) } + +#[cfg(test)] +mod tests { + use crate::write_theme; + use unindent::Unindent; + + #[test] + fn test_write_theme_into_settings_with_theme() { + let settings = r#" + { + "theme": "one-dark" + } + "# + .unindent(); + + let new_settings = r#" + { + "theme": "summerfruit-light" + } + "# + .unindent(); + + let settings_after_theme = write_theme(settings, "summerfruit-light"); + + assert_eq!(settings_after_theme, new_settings) + } + + #[test] + fn test_write_theme_into_empty_settings() { + let settings = r#" + { + } + "# + .unindent(); + + let new_settings = r#" + { + "theme": "summerfruit-light" + } + "# + .unindent(); + + let settings_after_theme = write_theme(settings, "summerfruit-light"); + + assert_eq!(settings_after_theme, new_settings) + } + + #[test] + fn test_write_theme_into_no_settings() { + let settings = "".to_string(); + + let new_settings = r#" + { + "theme": "summerfruit-light" + } + "# + .unindent(); + + let settings_after_theme = write_theme(settings, "summerfruit-light"); + + assert_eq!(settings_after_theme, new_settings) + } + + #[test] + fn test_write_theme_into_single_line_settings_without_theme() { + let settings = r#"{ "a": "", "ok": true }"#.to_string(); + let new_settings = r#"{ "theme": "summerfruit-light", "a": "", "ok": true }"#; + + let settings_after_theme = write_theme(settings, "summerfruit-light"); + + assert_eq!(settings_after_theme, new_settings) + } + + #[test] + fn test_write_theme_pre_object_whitespace() { + let settings = r#" { "a": "", "ok": true }"#.to_string(); + let new_settings = r#" { "theme": "summerfruit-light", "a": "", "ok": true }"#; + + let settings_after_theme = write_theme(settings, "summerfruit-light"); + + assert_eq!(settings_after_theme, new_settings) + } + + #[test] + fn test_write_theme_into_multi_line_settings_without_theme() { + let settings = r#" + { + "a": "b" + } + "# + .unindent(); + + let new_settings = r#" + { + "theme": "summerfruit-light", + "a": "b" + } + "# + .unindent(); + + let settings_after_theme = write_theme(settings, "summerfruit-light"); + + assert_eq!(settings_after_theme, new_settings) + } +} diff --git a/crates/theme_selector/Cargo.toml b/crates/theme_selector/Cargo.toml index 804eff2c7a..e323677d3b 100644 --- a/crates/theme_selector/Cargo.toml +++ b/crates/theme_selector/Cargo.toml @@ -14,8 +14,10 @@ gpui = { path = "../gpui" } picker = { path = "../picker" } theme = { path = "../theme" } settings = { path = "../settings" } +paths = { path = "../paths" } workspace = { path = "../workspace" } log = { version = "0.4.16", features = ["kv_unstable_serde"] } parking_lot = "0.11.1" postage = { version = "0.4.1", features = ["futures-traits"] } smol = "1.2.5" + diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index 59b0bc7e6a..a7d17ad533 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -1,10 +1,12 @@ use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; use gpui::{ - actions, elements::*, AnyViewHandle, AppContext, Element, ElementBox, Entity, MouseState, - MutableAppContext, RenderContext, View, ViewContext, ViewHandle, + actions, anyhow::Result, elements::*, AnyViewHandle, AppContext, Element, ElementBox, Entity, + MouseState, MutableAppContext, RenderContext, View, ViewContext, ViewHandle, }; +use paths::SETTINGS; use picker::{Picker, PickerDelegate}; -use settings::Settings; +use settings::{write_theme, Settings}; +use smol::{fs::read_to_string, io::AsyncWriteExt}; use std::sync::Arc; use theme::{Theme, ThemeMeta, ThemeRegistry}; use workspace::{AppState, Workspace}; @@ -107,7 +109,20 @@ impl ThemeSelector { fn show_selected_theme(&mut self, cx: &mut ViewContext) { if let Some(mat) = self.matches.get(self.selected_index) { match self.registry.get(&mat.string) { - Ok(theme) => Self::set_theme(theme, cx), + Ok(theme) => { + Self::set_theme(theme, cx); + + let theme_name = mat.string.clone(); + + cx.background() + .spawn(async move { + match write_theme_name(theme_name).await { + Ok(_) => {} + Err(_) => return, //TODO Pop toast + } + }) + .detach() + } Err(error) => { log::error!("error loading theme {}: {}", mat.string, error) } @@ -264,3 +279,18 @@ impl View for ThemeSelector { } } } + +async fn write_theme_name(theme_name: String) -> Result<()> { + let mut settings = read_to_string(SETTINGS.as_path()).await?; + settings = write_theme(settings, &theme_name); + + let mut file = smol::fs::OpenOptions::new() + .truncate(true) + .write(true) + .open(SETTINGS.as_path()) + .await?; + + file.write_all(settings.as_bytes()).await?; + + Ok(()) +} diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index d0b41b08f1..d57a2c21b1 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -40,6 +40,7 @@ journal = { path = "../journal" } language = { path = "../language" } lsp = { path = "../lsp" } outline = { path = "../outline" } +paths = { path = "../paths" } plugin_runtime = { path = "../plugin_runtime" } project = { path = "../project" } project_panel = { path = "../project_panel" } @@ -61,7 +62,6 @@ async-trait = "0.1" backtrace = "0.3" chrono = "0.4" ctor = "0.1.20" -dirs = "3.0" easy-parallel = "3.1.0" env_logger = "0.9" futures = "0.3" diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index f48f8b723e..519f04fe35 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -53,7 +53,7 @@ fn main() { .map_or("dev".to_string(), |v| v.to_string()); init_panic_hook(app_version, http.clone(), app.background()); let db = app.background().spawn(async move { - project::Db::open(&*zed::paths::DB) + project::Db::open(&*paths::DB) .log_err() .unwrap_or_else(project::Db::null) }); @@ -90,7 +90,7 @@ fn main() { app.run(move |cx| { let client = client::Client::new(http.clone(), cx); let mut languages = LanguageRegistry::new(login_shell_env_loaded); - languages.set_language_server_download_dir(zed::paths::LANGUAGES_DIR.clone()); + languages.set_language_server_download_dir(paths::LANGUAGES_DIR.clone()); let languages = Arc::new(languages); let init_languages = cx .background() @@ -200,23 +200,21 @@ fn main() { } fn init_paths() { - fs::create_dir_all(&*zed::paths::CONFIG_DIR).expect("could not create config path"); - fs::create_dir_all(&*zed::paths::LANGUAGES_DIR).expect("could not create languages path"); - fs::create_dir_all(&*zed::paths::DB_DIR).expect("could not create database path"); - fs::create_dir_all(&*zed::paths::LOGS_DIR).expect("could not create logs path"); + fs::create_dir_all(&*paths::CONFIG_DIR).expect("could not create config path"); + fs::create_dir_all(&*paths::LANGUAGES_DIR).expect("could not create languages path"); + fs::create_dir_all(&*paths::DB_DIR).expect("could not create database path"); + fs::create_dir_all(&*paths::LOGS_DIR).expect("could not create logs path"); // Copy setting files from legacy locations. TODO: remove this after a few releases. thread::spawn(|| { - if fs::metadata(&*zed::paths::legacy::SETTINGS).is_ok() - && fs::metadata(&*zed::paths::SETTINGS).is_err() + if fs::metadata(&*paths::legacy::SETTINGS).is_ok() + && fs::metadata(&*paths::SETTINGS).is_err() { - fs::copy(&*zed::paths::legacy::SETTINGS, &*zed::paths::SETTINGS).log_err(); + fs::copy(&*paths::legacy::SETTINGS, &*paths::SETTINGS).log_err(); } - if fs::metadata(&*zed::paths::legacy::KEYMAP).is_ok() - && fs::metadata(&*zed::paths::KEYMAP).is_err() - { - fs::copy(&*zed::paths::legacy::KEYMAP, &*zed::paths::KEYMAP).log_err(); + if fs::metadata(&*paths::legacy::KEYMAP).is_ok() && fs::metadata(&*paths::KEYMAP).is_err() { + fs::copy(&*paths::legacy::KEYMAP, &*paths::KEYMAP).log_err(); } }); } @@ -231,15 +229,14 @@ fn init_logger() { const KIB: u64 = 1024; const MIB: u64 = 1024 * KIB; const MAX_LOG_BYTES: u64 = MIB; - if fs::metadata(&*zed::paths::LOG).map_or(false, |metadata| metadata.len() > MAX_LOG_BYTES) - { - let _ = fs::rename(&*zed::paths::LOG, &*zed::paths::OLD_LOG); + if fs::metadata(&*paths::LOG).map_or(false, |metadata| metadata.len() > MAX_LOG_BYTES) { + let _ = fs::rename(&*paths::LOG, &*paths::OLD_LOG); } let log_file = OpenOptions::new() .create(true) .append(true) - .open(&*zed::paths::LOG) + .open(&*paths::LOG) .expect("could not open logfile"); simplelog::WriteLogger::init(level, simplelog::Config::default(), log_file) .expect("could not initialize logger"); @@ -251,7 +248,7 @@ fn init_panic_hook(app_version: String, http: Arc, background: A .spawn({ async move { let panic_report_url = format!("{}/api/panic", &*client::ZED_SERVER_URL); - let mut children = smol::fs::read_dir(&*zed::paths::LOGS_DIR).await?; + let mut children = smol::fs::read_dir(&*paths::LOGS_DIR).await?; while let Some(child) = children.next().await { let child = child?; let child_path = child.path(); @@ -339,7 +336,7 @@ fn init_panic_hook(app_version: String, http: Arc, background: A let panic_filename = chrono::Utc::now().format("%Y_%m_%d %H_%M_%S").to_string(); fs::write( - zed::paths::LOGS_DIR.join(format!("zed-{}-{}.panic", app_version, panic_filename)), + paths::LOGS_DIR.join(format!("zed-{}-{}.panic", app_version, panic_filename)), &message, ) .context("error writing panic to disk") @@ -473,8 +470,8 @@ fn load_config_files( .clone() .spawn(async move { let settings_file = - WatchedJsonFile::new(fs.clone(), &executor, zed::paths::SETTINGS.clone()).await; - let keymap_file = WatchedJsonFile::new(fs, &executor, zed::paths::KEYMAP.clone()).await; + WatchedJsonFile::new(fs.clone(), &executor, paths::SETTINGS.clone()).await; + let keymap_file = WatchedJsonFile::new(fs, &executor, paths::KEYMAP.clone()).await; tx.send((settings_file, keymap_file)).ok() }) .detach(); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index f6f3a34242..01b549e438 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1,7 +1,6 @@ mod feedback; pub mod languages; pub mod menus; -pub mod paths; pub mod settings_file; #[cfg(any(test, feature = "test-support"))] pub mod test; From 7ce758b3438e49b9c8bf945b207915ef51b4881b Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 10 Oct 2022 18:37:44 -0700 Subject: [PATCH 255/314] Added notes from working with Nathan --- crates/theme_selector/src/theme_selector.rs | 58 +++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index a7d17ad533..6484e0f627 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -114,6 +114,64 @@ impl ThemeSelector { let theme_name = mat.string.clone(); + // cx.global:: + // cx.global::() // lock + + // 1) Truncation can cause data loss, make it atomic by creating tmp file and moving + // 2) Maybe firing too often? Conceptually want to commit + // Having a lock on the settings file + + // - + // | | + // FS _ + + // General problem: whenever we want to persist stuff + // In memory representation -> File on disk + // Write font size, + // Write theme to disk + // Write -> See your own write -> Another Write + + // Memory Write 1 -> Write To Disk 1, | Memory Write 2, + // Blocking ->>>>>> | Read From Disk 1, + // Discard | Read WHATEVER is from disk | + + // Blocking lock -> + + // Whenever we update the settings in memory, we enqueue a write to disk + // When we receive a file system event, we only honor it if all pending disk writes are complete. + + // When the settings become dirty in memory, schedule a write to disk + // When we are sure the write is completed, consider the settings clean + // Only read settings from disk into memory when in memory settings are clean + // read settings just does not happen, if the settings are dirty + + // 10 settings queued up: + // lock() -> Only needs to be on the file + // How to set a setting: + // write to memory + // Read the whole file from disk + // Surgically inject the setting string + // Write to disk + // unlock() + + // Write 10 x change font size + // Read-open-write font size + // Read-open-write font size + // Read-open-write font size + // Read-open-write font size + // Read-open-write font size + // .. + // Read from file system, only gets the latest font size and uselessly sets font size + + // `SettingsFile` + // You can non-blocking, write to it as much as you need + // Debounces your changes, waits for you to be done, and then flushes them all to the file system + // And blocks the read + + // Read and write to memory. ^ up from the file system + + // If there's pendings writes, we need to wait until this whole thing is done' + cx.background() .spawn(async move { match write_theme_name(theme_name).await { From a833652077b37a4944de97e3c297aa23910081bf Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 11 Oct 2022 11:29:57 -0700 Subject: [PATCH 256/314] Undid change to paths variables and cleaned up leftovers --- Cargo.lock | 11 +-- constellation.txt | 10 --- crates/paths/Cargo.toml | 12 --- crates/theme_selector/Cargo.toml | 1 - crates/theme_selector/src/theme_selector.rs | 92 +-------------------- crates/zed/Cargo.toml | 2 +- crates/zed/src/main.rs | 39 +++++---- crates/{paths => zed}/src/paths.rs | 0 crates/zed/src/zed.rs | 1 + 9 files changed, 27 insertions(+), 141 deletions(-) delete mode 100644 constellation.txt delete mode 100644 crates/paths/Cargo.toml rename crates/{paths => zed}/src/paths.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 177b64c2ae..cea25c027a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3738,14 +3738,6 @@ dependencies = [ "rustc_version", ] -[[package]] -name = "paths" -version = "0.1.0" -dependencies = [ - "dirs 3.0.2", - "lazy_static", -] - [[package]] name = "pbkdf2" version = "0.8.0" @@ -5637,7 +5629,6 @@ dependencies = [ "gpui", "log", "parking_lot 0.11.2", - "paths", "picker", "postage", "settings", @@ -7263,6 +7254,7 @@ dependencies = [ "context_menu", "ctor", "diagnostics", + "dirs 3.0.2", "easy-parallel", "editor", "env_logger", @@ -7285,7 +7277,6 @@ dependencies = [ "num_cpus", "outline", "parking_lot 0.11.2", - "paths", "plugin_runtime", "postage", "project", diff --git a/constellation.txt b/constellation.txt deleted file mode 100644 index b8b8f77e03..0000000000 --- a/constellation.txt +++ /dev/null @@ -1,10 +0,0 @@ -Burn down pain points constellation - ---------------------- -| | -| * | Mikayla -| * | Julia -| | -| * | Kay -| | ---------------------- \ No newline at end of file diff --git a/crates/paths/Cargo.toml b/crates/paths/Cargo.toml deleted file mode 100644 index 3da33cce1b..0000000000 --- a/crates/paths/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "paths" -version = "0.1.0" -edition = "2021" - -[lib] -path = "src/paths.rs" - -[dependencies] -lazy_static = "1.4.0" -dirs = "3.0" - diff --git a/crates/theme_selector/Cargo.toml b/crates/theme_selector/Cargo.toml index e323677d3b..59cb5fbc2c 100644 --- a/crates/theme_selector/Cargo.toml +++ b/crates/theme_selector/Cargo.toml @@ -14,7 +14,6 @@ gpui = { path = "../gpui" } picker = { path = "../picker" } theme = { path = "../theme" } settings = { path = "../settings" } -paths = { path = "../paths" } workspace = { path = "../workspace" } log = { version = "0.4.16", features = ["kv_unstable_serde"] } parking_lot = "0.11.1" diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index 6484e0f627..1cd7b3f926 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -1,12 +1,10 @@ use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; use gpui::{ - actions, anyhow::Result, elements::*, AnyViewHandle, AppContext, Element, ElementBox, Entity, - MouseState, MutableAppContext, RenderContext, View, ViewContext, ViewHandle, + actions, elements::*, AnyViewHandle, AppContext, Element, ElementBox, Entity, MouseState, + MutableAppContext, RenderContext, View, ViewContext, ViewHandle, }; -use paths::SETTINGS; use picker::{Picker, PickerDelegate}; -use settings::{write_theme, Settings}; -use smol::{fs::read_to_string, io::AsyncWriteExt}; +use settings::Settings; use std::sync::Arc; use theme::{Theme, ThemeMeta, ThemeRegistry}; use workspace::{AppState, Workspace}; @@ -111,75 +109,6 @@ impl ThemeSelector { match self.registry.get(&mat.string) { Ok(theme) => { Self::set_theme(theme, cx); - - let theme_name = mat.string.clone(); - - // cx.global:: - // cx.global::() // lock - - // 1) Truncation can cause data loss, make it atomic by creating tmp file and moving - // 2) Maybe firing too often? Conceptually want to commit - // Having a lock on the settings file - - // - - // | | - // FS _ - - // General problem: whenever we want to persist stuff - // In memory representation -> File on disk - // Write font size, - // Write theme to disk - // Write -> See your own write -> Another Write - - // Memory Write 1 -> Write To Disk 1, | Memory Write 2, - // Blocking ->>>>>> | Read From Disk 1, - // Discard | Read WHATEVER is from disk | - - // Blocking lock -> - - // Whenever we update the settings in memory, we enqueue a write to disk - // When we receive a file system event, we only honor it if all pending disk writes are complete. - - // When the settings become dirty in memory, schedule a write to disk - // When we are sure the write is completed, consider the settings clean - // Only read settings from disk into memory when in memory settings are clean - // read settings just does not happen, if the settings are dirty - - // 10 settings queued up: - // lock() -> Only needs to be on the file - // How to set a setting: - // write to memory - // Read the whole file from disk - // Surgically inject the setting string - // Write to disk - // unlock() - - // Write 10 x change font size - // Read-open-write font size - // Read-open-write font size - // Read-open-write font size - // Read-open-write font size - // Read-open-write font size - // .. - // Read from file system, only gets the latest font size and uselessly sets font size - - // `SettingsFile` - // You can non-blocking, write to it as much as you need - // Debounces your changes, waits for you to be done, and then flushes them all to the file system - // And blocks the read - - // Read and write to memory. ^ up from the file system - - // If there's pendings writes, we need to wait until this whole thing is done' - - cx.background() - .spawn(async move { - match write_theme_name(theme_name).await { - Ok(_) => {} - Err(_) => return, //TODO Pop toast - } - }) - .detach() } Err(error) => { log::error!("error loading theme {}: {}", mat.string, error) @@ -337,18 +266,3 @@ impl View for ThemeSelector { } } } - -async fn write_theme_name(theme_name: String) -> Result<()> { - let mut settings = read_to_string(SETTINGS.as_path()).await?; - settings = write_theme(settings, &theme_name); - - let mut file = smol::fs::OpenOptions::new() - .truncate(true) - .write(true) - .open(SETTINGS.as_path()) - .await?; - - file.write_all(settings.as_bytes()).await?; - - Ok(()) -} diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index d57a2c21b1..d0b41b08f1 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -40,7 +40,6 @@ journal = { path = "../journal" } language = { path = "../language" } lsp = { path = "../lsp" } outline = { path = "../outline" } -paths = { path = "../paths" } plugin_runtime = { path = "../plugin_runtime" } project = { path = "../project" } project_panel = { path = "../project_panel" } @@ -62,6 +61,7 @@ async-trait = "0.1" backtrace = "0.3" chrono = "0.4" ctor = "0.1.20" +dirs = "3.0" easy-parallel = "3.1.0" env_logger = "0.9" futures = "0.3" diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 519f04fe35..f48f8b723e 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -53,7 +53,7 @@ fn main() { .map_or("dev".to_string(), |v| v.to_string()); init_panic_hook(app_version, http.clone(), app.background()); let db = app.background().spawn(async move { - project::Db::open(&*paths::DB) + project::Db::open(&*zed::paths::DB) .log_err() .unwrap_or_else(project::Db::null) }); @@ -90,7 +90,7 @@ fn main() { app.run(move |cx| { let client = client::Client::new(http.clone(), cx); let mut languages = LanguageRegistry::new(login_shell_env_loaded); - languages.set_language_server_download_dir(paths::LANGUAGES_DIR.clone()); + languages.set_language_server_download_dir(zed::paths::LANGUAGES_DIR.clone()); let languages = Arc::new(languages); let init_languages = cx .background() @@ -200,21 +200,23 @@ fn main() { } fn init_paths() { - fs::create_dir_all(&*paths::CONFIG_DIR).expect("could not create config path"); - fs::create_dir_all(&*paths::LANGUAGES_DIR).expect("could not create languages path"); - fs::create_dir_all(&*paths::DB_DIR).expect("could not create database path"); - fs::create_dir_all(&*paths::LOGS_DIR).expect("could not create logs path"); + fs::create_dir_all(&*zed::paths::CONFIG_DIR).expect("could not create config path"); + fs::create_dir_all(&*zed::paths::LANGUAGES_DIR).expect("could not create languages path"); + fs::create_dir_all(&*zed::paths::DB_DIR).expect("could not create database path"); + fs::create_dir_all(&*zed::paths::LOGS_DIR).expect("could not create logs path"); // Copy setting files from legacy locations. TODO: remove this after a few releases. thread::spawn(|| { - if fs::metadata(&*paths::legacy::SETTINGS).is_ok() - && fs::metadata(&*paths::SETTINGS).is_err() + if fs::metadata(&*zed::paths::legacy::SETTINGS).is_ok() + && fs::metadata(&*zed::paths::SETTINGS).is_err() { - fs::copy(&*paths::legacy::SETTINGS, &*paths::SETTINGS).log_err(); + fs::copy(&*zed::paths::legacy::SETTINGS, &*zed::paths::SETTINGS).log_err(); } - if fs::metadata(&*paths::legacy::KEYMAP).is_ok() && fs::metadata(&*paths::KEYMAP).is_err() { - fs::copy(&*paths::legacy::KEYMAP, &*paths::KEYMAP).log_err(); + if fs::metadata(&*zed::paths::legacy::KEYMAP).is_ok() + && fs::metadata(&*zed::paths::KEYMAP).is_err() + { + fs::copy(&*zed::paths::legacy::KEYMAP, &*zed::paths::KEYMAP).log_err(); } }); } @@ -229,14 +231,15 @@ fn init_logger() { const KIB: u64 = 1024; const MIB: u64 = 1024 * KIB; const MAX_LOG_BYTES: u64 = MIB; - if fs::metadata(&*paths::LOG).map_or(false, |metadata| metadata.len() > MAX_LOG_BYTES) { - let _ = fs::rename(&*paths::LOG, &*paths::OLD_LOG); + if fs::metadata(&*zed::paths::LOG).map_or(false, |metadata| metadata.len() > MAX_LOG_BYTES) + { + let _ = fs::rename(&*zed::paths::LOG, &*zed::paths::OLD_LOG); } let log_file = OpenOptions::new() .create(true) .append(true) - .open(&*paths::LOG) + .open(&*zed::paths::LOG) .expect("could not open logfile"); simplelog::WriteLogger::init(level, simplelog::Config::default(), log_file) .expect("could not initialize logger"); @@ -248,7 +251,7 @@ fn init_panic_hook(app_version: String, http: Arc, background: A .spawn({ async move { let panic_report_url = format!("{}/api/panic", &*client::ZED_SERVER_URL); - let mut children = smol::fs::read_dir(&*paths::LOGS_DIR).await?; + let mut children = smol::fs::read_dir(&*zed::paths::LOGS_DIR).await?; while let Some(child) = children.next().await { let child = child?; let child_path = child.path(); @@ -336,7 +339,7 @@ fn init_panic_hook(app_version: String, http: Arc, background: A let panic_filename = chrono::Utc::now().format("%Y_%m_%d %H_%M_%S").to_string(); fs::write( - paths::LOGS_DIR.join(format!("zed-{}-{}.panic", app_version, panic_filename)), + zed::paths::LOGS_DIR.join(format!("zed-{}-{}.panic", app_version, panic_filename)), &message, ) .context("error writing panic to disk") @@ -470,8 +473,8 @@ fn load_config_files( .clone() .spawn(async move { let settings_file = - WatchedJsonFile::new(fs.clone(), &executor, paths::SETTINGS.clone()).await; - let keymap_file = WatchedJsonFile::new(fs, &executor, paths::KEYMAP.clone()).await; + WatchedJsonFile::new(fs.clone(), &executor, zed::paths::SETTINGS.clone()).await; + let keymap_file = WatchedJsonFile::new(fs, &executor, zed::paths::KEYMAP.clone()).await; tx.send((settings_file, keymap_file)).ok() }) .detach(); diff --git a/crates/paths/src/paths.rs b/crates/zed/src/paths.rs similarity index 100% rename from crates/paths/src/paths.rs rename to crates/zed/src/paths.rs diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 01b549e438..6210242c51 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -2,6 +2,7 @@ mod feedback; pub mod languages; pub mod menus; pub mod settings_file; +pub mod paths; #[cfg(any(test, feature = "test-support"))] pub mod test; From 0a8e2f6bb0720f57944e482930cc827785ad851d Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 11 Oct 2022 13:03:36 -0700 Subject: [PATCH 257/314] Moved fs to it's own crate, build failing due to cyclic dependency on rope --- Cargo.lock | 16 ++++++++ crates/fs/Cargo.toml | 18 +++++++++ crates/{project => fs}/src/fs.rs | 59 +++++++++++++++++++++++++++- crates/{git => fs}/src/repository.rs | 0 crates/text/Cargo.toml | 1 + crates/text/src/text.rs | 56 -------------------------- crates/zed/src/settings_file.rs | 4 ++ 7 files changed, 96 insertions(+), 58 deletions(-) create mode 100644 crates/fs/Cargo.toml rename crates/{project => fs}/src/fs.rs (96%) rename crates/{git => fs}/src/repository.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index cea25c027a..408758f7ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1984,6 +1984,22 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "fs" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "collections", + "fsevent", + "futures", + "git", + "parking_lot 0.11.2", + "smol", + "text", + "util", +] + [[package]] name = "fs-set-times" version = "0.15.0" diff --git a/crates/fs/Cargo.toml b/crates/fs/Cargo.toml new file mode 100644 index 0000000000..1a4e325fa4 --- /dev/null +++ b/crates/fs/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "fs" +version = "0.1.0" +edition = "2021" + +[lib] +path = "src/fs.rs" + +[dependencies] +collections = { path = "../collections" } +fsevent = { path = "../fsevent" } +anyhow = "1.0.57" +async-trait = "0.1" +futures = "0.3" +parking_lot = "0.11.1" +smol = "1.2.5" +text = { path = "../text" } +util = { path = "../util" } diff --git a/crates/project/src/fs.rs b/crates/fs/src/fs.rs similarity index 96% rename from crates/project/src/fs.rs rename to crates/fs/src/fs.rs index a9a0a1707f..c24387844c 100644 --- a/crates/project/src/fs.rs +++ b/crates/fs/src/fs.rs @@ -1,8 +1,8 @@ +pub mod repository; + use anyhow::{anyhow, Result}; use fsevent::EventStream; use futures::{future::BoxFuture, Stream, StreamExt}; -use git::repository::{GitRepository, LibGitRepository}; -use language::LineEnding; use parking_lot::Mutex as SyncMutex; use smol::io::{AsyncReadExt, AsyncWriteExt}; use std::sync::Arc; @@ -25,6 +25,61 @@ use git::repository::FakeGitRepositoryState; #[cfg(any(test, feature = "test-support"))] use std::sync::Weak; +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum LineEnding { + Unix, + Windows, +} + +impl Default for LineEnding { + fn default() -> Self { + #[cfg(unix)] + return Self::Unix; + + #[cfg(not(unix))] + return Self::CRLF; + } +} + +impl LineEnding { + pub fn as_str(&self) -> &'static str { + match self { + LineEnding::Unix => "\n", + LineEnding::Windows => "\r\n", + } + } + + pub fn detect(text: &str) -> Self { + let mut max_ix = cmp::min(text.len(), 1000); + while !text.is_char_boundary(max_ix) { + max_ix -= 1; + } + + if let Some(ix) = text[..max_ix].find(&['\n']) { + if ix > 0 && text.as_bytes()[ix - 1] == b'\r' { + Self::Windows + } else { + Self::Unix + } + } else { + Self::default() + } + } + + pub fn normalize(text: &mut String) { + if let Cow::Owned(replaced) = CARRIAGE_RETURNS_REGEX.replace_all(text, "\n") { + *text = replaced; + } + } + + fn normalize_arc(text: Arc) -> Arc { + if let Cow::Owned(replaced) = CARRIAGE_RETURNS_REGEX.replace_all(&text, "\n") { + replaced.into() + } else { + text + } + } +} #[async_trait::async_trait] pub trait Fs: Send + Sync { async fn create_dir(&self, path: &Path) -> Result<()>; diff --git a/crates/git/src/repository.rs b/crates/fs/src/repository.rs similarity index 100% rename from crates/git/src/repository.rs rename to crates/fs/src/repository.rs diff --git a/crates/text/Cargo.toml b/crates/text/Cargo.toml index 4fc09eff46..0d47525021 100644 --- a/crates/text/Cargo.toml +++ b/crates/text/Cargo.toml @@ -13,6 +13,7 @@ test-support = ["rand"] [dependencies] clock = { path = "../clock" } collections = { path = "../collections" } +fs = { path = "../fs" } sum_tree = { path = "../sum_tree" } anyhow = "1.0.38" arrayvec = "0.7.1" diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index d201f87443..52859b7515 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -96,12 +96,6 @@ pub struct Transaction { pub start: clock::Global, } -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum LineEnding { - Unix, - Windows, -} - impl HistoryEntry { pub fn transaction_id(&self) -> TransactionId { self.transaction.id @@ -2370,56 +2364,6 @@ impl operation_queue::Operation for Operation { } } -impl Default for LineEnding { - fn default() -> Self { - #[cfg(unix)] - return Self::Unix; - - #[cfg(not(unix))] - return Self::CRLF; - } -} - -impl LineEnding { - pub fn as_str(&self) -> &'static str { - match self { - LineEnding::Unix => "\n", - LineEnding::Windows => "\r\n", - } - } - - pub fn detect(text: &str) -> Self { - let mut max_ix = cmp::min(text.len(), 1000); - while !text.is_char_boundary(max_ix) { - max_ix -= 1; - } - - if let Some(ix) = text[..max_ix].find(&['\n']) { - if ix > 0 && text.as_bytes()[ix - 1] == b'\r' { - Self::Windows - } else { - Self::Unix - } - } else { - Self::default() - } - } - - pub fn normalize(text: &mut String) { - if let Cow::Owned(replaced) = CARRIAGE_RETURNS_REGEX.replace_all(text, "\n") { - *text = replaced; - } - } - - fn normalize_arc(text: Arc) -> Arc { - if let Cow::Owned(replaced) = CARRIAGE_RETURNS_REGEX.replace_all(&text, "\n") { - replaced.into() - } else { - text - } - } -} - pub trait ToOffset { fn to_offset(&self, snapshot: &BufferSnapshot) -> usize; } diff --git a/crates/zed/src/settings_file.rs b/crates/zed/src/settings_file.rs index 14c9f63e95..25bd065b47 100644 --- a/crates/zed/src/settings_file.rs +++ b/crates/zed/src/settings_file.rs @@ -12,6 +12,10 @@ use util::ResultExt; #[derive(Clone)] pub struct WatchedJsonFile(pub watch::Receiver); +// 1) Do the refactoring to pull WatchedJSON and fs out and into everything else +// 2) Scaffold this by making the basic structs we'll need SettingsFile::atomic_write_theme() +// 3) Fix the overeager settings writing, if that works, and there's no data loss, call it? + impl WatchedJsonFile where T: 'static + for<'de> Deserialize<'de> + Clone + Default + Send + Sync, From acc85ad03c229ab8e3a6886adad81bfc2c76d4bb Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 11 Oct 2022 13:18:33 -0700 Subject: [PATCH 258/314] Impose a minimum height on the scrollbar --- crates/editor/src/element.rs | 22 ++++++++++++++++++++-- crates/theme/src/theme.rs | 1 + styles/src/styleTree/editor.ts | 1 + 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 04c67311de..5a8ea0d820 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -916,6 +916,9 @@ impl EditorElement { let view = self.view.clone(); let style = &self.style.theme.scrollbar; + let min_thumb_height = + style.min_height_factor * cx.font_cache.line_height(self.style.text.font_size); + let top = bounds.min_y(); let bottom = bounds.max_y(); let right = bounds.max_x(); @@ -925,8 +928,23 @@ impl EditorElement { let max_row = layout.max_row + ((row_range.end - row_range.start) as u32); let scrollbar_start = row_range.start as f32 / max_row as f32; let scrollbar_end = row_range.end as f32 / max_row as f32; - let thumb_top = top + scrollbar_start * height; - let thumb_bottom = top + scrollbar_end * height; + + let mut thumb_top = top + scrollbar_start * height; + let mut thumb_bottom = top + scrollbar_end * height; + let thumb_center = (thumb_top + thumb_bottom) / 2.0; + + if thumb_bottom - thumb_top < min_thumb_height { + thumb_top = thumb_center - min_thumb_height / 2.0; + thumb_bottom = thumb_center + min_thumb_height / 2.0; + if thumb_top < top { + thumb_top = top; + thumb_bottom = top + min_thumb_height; + } + if thumb_bottom > bottom { + thumb_bottom = bottom; + thumb_top = bottom - min_thumb_height; + } + } let track_bounds = RectF::from_points(vec2f(left, top), vec2f(right, bottom)); let thumb_bounds = RectF::from_points(vec2f(left, thumb_top), vec2f(right, thumb_bottom)); diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 7bd7913644..e1a1817b5c 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -518,6 +518,7 @@ pub struct Scrollbar { pub track: ContainerStyle, pub thumb: ContainerStyle, pub width: f32, + pub min_height_factor: f32, } #[derive(Clone, Deserialize, Default)] diff --git a/styles/src/styleTree/editor.ts b/styles/src/styleTree/editor.ts index 334931a981..6a333930db 100644 --- a/styles/src/styleTree/editor.ts +++ b/styles/src/styleTree/editor.ts @@ -173,6 +173,7 @@ export default function editor(theme: Theme) { }, scrollbar: { width: 12, + minHeightFactor: 1.0, track: { border: { left: true, From 673041d1f54577bc68e9229bbc9d6310096e175b Mon Sep 17 00:00:00 2001 From: K Simmons Date: Tue, 11 Oct 2022 15:17:29 -0700 Subject: [PATCH 259/314] working quote and bracket text objects --- assets/keymaps/vim.json | 13 +- crates/vim/src/normal/change.rs | 22 +- crates/vim/src/normal/delete.rs | 2 +- crates/vim/src/object.rs | 208 ++++++++++++++++-- crates/vim/src/visual.rs | 27 +-- ..._change_surrounding_character_objects.json | 1 + ..._delete_surrounding_character_objects.json | 1 + 7 files changed, 237 insertions(+), 37 deletions(-) create mode 100644 crates/vim/test_data/test_change_surrounding_character_objects.json create mode 100644 crates/vim/test_data/test_delete_surrounding_character_objects.json diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index 1c2f8ac24d..94729af21f 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -198,7 +198,18 @@ "ignorePunctuation": true } ], - "s": "vim::Sentence" + "s": "vim::Sentence", + "'": "vim::Quotes", + "`": "vim::BackQuotes", + "\"": "vim::DoubleQuotes", + "(": "vim::Parentheses", + ")": "vim::Parentheses", + "[": "vim::SquareBrackets", + "]": "vim::SquareBrackets", + "{": "vim::CurlyBrackets", + "}": "vim::CurlyBrackets", + "<": "vim::AngleBrackets", + ">": "vim::AngleBrackets" } }, { diff --git a/crates/vim/src/normal/change.rs b/crates/vim/src/normal/change.rs index cc62ce8db0..a343d43ba8 100644 --- a/crates/vim/src/normal/change.rs +++ b/crates/vim/src/normal/change.rs @@ -1,5 +1,5 @@ use crate::{motion::Motion, object::Object, state::Mode, utils::copy_selections_content, Vim}; -use editor::{char_kind, display_map::DisplaySnapshot, movement, Autoscroll, DisplayPoint}; +use editor::{char_kind, display_map::DisplaySnapshot, movement, Autoscroll, Bias, DisplayPoint}; use gpui::MutableAppContext; use language::Selection; @@ -25,20 +25,28 @@ pub fn change_motion(vim: &mut Vim, motion: Motion, times: usize, cx: &mut Mutab } pub fn change_object(vim: &mut Vim, object: Object, around: bool, cx: &mut MutableAppContext) { + let mut objects_found = false; vim.update_active_editor(cx, |editor, cx| { + // We are swapping to insert mode anyway. Just set the line end clipping behavior now + editor.set_clip_at_line_ends(false, cx); editor.transact(cx, |editor, cx| { - // We are swapping to insert mode anyway. Just set the line end clipping behavior now - editor.set_clip_at_line_ends(false, cx); editor.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_with(|map, selection| { - object.expand_selection(map, selection, around); + objects_found |= object.expand_selection(map, selection, around); }); }); - copy_selections_content(editor, false, cx); - editor.insert("", cx); + if objects_found { + copy_selections_content(editor, false, cx); + editor.insert("", cx); + } }); }); - vim.switch_mode(Mode::Insert, false, cx); + + if objects_found { + vim.switch_mode(Mode::Insert, false, cx); + } else { + vim.switch_mode(Mode::Normal, false, cx); + } } // From the docs https://vimhelp.org/change.txt.html#cw diff --git a/crates/vim/src/normal/delete.rs b/crates/vim/src/normal/delete.rs index 1465e3e377..a2c540a59c 100644 --- a/crates/vim/src/normal/delete.rs +++ b/crates/vim/src/normal/delete.rs @@ -51,7 +51,7 @@ pub fn delete_object(vim: &mut Vim, object: Object, around: bool, cx: &mut Mutab .chars_at(selection.start) .take_while(|(_, p)| p < &selection.end) .all(|(char, _)| char == '\n') - || offset_range.is_empty(); + && !offset_range.is_empty(); let end_at_newline = map .chars_at(selection.end) .next() diff --git a/crates/vim/src/object.rs b/crates/vim/src/object.rs index 5adc8397fa..ca7aff06dc 100644 --- a/crates/vim/src/object.rs +++ b/crates/vim/src/object.rs @@ -12,6 +12,13 @@ use crate::{motion, normal::normal_object, state::Mode, visual::visual_object, V pub enum Object { Word { ignore_punctuation: bool }, Sentence, + Quotes, + BackQuotes, + DoubleQuotes, + Parentheses, + SquareBrackets, + CurlyBrackets, + AngleBrackets, } #[derive(Clone, Deserialize, PartialEq)] @@ -21,7 +28,19 @@ struct Word { ignore_punctuation: bool, } -actions!(vim, [Sentence]); +actions!( + vim, + [ + Sentence, + Quotes, + BackQuotes, + DoubleQuotes, + Parentheses, + SquareBrackets, + CurlyBrackets, + AngleBrackets + ] +); impl_actions!(vim, [Word]); pub fn init(cx: &mut MutableAppContext) { @@ -31,6 +50,15 @@ pub fn init(cx: &mut MutableAppContext) { }, ); cx.add_action(|_: &mut Workspace, _: &Sentence, cx: _| object(Object::Sentence, cx)); + cx.add_action(|_: &mut Workspace, _: &Quotes, cx: _| object(Object::Quotes, cx)); + cx.add_action(|_: &mut Workspace, _: &BackQuotes, cx: _| object(Object::BackQuotes, cx)); + cx.add_action(|_: &mut Workspace, _: &DoubleQuotes, cx: _| object(Object::DoubleQuotes, cx)); + cx.add_action(|_: &mut Workspace, _: &Parentheses, cx: _| object(Object::Parentheses, cx)); + cx.add_action(|_: &mut Workspace, _: &SquareBrackets, cx: _| { + object(Object::SquareBrackets, cx) + }); + cx.add_action(|_: &mut Workspace, _: &CurlyBrackets, cx: _| object(Object::CurlyBrackets, cx)); + cx.add_action(|_: &mut Workspace, _: &AngleBrackets, cx: _| object(Object::AngleBrackets, cx)); } fn object(object: Object, cx: &mut MutableAppContext) { @@ -49,7 +77,7 @@ impl Object { map: &DisplaySnapshot, relative_to: DisplayPoint, around: bool, - ) -> Range { + ) -> Option> { match self { Object::Word { ignore_punctuation } => { if around { @@ -59,6 +87,13 @@ impl Object { } } Object::Sentence => sentence(map, relative_to, around), + Object::Quotes => surrounding_markers(map, relative_to, around, false, '\'', '\''), + Object::BackQuotes => surrounding_markers(map, relative_to, around, false, '`', '`'), + Object::DoubleQuotes => surrounding_markers(map, relative_to, around, false, '"', '"'), + Object::Parentheses => surrounding_markers(map, relative_to, around, true, '(', ')'), + Object::SquareBrackets => surrounding_markers(map, relative_to, around, true, '[', ']'), + Object::CurlyBrackets => surrounding_markers(map, relative_to, around, true, '{', '}'), + Object::AngleBrackets => surrounding_markers(map, relative_to, around, true, '<', '>'), } } @@ -67,10 +102,14 @@ impl Object { map: &DisplaySnapshot, selection: &mut Selection, around: bool, - ) { - let range = self.range(map, selection.head(), around); - selection.start = range.start; - selection.end = range.end; + ) -> bool { + if let Some(range) = self.range(map, selection.head(), around) { + selection.start = range.start; + selection.end = range.end; + true + } else { + false + } } } @@ -81,7 +120,7 @@ fn in_word( map: &DisplaySnapshot, relative_to: DisplayPoint, ignore_punctuation: bool, -) -> Range { +) -> Option> { // Use motion::right so that we consider the character under the cursor when looking for the start let start = movement::find_preceding_boundary_in_line( map, @@ -96,7 +135,7 @@ fn in_word( != char_kind(right).coerce_punctuation(ignore_punctuation) }); - start..end + Some(start..end) } /// Return a range that surrounds the word and following whitespace @@ -115,7 +154,7 @@ fn around_word( map: &DisplaySnapshot, relative_to: DisplayPoint, ignore_punctuation: bool, -) -> Range { +) -> Option> { let in_word = map .chars_at(relative_to) .next() @@ -133,15 +172,16 @@ fn around_containing_word( map: &DisplaySnapshot, relative_to: DisplayPoint, ignore_punctuation: bool, -) -> Range { - expand_to_include_whitespace(map, in_word(map, relative_to, ignore_punctuation), true) +) -> Option> { + in_word(map, relative_to, ignore_punctuation) + .map(|range| expand_to_include_whitespace(map, range, true)) } fn around_next_word( map: &DisplaySnapshot, relative_to: DisplayPoint, ignore_punctuation: bool, -) -> Range { +) -> Option> { // Get the start of the word let start = movement::find_preceding_boundary_in_line( map, @@ -166,10 +206,14 @@ fn around_next_word( found }); - start..end + Some(start..end) } -fn sentence(map: &DisplaySnapshot, relative_to: DisplayPoint, around: bool) -> Range { +fn sentence( + map: &DisplaySnapshot, + relative_to: DisplayPoint, + around: bool, +) -> Option> { let mut start = None; let mut previous_end = relative_to; @@ -220,7 +264,7 @@ fn sentence(map: &DisplaySnapshot, relative_to: DisplayPoint, around: bool) -> R range = expand_to_include_whitespace(map, range, false); } - range + Some(range) } fn is_possible_sentence_start(character: char) -> bool { @@ -306,6 +350,83 @@ fn expand_to_include_whitespace( range } +fn surrounding_markers( + map: &DisplaySnapshot, + relative_to: DisplayPoint, + around: bool, + search_across_lines: bool, + start_marker: char, + end_marker: char, +) -> Option> { + let mut matched_ends = 0; + let mut start = None; + for (char, mut point) in map.reverse_chars_at(relative_to) { + if char == start_marker { + if matched_ends > 0 { + matched_ends -= 1; + } else { + if around { + start = Some(point) + } else { + *point.column_mut() += char.len_utf8() as u32; + start = Some(point); + } + break; + } + } else if char == end_marker { + matched_ends += 1; + } else if char == '\n' && !search_across_lines { + break; + } + } + + let mut matched_starts = 0; + let mut end = None; + for (char, mut point) in map.chars_at(relative_to) { + if char == end_marker { + if start.is_none() { + break; + } + + if matched_starts > 0 { + matched_starts -= 1; + } else { + if around { + *point.column_mut() += char.len_utf8() as u32; + end = Some(point); + } else { + end = Some(point); + } + + break; + } + } + + if char == start_marker { + if start.is_none() { + if around { + start = Some(point); + } else { + *point.column_mut() += char.len_utf8() as u32; + start = Some(point); + } + } else { + matched_starts += 1; + } + } + + if char == '\n' && !search_across_lines { + break; + } + } + + if let (Some(start), Some(end)) = (start, end) { + Some(start..end) + } else { + None + } +} + #[cfg(test)] mod test { use indoc::indoc; @@ -459,4 +580,61 @@ mod test { // cx.assert_all(sentence_example).await; // } } + + // Test string with "`" for opening surrounders and "'" for closing surrounders + const SURROUNDING_MARKER_STRING: &str = indoc! {" + ˇTh'ˇe ˇ`ˇ'ˇquˇi`ˇck broˇ'wn` + 'ˇfox juˇmps ovˇ`ˇer + the ˇlazy dˇ'ˇoˇ`ˇg"}; + + const SURROUNDING_OBJECTS: &[(char, char)] = &[ + // ('\'', '\''), // Quote, + // ('`', '`'), // Back Quote + // ('"', '"'), // Double Quote + // ('"', '"'), // Double Quote + ('(', ')'), // Parentheses + ('[', ']'), // SquareBrackets + ('{', '}'), // CurlyBrackets + ('<', '>'), // AngleBrackets + ]; + + #[gpui::test] + async fn test_change_surrounding_character_objects(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + for (start, end) in SURROUNDING_OBJECTS { + let marked_string = SURROUNDING_MARKER_STRING + .replace('`', &start.to_string()) + .replace('\'', &end.to_string()); + + // cx.assert_binding_matches_all(["c", "i", &start.to_string()], &marked_string) + // .await; + cx.assert_binding_matches_all(["c", "i", &end.to_string()], &marked_string) + .await; + // cx.assert_binding_matches_all(["c", "a", &start.to_string()], &marked_string) + // .await; + cx.assert_binding_matches_all(["c", "a", &end.to_string()], &marked_string) + .await; + } + } + + #[gpui::test] + async fn test_delete_surrounding_character_objects(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + for (start, end) in SURROUNDING_OBJECTS { + let marked_string = SURROUNDING_MARKER_STRING + .replace('`', &start.to_string()) + .replace('\'', &end.to_string()); + + // cx.assert_binding_matches_all(["d", "i", &start.to_string()], &marked_string) + // .await; + cx.assert_binding_matches_all(["d", "i", &end.to_string()], &marked_string) + .await; + // cx.assert_binding_matches_all(["d", "a", &start.to_string()], &marked_string) + // .await; + cx.assert_binding_matches_all(["d", "a", &end.to_string()], &marked_string) + .await; + } + } } diff --git a/crates/vim/src/visual.rs b/crates/vim/src/visual.rs index 4fa195a2fa..1351439d7b 100644 --- a/crates/vim/src/visual.rs +++ b/crates/vim/src/visual.rs @@ -60,22 +60,23 @@ pub fn visual_object(object: Object, cx: &mut MutableAppContext) { editor.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_with(|map, selection| { let head = selection.head(); - let mut range = object.range(map, head, around); - if !range.is_empty() { - if let Some((_, end)) = map.reverse_chars_at(range.end).next() { - range.end = end; - } + if let Some(mut range) = object.range(map, head, around) { + if !range.is_empty() { + if let Some((_, end)) = map.reverse_chars_at(range.end).next() { + range.end = end; + } - if selection.is_empty() { - selection.start = range.start; - selection.end = range.end; - } else if selection.reversed { - selection.start = range.start; - } else { - selection.end = range.end; + if selection.is_empty() { + selection.start = range.start; + selection.end = range.end; + } else if selection.reversed { + selection.start = range.start; + } else { + selection.end = range.end; + } } } - }) + }); }); }); } diff --git a/crates/vim/test_data/test_change_surrounding_character_objects.json b/crates/vim/test_data/test_change_surrounding_character_objects.json new file mode 100644 index 0000000000..8a66f5b144 --- /dev/null +++ b/crates/vim/test_data/test_change_surrounding_character_objects.json @@ -0,0 +1 @@ +[{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"},{"Mode":"Insert"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Insert"},{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"},{"Mode":"Insert"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Insert"},{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"},{"Mode":"Insert"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Insert"},{"Text":"Th)e ()qui()wn(\n)fox jumps ov(er\nthe lazy d)o(g"},{"Mode":"Insert"},{"Selection":{"start":[0,11],"end":[0,11]}},{"Mode":"Insert"},{"Text":"Th)e ()qui()wn(\n)fox jumps ov(er\nthe lazy d)o(g"},{"Mode":"Insert"},{"Selection":{"start":[0,11],"end":[0,11]}},{"Mode":"Insert"},{"Text":"Th)e ()qui()wn(\n)fox jumps ov(er\nthe lazy d)o(g"},{"Mode":"Insert"},{"Selection":{"start":[0,11],"end":[0,11]}},{"Mode":"Insert"},{"Text":"Th)e ()qui()wn(\n)fox jumps ov(er\nthe lazy d)o(g"},{"Mode":"Insert"},{"Selection":{"start":[0,11],"end":[0,11]}},{"Mode":"Insert"},{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ov()o(g"},{"Mode":"Insert"},{"Selection":{"start":[1,14],"end":[1,14]}},{"Mode":"Insert"},{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ov()o(g"},{"Mode":"Insert"},{"Selection":{"start":[1,14],"end":[1,14]}},{"Mode":"Insert"},{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ov()o(g"},{"Mode":"Insert"},{"Selection":{"start":[1,14],"end":[1,14]}},{"Mode":"Insert"},{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ov()o(g"},{"Mode":"Insert"},{"Selection":{"start":[1,14],"end":[1,14]}},{"Mode":"Insert"},{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ov()o(g"},{"Mode":"Insert"},{"Selection":{"start":[1,14],"end":[1,14]}},{"Mode":"Insert"},{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ov()o(g"},{"Mode":"Insert"},{"Selection":{"start":[1,14],"end":[1,14]}},{"Mode":"Insert"},{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"},{"Mode":"Normal"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Normal"},{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"},{"Mode":"Normal"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Normal"},{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"},{"Mode":"Normal"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Normal"},{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Th)e qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"},{"Mode":"Insert"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Insert"},{"Text":"Th)e qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"},{"Mode":"Insert"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Insert"},{"Text":"Th)e qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"},{"Mode":"Insert"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Insert"},{"Text":"Th)e ()quiwn(\n)fox jumps ov(er\nthe lazy d)o(g"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"Th)e ()quiwn(\n)fox jumps ov(er\nthe lazy d)o(g"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"Th)e ()quiwn(\n)fox jumps ov(er\nthe lazy d)o(g"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"Th)e ()quiwn(\n)fox jumps ov(er\nthe lazy d)o(g"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ovo(g"},{"Mode":"Insert"},{"Selection":{"start":[1,13],"end":[1,13]}},{"Mode":"Insert"},{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ovo(g"},{"Mode":"Insert"},{"Selection":{"start":[1,13],"end":[1,13]}},{"Mode":"Insert"},{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ovo(g"},{"Mode":"Insert"},{"Selection":{"start":[1,13],"end":[1,13]}},{"Mode":"Insert"},{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ovo(g"},{"Mode":"Insert"},{"Selection":{"start":[1,13],"end":[1,13]}},{"Mode":"Insert"},{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ovo(g"},{"Mode":"Insert"},{"Selection":{"start":[1,13],"end":[1,13]}},{"Mode":"Insert"},{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ovo(g"},{"Mode":"Insert"},{"Selection":{"start":[1,13],"end":[1,13]}},{"Mode":"Insert"},{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"},{"Mode":"Normal"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Normal"},{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"},{"Mode":"Normal"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Normal"},{"Text":"Th)e ()qui(ck bro)wn(\n)fox jumps ov(er\nthe lazy d)o(g"},{"Mode":"Normal"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Normal"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"},{"Mode":"Insert"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Insert"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"},{"Mode":"Insert"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Insert"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"},{"Mode":"Insert"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Insert"},{"Text":"Th]e []qui[]wn[\n]fox jumps ov[er\nthe lazy d]o[g"},{"Mode":"Insert"},{"Selection":{"start":[0,11],"end":[0,11]}},{"Mode":"Insert"},{"Text":"Th]e []qui[]wn[\n]fox jumps ov[er\nthe lazy d]o[g"},{"Mode":"Insert"},{"Selection":{"start":[0,11],"end":[0,11]}},{"Mode":"Insert"},{"Text":"Th]e []qui[]wn[\n]fox jumps ov[er\nthe lazy d]o[g"},{"Mode":"Insert"},{"Selection":{"start":[0,11],"end":[0,11]}},{"Mode":"Insert"},{"Text":"Th]e []qui[]wn[\n]fox jumps ov[er\nthe lazy d]o[g"},{"Mode":"Insert"},{"Selection":{"start":[0,11],"end":[0,11]}},{"Mode":"Insert"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ov[]o[g"},{"Mode":"Insert"},{"Selection":{"start":[1,14],"end":[1,14]}},{"Mode":"Insert"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ov[]o[g"},{"Mode":"Insert"},{"Selection":{"start":[1,14],"end":[1,14]}},{"Mode":"Insert"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ov[]o[g"},{"Mode":"Insert"},{"Selection":{"start":[1,14],"end":[1,14]}},{"Mode":"Insert"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ov[]o[g"},{"Mode":"Insert"},{"Selection":{"start":[1,14],"end":[1,14]}},{"Mode":"Insert"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ov[]o[g"},{"Mode":"Insert"},{"Selection":{"start":[1,14],"end":[1,14]}},{"Mode":"Insert"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ov[]o[g"},{"Mode":"Insert"},{"Selection":{"start":[1,14],"end":[1,14]}},{"Mode":"Insert"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"},{"Mode":"Normal"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Normal"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"},{"Mode":"Normal"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Normal"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"},{"Mode":"Normal"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Normal"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Th]e qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"},{"Mode":"Insert"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Insert"},{"Text":"Th]e qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"},{"Mode":"Insert"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Insert"},{"Text":"Th]e qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"},{"Mode":"Insert"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Insert"},{"Text":"Th]e []quiwn[\n]fox jumps ov[er\nthe lazy d]o[g"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"Th]e []quiwn[\n]fox jumps ov[er\nthe lazy d]o[g"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"Th]e []quiwn[\n]fox jumps ov[er\nthe lazy d]o[g"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"Th]e []quiwn[\n]fox jumps ov[er\nthe lazy d]o[g"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ovo[g"},{"Mode":"Insert"},{"Selection":{"start":[1,13],"end":[1,13]}},{"Mode":"Insert"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ovo[g"},{"Mode":"Insert"},{"Selection":{"start":[1,13],"end":[1,13]}},{"Mode":"Insert"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ovo[g"},{"Mode":"Insert"},{"Selection":{"start":[1,13],"end":[1,13]}},{"Mode":"Insert"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ovo[g"},{"Mode":"Insert"},{"Selection":{"start":[1,13],"end":[1,13]}},{"Mode":"Insert"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ovo[g"},{"Mode":"Insert"},{"Selection":{"start":[1,13],"end":[1,13]}},{"Mode":"Insert"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ovo[g"},{"Mode":"Insert"},{"Selection":{"start":[1,13],"end":[1,13]}},{"Mode":"Insert"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"},{"Mode":"Normal"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Normal"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"},{"Mode":"Normal"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Normal"},{"Text":"Th]e []qui[ck bro]wn[\n]fox jumps ov[er\nthe lazy d]o[g"},{"Mode":"Normal"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Normal"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"},{"Mode":"Insert"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Insert"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"},{"Mode":"Insert"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Insert"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"},{"Mode":"Insert"},{"Selection":{"start":[0,6],"end":[0,6]}},{"Mode":"Insert"},{"Text":"Th}e {}qui{}wn{\n}fox jumps ov{er\nthe lazy d}o{g"},{"Mode":"Insert"},{"Selection":{"start":[0,11],"end":[0,11]}},{"Mode":"Insert"},{"Text":"Th}e {}qui{}wn{\n}fox jumps ov{er\nthe lazy d}o{g"},{"Mode":"Insert"},{"Selection":{"start":[0,11],"end":[0,11]}},{"Mode":"Insert"},{"Text":"Th}e {}qui{}wn{\n}fox jumps ov{er\nthe lazy d}o{g"},{"Mode":"Insert"},{"Selection":{"start":[0,11],"end":[0,11]}},{"Mode":"Insert"},{"Text":"Th}e {}qui{}wn{\n}fox jumps ov{er\nthe lazy d}o{g"},{"Mode":"Insert"},{"Selection":{"start":[0,11],"end":[0,11]}},{"Mode":"Insert"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{}o{g"},{"Mode":"Insert"},{"Selection":{"start":[1,14],"end":[1,14]}},{"Mode":"Insert"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{}o{g"},{"Mode":"Insert"},{"Selection":{"start":[1,14],"end":[1,14]}},{"Mode":"Insert"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{}o{g"},{"Mode":"Insert"},{"Selection":{"start":[1,14],"end":[1,14]}},{"Mode":"Insert"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{}o{g"},{"Mode":"Insert"},{"Selection":{"start":[1,14],"end":[1,14]}},{"Mode":"Insert"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{}o{g"},{"Mode":"Insert"},{"Selection":{"start":[1,14],"end":[1,14]}},{"Mode":"Insert"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{}o{g"},{"Mode":"Insert"},{"Selection":{"start":[1,14],"end":[1,14]}},{"Mode":"Insert"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"},{"Mode":"Normal"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Normal"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"},{"Mode":"Normal"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Normal"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"},{"Mode":"Normal"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Normal"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"},{"Mode":"Normal"},{"Selection":{"start":[0,0],"end":[0,0]}},{"Mode":"Normal"},{"Text":"Th}e qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"},{"Mode":"Insert"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Insert"},{"Text":"Th}e qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"},{"Mode":"Insert"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Insert"},{"Text":"Th}e qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"},{"Mode":"Insert"},{"Selection":{"start":[0,5],"end":[0,5]}},{"Mode":"Insert"},{"Text":"Th}e {}quiwn{\n}fox jumps ov{er\nthe lazy d}o{g"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"Th}e {}quiwn{\n}fox jumps ov{er\nthe lazy d}o{g"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"Th}e {}quiwn{\n}fox jumps ov{er\nthe lazy d}o{g"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"Th}e {}quiwn{\n}fox jumps ov{er\nthe lazy d}o{g"},{"Mode":"Insert"},{"Selection":{"start":[0,10],"end":[0,10]}},{"Mode":"Insert"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ovo{g"},{"Mode":"Insert"},{"Selection":{"start":[1,13],"end":[1,13]}},{"Mode":"Insert"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ovo{g"},{"Mode":"Insert"},{"Selection":{"start":[1,13],"end":[1,13]}},{"Mode":"Insert"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ovo{g"},{"Mode":"Insert"},{"Selection":{"start":[1,13],"end":[1,13]}},{"Mode":"Insert"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ovo{g"},{"Mode":"Insert"},{"Selection":{"start":[1,13],"end":[1,13]}},{"Mode":"Insert"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ovo{g"},{"Mode":"Insert"},{"Selection":{"start":[1,13],"end":[1,13]}},{"Mode":"Insert"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ovo{g"},{"Mode":"Insert"},{"Selection":{"start":[1,13],"end":[1,13]}},{"Mode":"Insert"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"},{"Mode":"Normal"},{"Selection":{"start":[2,11],"end":[2,11]}},{"Mode":"Normal"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"},{"Mode":"Normal"},{"Selection":{"start":[2,12],"end":[2,12]}},{"Mode":"Normal"},{"Text":"Th}e {}qui{ck bro}wn{\n}fox jumps ov{er\nthe lazy d}o{g"},{"Mode":"Normal"},{"Selection":{"start":[2,13],"end":[2,13]}},{"Mode":"Normal"},{"Text":"Th>e <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>qui<>wn<\n>fox jumps ovoe <>qui<>wn<\n>fox jumps ovoe <>qui<>wn<\n>fox jumps ovoe <>qui<>wn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ov<>oe <>quiwn<\n>fox jumps ov<>oe <>quiwn<\n>fox jumps ov<>oe <>quiwn<\n>fox jumps ov<>oe <>quiwn<\n>fox jumps ov<>oe <>quiwn<\n>fox jumps ov<>oe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe quiwn<\n>fox jumps ovoe quiwn<\n>fox jumps ovoe quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>qui<>wn<\n>fox jumps ovoe <>qui<>wn<\n>fox jumps ovoe <>qui<>wn<\n>fox jumps ovoe <>qui<>wn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ov<>oe <>quiwn<\n>fox jumps ov<>oe <>quiwn<\n>fox jumps ov<>oe <>quiwn<\n>fox jumps ov<>oe <>quiwn<\n>fox jumps ov<>oe <>quiwn<\n>fox jumps ov<>oe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe quiwn<\n>fox jumps ovoe quiwn<\n>fox jumps ovoe quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovoe <>quiwn<\n>fox jumps ovo Date: Tue, 11 Oct 2022 18:25:36 -0400 Subject: [PATCH 260/314] Add amplitude release (#1720) Co-authored-by: Max Brunsfeld --- ...iscord_webhook.yml => release_actions.yml} | 17 +++++++++-- .gitignore | 3 +- script/amplitude_release/main.py | 30 +++++++++++++++++++ script/amplitude_release/requirements.txt | 1 + 4 files changed, 47 insertions(+), 4 deletions(-) rename .github/workflows/{discord_webhook.yml => release_actions.yml} (50%) create mode 100644 script/amplitude_release/main.py create mode 100644 script/amplitude_release/requirements.txt diff --git a/.github/workflows/discord_webhook.yml b/.github/workflows/release_actions.yml similarity index 50% rename from .github/workflows/discord_webhook.yml rename to .github/workflows/release_actions.yml index b71d451f5b..8d2788e6f0 100644 --- a/.github/workflows/discord_webhook.yml +++ b/.github/workflows/release_actions.yml @@ -1,9 +1,9 @@ on: release: types: [published] - + jobs: - message: + discord_release: runs-on: ubuntu-latest steps: - name: Discord Webhook Action @@ -19,4 +19,15 @@ jobs: ### Changelog ${{ github.event.release.body }} - ``` \ No newline at end of file + ``` + amplitude_release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: "3.10.5" + architecture: "x64" + cache: "pip" + - run: pip install -r script/amplitude_release/requirements.txt + - run: python script/amplitude_release/main.py ${{ github.event.release.tag_name }} ${{ secrets.ZED_AMPLITUDE_API_KEY }} ${{ secrets.ZED_AMPLITUDE_SECRET_KEY }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 5e6963ba8b..2d721f8ad2 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ /vendor/bin /assets/themes/*.json /assets/themes/internal/*.json -/assets/themes/experiments/*.json \ No newline at end of file +/assets/themes/experiments/*.json +**/venv \ No newline at end of file diff --git a/script/amplitude_release/main.py b/script/amplitude_release/main.py new file mode 100644 index 0000000000..160e40b66c --- /dev/null +++ b/script/amplitude_release/main.py @@ -0,0 +1,30 @@ +import datetime +import sys + +from amplitude_python_sdk.v2.clients.releases_client import ReleasesAPIClient +from amplitude_python_sdk.v2.models.releases import Release + + +def main(): + version = sys.argv[1] + version = version.removeprefix("v") + + api_key = sys.argv[2] + secret_key = sys.argv[3] + + current_datetime = datetime.datetime.now(datetime.timezone.utc) + current_datetime = current_datetime.strftime("%Y-%m-%d %H:%M:%S") + + release = Release( + title=version, + version=version, + release_start=current_datetime, + created_by="GitHub Release Workflow", + chart_visibility=True + ) + + ReleasesAPIClient(api_key=api_key, secret_key=secret_key).create(release) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/script/amplitude_release/requirements.txt b/script/amplitude_release/requirements.txt new file mode 100644 index 0000000000..7ed3ea6515 --- /dev/null +++ b/script/amplitude_release/requirements.txt @@ -0,0 +1 @@ +amplitude-python-sdk==0.2.0 \ No newline at end of file From 0beb97547efe5bd9efb3005c72c575b184e90daa Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 11 Oct 2022 15:25:54 -0700 Subject: [PATCH 261/314] Finished refactoring out fs and rope --- Cargo.lock | 44 +++++++++++++++++--- Cargo.toml | 1 + crates/collab/Cargo.toml | 3 +- crates/collab/src/integration_tests.rs | 10 ++--- crates/diagnostics/Cargo.toml | 1 + crates/diagnostics/src/diagnostics.rs | 7 ++-- crates/editor/Cargo.toml | 1 + crates/editor/src/display_map.rs | 9 +++-- crates/editor/src/display_map/block_map.rs | 7 ++-- crates/editor/src/display_map/fold_map.rs | 9 +++-- crates/editor/src/display_map/tab_map.rs | 22 +++++----- crates/editor/src/display_map/wrap_map.rs | 12 +++--- crates/editor/src/editor.rs | 5 ++- crates/editor/src/editor_tests.rs | 2 +- crates/editor/src/element.rs | 3 +- crates/editor/src/items.rs | 3 +- crates/editor/src/movement.rs | 5 ++- crates/editor/src/multi_buffer.rs | 16 ++++---- crates/editor/src/multi_buffer/anchor.rs | 2 +- crates/editor/src/selections_collection.rs | 3 +- crates/fs/Cargo.toml | 18 +++++++-- crates/fs/src/fs.rs | 47 +++++++++++++++++++--- crates/git/Cargo.toml | 3 +- crates/git/src/diff.rs | 3 +- crates/git/src/git.rs | 1 - crates/go_to_line/Cargo.toml | 1 + crates/go_to_line/src/go_to_line.rs | 3 +- crates/language/Cargo.toml | 2 + crates/language/src/buffer.rs | 10 ++--- crates/language/src/buffer_tests.rs | 4 +- crates/language/src/diagnostic_set.rs | 3 +- crates/language/src/language.rs | 1 + crates/language/src/proto.rs | 14 +++---- crates/language/src/syntax_map.rs | 5 ++- crates/project/Cargo.toml | 4 +- crates/project/src/lsp_command.rs | 3 +- crates/project/src/project.rs | 34 ++-------------- crates/project/src/project_tests.rs | 6 ++- crates/project/src/worktree.rs | 17 ++++---- crates/rope/Cargo.toml | 20 +++++++++ crates/{text => rope}/src/offset_utf16.rs | 0 crates/{text => rope}/src/point.rs | 0 crates/{text => rope}/src/point_utf16.rs | 0 crates/{text => rope}/src/rope.rs | 11 +++-- crates/text/Cargo.toml | 8 ++-- crates/text/src/anchor.rs | 5 ++- crates/text/src/random_char_iter.rs | 36 ----------------- crates/text/src/selection.rs | 6 ++- crates/text/src/text.rs | 26 +++--------- crates/util/Cargo.toml | 5 +-- crates/util/src/lib.rs | 36 +++++++++++++++++ crates/vim/Cargo.toml | 1 + crates/vim/src/normal.rs | 3 +- crates/workspace/Cargo.toml | 1 + crates/workspace/src/workspace.rs | 3 +- crates/zed/Cargo.toml | 1 + crates/zed/src/main.rs | 40 +++++++++--------- crates/zed/src/zed.rs | 5 ++- 58 files changed, 328 insertions(+), 223 deletions(-) create mode 100644 crates/rope/Cargo.toml rename crates/{text => rope}/src/offset_utf16.rs (100%) rename crates/{text => rope}/src/point.rs (100%) rename crates/{text => rope}/src/point_utf16.rs (100%) rename crates/{text => rope}/src/rope.rs (99%) delete mode 100644 crates/text/src/random_char_iter.rs diff --git a/Cargo.lock b/Cargo.lock index 408758f7ad..ebf2d4376c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1045,6 +1045,7 @@ dependencies = [ "editor", "env_logger", "envy", + "fs", "futures", "git", "gpui", @@ -1060,6 +1061,7 @@ dependencies = [ "prometheus", "rand 0.8.5", "reqwest", + "rope", "rpc", "scrypt", "serde", @@ -1551,6 +1553,7 @@ dependencies = [ "language", "postage", "project", + "rope", "serde_json", "settings", "smallvec", @@ -1704,6 +1707,7 @@ dependencies = [ "postage", "project", "rand 0.8.5", + "rope", "rpc", "serde", "settings", @@ -1993,10 +1997,18 @@ dependencies = [ "collections", "fsevent", "futures", - "git", + "git2", + "gpui", + "lazy_static", + "libc", + "log", + "lsp", "parking_lot 0.11.2", + "regex", + "rope", + "serde", + "serde_json", "smol", - "text", "util", ] @@ -2250,6 +2262,7 @@ dependencies = [ "lazy_static", "log", "parking_lot 0.11.2", + "rope", "smol", "sum_tree", "text", @@ -2297,6 +2310,7 @@ dependencies = [ "gpui", "menu", "postage", + "rope", "settings", "text", "workspace", @@ -2884,6 +2898,7 @@ dependencies = [ "collections", "ctor", "env_logger", + "fs", "futures", "fuzzy", "git", @@ -2895,6 +2910,7 @@ dependencies = [ "postage", "rand 0.8.5", "regex", + "rope", "rpc", "serde", "serde_json", @@ -4028,6 +4044,7 @@ dependencies = [ "clock", "collections", "db", + "fs", "fsevent", "futures", "fuzzy", @@ -4036,7 +4053,6 @@ dependencies = [ "ignore", "language", "lazy_static", - "libc", "log", "lsp", "parking_lot 0.11.2", @@ -4045,6 +4061,7 @@ dependencies = [ "rand 0.8.5", "regex", "rocksdb", + "rope", "rpc", "serde", "serde_json", @@ -4531,6 +4548,20 @@ dependencies = [ "librocksdb-sys", ] +[[package]] +name = "rope" +version = "0.1.0" +dependencies = [ + "arrayvec 0.7.2", + "bromberg_sl2", + "gpui", + "log", + "rand 0.8.5", + "smallvec", + "sum_tree", + "util", +] + [[package]] name = "roxmltree" version = "0.14.1" @@ -5588,13 +5619,12 @@ name = "text" version = "0.1.0" dependencies = [ "anyhow", - "arrayvec 0.7.2", - "bromberg_sl2", "clock", "collections", "ctor", "digest 0.9.0", "env_logger", + "fs", "gpui", "lazy_static", "log", @@ -5602,6 +5632,7 @@ dependencies = [ "postage", "rand 0.8.5", "regex", + "rope", "smallvec", "sum_tree", "util", @@ -6504,6 +6535,7 @@ dependencies = [ "language", "log", "project", + "rope", "search", "serde", "settings", @@ -7192,6 +7224,7 @@ dependencies = [ "collections", "context_menu", "drag_and_drop", + "fs", "futures", "gpui", "language", @@ -7275,6 +7308,7 @@ dependencies = [ "editor", "env_logger", "file_finder", + "fs", "fsevent", "futures", "fuzzy", diff --git a/Cargo.toml b/Cargo.toml index b64df300c0..8d2a3fcc40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ resolver = "2" [workspace.dependencies] serde = { version = "1.0", features = ["derive", "rc"] } serde_json = { version = "1.0", features = ["preserve_order", "raw_value"] } +rand = { version = "0.8" } [patch.crates-io] tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "366210ae925d7ea0891bc7a0c738f60c77c04d7b" } diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index 840199c2bb..cf6b7f8b68 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -16,7 +16,8 @@ required-features = ["seed-support"] collections = { path = "../collections" } rpc = { path = "../rpc" } util = { path = "../util" } - +fs = { path = "../fs" } +rope = { path = "../rope" } anyhow = "1.0.40" async-trait = "0.1.50" async-tungstenite = "0.16" diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index bdd240fda8..a0a6f5feea 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -15,6 +15,7 @@ use editor::{ self, ConfirmCodeAction, ConfirmCompletion, ConfirmRename, Editor, Redo, Rename, ToOffset, ToggleCodeActions, Undo, }; +use fs::{FakeFs, Fs as _, LineEnding}; use futures::{channel::mpsc, Future, StreamExt as _}; use gpui::{ executor::{self, Deterministic}, @@ -24,17 +25,16 @@ use gpui::{ }; use language::{ range_to_lsp, tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language, - LanguageConfig, LanguageRegistry, LineEnding, OffsetRangeExt, Point, Rope, + LanguageConfig, LanguageRegistry, OffsetRangeExt, Rope, }; use lsp::{self, FakeLanguageServer}; use parking_lot::Mutex; use project::{ - fs::{FakeFs, Fs as _}, - search::SearchQuery, - worktree::WorktreeHandle, - DiagnosticSummary, Project, ProjectPath, ProjectStore, WorktreeId, + search::SearchQuery, worktree::WorktreeHandle, DiagnosticSummary, Project, ProjectPath, + ProjectStore, WorktreeId, }; use rand::prelude::*; +use rope::point::Point; use rpc::PeerId; use serde_json::json; use settings::{Formatter, Settings}; diff --git a/crates/diagnostics/Cargo.toml b/crates/diagnostics/Cargo.toml index 616f69117f..c4b851917e 100644 --- a/crates/diagnostics/Cargo.toml +++ b/crates/diagnostics/Cargo.toml @@ -15,6 +15,7 @@ editor = { path = "../editor" } language = { path = "../language" } gpui = { path = "../gpui" } project = { path = "../project" } +rope = { path = "../rope" } settings = { path = "../settings" } theme = { path = "../theme" } util = { path = "../util" } diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 3111d7a9f1..608b333d0d 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -14,10 +14,10 @@ use gpui::{ ViewHandle, WeakViewHandle, }; use language::{ - Anchor, Bias, Buffer, Diagnostic, DiagnosticEntry, DiagnosticSeverity, Point, Selection, - SelectionGoal, + Anchor, Bias, Buffer, Diagnostic, DiagnosticEntry, DiagnosticSeverity, Selection, SelectionGoal, }; use project::{DiagnosticSummary, Project, ProjectPath}; +use rope::point::Point; use serde_json::json; use settings::Settings; use smallvec::SmallVec; @@ -738,7 +738,8 @@ mod tests { DisplayPoint, }; use gpui::TestAppContext; - use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, PointUtf16}; + use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity}; + use rope::point_utf16::PointUtf16; use serde_json::json; use unindent::Unindent as _; use workspace::AppState; diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index db634376d0..4e0a10b70d 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -30,6 +30,7 @@ gpui = { path = "../gpui" } language = { path = "../language" } lsp = { path = "../lsp" } project = { path = "../project" } +rope = { path = "../rope" } rpc = { path = "../rpc" } settings = { path = "../settings" } snippet = { path = "../snippet" } diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 58fc2e4fe7..525af0d599 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -11,7 +11,8 @@ use gpui::{ fonts::{FontId, HighlightStyle}, Entity, ModelContext, ModelHandle, }; -use language::{OffsetUtf16, Point, Subscription as BufferSubscription}; +use language::Subscription as BufferSubscription; +use rope::{offset_utf16::OffsetUtf16, point::Point}; use settings::Settings; use std::{any::TypeId, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc}; use sum_tree::{Bias, TreeMap}; @@ -565,7 +566,7 @@ pub mod tests { use super::*; use crate::{movement, test::marked_display_snapshot}; use gpui::{color::Color, elements::*, test::observe, MutableAppContext}; - use language::{Buffer, Language, LanguageConfig, RandomCharIter, SelectionGoal}; + use language::{Buffer, Language, LanguageConfig, SelectionGoal}; use rand::{prelude::*, Rng}; use smol::stream::StreamExt; use std::{env, sync::Arc}; @@ -609,7 +610,9 @@ pub mod tests { let buffer = cx.update(|cx| { if rng.gen() { let len = rng.gen_range(0..10); - let text = RandomCharIter::new(&mut rng).take(len).collect::(); + let text = util::RandomCharIter::new(&mut rng) + .take(len) + .collect::(); MultiBuffer::build_simple(&text, cx) } else { MultiBuffer::build_random(&mut rng, cx) diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index 210daccac2..f0f2720f1a 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -7,6 +7,7 @@ use collections::{Bound, HashMap, HashSet}; use gpui::{ElementBox, RenderContext}; use language::{BufferSnapshot, Chunk, Patch}; use parking_lot::Mutex; +use rope::point::Point; use std::{ cell::RefCell, cmp::{self, Ordering}, @@ -18,7 +19,7 @@ use std::{ }, }; use sum_tree::{Bias, SumTree}; -use text::{Edit, Point}; +use text::Edit; const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize]; @@ -42,7 +43,7 @@ pub struct BlockSnapshot { pub struct BlockId(usize); #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] -pub struct BlockPoint(pub super::Point); +pub struct BlockPoint(pub Point); #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] struct BlockRow(u32); @@ -994,7 +995,7 @@ mod tests { use rand::prelude::*; use settings::Settings; use std::env; - use text::RandomCharIter; + use util::RandomCharIter; #[gpui::test] fn test_offset_for_row() { diff --git a/crates/editor/src/display_map/fold_map.rs b/crates/editor/src/display_map/fold_map.rs index c17cfa39f2..5bd1670542 100644 --- a/crates/editor/src/display_map/fold_map.rs +++ b/crates/editor/src/display_map/fold_map.rs @@ -5,8 +5,9 @@ use crate::{ }; use collections::BTreeMap; use gpui::fonts::HighlightStyle; -use language::{Chunk, Edit, Point, TextSummary}; +use language::{Chunk, Edit, TextSummary}; use parking_lot::Mutex; +use rope::point::Point; use std::{ any::TypeId, cmp::{self, Ordering}, @@ -18,11 +19,11 @@ use std::{ use sum_tree::{Bias, Cursor, FilterCursor, SumTree}; #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] -pub struct FoldPoint(pub super::Point); +pub struct FoldPoint(pub Point); impl FoldPoint { pub fn new(row: u32, column: u32) -> Self { - Self(super::Point::new(row, column)) + Self(Point::new(row, column)) } pub fn row(self) -> u32 { @@ -1196,8 +1197,8 @@ mod tests { use settings::Settings; use std::{cmp::Reverse, env, mem, sync::Arc}; use sum_tree::TreeMap; - use text::RandomCharIter; use util::test::sample_text; + use util::RandomCharIter; use Bias::{Left, Right}; #[gpui::test] diff --git a/crates/editor/src/display_map/tab_map.rs b/crates/editor/src/display_map/tab_map.rs index 4d89767a19..b7d8fac770 100644 --- a/crates/editor/src/display_map/tab_map.rs +++ b/crates/editor/src/display_map/tab_map.rs @@ -3,11 +3,12 @@ use super::{ TextHighlights, }; use crate::MultiBufferSnapshot; -use language::{rope, Chunk}; +use language::Chunk; use parking_lot::Mutex; +use rope; +use rope::point::Point; use std::{cmp, mem, num::NonZeroU32, ops::Range}; use sum_tree::Bias; -use text::Point; pub struct TabMap(Mutex); @@ -332,11 +333,11 @@ impl TabSnapshot { } #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] -pub struct TabPoint(pub super::Point); +pub struct TabPoint(pub Point); impl TabPoint { pub fn new(row: u32, column: u32) -> Self { - Self(super::Point::new(row, column)) + Self(Point::new(row, column)) } pub fn zero() -> Self { @@ -352,8 +353,8 @@ impl TabPoint { } } -impl From for TabPoint { - fn from(point: super::Point) -> Self { +impl From for TabPoint { + fn from(point: Point) -> Self { Self(point) } } @@ -362,7 +363,7 @@ pub type TabEdit = text::Edit; #[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct TextSummary { - pub lines: super::Point, + pub lines: Point, pub first_line_chars: u32, pub last_line_chars: u32, pub longest_row: u32, @@ -485,7 +486,6 @@ mod tests { use super::*; use crate::{display_map::fold_map::FoldMap, MultiBuffer}; use rand::{prelude::StdRng, Rng}; - use text::{RandomCharIter, Rope}; #[test] fn test_expand_tabs() { @@ -508,7 +508,9 @@ mod tests { let tab_size = NonZeroU32::new(rng.gen_range(1..=4)).unwrap(); let len = rng.gen_range(0..30); let buffer = if rng.gen() { - let text = RandomCharIter::new(&mut rng).take(len).collect::(); + let text = util::RandomCharIter::new(&mut rng) + .take(len) + .collect::(); MultiBuffer::build_simple(&text, cx) } else { MultiBuffer::build_random(&mut rng, cx) @@ -522,7 +524,7 @@ mod tests { log::info!("FoldMap text: {:?}", folds_snapshot.text()); let (_, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), tab_size); - let text = Rope::from(tabs_snapshot.text().as_str()); + let text = rope::Rope::from(tabs_snapshot.text().as_str()); log::info!( "TabMap text (tab size: {}): {:?}", tab_size, diff --git a/crates/editor/src/display_map/wrap_map.rs b/crates/editor/src/display_map/wrap_map.rs index ee6ce2860d..42156b905f 100644 --- a/crates/editor/src/display_map/wrap_map.rs +++ b/crates/editor/src/display_map/wrap_map.rs @@ -3,13 +3,14 @@ use super::{ tab_map::{self, TabEdit, TabPoint, TabSnapshot}, TextHighlights, }; -use crate::{MultiBufferSnapshot, Point}; +use crate::MultiBufferSnapshot; use gpui::{ fonts::FontId, text_layout::LineWrapper, Entity, ModelContext, ModelHandle, MutableAppContext, Task, }; use language::Chunk; use lazy_static::lazy_static; +use rope::point::Point; use smol::future::yield_now; use std::{cmp, collections::VecDeque, mem, ops::Range, time::Duration}; use sum_tree::{Bias, Cursor, SumTree}; @@ -52,7 +53,7 @@ struct TransformSummary { } #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] -pub struct WrapPoint(pub super::Point); +pub struct WrapPoint(pub Point); pub struct WrapChunks<'a> { input_chunks: tab_map::TabChunks<'a>, @@ -959,7 +960,7 @@ impl SumTreeExt for SumTree { impl WrapPoint { pub fn new(row: u32, column: u32) -> Self { - Self(super::Point::new(row, column)) + Self(Point::new(row, column)) } pub fn row(self) -> u32 { @@ -1029,7 +1030,6 @@ mod tests { MultiBuffer, }; use gpui::test::observe; - use language::RandomCharIter; use rand::prelude::*; use settings::Settings; use smol::stream::StreamExt; @@ -1067,7 +1067,9 @@ mod tests { MultiBuffer::build_random(&mut rng, cx) } else { let len = rng.gen_range(0..10); - let text = RandomCharIter::new(&mut rng).take(len).collect::(); + let text = util::RandomCharIter::new(&mut rng) + .take(len) + .collect::(); MultiBuffer::build_simple(&text, cx) } }); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index d1dfed3c85..5f424cd20b 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -43,8 +43,8 @@ pub use items::MAX_TAB_TITLE_LEN; pub use language::{char_kind, CharKind}; use language::{ AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, Diagnostic, - DiagnosticSeverity, IndentKind, IndentSize, Language, OffsetRangeExt, OffsetUtf16, Point, - Selection, SelectionGoal, TransactionId, + DiagnosticSeverity, IndentKind, IndentSize, Language, OffsetRangeExt, Selection, SelectionGoal, + TransactionId, }; use link_go_to_definition::{hide_link_definition, LinkGoToDefinitionState}; pub use multi_buffer::{ @@ -54,6 +54,7 @@ pub use multi_buffer::{ use multi_buffer::{MultiBufferChunks, ToOffsetUtf16}; use ordered_float::OrderedFloat; use project::{FormatTrigger, LocationLink, Project, ProjectPath, ProjectTransaction}; +use rope::{offset_utf16::OffsetUtf16, point::Point}; use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection}; use serde::{Deserialize, Serialize}; use settings::Settings; diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 063dcf6098..e152c85e4e 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -11,9 +11,9 @@ use gpui::{ use indoc::indoc; use language::{FakeLspAdapter, LanguageConfig, LanguageRegistry}; use project::FakeFs; +use rope::point::Point; use settings::EditorSettings; use std::{cell::RefCell, rc::Rc, time::Instant}; -use text::Point; use unindent::Unindent; use util::{ assert_set_eq, diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index a3b5ad43f6..114a1d72c9 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -35,8 +35,9 @@ use gpui::{ WeakViewHandle, }; use json::json; -use language::{Bias, DiagnosticSeverity, OffsetUtf16, Selection}; +use language::{Bias, DiagnosticSeverity, Selection}; use project::ProjectPath; +use rope::offset_utf16::OffsetUtf16; use settings::{GitGutter, Settings}; use smallvec::SmallVec; use std::{ diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index c1082020e5..727ee1f094 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -11,6 +11,7 @@ use gpui::{ }; use language::{Bias, Buffer, File as _, OffsetRangeExt, SelectionGoal}; use project::{File, FormatTrigger, Project, ProjectEntryId, ProjectPath}; +use rope::point::Point; use rpc::proto::{self, update_view}; use settings::Settings; use smallvec::SmallVec; @@ -21,7 +22,7 @@ use std::{ ops::Range, path::{Path, PathBuf}, }; -use text::{Point, Selection}; +use text::Selection; use util::TryFutureExt; use workspace::{ searchable::{Direction, SearchEvent, SearchableItem, SearchableItemHandle}, diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index 0db5cc0812..79d041fbab 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -1,6 +1,7 @@ +use rope::point::Point; + use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint}; use crate::{char_kind, CharKind, ToPoint}; -use language::Point; use std::ops::Range; pub fn left(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint { @@ -273,7 +274,7 @@ pub fn surrounding_word(map: &DisplaySnapshot, position: DisplayPoint) -> Range< mod tests { use super::*; use crate::{test::marked_display_snapshot, Buffer, DisplayMap, ExcerptRange, MultiBuffer}; - use language::Point; + use rope::point::Point; use settings::Settings; #[gpui::test] diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index a0eedb850c..23ee7df657 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -12,6 +12,7 @@ use language::{ DiagnosticEntry, Event, File, IndentSize, Language, OffsetRangeExt, Outline, OutlineItem, Selection, ToOffset as _, ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, }; +use rope::{offset_utf16::OffsetUtf16, point::Point, point_utf16::PointUtf16, TextDimension}; use smallvec::SmallVec; use std::{ borrow::Cow, @@ -27,9 +28,8 @@ use std::{ use sum_tree::{Bias, Cursor, SumTree}; use text::{ locator::Locator, - rope::TextDimension, subscription::{Subscription, Topic}, - Edit, OffsetUtf16, Point, PointUtf16, TextSummary, + Edit, TextSummary, }; use theme::SyntaxTheme; use util::post_inc; @@ -168,7 +168,7 @@ struct ExcerptChunks<'a> { } struct ExcerptBytes<'a> { - content_bytes: language::rope::Bytes<'a>, + content_bytes: rope::Bytes<'a>, footer_height: usize, } @@ -1412,7 +1412,7 @@ impl MultiBuffer { edit_count: usize, cx: &mut ModelContext, ) { - use text::RandomCharIter; + use util::RandomCharIter; let snapshot = self.read(cx); let mut edits: Vec<(Range, Arc)> = Vec::new(); @@ -1451,7 +1451,7 @@ impl MultiBuffer { ) { use rand::prelude::*; use std::env; - use text::RandomCharIter; + use util::RandomCharIter; let max_excerpts = env::var("MAX_EXCERPTS") .map(|i| i.parse().expect("invalid `MAX_EXCERPTS` variable")) @@ -3337,7 +3337,7 @@ mod tests { use rand::prelude::*; use settings::Settings; use std::{env, rc::Rc}; - use text::{Point, RandomCharIter}; + use util::test::sample_text; #[gpui::test] @@ -3955,7 +3955,9 @@ mod tests { } _ => { let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) { - let base_text = RandomCharIter::new(&mut rng).take(10).collect::(); + let base_text = util::RandomCharIter::new(&mut rng) + .take(10) + .collect::(); buffers.push(cx.add_model(|cx| Buffer::new(0, base_text, cx))); buffers.last().unwrap() } else { diff --git a/crates/editor/src/multi_buffer/anchor.rs b/crates/editor/src/multi_buffer/anchor.rs index cb8a1692b9..b30e4b5780 100644 --- a/crates/editor/src/multi_buffer/anchor.rs +++ b/crates/editor/src/multi_buffer/anchor.rs @@ -1,10 +1,10 @@ use super::{ExcerptId, MultiBufferSnapshot, ToOffset, ToOffsetUtf16, ToPoint}; +use rope::{offset_utf16::OffsetUtf16, point::Point, TextDimension}; use std::{ cmp::Ordering, ops::{Range, Sub}, }; use sum_tree::Bias; -use text::{rope::TextDimension, OffsetUtf16, Point}; #[derive(Clone, Eq, PartialEq, Debug, Hash)] pub struct Anchor { diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs index 9d6450f8ec..ed983d22d9 100644 --- a/crates/editor/src/selections_collection.rs +++ b/crates/editor/src/selections_collection.rs @@ -8,7 +8,8 @@ use std::{ use collections::HashMap; use gpui::{AppContext, ModelHandle, MutableAppContext}; use itertools::Itertools; -use language::{rope::TextDimension, Bias, Point, Selection, SelectionGoal, ToPoint}; +use language::{Bias, Selection, SelectionGoal, ToPoint}; +use rope::{point::Point, TextDimension}; use util::post_inc; use crate::{ diff --git a/crates/fs/Cargo.toml b/crates/fs/Cargo.toml index 1a4e325fa4..182d13894d 100644 --- a/crates/fs/Cargo.toml +++ b/crates/fs/Cargo.toml @@ -8,11 +8,23 @@ path = "src/fs.rs" [dependencies] collections = { path = "../collections" } -fsevent = { path = "../fsevent" } +gpui = { path = "../gpui" } +lsp = { path = "../lsp" } +rope = { path = "../rope" } +util = { path = "../util" } anyhow = "1.0.57" async-trait = "0.1" futures = "0.3" +fsevent = { path = "../fsevent" } +lazy_static = "1.4.0" parking_lot = "0.11.1" smol = "1.2.5" -text = { path = "../text" } -util = { path = "../util" } +regex = "1.5" +git2 = { version = "0.15", default-features = false } +serde = { workspace = true } +serde_json = { workspace = true } +log = { version = "0.4.16", features = ["kv_unstable_serde"] } +libc = "0.2" + +[features] +test-support = [] diff --git a/crates/fs/src/fs.rs b/crates/fs/src/fs.rs index c24387844c..26045f2776 100644 --- a/crates/fs/src/fs.rs +++ b/crates/fs/src/fs.rs @@ -3,8 +3,15 @@ pub mod repository; use anyhow::{anyhow, Result}; use fsevent::EventStream; use futures::{future::BoxFuture, Stream, StreamExt}; +use git2::Repository as LibGitRepository; +use lazy_static::lazy_static; use parking_lot::Mutex as SyncMutex; +use regex::Regex; +use repository::GitRepository; +use rope::Rope; use smol::io::{AsyncReadExt, AsyncWriteExt}; +use std::borrow::Cow; +use std::cmp; use std::sync::Arc; use std::{ io, @@ -13,7 +20,6 @@ use std::{ pin::Pin, time::{Duration, SystemTime}, }; -use text::Rope; use util::ResultExt; #[cfg(any(test, feature = "test-support"))] @@ -21,10 +27,14 @@ use collections::{btree_map, BTreeMap}; #[cfg(any(test, feature = "test-support"))] use futures::lock::Mutex; #[cfg(any(test, feature = "test-support"))] -use git::repository::FakeGitRepositoryState; +use repository::FakeGitRepositoryState; #[cfg(any(test, feature = "test-support"))] use std::sync::Weak; +lazy_static! { + static ref CARRIAGE_RETURNS_REGEX: Regex = Regex::new("\r\n|\r").unwrap(); +} + #[derive(Clone, Copy, Debug, PartialEq)] pub enum LineEnding { Unix, @@ -72,7 +82,7 @@ impl LineEnding { } } - fn normalize_arc(text: Arc) -> Arc { + pub fn normalize_arc(text: Arc) -> Arc { if let Cow::Owned(replaced) = CARRIAGE_RETURNS_REGEX.replace_all(&text, "\n") { replaced.into() } else { @@ -141,6 +151,33 @@ pub struct Metadata { pub is_dir: bool, } +impl From for CreateOptions { + fn from(options: lsp::CreateFileOptions) -> Self { + Self { + overwrite: options.overwrite.unwrap_or(false), + ignore_if_exists: options.ignore_if_exists.unwrap_or(false), + } + } +} + +impl From for RenameOptions { + fn from(options: lsp::RenameFileOptions) -> Self { + Self { + overwrite: options.overwrite.unwrap_or(false), + ignore_if_exists: options.ignore_if_exists.unwrap_or(false), + } + } +} + +impl From for RemoveOptions { + fn from(options: lsp::DeleteFileOptions) -> Self { + Self { + recursive: options.recursive.unwrap_or(false), + ignore_if_not_exists: options.ignore_if_not_exists.unwrap_or(false), + } + } +} + pub struct RealFs; #[async_trait::async_trait] @@ -340,7 +377,7 @@ enum FakeFsEntry { inode: u64, mtime: SystemTime, entries: BTreeMap>>, - git_repo_state: Option>>, + git_repo_state: Option>>, }, Symlink { target: PathBuf, @@ -952,7 +989,7 @@ impl Fs for FakeFs { Arc::new(SyncMutex::new(FakeGitRepositoryState::default())) }) .clone(); - Some(git::repository::FakeGitRepository::open(state)) + Some(repository::FakeGitRepository::open(state)) } else { None } diff --git a/crates/git/Cargo.toml b/crates/git/Cargo.toml index b8f3aac0b9..1d15c2b123 100644 --- a/crates/git/Cargo.toml +++ b/crates/git/Cargo.toml @@ -9,7 +9,7 @@ path = "src/git.rs" [dependencies] anyhow = "1.0.38" clock = { path = "../clock" } -git2 = { version = "0.15", default-features = false } +rope = { path = "../rope" } lazy_static = "1.4.0" sum_tree = { path = "../sum_tree" } text = { path = "../text" } @@ -20,6 +20,7 @@ smol = "1.2" parking_lot = "0.11.1" async-trait = "0.1" futures = "0.3" +git2 = { version = "0.15", default-features = false } [dev-dependencies] unindent = "0.1.7" diff --git a/crates/git/src/diff.rs b/crates/git/src/diff.rs index 4191e5d260..7f3f6101ce 100644 --- a/crates/git/src/diff.rs +++ b/crates/git/src/diff.rs @@ -1,7 +1,8 @@ use std::ops::Range; +use rope::point::Point; use sum_tree::SumTree; -use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point}; +use text::{Anchor, BufferSnapshot, OffsetRangeExt}; pub use git2 as libgit; use libgit::{DiffLineType as GitDiffLineType, DiffOptions as GitOptions, Patch as GitPatch}; diff --git a/crates/git/src/git.rs b/crates/git/src/git.rs index 36f54e706a..b1b885eca2 100644 --- a/crates/git/src/git.rs +++ b/crates/git/src/git.rs @@ -4,7 +4,6 @@ pub use git2 as libgit; pub use lazy_static::lazy_static; pub mod diff; -pub mod repository; lazy_static! { pub static ref DOT_GIT: &'static OsStr = OsStr::new(".git"); diff --git a/crates/go_to_line/Cargo.toml b/crates/go_to_line/Cargo.toml index 93ae96f93e..d69b1f239c 100644 --- a/crates/go_to_line/Cargo.toml +++ b/crates/go_to_line/Cargo.toml @@ -13,5 +13,6 @@ gpui = { path = "../gpui" } menu = { path = "../menu" } settings = { path = "../settings" } text = { path = "../text" } +rope = { path = "../rope" } workspace = { path = "../workspace" } postage = { version = "0.4", features = ["futures-traits"] } diff --git a/crates/go_to_line/src/go_to_line.rs b/crates/go_to_line/src/go_to_line.rs index 3ca50cee42..51ff87a943 100644 --- a/crates/go_to_line/src/go_to_line.rs +++ b/crates/go_to_line/src/go_to_line.rs @@ -4,8 +4,9 @@ use gpui::{ MutableAppContext, RenderContext, View, ViewContext, ViewHandle, }; use menu::{Cancel, Confirm}; +use rope::point::Point; use settings::Settings; -use text::{Bias, Point}; +use text::Bias; use workspace::Workspace; actions!(go_to_line, [Toggle]); diff --git a/crates/language/Cargo.toml b/crates/language/Cargo.toml index e5b6f4c79b..54d2147929 100644 --- a/crates/language/Cargo.toml +++ b/crates/language/Cargo.toml @@ -25,9 +25,11 @@ client = { path = "../client" } clock = { path = "../clock" } collections = { path = "../collections" } fuzzy = { path = "../fuzzy" } +fs = { path = "../fs" } git = { path = "../git" } gpui = { path = "../gpui" } lsp = { path = "../lsp" } +rope = { path = "../rope" } rpc = { path = "../rpc" } settings = { path = "../settings" } sum_tree = { path = "../sum_tree" } diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index a3c0c54d01..56aad18ead 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -13,9 +13,11 @@ use crate::{ }; use anyhow::{anyhow, Result}; use clock::ReplicaId; +use fs::LineEnding; use futures::FutureExt as _; use gpui::{fonts::HighlightStyle, AppContext, Entity, ModelContext, MutableAppContext, Task}; use parking_lot::Mutex; +use rope::point::Point; use settings::Settings; use similar::{ChangeTag, TextDiff}; use smol::future::yield_now; @@ -38,7 +40,7 @@ use sum_tree::TreeMap; use text::operation_queue::OperationQueue; pub use text::{Buffer as TextBuffer, BufferSnapshot as TextBufferSnapshot, Operation as _, *}; use theme::SyntaxTheme; -use util::TryFutureExt as _; +use util::{RandomCharIter, TryFutureExt as _}; #[cfg(any(test, feature = "test-support"))] pub use {tree_sitter_rust, tree_sitter_typescript}; @@ -368,7 +370,7 @@ impl Buffer { file, ); this.text.set_line_ending(proto::deserialize_line_ending( - proto::LineEnding::from_i32(message.line_ending) + rpc::proto::LineEnding::from_i32(message.line_ending) .ok_or_else(|| anyhow!("missing line_ending"))?, )); Ok(this) @@ -1633,9 +1635,7 @@ impl Buffer { last_end = Some(range.end); let new_text_len = rng.gen_range(0..10); - let new_text: String = crate::random_char_iter::RandomCharIter::new(&mut *rng) - .take(new_text_len) - .collect(); + let new_text: String = RandomCharIter::new(&mut *rng).take(new_text_len).collect(); edits.push((range, new_text)); } diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index 3cfddce71f..313c843b02 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -1,9 +1,11 @@ use super::*; use clock::ReplicaId; use collections::BTreeMap; +use fs::LineEnding; use gpui::{ModelHandle, MutableAppContext}; use proto::deserialize_operation; use rand::prelude::*; +use rope::point::Point; use settings::Settings; use std::{ cell::RefCell, @@ -14,7 +16,7 @@ use std::{ }; use text::network::Network; use unindent::Unindent as _; -use util::{post_inc, test::marked_text_ranges}; +use util::{post_inc, test::marked_text_ranges, RandomCharIter}; #[cfg(test)] #[ctor::ctor] diff --git a/crates/language/src/diagnostic_set.rs b/crates/language/src/diagnostic_set.rs index b52327cac0..dfbc32149c 100644 --- a/crates/language/src/diagnostic_set.rs +++ b/crates/language/src/diagnostic_set.rs @@ -1,12 +1,13 @@ use crate::Diagnostic; use collections::HashMap; +use rope::point_utf16::PointUtf16; use std::{ cmp::{Ordering, Reverse}, iter, ops::Range, }; use sum_tree::{self, Bias, SumTree}; -use text::{Anchor, FromAnchor, PointUtf16, ToOffset}; +use text::{Anchor, FromAnchor, ToOffset}; #[derive(Clone, Debug, Default)] pub struct DiagnosticSet { diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index bb75edbc32..4f8615606c 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -22,6 +22,7 @@ use lazy_static::lazy_static; use parking_lot::{Mutex, RwLock}; use postage::watch; use regex::Regex; +use rope::point_utf16::PointUtf16; use serde::{de, Deserialize, Deserializer}; use serde_json::Value; use std::{ diff --git a/crates/language/src/proto.rs b/crates/language/src/proto.rs index fddfb7961f..9e3ee7d46b 100644 --- a/crates/language/src/proto.rs +++ b/crates/language/src/proto.rs @@ -8,19 +8,19 @@ use rpc::proto; use std::{ops::Range, sync::Arc}; use text::*; -pub use proto::{BufferState, LineEnding, Operation, SelectionSet}; +pub use proto::{BufferState, Operation, SelectionSet}; -pub fn deserialize_line_ending(message: proto::LineEnding) -> text::LineEnding { +pub fn deserialize_line_ending(message: proto::LineEnding) -> fs::LineEnding { match message { - LineEnding::Unix => text::LineEnding::Unix, - LineEnding::Windows => text::LineEnding::Windows, + proto::LineEnding::Unix => fs::LineEnding::Unix, + proto::LineEnding::Windows => fs::LineEnding::Windows, } } -pub fn serialize_line_ending(message: text::LineEnding) -> proto::LineEnding { +pub fn serialize_line_ending(message: fs::LineEnding) -> proto::LineEnding { match message { - text::LineEnding::Unix => proto::LineEnding::Unix, - text::LineEnding::Windows => proto::LineEnding::Windows, + fs::LineEnding::Unix => proto::LineEnding::Unix, + fs::LineEnding::Windows => proto::LineEnding::Windows, } } diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index 64145e535b..3992d41081 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -1,6 +1,7 @@ use crate::{Grammar, InjectionConfig, Language, LanguageRegistry}; use lazy_static::lazy_static; use parking_lot::Mutex; +use rope::point::Point; use std::{ borrow::Cow, cell::RefCell, @@ -10,7 +11,7 @@ use std::{ sync::Arc, }; use sum_tree::{Bias, SeekTarget, SumTree}; -use text::{rope, Anchor, BufferSnapshot, OffsetRangeExt, Point, Rope, ToOffset, ToPoint}; +use text::{Anchor, BufferSnapshot, OffsetRangeExt, Rope, ToOffset, ToPoint}; use tree_sitter::{ Node, Parser, Query, QueryCapture, QueryCaptures, QueryCursor, QueryMatches, Tree, }; @@ -1242,7 +1243,7 @@ mod tests { use crate::LanguageConfig; use rand::rngs::StdRng; use std::env; - use text::{Buffer, Point}; + use text::Buffer; use unindent::Unindent as _; use util::test::marked_text_ranges; diff --git a/crates/project/Cargo.toml b/crates/project/Cargo.toml index 1e45e3c6ed..7a41318b86 100644 --- a/crates/project/Cargo.toml +++ b/crates/project/Cargo.toml @@ -22,12 +22,14 @@ client = { path = "../client" } clock = { path = "../clock" } collections = { path = "../collections" } db = { path = "../db" } +fs = { path = "../fs" } fsevent = { path = "../fsevent" } fuzzy = { path = "../fuzzy" } git = { path = "../git" } gpui = { path = "../gpui" } language = { path = "../language" } lsp = { path = "../lsp" } +rope = { path = "../rope" } rpc = { path = "../rpc" } settings = { path = "../settings" } sum_tree = { path = "../sum_tree" } @@ -38,7 +40,6 @@ async-trait = "0.1" futures = "0.3" ignore = "0.4" lazy_static = "1.4.0" -libc = "0.2" log = { version = "0.4.16", features = ["kv_unstable_serde"] } parking_lot = "0.11.1" postage = { version = "0.4.1", features = ["futures-traits"] } @@ -58,6 +59,7 @@ rocksdb = "0.18" client = { path = "../client", features = ["test-support"] } collections = { path = "../collections", features = ["test-support"] } db = { path = "../db", 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"] } diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index 37f6e76340..42098d2e8b 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -8,10 +8,11 @@ use gpui::{AppContext, AsyncAppContext, ModelHandle}; use language::{ point_from_lsp, point_to_lsp, proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version}, - range_from_lsp, Anchor, Bias, Buffer, CachedLspAdapter, PointUtf16, ToPointUtf16, + range_from_lsp, Anchor, Bias, Buffer, CachedLspAdapter, ToPointUtf16, }; use lsp::{DocumentHighlightKind, LanguageServer, ServerCapabilities}; use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag}; +use rope::point_utf16::PointUtf16; use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc}; #[async_trait(?Send)] diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 84f45070fd..11792bcf1e 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1,4 +1,3 @@ -pub mod fs; mod ignore; mod lsp_command; pub mod search; @@ -25,9 +24,8 @@ use language::{ }, range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, CachedLspAdapter, CharKind, CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Event as BufferEvent, - File as _, Language, LanguageRegistry, LanguageServerName, LineEnding, LocalFile, - OffsetRangeExt, Operation, Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, - Transaction, + File as _, Language, LanguageRegistry, LanguageServerName, LocalFile, OffsetRangeExt, + Operation, Patch, TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, }; use lsp::{ DiagnosticSeverity, DiagnosticTag, DocumentHighlightKind, LanguageServer, LanguageString, @@ -37,6 +35,7 @@ use lsp_command::*; use parking_lot::Mutex; use postage::watch; use rand::prelude::*; +use rope::point_utf16::PointUtf16; use search::SearchQuery; use serde::Serialize; use settings::{FormatOnSave, Formatter, Settings}; @@ -6019,33 +6018,6 @@ impl> From<(WorktreeId, P)> for ProjectPath { } } -impl From for fs::CreateOptions { - fn from(options: lsp::CreateFileOptions) -> Self { - Self { - overwrite: options.overwrite.unwrap_or(false), - ignore_if_exists: options.ignore_if_exists.unwrap_or(false), - } - } -} - -impl From for fs::RenameOptions { - fn from(options: lsp::RenameFileOptions) -> Self { - Self { - overwrite: options.overwrite.unwrap_or(false), - ignore_if_exists: options.ignore_if_exists.unwrap_or(false), - } - } -} - -impl From for fs::RemoveOptions { - fn from(options: lsp::DeleteFileOptions) -> Self { - Self { - recursive: options.recursive.unwrap_or(false), - ignore_if_not_exists: options.ignore_if_not_exists.unwrap_or(false), - } - } -} - fn serialize_symbol(symbol: &Symbol) -> proto::Symbol { proto::Symbol { language_server_name: symbol.language_server_name.0.to_string(), diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index ceb5d033a7..12da0a75db 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -1,12 +1,14 @@ use crate::{worktree::WorktreeHandle, Event, *}; -use fs::RealFs; +use fs::LineEnding; +use fs::{FakeFs, RealFs}; use futures::{future, StreamExt}; use gpui::{executor::Deterministic, test::subscribe}; use language::{ tree_sitter_rust, tree_sitter_typescript, Diagnostic, FakeLspAdapter, LanguageConfig, - LineEnding, OffsetRangeExt, Point, ToPoint, + OffsetRangeExt, ToPoint, }; use lsp::Url; +use rope::point::Point; use serde_json::json; use std::{cell::RefCell, os::unix, rc::Rc, task::Poll}; use unindent::Unindent as _; diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 968c2d4bc7..3073392a52 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -1,14 +1,12 @@ -use super::{ - fs::{self, Fs}, - ignore::IgnoreStack, - DiagnosticSummary, -}; +use super::{ignore::IgnoreStack, DiagnosticSummary}; use crate::{copy_recursive, ProjectEntryId, RemoveOptions}; use ::ignore::gitignore::{Gitignore, GitignoreBuilder}; use anyhow::{anyhow, Context, Result}; use client::{proto, Client}; use clock::ReplicaId; use collections::{HashMap, VecDeque}; +use fs::LineEnding; +use fs::{repository::GitRepository, Fs}; use futures::{ channel::{ mpsc::{self, UnboundedSender}, @@ -17,7 +15,6 @@ use futures::{ Stream, StreamExt, }; use fuzzy::CharBag; -use git::repository::GitRepository; use git::{DOT_GIT, GITIGNORE}; use gpui::{ executor, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, @@ -25,13 +22,14 @@ use gpui::{ }; use language::{ proto::{deserialize_version, serialize_line_ending, serialize_version}, - Buffer, DiagnosticEntry, LineEnding, PointUtf16, Rope, + Buffer, DiagnosticEntry, Rope, }; use parking_lot::Mutex; use postage::{ prelude::{Sink as _, Stream as _}, watch, }; +use rope::point_utf16::PointUtf16; use smol::channel::{self, Sender}; use std::{ @@ -2970,11 +2968,10 @@ async fn send_worktree_update(client: &Arc, update: proto::UpdateWorktre #[cfg(test)] mod tests { use super::*; - use crate::fs::FakeFs; use anyhow::Result; use client::test::FakeHttpClient; - use fs::RealFs; - use git::repository::FakeGitRepository; + use fs::repository::FakeGitRepository; + use fs::{FakeFs, RealFs}; use gpui::{executor::Deterministic, TestAppContext}; use rand::prelude::*; use serde_json::json; diff --git a/crates/rope/Cargo.toml b/crates/rope/Cargo.toml new file mode 100644 index 0000000000..0f754c1fb3 --- /dev/null +++ b/crates/rope/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "rope" +version = "0.1.0" +edition = "2021" + +[lib] +path = "src/rope.rs" + +[dependencies] +bromberg_sl2 = "0.6" +smallvec = { version = "1.6", features = ["union"] } +sum_tree = { path = "../sum_tree" } +arrayvec = "0.7.1" +log = { version = "0.4.16", features = ["kv_unstable_serde"] } + + +[dev-dependencies] +rand = "0.8.3" +util = { path = "../util", features = ["test-support"] } +gpui = { path = "../gpui", features = ["test-support"] } diff --git a/crates/text/src/offset_utf16.rs b/crates/rope/src/offset_utf16.rs similarity index 100% rename from crates/text/src/offset_utf16.rs rename to crates/rope/src/offset_utf16.rs diff --git a/crates/text/src/point.rs b/crates/rope/src/point.rs similarity index 100% rename from crates/text/src/point.rs rename to crates/rope/src/point.rs diff --git a/crates/text/src/point_utf16.rs b/crates/rope/src/point_utf16.rs similarity index 100% rename from crates/text/src/point_utf16.rs rename to crates/rope/src/point_utf16.rs diff --git a/crates/text/src/rope.rs b/crates/rope/src/rope.rs similarity index 99% rename from crates/text/src/rope.rs rename to crates/rope/src/rope.rs index e148c048bb..39dc9dc049 100644 --- a/crates/text/src/rope.rs +++ b/crates/rope/src/rope.rs @@ -1,7 +1,12 @@ -use super::Point; -use crate::{OffsetUtf16, PointUtf16}; +pub mod offset_utf16; +pub mod point; +pub mod point_utf16; + use arrayvec::ArrayString; use bromberg_sl2::{DigestString, HashMatrix}; +use offset_utf16::OffsetUtf16; +use point::Point; +use point_utf16::PointUtf16; use smallvec::SmallVec; use std::{cmp, fmt, io, mem, ops::Range, str}; use sum_tree::{Bias, Dimension, SumTree}; @@ -1073,9 +1078,9 @@ fn find_split_ix(text: &str) -> usize { #[cfg(test)] mod tests { use super::*; - use crate::random_char_iter::RandomCharIter; use rand::prelude::*; use std::{cmp::Ordering, env, io::Read}; + use util::RandomCharIter; use Bias::{Left, Right}; #[test] diff --git a/crates/text/Cargo.toml b/crates/text/Cargo.toml index 0d47525021..ad960ec93e 100644 --- a/crates/text/Cargo.toml +++ b/crates/text/Cargo.toml @@ -14,23 +14,23 @@ test-support = ["rand"] clock = { path = "../clock" } collections = { path = "../collections" } fs = { path = "../fs" } +rope = { path = "../rope" } sum_tree = { path = "../sum_tree" } anyhow = "1.0.38" -arrayvec = "0.7.1" digest = { version = "0.9", features = ["std"] } -bromberg_sl2 = "0.6" lazy_static = "1.4" log = { version = "0.4.16", features = ["kv_unstable_serde"] } parking_lot = "0.11" postage = { version = "0.4.1", features = ["futures-traits"] } rand = { version = "0.8.3", optional = true } -regex = "1.5" smallvec = { version = "1.6", features = ["union"] } +util = { path = "../util" } +regex = "1.5" + [dev-dependencies] collections = { path = "../collections", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } -util = { path = "../util", features = ["test-support"] } ctor = "0.1" env_logger = "0.9" rand = "0.8.3" diff --git a/crates/text/src/anchor.rs b/crates/text/src/anchor.rs index 9f70ae1cc7..024c7e643b 100644 --- a/crates/text/src/anchor.rs +++ b/crates/text/src/anchor.rs @@ -1,9 +1,10 @@ -use super::{Point, ToOffset}; -use crate::{rope::TextDimension, BufferSnapshot, PointUtf16, ToPoint, ToPointUtf16}; use anyhow::Result; +use rope::{point::Point, point_utf16::PointUtf16, TextDimension}; use std::{cmp::Ordering, fmt::Debug, ops::Range}; use sum_tree::Bias; +use crate::{BufferSnapshot, ToOffset, ToPoint, ToPointUtf16}; + #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, Default)] pub struct Anchor { pub timestamp: clock::Local, diff --git a/crates/text/src/random_char_iter.rs b/crates/text/src/random_char_iter.rs deleted file mode 100644 index 04cdcd3524..0000000000 --- a/crates/text/src/random_char_iter.rs +++ /dev/null @@ -1,36 +0,0 @@ -use rand::prelude::*; - -pub struct RandomCharIter(T); - -impl RandomCharIter { - pub fn new(rng: T) -> Self { - Self(rng) - } -} - -impl Iterator for RandomCharIter { - type Item = char; - - fn next(&mut self) -> Option { - if std::env::var("SIMPLE_TEXT").map_or(false, |v| !v.is_empty()) { - return if self.0.gen_range(0..100) < 5 { - Some('\n') - } else { - Some(self.0.gen_range(b'a'..b'z' + 1).into()) - }; - } - - match self.0.gen_range(0..100) { - // whitespace - 0..=19 => [' ', '\n', '\r', '\t'].choose(&mut self.0).copied(), - // two-byte greek letters - 20..=32 => char::from_u32(self.0.gen_range(('α' as u32)..('ω' as u32 + 1))), - // // three-byte characters - 33..=45 => ['✋', '✅', '❌', '❎', '⭐'].choose(&mut self.0).copied(), - // // four-byte characters - 46..=58 => ['🍐', '🏀', '🍗', '🎉'].choose(&mut self.0).copied(), - // ascii letters - _ => Some(self.0.gen_range(b'a'..b'z' + 1).into()), - } - } -} diff --git a/crates/text/src/selection.rs b/crates/text/src/selection.rs index e5acbd21bc..881fc8c432 100644 --- a/crates/text/src/selection.rs +++ b/crates/text/src/selection.rs @@ -1,5 +1,7 @@ -use crate::Anchor; -use crate::{rope::TextDimension, BufferSnapshot}; +use rope::TextDimension; + +use crate::{Anchor, BufferSnapshot}; + use std::cmp::Ordering; use std::ops::Range; diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index 52859b7515..2196e870f2 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -2,14 +2,8 @@ mod anchor; pub mod locator; #[cfg(any(test, feature = "test-support"))] pub mod network; -mod offset_utf16; pub mod operation_queue; mod patch; -mod point; -mod point_utf16; -#[cfg(any(test, feature = "test-support"))] -pub mod random_char_iter; -pub mod rope; mod selection; pub mod subscription; #[cfg(test)] @@ -20,22 +14,15 @@ pub use anchor::*; use anyhow::Result; use clock::ReplicaId; use collections::{HashMap, HashSet}; -use lazy_static::lazy_static; +use fs::LineEnding; use locator::Locator; -pub use offset_utf16::*; use operation_queue::OperationQueue; pub use patch::Patch; -pub use point::*; -pub use point_utf16::*; use postage::{barrier, oneshot, prelude::*}; -#[cfg(any(test, feature = "test-support"))] -pub use random_char_iter::*; -use regex::Regex; -use rope::TextDimension; +use rope::{offset_utf16::OffsetUtf16, point::Point, point_utf16::PointUtf16, TextDimension}; pub use rope::{Chunks, Rope, TextSummary}; pub use selection::*; use std::{ - borrow::Cow, cmp::{self, Ordering, Reverse}, future::Future, iter::Iterator, @@ -49,9 +36,8 @@ pub use sum_tree::Bias; use sum_tree::{FilterCursor, SumTree, TreeMap}; use undo_map::UndoMap; -lazy_static! { - static ref CARRIAGE_RETURNS_REGEX: Regex = Regex::new("\r\n|\r").unwrap(); -} +#[cfg(any(test, feature = "test-support"))] +use util::RandomCharIter; pub type TransactionId = clock::Local; @@ -1458,9 +1444,7 @@ impl Buffer { last_end = Some(range.end); let new_text_len = rng.gen_range(0..10); - let new_text: String = crate::random_char_iter::RandomCharIter::new(&mut *rng) - .take(new_text_len) - .collect(); + let new_text: String = RandomCharIter::new(&mut *rng).take(new_text_len).collect(); edits.push((range, new_text.into())); } diff --git a/crates/util/Cargo.toml b/crates/util/Cargo.toml index 78416aa5b5..c083137156 100644 --- a/crates/util/Cargo.toml +++ b/crates/util/Cargo.toml @@ -7,21 +7,20 @@ edition = "2021" doctest = false [features] -test-support = ["rand", "serde_json", "tempdir", "git2"] +test-support = ["serde_json", "tempdir", "git2"] [dependencies] anyhow = "1.0.38" futures = "0.3" log = { version = "0.4.16", features = ["kv_unstable_serde"] } lazy_static = "1.4.0" -rand = { version = "0.8", optional = true } +rand = { workspace = true } tempdir = { version = "0.3.7", optional = true } serde_json = { version = "1.0", features = ["preserve_order"], optional = true } git2 = { version = "0.15", default-features = false, optional = true } [dev-dependencies] -rand = { version = "0.8" } tempdir = { version = "0.3.7" } serde_json = { version = "1.0", features = ["preserve_order"] } git2 = { version = "0.15", default-features = false } diff --git a/crates/util/src/lib.rs b/crates/util/src/lib.rs index 97f409f410..e35f2df7d4 100644 --- a/crates/util/src/lib.rs +++ b/crates/util/src/lib.rs @@ -2,6 +2,7 @@ pub mod test; use futures::Future; +use rand::{seq::SliceRandom, Rng}; use std::{ cmp::Ordering, ops::AddAssign, @@ -155,6 +156,41 @@ pub fn defer(f: F) -> impl Drop { Defer(Some(f)) } +pub struct RandomCharIter(T); + +impl RandomCharIter { + pub fn new(rng: T) -> Self { + Self(rng) + } +} + +impl Iterator for RandomCharIter { + type Item = char; + + fn next(&mut self) -> Option { + if std::env::var("SIMPLE_TEXT").map_or(false, |v| !v.is_empty()) { + return if self.0.gen_range(0..100) < 5 { + Some('\n') + } else { + Some(self.0.gen_range(b'a'..b'z' + 1).into()) + }; + } + + match self.0.gen_range(0..100) { + // whitespace + 0..=19 => [' ', '\n', '\r', '\t'].choose(&mut self.0).copied(), + // two-byte greek letters + 20..=32 => char::from_u32(self.0.gen_range(('α' as u32)..('ω' as u32 + 1))), + // // three-byte characters + 33..=45 => ['✋', '✅', '❌', '❎', '⭐'].choose(&mut self.0).copied(), + // // four-byte characters + 46..=58 => ['🍐', '🏀', '🍗', '🎉'].choose(&mut self.0).copied(), + // ascii letters + _ => Some(self.0.gen_range(b'a'..b'z' + 1).into()), + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/vim/Cargo.toml b/crates/vim/Cargo.toml index 5bc32cd5bd..bf094c30fa 100644 --- a/crates/vim/Cargo.toml +++ b/crates/vim/Cargo.toml @@ -14,6 +14,7 @@ command_palette = { path = "../command_palette" } editor = { path = "../editor" } gpui = { path = "../gpui" } language = { path = "../language" } +rope = { path = "../rope" } search = { path = "../search" } serde = { version = "1.0", features = ["derive", "rc"] } settings = { path = "../settings" } diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index db5583b4ce..a06bcc54da 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -13,7 +13,8 @@ use change::init as change_init; use collections::HashSet; use editor::{Autoscroll, Bias, ClipboardSelection, DisplayPoint}; use gpui::{actions, MutableAppContext, ViewContext}; -use language::{AutoindentMode, Point, SelectionGoal}; +use language::{AutoindentMode, SelectionGoal}; +use rope::point::Point; use workspace::Workspace; use self::{change::change_over, delete::delete_over, yank::yank_over}; diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index 2fd43b7bcb..c0cb2e9edd 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -21,6 +21,7 @@ client = { path = "../client" } collections = { path = "../collections" } context_menu = { path = "../context_menu" } drag_and_drop = { path = "../drag_and_drop" } +fs = { path = "../fs" } gpui = { path = "../gpui" } language = { path = "../language" } menu = { path = "../menu" } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 705823003f..e11cd80f09 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -32,7 +32,8 @@ use log::{error, warn}; pub use pane::*; pub use pane_group::*; use postage::prelude::Stream; -use project::{fs, Fs, Project, ProjectEntryId, ProjectPath, ProjectStore, Worktree, WorktreeId}; +use fs::{self, Fs}; +use project::{Project, ProjectEntryId, ProjectPath, ProjectStore, Worktree, WorktreeId}; use searchable::SearchableItemHandle; use serde::Deserialize; use settings::{Autosave, DockAnchor, Settings}; diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index d0b41b08f1..6c92dd9bef 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -32,6 +32,7 @@ diagnostics = { path = "../diagnostics" } editor = { path = "../editor" } file_finder = { path = "../file_finder" } search = { path = "../search" } +fs = { path = "../fs" } fsevent = { path = "../fsevent" } fuzzy = { path = "../fuzzy" } go_to_line = { path = "../go_to_line" } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index f48f8b723e..6c93ff5ba6 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -14,7 +14,6 @@ use client::{ http::{self, HttpClient}, UserStore, ZED_SECRET_CLIENT_TOKEN, }; -use fs::OpenOptions; use futures::{ channel::{mpsc, oneshot}, FutureExt, SinkExt, StreamExt, @@ -28,16 +27,16 @@ use project::{Fs, ProjectStore}; use serde_json::json; use settings::{self, KeymapFileContent, Settings, SettingsFileContent, WorkingDirectory}; use smol::process::Command; -use std::{env, ffi::OsStr, fs, panic, path::PathBuf, sync::Arc, thread, time::Duration}; +use std::fs::OpenOptions; +use std::{env, ffi::OsStr, panic, path::PathBuf, sync::Arc, thread, time::Duration}; use terminal::terminal_container_view::{get_working_directory, TerminalContainer}; +use fs::RealFs; use theme::ThemeRegistry; use util::{ResultExt, TryFutureExt}; use workspace::{self, AppState, ItemHandle, NewFile, OpenPaths, Workspace}; use zed::{ - self, build_window_options, - fs::RealFs, - initialize_workspace, languages, menus, + self, build_window_options, initialize_workspace, languages, menus, settings_file::{watch_keymap_file, watch_settings_file, WatchedJsonFile}, }; @@ -200,23 +199,23 @@ fn main() { } fn init_paths() { - fs::create_dir_all(&*zed::paths::CONFIG_DIR).expect("could not create config path"); - fs::create_dir_all(&*zed::paths::LANGUAGES_DIR).expect("could not create languages path"); - fs::create_dir_all(&*zed::paths::DB_DIR).expect("could not create database path"); - fs::create_dir_all(&*zed::paths::LOGS_DIR).expect("could not create logs path"); + std::fs::create_dir_all(&*zed::paths::CONFIG_DIR).expect("could not create config path"); + std::fs::create_dir_all(&*zed::paths::LANGUAGES_DIR).expect("could not create languages path"); + std::fs::create_dir_all(&*zed::paths::DB_DIR).expect("could not create database path"); + std::fs::create_dir_all(&*zed::paths::LOGS_DIR).expect("could not create logs path"); // Copy setting files from legacy locations. TODO: remove this after a few releases. thread::spawn(|| { - if fs::metadata(&*zed::paths::legacy::SETTINGS).is_ok() - && fs::metadata(&*zed::paths::SETTINGS).is_err() + if std::fs::metadata(&*zed::paths::legacy::SETTINGS).is_ok() + && std::fs::metadata(&*zed::paths::SETTINGS).is_err() { - fs::copy(&*zed::paths::legacy::SETTINGS, &*zed::paths::SETTINGS).log_err(); + std::fs::copy(&*zed::paths::legacy::SETTINGS, &*zed::paths::SETTINGS).log_err(); } - if fs::metadata(&*zed::paths::legacy::KEYMAP).is_ok() - && fs::metadata(&*zed::paths::KEYMAP).is_err() + if std::fs::metadata(&*zed::paths::legacy::KEYMAP).is_ok() + && std::fs::metadata(&*zed::paths::KEYMAP).is_err() { - fs::copy(&*zed::paths::legacy::KEYMAP, &*zed::paths::KEYMAP).log_err(); + std::fs::copy(&*zed::paths::legacy::KEYMAP, &*zed::paths::KEYMAP).log_err(); } }); } @@ -231,9 +230,10 @@ fn init_logger() { const KIB: u64 = 1024; const MIB: u64 = 1024 * KIB; const MAX_LOG_BYTES: u64 = MIB; - if fs::metadata(&*zed::paths::LOG).map_or(false, |metadata| metadata.len() > MAX_LOG_BYTES) + if std::fs::metadata(&*zed::paths::LOG) + .map_or(false, |metadata| metadata.len() > MAX_LOG_BYTES) { - let _ = fs::rename(&*zed::paths::LOG, &*zed::paths::OLD_LOG); + let _ = std::fs::rename(&*zed::paths::LOG, &*zed::paths::OLD_LOG); } let log_file = OpenOptions::new() @@ -289,7 +289,7 @@ fn init_panic_hook(app_version: String, http: Arc, background: A .body(body.into())?; let response = http.send(request).await.context("error sending panic")?; if response.status().is_success() { - fs::remove_file(child_path) + std::fs::remove_file(child_path) .context("error removing panic after sending it successfully") .log_err(); } else { @@ -338,7 +338,7 @@ fn init_panic_hook(app_version: String, http: Arc, background: A }; let panic_filename = chrono::Utc::now().format("%Y_%m_%d %H_%M_%S").to_string(); - fs::write( + std::fs::write( zed::paths::LOGS_DIR.join(format!("zed-{}-{}.panic", app_version, panic_filename)), &message, ) @@ -395,7 +395,7 @@ fn stdout_is_a_pty() -> bool { fn collect_path_args() -> Vec { env::args() .skip(1) - .filter_map(|arg| match fs::canonicalize(arg) { + .filter_map(|arg| match std::fs::canonicalize(arg) { Ok(path) => Some(path), Err(error) => { log::error!("error parsing path argument: {}", error); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 6210242c51..a2857f01dc 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1,8 +1,8 @@ mod feedback; pub mod languages; pub mod menus; -pub mod settings_file; pub mod paths; +pub mod settings_file; #[cfg(any(test, feature = "test-support"))] pub mod test; @@ -14,6 +14,7 @@ use collab_ui::CollabTitlebarItem; use collections::VecDeque; pub use editor; use editor::{Editor, MultiBuffer}; + use gpui::{ actions, geometry::vector::vec2f, @@ -23,7 +24,7 @@ use gpui::{ }; use language::Rope; pub use lsp; -pub use project::{self, fs}; +pub use project; use project_panel::ProjectPanel; use search::{BufferSearchBar, ProjectSearchBar}; use serde::Deserialize; From bc2a6e429ca3444130210765fa3a234af1ba52d5 Mon Sep 17 00:00:00 2001 From: Joseph T Lyons Date: Tue, 11 Oct 2022 18:31:17 -0400 Subject: [PATCH 262/314] Use tag_name for Discord release message --- .github/workflows/release_actions.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release_actions.yml b/.github/workflows/release_actions.yml index 8d2788e6f0..9a3b2376df 100644 --- a/.github/workflows/release_actions.yml +++ b/.github/workflows/release_actions.yml @@ -11,7 +11,7 @@ jobs: with: webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }} content: | - 📣 Zed ${{ github.event.release.name }} was just released! + 📣 Zed ${{ github.event.release.tag_name }} was just released! Restart your Zed or head to https://zed.dev/releases to grab it. From 5487f99ac78bf4bd8ac2dc73d19db2e18757faa8 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 11 Oct 2022 16:03:38 -0700 Subject: [PATCH 263/314] Moved settings_file.rs into settings crate. Should be ready to start now :D --- Cargo.lock | 3 +++ crates/language/src/buffer.rs | 4 +++- crates/settings/Cargo.toml | 5 ++++- crates/settings/src/settings.rs | 1 + crates/{zed => settings}/src/settings_file.rs | 12 +++++++----- crates/zed/src/main.rs | 6 ++---- crates/zed/src/zed.rs | 1 - 7 files changed, 20 insertions(+), 12 deletions(-) rename crates/{zed => settings}/src/settings_file.rs (96%) diff --git a/Cargo.lock b/Cargo.lock index ebf2d4376c..fca454f1f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5060,8 +5060,11 @@ dependencies = [ "anyhow", "assets", "collections", + "fs", + "futures", "gpui", "json_comments", + "postage", "schemars", "serde", "serde_json", diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 56aad18ead..a9af41bb23 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -40,7 +40,9 @@ use sum_tree::TreeMap; use text::operation_queue::OperationQueue; pub use text::{Buffer as TextBuffer, BufferSnapshot as TextBufferSnapshot, Operation as _, *}; use theme::SyntaxTheme; -use util::{RandomCharIter, TryFutureExt as _}; +#[cfg(any(test, feature = "test-support"))] +use util::RandomCharIter; +use util::TryFutureExt as _; #[cfg(any(test, feature = "test-support"))] pub use {tree_sitter_rust, tree_sitter_typescript}; diff --git a/crates/settings/Cargo.toml b/crates/settings/Cargo.toml index e8fb58cd61..64c906a833 100644 --- a/crates/settings/Cargo.toml +++ b/crates/settings/Cargo.toml @@ -14,10 +14,13 @@ test-support = [] assets = { path = "../assets" } collections = { path = "../collections" } gpui = { path = "../gpui" } +fs = { path = "../fs" } +anyhow = "1.0.38" +futures = "0.3" theme = { path = "../theme" } util = { path = "../util" } -anyhow = "1.0.38" json_comments = "0.2" +postage = { version = "0.4.1", features = ["futures-traits"] } schemars = "0.8" serde = { workspace = true } serde_json = { workspace = true } diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 921b36051f..883d7694c7 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -1,4 +1,5 @@ mod keymap_file; +pub mod settings_file; use anyhow::Result; use gpui::{ diff --git a/crates/zed/src/settings_file.rs b/crates/settings/src/settings_file.rs similarity index 96% rename from crates/zed/src/settings_file.rs rename to crates/settings/src/settings_file.rs index 25bd065b47..f12f191041 100644 --- a/crates/zed/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -1,14 +1,16 @@ +use fs::Fs; use futures::StreamExt; use gpui::{executor, MutableAppContext}; use postage::sink::Sink as _; use postage::{prelude::Stream, watch}; -use project::Fs; use serde::Deserialize; -use settings::{parse_json_with_comments, KeymapFileContent, Settings, SettingsFileContent}; + use std::{path::Path, sync::Arc, time::Duration}; use theme::ThemeRegistry; use util::ResultExt; +use crate::{parse_json_with_comments, KeymapFileContent, Settings, SettingsFileContent}; + #[derive(Clone)] pub struct WatchedJsonFile(pub watch::Receiver); @@ -77,7 +79,7 @@ pub fn watch_settings_file( pub fn keymap_updated(content: KeymapFileContent, cx: &mut MutableAppContext) { cx.clear_bindings(); - settings::KeymapFileContent::load_defaults(cx); + KeymapFileContent::load_defaults(cx); content.add_to_cx(cx).log_err(); } @@ -105,8 +107,8 @@ pub fn watch_keymap_file(mut file: WatchedJsonFile, cx: &mut #[cfg(test)] mod tests { use super::*; - use project::FakeFs; - use settings::{EditorSettings, SoftWrap}; + use crate::{EditorSettings, SoftWrap}; + use fs::FakeFs; #[gpui::test] async fn test_watch_settings_files(cx: &mut gpui::TestAppContext) { diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 6c93ff5ba6..7dc30d34f5 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -32,13 +32,11 @@ use std::{env, ffi::OsStr, panic, path::PathBuf, sync::Arc, thread, time::Durati use terminal::terminal_container_view::{get_working_directory, TerminalContainer}; use fs::RealFs; +use settings::settings_file::{watch_keymap_file, watch_settings_file, WatchedJsonFile}; use theme::ThemeRegistry; use util::{ResultExt, TryFutureExt}; use workspace::{self, AppState, ItemHandle, NewFile, OpenPaths, Workspace}; -use zed::{ - self, build_window_options, initialize_workspace, languages, menus, - settings_file::{watch_keymap_file, watch_settings_file, WatchedJsonFile}, -}; +use zed::{self, build_window_options, initialize_workspace, languages, menus}; fn main() { let http = http::client(); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index a2857f01dc..9012bc89e2 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -2,7 +2,6 @@ mod feedback; pub mod languages; pub mod menus; pub mod paths; -pub mod settings_file; #[cfg(any(test, feature = "test-support"))] pub mod test; From d1f1eb9a2900b290a13df0deaac40b8a76e4a224 Mon Sep 17 00:00:00 2001 From: K Simmons Date: Tue, 11 Oct 2022 16:26:28 -0700 Subject: [PATCH 264/314] Add count argument to motion functions and add ability to jump to a given line --- Cargo.lock | 1284 ++++++++++++----------------- crates/vim/src/motion.rs | 171 ++-- crates/vim/src/normal.rs | 55 +- crates/vim/src/normal/change.rs | 2 +- crates/vim/src/object.rs | 6 +- crates/vim/src/visual.rs | 8 +- crates/vim/test_data/test_gg.json | 2 +- 7 files changed, 665 insertions(+), 863 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa9ad80001..0448538c35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.1.0" dependencies = [ "auto_update", "editor", - "futures 0.3.24", + "futures", "gpui", "language", "project", @@ -52,9 +52,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.19" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" dependencies = [ "memchr", ] @@ -113,15 +113,6 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec8ad6edb4840b78c5c3d88de606b22252d552b55f3a4699fbb10fc070ec3049" -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "ansi_term" version = "0.12.1" @@ -133,9 +124,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.65" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" +checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" [[package]] name = "arrayref" @@ -157,9 +148,9 @@ checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] name = "ascii" -version = "1.1.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" +checksum = "bbf56136a5198c7b01a49e3afcbef6cf84597273d298f54432926024107b0109" [[package]] name = "assets" @@ -183,33 +174,20 @@ dependencies = [ [[package]] name = "async-channel" -version = "1.7.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" +checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" dependencies = [ "concurrent-queue", "event-listener", "futures-core", ] -[[package]] -name = "async-compat" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b48b4ff0c2026db683dea961cd8ea874737f56cffca86fa84415eaddc51c00d" -dependencies = [ - "futures-core", - "futures-io", - "once_cell", - "pin-project-lite 0.2.9", - "tokio", -] - [[package]] name = "async-compression" -version = "0.3.15" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" +checksum = "345fd392ab01f746c717b1357165b76f0b67a60192007b234058c9045fdcf695" dependencies = [ "flate2", "futures-core", @@ -234,23 +212,21 @@ dependencies = [ [[package]] name = "async-fs" -version = "1.6.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" +checksum = "8b3ca4f8ff117c37c278a2f7415ce9be55560b846b5bc4412aaa5d29c1c3dae2" dependencies = [ "async-lock", - "autocfg 1.1.0", "blocking", "futures-lite", ] [[package]] name = "async-io" -version = "1.9.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e21f3a490c72b3b0cf44962180e60045de2925d8dff97918f7ee43c8f637c7" +checksum = "e5e18f61464ae81cde0a23e713ae8fd299580c54d697a35820cfd0625b8b0e07" dependencies = [ - "autocfg 1.1.0", "concurrent-queue", "futures-lite", "libc", @@ -275,12 +251,11 @@ dependencies = [ [[package]] name = "async-net" -version = "1.7.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4051e67316bc7eff608fe723df5d32ed639946adcd69e07df41fd42a7b411f1f" +checksum = "5373304df79b9b4395068fb080369ec7178608827306ce4d081cba51cac551df" dependencies = [ "async-io", - "autocfg 1.1.0", "blocking", "futures-lite", ] @@ -290,18 +265,17 @@ name = "async-pipe" version = "0.1.3" source = "git+https://github.com/zed-industries/async-pipe-rs?rev=82d00a04211cf4e1236029aa03e6b6ce2a74c553#82d00a04211cf4e1236029aa03e6b6ce2a74c553" dependencies = [ - "futures 0.3.24", + "futures", "log", ] [[package]] name = "async-process" -version = "1.5.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02111fd8655a613c25069ea89fc8d9bb89331fa77486eb3bc059ee757cfa481c" +checksum = "cf2c06e30a24e8c78a3987d07f0930edf76ef35e027e7bdb063fccafdad1f60c" dependencies = [ "async-io", - "autocfg 1.1.0", "blocking", "cfg-if 1.0.0", "event-listener", @@ -364,9 +338,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.57" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" +checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" dependencies = [ "proc-macro2", "quote", @@ -461,15 +435,15 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.5.16" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e3356844c4d6a6d6467b8da2cffb4a2820be256f50a3a386c9d152bab31043" +checksum = "c2cc6e8e8c993cb61a005fab8c1e5093a29199b7253b05a6883999312935c1ff" dependencies = [ "async-trait", "axum-core", "base64", "bitflags", - "bytes 1.2.1", + "bytes", "futures-util", "headers", "http", @@ -496,28 +470,26 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.2.8" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f0c0a60006f2a293d82d571f635042a72edf927539b7685bd62d361963839b" +checksum = "cf4d047478b986f14a13edad31a009e2e05cb241f9805d0d75e4cba4e129ad4d" dependencies = [ "async-trait", - "bytes 1.2.1", + "bytes", "futures-util", "http", "http-body", "mime", - "tower-layer", - "tower-service", ] [[package]] name = "axum-extra" -version = "0.3.7" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69034b3b0fd97923eee2ce8a47540edb21e07f48f87f67d44bb4271cec622bdb" +checksum = "277c75e6c814b061ae4947d02335d9659db9771b9950cca670002ae986372f44" dependencies = [ "axum", - "bytes 1.2.1", + "bytes", "futures-util", "http", "mime", @@ -533,16 +505,16 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.66" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" dependencies = [ "addr2line", "cc", "cfg-if 1.0.0", "libc", - "miniz_oxide 0.5.4", - "object 0.29.0", + "miniz_oxide 0.5.3", + "object", "rustc-demangle", ] @@ -554,9 +526,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "base64ct" -version = "1.5.2" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2b2456fd614d856680dcd9fcc660a51a820fa09daef2e49772b56a193c8474" +checksum = "3bdca834647821e0b13d9539a8634eb62d3501b6b6c2cec1722786ee6671b851" [[package]] name = "bincode" @@ -613,9 +585,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" dependencies = [ "generic-array", ] @@ -673,15 +645,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.11.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" [[package]] name = "bytemuck" -version = "1.12.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da" +checksum = "c53dfa917ec274df8ed3c572698f381a24eef2efba9492d797301b72b6db408a" [[package]] name = "byteorder" @@ -689,16 +661,6 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" -[[package]] -name = "bytes" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" -dependencies = [ - "byteorder", - "iovec", -] - [[package]] name = "bytes" version = "1.2.1" @@ -722,6 +684,20 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" +[[package]] +name = "call" +version = "0.1.0" +dependencies = [ + "anyhow", + "client", + "collections", + "futures", + "gpui", + "postage", + "project", + "util", +] + [[package]] name = "cap-fs-ext" version = "0.24.4" @@ -796,12 +772,12 @@ dependencies = [ "bindgen", "block", "byteorder", - "bytes 1.2.1", + "bytes", "cocoa", "core-foundation", "core-graphics", "foreign-types", - "futures 0.3.24", + "futures", "gpui", "hmac 0.12.1", "jwt", @@ -812,7 +788,7 @@ dependencies = [ "parking_lot 0.11.2", "postage", "serde", - "sha2 0.10.6", + "sha2 0.10.2", "simplelog", ] @@ -863,23 +839,21 @@ dependencies = [ "postage", "settings", "theme", - "time 0.3.15", + "time 0.3.11", "util", "workspace", ] [[package]] name = "chrono" -version = "0.4.22" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" dependencies = [ - "iana-time-zone", - "js-sys", + "libc", "num-integer", "num-traits", "time 0.1.44", - "wasm-bindgen", "winapi 0.3.9", ] @@ -900,9 +874,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.4.0" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" +checksum = "5a050e2153c5be08febd6734e29298e844fdb0fa21aeddd63b4eb7baa106c69b" dependencies = [ "glob", "libc", @@ -926,9 +900,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.22" +version = "3.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" +checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83" dependencies = [ "atty", "bitflags", @@ -938,14 +912,14 @@ dependencies = [ "once_cell", "strsim 0.10.0", "termcolor", - "textwrap 0.15.1", + "textwrap 0.15.0", ] [[package]] name = "clap_derive" -version = "3.2.18" +version = "3.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902" dependencies = [ "heck 0.4.0", "proc-macro-error", @@ -968,7 +942,7 @@ name = "cli" version = "0.1.0" dependencies = [ "anyhow", - "clap 3.2.22", + "clap 3.2.8", "core-foundation", "core-services", "dirs 3.0.2", @@ -986,7 +960,7 @@ dependencies = [ "async-tungstenite", "collections", "db", - "futures 0.3.24", + "futures", "gpui", "image", "isahc", @@ -1001,11 +975,11 @@ dependencies = [ "sum_tree", "tempfile", "thiserror", - "time 0.3.15", + "time 0.3.11", "tiny_http", "url", "util", - "uuid 1.2.1", + "uuid 1.1.2", ] [[package]] @@ -1053,16 +1027,6 @@ dependencies = [ "objc", ] -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - [[package]] name = "collab" version = "0.1.0" @@ -1073,14 +1037,15 @@ dependencies = [ "axum", "axum-extra", "base64", - "clap 3.2.22", + "call", + "clap 3.2.8", "client", "collections", "ctor", "editor", "env_logger", "envy", - "futures 0.3.24", + "futures", "git", "gpui", "hyper", @@ -1103,7 +1068,7 @@ dependencies = [ "sha-1 0.9.8", "sqlx", "theme", - "time 0.3.15", + "time 0.3.11", "tokio", "tokio-tungstenite", "toml", @@ -1117,6 +1082,31 @@ dependencies = [ "workspace", ] +[[package]] +name = "collab_ui" +version = "0.1.0" +dependencies = [ + "anyhow", + "call", + "client", + "clock", + "collections", + "editor", + "futures", + "fuzzy", + "gpui", + "log", + "menu", + "picker", + "postage", + "project", + "serde", + "settings", + "theme", + "util", + "workspace", +] + [[package]] name = "collections" version = "0.1.0" @@ -1151,61 +1141,13 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "1.2.4" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" +checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" dependencies = [ "cache-padded", ] -[[package]] -name = "contacts_panel" -version = "0.1.0" -dependencies = [ - "anyhow", - "client", - "collections", - "editor", - "futures 0.3.24", - "fuzzy", - "gpui", - "language", - "log", - "menu", - "picker", - "postage", - "project", - "serde", - "settings", - "theme", - "util", - "workspace", -] - -[[package]] -name = "contacts_status_item" -version = "0.1.0" -dependencies = [ - "anyhow", - "client", - "collections", - "editor", - "futures 0.3.24", - "fuzzy", - "gpui", - "language", - "log", - "menu", - "picker", - "postage", - "project", - "serde", - "settings", - "theme", - "util", - "workspace", -] - [[package]] name = "context_menu" version = "0.1.0" @@ -1286,27 +1228,27 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" dependencies = [ "libc", ] [[package]] name = "cranelift-bforest" -version = "0.85.3" +version = "0.85.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "749d0d6022c9038dccf480bdde2a38d435937335bf2bb0f14e815d94517cdce8" +checksum = "7901fbba05decc537080b07cb3f1cadf53be7b7602ca8255786288a8692ae29a" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.85.3" +version = "0.85.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94370cc7b37bf652ccd8bb8f09bd900997f7ccf97520edfc75554bb5c4abbea" +checksum = "37ba1b45d243a4a28e12d26cd5f2507da74e77c45927d40de8b6ffbf088b46b5" dependencies = [ "cranelift-bforest", "cranelift-codegen-meta", @@ -1322,33 +1264,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.85.3" +version = "0.85.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a3cea8fdab90e44018c5b9a1dfd460d8ee265ac354337150222a354628bdb6" +checksum = "54cc30032171bf230ce22b99c07c3a1de1221cb5375bd6dbe6dbe77d0eed743c" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.85.3" +version = "0.85.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac72f76f2698598951ab26d8c96eaa854810e693e7dd52523958b5909fde6b2" +checksum = "a23f2672426d2bb4c9c3ef53e023076cfc4d8922f0eeaebaf372c92fae8b5c69" [[package]] name = "cranelift-entity" -version = "0.85.3" +version = "0.85.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09eaeacfcd2356fe0e66b295e8f9d59fdd1ac3ace53ba50de14d628ec902f72d" +checksum = "886c59a5e0de1f06dbb7da80db149c75de10d5e2caca07cdd9fef8a5918a6336" dependencies = [ "serde", ] [[package]] name = "cranelift-frontend" -version = "0.85.3" +version = "0.85.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dba69c9980d5ffd62c18a2bde927855fcd7c8dc92f29feaf8636052662cbd99c" +checksum = "ace74eeca11c439a9d4ed1a5cb9df31a54cd0f7fbddf82c8ce4ea8e9ad2a8fe0" dependencies = [ "cranelift-codegen", "log", @@ -1358,15 +1300,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.85.3" +version = "0.85.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2920dc1e05cac40304456ed3301fde2c09bd6a9b0210bcfa2f101398d628d5b" +checksum = "db1ae52a5cc2cad0d86fdd3dcb16b7217d2f1e65ab4f5814aa4f014ad335fa43" [[package]] name = "cranelift-native" -version = "0.85.3" +version = "0.85.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04dfa45f9b2a6f587c564d6b63388e00cd6589d2df6ea2758cf79e1a13285e6" +checksum = "dadcfb7852900780d37102bce5698bcd401736403f07b52e714ff7a180e0e22f" dependencies = [ "cranelift-codegen", "libc", @@ -1375,9 +1317,9 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.85.3" +version = "0.85.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31a46513ae6f26f3f267d8d75b5373d555fbbd1e68681f348d99df43f747ec54" +checksum = "c84e3410960389110b88f97776f39f6d2c8becdaa4cd59e390e6b76d9d0e7190" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1425,46 +1367,47 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.6" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.12", + "crossbeam-utils 0.8.10", ] [[package]] name = "crossbeam-deque" -version = "0.8.2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" dependencies = [ "cfg-if 1.0.0", "crossbeam-epoch", - "crossbeam-utils 0.8.12", + "crossbeam-utils 0.8.10", ] [[package]] name = "crossbeam-epoch" -version = "0.9.11" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" +checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d" dependencies = [ "autocfg 1.1.0", "cfg-if 1.0.0", - "crossbeam-utils 0.8.12", + "crossbeam-utils 0.8.10", "memoffset", + "once_cell", "scopeguard", ] [[package]] name = "crossbeam-queue" -version = "0.3.6" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd42583b04998a5363558e5f9291ee5a5ff6b49944332103f251e7479a82aa7" +checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.12", + "crossbeam-utils 0.8.10", ] [[package]] @@ -1480,18 +1423,19 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.12" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" +checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83" dependencies = [ "cfg-if 1.0.0", + "once_cell", ] [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "5999502d32b9c48d492abe66392408144895020ec4709e549e840799f3bb74c0" dependencies = [ "generic-array", "typenum", @@ -1509,9 +1453,9 @@ dependencies = [ [[package]] name = "ctor" -version = "0.1.23" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" +checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c" dependencies = [ "quote", "syn", @@ -1519,9 +1463,9 @@ dependencies = [ [[package]] name = "curl" -version = "0.4.44" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22" +checksum = "37d855aeef205b43f65a5001e0997d81f8efca7badad4fad7d897aa7f0d0651f" dependencies = [ "curl-sys", "libc", @@ -1534,9 +1478,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.56+curl-7.83.1" +version = "0.4.55+curl-7.83.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093e169dd4de29e468fa649fbae11cdcd5551c81fe5bf1b0677adad7ef3d26f" +checksum = "23734ec77368ec583c2e61dd3f0b0e5c98b93abe6d2a004ca06b91dd7e3e2762" dependencies = [ "cc", "libc", @@ -1548,50 +1492,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "cxx" -version = "1.0.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f39818dcfc97d45b03953c1292efc4e80954e1583c4aa770bac1383e2310a4" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e580d70777c116df50c390d1211993f62d40302881e54d4b79727acb83d0199" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56a46460b88d1cec95112c8c363f0e2c39afdb237f60583b0b36343bf627ea9c" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747b608fecf06b0d72d440f27acc99288207324b793be2c17991839f3d4995ea" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "data-url" version = "0.1.1" @@ -1626,13 +1526,13 @@ dependencies = [ [[package]] name = "dhat" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0684eaa19a59be283a6f99369917b679bd4d1d06604b2eb2e2f87b4bbd67668d" +checksum = "47003dc9f6368a88e85956c3b2573a7e6872746a3e5d762a8885da3a136a0381" dependencies = [ "backtrace", "lazy_static", - "parking_lot 0.12.1", + "parking_lot 0.11.2", "rustc-hash", "serde", "serde_json", @@ -1671,11 +1571,11 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.5" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" dependencies = [ - "block-buffer 0.10.3", + "block-buffer 0.10.2", "crypto-common", "subtle", ] @@ -1741,13 +1641,10 @@ dependencies = [ ] [[package]] -name = "dotenvy" -version = "0.15.5" +name = "dotenv" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9155c8f4dc55c7470ae9da3f63c6785245093b3f6aeb0f5bf2e968efbba314" -dependencies = [ - "dirs 4.0.0", -] +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" [[package]] name = "drag_and_drop" @@ -1771,9 +1668,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.9" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2" +checksum = "140206b78fb2bc3edbcfc9b5ccbd0b30699cfe8d348b8b31b330e47df5291a5a" [[package]] name = "easy-parallel" @@ -1792,7 +1689,7 @@ dependencies = [ "context_menu", "ctor", "env_logger", - "futures 0.3.24", + "futures", "fuzzy", "git", "gpui", @@ -1827,9 +1724,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" [[package]] name = "encoding_rs" @@ -1842,9 +1739,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.9.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" dependencies = [ "atty", "humantime", @@ -1864,9 +1761,9 @@ dependencies = [ [[package]] name = "erased-serde" -version = "0.3.23" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54558e0ba96fbe24280072642eceb9d7d442e32c7ec0ea9e7ecd7b4ea2cf4e11" +checksum = "81d013529d5574a60caeda29e179e695125448e5de52e3874f7b4c1d7360e18e" dependencies = [ "serde", ] @@ -1913,9 +1810,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "2.5.3" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" [[package]] name = "expat-sys" @@ -1935,9 +1832,9 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fastrand" -version = "1.8.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" dependencies = [ "instant", ] @@ -1985,7 +1882,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" dependencies = [ "crc32fast", - "miniz_oxide 0.5.4", + "miniz_oxide 0.5.3", ] [[package]] @@ -2058,10 +1955,11 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" dependencies = [ + "matches", "percent-encoding", ] @@ -2140,15 +2038,9 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" -version = "0.1.31" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" - -[[package]] -name = "futures" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" +checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" dependencies = [ "futures-channel", "futures-core", @@ -2161,9 +2053,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.24" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" dependencies = [ "futures-core", "futures-sink", @@ -2171,15 +2063,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.24" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" [[package]] name = "futures-executor" -version = "0.3.24" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" +checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" dependencies = [ "futures-core", "futures-task", @@ -2199,9 +2091,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.24" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" +checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" [[package]] name = "futures-lite" @@ -2220,9 +2112,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.24" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" +checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" dependencies = [ "proc-macro2", "quote", @@ -2231,23 +2123,22 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.24" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" [[package]] name = "futures-task" -version = "0.3.24" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" [[package]] name = "futures-util" -version = "0.3.24" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" dependencies = [ - "futures 0.1.31", "futures-channel", "futures-core", "futures-io", @@ -2258,7 +2149,6 @@ dependencies = [ "pin-project-lite 0.2.9", "pin-utils", "slab", - "tokio-io", ] [[package]] @@ -2280,9 +2170,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" dependencies = [ "typenum", "version_check", @@ -2322,9 +2212,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.26.2" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" dependencies = [ "fallible-iterator", "indexmap", @@ -2339,7 +2229,7 @@ dependencies = [ "async-trait", "clock", "collections", - "futures 0.3.24", + "futures", "git2", "lazy_static", "log", @@ -2417,10 +2307,9 @@ dependencies = [ "etagere", "font-kit", "foreign-types", - "futures 0.3.24", + "futures", "gpui_macros", "image", - "itertools", "lazy_static", "log", "media", @@ -2443,7 +2332,7 @@ dependencies = [ "smallvec", "smol", "sum_tree", - "time 0.3.15", + "time 0.3.11", "tiny-skia", "tree-sitter", "usvg", @@ -2462,11 +2351,11 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.14" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" dependencies = [ - "bytes 1.2.1", + "bytes", "fnv", "futures-core", "futures-sink", @@ -2475,7 +2364,7 @@ dependencies = [ "indexmap", "slab", "tokio", - "tokio-util 0.7.4", + "tokio-util 0.7.3", "tracing", ] @@ -2490,36 +2379,36 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.3" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" dependencies = [ "ahash", ] [[package]] name = "hashlink" -version = "0.8.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa" +checksum = "d452c155cb93fecdfb02a73dd57b5d8e442c2063bd7aac72f1bc5e4263a43086" dependencies = [ - "hashbrown 0.12.3", + "hashbrown 0.12.1", ] [[package]] name = "headers" -version = "0.3.8" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" +checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d" dependencies = [ "base64", "bitflags", - "bytes 1.2.1", + "bytes", "headers-core", "http", "httpdate", "mime", - "sha1", + "sha-1 0.10.0", ] [[package]] @@ -2560,9 +2449,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.6" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +checksum = "d37fb7dc756218a0559bfc21e4381f03cbb696cdaf959e7e95e927496f0564cd" dependencies = [ "libc", ] @@ -2598,7 +2487,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.5", + "digest 0.10.3", ] [[package]] @@ -2607,7 +2496,7 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ - "bytes 1.2.1", + "bytes", "fnv", "itoa", ] @@ -2618,7 +2507,7 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ - "bytes 1.2.1", + "bytes", "http", "pin-project-lite 0.2.9", ] @@ -2631,9 +2520,9 @@ checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" [[package]] name = "httparse" -version = "1.8.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" [[package]] name = "httpdate" @@ -2649,11 +2538,11 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.20" +version = "0.14.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f" dependencies = [ - "bytes 1.2.1", + "bytes", "futures-channel", "futures-core", "futures-util", @@ -2689,43 +2578,20 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ - "bytes 1.2.1", + "bytes", "hyper", "native-tls", "tokio", "tokio-native-tls", ] -[[package]] -name = "iana-time-zone" -version = "0.1.51" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5a6ef98976b22b3b7f2f3a806f858cb862044cfa66805aa3ad84cb3d3b785ed" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "winapi 0.3.9", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fde6edd6cef363e9359ed3c98ba64590ba9eecba2293eb5a723ab32aee8926aa" -dependencies = [ - "cxx", - "cxx-build", -] - [[package]] name = "idna" -version = "0.3.0" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" dependencies = [ + "matches", "unicode-bidi", "unicode-normalization", ] @@ -2736,7 +2602,7 @@ version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" dependencies = [ - "crossbeam-utils 0.8.12", + "crossbeam-utils 0.8.10", "globset", "lazy_static", "log", @@ -2774,15 +2640,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg 1.1.0", - "hashbrown 0.12.3", + "hashbrown 0.12.1", "serde", ] [[package]] name = "indoc" -version = "1.0.7" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3" +checksum = "05a0bd019339e5d968b37855180087b7b9d512c5046fbd244cf8c95687927d6e" [[package]] name = "instant" @@ -2853,7 +2719,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c89a757e762896bdbdfadf2860d0f8b0cea5e363d8cf3e7bdfeb63d1d976352" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi 0.2.3", "io-lifetimes", "rustix", "winapi 0.3.9", @@ -2867,7 +2733,7 @@ checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9" dependencies = [ "async-channel", "castaway", - "crossbeam-utils 0.8.12", + "crossbeam-utils 0.8.10", "curl", "curl-sys", "encoding_rs", @@ -2888,18 +2754,18 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.5" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.4" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" [[package]] name = "ittapi-rs" @@ -2912,9 +2778,9 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.25" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" dependencies = [ "libc", ] @@ -2943,9 +2809,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" dependencies = [ "wasm-bindgen", ] @@ -2964,11 +2830,11 @@ checksum = "6204285f77fe7d9784db3fdc449ecce1a0114927a51d5a41c4c7a292011c015f" dependencies = [ "base64", "crypto-common", - "digest 0.10.5", + "digest 0.10.3", "hmac 0.12.1", "serde", "serde_json", - "sha2 0.10.6", + "sha2 0.10.2", ] [[package]] @@ -3002,7 +2868,7 @@ dependencies = [ "collections", "ctor", "env_logger", - "futures 0.3.24", + "futures", "fuzzy", "git", "gpui", @@ -3057,9 +2923,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.134" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "libgit2-sys" @@ -3085,9 +2951,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.5" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "292a948cd991e376cf75541fe5b97a1081d713c618b4f1b9500f8844e49eb565" +checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db" [[package]] name = "libnghttp2-sys" @@ -3134,15 +3000,6 @@ dependencies = [ "safemem", ] -[[package]] -name = "link-cplusplus" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" -dependencies = [ - "cc", -] - [[package]] name = "linked-hash-map" version = "0.5.6" @@ -3172,7 +3029,7 @@ dependencies = [ "anyhow", "core-foundation", "core-graphics", - "futures 0.3.24", + "futures", "media", "parking_lot 0.11.2", "serde", @@ -3181,9 +3038,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" dependencies = [ "autocfg 1.1.0", "scopeguard", @@ -3209,7 +3066,7 @@ dependencies = [ "collections", "ctor", "env_logger", - "futures 0.3.24", + "futures", "gpui", "log", "lsp-types", @@ -3288,11 +3145,11 @@ checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "md-5" -version = "0.10.5" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" dependencies = [ - "digest 0.10.5", + "digest 0.10.3", ] [[package]] @@ -3302,7 +3159,7 @@ dependencies = [ "anyhow", "bindgen", "block", - "bytes 1.2.1", + "bytes", "core-foundation", "foreign-types", "metal", @@ -3396,9 +3253,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.5.4" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" dependencies = [ "adler", ] @@ -3572,16 +3429,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi 0.3.9", -] - [[package]] name = "num-bigint" version = "0.4.3" @@ -3672,21 +3519,6 @@ dependencies = [ "libc", ] -[[package]] -name = "nvim-rs" -version = "0.5.0" -source = "git+https://github.com/KillTheMule/nvim-rs?branch=master#d701c2790dcb2579f8f4d7003ba30e2100a7d25b" -dependencies = [ - "async-trait", - "futures 0.3.24", - "log", - "parity-tokio-ipc", - "rmp", - "rmpv", - "tokio", - "tokio-util 0.7.4", -] - [[package]] name = "objc" version = "0.2.7" @@ -3718,20 +3550,11 @@ dependencies = [ "memchr", ] -[[package]] -name = "object" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" -version = "1.15.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" [[package]] name = "opaque-debug" @@ -3741,9 +3564,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.42" +version = "0.10.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +checksum = "fb81a6430ac911acb25fe5ac8f1d2af1b4ea8a4fdfda0f1ee4292af2e2d8eb0e" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -3773,9 +3596,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.76" +version = "0.9.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" +checksum = "835363342df5fba8354c5b453325b110ffd54044e588c539cf2f20a8014e4cb1" dependencies = [ "autocfg 1.1.0", "cc", @@ -3795,9 +3618,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.3.0" +version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" +checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" [[package]] name = "outline" @@ -3816,26 +3639,6 @@ dependencies = [ "workspace", ] -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "parity-tokio-ipc" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9981e32fb75e004cc148f5fb70342f393830e0a4aa62e3cc93b50976218d42b6" -dependencies = [ - "futures 0.3.24", - "libc", - "log", - "rand 0.7.3", - "tokio", - "winapi 0.3.9", -] - [[package]] name = "parking" version = "2.0.0" @@ -3897,15 +3700,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77e0b28ace46c5a396546bcf443bf422b57049617433d8854227352a4a9b24e7" dependencies = [ "base64ct", - "rand_core 0.6.4", + "rand_core 0.6.3", "subtle", ] [[package]] name = "paste" -version = "1.0.9" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" +checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" [[package]] name = "pathfinder_color" @@ -3963,17 +3766,16 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pest" -version = "2.4.0" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc7bc69c062e492337d74d59b120c274fd3d261b6bf6d3207d499b4b379c41a" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" dependencies = [ - "thiserror", "ucd-trie", ] @@ -4011,18 +3813,18 @@ checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" [[package]] name = "pin-project" -version = "1.0.12" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "78203e83c48cffbe01e4a2d35d566ca4de445d79a85372fc64e378bfc812a260" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "710faf75e1b33345361201d36d04e98ac1ed8909151a017ed384700836104c74" dependencies = [ "proc-macro2", "quote", @@ -4063,7 +3865,7 @@ dependencies = [ "indexmap", "line-wrap", "serde", - "time 0.3.15", + "time 0.3.11", "xml-rs", ] @@ -4116,11 +3918,10 @@ dependencies = [ [[package]] name = "polling" -version = "2.3.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899b00b9c8ab553c743b3e11e87c5c7d423b2a2de229ba95b24a756344748011" +checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" dependencies = [ - "autocfg 1.1.0", "cfg-if 1.0.0", "libc", "log", @@ -4142,7 +3943,7 @@ checksum = "a63d25391d04a097954b76aba742b6b5b74f213dfe3dbaeeb36e8ddc1c657f0b" dependencies = [ "atomic", "crossbeam-queue", - "futures 0.3.24", + "futures", "log", "pin-project", "pollster", @@ -4182,9 +3983,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.46" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" dependencies = [ "unicode-ident", ] @@ -4212,7 +4013,7 @@ dependencies = [ "collections", "db", "fsevent", - "futures 0.3.24", + "futures", "fuzzy", "git", "gpui", @@ -4232,7 +4033,7 @@ dependencies = [ "serde", "serde_json", "settings", - "sha2 0.10.6", + "sha2 0.10.2", "similar", "smol", "sum_tree", @@ -4250,7 +4051,7 @@ version = "0.1.0" dependencies = [ "context_menu", "editor", - "futures 0.3.24", + "futures", "gpui", "menu", "postage", @@ -4269,7 +4070,7 @@ version = "0.1.0" dependencies = [ "anyhow", "editor", - "futures 0.3.24", + "futures", "fuzzy", "gpui", "language", @@ -4287,9 +4088,9 @@ dependencies = [ [[package]] name = "prometheus" -version = "0.13.2" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c8babc29389186697fe5a2a4859d697825496b83db5d0b65271cdc0488e88c" +checksum = "cface98dfa6d645ea4c789839f176e4b072265d085bfcc48eaa8d137f58d3c39" dependencies = [ "cfg-if 1.0.0", "fnv", @@ -4306,7 +4107,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020" dependencies = [ - "bytes 1.2.1", + "bytes", "prost-derive 0.8.0", ] @@ -4316,7 +4117,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" dependencies = [ - "bytes 1.2.1", + "bytes", "prost-derive 0.9.0", ] @@ -4326,7 +4127,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62941722fb675d463659e49c4f3fe1fe792ff24fe5bbaa9c08cd3b98a1c354f5" dependencies = [ - "bytes 1.2.1", + "bytes", "heck 0.3.3", "itertools", "lazy_static", @@ -4372,30 +4173,30 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a" dependencies = [ - "bytes 1.2.1", + "bytes", "prost 0.9.0", ] [[package]] name = "protobuf" -version = "2.28.0" +version = "2.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" +checksum = "cf7e6d18738ecd0902d30d1ad232c9125985a3422929b16c65517b38adc14f96" [[package]] name = "psm" -version = "0.1.21" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +checksum = "accd89aa18fbf9533a581355a22438101fe9c2ed8c9e2f0dcf520552a3afddf2" dependencies = [ "cc", ] [[package]] name = "pulldown-cmark" -version = "0.9.2" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" +checksum = "34f197a544b0c9ab3ae46c359a7ec9cbbb5c7bf97054266fecb7ead794a181d6" dependencies = [ "bitflags", "memchr", @@ -4404,9 +4205,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.21" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" dependencies = [ "proc-macro2", ] @@ -4445,7 +4246,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha 0.3.1", - "rand_core 0.6.4", + "rand_core 0.6.3", ] [[package]] @@ -4465,7 +4266,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", + "rand_core 0.6.3", ] [[package]] @@ -4494,9 +4295,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.4" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ "getrandom 0.2.7", ] @@ -4528,9 +4329,9 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" dependencies = [ - "crossbeam-channel 0.5.6", + "crossbeam-channel 0.5.5", "crossbeam-deque", - "crossbeam-utils 0.8.12", + "crossbeam-utils 0.8.10", "num_cpus", ] @@ -4551,9 +4352,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" dependencies = [ "bitflags", ] @@ -4630,12 +4431,12 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.12" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" dependencies = [ "base64", - "bytes 1.2.1", + "bytes", "encoding_rs", "futures-core", "futures-util", @@ -4646,10 +4447,10 @@ dependencies = [ "hyper-tls", "ipnet", "js-sys", + "lazy_static", "log", "mime", "native-tls", - "once_cell", "percent-encoding", "pin-project-lite 0.2.9", "serde", @@ -4683,9 +4484,9 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.34" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3603b7d71ca82644f79b5a06d1220e9a58ede60bd32255f698cb1af8838b8db3" +checksum = "c3b221de559e4a29df3b957eec92bc0de6bc8eaf6ca9cfed43e5e1d67ff65a34" dependencies = [ "bytemuck", ] @@ -4705,27 +4506,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "rmp" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44519172358fd6d58656c86ab8e7fbc9e1490c3e8f14d35ed78ca0dd07403c9f" -dependencies = [ - "byteorder", - "num-traits", - "paste", -] - -[[package]] -name = "rmpv" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de8813b3a2f95c5138fe5925bfb8784175d88d6bff059ba8ce090aa891319754" -dependencies = [ - "num-traits", - "rmp", -] - [[package]] name = "rocksdb" version = "0.18.0" @@ -4756,7 +4536,7 @@ dependencies = [ "collections", "ctor", "env_logger", - "futures 0.3.24", + "futures", "gpui", "parking_lot 0.11.2", "prost 0.8.0", @@ -4794,9 +4574,9 @@ dependencies = [ [[package]] name = "rust-embed" -version = "6.4.1" +version = "6.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e26934cd67a1da1165efe61cba4047cc1b4a526019da609fcce13a1000afb5fa" +checksum = "9a17e5ac65b318f397182ae94e532da0ba56b88dd1200b774715d36c4943b1c3" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -4805,9 +4585,9 @@ dependencies = [ [[package]] name = "rust-embed-impl" -version = "6.3.0" +version = "6.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35d7b402e273544cc08e0824aa3404333fab8a90ac43589d3d5b72f4b346e12" +checksum = "94e763e24ba2bf0c72bc6be883f967f794a019fafd1b86ba1daff9c91a7edd30" dependencies = [ "proc-macro2", "quote", @@ -4818,12 +4598,12 @@ dependencies = [ [[package]] name = "rust-embed-utils" -version = "7.3.0" +version = "7.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1669d81dfabd1b5f8e2856b8bbe146c6192b0ba22162edc738ac0a5de18f054" +checksum = "756feca3afcbb1487a1d01f4ecd94cf8ec98ea074c55a69e7136d29fb6166029" dependencies = [ "globset", - "sha2 0.10.6", + "sha2 0.9.9", "walkdir", ] @@ -4891,9 +4671,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +checksum = "e7522c9de787ff061458fe9a829dc790a3f5b22dc571694fc5883f448b94d9a9" dependencies = [ "base64", ] @@ -4916,9 +4696,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" [[package]] name = "safe_arch" @@ -4965,9 +4745,9 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.11" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307" +checksum = "1847b767a3d62d95cbf3d8a9f0e421cf57a0d8aa4f411d4b16525afb0284d4ed" dependencies = [ "dyn-clone", "schemars_derive", @@ -4977,9 +4757,9 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.11" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9" +checksum = "af4d7e1b012cb3d9129567661a63755ea4b8a7386d339dc945ae187e403c6743" dependencies = [ "proc-macro2", "quote", @@ -4999,12 +4779,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "scratch" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" - [[package]] name = "scrypt" version = "0.7.0" @@ -5070,9 +4844,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.7.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" dependencies = [ "bitflags", "core-foundation", @@ -5117,18 +4891,18 @@ checksum = "5a9f47faea3cad316faa914d013d24f471cd90bfca1a0c70f05a3f42c6441e99" [[package]] name = "serde" -version = "1.0.145" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.145" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c" dependencies = [ "proc-macro2", "quote", @@ -5157,9 +4931,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" dependencies = [ "indexmap", "itoa", @@ -5169,18 +4943,18 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.8" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "184c643044780f7ceb59104cef98a5a6f12cb2288a7bc701ab93a362b49fd47d" +checksum = "d7868ad3b8196a8a0aea99a8220b124278ee5320a55e4fde97794b6f85b1a377" dependencies = [ "serde", ] [[package]] name = "serde_repr" -version = "0.1.9" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca" +checksum = "a2ad84e47328a31223de7fed7a4f5087f2d6ddfe586cf3ca25b7a165bc0a5aed" dependencies = [ "proc-macro2", "quote", @@ -5201,9 +4975,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.8.26" +version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +checksum = "707d15895415db6628332b737c838b88c598522e4dc70647e59b72312924aebc" dependencies = [ "indexmap", "ryu", @@ -5271,18 +5045,7 @@ checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.5", -] - -[[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.10.5", + "digest 0.10.3", ] [[package]] @@ -5300,13 +5063,13 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.5", + "digest 0.10.3", ] [[package]] @@ -5320,11 +5083,11 @@ dependencies = [ [[package]] name = "shellexpand" -version = "2.1.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4" +checksum = "83bdb7831b2d85ddf4a7b148aa19d0587eddbe8671a436b7bd1182eaad0f2829" dependencies = [ - "dirs 4.0.0", + "dirs-next", ] [[package]] @@ -5410,12 +5173,9 @@ checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" [[package]] name = "slab" -version = "0.4.7" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" -dependencies = [ - "autocfg 1.1.0", -] +checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" [[package]] name = "slice-group-by" @@ -5436,9 +5196,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] name = "smol" @@ -5478,9 +5238,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" dependencies = [ "libc", "winapi 0.3.9", @@ -5500,9 +5260,9 @@ checksum = "be6c3f39c37a4283ee4b43d1311c828f2e1fb0541e76ea0cb1a2abd9ef2f5b3b" [[package]] name = "sqlformat" -version = "0.2.0" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87e292b4291f154971a43c3774364e2cbcaec599d3f5bf6fa9d122885dbc38a" +checksum = "b4b7922be017ee70900be125523f38bdd644f4f06a1b16e8fa5a8ee8c34bffd4" dependencies = [ "itertools", "nom", @@ -5511,9 +5271,9 @@ dependencies = [ [[package]] name = "sqlx" -version = "0.6.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9249290c05928352f71c077cc44a464d880c63f26f7534728cca008e135c0428" +checksum = "1f82cbe94f41641d6c410ded25bbf5097c240cefdf8e3b06d04198d0a96af6a4" dependencies = [ "sqlx-core", "sqlx-macros", @@ -5521,20 +5281,19 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.6.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbc16ddba161afc99e14d1713a453747a2b07fc097d2009f4c300ec99286105" +checksum = "6b69bf218860335ddda60d6ce85ee39f6cf6e5630e300e19757d1de15886a093" dependencies = [ "ahash", "atoi", "base64", "bitflags", "byteorder", - "bytes 1.2.1", + "bytes", "crc", "crossbeam-queue", "dirs 4.0.0", - "dotenvy", "either", "event-listener", "futures-channel", @@ -5559,34 +5318,34 @@ dependencies = [ "rustls-pemfile", "serde", "serde_json", - "sha1", - "sha2 0.10.6", + "sha-1 0.10.0", + "sha2 0.10.2", "smallvec", "sqlformat", "sqlx-rt", "stringprep", "thiserror", - "time 0.3.15", + "time 0.3.11", "tokio-stream", "url", - "uuid 1.2.1", - "webpki-roots 0.22.5", + "uuid 1.1.2", + "webpki-roots 0.22.3", "whoami", ] [[package]] name = "sqlx-macros" -version = "0.6.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b850fa514dc11f2ee85be9d055c512aa866746adfacd1cb42d867d68e6a5b0d9" +checksum = "f40c63177cf23d356b159b60acd27c54af7423f1736988502e36bae9a712118f" dependencies = [ - "dotenvy", + "dotenv", "either", "heck 0.4.0", "once_cell", "proc-macro2", "quote", - "sha2 0.10.6", + "sha2 0.10.2", "sqlx-core", "sqlx-rt", "syn", @@ -5595,9 +5354,9 @@ dependencies = [ [[package]] name = "sqlx-rt" -version = "0.6.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24c5b2d25fa654cc5f841750b8e1cdedbe21189bf9a9382ee90bfa9dd3562396" +checksum = "874e93a365a598dc3dadb197565952cb143ae4aa716f7bcc933a8d836f6bf89f" dependencies = [ "once_cell", "tokio", @@ -5692,9 +5451,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.102" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" dependencies = [ "proc-macro2", "quote", @@ -5784,7 +5543,7 @@ dependencies = [ "context_menu", "dirs 4.0.0", "editor", - "futures 0.3.24", + "futures", "gpui", "itertools", "lazy_static", @@ -5840,9 +5599,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.15.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" [[package]] name = "theme" @@ -5877,18 +5636,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" dependencies = [ "proc-macro2", "quote", @@ -5934,9 +5693,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.15" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d634a985c4d4238ec39cacaed2e7ae552fbd3c476b552c1deac3021b7d7eaf0c" +checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217" dependencies = [ "itoa", "libc", @@ -5995,16 +5754,16 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.21.2" +version = "1.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" dependencies = [ - "autocfg 1.1.0", - "bytes 1.2.1", + "bytes", "libc", "memchr", "mio 0.8.4", "num_cpus", + "once_cell", "parking_lot 0.12.1", "pin-project-lite 0.2.9", "signal-hook-registry", @@ -6013,17 +5772,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "tokio-io" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" -dependencies = [ - "bytes 0.4.12", - "futures 0.1.31", - "log", -] - [[package]] name = "tokio-io-timeout" version = "1.2.0" @@ -6068,9 +5816,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.10" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6edf2d6bc038a43d31353570e27270603f4648d18f5ed10c0e179abe43255af" +checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9" dependencies = [ "futures-core", "pin-project-lite 0.2.9", @@ -6079,14 +5827,14 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.17.2" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" +checksum = "06cda1232a49558c46f8a504d5b93101d42c0bf7f911f12a105ba48168f821ae" dependencies = [ "futures-util", "log", "tokio", - "tungstenite 0.17.3", + "tungstenite 0.17.2", ] [[package]] @@ -6095,7 +5843,7 @@ version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" dependencies = [ - "bytes 1.2.1", + "bytes", "futures-core", "futures-sink", "log", @@ -6105,13 +5853,12 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" dependencies = [ - "bytes 1.2.1", + "bytes", "futures-core", - "futures-io", "futures-sink", "pin-project-lite 0.2.9", "tokio", @@ -6136,7 +5883,7 @@ dependencies = [ "async-stream", "async-trait", "base64", - "bytes 1.2.1", + "bytes", "futures-core", "futures-util", "h2", @@ -6172,7 +5919,7 @@ dependencies = [ "rand 0.8.5", "slab", "tokio", - "tokio-util 0.7.4", + "tokio-util 0.7.3", "tower-layer", "tower-service", "tracing", @@ -6185,7 +5932,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" dependencies = [ "bitflags", - "bytes 1.2.1", + "bytes", "futures-core", "futures-util", "http", @@ -6211,9 +5958,9 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" dependencies = [ "cfg-if 1.0.0", "log", @@ -6224,9 +5971,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" dependencies = [ "proc-macro2", "quote", @@ -6235,9 +5982,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" dependencies = [ "once_cell", "valuable", @@ -6276,12 +6023,12 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.16" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +checksum = "3a713421342a5a666b7577783721d3117f1b69a393df803ee17bb73b1e122a59" dependencies = [ + "ansi_term", "matchers", - "nu-ansi-term", "once_cell", "regex", "serde", @@ -6306,9 +6053,9 @@ dependencies = [ [[package]] name = "tree-sitter-c" -version = "0.20.2" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cca211f4827d4b4dc79f388bf67b6fa3bc8a8cfa642161ef24f99f371ba34c7b" +checksum = "7bdc5574c6cbc39c409246caeb1dd4d3c4bd6d30d4e9b399776086c20365fd24" dependencies = [ "cc", "tree-sitter", @@ -6464,7 +6211,7 @@ checksum = "6ad3713a14ae247f22a728a0456a545df14acf3867f905adff84be99e23b3ad1" dependencies = [ "base64", "byteorder", - "bytes 1.2.1", + "bytes", "http", "httparse", "log", @@ -6477,13 +6224,13 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.17.3" +version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" +checksum = "d96a2dea40e7570482f28eb57afbe42d97551905da6a9400acc5c328d24004f5" dependencies = [ "base64", "byteorder", - "bytes 1.2.1", + "bytes", "http", "httparse", "log", @@ -6502,9 +6249,9 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "ucd-trie" -version = "0.1.5" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" +checksum = "89570599c4fe5585de2b388aab47e99f7fa4e9238a1399f707a02e356058141c" [[package]] name = "unicase" @@ -6541,30 +6288,30 @@ checksum = "7f9af028e052a610d99e066b33304625dea9613170a2563314490a4e6ec5cf7f" [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" dependencies = [ "tinyvec", ] [[package]] name = "unicode-script" -version = "0.5.5" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc" +checksum = "58dd944fd05f2f0b5c674917aea8a4df6af84f2d8de3fe8d988b95d28fb8fb09" [[package]] name = "unicode-segmentation" -version = "1.10.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" +checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" [[package]] name = "unicode-vo" @@ -6574,15 +6321,15 @@ checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" [[package]] name = "unicode_categories" @@ -6592,9 +6339,9 @@ checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" [[package]] name = "unindent" -version = "0.1.10" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ee9362deb4a96cef4d437d1ad49cffc9b9e92d202b6995674e928ce684f112" +checksum = "52fee519a3e570f7df377a06a1a7775cdbfb7aa460be7e08de2b1f0e69973a44" [[package]] name = "untrusted" @@ -6604,12 +6351,13 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.3.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" dependencies = [ "form_urlencoded", "idna", + "matches", "percent-encoding", "serde", ] @@ -6658,7 +6406,7 @@ name = "util" version = "0.1.0" dependencies = [ "anyhow", - "futures 0.3.24", + "futures", "git2", "lazy_static", "log", @@ -6678,9 +6426,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.2.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" +checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" dependencies = [ "getrandom 0.2.7", ] @@ -6728,8 +6476,6 @@ name = "vim" version = "0.1.0" dependencies = [ "assets", - "async-compat", - "async-trait", "collections", "command_palette", "editor", @@ -6737,16 +6483,11 @@ dependencies = [ "indoc", "itertools", "language", - "lazy_static", "log", - "nvim-rs", - "parking_lot 0.11.2", "project", "search", "serde", - "serde_json", "settings", - "tokio", "util", "workspace", ] @@ -6818,9 +6559,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi-cap-std-sync" -version = "0.38.3" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f086c5026d2fc3b268d138e65373f46422cc810f46d6e0776859c5027cb18728" +checksum = "c1c4e73ed64b92ae87b416f4274b3c827180b02b67f835f66a86fc4267b77349" dependencies = [ "anyhow", "async-trait", @@ -6842,9 +6583,9 @@ dependencies = [ [[package]] name = "wasi-common" -version = "0.38.3" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e8844fede1c3787cc08853872f47e8bd91f6c939c7406bc7a5dba496b260c08" +checksum = "cc983eb93607a61f64152ec8728bf453f4dfdf22e7ab1784faac3297fe9a035e" dependencies = [ "anyhow", "bitflags", @@ -6860,9 +6601,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -6870,13 +6611,13 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" dependencies = [ "bumpalo", + "lazy_static", "log", - "once_cell", "proc-macro2", "quote", "syn", @@ -6885,9 +6626,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -6897,9 +6638,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6907,9 +6648,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" dependencies = [ "proc-macro2", "quote", @@ -6920,15 +6661,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" [[package]] name = "wasm-encoder" -version = "0.18.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64ac98d5d61192cc45c701b7e4bd0b9aff91e2edfc7a088406cfe2288581e2c" +checksum = "f76068e87fe9b837a6bc2ccded66784173eadb828c4168643e9fddf6f9ed2e61" dependencies = [ "leb128", ] @@ -6944,9 +6685,9 @@ dependencies = [ [[package]] name = "wasmtime" -version = "0.38.3" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f50eadf868ab6a04b7b511460233377d0bfbb92e417b2f6a98b98fef2e098f5" +checksum = "e76e2b2833bb0ece666ccdbed7b71b617d447da11f1bb61f4f2bab2648f745ee" dependencies = [ "anyhow", "async-trait", @@ -6957,7 +6698,7 @@ dependencies = [ "lazy_static", "libc", "log", - "object 0.28.4", + "object", "once_cell", "paste", "psm", @@ -6978,9 +6719,9 @@ dependencies = [ [[package]] name = "wasmtime-cache" -version = "0.38.3" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1df23c642e1376892f3b72f311596976979cbf8b85469680cdd3a8a063d12a2" +checksum = "743a9f142d93318262d7e1fe329394ff2e8f86a1df45ae5e4f0eedba215ca5ce" dependencies = [ "anyhow", "base64", @@ -6998,9 +6739,9 @@ dependencies = [ [[package]] name = "wasmtime-cranelift" -version = "0.38.3" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f264ff6b4df247d15584f2f53d009fbc90032cfdc2605b52b961bffc71b6eccd" +checksum = "5dc0f80afa1ce97083a7168e6b6948d015d6237369e9f4a511d38c9c4ac8fbb9" dependencies = [ "anyhow", "cranelift-codegen", @@ -7011,7 +6752,7 @@ dependencies = [ "gimli", "log", "more-asserts", - "object 0.28.4", + "object", "target-lexicon", "thiserror", "wasmparser", @@ -7020,9 +6761,9 @@ dependencies = [ [[package]] name = "wasmtime-environ" -version = "0.38.3" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "839d2820e4b830f4b9e7aa08d4c0acabf4a5036105d639f6dfa1c6891c73bdc6" +checksum = "0816d9365196f1f447060087e0f87239ccded830bd54970a1168b0c9c8e824c9" dependencies = [ "anyhow", "cranelift-entity", @@ -7030,7 +6771,7 @@ dependencies = [ "indexmap", "log", "more-asserts", - "object 0.28.4", + "object", "serde", "target-lexicon", "thiserror", @@ -7040,9 +6781,9 @@ dependencies = [ [[package]] name = "wasmtime-fiber" -version = "0.38.3" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3248be3c4911233535356025f6562193614a40155ee9094bb6a2b43f0dc82803" +checksum = "715afdb87a3bcf1eae3f098c742d650fb783abdb8a7ca87076ea1cabecabea5d" dependencies = [ "cc", "rustix", @@ -7051,9 +6792,9 @@ dependencies = [ [[package]] name = "wasmtime-jit" -version = "0.38.3" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef0a0bcbfa18b946d890078ba0e1bc76bcc53eccfb40806c0020ec29dcd1bd49" +checksum = "5c687f33cfa0f89ec1646929d0ff102087052cf9f0d15533de56526b0da0d1b3" dependencies = [ "addr2line", "anyhow", @@ -7063,7 +6804,7 @@ dependencies = [ "gimli", "ittapi-rs", "log", - "object 0.28.4", + "object", "region", "rustc-demangle", "rustix", @@ -7078,20 +6819,20 @@ dependencies = [ [[package]] name = "wasmtime-jit-debug" -version = "0.38.3" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4779d976206c458edd643d1ac622b6c37e4a0800a8b1d25dfbf245ac2f2cac" +checksum = "b252d1d025f94f3954ba2111f12f3a22826a0764a11c150c2d46623115a69e27" dependencies = [ "lazy_static", - "object 0.28.4", + "object", "rustix", ] [[package]] name = "wasmtime-runtime" -version = "0.38.3" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7eb6ffa169eb5dcd18ac9473c817358cd57bc62c244622210566d473397954a" +checksum = "ace251693103c9facbbd7df87a29a75e68016e48bc83c09133f2fda6b575e0ab" dependencies = [ "anyhow", "backtrace", @@ -7116,9 +6857,9 @@ dependencies = [ [[package]] name = "wasmtime-types" -version = "0.38.3" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d932b0ac5336f7308d869703dd225610a6a3aeaa8e968c52b43eed96cefb1c2" +checksum = "d129b0487a95986692af8708ffde9c50b0568dcefd79200941d475713b4f40bb" dependencies = [ "cranelift-entity", "serde", @@ -7128,9 +6869,9 @@ dependencies = [ [[package]] name = "wasmtime-wasi" -version = "0.38.3" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b68b7d77fb6f2975a6fe6cc4d0015d6b0cebb65c39fce1dd4cc00880dbf7789c" +checksum = "fb49791530b3a3375897a6d5a8bfa9914101ef8a672d01c951e70b46fd953c15" dependencies = [ "anyhow", "wasi-cap-std-sync", @@ -7150,9 +6891,9 @@ dependencies = [ [[package]] name = "wast" -version = "47.0.1" +version = "43.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b98502f3978adea49551e801a6687678e6015317d7d9470a67fe813393f2a8" +checksum = "408feaebf6dbf9d154957873b14d00e8fba4cbc17a8cbb1bc9e4c1db425c50a8" dependencies = [ "leb128", "memchr", @@ -7162,18 +6903,18 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.49" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aab4e20c60429fbba9670a6cae0fff9520046ba0aa3e6d0b1cd2653bea14898" +checksum = "2b70bfff0cfaf33dc9d641196dbcd0023a2da8b4b9030c59535cb44e2884983b" dependencies = [ - "wast 47.0.1", + "wast 43.0.0", ] [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" dependencies = [ "js-sys", "wasm-bindgen", @@ -7210,18 +6951,18 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.5" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be" +checksum = "44d8de8415c823c8abd270ad483c6feeac771fad964890779f9a8cb24fbbc1bf" dependencies = [ "webpki 0.22.0", ] [[package]] name = "weezl" -version = "0.1.7" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" +checksum = "9c97e489d8f836838d497091de568cf16b117486d529ec5579233521065bd5e4" [[package]] name = "wepoll-ffi" @@ -7234,31 +6975,30 @@ dependencies = [ [[package]] name = "which" -version = "4.3.0" +version = "4.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" +checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" dependencies = [ "either", + "lazy_static", "libc", - "once_cell", ] [[package]] name = "whoami" -version = "1.2.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6631b6a2fd59b1841b622e8f1a7ad241ef0a46f2d580464ce8140ac94cbd571" +checksum = "524b58fa5a20a2fb3014dd6358b70e6579692a56ef6fce928834e488f42f65e8" dependencies = [ - "bumpalo", "wasm-bindgen", "web-sys", ] [[package]] name = "wiggle" -version = "0.38.3" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67dadac11343d2aabc8a906a0db0aaf7cb5046ec3d6fffccdaf2847dccdef8d6" +checksum = "91c38020359fabec5e5ce5a3f667af72e9a203bc6fe8caeb8931d3a870754d9d" dependencies = [ "anyhow", "async-trait", @@ -7271,9 +7011,9 @@ dependencies = [ [[package]] name = "wiggle-generate" -version = "0.38.3" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63a1dccd6b3fbd9a27417f5d30ce9aa3ee9cf529aad453abbf88a49c5d605b79" +checksum = "adc4e4420b496b04920ae3e41424029aba95c15a5e2e2b4012d14ec83770a3ef" dependencies = [ "anyhow", "heck 0.4.0", @@ -7286,9 +7026,9 @@ dependencies = [ [[package]] name = "wiggle-macro" -version = "0.38.3" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c368d57d9560c34deaa67e06b0953ccf65edb906c525e5a2c866c849b48ec2" +checksum = "2e541a0be1f2c4d53471d8a9df81c2d8725a3f023d8259f555c65b03d515aaab" dependencies = [ "proc-macro2", "quote", @@ -7428,12 +7168,12 @@ name = "workspace" version = "0.1.0" dependencies = [ "anyhow", + "call", "client", - "clock", "collections", "context_menu", "drag_and_drop", - "futures 0.3.24", + "futures", "gpui", "language", "log", @@ -7499,15 +7239,15 @@ dependencies = [ "auto_update", "backtrace", "breadcrumbs", + "call", "chat_panel", "chrono", "cli", "client", "clock", + "collab_ui", "collections", "command_palette", - "contacts_panel", - "contacts_status_item", "context_menu", "ctor", "diagnostics", @@ -7517,7 +7257,7 @@ dependencies = [ "env_logger", "file_finder", "fsevent", - "futures 0.3.24", + "futures", "fuzzy", "go_to_line", "gpui", diff --git a/crates/vim/src/motion.rs b/crates/vim/src/motion.rs index 5d25ece562..860b61a4ef 100644 --- a/crates/vim/src/motion.rs +++ b/crates/vim/src/motion.rs @@ -155,31 +155,32 @@ impl Motion { map: &DisplaySnapshot, point: DisplayPoint, goal: SelectionGoal, + times: usize, ) -> (DisplayPoint, SelectionGoal) { use Motion::*; match self { - Left => (left(map, point), SelectionGoal::None), - Backspace => (movement::left(map, point), SelectionGoal::None), - Down => movement::down(map, point, goal, true), - Up => movement::up(map, point, goal, true), - Right => (right(map, point), SelectionGoal::None), + Left => (left(map, point, times), SelectionGoal::None), + Backspace => (backspace(map, point, times), SelectionGoal::None), + Down => down(map, point, goal, times), + Up => up(map, point, goal, times), + Right => (right(map, point, times), SelectionGoal::None), NextWordStart { ignore_punctuation } => ( - next_word_start(map, point, ignore_punctuation), + next_word_start(map, point, ignore_punctuation, times), SelectionGoal::None, ), NextWordEnd { ignore_punctuation } => ( - next_word_end(map, point, ignore_punctuation), + next_word_end(map, point, ignore_punctuation, times), SelectionGoal::None, ), PreviousWordStart { ignore_punctuation } => ( - previous_word_start(map, point, ignore_punctuation), + previous_word_start(map, point, ignore_punctuation, times), SelectionGoal::None, ), FirstNonWhitespace => (first_non_whitespace(map, point), SelectionGoal::None), StartOfLine => (start_of_line(map, point), SelectionGoal::None), EndOfLine => (end_of_line(map, point), SelectionGoal::None), CurrentLine => (end_of_line(map, point), SelectionGoal::None), - StartOfDocument => (start_of_document(map, point), SelectionGoal::None), + StartOfDocument => (start_of_document(map, point, times), SelectionGoal::None), EndOfDocument => (end_of_document(map, point), SelectionGoal::None), Matching => (matching(map, point), SelectionGoal::None), } @@ -193,10 +194,8 @@ impl Motion { times: usize, expand_to_surrounding_newline: bool, ) { - for _ in 0..times { - let (head, goal) = self.move_point(map, selection.head(), selection.goal); - selection.set_head(head, goal); - } + let (head, goal) = self.move_point(map, selection.head(), selection.goal, times); + selection.set_head(head, goal); if self.linewise() { selection.start = map.prev_line_boundary(selection.start.to_point(map)).1; @@ -243,77 +242,133 @@ impl Motion { } } -fn left(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint { - *point.column_mut() = point.column().saturating_sub(1); - map.clip_point(point, Bias::Left) +fn left(map: &DisplaySnapshot, mut point: DisplayPoint, times: usize) -> DisplayPoint { + for _ in 0..times { + *point.column_mut() = point.column().saturating_sub(1); + point = map.clip_point(point, Bias::Right); + if point.column() == 0 { + break; + } + } + point } -pub(crate) fn right(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint { - *point.column_mut() += 1; - map.clip_point(point, Bias::Right) +fn backspace(map: &DisplaySnapshot, mut point: DisplayPoint, times: usize) -> DisplayPoint { + for _ in 0..times { + point = movement::left(map, point); + } + point +} + +fn down( + map: &DisplaySnapshot, + mut point: DisplayPoint, + mut goal: SelectionGoal, + times: usize, +) -> (DisplayPoint, SelectionGoal) { + for _ in 0..times { + (point, goal) = movement::down(map, point, goal, true); + } + (point, goal) +} + +fn up( + map: &DisplaySnapshot, + mut point: DisplayPoint, + mut goal: SelectionGoal, + times: usize, +) -> (DisplayPoint, SelectionGoal) { + for _ in 0..times { + (point, goal) = movement::up(map, point, goal, true); + } + (point, goal) +} + +pub(crate) fn right(map: &DisplaySnapshot, mut point: DisplayPoint, times: usize) -> DisplayPoint { + for _ in 0..times { + let mut new_point = point; + *new_point.column_mut() += 1; + let new_point = map.clip_point(new_point, Bias::Right); + if point == new_point { + break; + } + point = new_point; + } + point } pub(crate) fn next_word_start( map: &DisplaySnapshot, - point: DisplayPoint, + mut point: DisplayPoint, ignore_punctuation: bool, + times: usize, ) -> DisplayPoint { - let mut crossed_newline = false; - movement::find_boundary(map, point, |left, right| { - let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation); - let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation); - let at_newline = right == '\n'; + for _ in 0..times { + let mut crossed_newline = false; + point = movement::find_boundary(map, point, |left, right| { + let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation); + let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation); + let at_newline = right == '\n'; - let found = (left_kind != right_kind && right_kind != CharKind::Whitespace) - || at_newline && crossed_newline - || at_newline && left == '\n'; // Prevents skipping repeated empty lines + let found = (left_kind != right_kind && right_kind != CharKind::Whitespace) + || at_newline && crossed_newline + || at_newline && left == '\n'; // Prevents skipping repeated empty lines - if at_newline { - crossed_newline = true; - } - found - }) + if at_newline { + crossed_newline = true; + } + found + }) + } + point } fn next_word_end( map: &DisplaySnapshot, mut point: DisplayPoint, ignore_punctuation: bool, + times: usize, ) -> DisplayPoint { - *point.column_mut() += 1; - point = movement::find_boundary(map, point, |left, right| { - let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation); - let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation); + for _ in 0..times { + *point.column_mut() += 1; + point = movement::find_boundary(map, point, |left, right| { + let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation); + let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation); - left_kind != right_kind && left_kind != CharKind::Whitespace - }); + left_kind != right_kind && left_kind != CharKind::Whitespace + }); - // find_boundary clips, so if the character after the next character is a newline or at the end of the document, we know - // we have backtraced already - if !map - .chars_at(point) - .nth(1) - .map(|(c, _)| c == '\n') - .unwrap_or(true) - { - *point.column_mut() = point.column().saturating_sub(1); + // find_boundary clips, so if the character after the next character is a newline or at the end of the document, we know + // we have backtraced already + if !map + .chars_at(point) + .nth(1) + .map(|(c, _)| c == '\n') + .unwrap_or(true) + { + *point.column_mut() = point.column().saturating_sub(1); + } + point = map.clip_point(point, Bias::Left); } - map.clip_point(point, Bias::Left) + point } fn previous_word_start( map: &DisplaySnapshot, mut point: DisplayPoint, ignore_punctuation: bool, + times: usize, ) -> DisplayPoint { - // This works even though find_preceding_boundary is called for every character in the line containing - // cursor because the newline is checked only once. - point = movement::find_preceding_boundary(map, point, |left, right| { - let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation); - let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation); + for _ in 0..times { + // This works even though find_preceding_boundary is called for every character in the line containing + // cursor because the newline is checked only once. + point = movement::find_preceding_boundary(map, point, |left, right| { + let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation); + let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation); - (left_kind != right_kind && !right.is_whitespace()) || left == '\n' - }); + (left_kind != right_kind && !right.is_whitespace()) || left == '\n' + }); + } point } @@ -342,8 +397,8 @@ fn end_of_line(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint { map.clip_point(map.next_line_boundary(point.to_point(map)).1, Bias::Left) } -fn start_of_document(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint { - let mut new_point = 0usize.to_display_point(map); +fn start_of_document(map: &DisplaySnapshot, point: DisplayPoint, line: usize) -> DisplayPoint { + let mut new_point = (line - 1).to_display_point(map); *new_point.column_mut() = point.column(); map.clip_point(new_point, Bias::Left) } diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index 969a11bffc..894b77e6e8 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -115,13 +115,7 @@ pub fn normal_object(object: Object, cx: &mut MutableAppContext) { fn move_cursor(vim: &mut Vim, motion: Motion, times: usize, cx: &mut MutableAppContext) { vim.update_active_editor(cx, |editor, cx| { editor.change_selections(Some(Autoscroll::Fit), cx, |s| { - s.move_cursors_with(|map, cursor, goal| { - let mut result = (cursor, goal); - for _ in 0..times { - result = motion.move_point(map, result.0, result.1); - } - result - }) + s.move_cursors_with(|map, cursor, goal| motion.move_point(map, cursor, goal, times)) }) }); } @@ -132,7 +126,7 @@ fn insert_after(_: &mut Workspace, _: &InsertAfter, cx: &mut ViewContext Date: Tue, 11 Oct 2022 18:50:04 -0700 Subject: [PATCH 265/314] Impose min scrollbar height in a way that doesn't impede scrollbar's movement Also, fix the editor's scroll max so that you can scroll to the last display row. --- crates/editor/src/editor.rs | 2 +- crates/editor/src/element.rs | 36 +++++++++++++++--------------------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 4be6d72017..a7acc9f609 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1271,7 +1271,7 @@ impl Editor { let max_scroll_top = if matches!(self.mode, EditorMode::AutoHeight { .. }) { (display_map.max_point().row() as f32 - visible_lines + 1.).max(0.) } else { - display_map.max_point().row().saturating_sub(1) as f32 + display_map.max_point().row() as f32 }; if scroll_position.y() > max_scroll_top { scroll_position.set_y(max_scroll_top); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index ec39bf1eb2..36d717641e 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -916,36 +916,30 @@ impl EditorElement { let view = self.view.clone(); let style = &self.style.theme.scrollbar; - let min_thumb_height = - style.min_height_factor * cx.font_cache.line_height(self.style.text.font_size); let top = bounds.min_y(); let bottom = bounds.max_y(); let right = bounds.max_x(); let left = right - style.width; - let height = bounds.height(); let row_range = &layout.scrollbar_row_range; - let max_row = layout.max_row + ((row_range.end - row_range.start) as u32); - let scrollbar_start = row_range.start as f32 / max_row as f32; - let scrollbar_end = row_range.end as f32 / max_row as f32; + let max_row = layout.max_row as f32 + (row_range.end - row_range.start); - let mut thumb_top = top + scrollbar_start * height; - let mut thumb_bottom = top + scrollbar_end * height; - let thumb_center = (thumb_top + thumb_bottom) / 2.0; + let mut height = bounds.height(); + let mut first_row_y_offset = 0.0; - if thumb_bottom - thumb_top < min_thumb_height { - thumb_top = thumb_center - min_thumb_height / 2.0; - thumb_bottom = thumb_center + min_thumb_height / 2.0; - if thumb_top < top { - thumb_top = top; - thumb_bottom = top + min_thumb_height; - } - if thumb_bottom > bottom { - thumb_bottom = bottom; - thumb_top = bottom - min_thumb_height; - } + // Impose a minimum height on the scrollbar thumb + let min_thumb_height = + style.min_height_factor * cx.font_cache.line_height(self.style.text.font_size); + let thumb_height = (row_range.end - row_range.start) * height / max_row; + if thumb_height < min_thumb_height { + first_row_y_offset = (min_thumb_height - thumb_height) / 2.0; + height -= min_thumb_height - thumb_height; } + let y_for_row = |row: f32| -> f32 { top + first_row_y_offset + row * height / max_row }; + + let thumb_top = y_for_row(row_range.start) - first_row_y_offset; + let thumb_bottom = y_for_row(row_range.end) + first_row_y_offset; let track_bounds = RectF::from_points(vec2f(left, top), vec2f(right, bottom)); let thumb_bounds = RectF::from_points(vec2f(left, thumb_top), vec2f(right, thumb_bottom)); @@ -1723,7 +1717,7 @@ impl Element for EditorElement { let scroll_max = vec2f( ((scroll_width - text_size.x()) / em_width).max(0.0), - max_row.saturating_sub(1) as f32, + max_row as f32, ); self.update_view(cx.app, |view, cx| { From e7b6d1befeb0c44733b10a26157b56773c1bb061 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 11 Oct 2022 19:18:29 -0700 Subject: [PATCH 266/314] Added theme and dock anchor saving :D --- Cargo.lock | 2 + crates/fs/Cargo.toml | 1 + crates/fs/src/fs.rs | 23 ++++++++++ crates/settings/Cargo.toml | 2 + crates/settings/src/settings.rs | 31 ++++++++----- crates/settings/src/settings_file.rs | 49 ++++++++++++++++++--- crates/theme_selector/src/theme_selector.rs | 4 ++ crates/workspace/src/dock.rs | 7 +++ crates/zed/src/main.rs | 11 +++-- 9 files changed, 111 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fca454f1f8..53f8aae9c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2009,6 +2009,7 @@ dependencies = [ "serde", "serde_json", "smol", + "tempfile", "util", ] @@ -5065,6 +5066,7 @@ dependencies = [ "gpui", "json_comments", "postage", + "rope", "schemars", "serde", "serde_json", diff --git a/crates/fs/Cargo.toml b/crates/fs/Cargo.toml index 182d13894d..5b9082d114 100644 --- a/crates/fs/Cargo.toml +++ b/crates/fs/Cargo.toml @@ -15,6 +15,7 @@ util = { path = "../util" } anyhow = "1.0.57" async-trait = "0.1" futures = "0.3" +tempfile = "3" fsevent = { path = "../fsevent" } lazy_static = "1.4.0" parking_lot = "0.11.1" diff --git a/crates/fs/src/fs.rs b/crates/fs/src/fs.rs index 26045f2776..2061d3734b 100644 --- a/crates/fs/src/fs.rs +++ b/crates/fs/src/fs.rs @@ -12,6 +12,7 @@ use rope::Rope; use smol::io::{AsyncReadExt, AsyncWriteExt}; use std::borrow::Cow; use std::cmp; +use std::io::Write; use std::sync::Arc; use std::{ io, @@ -20,6 +21,7 @@ use std::{ pin::Pin, time::{Duration, SystemTime}, }; +use tempfile::NamedTempFile; use util::ResultExt; #[cfg(any(test, feature = "test-support"))] @@ -100,6 +102,7 @@ pub trait Fs: Send + Sync { async fn remove_file(&self, path: &Path, options: RemoveOptions) -> Result<()>; async fn open_sync(&self, path: &Path) -> Result>; async fn load(&self, path: &Path) -> Result; + async fn atomic_write(&self, path: PathBuf, text: String) -> Result<()>; async fn save(&self, path: &Path, text: &Rope, line_ending: LineEnding) -> Result<()>; async fn canonicalize(&self, path: &Path) -> Result; async fn is_file(&self, path: &Path) -> bool; @@ -260,6 +263,18 @@ impl Fs for RealFs { Ok(text) } + async fn atomic_write(&self, path: PathBuf, data: String) -> Result<()> { + smol::unblock(move || { + let mut tmp_file = NamedTempFile::new()?; + tmp_file.write_all(data.as_bytes())?; + tmp_file.persist(path)?; + Ok::<(), anyhow::Error>(()) + }) + .await?; + + Ok(()) + } + async fn save(&self, path: &Path, text: &Rope, line_ending: LineEnding) -> Result<()> { let buffer_size = text.summary().len.min(10 * 1024); let file = smol::fs::File::create(path).await?; @@ -880,6 +895,14 @@ impl Fs for FakeFs { entry.file_content(&path).cloned() } + async fn atomic_write(&self, path: PathBuf, data: String) -> Result<()> { + self.simulate_random_delay().await; + let path = normalize_path(path.as_path()); + self.insert_file(path, data.to_string()).await; + + Ok(()) + } + async fn save(&self, path: &Path, text: &Rope, line_ending: LineEnding) -> Result<()> { self.simulate_random_delay().await; let path = normalize_path(path); diff --git a/crates/settings/Cargo.toml b/crates/settings/Cargo.toml index 64c906a833..1cc73fabc4 100644 --- a/crates/settings/Cargo.toml +++ b/crates/settings/Cargo.toml @@ -19,6 +19,7 @@ anyhow = "1.0.38" futures = "0.3" theme = { path = "../theme" } util = { path = "../util" } +rope = { path = "../rope" } json_comments = "0.2" postage = { version = "0.4.1", features = ["futures-traits"] } schemars = "0.8" @@ -32,3 +33,4 @@ tree-sitter-json = "*" [dev-dependencies] unindent = "0.1" gpui = { path = "../gpui", features = ["test-support"] } +fs = { path = "../fs", features = ["test-support"] } diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 883d7694c7..2e7dc08d16 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -503,7 +503,11 @@ pub fn settings_file_json_schema( serde_json::to_value(root_schema).unwrap() } -pub fn write_theme(mut settings_content: String, new_val: &str) -> String { +pub fn write_top_level_setting( + mut settings_content: String, + top_level_key: &str, + new_val: &str, +) -> String { let mut parser = tree_sitter::Parser::new(); parser.set_language(tree_sitter_json::language()).unwrap(); let tree = parser.parse(&settings_content, None).unwrap(); @@ -536,7 +540,7 @@ pub fn write_theme(mut settings_content: String, new_val: &str) -> String { first_key_start.get_or_insert_with(|| key.node.start_byte()); if let Some(key_text) = settings_content.get(key.node.byte_range()) { - if key_text == "\"theme\"" { + if key_text == format!("\"{top_level_key}\"") { existing_value_range = Some(value.node.byte_range()); break; } @@ -547,7 +551,12 @@ pub fn write_theme(mut settings_content: String, new_val: &str) -> String { (None, None) => { // No document, create a new object and overwrite settings_content.clear(); - write!(settings_content, "{{\n \"theme\": \"{new_val}\"\n}}\n").unwrap(); + write!( + settings_content, + "{{\n \"{}\": \"{new_val}\"\n}}\n", + top_level_key + ) + .unwrap(); } (_, Some(existing_value_range)) => { @@ -572,7 +581,7 @@ pub fn write_theme(mut settings_content: String, new_val: &str) -> String { } } - let content = format!(r#""theme": "{new_val}","#); + let content = format!(r#""{top_level_key}": "{new_val}","#); settings_content.insert_str(first_key_start, &content); if row > 0 { @@ -603,7 +612,7 @@ pub fn parse_json_with_comments(content: &str) -> Result #[cfg(test)] mod tests { - use crate::write_theme; + use crate::write_top_level_setting; use unindent::Unindent; #[test] @@ -622,7 +631,7 @@ mod tests { "# .unindent(); - let settings_after_theme = write_theme(settings, "summerfruit-light"); + let settings_after_theme = write_top_level_setting(settings, "theme", "summerfruit-light"); assert_eq!(settings_after_theme, new_settings) } @@ -642,7 +651,7 @@ mod tests { "# .unindent(); - let settings_after_theme = write_theme(settings, "summerfruit-light"); + let settings_after_theme = write_top_level_setting(settings, "theme", "summerfruit-light"); assert_eq!(settings_after_theme, new_settings) } @@ -658,7 +667,7 @@ mod tests { "# .unindent(); - let settings_after_theme = write_theme(settings, "summerfruit-light"); + let settings_after_theme = write_top_level_setting(settings, "theme", "summerfruit-light"); assert_eq!(settings_after_theme, new_settings) } @@ -668,7 +677,7 @@ mod tests { let settings = r#"{ "a": "", "ok": true }"#.to_string(); let new_settings = r#"{ "theme": "summerfruit-light", "a": "", "ok": true }"#; - let settings_after_theme = write_theme(settings, "summerfruit-light"); + let settings_after_theme = write_top_level_setting(settings, "theme", "summerfruit-light"); assert_eq!(settings_after_theme, new_settings) } @@ -678,7 +687,7 @@ mod tests { let settings = r#" { "a": "", "ok": true }"#.to_string(); let new_settings = r#" { "theme": "summerfruit-light", "a": "", "ok": true }"#; - let settings_after_theme = write_theme(settings, "summerfruit-light"); + let settings_after_theme = write_top_level_setting(settings, "theme", "summerfruit-light"); assert_eq!(settings_after_theme, new_settings) } @@ -700,7 +709,7 @@ mod tests { "# .unindent(); - let settings_after_theme = write_theme(settings, "summerfruit-light"); + let settings_after_theme = write_top_level_setting(settings, "theme", "summerfruit-light"); assert_eq!(settings_after_theme, new_settings) } diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index f12f191041..6a7c96fd81 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -9,15 +9,54 @@ use std::{path::Path, sync::Arc, time::Duration}; use theme::ThemeRegistry; use util::ResultExt; -use crate::{parse_json_with_comments, KeymapFileContent, Settings, SettingsFileContent}; +use crate::{ + parse_json_with_comments, write_top_level_setting, KeymapFileContent, Settings, + SettingsFileContent, +}; + +// TODO: Switch SettingsFile to open a worktree and buffer for synchronization +// And instant updates in the Zed editor +#[derive(Clone)] +pub struct SettingsFile { + path: &'static Path, + fs: Arc, +} + +impl SettingsFile { + pub fn new(path: &'static Path, fs: Arc) -> Self { + SettingsFile { path, fs } + } + + pub async fn rewrite_settings_file(&self, f: F) -> anyhow::Result<()> + where + F: Fn(String) -> String, + { + let content = self.fs.load(self.path).await?; + + let new_settings = f(content); + + self.fs + .atomic_write(self.path.to_path_buf(), new_settings) + .await?; + + Ok(()) + } +} + +pub fn write_setting(key: &'static str, val: String, cx: &mut MutableAppContext) { + let settings_file = cx.global::().clone(); + cx.background() + .spawn(async move { + settings_file + .rewrite_settings_file(|settings| write_top_level_setting(settings, key, &val)) + .await + }) + .detach_and_log_err(cx); +} #[derive(Clone)] pub struct WatchedJsonFile(pub watch::Receiver); -// 1) Do the refactoring to pull WatchedJSON and fs out and into everything else -// 2) Scaffold this by making the basic structs we'll need SettingsFile::atomic_write_theme() -// 3) Fix the overeager settings writing, if that works, and there's no data loss, call it? - impl WatchedJsonFile where T: 'static + for<'de> Deserialize<'de> + Clone + Default + Send + Sync, diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index 1cd7b3f926..f3ca38b78b 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -153,6 +153,10 @@ impl PickerDelegate for ThemeSelector { fn confirm(&mut self, cx: &mut ViewContext) { self.selection_completed = true; + + let theme_name = cx.global::().theme.meta.name.clone(); + settings::settings_file::write_setting("theme", theme_name, cx); + cx.emit(Event::Dismissed); } diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 7e626c60a9..30607afdff 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -35,16 +35,23 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action(Dock::move_dock); cx.add_action( |workspace: &mut Workspace, _: &AnchorDockRight, cx: &mut ViewContext| { + settings::settings_file::write_setting("default_dock_anchor", "right".to_string(), cx); Dock::move_dock(workspace, &MoveDock(DockAnchor::Right), cx) }, ); cx.add_action( |workspace: &mut Workspace, _: &AnchorDockBottom, cx: &mut ViewContext| { + settings::settings_file::write_setting("default_dock_anchor", "bottom".to_string(), cx); Dock::move_dock(workspace, &MoveDock(DockAnchor::Bottom), cx) }, ); cx.add_action( |workspace: &mut Workspace, _: &ExpandDock, cx: &mut ViewContext| { + settings::settings_file::write_setting( + "default_dock_anchor", + "expanded".to_string(), + cx, + ); Dock::move_dock(workspace, &MoveDock(DockAnchor::Expanded), cx) }, ); diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 7dc30d34f5..1c6a818ef3 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -25,7 +25,10 @@ use log::LevelFilter; use parking_lot::Mutex; use project::{Fs, ProjectStore}; use serde_json::json; -use settings::{self, KeymapFileContent, Settings, SettingsFileContent, WorkingDirectory}; +use settings::{ + self, settings_file::SettingsFile, KeymapFileContent, Settings, SettingsFileContent, + WorkingDirectory, +}; use smol::process::Command; use std::fs::OpenOptions; use std::{env, ffi::OsStr, panic, path::PathBuf, sync::Arc, thread, time::Duration}; @@ -62,6 +65,7 @@ fn main() { let themes = ThemeRegistry::new(Assets, app.font_cache()); let default_settings = Settings::defaults(Assets, &app.font_cache(), &themes); + let settings_file = SettingsFile::new(&*zed::paths::SETTINGS, fs.clone()); let config_files = load_config_files(&app, fs.clone()); let login_shell_env_loaded = if stdout_is_a_pty() { @@ -94,10 +98,11 @@ fn main() { .spawn(languages::init(languages.clone(), cx.background().clone())); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http.clone(), cx)); - let (settings_file, keymap_file) = cx.background().block(config_files).unwrap(); + let (settings_file_content, keymap_file) = cx.background().block(config_files).unwrap(); //Setup settings global before binding actions - watch_settings_file(default_settings, settings_file, themes.clone(), cx); + cx.set_global(settings_file); + watch_settings_file(default_settings, settings_file_content, themes.clone(), cx); watch_keymap_file(keymap_file, cx); context_menu::init(cx); From b1f64d9550297f978984adf60d997a9c1f760920 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 11 Oct 2022 20:25:39 -0700 Subject: [PATCH 267/314] Updated new vim tests with new rope crate --- Cargo.lock | 251 ++++++++++++------ .../src/test/neovim_backed_test_context.rs | 2 +- crates/vim/src/test/neovim_connection.rs | 5 +- crates/workspace/Cargo.toml | 7 +- crates/workspace/src/workspace.rs | 7 +- 5 files changed, 190 insertions(+), 82 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 53f8aae9c5..fedb6258f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.1.0" dependencies = [ "auto_update", "editor", - "futures", + "futures 0.3.24", "gpui", "language", "project", @@ -183,6 +183,19 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-compat" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b48b4ff0c2026db683dea961cd8ea874737f56cffca86fa84415eaddc51c00d" +dependencies = [ + "futures-core", + "futures-io", + "once_cell", + "pin-project-lite 0.2.9", + "tokio", +] + [[package]] name = "async-compression" version = "0.3.14" @@ -265,7 +278,7 @@ name = "async-pipe" version = "0.1.3" source = "git+https://github.com/zed-industries/async-pipe-rs?rev=82d00a04211cf4e1236029aa03e6b6ce2a74c553#82d00a04211cf4e1236029aa03e6b6ce2a74c553" dependencies = [ - "futures", + "futures 0.3.24", "log", ] @@ -338,9 +351,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" +checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" dependencies = [ "proc-macro2", "quote", @@ -443,7 +456,7 @@ dependencies = [ "axum-core", "base64", "bitflags", - "bytes", + "bytes 1.2.1", "futures-util", "headers", "http", @@ -475,7 +488,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4d047478b986f14a13edad31a009e2e05cb241f9805d0d75e4cba4e129ad4d" dependencies = [ "async-trait", - "bytes", + "bytes 1.2.1", "futures-util", "http", "http-body", @@ -489,7 +502,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "277c75e6c814b061ae4947d02335d9659db9771b9950cca670002ae986372f44" dependencies = [ "axum", - "bytes", + "bytes 1.2.1", "futures-util", "http", "mime", @@ -661,6 +674,16 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +dependencies = [ + "byteorder", + "iovec", +] + [[package]] name = "bytes" version = "1.2.1" @@ -691,7 +714,7 @@ dependencies = [ "anyhow", "client", "collections", - "futures", + "futures 0.3.24", "gpui", "postage", "project", @@ -772,12 +795,12 @@ dependencies = [ "bindgen", "block", "byteorder", - "bytes", + "bytes 1.2.1", "cocoa", "core-foundation", "core-graphics", "foreign-types", - "futures", + "futures 0.3.24", "gpui", "hmac 0.12.1", "jwt", @@ -960,7 +983,7 @@ dependencies = [ "async-tungstenite", "collections", "db", - "futures", + "futures 0.3.24", "gpui", "image", "isahc", @@ -1046,7 +1069,7 @@ dependencies = [ "env_logger", "envy", "fs", - "futures", + "futures 0.3.24", "git", "gpui", "hyper", @@ -1094,7 +1117,7 @@ dependencies = [ "clock", "collections", "editor", - "futures", + "futures 0.3.24", "fuzzy", "gpui", "log", @@ -1692,7 +1715,7 @@ dependencies = [ "context_menu", "ctor", "env_logger", - "futures", + "futures 0.3.24", "fuzzy", "git", "gpui", @@ -1996,7 +2019,7 @@ dependencies = [ "async-trait", "collections", "fsevent", - "futures", + "futures 0.3.24", "git2", "gpui", "lazy_static", @@ -2067,9 +2090,15 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" -version = "0.3.21" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" + +[[package]] +name = "futures" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" dependencies = [ "futures-channel", "futures-core", @@ -2082,9 +2111,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" dependencies = [ "futures-core", "futures-sink", @@ -2092,15 +2121,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" +checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" [[package]] name = "futures-executor" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" dependencies = [ "futures-core", "futures-task", @@ -2120,9 +2149,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" +checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" [[package]] name = "futures-lite" @@ -2141,9 +2170,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" dependencies = [ "proc-macro2", "quote", @@ -2152,22 +2181,23 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" +checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" [[package]] name = "futures-task" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" +checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" [[package]] name = "futures-util" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" dependencies = [ + "futures 0.1.31", "futures-channel", "futures-core", "futures-io", @@ -2178,6 +2208,7 @@ dependencies = [ "pin-project-lite 0.2.9", "pin-utils", "slab", + "tokio-io", ] [[package]] @@ -2258,7 +2289,7 @@ dependencies = [ "async-trait", "clock", "collections", - "futures", + "futures 0.3.24", "git2", "lazy_static", "log", @@ -2338,9 +2369,10 @@ dependencies = [ "etagere", "font-kit", "foreign-types", - "futures", + "futures 0.3.24", "gpui_macros", "image", + "itertools", "lazy_static", "log", "media", @@ -2386,7 +2418,7 @@ version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" dependencies = [ - "bytes", + "bytes 1.2.1", "fnv", "futures-core", "futures-sink", @@ -2395,7 +2427,7 @@ dependencies = [ "indexmap", "slab", "tokio", - "tokio-util 0.7.3", + "tokio-util 0.7.4", "tracing", ] @@ -2434,7 +2466,7 @@ checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d" dependencies = [ "base64", "bitflags", - "bytes", + "bytes 1.2.1", "headers-core", "http", "httpdate", @@ -2527,7 +2559,7 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ - "bytes", + "bytes 1.2.1", "fnv", "itoa", ] @@ -2538,7 +2570,7 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ - "bytes", + "bytes 1.2.1", "http", "pin-project-lite 0.2.9", ] @@ -2573,7 +2605,7 @@ version = "0.14.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f" dependencies = [ - "bytes", + "bytes 1.2.1", "futures-channel", "futures-core", "futures-util", @@ -2609,7 +2641,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ - "bytes", + "bytes 1.2.1", "hyper", "native-tls", "tokio", @@ -2900,7 +2932,7 @@ dependencies = [ "ctor", "env_logger", "fs", - "futures", + "futures 0.3.24", "fuzzy", "git", "gpui", @@ -3062,7 +3094,7 @@ dependencies = [ "anyhow", "core-foundation", "core-graphics", - "futures", + "futures 0.3.24", "media", "parking_lot 0.11.2", "serde", @@ -3099,7 +3131,7 @@ dependencies = [ "collections", "ctor", "env_logger", - "futures", + "futures 0.3.24", "gpui", "log", "lsp-types", @@ -3192,7 +3224,7 @@ dependencies = [ "anyhow", "bindgen", "block", - "bytes", + "bytes 1.2.1", "core-foundation", "foreign-types", "metal", @@ -3552,6 +3584,21 @@ dependencies = [ "libc", ] +[[package]] +name = "nvim-rs" +version = "0.5.0" +source = "git+https://github.com/KillTheMule/nvim-rs?branch=master#d701c2790dcb2579f8f4d7003ba30e2100a7d25b" +dependencies = [ + "async-trait", + "futures 0.3.24", + "log", + "parity-tokio-ipc", + "rmp", + "rmpv", + "tokio", + "tokio-util 0.7.4", +] + [[package]] name = "objc" version = "0.2.7" @@ -3672,6 +3719,20 @@ dependencies = [ "workspace", ] +[[package]] +name = "parity-tokio-ipc" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9981e32fb75e004cc148f5fb70342f393830e0a4aa62e3cc93b50976218d42b6" +dependencies = [ + "futures 0.3.24", + "libc", + "log", + "rand 0.7.3", + "tokio", + "winapi 0.3.9", +] + [[package]] name = "parking" version = "2.0.0" @@ -3976,7 +4037,7 @@ checksum = "a63d25391d04a097954b76aba742b6b5b74f213dfe3dbaeeb36e8ddc1c657f0b" dependencies = [ "atomic", "crossbeam-queue", - "futures", + "futures 0.3.24", "log", "pin-project", "pollster", @@ -4047,7 +4108,7 @@ dependencies = [ "db", "fs", "fsevent", - "futures", + "futures 0.3.24", "fuzzy", "git", "gpui", @@ -4085,7 +4146,7 @@ version = "0.1.0" dependencies = [ "context_menu", "editor", - "futures", + "futures 0.3.24", "gpui", "menu", "postage", @@ -4104,7 +4165,7 @@ version = "0.1.0" dependencies = [ "anyhow", "editor", - "futures", + "futures 0.3.24", "fuzzy", "gpui", "language", @@ -4141,7 +4202,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020" dependencies = [ - "bytes", + "bytes 1.2.1", "prost-derive 0.8.0", ] @@ -4151,7 +4212,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" dependencies = [ - "bytes", + "bytes 1.2.1", "prost-derive 0.9.0", ] @@ -4161,7 +4222,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62941722fb675d463659e49c4f3fe1fe792ff24fe5bbaa9c08cd3b98a1c354f5" dependencies = [ - "bytes", + "bytes 1.2.1", "heck 0.3.3", "itertools", "lazy_static", @@ -4207,7 +4268,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a" dependencies = [ - "bytes", + "bytes 1.2.1", "prost 0.9.0", ] @@ -4470,7 +4531,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" dependencies = [ "base64", - "bytes", + "bytes 1.2.1", "encoding_rs", "futures-core", "futures-util", @@ -4540,6 +4601,27 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "rmp" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44519172358fd6d58656c86ab8e7fbc9e1490c3e8f14d35ed78ca0dd07403c9f" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmpv" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de8813b3a2f95c5138fe5925bfb8784175d88d6bff059ba8ce090aa891319754" +dependencies = [ + "num-traits", + "rmp", +] + [[package]] name = "rocksdb" version = "0.18.0" @@ -4584,7 +4666,7 @@ dependencies = [ "collections", "ctor", "env_logger", - "futures", + "futures 0.3.24", "gpui", "parking_lot 0.11.2", "prost 0.8.0", @@ -5062,7 +5144,7 @@ dependencies = [ "assets", "collections", "fs", - "futures", + "futures 0.3.24", "gpui", "json_comments", "postage", @@ -5345,7 +5427,7 @@ dependencies = [ "base64", "bitflags", "byteorder", - "bytes", + "bytes 1.2.1", "crc", "crossbeam-queue", "dirs 4.0.0", @@ -5598,7 +5680,7 @@ dependencies = [ "context_menu", "dirs 4.0.0", "editor", - "futures", + "futures 0.3.24", "gpui", "itertools", "lazy_static", @@ -5809,16 +5891,16 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.19.2" +version = "1.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" +checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" dependencies = [ - "bytes", + "autocfg 1.1.0", + "bytes 1.2.1", "libc", "memchr", "mio 0.8.4", "num_cpus", - "once_cell", "parking_lot 0.12.1", "pin-project-lite 0.2.9", "signal-hook-registry", @@ -5827,6 +5909,17 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "tokio-io" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.31", + "log", +] + [[package]] name = "tokio-io-timeout" version = "1.2.0" @@ -5898,7 +5991,7 @@ version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" dependencies = [ - "bytes", + "bytes 1.2.1", "futures-core", "futures-sink", "log", @@ -5908,12 +6001,13 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" dependencies = [ - "bytes", + "bytes 1.2.1", "futures-core", + "futures-io", "futures-sink", "pin-project-lite 0.2.9", "tokio", @@ -5938,7 +6032,7 @@ dependencies = [ "async-stream", "async-trait", "base64", - "bytes", + "bytes 1.2.1", "futures-core", "futures-util", "h2", @@ -5974,7 +6068,7 @@ dependencies = [ "rand 0.8.5", "slab", "tokio", - "tokio-util 0.7.3", + "tokio-util 0.7.4", "tower-layer", "tower-service", "tracing", @@ -5987,7 +6081,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" dependencies = [ "bitflags", - "bytes", + "bytes 1.2.1", "futures-core", "futures-util", "http", @@ -6266,7 +6360,7 @@ checksum = "6ad3713a14ae247f22a728a0456a545df14acf3867f905adff84be99e23b3ad1" dependencies = [ "base64", "byteorder", - "bytes", + "bytes 1.2.1", "http", "httparse", "log", @@ -6285,7 +6379,7 @@ checksum = "d96a2dea40e7570482f28eb57afbe42d97551905da6a9400acc5c328d24004f5" dependencies = [ "base64", "byteorder", - "bytes", + "bytes 1.2.1", "http", "httparse", "log", @@ -6461,7 +6555,7 @@ name = "util" version = "0.1.0" dependencies = [ "anyhow", - "futures", + "futures 0.3.24", "git2", "lazy_static", "log", @@ -6531,6 +6625,8 @@ name = "vim" version = "0.1.0" dependencies = [ "assets", + "async-compat", + "async-trait", "collections", "command_palette", "editor", @@ -6538,12 +6634,17 @@ dependencies = [ "indoc", "itertools", "language", + "lazy_static", "log", + "nvim-rs", + "parking_lot 0.11.2", "project", "rope", "search", "serde", + "serde_json", "settings", + "tokio", "util", "workspace", ] @@ -7230,7 +7331,7 @@ dependencies = [ "context_menu", "drag_and_drop", "fs", - "futures", + "futures 0.3.24", "gpui", "language", "log", @@ -7315,7 +7416,7 @@ dependencies = [ "file_finder", "fs", "fsevent", - "futures", + "futures 0.3.24", "fuzzy", "go_to_line", "gpui", diff --git a/crates/vim/src/test/neovim_backed_test_context.rs b/crates/vim/src/test/neovim_backed_test_context.rs index bb8ba26b74..bdac8fe158 100644 --- a/crates/vim/src/test/neovim_backed_test_context.rs +++ b/crates/vim/src/test/neovim_backed_test_context.rs @@ -51,7 +51,7 @@ impl<'a> NeovimBackedTestContext<'a> { pub async fn set_shared_state(&mut self, marked_text: &str) -> ContextHandle { let context_handle = self.set_state(marked_text, Mode::Normal); - let selection = self.editor(|editor, cx| editor.selections.newest::(cx)); + let selection = self.editor(|editor, cx| editor.selections.newest::(cx)); let text = self.buffer_text(); self.neovim.set_state(selection, &text).await; diff --git a/crates/vim/src/test/neovim_connection.rs b/crates/vim/src/test/neovim_connection.rs index ff4e10cfe5..60ac345323 100644 --- a/crates/vim/src/test/neovim_connection.rs +++ b/crates/vim/src/test/neovim_connection.rs @@ -8,7 +8,10 @@ use async_compat::Compat; use async_trait::async_trait; #[cfg(feature = "neovim")] use gpui::keymap::Keystroke; -use language::{Point, Selection}; + +use language::Selection; +use rope::point::Point; + #[cfg(feature = "neovim")] use lazy_static::lazy_static; #[cfg(feature = "neovim")] diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index c0cb2e9edd..54e7eaf463 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -12,7 +12,9 @@ test-support = [ "call/test-support", "client/test-support", "project/test-support", - "settings/test-support" + "settings/test-support", + "gpui/test-support", + "fs/test-support" ] [dependencies] @@ -43,4 +45,5 @@ call = { path = "../call", features = ["test-support"] } client = { path = "../client", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } -settings = { path = "../settings", features = ["test-support"] } \ No newline at end of file +settings = { path = "../settings", features = ["test-support"] } +fs = { path = "../fs", features = ["test-support"] } \ No newline at end of file diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index e11cd80f09..9a6bc9e6ac 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -17,6 +17,7 @@ use client::{proto, Client, PeerId, TypedEnvelope, UserStore}; use collections::{hash_map, HashMap, HashSet}; use dock::{DefaultItemFactory, Dock, ToggleDockButton}; use drag_and_drop::DragAndDrop; +use fs::{self, Fs}; use futures::{channel::oneshot, FutureExt, StreamExt}; use gpui::{ actions, @@ -32,7 +33,6 @@ use log::{error, warn}; pub use pane::*; pub use pane_group::*; use postage::prelude::Stream; -use fs::{self, Fs}; use project::{Project, ProjectEntryId, ProjectPath, ProjectStore, Worktree, WorktreeId}; use searchable::SearchableItemHandle; use serde::Deserialize; @@ -931,7 +931,7 @@ impl AppState { let settings = Settings::test(cx); cx.set_global(settings); - let fs = project::FakeFs::new(cx.background().clone()); + let fs = fs::FakeFs::new(cx.background().clone()); let languages = Arc::new(LanguageRegistry::test()); let http_client = client::test::FakeHttpClient::with_404_response(); let client = Client::new(http_client.clone(), cx); @@ -2806,8 +2806,9 @@ mod tests { use crate::sidebar::SidebarItem; use super::*; + use fs::FakeFs; use gpui::{executor::Deterministic, ModelHandle, TestAppContext, ViewContext}; - use project::{FakeFs, Project, ProjectEntryId}; + use project::{Project, ProjectEntryId}; use serde_json::json; pub fn default_item_factory( From 9adbab5d99f81025eda5469ed70cab0dd577eaee Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 12 Oct 2022 10:28:17 +0200 Subject: [PATCH 268/314] Fix opening a buffer after leaving and joining the same project This bug existed prior to #1700 and was caused by not clearing the buffers that were already shared with a peer that left and opened a project using the same connection. When such peer would re-join the project and open a buffer that it had opened previously, the host assumed the peer had already seen that buffer and wouldn't bother sending it again. --- crates/collab/src/integration_tests.rs | 52 +++++++++++++++++++++++--- crates/collab/src/rpc/store.rs | 2 +- crates/project/src/project.rs | 1 + 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index f6f0f5c7f2..0809a7c61e 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -2017,7 +2017,7 @@ async fn test_leaving_project( .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) .await .unwrap(); - let project_b = client_b.build_remote_project(project_id, cx_b).await; + let project_b1 = client_b.build_remote_project(project_id, cx_b).await; let project_c = client_c.build_remote_project(project_id, cx_c).await; // Client A sees that a guest has joined. @@ -2025,20 +2025,62 @@ async fn test_leaving_project( project_a.read_with(cx_a, |project, _| { assert_eq!(project.collaborators().len(), 2); }); - project_b.read_with(cx_b, |project, _| { + project_b1.read_with(cx_b, |project, _| { assert_eq!(project.collaborators().len(), 2); }); project_c.read_with(cx_c, |project, _| { assert_eq!(project.collaborators().len(), 2); }); - // Drop client B's connection and ensure client A and client C observe client B leaving the project. + // Client B opens a buffer. + let buffer_b1 = project_b1 + .update(cx_b, |project, cx| { + let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id(); + project.open_buffer((worktree_id, "a.txt"), cx) + }) + .await + .unwrap(); + buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents")); + + // Drop client B's project and ensure client A and client C observe client B leaving. + cx_b.update(|_| drop(project_b1)); + deterministic.run_until_parked(); + project_a.read_with(cx_a, |project, _| { + assert_eq!(project.collaborators().len(), 1); + }); + project_c.read_with(cx_c, |project, _| { + assert_eq!(project.collaborators().len(), 1); + }); + + // Client B re-joins the project and can open buffers as before. + let project_b2 = client_b.build_remote_project(project_id, cx_b).await; + deterministic.run_until_parked(); + project_a.read_with(cx_a, |project, _| { + assert_eq!(project.collaborators().len(), 2); + }); + project_b2.read_with(cx_b, |project, _| { + assert_eq!(project.collaborators().len(), 2); + }); + project_c.read_with(cx_c, |project, _| { + assert_eq!(project.collaborators().len(), 2); + }); + + let buffer_b2 = project_b2 + .update(cx_b, |project, cx| { + let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id(); + project.open_buffer((worktree_id, "a.txt"), cx) + }) + .await + .unwrap(); + buffer_b2.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents")); + + // Drop client B's connection and ensure client A and client C observe client B leaving. client_b.disconnect(&cx_b.to_async()).unwrap(); deterministic.run_until_parked(); project_a.read_with(cx_a, |project, _| { assert_eq!(project.collaborators().len(), 1); }); - project_b.read_with(cx_b, |project, _| { + project_b2.read_with(cx_b, |project, _| { assert!(project.is_read_only()); }); project_c.read_with(cx_c, |project, _| { @@ -2068,7 +2110,7 @@ async fn test_leaving_project( project_a.read_with(cx_a, |project, _| { assert_eq!(project.collaborators().len(), 0); }); - project_b.read_with(cx_b, |project, _| { + project_b2.read_with(cx_b, |project, _| { assert!(project.is_read_only()); }); project_c.read_with(cx_c, |project, _| { diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index cc34094782..b7dd39cff1 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -1205,7 +1205,7 @@ impl Store { let guest_connection = self.connections.get(guest_connection_id).unwrap(); assert!(guest_connection.projects.contains(project_id)); } - assert_eq!(project.active_replica_ids.len(), project.guests.len(),); + assert_eq!(project.active_replica_ids.len(), project.guests.len()); assert_eq!( project.active_replica_ids, project diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 11792bcf1e..6db9ce8aca 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4561,6 +4561,7 @@ impl Project { buffer.update(cx, |buffer, cx| buffer.remove_peer(replica_id, cx)); } } + this.shared_buffers.remove(&peer_id); cx.emit(Event::CollaboratorLeft(peer_id)); cx.notify(); From 83d3fad80dbdaa7fc344172e978ff2a472e0d3c3 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 12 Oct 2022 10:53:44 +0200 Subject: [PATCH 269/314] Clear auto-indent requests if they couldn't be computed --- crates/language/src/buffer.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index a9af41bb23..a5cf24877c 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -866,6 +866,8 @@ impl Buffer { })); } } + } else { + self.autoindent_requests.clear(); } } From cc56fa9ea63a4c46ce673acdc707a412f3f1a9b9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 12 Oct 2022 15:11:07 +0200 Subject: [PATCH 270/314] Introduce client-side timeout when trying to connect --- crates/client/src/client.rs | 131 +++++++++++++++++++++++++++++------- crates/client/src/test.rs | 10 +-- 2 files changed, 112 insertions(+), 29 deletions(-) diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index e8f6b80173..cb1b17107b 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -53,6 +53,8 @@ lazy_static! { } pub const ZED_SECRET_CLIENT_TOKEN: &str = "618033988749894"; +pub const INITIAL_RECONNECTION_DELAY: Duration = Duration::from_millis(100); +pub const CONNECTION_TIMEOUT: Duration = Duration::from_secs(5); actions!(client, [Authenticate]); @@ -330,7 +332,7 @@ impl Client { let reconnect_interval = state.reconnect_interval; state._reconnect_task = Some(cx.spawn(|cx| async move { let mut rng = StdRng::from_entropy(); - let mut delay = Duration::from_millis(100); + let mut delay = INITIAL_RECONNECTION_DELAY; while let Err(error) = this.authenticate_and_connect(true, &cx).await { log::error!("failed to connect {}", error); if matches!(*this.status().borrow(), Status::ConnectionError) { @@ -661,33 +663,42 @@ impl Client { self.set_status(Status::Reconnecting, cx); } - match self.establish_connection(&credentials, cx).await { - Ok(conn) => { - self.state.write().credentials = Some(credentials.clone()); - if !read_from_keychain && IMPERSONATE_LOGIN.is_none() { - write_credentials_to_keychain(&credentials, cx).log_err(); - } - self.set_connection(conn, cx).await; - Ok(()) - } - Err(EstablishConnectionError::Unauthorized) => { - self.state.write().credentials.take(); - if read_from_keychain { - cx.platform().delete_credentials(&ZED_SERVER_URL).log_err(); - self.set_status(Status::SignedOut, cx); - self.authenticate_and_connect(false, cx).await - } else { - self.set_status(Status::ConnectionError, cx); - Err(EstablishConnectionError::Unauthorized)? + let mut timeout = cx.background().timer(CONNECTION_TIMEOUT).fuse(); + futures::select_biased! { + connection = self.establish_connection(&credentials, cx).fuse() => { + match connection { + Ok(conn) => { + self.state.write().credentials = Some(credentials.clone()); + if !read_from_keychain && IMPERSONATE_LOGIN.is_none() { + write_credentials_to_keychain(&credentials, cx).log_err(); + } + self.set_connection(conn, cx).await; + Ok(()) + } + Err(EstablishConnectionError::Unauthorized) => { + self.state.write().credentials.take(); + if read_from_keychain { + cx.platform().delete_credentials(&ZED_SERVER_URL).log_err(); + self.set_status(Status::SignedOut, cx); + self.authenticate_and_connect(false, cx).await + } else { + self.set_status(Status::ConnectionError, cx); + Err(EstablishConnectionError::Unauthorized)? + } + } + Err(EstablishConnectionError::UpgradeRequired) => { + self.set_status(Status::UpgradeRequired, cx); + Err(EstablishConnectionError::UpgradeRequired)? + } + Err(error) => { + self.set_status(Status::ConnectionError, cx); + Err(error)? + } } } - Err(EstablishConnectionError::UpgradeRequired) => { - self.set_status(Status::UpgradeRequired, cx); - Err(EstablishConnectionError::UpgradeRequired)? - } - Err(error) => { + _ = timeout => { self.set_status(Status::ConnectionError, cx); - Err(error)? + Err(anyhow!("timed out trying to establish connection")) } } } @@ -1169,6 +1180,76 @@ mod tests { assert_eq!(server.auth_count(), 2); // Client re-authenticated due to an invalid token } + #[gpui::test(iterations = 10)] + async fn test_connection_timeout(deterministic: Arc, cx: &mut TestAppContext) { + deterministic.forbid_parking(); + + let user_id = 5; + let client = cx.update(|cx| Client::new(FakeHttpClient::with_404_response(), cx)); + let mut status = client.status(); + + // Time out when client tries to connect. + client.override_authenticate(move |cx| { + cx.foreground().spawn(async move { + Ok(Credentials { + user_id, + access_token: "token".into(), + }) + }) + }); + client.override_establish_connection(|_, cx| { + cx.foreground().spawn(async move { + future::pending::<()>().await; + unreachable!() + }) + }); + let auth_and_connect = cx.spawn({ + let client = client.clone(); + |cx| async move { client.authenticate_and_connect(false, &cx).await } + }); + deterministic.run_until_parked(); + assert!(matches!(status.next().await, Some(Status::Connecting))); + + deterministic.advance_clock(CONNECTION_TIMEOUT); + assert!(matches!( + status.next().await, + Some(Status::ConnectionError { .. }) + )); + auth_and_connect.await.unwrap_err(); + + // Allow the connection to be established. + let server = FakeServer::for_client(user_id, &client, cx).await; + assert!(matches!( + status.next().await, + Some(Status::Connected { .. }) + )); + + // Disconnect client. + server.forbid_connections(); + server.disconnect(); + while !matches!(status.next().await, Some(Status::ReconnectionError { .. })) {} + + // Time out when re-establishing the connection. + server.allow_connections(); + client.override_establish_connection(|_, cx| { + cx.foreground().spawn(async move { + future::pending::<()>().await; + unreachable!() + }) + }); + deterministic.advance_clock(2 * INITIAL_RECONNECTION_DELAY); + assert!(matches!( + status.next().await, + Some(Status::Reconnecting { .. }) + )); + + deterministic.advance_clock(CONNECTION_TIMEOUT); + assert!(matches!( + status.next().await, + Some(Status::ReconnectionError { .. }) + )); + } + #[gpui::test(iterations = 10)] async fn test_authenticating_more_than_once( cx: &mut TestAppContext, diff --git a/crates/client/src/test.rs b/crates/client/src/test.rs index 288c9a31fa..d7b7883b17 100644 --- a/crates/client/src/test.rs +++ b/crates/client/src/test.rs @@ -101,10 +101,12 @@ impl FakeServer { } pub fn disconnect(&self) { - self.peer.disconnect(self.connection_id()); - let mut state = self.state.lock(); - state.connection_id.take(); - state.incoming.take(); + if self.state.lock().connection_id.is_some() { + self.peer.disconnect(self.connection_id()); + let mut state = self.state.lock(); + state.connection_id.take(); + state.incoming.take(); + } } pub fn auth_count(&self) -> usize { From ec19f0f8e958ad80d85b017050ebd759e2b21a6a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 12 Oct 2022 15:13:41 +0200 Subject: [PATCH 271/314] Remove unnecessary async from `Peer::add_connection` --- crates/client/src/client.rs | 7 ++--- crates/client/src/test.rs | 2 +- crates/collab/src/rpc.rs | 3 +- crates/rpc/src/peer.rs | 55 +++++++++++++++---------------------- 4 files changed, 27 insertions(+), 40 deletions(-) diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index cb1b17107b..32db5e940d 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -672,7 +672,7 @@ impl Client { if !read_from_keychain && IMPERSONATE_LOGIN.is_none() { write_credentials_to_keychain(&credentials, cx).log_err(); } - self.set_connection(conn, cx).await; + self.set_connection(conn, cx); Ok(()) } Err(EstablishConnectionError::Unauthorized) => { @@ -703,13 +703,12 @@ impl Client { } } - async fn set_connection(self: &Arc, conn: Connection, cx: &AsyncAppContext) { + fn set_connection(self: &Arc, conn: Connection, cx: &AsyncAppContext) { let executor = cx.background(); log::info!("add connection to peer"); let (connection_id, handle_io, mut incoming) = self .peer - .add_connection(conn, move |duration| executor.timer(duration)) - .await; + .add_connection(conn, move |duration| executor.timer(duration)); log::info!("set status to connected {}", connection_id); self.set_status(Status::Connected { connection_id }, cx); cx.foreground() diff --git a/crates/client/src/test.rs b/crates/client/src/test.rs index d7b7883b17..ade21f02f4 100644 --- a/crates/client/src/test.rs +++ b/crates/client/src/test.rs @@ -82,7 +82,7 @@ impl FakeServer { let (client_conn, server_conn, _) = Connection::in_memory(cx.background()); let (connection_id, io, incoming) = - peer.add_test_connection(server_conn, cx.background()).await; + peer.add_test_connection(server_conn, cx.background()); cx.background().spawn(io).detach(); let mut state = state.lock(); state.connection_id = Some(connection_id); diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 84449e79d5..470692bf35 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -365,8 +365,7 @@ impl Server { timer.await; } } - }) - .await; + }); tracing::info!(%user_id, %login, %connection_id, %address, "connection opened"); diff --git a/crates/rpc/src/peer.rs b/crates/rpc/src/peer.rs index 5b1ed6c2af..4dbade4fec 100644 --- a/crates/rpc/src/peer.rs +++ b/crates/rpc/src/peer.rs @@ -113,7 +113,7 @@ impl Peer { } #[instrument(skip_all)] - pub async fn add_connection( + pub fn add_connection( self: &Arc, connection: Connection, create_timer: F, @@ -326,7 +326,7 @@ impl Peer { } #[cfg(any(test, feature = "test-support"))] - pub async fn add_test_connection( + pub fn add_test_connection( self: &Arc, connection: Connection, executor: Arc, @@ -337,7 +337,6 @@ impl Peer { ) { let executor = executor.clone(); self.add_connection(connection, move |duration| executor.timer(duration)) - .await } pub fn disconnect(&self, connection_id: ConnectionId) { @@ -522,21 +521,17 @@ mod tests { let (client1_to_server_conn, server_to_client_1_conn, _kill) = Connection::in_memory(cx.background()); - let (client1_conn_id, io_task1, client1_incoming) = client1 - .add_test_connection(client1_to_server_conn, cx.background()) - .await; - let (_, io_task2, server_incoming1) = server - .add_test_connection(server_to_client_1_conn, cx.background()) - .await; + let (client1_conn_id, io_task1, client1_incoming) = + client1.add_test_connection(client1_to_server_conn, cx.background()); + let (_, io_task2, server_incoming1) = + server.add_test_connection(server_to_client_1_conn, cx.background()); let (client2_to_server_conn, server_to_client_2_conn, _kill) = Connection::in_memory(cx.background()); - let (client2_conn_id, io_task3, client2_incoming) = client2 - .add_test_connection(client2_to_server_conn, cx.background()) - .await; - let (_, io_task4, server_incoming2) = server - .add_test_connection(server_to_client_2_conn, cx.background()) - .await; + let (client2_conn_id, io_task3, client2_incoming) = + client2.add_test_connection(client2_to_server_conn, cx.background()); + let (_, io_task4, server_incoming2) = + server.add_test_connection(server_to_client_2_conn, cx.background()); executor.spawn(io_task1).detach(); executor.spawn(io_task2).detach(); @@ -619,12 +614,10 @@ mod tests { let (client_to_server_conn, server_to_client_conn, _kill) = Connection::in_memory(cx.background()); - let (client_to_server_conn_id, io_task1, mut client_incoming) = client - .add_test_connection(client_to_server_conn, cx.background()) - .await; - let (server_to_client_conn_id, io_task2, mut server_incoming) = server - .add_test_connection(server_to_client_conn, cx.background()) - .await; + let (client_to_server_conn_id, io_task1, mut client_incoming) = + client.add_test_connection(client_to_server_conn, cx.background()); + let (server_to_client_conn_id, io_task2, mut server_incoming) = + server.add_test_connection(server_to_client_conn, cx.background()); executor.spawn(io_task1).detach(); executor.spawn(io_task2).detach(); @@ -719,12 +712,10 @@ mod tests { let (client_to_server_conn, server_to_client_conn, _kill) = Connection::in_memory(cx.background()); - let (client_to_server_conn_id, io_task1, mut client_incoming) = client - .add_test_connection(client_to_server_conn, cx.background()) - .await; - let (server_to_client_conn_id, io_task2, mut server_incoming) = server - .add_test_connection(server_to_client_conn, cx.background()) - .await; + let (client_to_server_conn_id, io_task1, mut client_incoming) = + client.add_test_connection(client_to_server_conn, cx.background()); + let (server_to_client_conn_id, io_task2, mut server_incoming) = + server.add_test_connection(server_to_client_conn, cx.background()); executor.spawn(io_task1).detach(); executor.spawn(io_task2).detach(); @@ -832,9 +823,8 @@ mod tests { let (client_conn, mut server_conn, _kill) = Connection::in_memory(cx.background()); let client = Peer::new(); - let (connection_id, io_handler, mut incoming) = client - .add_test_connection(client_conn, cx.background()) - .await; + let (connection_id, io_handler, mut incoming) = + client.add_test_connection(client_conn, cx.background()); let (io_ended_tx, io_ended_rx) = oneshot::channel(); executor @@ -868,9 +858,8 @@ mod tests { let (client_conn, mut server_conn, _kill) = Connection::in_memory(cx.background()); let client = Peer::new(); - let (connection_id, io_handler, mut incoming) = client - .add_test_connection(client_conn, cx.background()) - .await; + let (connection_id, io_handler, mut incoming) = + client.add_test_connection(client_conn, cx.background()); executor.spawn(io_handler).detach(); executor .spawn(async move { incoming.next().await }) From c4dde0f4e2211d466c955ca30558ec1b4b290352 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 12 Oct 2022 15:35:28 +0200 Subject: [PATCH 272/314] :lipstick: --- crates/client/src/client.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 32db5e940d..cc6bdf6279 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -663,7 +663,6 @@ impl Client { self.set_status(Status::Reconnecting, cx); } - let mut timeout = cx.background().timer(CONNECTION_TIMEOUT).fuse(); futures::select_biased! { connection = self.establish_connection(&credentials, cx).fuse() => { match connection { @@ -696,7 +695,7 @@ impl Client { } } } - _ = timeout => { + _ = cx.background().timer(CONNECTION_TIMEOUT).fuse() => { self.set_status(Status::ConnectionError, cx); Err(anyhow!("timed out trying to establish connection")) } From 1179f8f7be0d78cbc87d653460bf4095d99f832d Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 12 Oct 2022 09:19:29 -0700 Subject: [PATCH 273/314] Fix rounding error in computing editor's row range during layout Co-authored-by: Nathan Sobo Co-authored-by: Antonio Scandurra --- crates/editor/src/element.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 2842928914..f29eb52804 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1582,11 +1582,14 @@ impl Element for EditorElement { // The scroll position is a fractional point, the whole number of which represents // the top of the window in terms of display rows. let start_row = scroll_position.y() as u32; - let visible_row_count = (size.y() / line_height).ceil() as u32; + let height_in_lines = size.y() / line_height; let max_row = snapshot.max_point().row(); // Add 1 to ensure selections bleed off screen - let end_row = 1 + cmp::min(start_row + visible_row_count, max_row); + let end_row = 1 + cmp::min( + (scroll_position.y() + height_in_lines).ceil() as u32, + max_row, + ); let start_anchor = if start_row == 0 { Anchor::min() @@ -1680,8 +1683,7 @@ impl Element for EditorElement { .git_diff_hunks_in_range(start_row..end_row) .collect(); - let scrollbar_row_range = - scroll_position.y()..(scroll_position.y() + visible_row_count as f32); + let scrollbar_row_range = scroll_position.y()..(scroll_position.y() + height_in_lines); let mut max_visible_line_width = 0.0; let line_layouts = self.layout_lines(start_row..end_row, &snapshot, cx); From 47332f97c72b330d2b39d15832e5f0d7f11a127f Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 12 Oct 2022 09:28:55 -0700 Subject: [PATCH 274/314] Stops the dock anchor from being written to settings --- crates/workspace/src/dock.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 30607afdff..7e626c60a9 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -35,23 +35,16 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action(Dock::move_dock); cx.add_action( |workspace: &mut Workspace, _: &AnchorDockRight, cx: &mut ViewContext| { - settings::settings_file::write_setting("default_dock_anchor", "right".to_string(), cx); Dock::move_dock(workspace, &MoveDock(DockAnchor::Right), cx) }, ); cx.add_action( |workspace: &mut Workspace, _: &AnchorDockBottom, cx: &mut ViewContext| { - settings::settings_file::write_setting("default_dock_anchor", "bottom".to_string(), cx); Dock::move_dock(workspace, &MoveDock(DockAnchor::Bottom), cx) }, ); cx.add_action( |workspace: &mut Workspace, _: &ExpandDock, cx: &mut ViewContext| { - settings::settings_file::write_setting( - "default_dock_anchor", - "expanded".to_string(), - cx, - ); Dock::move_dock(workspace, &MoveDock(DockAnchor::Expanded), cx) }, ); From aac24938f5751e04e8359fd28321f73a46fa5221 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 12 Oct 2022 09:34:17 -0700 Subject: [PATCH 275/314] Fix a bug in how I parse alacritty's styles --- crates/terminal/src/terminal_element.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/crates/terminal/src/terminal_element.rs b/crates/terminal/src/terminal_element.rs index edf445a1d3..df745dae46 100644 --- a/crates/terminal/src/terminal_element.rs +++ b/crates/terminal/src/terminal_element.rs @@ -330,13 +330,10 @@ impl TerminalElement { } let mut properties = Properties::new(); - if indexed - .flags - .intersects(Flags::BOLD | Flags::BOLD_ITALIC | Flags::DIM_BOLD) - { + if indexed.flags.intersects(Flags::BOLD | Flags::DIM_BOLD) { properties = *properties.weight(Weight::BOLD); } - if indexed.flags.intersects(Flags::ITALIC | Flags::BOLD_ITALIC) { + if indexed.flags.intersects(Flags::ITALIC) { properties = *properties.style(Italic); } From 3c3671a193beb25ea468abf0a059c2605ed2b870 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 12 Oct 2022 11:33:19 -0700 Subject: [PATCH 276/314] Avoid sending stale diagnostics after sharing a worktree Co-authored-by: Antonio Scandurra --- crates/collab/src/integration_tests.rs | 34 +++++++++++++++++++------- crates/project/src/worktree.rs | 21 +++++++++------- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 0809a7c61e..65d17e2da3 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -2159,10 +2159,6 @@ async fn test_collaborating_with_diagnostics( ) .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; - let project_id = active_call_a - .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) - .await - .unwrap(); // Cause the language server to start. let _buffer = cx_a @@ -2179,14 +2175,31 @@ async fn test_collaborating_with_diagnostics( .await .unwrap(); - // Join the worktree as client B. - let project_b = client_b.build_remote_project(project_id, cx_b).await; - // Simulate a language server reporting errors for a file. let mut fake_language_server = fake_language_servers.next().await.unwrap(); fake_language_server .receive_notification::() .await; + fake_language_server.notify::( + lsp::PublishDiagnosticsParams { + uri: lsp::Url::from_file_path("/a/a.rs").unwrap(), + version: None, + diagnostics: vec![lsp::Diagnostic { + severity: Some(lsp::DiagnosticSeverity::WARNING), + range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)), + message: "message 0".to_string(), + ..Default::default() + }], + }, + ); + + // Client A shares the project and, simultaneously, the language server + // publishes a diagnostic. This is done to ensure that the server always + // observes the latest diagnostics for a worktree. + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) + .await + .unwrap(); fake_language_server.notify::( lsp::PublishDiagnosticsParams { uri: lsp::Url::from_file_path("/a/a.rs").unwrap(), @@ -2200,6 +2213,9 @@ async fn test_collaborating_with_diagnostics( }, ); + // Join the worktree as client B. + let project_b = client_b.build_remote_project(project_id, cx_b).await; + // Wait for server to see the diagnostics update. deterministic.run_until_parked(); { @@ -2321,7 +2337,7 @@ async fn test_collaborating_with_diagnostics( DiagnosticEntry { range: Point::new(0, 4)..Point::new(0, 7), diagnostic: Diagnostic { - group_id: 1, + group_id: 2, message: "message 1".to_string(), severity: lsp::DiagnosticSeverity::ERROR, is_primary: true, @@ -2331,7 +2347,7 @@ async fn test_collaborating_with_diagnostics( DiagnosticEntry { range: Point::new(0, 10)..Point::new(0, 13), diagnostic: Diagnostic { - group_id: 2, + group_id: 3, severity: lsp::DiagnosticSeverity::WARNING, message: "message 2".to_string(), is_primary: true, diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 3073392a52..0de647029d 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -959,9 +959,20 @@ impl LocalWorktree { let (snapshots_tx, mut snapshots_rx) = watch::channel_with(self.snapshot()); let rpc = self.client.clone(); let worktree_id = cx.model_id() as u64; + + for (path, summary) in self.diagnostic_summaries.iter() { + if let Err(e) = rpc.send(proto::UpdateDiagnosticSummary { + project_id, + worktree_id, + summary: Some(summary.to_proto(&path.0)), + }) { + return Task::ready(Err(e)); + } + } + let maintain_remote_snapshot = cx.background().spawn({ let rpc = rpc; - let diagnostic_summaries = self.diagnostic_summaries.clone(); + async move { let mut prev_snapshot = match snapshots_rx.recv().await { Some(snapshot) => { @@ -994,14 +1005,6 @@ impl LocalWorktree { } }; - for (path, summary) in diagnostic_summaries.iter() { - rpc.send(proto::UpdateDiagnosticSummary { - project_id, - worktree_id, - summary: Some(summary.to_proto(&path.0)), - })?; - } - while let Some(snapshot) = snapshots_rx.recv().await { send_worktree_update( &rpc, From 69dcfbb42367553c5bdeacca3f06084b937403a7 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 12 Oct 2022 13:49:09 -0700 Subject: [PATCH 277/314] Send guests DiskBasedDiagnosticsFinished messages when they join a project Co-authored-by: Antonio Scandurra --- crates/collab/src/integration_tests.rs | 50 +++++++++++++++----------- crates/collab/src/rpc.rs | 15 ++++++++ crates/project/src/project.rs | 14 ++++---- 3 files changed, 52 insertions(+), 27 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 65d17e2da3..bac5cc040e 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -2161,9 +2161,8 @@ async fn test_collaborating_with_diagnostics( let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; // Cause the language server to start. - let _buffer = cx_a - .background() - .spawn(project_a.update(cx_a, |project, cx| { + let _buffer = project_a + .update(cx_a, |project, cx| { project.open_buffer( ProjectPath { worktree_id, @@ -2171,7 +2170,7 @@ async fn test_collaborating_with_diagnostics( }, cx, ) - })) + }) .await .unwrap(); @@ -2245,24 +2244,35 @@ async fn test_collaborating_with_diagnostics( // Join project as client C and observe the diagnostics. let project_c = client_c.build_remote_project(project_id, cx_c).await; - deterministic.run_until_parked(); - project_c.read_with(cx_c, |project, cx| { - assert_eq!( - project.diagnostic_summaries(cx).collect::>(), - &[( - ProjectPath { - worktree_id, - path: Arc::from(Path::new("a.rs")), - }, - DiagnosticSummary { - error_count: 1, - warning_count: 0, - ..Default::default() - }, - )] - ) + let project_c_diagnostic_summaries = Rc::new(RefCell::new(Vec::new())); + project_c.update(cx_c, |_, cx| { + let summaries = project_c_diagnostic_summaries.clone(); + cx.subscribe(&project_c, { + move |p, _, event, cx| { + if let project::Event::DiskBasedDiagnosticsFinished { .. } = event { + *summaries.borrow_mut() = p.diagnostic_summaries(cx).collect(); + } + } + }) + .detach(); }); + deterministic.run_until_parked(); + assert_eq!( + project_c_diagnostic_summaries.borrow().as_slice(), + &[( + ProjectPath { + worktree_id, + path: Arc::from(Path::new("a.rs")), + }, + DiagnosticSummary { + error_count: 1, + warning_count: 0, + ..Default::default() + }, + )] + ); + // Simulate a language server reporting more errors for a file. fake_language_server.notify::( lsp::PublishDiagnosticsParams { diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 470692bf35..564e173fec 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -1012,6 +1012,21 @@ impl Server { } } + for language_server in &project.language_servers { + self.peer.send( + request.sender_id, + proto::UpdateLanguageServer { + project_id: project_id.to_proto(), + language_server_id: language_server.id, + variant: Some( + proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated( + proto::LspDiskBasedDiagnosticsUpdated {}, + ), + ), + }, + )?; + } + Ok(()) } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 6db9ce8aca..de4d76ebc3 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1084,13 +1084,6 @@ impl Project { } } - for worktree in self.worktrees(cx).collect::>() { - worktree.update(cx, |worktree, cx| { - let worktree = worktree.as_local_mut().unwrap(); - worktree_share_tasks.push(worktree.share(project_id, cx)); - }); - } - for (server_id, status) in &self.language_server_statuses { self.client .send(proto::StartLanguageServer { @@ -1103,6 +1096,13 @@ impl Project { .log_err(); } + for worktree in self.worktrees(cx).collect::>() { + worktree.update(cx, |worktree, cx| { + let worktree = worktree.as_local_mut().unwrap(); + worktree_share_tasks.push(worktree.share(project_id, cx)); + }); + } + self.client_subscriptions .push(self.client.add_model_for_remote_entity(project_id, cx)); self.metadata_changed(cx); From 89f05ada0b8bd3e00814feb311adddccae2a39b0 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 12 Oct 2022 14:14:03 -0700 Subject: [PATCH 278/314] Allow toggling collaboration menu from the keyboard --- assets/keymaps/default.json | 1 + crates/collab_ui/src/collab_titlebar_item.rs | 17 +++++++++-------- crates/collab_ui/src/collab_ui.rs | 2 +- crates/workspace/src/workspace.rs | 4 ++++ crates/zed/src/zed.rs | 18 +++++++++++++++++- 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index e2adfc0f81..841d9ab7c8 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -376,6 +376,7 @@ { "bindings": { "ctrl-alt-cmd-f": "workspace::FollowNextCollaborator", + "cmd-shift-c": "collab::ToggleCollaborationMenu", "cmd-alt-i": "zed::DebugElements" } }, diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 9faea76a10..928cf3273b 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -17,10 +17,7 @@ use std::ops::Range; use theme::Theme; use workspace::{FollowNextCollaborator, JoinProject, ToggleFollow, Workspace}; -actions!( - contacts_titlebar_item, - [ToggleContactsPopover, ShareProject] -); +actions!(collab, [ToggleCollaborationMenu, ShareProject]); pub fn init(cx: &mut MutableAppContext) { cx.add_action(CollabTitlebarItem::toggle_contacts_popover); @@ -143,7 +140,11 @@ impl CollabTitlebarItem { } } - fn toggle_contacts_popover(&mut self, _: &ToggleContactsPopover, cx: &mut ViewContext) { + pub fn toggle_contacts_popover( + &mut self, + _: &ToggleCollaborationMenu, + cx: &mut ViewContext, + ) { match self.contacts_popover.take() { Some(_) => {} None => { @@ -197,7 +198,7 @@ impl CollabTitlebarItem { }; Stack::new() .with_child( - MouseEventHandler::::new(0, cx, |state, _| { + MouseEventHandler::::new(0, cx, |state, _| { let style = titlebar .toggle_contacts_button .style_for(state, self.contacts_popover.is_some()); @@ -214,8 +215,8 @@ impl CollabTitlebarItem { .boxed() }) .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, |_, cx| { - cx.dispatch_action(ToggleContactsPopover); + .on_click(MouseButton::Left, move |_, cx| { + cx.dispatch_action(ToggleCollaborationMenu); }) .aligned() .boxed(), diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 4b7e3dae01..f5f508ce5b 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -8,7 +8,7 @@ mod notifications; mod project_shared_notification; use call::ActiveCall; -pub use collab_titlebar_item::CollabTitlebarItem; +pub use collab_titlebar_item::{CollabTitlebarItem, ToggleCollaborationMenu}; use gpui::MutableAppContext; use project::Project; use std::sync::Arc; diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 9a6bc9e6ac..be2b09baa9 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1173,6 +1173,10 @@ impl Workspace { cx.notify(); } + pub fn titlebar_item(&self) -> Option { + self.titlebar_item.clone() + } + /// Call the given callback with a workspace whose project is local. /// /// If the given workspace has a local project, then it will be passed diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 9012bc89e2..ef0c84909a 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -9,7 +9,7 @@ use anyhow::{anyhow, Context, Result}; use assets::Assets; use breadcrumbs::Breadcrumbs; pub use client; -use collab_ui::CollabTitlebarItem; +use collab_ui::{CollabTitlebarItem, ToggleCollaborationMenu}; use collections::VecDeque; pub use editor; use editor::{Editor, MultiBuffer}; @@ -94,6 +94,22 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { cx.toggle_full_screen(); }, ); + cx.add_action( + |workspace: &mut Workspace, + _: &ToggleCollaborationMenu, + cx: &mut ViewContext| { + if let Some(item) = workspace + .titlebar_item() + .and_then(|item| item.downcast::()) + { + cx.as_mut().defer(move |cx| { + item.update(cx, |item, cx| { + item.toggle_contacts_popover(&Default::default(), cx); + }); + }); + } + }, + ); cx.add_global_action(quit); cx.add_global_action(move |action: &OpenBrowser, cx| cx.platform().open_url(&action.url)); cx.add_global_action(move |_: &IncreaseBufferFontSize, cx| { From 490a60866371d5970e788d5d85ea1c5a914cc60b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 12 Oct 2022 14:18:40 -0700 Subject: [PATCH 279/314] v0.60.0 --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fedb6258f0..0b875441ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7386,7 +7386,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.59.0" +version = "0.60.0" dependencies = [ "activity_indicator", "anyhow", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 6c92dd9bef..5bfeb681a6 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] description = "The fast, collaborative code editor." edition = "2021" name = "zed" -version = "0.59.0" +version = "0.60.0" [lib] name = "zed" From fedec68d395fca6546c352099c614e7b74c6038e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 12 Oct 2022 14:25:40 -0700 Subject: [PATCH 280/314] Update Dockerfiles to use Rust 1.64 --- Dockerfile | 2 +- Dockerfile.migrator | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6bfd49be55..122600bf94 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax = docker/dockerfile:1.2 -FROM rust:1.62-bullseye as builder +FROM rust:1.64-bullseye as builder WORKDIR app COPY . . diff --git a/Dockerfile.migrator b/Dockerfile.migrator index b6393ed1b5..482228a2eb 100644 --- a/Dockerfile.migrator +++ b/Dockerfile.migrator @@ -1,6 +1,6 @@ # syntax = docker/dockerfile:1.2 -FROM rust:1.62-bullseye as builder +FROM rust:1.64-bullseye as builder WORKDIR app RUN --mount=type=cache,target=/usr/local/cargo/registry \ --mount=type=cache,target=./target \ From 1d8717f4de3ca85cea4977a2a1f482fcfbf932a4 Mon Sep 17 00:00:00 2001 From: K Simmons Date: Wed, 12 Oct 2022 15:10:00 -0700 Subject: [PATCH 281/314] Remove focus filtering from gpui so all focus events result in focus-in and focus-out calls Remove pane focused event in favor of focus_in at the workspace level Added is_child to ViewContext to determine if a given view is a child of the current view Fix issue where dock would get in a infinite loop when activated after dragging an item out of it Fix issue where the last focused view in an item was not correctly refocused when a pane is focused after switching active tabs Co-Authored-By: Mikayla Maki --- Cargo.lock | 247 ++++++++++++++++++++-------- crates/gpui/src/app.rs | 20 +-- crates/search/src/project_search.rs | 4 + crates/workspace/src/dock.rs | 10 +- crates/workspace/src/pane.rs | 77 ++++----- crates/workspace/src/workspace.rs | 22 ++- 6 files changed, 249 insertions(+), 131 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0448538c35..c5bbeac616 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.1.0" dependencies = [ "auto_update", "editor", - "futures", + "futures 0.3.24", "gpui", "language", "project", @@ -183,6 +183,19 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-compat" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b48b4ff0c2026db683dea961cd8ea874737f56cffca86fa84415eaddc51c00d" +dependencies = [ + "futures-core", + "futures-io", + "once_cell", + "pin-project-lite 0.2.9", + "tokio", +] + [[package]] name = "async-compression" version = "0.3.14" @@ -265,7 +278,7 @@ name = "async-pipe" version = "0.1.3" source = "git+https://github.com/zed-industries/async-pipe-rs?rev=82d00a04211cf4e1236029aa03e6b6ce2a74c553#82d00a04211cf4e1236029aa03e6b6ce2a74c553" dependencies = [ - "futures", + "futures 0.3.24", "log", ] @@ -338,9 +351,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" +checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" dependencies = [ "proc-macro2", "quote", @@ -443,7 +456,7 @@ dependencies = [ "axum-core", "base64", "bitflags", - "bytes", + "bytes 1.2.1", "futures-util", "headers", "http", @@ -475,7 +488,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4d047478b986f14a13edad31a009e2e05cb241f9805d0d75e4cba4e129ad4d" dependencies = [ "async-trait", - "bytes", + "bytes 1.2.1", "futures-util", "http", "http-body", @@ -489,7 +502,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "277c75e6c814b061ae4947d02335d9659db9771b9950cca670002ae986372f44" dependencies = [ "axum", - "bytes", + "bytes 1.2.1", "futures-util", "http", "mime", @@ -661,6 +674,16 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +dependencies = [ + "byteorder", + "iovec", +] + [[package]] name = "bytes" version = "1.2.1" @@ -691,7 +714,7 @@ dependencies = [ "anyhow", "client", "collections", - "futures", + "futures 0.3.24", "gpui", "postage", "project", @@ -772,12 +795,12 @@ dependencies = [ "bindgen", "block", "byteorder", - "bytes", + "bytes 1.2.1", "cocoa", "core-foundation", "core-graphics", "foreign-types", - "futures", + "futures 0.3.24", "gpui", "hmac 0.12.1", "jwt", @@ -960,7 +983,7 @@ dependencies = [ "async-tungstenite", "collections", "db", - "futures", + "futures 0.3.24", "gpui", "image", "isahc", @@ -1045,7 +1068,7 @@ dependencies = [ "editor", "env_logger", "envy", - "futures", + "futures 0.3.24", "git", "gpui", "hyper", @@ -1092,7 +1115,7 @@ dependencies = [ "clock", "collections", "editor", - "futures", + "futures 0.3.24", "fuzzy", "gpui", "log", @@ -1689,7 +1712,7 @@ dependencies = [ "context_menu", "ctor", "env_logger", - "futures", + "futures 0.3.24", "fuzzy", "git", "gpui", @@ -2038,9 +2061,15 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" -version = "0.3.21" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" + +[[package]] +name = "futures" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" dependencies = [ "futures-channel", "futures-core", @@ -2053,9 +2082,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" dependencies = [ "futures-core", "futures-sink", @@ -2063,15 +2092,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" +checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" [[package]] name = "futures-executor" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" dependencies = [ "futures-core", "futures-task", @@ -2091,9 +2120,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" +checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" [[package]] name = "futures-lite" @@ -2112,9 +2141,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" dependencies = [ "proc-macro2", "quote", @@ -2123,22 +2152,23 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" +checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" [[package]] name = "futures-task" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" +checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" [[package]] name = "futures-util" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" dependencies = [ + "futures 0.1.31", "futures-channel", "futures-core", "futures-io", @@ -2149,6 +2179,7 @@ dependencies = [ "pin-project-lite 0.2.9", "pin-utils", "slab", + "tokio-io", ] [[package]] @@ -2229,7 +2260,7 @@ dependencies = [ "async-trait", "clock", "collections", - "futures", + "futures 0.3.24", "git2", "lazy_static", "log", @@ -2307,9 +2338,10 @@ dependencies = [ "etagere", "font-kit", "foreign-types", - "futures", + "futures 0.3.24", "gpui_macros", "image", + "itertools", "lazy_static", "log", "media", @@ -2355,7 +2387,7 @@ version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" dependencies = [ - "bytes", + "bytes 1.2.1", "fnv", "futures-core", "futures-sink", @@ -2364,7 +2396,7 @@ dependencies = [ "indexmap", "slab", "tokio", - "tokio-util 0.7.3", + "tokio-util 0.7.4", "tracing", ] @@ -2403,7 +2435,7 @@ checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d" dependencies = [ "base64", "bitflags", - "bytes", + "bytes 1.2.1", "headers-core", "http", "httpdate", @@ -2496,7 +2528,7 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ - "bytes", + "bytes 1.2.1", "fnv", "itoa", ] @@ -2507,7 +2539,7 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ - "bytes", + "bytes 1.2.1", "http", "pin-project-lite 0.2.9", ] @@ -2542,7 +2574,7 @@ version = "0.14.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f" dependencies = [ - "bytes", + "bytes 1.2.1", "futures-channel", "futures-core", "futures-util", @@ -2578,7 +2610,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ - "bytes", + "bytes 1.2.1", "hyper", "native-tls", "tokio", @@ -2868,7 +2900,7 @@ dependencies = [ "collections", "ctor", "env_logger", - "futures", + "futures 0.3.24", "fuzzy", "git", "gpui", @@ -3029,7 +3061,7 @@ dependencies = [ "anyhow", "core-foundation", "core-graphics", - "futures", + "futures 0.3.24", "media", "parking_lot 0.11.2", "serde", @@ -3066,7 +3098,7 @@ dependencies = [ "collections", "ctor", "env_logger", - "futures", + "futures 0.3.24", "gpui", "log", "lsp-types", @@ -3159,7 +3191,7 @@ dependencies = [ "anyhow", "bindgen", "block", - "bytes", + "bytes 1.2.1", "core-foundation", "foreign-types", "metal", @@ -3519,6 +3551,21 @@ dependencies = [ "libc", ] +[[package]] +name = "nvim-rs" +version = "0.5.0" +source = "git+https://github.com/KillTheMule/nvim-rs?branch=master#d701c2790dcb2579f8f4d7003ba30e2100a7d25b" +dependencies = [ + "async-trait", + "futures 0.3.24", + "log", + "parity-tokio-ipc", + "rmp", + "rmpv", + "tokio", + "tokio-util 0.7.4", +] + [[package]] name = "objc" version = "0.2.7" @@ -3639,6 +3686,20 @@ dependencies = [ "workspace", ] +[[package]] +name = "parity-tokio-ipc" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9981e32fb75e004cc148f5fb70342f393830e0a4aa62e3cc93b50976218d42b6" +dependencies = [ + "futures 0.3.24", + "libc", + "log", + "rand 0.7.3", + "tokio", + "winapi 0.3.9", +] + [[package]] name = "parking" version = "2.0.0" @@ -3943,7 +4004,7 @@ checksum = "a63d25391d04a097954b76aba742b6b5b74f213dfe3dbaeeb36e8ddc1c657f0b" dependencies = [ "atomic", "crossbeam-queue", - "futures", + "futures 0.3.24", "log", "pin-project", "pollster", @@ -4013,7 +4074,7 @@ dependencies = [ "collections", "db", "fsevent", - "futures", + "futures 0.3.24", "fuzzy", "git", "gpui", @@ -4051,7 +4112,7 @@ version = "0.1.0" dependencies = [ "context_menu", "editor", - "futures", + "futures 0.3.24", "gpui", "menu", "postage", @@ -4070,7 +4131,7 @@ version = "0.1.0" dependencies = [ "anyhow", "editor", - "futures", + "futures 0.3.24", "fuzzy", "gpui", "language", @@ -4107,7 +4168,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020" dependencies = [ - "bytes", + "bytes 1.2.1", "prost-derive 0.8.0", ] @@ -4117,7 +4178,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" dependencies = [ - "bytes", + "bytes 1.2.1", "prost-derive 0.9.0", ] @@ -4127,7 +4188,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62941722fb675d463659e49c4f3fe1fe792ff24fe5bbaa9c08cd3b98a1c354f5" dependencies = [ - "bytes", + "bytes 1.2.1", "heck 0.3.3", "itertools", "lazy_static", @@ -4173,7 +4234,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a" dependencies = [ - "bytes", + "bytes 1.2.1", "prost 0.9.0", ] @@ -4436,7 +4497,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" dependencies = [ "base64", - "bytes", + "bytes 1.2.1", "encoding_rs", "futures-core", "futures-util", @@ -4506,6 +4567,27 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "rmp" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44519172358fd6d58656c86ab8e7fbc9e1490c3e8f14d35ed78ca0dd07403c9f" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmpv" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de8813b3a2f95c5138fe5925bfb8784175d88d6bff059ba8ce090aa891319754" +dependencies = [ + "num-traits", + "rmp", +] + [[package]] name = "rocksdb" version = "0.18.0" @@ -4536,7 +4618,7 @@ dependencies = [ "collections", "ctor", "env_logger", - "futures", + "futures 0.3.24", "gpui", "parking_lot 0.11.2", "prost 0.8.0", @@ -5290,7 +5372,7 @@ dependencies = [ "base64", "bitflags", "byteorder", - "bytes", + "bytes 1.2.1", "crc", "crossbeam-queue", "dirs 4.0.0", @@ -5543,7 +5625,7 @@ dependencies = [ "context_menu", "dirs 4.0.0", "editor", - "futures", + "futures 0.3.24", "gpui", "itertools", "lazy_static", @@ -5754,16 +5836,16 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.19.2" +version = "1.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" +checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" dependencies = [ - "bytes", + "autocfg 1.1.0", + "bytes 1.2.1", "libc", "memchr", "mio 0.8.4", "num_cpus", - "once_cell", "parking_lot 0.12.1", "pin-project-lite 0.2.9", "signal-hook-registry", @@ -5772,6 +5854,17 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "tokio-io" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.31", + "log", +] + [[package]] name = "tokio-io-timeout" version = "1.2.0" @@ -5843,7 +5936,7 @@ version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" dependencies = [ - "bytes", + "bytes 1.2.1", "futures-core", "futures-sink", "log", @@ -5853,12 +5946,13 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" dependencies = [ - "bytes", + "bytes 1.2.1", "futures-core", + "futures-io", "futures-sink", "pin-project-lite 0.2.9", "tokio", @@ -5883,7 +5977,7 @@ dependencies = [ "async-stream", "async-trait", "base64", - "bytes", + "bytes 1.2.1", "futures-core", "futures-util", "h2", @@ -5919,7 +6013,7 @@ dependencies = [ "rand 0.8.5", "slab", "tokio", - "tokio-util 0.7.3", + "tokio-util 0.7.4", "tower-layer", "tower-service", "tracing", @@ -5932,7 +6026,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" dependencies = [ "bitflags", - "bytes", + "bytes 1.2.1", "futures-core", "futures-util", "http", @@ -6211,7 +6305,7 @@ checksum = "6ad3713a14ae247f22a728a0456a545df14acf3867f905adff84be99e23b3ad1" dependencies = [ "base64", "byteorder", - "bytes", + "bytes 1.2.1", "http", "httparse", "log", @@ -6230,7 +6324,7 @@ checksum = "d96a2dea40e7570482f28eb57afbe42d97551905da6a9400acc5c328d24004f5" dependencies = [ "base64", "byteorder", - "bytes", + "bytes 1.2.1", "http", "httparse", "log", @@ -6406,7 +6500,7 @@ name = "util" version = "0.1.0" dependencies = [ "anyhow", - "futures", + "futures 0.3.24", "git2", "lazy_static", "log", @@ -6476,6 +6570,8 @@ name = "vim" version = "0.1.0" dependencies = [ "assets", + "async-compat", + "async-trait", "collections", "command_palette", "editor", @@ -6483,11 +6579,16 @@ dependencies = [ "indoc", "itertools", "language", + "lazy_static", "log", + "nvim-rs", + "parking_lot 0.11.2", "project", "search", "serde", + "serde_json", "settings", + "tokio", "util", "workspace", ] @@ -7173,7 +7274,7 @@ dependencies = [ "collections", "context_menu", "drag_and_drop", - "futures", + "futures 0.3.24", "gpui", "language", "log", @@ -7257,7 +7358,7 @@ dependencies = [ "env_logger", "file_finder", "fsevent", - "futures", + "futures 0.3.24", "fuzzy", "go_to_line", "gpui", diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index cd6b2dca4b..bdb83ddc55 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -618,7 +618,6 @@ pub struct MutableAppContext { HashMap>, Box)>, foreground: Rc, pending_effects: VecDeque, - pending_focus_index: Option, pending_notifications: HashSet, pending_global_notifications: HashSet, pending_flushes: usize, @@ -673,7 +672,6 @@ impl MutableAppContext { presenters_and_platform_windows: Default::default(), foreground, pending_effects: VecDeque::new(), - pending_focus_index: None, pending_notifications: Default::default(), pending_global_notifications: Default::default(), pending_flushes: 0, @@ -1876,9 +1874,6 @@ impl MutableAppContext { let mut refreshing = false; loop { if let Some(effect) = self.pending_effects.pop_front() { - if let Some(pending_focus_index) = self.pending_focus_index.as_mut() { - *pending_focus_index = pending_focus_index.saturating_sub(1); - } match effect { Effect::Subscription { entity_id, @@ -2259,8 +2254,6 @@ impl MutableAppContext { } fn handle_focus_effect(&mut self, window_id: usize, focused_id: Option) { - self.pending_focus_index.take(); - if self .cx .windows @@ -2383,10 +2376,6 @@ impl MutableAppContext { } pub fn focus(&mut self, window_id: usize, view_id: Option) { - if let Some(pending_focus_index) = self.pending_focus_index { - self.pending_effects.remove(pending_focus_index); - } - self.pending_focus_index = Some(self.pending_effects.len()); self.pending_effects .push_back(Effect::Focus { window_id, view_id }); } @@ -3465,6 +3454,15 @@ impl<'a, T: View> ViewContext<'a, T> { self.app.focused_view_id(self.window_id) == Some(self.view_id) } + pub fn is_child(&self, view: impl Into) -> bool { + let view = view.into(); + if self.window_id != view.window_id { + return false; + } + self.parents(view.window_id, view.view_id) + .any(|parent| parent == self.view_id) + } + pub fn blur(&mut self) { self.app.focus(self.window_id, None); } diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 4992d03737..d3c0743325 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -200,6 +200,10 @@ impl View for ProjectSearchView { .0 .insert(self.model.read(cx).project.downgrade(), handle) }); + + if cx.is_self_focused() { + self.focus_query_editor(cx); + } } } diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 7e626c60a9..dcfcc9e983 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -170,7 +170,11 @@ impl Dock { } else { cx.focus(pane); } - } else if let Some(last_active_center_pane) = workspace.last_active_center_pane.clone() { + } else if let Some(last_active_center_pane) = workspace + .last_active_center_pane + .as_ref() + .and_then(|pane| pane.upgrade(cx)) + { cx.focus(last_active_center_pane); } cx.emit(crate::Event::DockAnchorChanged); @@ -583,10 +587,11 @@ mod tests { } pub fn center_pane_handle(&self) -> ViewHandle { - self.workspace(|workspace, _| { + self.workspace(|workspace, cx| { workspace .last_active_center_pane .clone() + .and_then(|pane| pane.upgrade(cx)) .unwrap_or_else(|| workspace.center.panes()[0].clone()) }) } @@ -597,6 +602,7 @@ mod tests { let pane = workspace .last_active_center_pane .clone() + .and_then(|pane| pane.upgrade(cx)) .unwrap_or_else(|| workspace.center.panes()[0].clone()); Pane::add_item( workspace, diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 46ce9eb97f..7e2cc0d599 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -112,10 +112,10 @@ pub fn init(cx: &mut MutableAppContext) { pane.activate_item(pane.items.len() - 1, true, true, cx); }); cx.add_action(|pane: &mut Pane, _: &ActivatePrevItem, cx| { - pane.activate_prev_item(cx); + pane.activate_prev_item(true, cx); }); cx.add_action(|pane: &mut Pane, _: &ActivateNextItem, cx| { - pane.activate_next_item(cx); + pane.activate_next_item(true, cx); }); cx.add_async_action(Pane::close_active_item); cx.add_async_action(Pane::close_inactive_items); @@ -189,7 +189,6 @@ pub fn init(cx: &mut MutableAppContext) { #[derive(Debug)] pub enum Event { - Focused, ActivateItem { local: bool }, Remove, RemoveItem { item_id: usize }, @@ -201,7 +200,7 @@ pub struct Pane { items: Vec>, is_active: bool, active_item_index: usize, - last_focused_view: Option, + last_focused_view_by_item: HashMap, autoscroll: bool, nav_history: Rc>, toolbar: ViewHandle, @@ -263,7 +262,7 @@ impl Pane { items: Vec::new(), is_active: true, active_item_index: 0, - last_focused_view: None, + last_focused_view_by_item: Default::default(), autoscroll: false, nav_history: Rc::new(RefCell::new(NavHistory { mode: NavigationMode::Normal, @@ -632,32 +631,29 @@ impl Pane { if focus_item { self.focus_active_item(cx); } - if activate_pane { - cx.emit(Event::Focused); - } self.autoscroll = true; cx.notify(); } } - pub fn activate_prev_item(&mut self, cx: &mut ViewContext) { + pub fn activate_prev_item(&mut self, activate_pane: bool, cx: &mut ViewContext) { let mut index = self.active_item_index; if index > 0 { index -= 1; } else if !self.items.is_empty() { index = self.items.len() - 1; } - self.activate_item(index, true, true, cx); + self.activate_item(index, activate_pane, activate_pane, cx); } - pub fn activate_next_item(&mut self, cx: &mut ViewContext) { + pub fn activate_next_item(&mut self, activate_pane: bool, cx: &mut ViewContext) { let mut index = self.active_item_index; if index + 1 < self.items.len() { index += 1; } else { index = 0; } - self.activate_item(index, true, true, cx); + self.activate_item(index, activate_pane, activate_pane, cx); } pub fn close_active_item( @@ -784,7 +780,7 @@ impl Pane { // Remove the item from the pane. pane.update(&mut cx, |pane, cx| { if let Some(item_ix) = pane.items.iter().position(|i| i.id() == item.id()) { - pane.remove_item(item_ix, cx); + pane.remove_item(item_ix, false, cx); } }); } @@ -794,15 +790,15 @@ impl Pane { }) } - fn remove_item(&mut self, item_ix: usize, cx: &mut ViewContext) { + fn remove_item(&mut self, item_ix: usize, activate_pane: bool, cx: &mut ViewContext) { if item_ix == self.active_item_index { // Activate the previous item if possible. // This returns the user to the previously opened tab if they closed // a new item they just navigated to. if item_ix > 0 { - self.activate_prev_item(cx); + self.activate_prev_item(activate_pane, cx); } else if item_ix + 1 < self.items.len() { - self.activate_next_item(cx); + self.activate_next_item(activate_pane, cx); } } @@ -965,26 +961,27 @@ impl Pane { log::warn!("Tried to move item handle which was not in `from` pane. Maybe tab was closed during drop"); return; } - let (item_ix, item_handle) = item_to_move.unwrap(); + let item_handle = item_handle.clone(); + + if from != to { + // Close item from previous pane + from.update(cx, |from, cx| { + from.remove_item(item_ix, false, cx); + }); + } + // This automatically removes duplicate items in the pane Pane::add_item( workspace, &to, - item_handle.clone(), + item_handle, true, true, Some(destination_index), cx, ); - if from != to { - // Close item from previous pane - from.update(cx, |from, cx| { - from.remove_item(item_ix, cx); - }); - } - cx.focus(to); } @@ -1488,21 +1485,27 @@ impl View for Pane { } fn on_focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext) { - if cx.is_self_focused() { - if let Some(last_focused_view) = self - .last_focused_view - .as_ref() - .and_then(|handle| handle.upgrade(cx)) - .filter(|handle| handle.id() != self.tab_bar_context_menu.id()) - { - cx.focus(last_focused_view); + if let Some(active_item) = self.active_item() { + if cx.is_self_focused() { + // Pane was focused directly. We need to either focus a view inside the active item, + // or focus the active item itself + if let Some(weak_last_focused_view) = + self.last_focused_view_by_item.get(&active_item.id()) + { + if let Some(last_focused_view) = weak_last_focused_view.upgrade(cx) { + cx.focus(last_focused_view); + return; + } else { + self.last_focused_view_by_item.remove(&active_item.id()); + } + } + + cx.focus(active_item); } else { - self.focus_active_item(cx); + self.last_focused_view_by_item + .insert(active_item.id(), focused.downgrade()); } - } else { - self.last_focused_view = Some(focused.downgrade()); } - cx.emit(Event::Focused); } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 705823003f..b07eca465e 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1,5 +1,4 @@ -/// NOTE: Focus only 'takes' after an update has flushed_effects. Pane sends an event in on_focus_in -/// which the workspace uses to change the activated pane. +/// NOTE: Focus only 'takes' after an update has flushed_effects. /// /// This may cause issues when you're trying to write tests that use workspace focus to add items at /// specific locations. @@ -970,7 +969,7 @@ pub struct Workspace { panes: Vec>, panes_by_item: HashMap>, active_pane: ViewHandle, - last_active_center_pane: Option>, + last_active_center_pane: Option>, status_bar: ViewHandle, titlebar_item: Option, dock: Dock, @@ -1110,7 +1109,7 @@ impl Workspace { panes: vec![dock_pane, center_pane.clone()], panes_by_item: Default::default(), active_pane: center_pane.clone(), - last_active_center_pane: Some(center_pane.clone()), + last_active_center_pane: Some(center_pane.downgrade()), status_bar, titlebar_item: None, notifications: Default::default(), @@ -1845,7 +1844,7 @@ impl Workspace { if &pane == self.dock_pane() { Dock::show(self, cx); } else { - self.last_active_center_pane = Some(pane.clone()); + self.last_active_center_pane = Some(pane.downgrade()); if self.dock.is_anchored_at(DockAnchor::Expanded) { Dock::hide(self, cx); } @@ -1876,7 +1875,6 @@ impl Workspace { } pane::Event::Remove if !is_dock => self.remove_pane(pane, cx), pane::Event::Remove if is_dock => Dock::hide(self, cx), - pane::Event::Focused => self.handle_pane_focused(pane, cx), pane::Event::ActivateItem { local } => { if *local { self.unfollow(&pane, cx); @@ -1937,7 +1935,7 @@ impl Workspace { for removed_item in pane.read(cx).items() { self.panes_by_item.remove(&removed_item.id()); } - if self.last_active_center_pane == Some(pane) { + if self.last_active_center_pane == Some(pane.downgrade()) { self.last_active_center_pane = None; } @@ -2647,9 +2645,17 @@ impl View for Workspace { .named("workspace") } - fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { + fn on_focus_in(&mut self, view: AnyViewHandle, cx: &mut ViewContext) { if cx.is_self_focused() { cx.focus(&self.active_pane); + } else { + for pane in self.panes() { + let view = view.clone(); + if pane.update(cx, |_, cx| cx.is_child(view)) { + self.handle_pane_focused(pane.clone(), cx); + break; + } + } } } From 2ff6ffff5873950607ae3fdffda30717832de31b Mon Sep 17 00:00:00 2001 From: K Simmons Date: Wed, 12 Oct 2022 15:39:04 -0700 Subject: [PATCH 282/314] fix lock merge error --- Cargo.lock | 962 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 553 insertions(+), 409 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5e18671305..21cad05692 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,9 +52,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" dependencies = [ "memchr", ] @@ -113,6 +113,15 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec8ad6edb4840b78c5c3d88de606b22252d552b55f3a4699fbb10fc070ec3049" +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "ansi_term" version = "0.12.1" @@ -124,9 +133,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.58" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" [[package]] name = "arrayref" @@ -148,9 +157,9 @@ checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] name = "ascii" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf56136a5198c7b01a49e3afcbef6cf84597273d298f54432926024107b0109" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" [[package]] name = "assets" @@ -174,9 +183,9 @@ dependencies = [ [[package]] name = "async-channel" -version = "1.6.1" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" +checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" dependencies = [ "concurrent-queue", "event-listener", @@ -198,9 +207,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345fd392ab01f746c717b1357165b76f0b67a60192007b234058c9045fdcf695" +checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" dependencies = [ "flate2", "futures-core", @@ -225,21 +234,23 @@ dependencies = [ [[package]] name = "async-fs" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b3ca4f8ff117c37c278a2f7415ce9be55560b846b5bc4412aaa5d29c1c3dae2" +checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" dependencies = [ "async-lock", + "autocfg 1.1.0", "blocking", "futures-lite", ] [[package]] name = "async-io" -version = "1.7.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5e18f61464ae81cde0a23e713ae8fd299580c54d697a35820cfd0625b8b0e07" +checksum = "83e21f3a490c72b3b0cf44962180e60045de2925d8dff97918f7ee43c8f637c7" dependencies = [ + "autocfg 1.1.0", "concurrent-queue", "futures-lite", "libc", @@ -264,11 +275,12 @@ dependencies = [ [[package]] name = "async-net" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5373304df79b9b4395068fb080369ec7178608827306ce4d081cba51cac551df" +checksum = "4051e67316bc7eff608fe723df5d32ed639946adcd69e07df41fd42a7b411f1f" dependencies = [ "async-io", + "autocfg 1.1.0", "blocking", "futures-lite", ] @@ -284,11 +296,12 @@ dependencies = [ [[package]] name = "async-process" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2c06e30a24e8c78a3987d07f0930edf76ef35e027e7bdb063fccafdad1f60c" +checksum = "02111fd8655a613c25069ea89fc8d9bb89331fa77486eb3bc059ee757cfa481c" dependencies = [ "async-io", + "autocfg 1.1.0", "blocking", "cfg-if 1.0.0", "event-listener", @@ -448,9 +461,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.5.11" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2cc6e8e8c993cb61a005fab8c1e5093a29199b7253b05a6883999312935c1ff" +checksum = "c9e3356844c4d6a6d6467b8da2cffb4a2820be256f50a3a386c9d152bab31043" dependencies = [ "async-trait", "axum-core", @@ -483,9 +496,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4d047478b986f14a13edad31a009e2e05cb241f9805d0d75e4cba4e129ad4d" +checksum = "d9f0c0a60006f2a293d82d571f635042a72edf927539b7685bd62d361963839b" dependencies = [ "async-trait", "bytes 1.2.1", @@ -493,13 +506,15 @@ dependencies = [ "http", "http-body", "mime", + "tower-layer", + "tower-service", ] [[package]] name = "axum-extra" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "277c75e6c814b061ae4947d02335d9659db9771b9950cca670002ae986372f44" +checksum = "69034b3b0fd97923eee2ce8a47540edb21e07f48f87f67d44bb4271cec622bdb" dependencies = [ "axum", "bytes 1.2.1", @@ -518,16 +533,16 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.65" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" dependencies = [ "addr2line", "cc", "cfg-if 1.0.0", "libc", - "miniz_oxide 0.5.3", - "object", + "miniz_oxide 0.5.4", + "object 0.29.0", "rustc-demangle", ] @@ -539,9 +554,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "base64ct" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdca834647821e0b13d9539a8634eb62d3501b6b6c2cec1722786ee6671b851" +checksum = "ea2b2456fd614d856680dcd9fcc660a51a820fa09daef2e49772b56a193c8474" [[package]] name = "bincode" @@ -598,9 +613,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ "generic-array", ] @@ -658,15 +673,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.10.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" +checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" [[package]] name = "bytemuck" -version = "1.10.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c53dfa917ec274df8ed3c572698f381a24eef2efba9492d797301b72b6db408a" +checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da" [[package]] name = "byteorder" @@ -811,7 +826,7 @@ dependencies = [ "parking_lot 0.11.2", "postage", "serde", - "sha2 0.10.2", + "sha2 0.10.6", "simplelog", ] @@ -862,21 +877,23 @@ dependencies = [ "postage", "settings", "theme", - "time 0.3.11", + "time 0.3.15", "util", "workspace", ] [[package]] name = "chrono" -version = "0.4.19" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" dependencies = [ - "libc", + "iana-time-zone", + "js-sys", "num-integer", "num-traits", "time 0.1.44", + "wasm-bindgen", "winapi 0.3.9", ] @@ -897,9 +914,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.3.3" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a050e2153c5be08febd6734e29298e844fdb0fa21aeddd63b4eb7baa106c69b" +checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" dependencies = [ "glob", "libc", @@ -923,9 +940,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.8" +version = "3.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83" +checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" dependencies = [ "atty", "bitflags", @@ -935,14 +952,14 @@ dependencies = [ "once_cell", "strsim 0.10.0", "termcolor", - "textwrap 0.15.0", + "textwrap 0.15.1", ] [[package]] name = "clap_derive" -version = "3.2.7" +version = "3.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" dependencies = [ "heck 0.4.0", "proc-macro-error", @@ -965,7 +982,7 @@ name = "cli" version = "0.1.0" dependencies = [ "anyhow", - "clap 3.2.8", + "clap 3.2.22", "core-foundation", "core-services", "dirs 3.0.2", @@ -998,11 +1015,11 @@ dependencies = [ "sum_tree", "tempfile", "thiserror", - "time 0.3.11", + "time 0.3.15", "tiny_http", "url", "util", - "uuid 1.1.2", + "uuid 1.2.1", ] [[package]] @@ -1050,6 +1067,16 @@ dependencies = [ "objc", ] +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "collab" version = "0.1.0" @@ -1061,7 +1088,7 @@ dependencies = [ "axum-extra", "base64", "call", - "clap 3.2.8", + "clap 3.2.22", "client", "collections", "ctor", @@ -1093,7 +1120,7 @@ dependencies = [ "sha-1 0.9.8", "sqlx", "theme", - "time 0.3.11", + "time 0.3.15", "tokio", "tokio-tungstenite", "toml", @@ -1166,9 +1193,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "1.2.2" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" dependencies = [ "cache-padded", ] @@ -1253,27 +1280,27 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" dependencies = [ "libc", ] [[package]] name = "cranelift-bforest" -version = "0.85.1" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7901fbba05decc537080b07cb3f1cadf53be7b7602ca8255786288a8692ae29a" +checksum = "749d0d6022c9038dccf480bdde2a38d435937335bf2bb0f14e815d94517cdce8" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.85.1" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ba1b45d243a4a28e12d26cd5f2507da74e77c45927d40de8b6ffbf088b46b5" +checksum = "e94370cc7b37bf652ccd8bb8f09bd900997f7ccf97520edfc75554bb5c4abbea" dependencies = [ "cranelift-bforest", "cranelift-codegen-meta", @@ -1289,33 +1316,33 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.85.1" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54cc30032171bf230ce22b99c07c3a1de1221cb5375bd6dbe6dbe77d0eed743c" +checksum = "e0a3cea8fdab90e44018c5b9a1dfd460d8ee265ac354337150222a354628bdb6" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.85.1" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23f2672426d2bb4c9c3ef53e023076cfc4d8922f0eeaebaf372c92fae8b5c69" +checksum = "5ac72f76f2698598951ab26d8c96eaa854810e693e7dd52523958b5909fde6b2" [[package]] name = "cranelift-entity" -version = "0.85.1" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "886c59a5e0de1f06dbb7da80db149c75de10d5e2caca07cdd9fef8a5918a6336" +checksum = "09eaeacfcd2356fe0e66b295e8f9d59fdd1ac3ace53ba50de14d628ec902f72d" dependencies = [ "serde", ] [[package]] name = "cranelift-frontend" -version = "0.85.1" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace74eeca11c439a9d4ed1a5cb9df31a54cd0f7fbddf82c8ce4ea8e9ad2a8fe0" +checksum = "dba69c9980d5ffd62c18a2bde927855fcd7c8dc92f29feaf8636052662cbd99c" dependencies = [ "cranelift-codegen", "log", @@ -1325,15 +1352,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.85.1" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db1ae52a5cc2cad0d86fdd3dcb16b7217d2f1e65ab4f5814aa4f014ad335fa43" +checksum = "d2920dc1e05cac40304456ed3301fde2c09bd6a9b0210bcfa2f101398d628d5b" [[package]] name = "cranelift-native" -version = "0.85.1" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dadcfb7852900780d37102bce5698bcd401736403f07b52e714ff7a180e0e22f" +checksum = "f04dfa45f9b2a6f587c564d6b63388e00cd6589d2df6ea2758cf79e1a13285e6" dependencies = [ "cranelift-codegen", "libc", @@ -1342,9 +1369,9 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.85.1" +version = "0.85.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c84e3410960389110b88f97776f39f6d2c8becdaa4cd59e390e6b76d9d0e7190" +checksum = "31a46513ae6f26f3f267d8d75b5373d555fbbd1e68681f348d99df43f747ec54" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1392,47 +1419,46 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.10", + "crossbeam-utils 0.8.12", ] [[package]] name = "crossbeam-deque" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" dependencies = [ "cfg-if 1.0.0", "crossbeam-epoch", - "crossbeam-utils 0.8.10", + "crossbeam-utils 0.8.12", ] [[package]] name = "crossbeam-epoch" -version = "0.9.9" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d" +checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" dependencies = [ "autocfg 1.1.0", "cfg-if 1.0.0", - "crossbeam-utils 0.8.10", + "crossbeam-utils 0.8.12", "memoffset", - "once_cell", "scopeguard", ] [[package]] name = "crossbeam-queue" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2" +checksum = "1cd42583b04998a5363558e5f9291ee5a5ff6b49944332103f251e7479a82aa7" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.10", + "crossbeam-utils 0.8.12", ] [[package]] @@ -1448,19 +1474,18 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.10" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83" +checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" dependencies = [ "cfg-if 1.0.0", - "once_cell", ] [[package]] name = "crypto-common" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5999502d32b9c48d492abe66392408144895020ec4709e549e840799f3bb74c0" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", @@ -1478,9 +1503,9 @@ dependencies = [ [[package]] name = "ctor" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c" +checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" dependencies = [ "quote", "syn", @@ -1488,9 +1513,9 @@ dependencies = [ [[package]] name = "curl" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d855aeef205b43f65a5001e0997d81f8efca7badad4fad7d897aa7f0d0651f" +checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22" dependencies = [ "curl-sys", "libc", @@ -1503,9 +1528,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.55+curl-7.83.1" +version = "0.4.56+curl-7.83.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23734ec77368ec583c2e61dd3f0b0e5c98b93abe6d2a004ca06b91dd7e3e2762" +checksum = "6093e169dd4de29e468fa649fbae11cdcd5551c81fe5bf1b0677adad7ef3d26f" dependencies = [ "cc", "libc", @@ -1517,6 +1542,50 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "cxx" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f39818dcfc97d45b03953c1292efc4e80954e1583c4aa770bac1383e2310a4" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e580d70777c116df50c390d1211993f62d40302881e54d4b79727acb83d0199" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56a46460b88d1cec95112c8c363f0e2c39afdb237f60583b0b36343bf627ea9c" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747b608fecf06b0d72d440f27acc99288207324b793be2c17991839f3d4995ea" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "data-url" version = "0.1.1" @@ -1551,13 +1620,13 @@ dependencies = [ [[package]] name = "dhat" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47003dc9f6368a88e85956c3b2573a7e6872746a3e5d762a8885da3a136a0381" +checksum = "0684eaa19a59be283a6f99369917b679bd4d1d06604b2eb2e2f87b4bbd67668d" dependencies = [ "backtrace", "lazy_static", - "parking_lot 0.11.2", + "parking_lot 0.12.1", "rustc-hash", "serde", "serde_json", @@ -1597,11 +1666,11 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" dependencies = [ - "block-buffer 0.10.2", + "block-buffer 0.10.3", "crypto-common", "subtle", ] @@ -1667,10 +1736,13 @@ dependencies = [ ] [[package]] -name = "dotenv" -version = "0.15.0" +name = "dotenvy" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" +checksum = "ed9155c8f4dc55c7470ae9da3f63c6785245093b3f6aeb0f5bf2e968efbba314" +dependencies = [ + "dirs 4.0.0", +] [[package]] name = "drag_and_drop" @@ -1694,9 +1766,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "140206b78fb2bc3edbcfc9b5ccbd0b30699cfe8d348b8b31b330e47df5291a5a" +checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2" [[package]] name = "easy-parallel" @@ -1751,9 +1823,9 @@ dependencies = [ [[package]] name = "either" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "encoding_rs" @@ -1766,9 +1838,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272" dependencies = [ "atty", "humantime", @@ -1788,9 +1860,9 @@ dependencies = [ [[package]] name = "erased-serde" -version = "0.3.21" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81d013529d5574a60caeda29e179e695125448e5de52e3874f7b4c1d7360e18e" +checksum = "54558e0ba96fbe24280072642eceb9d7d442e32c7ec0ea9e7ecd7b4ea2cf4e11" dependencies = [ "serde", ] @@ -1837,9 +1909,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "expat-sys" @@ -1859,9 +1931,9 @@ checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" [[package]] name = "fastrand" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" dependencies = [ "instant", ] @@ -1909,7 +1981,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" dependencies = [ "crc32fast", - "miniz_oxide 0.5.3", + "miniz_oxide 0.5.4", ] [[package]] @@ -1982,11 +2054,10 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" dependencies = [ - "matches", "percent-encoding", ] @@ -2094,13 +2165,6 @@ version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" -[[package]] -name = "futures" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" - [[package]] name = "futures" version = "0.3.24" @@ -2237,9 +2301,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.5" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", "version_check", @@ -2279,9 +2343,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" dependencies = [ "fallible-iterator", "indexmap", @@ -2402,7 +2466,7 @@ dependencies = [ "smallvec", "smol", "sum_tree", - "time 0.3.11", + "time 0.3.15", "tiny-skia", "tree-sitter", "usvg", @@ -2421,9 +2485,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" dependencies = [ "bytes 1.2.1", "fnv", @@ -2449,27 +2513,27 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ "ahash", ] [[package]] name = "hashlink" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d452c155cb93fecdfb02a73dd57b5d8e442c2063bd7aac72f1bc5e4263a43086" +checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa" dependencies = [ - "hashbrown 0.12.1", + "hashbrown 0.12.3", ] [[package]] name = "headers" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d" +checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" dependencies = [ "base64", "bitflags", @@ -2478,7 +2542,7 @@ dependencies = [ "http", "httpdate", "mime", - "sha-1 0.10.0", + "sha1", ] [[package]] @@ -2519,9 +2583,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.3" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d37fb7dc756218a0559bfc21e4381f03cbb696cdaf959e7e95e927496f0564cd" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" dependencies = [ "libc", ] @@ -2557,7 +2621,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.3", + "digest 0.10.5", ] [[package]] @@ -2590,9 +2654,9 @@ checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" [[package]] name = "httparse" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" @@ -2608,9 +2672,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.19" +version = "0.14.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" dependencies = [ "bytes 1.2.1", "futures-channel", @@ -2656,12 +2720,35 @@ dependencies = [ ] [[package]] -name = "idna" -version = "0.2.3" +name = "iana-time-zone" +version = "0.1.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "f5a6ef98976b22b3b7f2f3a806f858cb862044cfa66805aa3ad84cb3d3b785ed" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi 0.3.9", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fde6edd6cef363e9359ed3c98ba64590ba9eecba2293eb5a723ab32aee8926aa" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] @@ -2672,7 +2759,7 @@ version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" dependencies = [ - "crossbeam-utils 0.8.10", + "crossbeam-utils 0.8.12", "globset", "lazy_static", "log", @@ -2710,15 +2797,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg 1.1.0", - "hashbrown 0.12.1", + "hashbrown 0.12.3", "serde", ] [[package]] name = "indoc" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05a0bd019339e5d968b37855180087b7b9d512c5046fbd244cf8c95687927d6e" +checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3" [[package]] name = "instant" @@ -2789,7 +2876,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c89a757e762896bdbdfadf2860d0f8b0cea5e363d8cf3e7bdfeb63d1d976352" dependencies = [ - "hermit-abi 0.2.3", + "hermit-abi 0.2.6", "io-lifetimes", "rustix", "winapi 0.3.9", @@ -2803,7 +2890,7 @@ checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9" dependencies = [ "async-channel", "castaway", - "crossbeam-utils 0.8.10", + "crossbeam-utils 0.8.12", "curl", "curl-sys", "encoding_rs", @@ -2824,18 +2911,18 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" [[package]] name = "ittapi-rs" @@ -2848,9 +2935,9 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" dependencies = [ "libc", ] @@ -2879,9 +2966,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.58" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" dependencies = [ "wasm-bindgen", ] @@ -2900,11 +2987,11 @@ checksum = "6204285f77fe7d9784db3fdc449ecce1a0114927a51d5a41c4c7a292011c015f" dependencies = [ "base64", "crypto-common", - "digest 0.10.3", + "digest 0.10.5", "hmac 0.12.1", "serde", "serde_json", - "sha2 0.10.2", + "sha2 0.10.6", ] [[package]] @@ -2995,9 +3082,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" [[package]] name = "libgit2-sys" @@ -3023,9 +3110,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db" +checksum = "292a948cd991e376cf75541fe5b97a1081d713c618b4f1b9500f8844e49eb565" [[package]] name = "libnghttp2-sys" @@ -3072,6 +3159,15 @@ dependencies = [ "safemem", ] +[[package]] +name = "link-cplusplus" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +dependencies = [ + "cc", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -3110,9 +3206,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ "autocfg 1.1.0", "scopeguard", @@ -3217,11 +3313,11 @@ checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "md-5" -version = "0.10.1" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" dependencies = [ - "digest 0.10.3", + "digest 0.10.5", ] [[package]] @@ -3325,9 +3421,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" dependencies = [ "adler", ] @@ -3501,6 +3597,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi 0.3.9", +] + [[package]] name = "num-bigint" version = "0.4.3" @@ -3638,10 +3744,19 @@ dependencies = [ ] [[package]] -name = "once_cell" -version = "1.13.0" +name = "object" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" [[package]] name = "opaque-debug" @@ -3651,9 +3766,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.40" +version = "0.10.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb81a6430ac911acb25fe5ac8f1d2af1b4ea8a4fdfda0f1ee4292af2e2d8eb0e" +checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -3683,9 +3798,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.74" +version = "0.9.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835363342df5fba8354c5b453325b110ffd54044e588c539cf2f20a8014e4cb1" +checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" dependencies = [ "autocfg 1.1.0", "cc", @@ -3705,9 +3820,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.1.0" +version = "6.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" [[package]] name = "outline" @@ -3726,6 +3841,12 @@ dependencies = [ "workspace", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parity-tokio-ipc" version = "0.9.0" @@ -3801,15 +3922,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77e0b28ace46c5a396546bcf443bf422b57049617433d8854227352a4a9b24e7" dependencies = [ "base64ct", - "rand_core 0.6.3", + "rand_core 0.6.4", "subtle", ] [[package]] name = "paste" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" +checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" [[package]] name = "pathfinder_color" @@ -3867,16 +3988,17 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.1.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +checksum = "dbc7bc69c062e492337d74d59b120c274fd3d261b6bf6d3207d499b4b379c41a" dependencies = [ + "thiserror", "ucd-trie", ] @@ -3914,18 +4036,18 @@ checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" [[package]] name = "pin-project" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78203e83c48cffbe01e4a2d35d566ca4de445d79a85372fc64e378bfc812a260" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "710faf75e1b33345361201d36d04e98ac1ed8909151a017ed384700836104c74" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ "proc-macro2", "quote", @@ -3966,7 +4088,7 @@ dependencies = [ "indexmap", "line-wrap", "serde", - "time 0.3.11", + "time 0.3.15", "xml-rs", ] @@ -4019,10 +4141,11 @@ dependencies = [ [[package]] name = "polling" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" +checksum = "899b00b9c8ab553c743b3e11e87c5c7d423b2a2de229ba95b24a756344748011" dependencies = [ + "autocfg 1.1.0", "cfg-if 1.0.0", "libc", "log", @@ -4084,9 +4207,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.40" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" dependencies = [ "unicode-ident", ] @@ -4135,7 +4258,7 @@ dependencies = [ "serde", "serde_json", "settings", - "sha2 0.10.2", + "sha2 0.10.6", "similar", "smol", "sum_tree", @@ -4190,9 +4313,9 @@ dependencies = [ [[package]] name = "prometheus" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cface98dfa6d645ea4c789839f176e4b072265d085bfcc48eaa8d137f58d3c39" +checksum = "45c8babc29389186697fe5a2a4859d697825496b83db5d0b65271cdc0488e88c" dependencies = [ "cfg-if 1.0.0", "fnv", @@ -4281,24 +4404,24 @@ dependencies = [ [[package]] name = "protobuf" -version = "2.27.1" +version = "2.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf7e6d18738ecd0902d30d1ad232c9125985a3422929b16c65517b38adc14f96" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" [[package]] name = "psm" -version = "0.1.19" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd89aa18fbf9533a581355a22438101fe9c2ed8c9e2f0dcf520552a3afddf2" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" dependencies = [ "cc", ] [[package]] name = "pulldown-cmark" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34f197a544b0c9ab3ae46c359a7ec9cbbb5c7bf97054266fecb7ead794a181d6" +checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" dependencies = [ "bitflags", "memchr", @@ -4307,9 +4430,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] @@ -4348,7 +4471,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha 0.3.1", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -4368,7 +4491,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -4397,9 +4520,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom 0.2.7", ] @@ -4431,9 +4554,9 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" dependencies = [ - "crossbeam-channel 0.5.5", + "crossbeam-channel 0.5.6", "crossbeam-deque", - "crossbeam-utils 0.8.10", + "crossbeam-utils 0.8.12", "num_cpus", ] @@ -4454,9 +4577,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] @@ -4533,9 +4656,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.11" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" +checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" dependencies = [ "base64", "bytes 1.2.1", @@ -4549,10 +4672,10 @@ dependencies = [ "hyper-tls", "ipnet", "js-sys", - "lazy_static", "log", "mime", "native-tls", + "once_cell", "percent-encoding", "pin-project-lite 0.2.9", "serde", @@ -4586,9 +4709,9 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3b221de559e4a29df3b957eec92bc0de6bc8eaf6ca9cfed43e5e1d67ff65a34" +checksum = "3603b7d71ca82644f79b5a06d1220e9a58ede60bd32255f698cb1af8838b8db3" dependencies = [ "bytemuck", ] @@ -4711,9 +4834,9 @@ dependencies = [ [[package]] name = "rust-embed" -version = "6.4.0" +version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a17e5ac65b318f397182ae94e532da0ba56b88dd1200b774715d36c4943b1c3" +checksum = "e26934cd67a1da1165efe61cba4047cc1b4a526019da609fcce13a1000afb5fa" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -4722,9 +4845,9 @@ dependencies = [ [[package]] name = "rust-embed-impl" -version = "6.2.0" +version = "6.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e763e24ba2bf0c72bc6be883f967f794a019fafd1b86ba1daff9c91a7edd30" +checksum = "e35d7b402e273544cc08e0824aa3404333fab8a90ac43589d3d5b72f4b346e12" dependencies = [ "proc-macro2", "quote", @@ -4735,12 +4858,12 @@ dependencies = [ [[package]] name = "rust-embed-utils" -version = "7.2.0" +version = "7.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756feca3afcbb1487a1d01f4ecd94cf8ec98ea074c55a69e7136d29fb6166029" +checksum = "c1669d81dfabd1b5f8e2856b8bbe146c6192b0ba22162edc738ac0a5de18f054" dependencies = [ "globset", - "sha2 0.9.9", + "sha2 0.10.6", "walkdir", ] @@ -4808,9 +4931,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7522c9de787ff061458fe9a829dc790a3f5b22dc571694fc5883f448b94d9a9" +checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" dependencies = [ "base64", ] @@ -4833,9 +4956,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] name = "safe_arch" @@ -4882,9 +5005,9 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1847b767a3d62d95cbf3d8a9f0e421cf57a0d8aa4f411d4b16525afb0284d4ed" +checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307" dependencies = [ "dyn-clone", "schemars_derive", @@ -4894,9 +5017,9 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4d7e1b012cb3d9129567661a63755ea4b8a7386d339dc945ae187e403c6743" +checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9" dependencies = [ "proc-macro2", "quote", @@ -4916,6 +5039,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scratch" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" + [[package]] name = "scrypt" version = "0.7.0" @@ -4981,9 +5110,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.6.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" +checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" dependencies = [ "bitflags", "core-foundation", @@ -5028,18 +5157,18 @@ checksum = "5a9f47faea3cad316faa914d013d24f471cd90bfca1a0c70f05a3f42c6441e99" [[package]] name = "serde" -version = "1.0.138" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.138" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" dependencies = [ "proc-macro2", "quote", @@ -5068,9 +5197,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074" dependencies = [ "indexmap", "itoa", @@ -5080,18 +5209,18 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7868ad3b8196a8a0aea99a8220b124278ee5320a55e4fde97794b6f85b1a377" +checksum = "184c643044780f7ceb59104cef98a5a6f12cb2288a7bc701ab93a362b49fd47d" dependencies = [ "serde", ] [[package]] name = "serde_repr" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ad84e47328a31223de7fed7a4f5087f2d6ddfe586cf3ca25b7a165bc0a5aed" +checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca" dependencies = [ "proc-macro2", "quote", @@ -5112,9 +5241,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.8.24" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707d15895415db6628332b737c838b88c598522e4dc70647e59b72312924aebc" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" dependencies = [ "indexmap", "ryu", @@ -5189,7 +5318,18 @@ checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.3", + "digest 0.10.5", +] + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.5", ] [[package]] @@ -5207,13 +5347,13 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.2" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.3", + "digest 0.10.5", ] [[package]] @@ -5227,11 +5367,11 @@ dependencies = [ [[package]] name = "shellexpand" -version = "2.1.0" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83bdb7831b2d85ddf4a7b148aa19d0587eddbe8671a436b7bd1182eaad0f2829" +checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4" dependencies = [ - "dirs-next", + "dirs 4.0.0", ] [[package]] @@ -5317,9 +5457,12 @@ checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" [[package]] name = "slab" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg 1.1.0", +] [[package]] name = "slice-group-by" @@ -5340,9 +5483,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "smol" @@ -5382,9 +5525,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.4" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" dependencies = [ "libc", "winapi 0.3.9", @@ -5404,9 +5547,9 @@ checksum = "be6c3f39c37a4283ee4b43d1311c828f2e1fb0541e76ea0cb1a2abd9ef2f5b3b" [[package]] name = "sqlformat" -version = "0.1.8" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b7922be017ee70900be125523f38bdd644f4f06a1b16e8fa5a8ee8c34bffd4" +checksum = "f87e292b4291f154971a43c3774364e2cbcaec599d3f5bf6fa9d122885dbc38a" dependencies = [ "itertools", "nom", @@ -5415,9 +5558,9 @@ dependencies = [ [[package]] name = "sqlx" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f82cbe94f41641d6c410ded25bbf5097c240cefdf8e3b06d04198d0a96af6a4" +checksum = "9249290c05928352f71c077cc44a464d880c63f26f7534728cca008e135c0428" dependencies = [ "sqlx-core", "sqlx-macros", @@ -5425,9 +5568,9 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b69bf218860335ddda60d6ce85ee39f6cf6e5630e300e19757d1de15886a093" +checksum = "dcbc16ddba161afc99e14d1713a453747a2b07fc097d2009f4c300ec99286105" dependencies = [ "ahash", "atoi", @@ -5438,6 +5581,7 @@ dependencies = [ "crc", "crossbeam-queue", "dirs 4.0.0", + "dotenvy", "either", "event-listener", "futures-channel", @@ -5462,34 +5606,34 @@ dependencies = [ "rustls-pemfile", "serde", "serde_json", - "sha-1 0.10.0", - "sha2 0.10.2", + "sha1", + "sha2 0.10.6", "smallvec", "sqlformat", "sqlx-rt", "stringprep", "thiserror", - "time 0.3.11", + "time 0.3.15", "tokio-stream", "url", - "uuid 1.1.2", - "webpki-roots 0.22.3", + "uuid 1.2.1", + "webpki-roots 0.22.5", "whoami", ] [[package]] name = "sqlx-macros" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40c63177cf23d356b159b60acd27c54af7423f1736988502e36bae9a712118f" +checksum = "b850fa514dc11f2ee85be9d055c512aa866746adfacd1cb42d867d68e6a5b0d9" dependencies = [ - "dotenv", + "dotenvy", "either", "heck 0.4.0", "once_cell", "proc-macro2", "quote", - "sha2 0.10.2", + "sha2 0.10.6", "sqlx-core", "sqlx-rt", "syn", @@ -5498,9 +5642,9 @@ dependencies = [ [[package]] name = "sqlx-rt" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "874e93a365a598dc3dadb197565952cb143ae4aa716f7bcc933a8d836f6bf89f" +checksum = "24c5b2d25fa654cc5f841750b8e1cdedbe21189bf9a9382ee90bfa9dd3562396" dependencies = [ "once_cell", "tokio", @@ -5595,9 +5739,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.98" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" dependencies = [ "proc-macro2", "quote", @@ -5743,9 +5887,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" [[package]] name = "theme" @@ -5780,18 +5924,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.31" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ "proc-macro2", "quote", @@ -5837,9 +5981,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.11" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217" +checksum = "d634a985c4d4238ec39cacaed2e7ae552fbd3c476b552c1deac3021b7d7eaf0c" dependencies = [ "itoa", "libc", @@ -5971,9 +6115,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9" +checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" dependencies = [ "futures-core", "pin-project-lite 0.2.9", @@ -5982,14 +6126,14 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.17.1" +version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06cda1232a49558c46f8a504d5b93101d42c0bf7f911f12a105ba48168f821ae" +checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" dependencies = [ "futures-util", "log", "tokio", - "tungstenite 0.17.2", + "tungstenite 0.17.3", ] [[package]] @@ -6102,9 +6246,9 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" [[package]] name = "tower-service" @@ -6114,9 +6258,9 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.35" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if 1.0.0", "log", @@ -6127,9 +6271,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", @@ -6138,9 +6282,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", "valuable", @@ -6179,12 +6323,12 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.14" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a713421342a5a666b7577783721d3117f1b69a393df803ee17bb73b1e122a59" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" dependencies = [ - "ansi_term", "matchers", + "nu-ansi-term", "once_cell", "regex", "serde", @@ -6209,9 +6353,9 @@ dependencies = [ [[package]] name = "tree-sitter-c" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bdc5574c6cbc39c409246caeb1dd4d3c4bd6d30d4e9b399776086c20365fd24" +checksum = "cca211f4827d4b4dc79f388bf67b6fa3bc8a8cfa642161ef24f99f371ba34c7b" dependencies = [ "cc", "tree-sitter", @@ -6380,9 +6524,9 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.17.2" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96a2dea40e7570482f28eb57afbe42d97551905da6a9400acc5c328d24004f5" +checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" dependencies = [ "base64", "byteorder", @@ -6405,9 +6549,9 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "ucd-trie" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89570599c4fe5585de2b388aab47e99f7fa4e9238a1399f707a02e356058141c" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "unicase" @@ -6444,30 +6588,30 @@ checksum = "7f9af028e052a610d99e066b33304625dea9613170a2563314490a4e6ec5cf7f" [[package]] name = "unicode-ident" -version = "1.0.1" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[package]] name = "unicode-normalization" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "unicode-script" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58dd944fd05f2f0b5c674917aea8a4df6af84f2d8de3fe8d988b95d28fb8fb09" +checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc" [[package]] name = "unicode-segmentation" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" [[package]] name = "unicode-vo" @@ -6477,15 +6621,15 @@ checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" [[package]] name = "unicode-width" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "unicode-xid" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "unicode_categories" @@ -6495,9 +6639,9 @@ checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" [[package]] name = "unindent" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52fee519a3e570f7df377a06a1a7775cdbfb7aa460be7e08de2b1f0e69973a44" +checksum = "58ee9362deb4a96cef4d437d1ad49cffc9b9e92d202b6995674e928ce684f112" [[package]] name = "untrusted" @@ -6507,13 +6651,12 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.2.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", "idna", - "matches", "percent-encoding", "serde", ] @@ -6582,9 +6725,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.1.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" +checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" dependencies = [ "getrandom 0.2.7", ] @@ -6723,9 +6866,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi-cap-std-sync" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1c4e73ed64b92ae87b416f4274b3c827180b02b67f835f66a86fc4267b77349" +checksum = "f086c5026d2fc3b268d138e65373f46422cc810f46d6e0776859c5027cb18728" dependencies = [ "anyhow", "async-trait", @@ -6747,9 +6890,9 @@ dependencies = [ [[package]] name = "wasi-common" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc983eb93607a61f64152ec8728bf453f4dfdf22e7ab1784faac3297fe9a035e" +checksum = "4e8844fede1c3787cc08853872f47e8bd91f6c939c7406bc7a5dba496b260c08" dependencies = [ "anyhow", "bitflags", @@ -6765,9 +6908,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -6775,13 +6918,13 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", "syn", @@ -6790,9 +6933,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.31" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f" +checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -6802,9 +6945,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6812,9 +6955,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", @@ -6825,15 +6968,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "wasm-encoder" -version = "0.14.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f76068e87fe9b837a6bc2ccded66784173eadb828c4168643e9fddf6f9ed2e61" +checksum = "c64ac98d5d61192cc45c701b7e4bd0b9aff91e2edfc7a088406cfe2288581e2c" dependencies = [ "leb128", ] @@ -6849,9 +6992,9 @@ dependencies = [ [[package]] name = "wasmtime" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e76e2b2833bb0ece666ccdbed7b71b617d447da11f1bb61f4f2bab2648f745ee" +checksum = "1f50eadf868ab6a04b7b511460233377d0bfbb92e417b2f6a98b98fef2e098f5" dependencies = [ "anyhow", "async-trait", @@ -6862,7 +7005,7 @@ dependencies = [ "lazy_static", "libc", "log", - "object", + "object 0.28.4", "once_cell", "paste", "psm", @@ -6883,9 +7026,9 @@ dependencies = [ [[package]] name = "wasmtime-cache" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743a9f142d93318262d7e1fe329394ff2e8f86a1df45ae5e4f0eedba215ca5ce" +checksum = "d1df23c642e1376892f3b72f311596976979cbf8b85469680cdd3a8a063d12a2" dependencies = [ "anyhow", "base64", @@ -6903,9 +7046,9 @@ dependencies = [ [[package]] name = "wasmtime-cranelift" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dc0f80afa1ce97083a7168e6b6948d015d6237369e9f4a511d38c9c4ac8fbb9" +checksum = "f264ff6b4df247d15584f2f53d009fbc90032cfdc2605b52b961bffc71b6eccd" dependencies = [ "anyhow", "cranelift-codegen", @@ -6916,7 +7059,7 @@ dependencies = [ "gimli", "log", "more-asserts", - "object", + "object 0.28.4", "target-lexicon", "thiserror", "wasmparser", @@ -6925,9 +7068,9 @@ dependencies = [ [[package]] name = "wasmtime-environ" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0816d9365196f1f447060087e0f87239ccded830bd54970a1168b0c9c8e824c9" +checksum = "839d2820e4b830f4b9e7aa08d4c0acabf4a5036105d639f6dfa1c6891c73bdc6" dependencies = [ "anyhow", "cranelift-entity", @@ -6935,7 +7078,7 @@ dependencies = [ "indexmap", "log", "more-asserts", - "object", + "object 0.28.4", "serde", "target-lexicon", "thiserror", @@ -6945,9 +7088,9 @@ dependencies = [ [[package]] name = "wasmtime-fiber" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715afdb87a3bcf1eae3f098c742d650fb783abdb8a7ca87076ea1cabecabea5d" +checksum = "3248be3c4911233535356025f6562193614a40155ee9094bb6a2b43f0dc82803" dependencies = [ "cc", "rustix", @@ -6956,9 +7099,9 @@ dependencies = [ [[package]] name = "wasmtime-jit" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c687f33cfa0f89ec1646929d0ff102087052cf9f0d15533de56526b0da0d1b3" +checksum = "ef0a0bcbfa18b946d890078ba0e1bc76bcc53eccfb40806c0020ec29dcd1bd49" dependencies = [ "addr2line", "anyhow", @@ -6968,7 +7111,7 @@ dependencies = [ "gimli", "ittapi-rs", "log", - "object", + "object 0.28.4", "region", "rustc-demangle", "rustix", @@ -6983,20 +7126,20 @@ dependencies = [ [[package]] name = "wasmtime-jit-debug" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b252d1d025f94f3954ba2111f12f3a22826a0764a11c150c2d46623115a69e27" +checksum = "4f4779d976206c458edd643d1ac622b6c37e4a0800a8b1d25dfbf245ac2f2cac" dependencies = [ "lazy_static", - "object", + "object 0.28.4", "rustix", ] [[package]] name = "wasmtime-runtime" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace251693103c9facbbd7df87a29a75e68016e48bc83c09133f2fda6b575e0ab" +checksum = "b7eb6ffa169eb5dcd18ac9473c817358cd57bc62c244622210566d473397954a" dependencies = [ "anyhow", "backtrace", @@ -7021,9 +7164,9 @@ dependencies = [ [[package]] name = "wasmtime-types" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d129b0487a95986692af8708ffde9c50b0568dcefd79200941d475713b4f40bb" +checksum = "8d932b0ac5336f7308d869703dd225610a6a3aeaa8e968c52b43eed96cefb1c2" dependencies = [ "cranelift-entity", "serde", @@ -7033,9 +7176,9 @@ dependencies = [ [[package]] name = "wasmtime-wasi" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb49791530b3a3375897a6d5a8bfa9914101ef8a672d01c951e70b46fd953c15" +checksum = "b68b7d77fb6f2975a6fe6cc4d0015d6b0cebb65c39fce1dd4cc00880dbf7789c" dependencies = [ "anyhow", "wasi-cap-std-sync", @@ -7055,9 +7198,9 @@ dependencies = [ [[package]] name = "wast" -version = "43.0.0" +version = "47.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "408feaebf6dbf9d154957873b14d00e8fba4cbc17a8cbb1bc9e4c1db425c50a8" +checksum = "02b98502f3978adea49551e801a6687678e6015317d7d9470a67fe813393f2a8" dependencies = [ "leb128", "memchr", @@ -7067,18 +7210,18 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.45" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b70bfff0cfaf33dc9d641196dbcd0023a2da8b4b9030c59535cb44e2884983b" +checksum = "7aab4e20c60429fbba9670a6cae0fff9520046ba0aa3e6d0b1cd2653bea14898" dependencies = [ - "wast 43.0.0", + "wast 47.0.1", ] [[package]] name = "web-sys" -version = "0.3.58" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" dependencies = [ "js-sys", "wasm-bindgen", @@ -7115,18 +7258,18 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.3" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d8de8415c823c8abd270ad483c6feeac771fad964890779f9a8cb24fbbc1bf" +checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be" dependencies = [ "webpki 0.22.0", ] [[package]] name = "weezl" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c97e489d8f836838d497091de568cf16b117486d529ec5579233521065bd5e4" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" [[package]] name = "wepoll-ffi" @@ -7139,30 +7282,31 @@ dependencies = [ [[package]] name = "which" -version = "4.2.5" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" +checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" dependencies = [ "either", - "lazy_static", "libc", + "once_cell", ] [[package]] name = "whoami" -version = "1.2.1" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524b58fa5a20a2fb3014dd6358b70e6579692a56ef6fce928834e488f42f65e8" +checksum = "d6631b6a2fd59b1841b622e8f1a7ad241ef0a46f2d580464ce8140ac94cbd571" dependencies = [ + "bumpalo", "wasm-bindgen", "web-sys", ] [[package]] name = "wiggle" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c38020359fabec5e5ce5a3f667af72e9a203bc6fe8caeb8931d3a870754d9d" +checksum = "67dadac11343d2aabc8a906a0db0aaf7cb5046ec3d6fffccdaf2847dccdef8d6" dependencies = [ "anyhow", "async-trait", @@ -7175,9 +7319,9 @@ dependencies = [ [[package]] name = "wiggle-generate" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc4e4420b496b04920ae3e41424029aba95c15a5e2e2b4012d14ec83770a3ef" +checksum = "63a1dccd6b3fbd9a27417f5d30ce9aa3ee9cf529aad453abbf88a49c5d605b79" dependencies = [ "anyhow", "heck 0.4.0", @@ -7190,9 +7334,9 @@ dependencies = [ [[package]] name = "wiggle-macro" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e541a0be1f2c4d53471d8a9df81c2d8725a3f023d8259f555c65b03d515aaab" +checksum = "f1c368d57d9560c34deaa67e06b0953ccf65edb906c525e5a2c866c849b48ec2" dependencies = [ "proc-macro2", "quote", From 6cdf4e98fc7d98605075f9d8a94b4b72d4b0aeaa Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 12 Oct 2022 15:48:19 -0700 Subject: [PATCH 283/314] Re-export basic text types from text and language crates Also avoid production dependencies on fs and rope in collab --- Cargo.lock | 9 --------- crates/collab/Cargo.toml | 6 ++---- crates/collab/src/integration_tests.rs | 3 +-- crates/diagnostics/Cargo.toml | 1 - crates/diagnostics/src/diagnostics.rs | 7 +++---- crates/editor/Cargo.toml | 1 - crates/editor/src/display_map.rs | 3 +-- crates/editor/src/display_map/block_map.rs | 3 +-- crates/editor/src/display_map/fold_map.rs | 3 +-- crates/editor/src/display_map/tab_map.rs | 8 +++----- crates/editor/src/display_map/wrap_map.rs | 3 +-- crates/editor/src/editor.rs | 5 ++--- crates/editor/src/editor_tests.rs | 3 +-- crates/editor/src/element.rs | 3 +-- crates/editor/src/items.rs | 3 +-- crates/editor/src/movement.rs | 4 +--- crates/editor/src/multi_buffer.rs | 8 ++++---- crates/editor/src/multi_buffer/anchor.rs | 2 +- crates/editor/src/selections_collection.rs | 3 +-- crates/git/Cargo.toml | 1 - crates/git/src/diff.rs | 4 +--- crates/go_to_line/Cargo.toml | 1 - crates/go_to_line/src/go_to_line.rs | 3 +-- crates/language/Cargo.toml | 1 - crates/language/src/buffer.rs | 3 +-- crates/language/src/buffer_tests.rs | 1 - crates/language/src/diagnostic_set.rs | 3 +-- crates/language/src/language.rs | 1 - crates/language/src/syntax_map.rs | 5 ++--- crates/project/Cargo.toml | 1 - crates/project/src/lsp_command.rs | 3 +-- crates/project/src/project.rs | 3 +-- crates/project/src/project_tests.rs | 3 +-- crates/project/src/worktree.rs | 3 +-- crates/rope/src/rope.rs | 13 +++++++------ crates/settings/Cargo.toml | 1 - crates/text/src/anchor.rs | 4 +--- crates/text/src/selection.rs | 5 +---- crates/text/src/text.rs | 5 +++-- crates/vim/Cargo.toml | 1 - crates/vim/src/normal.rs | 3 +-- crates/vim/src/test/neovim_backed_test_context.rs | 4 ++-- crates/vim/src/test/neovim_connection.rs | 3 +-- 43 files changed, 51 insertions(+), 102 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0b875441ca..5e960df642 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1084,7 +1084,6 @@ dependencies = [ "prometheus", "rand 0.8.5", "reqwest", - "rope", "rpc", "scrypt", "serde", @@ -1576,7 +1575,6 @@ dependencies = [ "language", "postage", "project", - "rope", "serde_json", "settings", "smallvec", @@ -1730,7 +1728,6 @@ dependencies = [ "postage", "project", "rand 0.8.5", - "rope", "rpc", "serde", "settings", @@ -2294,7 +2291,6 @@ dependencies = [ "lazy_static", "log", "parking_lot 0.11.2", - "rope", "smol", "sum_tree", "text", @@ -2342,7 +2338,6 @@ dependencies = [ "gpui", "menu", "postage", - "rope", "settings", "text", "workspace", @@ -2943,7 +2938,6 @@ dependencies = [ "postage", "rand 0.8.5", "regex", - "rope", "rpc", "serde", "serde_json", @@ -4123,7 +4117,6 @@ dependencies = [ "rand 0.8.5", "regex", "rocksdb", - "rope", "rpc", "serde", "serde_json", @@ -5148,7 +5141,6 @@ dependencies = [ "gpui", "json_comments", "postage", - "rope", "schemars", "serde", "serde_json", @@ -6639,7 +6631,6 @@ dependencies = [ "nvim-rs", "parking_lot 0.11.2", "project", - "rope", "search", "serde", "serde_json", diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index cf6b7f8b68..de41e8a1f3 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -16,8 +16,6 @@ required-features = ["seed-support"] collections = { path = "../collections" } rpc = { path = "../rpc" } util = { path = "../util" } -fs = { path = "../fs" } -rope = { path = "../rope" } anyhow = "1.0.40" async-trait = "0.1.50" async-tungstenite = "0.16" @@ -27,7 +25,6 @@ base64 = "0.13" clap = { version = "3.1", features = ["derive"], optional = true } envy = "0.4.2" futures = "0.3" -git = { path = "../git" } hyper = "0.14" lazy_static = "1.4" lipsum = { version = "0.8", optional = true } @@ -61,6 +58,8 @@ call = { path = "../call", features = ["test-support"] } client = { path = "../client", features = ["test-support"] } editor = { path = "../editor", features = ["test-support"] } language = { path = "../language", features = ["test-support"] } +fs = { path = "../fs", features = ["test-support"] } +git = { path = "../git", features = ["test-support"] } log = { version = "0.4.16", features = ["kv_unstable_serde"] } lsp = { path = "../lsp", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } @@ -68,7 +67,6 @@ rpc = { path = "../rpc", features = ["test-support"] } settings = { path = "../settings", features = ["test-support"] } theme = { path = "../theme" } workspace = { path = "../workspace", features = ["test-support"] } -git = { path = "../git", features = ["test-support"] } ctor = "0.1" env_logger = "0.9" util = { path = "../util" } diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index bac5cc040e..8ab852810b 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -25,7 +25,7 @@ use gpui::{ }; use language::{ range_to_lsp, tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language, - LanguageConfig, LanguageRegistry, OffsetRangeExt, Rope, + LanguageConfig, LanguageRegistry, OffsetRangeExt, Rope, Point, }; use lsp::{self, FakeLanguageServer}; use parking_lot::Mutex; @@ -34,7 +34,6 @@ use project::{ ProjectStore, WorktreeId, }; use rand::prelude::*; -use rope::point::Point; use rpc::PeerId; use serde_json::json; use settings::{Formatter, Settings}; diff --git a/crates/diagnostics/Cargo.toml b/crates/diagnostics/Cargo.toml index c4b851917e..616f69117f 100644 --- a/crates/diagnostics/Cargo.toml +++ b/crates/diagnostics/Cargo.toml @@ -15,7 +15,6 @@ editor = { path = "../editor" } language = { path = "../language" } gpui = { path = "../gpui" } project = { path = "../project" } -rope = { path = "../rope" } settings = { path = "../settings" } theme = { path = "../theme" } util = { path = "../util" } diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 608b333d0d..3111d7a9f1 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -14,10 +14,10 @@ use gpui::{ ViewHandle, WeakViewHandle, }; use language::{ - Anchor, Bias, Buffer, Diagnostic, DiagnosticEntry, DiagnosticSeverity, Selection, SelectionGoal, + Anchor, Bias, Buffer, Diagnostic, DiagnosticEntry, DiagnosticSeverity, Point, Selection, + SelectionGoal, }; use project::{DiagnosticSummary, Project, ProjectPath}; -use rope::point::Point; use serde_json::json; use settings::Settings; use smallvec::SmallVec; @@ -738,8 +738,7 @@ mod tests { DisplayPoint, }; use gpui::TestAppContext; - use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity}; - use rope::point_utf16::PointUtf16; + use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, PointUtf16}; use serde_json::json; use unindent::Unindent as _; use workspace::AppState; diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index 4e0a10b70d..db634376d0 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -30,7 +30,6 @@ gpui = { path = "../gpui" } language = { path = "../language" } lsp = { path = "../lsp" } project = { path = "../project" } -rope = { path = "../rope" } rpc = { path = "../rpc" } settings = { path = "../settings" } snippet = { path = "../snippet" } diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 91a3a30267..e32276df41 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -11,8 +11,7 @@ use gpui::{ fonts::{FontId, HighlightStyle}, Entity, ModelContext, ModelHandle, }; -use language::Subscription as BufferSubscription; -use rope::{offset_utf16::OffsetUtf16, point::Point}; +use language::{OffsetUtf16, Point, Subscription as BufferSubscription}; use settings::Settings; use std::{any::TypeId, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc}; use sum_tree::{Bias, TreeMap}; diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index f0f2720f1a..3ae259f335 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -5,9 +5,8 @@ use super::{ use crate::{Anchor, ExcerptRange, ToPoint as _}; use collections::{Bound, HashMap, HashSet}; use gpui::{ElementBox, RenderContext}; -use language::{BufferSnapshot, Chunk, Patch}; +use language::{BufferSnapshot, Chunk, Patch, Point}; use parking_lot::Mutex; -use rope::point::Point; use std::{ cell::RefCell, cmp::{self, Ordering}, diff --git a/crates/editor/src/display_map/fold_map.rs b/crates/editor/src/display_map/fold_map.rs index 5bd1670542..663de0f80c 100644 --- a/crates/editor/src/display_map/fold_map.rs +++ b/crates/editor/src/display_map/fold_map.rs @@ -5,9 +5,8 @@ use crate::{ }; use collections::BTreeMap; use gpui::fonts::HighlightStyle; -use language::{Chunk, Edit, TextSummary}; +use language::{Chunk, Edit, Point, TextSummary}; use parking_lot::Mutex; -use rope::point::Point; use std::{ any::TypeId, cmp::{self, Ordering}, diff --git a/crates/editor/src/display_map/tab_map.rs b/crates/editor/src/display_map/tab_map.rs index b7d8fac770..39bcdc4d9f 100644 --- a/crates/editor/src/display_map/tab_map.rs +++ b/crates/editor/src/display_map/tab_map.rs @@ -3,10 +3,8 @@ use super::{ TextHighlights, }; use crate::MultiBufferSnapshot; -use language::Chunk; +use language::{Chunk, Point}; use parking_lot::Mutex; -use rope; -use rope::point::Point; use std::{cmp, mem, num::NonZeroU32, ops::Range}; use sum_tree::Bias; @@ -372,7 +370,7 @@ pub struct TextSummary { impl<'a> From<&'a str> for TextSummary { fn from(text: &'a str) -> Self { - let sum = rope::TextSummary::from(text); + let sum = text::TextSummary::from(text); TextSummary { lines: sum.lines, @@ -524,7 +522,7 @@ mod tests { log::info!("FoldMap text: {:?}", folds_snapshot.text()); let (_, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), tab_size); - let text = rope::Rope::from(tabs_snapshot.text().as_str()); + let text = text::Rope::from(tabs_snapshot.text().as_str()); log::info!( "TabMap text (tab size: {}): {:?}", tab_size, diff --git a/crates/editor/src/display_map/wrap_map.rs b/crates/editor/src/display_map/wrap_map.rs index 42156b905f..0b6713119d 100644 --- a/crates/editor/src/display_map/wrap_map.rs +++ b/crates/editor/src/display_map/wrap_map.rs @@ -8,9 +8,8 @@ use gpui::{ fonts::FontId, text_layout::LineWrapper, Entity, ModelContext, ModelHandle, MutableAppContext, Task, }; -use language::Chunk; +use language::{Chunk, Point}; use lazy_static::lazy_static; -use rope::point::Point; use smol::future::yield_now; use std::{cmp, collections::VecDeque, mem, ops::Range, time::Duration}; use sum_tree::{Bias, Cursor, SumTree}; diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index c8bb16ee00..a7acc9f609 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -43,8 +43,8 @@ pub use items::MAX_TAB_TITLE_LEN; pub use language::{char_kind, CharKind}; use language::{ AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, Diagnostic, - DiagnosticSeverity, IndentKind, IndentSize, Language, OffsetRangeExt, Selection, SelectionGoal, - TransactionId, + DiagnosticSeverity, IndentKind, IndentSize, Language, OffsetRangeExt, OffsetUtf16, Point, + Selection, SelectionGoal, TransactionId, }; use link_go_to_definition::{hide_link_definition, LinkGoToDefinitionState}; pub use multi_buffer::{ @@ -54,7 +54,6 @@ pub use multi_buffer::{ use multi_buffer::{MultiBufferChunks, ToOffsetUtf16}; use ordered_float::OrderedFloat; use project::{FormatTrigger, LocationLink, Project, ProjectPath, ProjectTransaction}; -use rope::{offset_utf16::OffsetUtf16, point::Point}; use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection}; use serde::{Deserialize, Serialize}; use settings::Settings; diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 8b41990574..406969c8fe 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -13,9 +13,8 @@ use gpui::{ geometry::rect::RectF, platform::{WindowBounds, WindowOptions}, }; -use language::{FakeLspAdapter, LanguageConfig, LanguageRegistry}; +use language::{FakeLspAdapter, LanguageConfig, LanguageRegistry, Point}; use project::FakeFs; -use rope::point::Point; use settings::EditorSettings; use util::{ assert_set_eq, diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index f29eb52804..912cea8c5f 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -35,9 +35,8 @@ use gpui::{ WeakViewHandle, }; use json::json; -use language::{Bias, DiagnosticSeverity, Selection}; +use language::{Bias, DiagnosticSeverity, OffsetUtf16, Selection}; use project::ProjectPath; -use rope::offset_utf16::OffsetUtf16; use settings::{GitGutter, Settings}; use smallvec::SmallVec; use std::{ diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 727ee1f094..e6a4eebffb 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -9,9 +9,8 @@ use gpui::{ elements::*, geometry::vector::vec2f, AppContext, Entity, ModelHandle, MutableAppContext, RenderContext, Subscription, Task, View, ViewContext, ViewHandle, }; -use language::{Bias, Buffer, File as _, OffsetRangeExt, SelectionGoal}; +use language::{Bias, Buffer, File as _, OffsetRangeExt, Point, SelectionGoal}; use project::{File, FormatTrigger, Project, ProjectEntryId, ProjectPath}; -use rope::point::Point; use rpc::proto::{self, update_view}; use settings::Settings; use smallvec::SmallVec; diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index e5dcf94841..40908d2bb3 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -1,7 +1,6 @@ -use rope::point::Point; - use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint}; use crate::{char_kind, CharKind, ToPoint}; +use language::Point; use std::ops::Range; pub fn left(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint { @@ -337,7 +336,6 @@ pub fn surrounding_word(map: &DisplaySnapshot, position: DisplayPoint) -> Range< mod tests { use super::*; use crate::{test::marked_display_snapshot, Buffer, DisplayMap, ExcerptRange, MultiBuffer}; - use rope::point::Point; use settings::Settings; #[gpui::test] diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 23ee7df657..d9bb77f1eb 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -9,10 +9,10 @@ use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task}; pub use language::Completion; use language::{ char_kind, AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, - DiagnosticEntry, Event, File, IndentSize, Language, OffsetRangeExt, Outline, OutlineItem, - Selection, ToOffset as _, ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, + DiagnosticEntry, Event, File, IndentSize, Language, OffsetRangeExt, OffsetUtf16, Outline, + OutlineItem, Point, PointUtf16, Selection, TextDimension, ToOffset as _, ToOffsetUtf16 as _, + ToPoint as _, ToPointUtf16 as _, TransactionId, }; -use rope::{offset_utf16::OffsetUtf16, point::Point, point_utf16::PointUtf16, TextDimension}; use smallvec::SmallVec; use std::{ borrow::Cow, @@ -168,7 +168,7 @@ struct ExcerptChunks<'a> { } struct ExcerptBytes<'a> { - content_bytes: rope::Bytes<'a>, + content_bytes: text::Bytes<'a>, footer_height: usize, } diff --git a/crates/editor/src/multi_buffer/anchor.rs b/crates/editor/src/multi_buffer/anchor.rs index b30e4b5780..43723b95fc 100644 --- a/crates/editor/src/multi_buffer/anchor.rs +++ b/crates/editor/src/multi_buffer/anchor.rs @@ -1,5 +1,5 @@ use super::{ExcerptId, MultiBufferSnapshot, ToOffset, ToOffsetUtf16, ToPoint}; -use rope::{offset_utf16::OffsetUtf16, point::Point, TextDimension}; +use language::{OffsetUtf16, Point, TextDimension}; use std::{ cmp::Ordering, ops::{Range, Sub}, diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs index ed983d22d9..999f410db5 100644 --- a/crates/editor/src/selections_collection.rs +++ b/crates/editor/src/selections_collection.rs @@ -8,8 +8,7 @@ use std::{ use collections::HashMap; use gpui::{AppContext, ModelHandle, MutableAppContext}; use itertools::Itertools; -use language::{Bias, Selection, SelectionGoal, ToPoint}; -use rope::{point::Point, TextDimension}; +use language::{Bias, Point, Selection, SelectionGoal, TextDimension, ToPoint}; use util::post_inc; use crate::{ diff --git a/crates/git/Cargo.toml b/crates/git/Cargo.toml index 1d15c2b123..66202a489a 100644 --- a/crates/git/Cargo.toml +++ b/crates/git/Cargo.toml @@ -9,7 +9,6 @@ path = "src/git.rs" [dependencies] anyhow = "1.0.38" clock = { path = "../clock" } -rope = { path = "../rope" } lazy_static = "1.4.0" sum_tree = { path = "../sum_tree" } text = { path = "../text" } diff --git a/crates/git/src/diff.rs b/crates/git/src/diff.rs index 7f3f6101ce..803607895a 100644 --- a/crates/git/src/diff.rs +++ b/crates/git/src/diff.rs @@ -1,8 +1,6 @@ use std::ops::Range; - -use rope::point::Point; use sum_tree::SumTree; -use text::{Anchor, BufferSnapshot, OffsetRangeExt}; +use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point}; pub use git2 as libgit; use libgit::{DiffLineType as GitDiffLineType, DiffOptions as GitOptions, Patch as GitPatch}; diff --git a/crates/go_to_line/Cargo.toml b/crates/go_to_line/Cargo.toml index d69b1f239c..93ae96f93e 100644 --- a/crates/go_to_line/Cargo.toml +++ b/crates/go_to_line/Cargo.toml @@ -13,6 +13,5 @@ gpui = { path = "../gpui" } menu = { path = "../menu" } settings = { path = "../settings" } text = { path = "../text" } -rope = { path = "../rope" } workspace = { path = "../workspace" } postage = { version = "0.4", features = ["futures-traits"] } diff --git a/crates/go_to_line/src/go_to_line.rs b/crates/go_to_line/src/go_to_line.rs index 51ff87a943..3ca50cee42 100644 --- a/crates/go_to_line/src/go_to_line.rs +++ b/crates/go_to_line/src/go_to_line.rs @@ -4,9 +4,8 @@ use gpui::{ MutableAppContext, RenderContext, View, ViewContext, ViewHandle, }; use menu::{Cancel, Confirm}; -use rope::point::Point; use settings::Settings; -use text::Bias; +use text::{Bias, Point}; use workspace::Workspace; actions!(go_to_line, [Toggle]); diff --git a/crates/language/Cargo.toml b/crates/language/Cargo.toml index 54d2147929..96feadbfbc 100644 --- a/crates/language/Cargo.toml +++ b/crates/language/Cargo.toml @@ -29,7 +29,6 @@ fs = { path = "../fs" } git = { path = "../git" } gpui = { path = "../gpui" } lsp = { path = "../lsp" } -rope = { path = "../rope" } rpc = { path = "../rpc" } settings = { path = "../settings" } sum_tree = { path = "../sum_tree" } diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index a5cf24877c..274777b81c 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -17,7 +17,6 @@ use fs::LineEnding; use futures::FutureExt as _; use gpui::{fonts::HighlightStyle, AppContext, Entity, ModelContext, MutableAppContext, Task}; use parking_lot::Mutex; -use rope::point::Point; use settings::Settings; use similar::{ChangeTag, TextDiff}; use smol::future::yield_now; @@ -283,7 +282,7 @@ struct BufferChunkHighlights<'a> { pub struct BufferChunks<'a> { range: Range, - chunks: rope::Chunks<'a>, + chunks: text::Chunks<'a>, diagnostic_endpoints: Peekable>, error_depth: usize, warning_depth: usize, diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index 313c843b02..0f3ab50f4a 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -5,7 +5,6 @@ use fs::LineEnding; use gpui::{ModelHandle, MutableAppContext}; use proto::deserialize_operation; use rand::prelude::*; -use rope::point::Point; use settings::Settings; use std::{ cell::RefCell, diff --git a/crates/language/src/diagnostic_set.rs b/crates/language/src/diagnostic_set.rs index dfbc32149c..b52327cac0 100644 --- a/crates/language/src/diagnostic_set.rs +++ b/crates/language/src/diagnostic_set.rs @@ -1,13 +1,12 @@ use crate::Diagnostic; use collections::HashMap; -use rope::point_utf16::PointUtf16; use std::{ cmp::{Ordering, Reverse}, iter, ops::Range, }; use sum_tree::{self, Bias, SumTree}; -use text::{Anchor, FromAnchor, ToOffset}; +use text::{Anchor, FromAnchor, PointUtf16, ToOffset}; #[derive(Clone, Debug, Default)] pub struct DiagnosticSet { diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 4f8615606c..bb75edbc32 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -22,7 +22,6 @@ use lazy_static::lazy_static; use parking_lot::{Mutex, RwLock}; use postage::watch; use regex::Regex; -use rope::point_utf16::PointUtf16; use serde::{de, Deserialize, Deserializer}; use serde_json::Value; use std::{ diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index 3992d41081..5dd9c483af 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -1,7 +1,6 @@ use crate::{Grammar, InjectionConfig, Language, LanguageRegistry}; use lazy_static::lazy_static; use parking_lot::Mutex; -use rope::point::Point; use std::{ borrow::Cow, cell::RefCell, @@ -11,7 +10,7 @@ use std::{ sync::Arc, }; use sum_tree::{Bias, SeekTarget, SumTree}; -use text::{Anchor, BufferSnapshot, OffsetRangeExt, Rope, ToOffset, ToPoint}; +use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point, Rope, ToOffset, ToPoint}; use tree_sitter::{ Node, Parser, Query, QueryCapture, QueryCaptures, QueryCursor, QueryMatches, Tree, }; @@ -135,7 +134,7 @@ struct ChangeRegionSet(Vec); struct TextProvider<'a>(&'a Rope); -struct ByteChunks<'a>(rope::Chunks<'a>); +struct ByteChunks<'a>(text::Chunks<'a>); struct QueryCursorHandle(Option); diff --git a/crates/project/Cargo.toml b/crates/project/Cargo.toml index 7a41318b86..4e6dc09e4e 100644 --- a/crates/project/Cargo.toml +++ b/crates/project/Cargo.toml @@ -29,7 +29,6 @@ git = { path = "../git" } gpui = { path = "../gpui" } language = { path = "../language" } lsp = { path = "../lsp" } -rope = { path = "../rope" } rpc = { path = "../rpc" } settings = { path = "../settings" } sum_tree = { path = "../sum_tree" } diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index 42098d2e8b..37f6e76340 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -8,11 +8,10 @@ use gpui::{AppContext, AsyncAppContext, ModelHandle}; use language::{ point_from_lsp, point_to_lsp, proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version}, - range_from_lsp, Anchor, Bias, Buffer, CachedLspAdapter, ToPointUtf16, + range_from_lsp, Anchor, Bias, Buffer, CachedLspAdapter, PointUtf16, ToPointUtf16, }; use lsp::{DocumentHighlightKind, LanguageServer, ServerCapabilities}; use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag}; -use rope::point_utf16::PointUtf16; use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc}; #[async_trait(?Send)] diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index de4d76ebc3..17e0d5fafe 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -25,7 +25,7 @@ use language::{ range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, CachedLspAdapter, CharKind, CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName, LocalFile, OffsetRangeExt, - Operation, Patch, TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, + Operation, Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, }; use lsp::{ DiagnosticSeverity, DiagnosticTag, DocumentHighlightKind, LanguageServer, LanguageString, @@ -35,7 +35,6 @@ use lsp_command::*; use parking_lot::Mutex; use postage::watch; use rand::prelude::*; -use rope::point_utf16::PointUtf16; use search::SearchQuery; use serde::Serialize; use settings::{FormatOnSave, Formatter, Settings}; diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 12da0a75db..a631051c58 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -5,10 +5,9 @@ use futures::{future, StreamExt}; use gpui::{executor::Deterministic, test::subscribe}; use language::{ tree_sitter_rust, tree_sitter_typescript, Diagnostic, FakeLspAdapter, LanguageConfig, - OffsetRangeExt, ToPoint, + OffsetRangeExt, Point, ToPoint, }; use lsp::Url; -use rope::point::Point; use serde_json::json; use std::{cell::RefCell, os::unix, rc::Rc, task::Poll}; use unindent::Unindent as _; diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 0de647029d..f0e9aa7934 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -22,14 +22,13 @@ use gpui::{ }; use language::{ proto::{deserialize_version, serialize_line_ending, serialize_version}, - Buffer, DiagnosticEntry, Rope, + Buffer, DiagnosticEntry, PointUtf16, Rope, }; use parking_lot::Mutex; use postage::{ prelude::{Sink as _, Stream as _}, watch, }; -use rope::point_utf16::PointUtf16; use smol::channel::{self, Sender}; use std::{ diff --git a/crates/rope/src/rope.rs b/crates/rope/src/rope.rs index 39dc9dc049..8c357801e3 100644 --- a/crates/rope/src/rope.rs +++ b/crates/rope/src/rope.rs @@ -1,16 +1,17 @@ -pub mod offset_utf16; -pub mod point; -pub mod point_utf16; +mod offset_utf16; +mod point; +mod point_utf16; use arrayvec::ArrayString; use bromberg_sl2::{DigestString, HashMatrix}; -use offset_utf16::OffsetUtf16; -use point::Point; -use point_utf16::PointUtf16; use smallvec::SmallVec; use std::{cmp, fmt, io, mem, ops::Range, str}; use sum_tree::{Bias, Dimension, SumTree}; +pub use offset_utf16::OffsetUtf16; +pub use point::Point; +pub use point_utf16::PointUtf16; + #[cfg(test)] const CHUNK_BASE: usize = 6; diff --git a/crates/settings/Cargo.toml b/crates/settings/Cargo.toml index 1cc73fabc4..ad184ad313 100644 --- a/crates/settings/Cargo.toml +++ b/crates/settings/Cargo.toml @@ -19,7 +19,6 @@ anyhow = "1.0.38" futures = "0.3" theme = { path = "../theme" } util = { path = "../util" } -rope = { path = "../rope" } json_comments = "0.2" postage = { version = "0.4.1", features = ["futures-traits"] } schemars = "0.8" diff --git a/crates/text/src/anchor.rs b/crates/text/src/anchor.rs index 024c7e643b..68ec00056a 100644 --- a/crates/text/src/anchor.rs +++ b/crates/text/src/anchor.rs @@ -1,10 +1,8 @@ +use crate::{BufferSnapshot, Point, PointUtf16, TextDimension, ToOffset, ToPoint, ToPointUtf16}; use anyhow::Result; -use rope::{point::Point, point_utf16::PointUtf16, TextDimension}; use std::{cmp::Ordering, fmt::Debug, ops::Range}; use sum_tree::Bias; -use crate::{BufferSnapshot, ToOffset, ToPoint, ToPointUtf16}; - #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, Default)] pub struct Anchor { pub timestamp: clock::Local, diff --git a/crates/text/src/selection.rs b/crates/text/src/selection.rs index 881fc8c432..d4f55a043b 100644 --- a/crates/text/src/selection.rs +++ b/crates/text/src/selection.rs @@ -1,7 +1,4 @@ -use rope::TextDimension; - -use crate::{Anchor, BufferSnapshot}; - +use crate::{Anchor, BufferSnapshot, TextDimension}; use std::cmp::Ordering; use std::ops::Range; diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index 2196e870f2..72ae018a16 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -19,9 +19,10 @@ use locator::Locator; use operation_queue::OperationQueue; pub use patch::Patch; use postage::{barrier, oneshot, prelude::*}; -use rope::{offset_utf16::OffsetUtf16, point::Point, point_utf16::PointUtf16, TextDimension}; -pub use rope::{Chunks, Rope, TextSummary}; + +pub use rope::*; pub use selection::*; + use std::{ cmp::{self, Ordering, Reverse}, future::Future, diff --git a/crates/vim/Cargo.toml b/crates/vim/Cargo.toml index 85c9636c69..44f2a8cb16 100644 --- a/crates/vim/Cargo.toml +++ b/crates/vim/Cargo.toml @@ -27,7 +27,6 @@ command_palette = { path = "../command_palette" } editor = { path = "../editor" } gpui = { path = "../gpui" } language = { path = "../language" } -rope = { path = "../rope" } search = { path = "../search" } settings = { path = "../settings" } workspace = { path = "../workspace" } diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index 6741d8ac0b..894b77e6e8 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -15,8 +15,7 @@ use editor::{ display_map::ToDisplayPoint, Anchor, Autoscroll, Bias, ClipboardSelection, DisplayPoint, }; use gpui::{actions, MutableAppContext, ViewContext}; -use language::{AutoindentMode, SelectionGoal}; -use rope::point::Point; +use language::{AutoindentMode, Point, SelectionGoal}; use workspace::Workspace; use self::{ diff --git a/crates/vim/src/test/neovim_backed_test_context.rs b/crates/vim/src/test/neovim_backed_test_context.rs index bdac8fe158..e66099963b 100644 --- a/crates/vim/src/test/neovim_backed_test_context.rs +++ b/crates/vim/src/test/neovim_backed_test_context.rs @@ -2,7 +2,7 @@ use std::ops::{Deref, DerefMut}; use collections::{HashMap, HashSet}; use gpui::ContextHandle; -use language::OffsetRangeExt; +use language::{OffsetRangeExt, Point}; use util::test::marked_text_offsets; use super::{neovim_connection::NeovimConnection, NeovimBackedBindingTestContext, VimTestContext}; @@ -51,7 +51,7 @@ impl<'a> NeovimBackedTestContext<'a> { pub async fn set_shared_state(&mut self, marked_text: &str) -> ContextHandle { let context_handle = self.set_state(marked_text, Mode::Normal); - let selection = self.editor(|editor, cx| editor.selections.newest::(cx)); + let selection = self.editor(|editor, cx| editor.selections.newest::(cx)); let text = self.buffer_text(); self.neovim.set_state(selection, &text).await; diff --git a/crates/vim/src/test/neovim_connection.rs b/crates/vim/src/test/neovim_connection.rs index 60ac345323..e2522a76aa 100644 --- a/crates/vim/src/test/neovim_connection.rs +++ b/crates/vim/src/test/neovim_connection.rs @@ -9,8 +9,7 @@ use async_trait::async_trait; #[cfg(feature = "neovim")] use gpui::keymap::Keystroke; -use language::Selection; -use rope::point::Point; +use language::{Point, Selection}; #[cfg(feature = "neovim")] use lazy_static::lazy_static; From 2a1dbd6fb5a341d761c0113478df6f7203ca3351 Mon Sep 17 00:00:00 2001 From: K Simmons Date: Wed, 12 Oct 2022 15:57:29 -0700 Subject: [PATCH 284/314] Update gpui focus test to match the new removal of intermediate focus filtering --- crates/gpui/src/app.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index bdb83ddc55..3b19200015 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -6376,18 +6376,29 @@ mod tests { assert_eq!(mem::take(&mut *observed_events.lock()), Vec::<&str>::new()); view_1.update(cx, |_, cx| { - // Ensure only the latest focus is honored. + // Ensure focus events are sent for all intermediate focuses cx.focus(&view_2); cx.focus(&view_1); cx.focus(&view_2); }); assert_eq!( mem::take(&mut *view_events.lock()), - ["view 1 blurred", "view 2 focused"], + [ + "view 1 blurred", + "view 2 focused", + "view 2 blurred", + "view 1 focused", + "view 1 blurred", + "view 2 focused" + ], ); assert_eq!( mem::take(&mut *observed_events.lock()), [ + "view 2 observed view 1's blur", + "view 1 observed view 2's focus", + "view 1 observed view 2's blur", + "view 2 observed view 1's focus", "view 2 observed view 1's blur", "view 1 observed view 2's focus" ] From dd1320e6d19864651df8e672c1b17d6f40295c18 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 12 Oct 2022 17:05:23 -0700 Subject: [PATCH 285/314] Improved settings writing to be strongly typed and based on settings file content diffs Co-Authored-By: kay@zed.dev --- crates/settings/src/settings.rs | 43 +++-- crates/settings/src/settings_file.rs | 193 +++++++------------- crates/settings/src/watched_json.rs | 105 +++++++++++ crates/theme_selector/src/theme_selector.rs | 6 +- crates/zed/src/main.rs | 9 +- 5 files changed, 206 insertions(+), 150 deletions(-) create mode 100644 crates/settings/src/watched_json.rs diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 2e7dc08d16..8ef798640a 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -1,5 +1,6 @@ mod keymap_file; pub mod settings_file; +pub mod watched_json; use anyhow::Result; use gpui::{ @@ -11,7 +12,7 @@ use schemars::{ schema::{InstanceType, ObjectValidation, Schema, SchemaObject, SingleOrVec}, JsonSchema, }; -use serde::{de::DeserializeOwned, Deserialize}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde_json::Value; use std::{collections::HashMap, fmt::Write as _, num::NonZeroU32, str, sync::Arc}; use theme::{Theme, ThemeRegistry}; @@ -45,7 +46,7 @@ pub struct Settings { pub staff_mode: bool, } -#[derive(Copy, Clone, Debug, Default, Deserialize, JsonSchema)] +#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] pub struct FeatureFlags { pub experimental_themes: bool, } @@ -56,13 +57,13 @@ impl FeatureFlags { } } -#[derive(Copy, Clone, Debug, Default, Deserialize, JsonSchema)] +#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] pub struct GitSettings { pub git_gutter: Option, pub gutter_debounce: Option, } -#[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema)] +#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum GitGutter { #[default] @@ -72,7 +73,7 @@ pub enum GitGutter { pub struct GitGutterConfig {} -#[derive(Clone, Debug, Default, Deserialize, JsonSchema)] +#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] pub struct EditorSettings { pub tab_size: Option, pub hard_tabs: Option, @@ -83,14 +84,14 @@ pub struct EditorSettings { pub enable_language_server: Option, } -#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum SoftWrap { None, EditorWidth, PreferredLineLength, } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum FormatOnSave { On, @@ -102,7 +103,7 @@ pub enum FormatOnSave { }, } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum Formatter { LanguageServer, @@ -112,7 +113,7 @@ pub enum Formatter { }, } -#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum Autosave { Off, @@ -121,7 +122,7 @@ pub enum Autosave { OnWindowChange, } -#[derive(Clone, Debug, Default, Deserialize, JsonSchema)] +#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] pub struct TerminalSettings { pub shell: Option, pub working_directory: Option, @@ -134,7 +135,7 @@ pub struct TerminalSettings { pub copy_on_select: Option, } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum TerminalBlink { Off, @@ -148,7 +149,7 @@ impl Default for TerminalBlink { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum Shell { System, @@ -162,7 +163,7 @@ impl Default for Shell { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum AlternateScroll { On, @@ -175,7 +176,7 @@ impl Default for AlternateScroll { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum WorkingDirectory { CurrentProjectDirectory, @@ -184,7 +185,7 @@ pub enum WorkingDirectory { Always { directory: String }, } -#[derive(PartialEq, Eq, Debug, Default, Copy, Clone, Hash, Deserialize, JsonSchema)] +#[derive(PartialEq, Eq, Debug, Default, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum DockAnchor { #[default] @@ -193,7 +194,7 @@ pub enum DockAnchor { Expanded, } -#[derive(Clone, Debug, Default, Deserialize, JsonSchema)] +#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] pub struct SettingsFileContent { pub experiments: Option, #[serde(default)] @@ -229,7 +230,7 @@ pub struct SettingsFileContent { pub staff_mode: Option, } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub struct LspSettings { pub initialization_options: Option, @@ -503,6 +504,8 @@ pub fn settings_file_json_schema( serde_json::to_value(root_schema).unwrap() } +/// Expects the key to be unquoted, and the value to be valid JSON +/// (e.g. values should be unquoted for numbers and bools, quoted for strings) pub fn write_top_level_setting( mut settings_content: String, top_level_key: &str, @@ -553,7 +556,7 @@ pub fn write_top_level_setting( settings_content.clear(); write!( settings_content, - "{{\n \"{}\": \"{new_val}\"\n}}\n", + "{{\n \"{}\": {new_val}\n}}\n", top_level_key ) .unwrap(); @@ -561,7 +564,7 @@ pub fn write_top_level_setting( (_, Some(existing_value_range)) => { // Existing theme key, overwrite - settings_content.replace_range(existing_value_range, &format!("\"{new_val}\"")); + settings_content.replace_range(existing_value_range, &new_val); } (Some(first_key_start), None) => { @@ -581,7 +584,7 @@ pub fn write_top_level_setting( } } - let content = format!(r#""{top_level_key}": "{new_val}","#); + let content = format!(r#""{top_level_key}": {new_val},"#); settings_content.insert_str(first_key_start, &content); if row > 0 { diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index 6a7c96fd81..506ebc8c3d 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -1,153 +1,96 @@ +use crate::{watched_json::WatchedJsonFile, write_top_level_setting, SettingsFileContent}; +use anyhow::Result; use fs::Fs; -use futures::StreamExt; -use gpui::{executor, MutableAppContext}; -use postage::sink::Sink as _; -use postage::{prelude::Stream, watch}; -use serde::Deserialize; - -use std::{path::Path, sync::Arc, time::Duration}; -use theme::ThemeRegistry; -use util::ResultExt; - -use crate::{ - parse_json_with_comments, write_top_level_setting, KeymapFileContent, Settings, - SettingsFileContent, -}; +use gpui::MutableAppContext; +use serde_json::Value; +use std::{path::Path, sync::Arc}; // TODO: Switch SettingsFile to open a worktree and buffer for synchronization // And instant updates in the Zed editor #[derive(Clone)] pub struct SettingsFile { path: &'static Path, + settings_file_content: WatchedJsonFile, fs: Arc, } impl SettingsFile { - pub fn new(path: &'static Path, fs: Arc) -> Self { - SettingsFile { path, fs } - } - - pub async fn rewrite_settings_file(&self, f: F) -> anyhow::Result<()> - where - F: Fn(String) -> String, - { - let content = self.fs.load(self.path).await?; - - let new_settings = f(content); - - self.fs - .atomic_write(self.path.to_path_buf(), new_settings) - .await?; - - Ok(()) - } -} - -pub fn write_setting(key: &'static str, val: String, cx: &mut MutableAppContext) { - let settings_file = cx.global::().clone(); - cx.background() - .spawn(async move { - settings_file - .rewrite_settings_file(|settings| write_top_level_setting(settings, key, &val)) - .await - }) - .detach_and_log_err(cx); -} - -#[derive(Clone)] -pub struct WatchedJsonFile(pub watch::Receiver); - -impl WatchedJsonFile -where - T: 'static + for<'de> Deserialize<'de> + Clone + Default + Send + Sync, -{ - pub async fn new( + pub fn new( + path: &'static Path, + settings_file_content: WatchedJsonFile, fs: Arc, - executor: &executor::Background, - path: impl Into>, ) -> Self { - let path = path.into(); - let settings = Self::load(fs.clone(), &path).await.unwrap_or_default(); - let mut events = fs.watch(&path, Duration::from_millis(500)).await; - let (mut tx, rx) = watch::channel_with(settings); - executor + SettingsFile { + path, + settings_file_content, + fs, + } + } + + pub fn update(cx: &mut MutableAppContext, update: impl FnOnce(&mut SettingsFileContent)) { + let this = cx.global::(); + + let current_file_content = this.settings_file_content.current(); + let mut new_file_content = current_file_content.clone(); + + update(&mut new_file_content); + + let fs = this.fs.clone(); + let path = this.path.clone(); + + cx.background() .spawn(async move { - while events.next().await.is_some() { - if let Some(settings) = Self::load(fs.clone(), &path).await { - if tx.send(settings).await.is_err() { - break; + // Unwrap safety: These values are all guarnteed to be well formed, and we know + // that they will deserialize to our settings object. All of the following unwraps + // are therefore safe. + let tmp = serde_json::to_value(current_file_content).unwrap(); + let old_json = tmp.as_object().unwrap(); + + let new_tmp = serde_json::to_value(new_file_content).unwrap(); + let new_json = new_tmp.as_object().unwrap(); + + // Find changed fields + let mut diffs = vec![]; + for (key, old_value) in old_json.iter() { + let new_value = new_json.get(key).unwrap(); + if old_value != new_value { + if matches!( + new_value, + &Value::Null | &Value::Object(_) | &Value::Array(_) + ) { + unimplemented!( + "We only support updating basic values at the top level" + ); } + + let new_json = serde_json::to_string_pretty(new_value) + .expect("Could not serialize new json field to string"); + + diffs.push((key, new_json)); } } + + // Have diffs, rewrite the settings file now. + let mut content = fs.load(path).await?; + + for (key, new_value) in diffs { + content = write_top_level_setting(content, key, &new_value) + } + + fs.atomic_write(path.to_path_buf(), content).await?; + + Ok(()) as Result<()> }) - .detach(); - Self(rx) + .detach_and_log_err(cx); } - - ///Loads the given watched JSON file. In the special case that the file is - ///empty (ignoring whitespace) or is not a file, this will return T::default() - async fn load(fs: Arc, path: &Path) -> Option { - if !fs.is_file(path).await { - return Some(T::default()); - } - - fs.load(path).await.log_err().and_then(|data| { - if data.trim().is_empty() { - Some(T::default()) - } else { - parse_json_with_comments(&data).log_err() - } - }) - } -} - -pub fn watch_settings_file( - defaults: Settings, - mut file: WatchedJsonFile, - theme_registry: Arc, - cx: &mut MutableAppContext, -) { - settings_updated(&defaults, file.0.borrow().clone(), &theme_registry, cx); - cx.spawn(|mut cx| async move { - while let Some(content) = file.0.recv().await { - cx.update(|cx| settings_updated(&defaults, content, &theme_registry, cx)); - } - }) - .detach(); -} - -pub fn keymap_updated(content: KeymapFileContent, cx: &mut MutableAppContext) { - cx.clear_bindings(); - KeymapFileContent::load_defaults(cx); - content.add_to_cx(cx).log_err(); -} - -pub fn settings_updated( - defaults: &Settings, - content: SettingsFileContent, - theme_registry: &Arc, - cx: &mut MutableAppContext, -) { - let mut settings = defaults.clone(); - settings.set_user_settings(content, theme_registry, cx.font_cache()); - cx.set_global(settings); - cx.refresh_windows(); -} - -pub fn watch_keymap_file(mut file: WatchedJsonFile, cx: &mut MutableAppContext) { - cx.spawn(|mut cx| async move { - while let Some(content) = file.0.recv().await { - cx.update(|cx| keymap_updated(content, cx)); - } - }) - .detach(); } #[cfg(test)] mod tests { use super::*; - use crate::{EditorSettings, SoftWrap}; + use crate::{watched_json::watch_settings_file, EditorSettings, Settings, SoftWrap}; use fs::FakeFs; + use theme::ThemeRegistry; #[gpui::test] async fn test_watch_settings_files(cx: &mut gpui::TestAppContext) { diff --git a/crates/settings/src/watched_json.rs b/crates/settings/src/watched_json.rs new file mode 100644 index 0000000000..e304842aa2 --- /dev/null +++ b/crates/settings/src/watched_json.rs @@ -0,0 +1,105 @@ +use fs::Fs; +use futures::StreamExt; +use gpui::{executor, MutableAppContext}; +use postage::sink::Sink as _; +use postage::{prelude::Stream, watch}; +use serde::Deserialize; + +use std::{path::Path, sync::Arc, time::Duration}; +use theme::ThemeRegistry; +use util::ResultExt; + +use crate::{parse_json_with_comments, KeymapFileContent, Settings, SettingsFileContent}; + +#[derive(Clone)] +pub struct WatchedJsonFile(pub watch::Receiver); + +impl WatchedJsonFile +where + T: 'static + for<'de> Deserialize<'de> + Clone + Default + Send + Sync, +{ + pub async fn new( + fs: Arc, + executor: &executor::Background, + path: impl Into>, + ) -> Self { + let path = path.into(); + let settings = Self::load(fs.clone(), &path).await.unwrap_or_default(); + let mut events = fs.watch(&path, Duration::from_millis(500)).await; + let (mut tx, rx) = watch::channel_with(settings); + executor + .spawn(async move { + while events.next().await.is_some() { + if let Some(settings) = Self::load(fs.clone(), &path).await { + if tx.send(settings).await.is_err() { + break; + } + } + } + }) + .detach(); + Self(rx) + } + + ///Loads the given watched JSON file. In the special case that the file is + ///empty (ignoring whitespace) or is not a file, this will return T::default() + async fn load(fs: Arc, path: &Path) -> Option { + if !fs.is_file(path).await { + return Some(T::default()); + } + + fs.load(path).await.log_err().and_then(|data| { + if data.trim().is_empty() { + Some(T::default()) + } else { + parse_json_with_comments(&data).log_err() + } + }) + } + + pub fn current(&self) -> T { + self.0.borrow().clone() + } +} + +pub fn watch_settings_file( + defaults: Settings, + mut file: WatchedJsonFile, + theme_registry: Arc, + cx: &mut MutableAppContext, +) { + settings_updated(&defaults, file.0.borrow().clone(), &theme_registry, cx); + cx.spawn(|mut cx| async move { + while let Some(content) = file.0.recv().await { + cx.update(|cx| settings_updated(&defaults, content, &theme_registry, cx)); + } + }) + .detach(); +} + +pub fn keymap_updated(content: KeymapFileContent, cx: &mut MutableAppContext) { + cx.clear_bindings(); + KeymapFileContent::load_defaults(cx); + content.add_to_cx(cx).log_err(); +} + +pub fn settings_updated( + defaults: &Settings, + content: SettingsFileContent, + theme_registry: &Arc, + cx: &mut MutableAppContext, +) { + let mut settings = defaults.clone(); + settings.set_user_settings(content, theme_registry, cx.font_cache()); + cx.set_global(settings); + cx.refresh_windows(); +} + +pub fn watch_keymap_file(mut file: WatchedJsonFile, cx: &mut MutableAppContext) { + cx.spawn(|mut cx| async move { + while let Some(content) = file.0.recv().await { + cx.update(|cx| keymap_updated(content, cx)); + } + }) + .detach(); +} diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index f3ca38b78b..729cdad739 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -4,7 +4,7 @@ use gpui::{ MutableAppContext, RenderContext, View, ViewContext, ViewHandle, }; use picker::{Picker, PickerDelegate}; -use settings::Settings; +use settings::{settings_file::SettingsFile, Settings}; use std::sync::Arc; use theme::{Theme, ThemeMeta, ThemeRegistry}; use workspace::{AppState, Workspace}; @@ -155,7 +155,9 @@ impl PickerDelegate for ThemeSelector { self.selection_completed = true; let theme_name = cx.global::().theme.meta.name.clone(); - settings::settings_file::write_setting("theme", theme_name, cx); + SettingsFile::update(cx, |settings_content| { + settings_content.theme = Some(theme_name); + }); cx.emit(Event::Dismissed); } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 1c6a818ef3..a921bc2680 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -35,7 +35,7 @@ use std::{env, ffi::OsStr, panic, path::PathBuf, sync::Arc, thread, time::Durati use terminal::terminal_container_view::{get_working_directory, TerminalContainer}; use fs::RealFs; -use settings::settings_file::{watch_keymap_file, watch_settings_file, WatchedJsonFile}; +use settings::watched_json::{watch_keymap_file, watch_settings_file, WatchedJsonFile}; use theme::ThemeRegistry; use util::{ResultExt, TryFutureExt}; use workspace::{self, AppState, ItemHandle, NewFile, OpenPaths, Workspace}; @@ -65,7 +65,6 @@ fn main() { let themes = ThemeRegistry::new(Assets, app.font_cache()); let default_settings = Settings::defaults(Assets, &app.font_cache(), &themes); - let settings_file = SettingsFile::new(&*zed::paths::SETTINGS, fs.clone()); let config_files = load_config_files(&app, fs.clone()); let login_shell_env_loaded = if stdout_is_a_pty() { @@ -101,7 +100,11 @@ fn main() { let (settings_file_content, keymap_file) = cx.background().block(config_files).unwrap(); //Setup settings global before binding actions - cx.set_global(settings_file); + cx.set_global(SettingsFile::new( + &*zed::paths::SETTINGS, + settings_file_content.clone(), + fs.clone(), + )); watch_settings_file(default_settings, settings_file_content, themes.clone(), cx); watch_keymap_file(keymap_file, cx); From e73270085bd8b748cbb8f46898201fc7608b73ce Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 12 Oct 2022 17:11:47 -0700 Subject: [PATCH 286/314] Fixed settings --- crates/settings/src/settings.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 8ef798640a..88ce173a17 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -634,7 +634,8 @@ mod tests { "# .unindent(); - let settings_after_theme = write_top_level_setting(settings, "theme", "summerfruit-light"); + let settings_after_theme = + write_top_level_setting(settings, "theme", "\"summerfruit-light\""); assert_eq!(settings_after_theme, new_settings) } @@ -654,7 +655,8 @@ mod tests { "# .unindent(); - let settings_after_theme = write_top_level_setting(settings, "theme", "summerfruit-light"); + let settings_after_theme = + write_top_level_setting(settings, "theme", "\"summerfruit-light\""); assert_eq!(settings_after_theme, new_settings) } @@ -670,7 +672,8 @@ mod tests { "# .unindent(); - let settings_after_theme = write_top_level_setting(settings, "theme", "summerfruit-light"); + let settings_after_theme = + write_top_level_setting(settings, "theme", "\"summerfruit-light\""); assert_eq!(settings_after_theme, new_settings) } @@ -680,7 +683,8 @@ mod tests { let settings = r#"{ "a": "", "ok": true }"#.to_string(); let new_settings = r#"{ "theme": "summerfruit-light", "a": "", "ok": true }"#; - let settings_after_theme = write_top_level_setting(settings, "theme", "summerfruit-light"); + let settings_after_theme = + write_top_level_setting(settings, "theme", "\"summerfruit-light\""); assert_eq!(settings_after_theme, new_settings) } @@ -690,7 +694,8 @@ mod tests { let settings = r#" { "a": "", "ok": true }"#.to_string(); let new_settings = r#" { "theme": "summerfruit-light", "a": "", "ok": true }"#; - let settings_after_theme = write_top_level_setting(settings, "theme", "summerfruit-light"); + let settings_after_theme = + write_top_level_setting(settings, "theme", "\"summerfruit-light\""); assert_eq!(settings_after_theme, new_settings) } @@ -712,7 +717,8 @@ mod tests { "# .unindent(); - let settings_after_theme = write_top_level_setting(settings, "theme", "summerfruit-light"); + let settings_after_theme = + write_top_level_setting(settings, "theme", "\"summerfruit-light\""); assert_eq!(settings_after_theme, new_settings) } From 513c02e67f785712ec12136b24230ebcf8af4a7c Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 12 Oct 2022 17:39:44 -0700 Subject: [PATCH 287/314] Remove spurious focus of contact popover when opening it Co-authored-by: Nathan Sobo --- crates/collab_ui/src/collab_titlebar_item.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 928cf3273b..c78a50b86d 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -152,7 +152,6 @@ impl CollabTitlebarItem { let project = workspace.read(cx).project().clone(); let user_store = workspace.read(cx).user_store().clone(); let view = cx.add_view(|cx| ContactsPopover::new(project, user_store, cx)); - cx.focus(&view); cx.subscribe(&view, |this, _, event, cx| { match event { contacts_popover::Event::Dismissed => { From b5786cbf306c3c31138214f6011b812da3c82baa Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 12 Oct 2022 17:55:11 -0700 Subject: [PATCH 288/314] Dismiss contacts popover when clicking outside, even w/o focus change Co-authored-by: Nathan Sobo --- crates/collab_ui/src/contacts_popover.rs | 119 ++++++++++++----------- 1 file changed, 64 insertions(+), 55 deletions(-) diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index deedac9f98..73bcad9fd7 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -1,4 +1,4 @@ -use crate::{contact_finder::ContactFinder, contact_list::ContactList}; +use crate::{contact_finder::ContactFinder, contact_list::ContactList, ToggleCollaborationMenu}; use client::UserStore; use gpui::{ actions, elements::*, ClipboardItem, CursorStyle, Entity, ModelHandle, MouseButton, @@ -92,63 +92,72 @@ impl View for ContactsPopover { Child::ContactFinder(child) => ChildView::new(child), }; - Flex::column() - .with_child(child.flex(1., true).boxed()) - .with_children( - self.user_store - .read(cx) - .invite_info() - .cloned() - .and_then(|info| { - enum InviteLink {} + MouseEventHandler::::new(0, cx, |_, cx| { + Flex::column() + .with_child(child.flex(1., true).boxed()) + .with_children( + self.user_store + .read(cx) + .invite_info() + .cloned() + .and_then(|info| { + enum InviteLink {} - if info.count > 0 { - Some( - MouseEventHandler::::new(0, cx, |state, cx| { - let style = theme - .contacts_popover - .invite_row - .style_for(state, false) - .clone(); + if info.count > 0 { + Some( + MouseEventHandler::::new(0, cx, |state, cx| { + let style = theme + .contacts_popover + .invite_row + .style_for(state, false) + .clone(); - let copied = cx.read_from_clipboard().map_or(false, |item| { - item.text().as_str() == info.url.as_ref() - }); + let copied = + cx.read_from_clipboard().map_or(false, |item| { + item.text().as_str() == info.url.as_ref() + }); - Label::new( - format!( - "{} invite link ({} left)", - if copied { "Copied" } else { "Copy" }, - info.count - ), - style.label.clone(), - ) - .aligned() - .left() - .constrained() - .with_height(theme.contacts_popover.invite_row_height) - .contained() - .with_style(style.container) - .boxed() - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, cx| { - cx.write_to_clipboard(ClipboardItem::new(info.url.to_string())); - cx.notify(); - }) - .boxed(), - ) - } else { - None - } - }), - ) - .contained() - .with_style(theme.contacts_popover.container) - .constrained() - .with_width(theme.contacts_popover.width) - .with_height(theme.contacts_popover.height) - .boxed() + Label::new( + format!( + "{} invite link ({} left)", + if copied { "Copied" } else { "Copy" }, + info.count + ), + style.label.clone(), + ) + .aligned() + .left() + .constrained() + .with_height(theme.contacts_popover.invite_row_height) + .contained() + .with_style(style.container) + .boxed() + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, cx| { + cx.write_to_clipboard(ClipboardItem::new( + info.url.to_string(), + )); + cx.notify(); + }) + .boxed(), + ) + } else { + None + } + }), + ) + .contained() + .with_style(theme.contacts_popover.container) + .constrained() + .with_width(theme.contacts_popover.width) + .with_height(theme.contacts_popover.height) + .boxed() + }) + .on_down_out(MouseButton::Left, move |_, cx| { + cx.dispatch_action(ToggleCollaborationMenu); + }) + .boxed() } fn on_focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext) { From 0a1aea6cb814810341a2b97ec6d48e7bebd9ac18 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 12 Oct 2022 17:22:44 +0200 Subject: [PATCH 289/314] Add test to ensure buffer identity is kept across `Project::rename` --- crates/project/src/project_tests.rs | 43 +++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index a631051c58..702f28fc59 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -2260,6 +2260,49 @@ async fn test_rescan_and_remote_updates( }); } +#[gpui::test(iterations = 10)] +async fn test_buffer_identity_across_renames(cx: &mut gpui::TestAppContext) { + let dir = temp_tree(json!({ + "a": { + "file1": "", + } + })); + + let project = Project::test(Arc::new(RealFs), [dir.path()], cx).await; + let tree = project.read_with(cx, |project, cx| project.worktrees(cx).next().unwrap()); + let tree_id = tree.read_with(cx, |tree, _| tree.id()); + + let id_for_path = |path: &'static str, cx: &gpui::TestAppContext| { + project.read_with(cx, |project, cx| { + let tree = project.worktrees(cx).next().unwrap(); + tree.read(cx) + .entry_for_path(path) + .unwrap_or_else(|| panic!("no entry for path {}", path)) + .id + }) + }; + + let dir_id = id_for_path("a", cx); + let file_id = id_for_path("a/file1", cx); + let buffer = project + .update(cx, |p, cx| p.open_buffer((tree_id, "a/file1"), cx)) + .await + .unwrap(); + buffer.read_with(cx, |buffer, _| assert!(!buffer.is_dirty())); + + project + .update(cx, |project, cx| { + project.rename_entry(dir_id, Path::new("b"), cx) + }) + .unwrap() + .await + .unwrap(); + tree.flush_fs_events(cx).await; + assert_eq!(id_for_path("b", cx), dir_id); + assert_eq!(id_for_path("b/file1", cx), file_id); + buffer.read_with(cx, |buffer, _| assert!(!buffer.is_dirty())); +} + #[gpui::test] async fn test_buffer_deduping(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.background()); From f28cc5ca0c7224f1fed578c2a081c1579edb795a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 13 Oct 2022 09:10:10 +0200 Subject: [PATCH 290/314] Preserve buffer identity when underlying entry temporarily disappears --- crates/project/src/project.rs | 13 +++++++------ crates/project/src/project_tests.rs | 1 + crates/project/src/worktree.rs | 23 ++++++++++++++++------- crates/rpc/proto/zed.proto | 3 ++- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 17e0d5fafe..ea74dda434 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4298,34 +4298,35 @@ impl Project { return; } - let new_file = if let Some(entry) = old_file - .entry_id - .and_then(|entry_id| snapshot.entry_for_id(entry_id)) + let new_file = if let Some(entry) = snapshot.entry_for_id(old_file.entry_id) { File { is_local: true, - entry_id: Some(entry.id), + entry_id: entry.id, mtime: entry.mtime, path: entry.path.clone(), worktree: worktree_handle.clone(), + is_deleted: false, } } else if let Some(entry) = snapshot.entry_for_path(old_file.path().as_ref()) { File { is_local: true, - entry_id: Some(entry.id), + entry_id: entry.id, mtime: entry.mtime, path: entry.path.clone(), worktree: worktree_handle.clone(), + is_deleted: false, } } else { File { is_local: true, - entry_id: None, + entry_id: old_file.entry_id, path: old_file.path().clone(), mtime: old_file.mtime(), worktree: worktree_handle.clone(), + is_deleted: true, } }; diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 702f28fc59..72f4778a38 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -2457,6 +2457,7 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) { .await .unwrap(); cx.foreground().run_until_parked(); + buffer2.read_with(cx, |buffer, _| assert!(buffer.is_dirty())); assert_eq!( *events.borrow(), &[ diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index f0e9aa7934..383c9ac35b 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -688,11 +688,12 @@ impl LocalWorktree { Ok(( File { - entry_id: Some(entry.id), + entry_id: entry.id, worktree: handle, path: entry.path, mtime: entry.mtime, is_local: true, + is_deleted: false, }, text, diff_base, @@ -715,11 +716,12 @@ impl LocalWorktree { cx.as_mut().spawn(|mut cx| async move { let entry = save.await?; let file = File { - entry_id: Some(entry.id), + entry_id: entry.id, worktree: handle, path: entry.path, mtime: entry.mtime, is_local: true, + is_deleted: false, }; buffer_handle.update(&mut cx, |buffer, cx| { @@ -1813,8 +1815,9 @@ pub struct File { pub worktree: ModelHandle, pub path: Arc, pub mtime: SystemTime, - pub(crate) entry_id: Option, + pub(crate) entry_id: ProjectEntryId, pub(crate) is_local: bool, + pub(crate) is_deleted: bool, } impl language::File for File { @@ -1852,7 +1855,7 @@ impl language::File for File { } fn is_deleted(&self) -> bool { - self.entry_id.is_none() + self.is_deleted } fn save( @@ -1912,9 +1915,10 @@ impl language::File for File { fn to_proto(&self) -> rpc::proto::File { rpc::proto::File { worktree_id: self.worktree.id() as u64, - entry_id: self.entry_id.map(|entry_id| entry_id.to_proto()), + entry_id: self.entry_id.to_proto(), path: self.path.to_string_lossy().into(), mtime: Some(self.mtime.into()), + is_deleted: self.is_deleted, } } } @@ -1983,8 +1987,9 @@ impl File { worktree, path: Path::new(&proto.path).into(), mtime: proto.mtime.ok_or_else(|| anyhow!("no timestamp"))?.into(), - entry_id: proto.entry_id.map(ProjectEntryId::from_proto), + entry_id: ProjectEntryId::from_proto(proto.entry_id), is_local: false, + is_deleted: proto.is_deleted, }) } @@ -1997,7 +2002,11 @@ impl File { } pub fn project_entry_id(&self, _: &AppContext) -> Option { - self.entry_id + if self.is_deleted { + None + } else { + Some(self.entry_id) + } } } diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 283b11fd78..1248bb0551 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -868,9 +868,10 @@ message User { message File { uint64 worktree_id = 1; - optional uint64 entry_id = 2; + uint64 entry_id = 2; string path = 3; Timestamp mtime = 4; + bool is_deleted = 5; } message Entry { From 37a0fd33c5189153d862704ee160768287616601 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 13 Oct 2022 09:33:55 +0200 Subject: [PATCH 291/314] Use fake file system for buffer identity test --- crates/project/src/project_tests.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 72f4778a38..1b0294c4d1 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -2261,14 +2261,22 @@ async fn test_rescan_and_remote_updates( } #[gpui::test(iterations = 10)] -async fn test_buffer_identity_across_renames(cx: &mut gpui::TestAppContext) { - let dir = temp_tree(json!({ - "a": { - "file1": "", - } - })); +async fn test_buffer_identity_across_renames( + deterministic: Arc, + cx: &mut gpui::TestAppContext, +) { + let fs = FakeFs::new(cx.background()); + fs.insert_tree( + "/dir", + json!({ + "a": { + "file1": "", + } + }), + ) + .await; - let project = Project::test(Arc::new(RealFs), [dir.path()], cx).await; + let project = Project::test(fs, [Path::new("/dir")], cx).await; let tree = project.read_with(cx, |project, cx| project.worktrees(cx).next().unwrap()); let tree_id = tree.read_with(cx, |tree, _| tree.id()); @@ -2297,7 +2305,7 @@ async fn test_buffer_identity_across_renames(cx: &mut gpui::TestAppContext) { .unwrap() .await .unwrap(); - tree.flush_fs_events(cx).await; + deterministic.run_until_parked(); assert_eq!(id_for_path("b", cx), dir_id); assert_eq!(id_for_path("b/file1", cx), file_id); buffer.read_with(cx, |buffer, _| assert!(!buffer.is_dirty())); From c4680e66ffc36c65f63089ced1cb802853e5da3e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 13 Oct 2022 11:10:23 +0200 Subject: [PATCH 292/314] Fix error on clangd when `compile-commands.json` is present The language server was failing because we were forgetting to provide a `jsonrpc` field for responses to requests coming from the lsp. --- crates/lsp/src/lsp.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index 95ef299c72..6e7def92e9 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -56,7 +56,7 @@ pub struct Subscription { #[derive(Serialize, Deserialize)] struct Request<'a, T> { - jsonrpc: &'a str, + jsonrpc: &'static str, id: usize, method: &'a str, params: T, @@ -73,6 +73,7 @@ struct AnyResponse<'a> { #[derive(Serialize)] struct Response { + jsonrpc: &'static str, id: usize, result: Option, error: Option, @@ -80,8 +81,7 @@ struct Response { #[derive(Serialize, Deserialize)] struct Notification<'a, T> { - #[serde(borrow)] - jsonrpc: &'a str, + jsonrpc: &'static str, #[serde(borrow)] method: &'a str, params: T, @@ -453,11 +453,13 @@ impl LanguageServer { async move { let response = match response.await { Ok(result) => Response { + jsonrpc: JSON_RPC_VERSION, id, result: Some(result), error: None, }, Err(error) => Response { + jsonrpc: JSON_RPC_VERSION, id, result: None, error: Some(Error { From 06dfb74663f4c40732aacda8865177063ce230bf Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 13 Oct 2022 14:53:58 +0200 Subject: [PATCH 293/314] Prevent `ChildView` from retaining an otherwise dropped view --- crates/gpui/src/app.rs | 4 ++++ crates/gpui/src/presenter.rs | 20 +++++++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 3b19200015..f7672db9ae 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -4732,6 +4732,10 @@ pub struct AnyWeakViewHandle { } impl AnyWeakViewHandle { + pub fn id(&self) -> usize { + self.view_id + } + pub fn upgrade(&self, cx: &impl UpgradeViewHandle) -> Option { cx.upgrade_any_view_handle(self) } diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index eaccd8f410..e4aae8fbd9 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -12,10 +12,10 @@ use crate::{ UpOutRegionEvent, UpRegionEvent, }, text_layout::TextLayoutCache, - Action, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, Appearance, AssetCache, ElementBox, - Entity, FontSystem, ModelHandle, MouseButton, MouseMovedEvent, MouseRegion, MouseRegionId, - ParentId, ReadModel, ReadView, RenderContext, RenderParams, Scene, UpgradeModelHandle, - UpgradeViewHandle, View, ViewHandle, WeakModelHandle, WeakViewHandle, + Action, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AnyWeakViewHandle, Appearance, + AssetCache, ElementBox, Entity, FontSystem, ModelHandle, MouseButton, MouseMovedEvent, + MouseRegion, MouseRegionId, ParentId, ReadModel, ReadView, RenderContext, RenderParams, Scene, + UpgradeModelHandle, UpgradeViewHandle, View, ViewHandle, WeakModelHandle, WeakViewHandle, }; use collections::{HashMap, HashSet}; use pathfinder_geometry::vector::{vec2f, Vector2F}; @@ -972,12 +972,14 @@ impl ToJson for SizeConstraint { } pub struct ChildView { - view: AnyViewHandle, + view: AnyWeakViewHandle, } impl ChildView { pub fn new(view: impl Into) -> Self { - Self { view: view.into() } + Self { + view: view.into().downgrade(), + } } } @@ -1039,7 +1041,11 @@ impl Element for ChildView { "type": "ChildView", "view_id": self.view.id(), "bounds": bounds.to_json(), - "view": self.view.debug_json(cx.app), + "view": if let Some(view) = self.view.upgrade(cx.app) { + view.debug_json(cx.app) + } else { + json!(null) + }, "child": if let Some(view) = cx.rendered_views.get(&self.view.id()) { view.debug(cx) } else { From edb61a9c8f924eda870865770b3cdf68f53f5f6c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 13 Oct 2022 15:11:57 +0200 Subject: [PATCH 294/314] Avoid panicking if child view points to a view that was not rendered --- crates/gpui/src/presenter.rs | 38 ++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index e4aae8fbd9..8cf9e99063 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -984,7 +984,7 @@ impl ChildView { } impl Element for ChildView { - type LayoutState = (); + type LayoutState = bool; type PaintState = (); fn layout( @@ -992,18 +992,27 @@ impl Element for ChildView { constraint: SizeConstraint, cx: &mut LayoutContext, ) -> (Vector2F, Self::LayoutState) { - let size = cx.layout(self.view.id(), constraint); - (size, ()) + if cx.rendered_views.contains_key(&self.view.id()) { + let size = cx.layout(self.view.id(), constraint); + (size, true) + } else { + log::error!("layout called on a view that was not rendered"); + (Vector2F::zero(), false) + } } fn paint( &mut self, bounds: RectF, visible_bounds: RectF, - _: &mut Self::LayoutState, + view_is_valid: &mut Self::LayoutState, cx: &mut PaintContext, - ) -> Self::PaintState { - cx.paint(self.view.id(), bounds.origin(), visible_bounds); + ) { + if *view_is_valid { + cx.paint(self.view.id(), bounds.origin(), visible_bounds); + } else { + log::error!("paint called on a view that was not rendered"); + } } fn dispatch_event( @@ -1011,11 +1020,16 @@ impl Element for ChildView { event: &Event, _: RectF, _: RectF, - _: &mut Self::LayoutState, + view_is_valid: &mut Self::LayoutState, _: &mut Self::PaintState, cx: &mut EventContext, ) -> bool { - cx.dispatch_event(self.view.id(), event) + if *view_is_valid { + cx.dispatch_event(self.view.id(), event) + } else { + log::error!("dispatch_event called on a view that was not rendered"); + false + } } fn rect_for_text_range( @@ -1023,11 +1037,15 @@ impl Element for ChildView { range_utf16: Range, _: RectF, _: RectF, - _: &Self::LayoutState, + view_is_valid: &Self::LayoutState, _: &Self::PaintState, cx: &MeasurementContext, ) -> Option { - cx.rect_for_text_range(self.view.id(), range_utf16) + if *view_is_valid { + cx.rect_for_text_range(self.view.id(), range_utf16) + } else { + None + } } fn debug( From a5a60eb854e53a43150a5ec058e52bcf640a2570 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 13 Oct 2022 15:40:21 +0200 Subject: [PATCH 295/314] Log view name alongside error in `ChildView` --- crates/chat_panel/src/chat_panel.rs | 4 +-- crates/collab/src/integration_tests.rs | 2 +- crates/collab_ui/src/collab_titlebar_item.rs | 2 +- crates/collab_ui/src/contact_finder.rs | 4 +-- crates/collab_ui/src/contact_list.rs | 2 +- crates/collab_ui/src/contacts_popover.rs | 4 +-- crates/command_palette/src/command_palette.rs | 8 ++--- crates/diagnostics/src/diagnostics.rs | 2 +- crates/editor/src/editor.rs | 4 +-- crates/file_finder/src/file_finder.rs | 4 +-- crates/go_to_line/src/go_to_line.rs | 2 +- crates/gpui/src/app.rs | 8 +++++ crates/gpui/src/presenter.rs | 31 ++++++++++++++++--- crates/outline/src/outline.rs | 4 +-- crates/picker/src/picker.rs | 2 +- crates/project_panel/src/project_panel.rs | 6 ++-- crates/project_symbols/src/project_symbols.rs | 4 +-- crates/search/src/buffer_search.rs | 2 +- crates/search/src/project_search.rs | 6 ++-- .../terminal/src/terminal_container_view.rs | 4 +-- crates/terminal/src/terminal_view.rs | 2 +- crates/theme_selector/src/theme_selector.rs | 4 +-- crates/workspace/src/dock.rs | 6 ++-- crates/workspace/src/pane.rs | 6 ++-- crates/workspace/src/pane_group.rs | 7 ++++- crates/workspace/src/sidebar.rs | 2 +- crates/workspace/src/status_bar.rs | 4 +-- crates/workspace/src/toolbar.rs | 6 ++-- crates/workspace/src/workspace.rs | 26 ++++++++++------ 29 files changed, 105 insertions(+), 63 deletions(-) diff --git a/crates/chat_panel/src/chat_panel.rs b/crates/chat_panel/src/chat_panel.rs index 6744ae9339..60eda235ac 100644 --- a/crates/chat_panel/src/chat_panel.rs +++ b/crates/chat_panel/src/chat_panel.rs @@ -200,7 +200,7 @@ impl ChatPanel { let theme = &cx.global::().theme; Flex::column() .with_child( - Container::new(ChildView::new(&self.channel_select).boxed()) + Container::new(ChildView::new(&self.channel_select, cx).boxed()) .with_style(theme.chat_panel.channel_select.container) .boxed(), ) @@ -265,7 +265,7 @@ impl ChatPanel { fn render_input_box(&self, cx: &AppContext) -> ElementBox { let theme = &cx.global::().theme; - Container::new(ChildView::new(&self.input_editor).boxed()) + Container::new(ChildView::new(&self.input_editor, cx).boxed()) .with_style(theme.chat_panel.input_editor.container) .boxed() } diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 8ab852810b..9dddb74977 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -25,7 +25,7 @@ use gpui::{ }; use language::{ range_to_lsp, tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language, - LanguageConfig, LanguageRegistry, OffsetRangeExt, Rope, Point, + LanguageConfig, LanguageRegistry, OffsetRangeExt, Point, Rope, }; use lsp::{self, FakeLanguageServer}; use parking_lot::Mutex; diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index c78a50b86d..702d8a9121 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -223,7 +223,7 @@ impl CollabTitlebarItem { .with_children(badge) .with_children(self.contacts_popover.as_ref().map(|popover| { Overlay::new( - ChildView::new(popover) + ChildView::new(popover, cx) .contained() .with_margin_top(titlebar.height) .with_margin_left(titlebar.toggle_contacts_button.default.button_width) diff --git a/crates/collab_ui/src/contact_finder.rs b/crates/collab_ui/src/contact_finder.rs index 25726e381e..8835dc0b0e 100644 --- a/crates/collab_ui/src/contact_finder.rs +++ b/crates/collab_ui/src/contact_finder.rs @@ -32,8 +32,8 @@ impl View for ContactFinder { "ContactFinder" } - fn render(&mut self, _: &mut RenderContext) -> ElementBox { - ChildView::new(self.picker.clone()).boxed() + fn render(&mut self, cx: &mut RenderContext) -> ElementBox { + ChildView::new(self.picker.clone(), cx).boxed() } fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { diff --git a/crates/collab_ui/src/contact_list.rs b/crates/collab_ui/src/contact_list.rs index c04f0fe72d..e1d96cd1da 100644 --- a/crates/collab_ui/src/contact_list.rs +++ b/crates/collab_ui/src/contact_list.rs @@ -1072,7 +1072,7 @@ impl View for ContactList { .with_child( Flex::row() .with_child( - ChildView::new(self.filter_editor.clone()) + ChildView::new(self.filter_editor.clone(), cx) .contained() .with_style(theme.contact_list.user_query_editor.container) .flex(1., true) diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index 73bcad9fd7..075255d727 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -88,8 +88,8 @@ impl View for ContactsPopover { fn render(&mut self, cx: &mut RenderContext) -> ElementBox { let theme = cx.global::().theme.clone(); let child = match &self.child { - Child::ContactList(child) => ChildView::new(child), - Child::ContactFinder(child) => ChildView::new(child), + Child::ContactList(child) => ChildView::new(child, cx), + Child::ContactFinder(child) => ChildView::new(child, cx), }; MouseEventHandler::::new(0, cx, |_, cx| { diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index c12e68a854..125a4eeb02 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -4,8 +4,8 @@ use gpui::{ actions, elements::{ChildView, Flex, Label, ParentElement}, keymap::Keystroke, - Action, AnyViewHandle, Element, Entity, MouseState, MutableAppContext, View, ViewContext, - ViewHandle, + Action, AnyViewHandle, Element, Entity, MouseState, MutableAppContext, RenderContext, View, + ViewContext, ViewHandle, }; use picker::{Picker, PickerDelegate}; use settings::Settings; @@ -131,8 +131,8 @@ impl View for CommandPalette { "CommandPalette" } - fn render(&mut self, _: &mut gpui::RenderContext<'_, Self>) -> gpui::ElementBox { - ChildView::new(self.picker.clone()).boxed() + fn render(&mut self, cx: &mut RenderContext) -> gpui::ElementBox { + ChildView::new(self.picker.clone(), cx).boxed() } fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 3111d7a9f1..8180a6c9f6 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -95,7 +95,7 @@ impl View for ProjectDiagnosticsEditor { .with_style(theme.container) .boxed() } else { - ChildView::new(&self.editor).boxed() + ChildView::new(&self.editor, cx).boxed() } } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index a7acc9f609..2f7431a6c9 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -5206,7 +5206,7 @@ impl Editor { render: Arc::new({ let editor = rename_editor.clone(); move |cx: &mut BlockContext| { - ChildView::new(editor.clone()) + ChildView::new(editor.clone(), cx) .contained() .with_padding_left(cx.anchor_x) .boxed() @@ -6270,7 +6270,7 @@ impl View for Editor { .with_child( EditorElement::new(self.handle.clone(), style.clone(), self.cursor_shape).boxed(), ) - .with_child(ChildView::new(&self.mouse_context_menu).boxed()) + .with_child(ChildView::new(&self.mouse_context_menu, cx).boxed()) .boxed() } diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index aa2174b959..fe0fa3ae77 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -49,8 +49,8 @@ impl View for FileFinder { "FileFinder" } - fn render(&mut self, _: &mut RenderContext) -> ElementBox { - ChildView::new(self.picker.clone()).boxed() + fn render(&mut self, cx: &mut RenderContext) -> ElementBox { + ChildView::new(self.picker.clone(), cx).boxed() } fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { diff --git a/crates/go_to_line/src/go_to_line.rs b/crates/go_to_line/src/go_to_line.rs index 3ca50cee42..eddb014b63 100644 --- a/crates/go_to_line/src/go_to_line.rs +++ b/crates/go_to_line/src/go_to_line.rs @@ -165,7 +165,7 @@ impl View for GoToLine { Container::new( Flex::new(Axis::Vertical) .with_child( - Container::new(ChildView::new(&self.line_editor).boxed()) + Container::new(ChildView::new(&self.line_editor, cx).boxed()) .with_style(theme.input_editor.container) .boxed(), ) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index f7672db9ae..83936c617e 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -2571,6 +2571,10 @@ impl AppContext { .and_then(|window| window.focused_view_id) } + pub fn view_ui_name(&self, window_id: usize, view_id: usize) -> Option<&'static str> { + Some(self.views.get(&(window_id, view_id))?.ui_name()) + } + pub fn background(&self) -> &Arc { &self.background } @@ -4416,6 +4420,10 @@ impl AnyViewHandle { } } + pub fn window_id(&self) -> usize { + self.window_id + } + pub fn id(&self) -> usize { self.view_id } diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index 8cf9e99063..1d59905d7c 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -973,12 +973,16 @@ impl ToJson for SizeConstraint { pub struct ChildView { view: AnyWeakViewHandle, + view_name: &'static str, } impl ChildView { - pub fn new(view: impl Into) -> Self { + pub fn new(view: impl Into, cx: &AppContext) -> Self { + let view = view.into(); + let view_name = cx.view_ui_name(view.window_id(), view.id()).unwrap(); Self { - view: view.into().downgrade(), + view: view.downgrade(), + view_name, } } } @@ -996,7 +1000,11 @@ impl Element for ChildView { let size = cx.layout(self.view.id(), constraint); (size, true) } else { - log::error!("layout called on a view that was not rendered"); + log::error!( + "layout called on a view (id: {}, name: {:?}) that was not rendered", + self.view.id(), + self.view_name + ); (Vector2F::zero(), false) } } @@ -1011,7 +1019,11 @@ impl Element for ChildView { if *view_is_valid { cx.paint(self.view.id(), bounds.origin(), visible_bounds); } else { - log::error!("paint called on a view that was not rendered"); + log::error!( + "paint called on a view (id: {}, name: {:?}) that was not rendered", + self.view.id(), + self.view_name + ); } } @@ -1027,7 +1039,11 @@ impl Element for ChildView { if *view_is_valid { cx.dispatch_event(self.view.id(), event) } else { - log::error!("dispatch_event called on a view that was not rendered"); + log::error!( + "dispatch_event called on a view (id: {}, name: {:?}) that was not rendered", + self.view.id(), + self.view_name + ); false } } @@ -1044,6 +1060,11 @@ impl Element for ChildView { if *view_is_valid { cx.rect_for_text_range(self.view.id(), range_utf16) } else { + log::error!( + "rect_for_text_range called on a view (id: {}, name: {:?}) that was not rendered", + self.view.id(), + self.view_name + ); None } } diff --git a/crates/outline/src/outline.rs b/crates/outline/src/outline.rs index f814276306..afcfa263a8 100644 --- a/crates/outline/src/outline.rs +++ b/crates/outline/src/outline.rs @@ -48,8 +48,8 @@ impl View for OutlineView { "OutlineView" } - fn render(&mut self, _: &mut RenderContext) -> ElementBox { - ChildView::new(self.picker.clone()).boxed() + fn render(&mut self, cx: &mut RenderContext) -> ElementBox { + ChildView::new(self.picker.clone(), cx).boxed() } fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { diff --git a/crates/picker/src/picker.rs b/crates/picker/src/picker.rs index 622dc13309..a093740e25 100644 --- a/crates/picker/src/picker.rs +++ b/crates/picker/src/picker.rs @@ -63,7 +63,7 @@ impl View for Picker { Flex::new(Axis::Vertical) .with_child( - ChildView::new(&self.query_editor) + ChildView::new(&self.query_editor, cx) .contained() .with_style(theme.input_editor.container) .boxed(), diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 8ba70ee4eb..abd8f44603 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1012,7 +1012,7 @@ impl ProjectPanel { ) -> ElementBox { let kind = details.kind; let show_editor = details.is_editing && !details.is_processing; - MouseEventHandler::::new(entry_id.to_usize(), cx, |state, _| { + MouseEventHandler::::new(entry_id.to_usize(), cx, |state, cx| { let padding = theme.container.padding.left + details.depth as f32 * theme.indent_width; let mut style = theme.entry.style_for(state, details.is_selected).clone(); if details.is_ignored { @@ -1051,7 +1051,7 @@ impl ProjectPanel { .boxed(), ) .with_child(if show_editor { - ChildView::new(editor.clone()) + ChildView::new(editor.clone(), cx) .contained() .with_margin_left(theme.entry.default.icon_spacing) .aligned() @@ -1147,7 +1147,7 @@ impl View for ProjectPanel { }) .boxed(), ) - .with_child(ChildView::new(&self.context_menu).boxed()) + .with_child(ChildView::new(&self.context_menu, cx).boxed()) .boxed() } diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index c310cfb043..6aa866342a 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -47,8 +47,8 @@ impl View for ProjectSymbolsView { "ProjectSymbolsView" } - fn render(&mut self, _: &mut RenderContext) -> ElementBox { - ChildView::new(self.picker.clone()).boxed() + fn render(&mut self, cx: &mut RenderContext) -> ElementBox { + ChildView::new(self.picker.clone(), cx).boxed() } fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 6f75888f48..a43f3eb486 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -105,7 +105,7 @@ impl View for BufferSearchBar { .with_child( Flex::row() .with_child( - ChildView::new(&self.query_editor) + ChildView::new(&self.query_editor, cx) .aligned() .left() .flex(1., true) diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index d3c0743325..eb5bf7d699 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -189,7 +189,9 @@ impl View for ProjectSearchView { }) .boxed() } else { - ChildView::new(&self.results_editor).flex(1., true).boxed() + ChildView::new(&self.results_editor, cx) + .flex(1., true) + .boxed() } } @@ -824,7 +826,7 @@ impl View for ProjectSearchBar { .with_child( Flex::row() .with_child( - ChildView::new(&search.query_editor) + ChildView::new(&search.query_editor, cx) .aligned() .left() .flex(1., true) diff --git a/crates/terminal/src/terminal_container_view.rs b/crates/terminal/src/terminal_container_view.rs index e0fe6ef6cb..5cad16774d 100644 --- a/crates/terminal/src/terminal_container_view.rs +++ b/crates/terminal/src/terminal_container_view.rs @@ -162,8 +162,8 @@ impl View for TerminalContainer { fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> ElementBox { let child_view = match &self.content { - TerminalContainerContent::Connected(connected) => ChildView::new(connected), - TerminalContainerContent::Error(error) => ChildView::new(error), + TerminalContainerContent::Connected(connected) => ChildView::new(connected, cx), + TerminalContainerContent::Error(error) => ChildView::new(error, cx), }; if self.modal { let settings = cx.global::(); diff --git a/crates/terminal/src/terminal_view.rs b/crates/terminal/src/terminal_view.rs index 2742076045..675766b066 100644 --- a/crates/terminal/src/terminal_view.rs +++ b/crates/terminal/src/terminal_view.rs @@ -339,7 +339,7 @@ impl View for TerminalView { .contained() .boxed(), ) - .with_child(ChildView::new(&self.context_menu).boxed()) + .with_child(ChildView::new(&self.context_menu, cx).boxed()) .boxed() } diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index 729cdad739..a014fc8e92 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -262,8 +262,8 @@ impl View for ThemeSelector { "ThemeSelector" } - fn render(&mut self, _: &mut RenderContext) -> ElementBox { - ChildView::new(self.picker.clone()).boxed() + fn render(&mut self, cx: &mut RenderContext) -> ElementBox { + ChildView::new(self.picker.clone(), cx).boxed() } fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index dcfcc9e983..cf6c8e287b 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -255,7 +255,7 @@ impl Dock { enum DockResizeHandle {} - let resizable = Container::new(ChildView::new(self.pane.clone()).boxed()) + let resizable = Container::new(ChildView::new(self.pane.clone(), cx).boxed()) .with_style(panel_style) .with_resize_handle::( resize_side as usize, @@ -285,8 +285,8 @@ impl Dock { enum ExpandedDockPane {} Container::new( MouseEventHandler::::new(0, cx, |_state, cx| { - MouseEventHandler::::new(0, cx, |_state, _cx| { - ChildView::new(self.pane.clone()).boxed() + MouseEventHandler::::new(0, cx, |_state, cx| { + ChildView::new(&self.pane, cx).boxed() }) .capture_all() .contained() diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 7e2cc0d599..c8019e1123 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1439,8 +1439,8 @@ impl View for Pane { .flex(1., false) .named("tab bar") }) - .with_child(ChildView::new(&self.toolbar).expanded().boxed()) - .with_child(ChildView::new(active_item).flex(1., true).boxed()) + .with_child(ChildView::new(&self.toolbar, cx).expanded().boxed()) + .with_child(ChildView::new(active_item, cx).flex(1., true).boxed()) .boxed() } else { enum EmptyPane {} @@ -1480,7 +1480,7 @@ impl View for Pane { }) .boxed(), ) - .with_child(ChildView::new(&self.tab_bar_context_menu).boxed()) + .with_child(ChildView::new(&self.tab_bar_context_menu, cx).boxed()) .named("pane") } diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index f09c31741e..10fac09fff 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -222,7 +222,12 @@ impl Member { }; Stack::new() - .with_child(ChildView::new(pane).contained().with_border(border).boxed()) + .with_child( + ChildView::new(pane, cx) + .contained() + .with_border(border) + .boxed(), + ) .with_children(prompt) .boxed() } diff --git a/crates/workspace/src/sidebar.rs b/crates/workspace/src/sidebar.rs index 5cf986128a..214f227757 100644 --- a/crates/workspace/src/sidebar.rs +++ b/crates/workspace/src/sidebar.rs @@ -192,7 +192,7 @@ impl View for Sidebar { if let Some(active_item) = self.active_item() { enum ResizeHandleTag {} let style = &cx.global::().theme.workspace.sidebar; - ChildView::new(active_item.to_any()) + ChildView::new(active_item.to_any(), cx) .contained() .with_style(style.container) .with_resize_handle::( diff --git a/crates/workspace/src/status_bar.rs b/crates/workspace/src/status_bar.rs index f055168075..5261d22b6c 100644 --- a/crates/workspace/src/status_bar.rs +++ b/crates/workspace/src/status_bar.rs @@ -42,14 +42,14 @@ impl View for StatusBar { let theme = &cx.global::().theme.workspace.status_bar; Flex::row() .with_children(self.left_items.iter().map(|i| { - ChildView::new(i.as_ref()) + ChildView::new(i.as_ref(), cx) .aligned() .contained() .with_margin_right(theme.item_spacing) .boxed() })) .with_children(self.right_items.iter().rev().map(|i| { - ChildView::new(i.as_ref()) + ChildView::new(i.as_ref(), cx) .aligned() .contained() .with_margin_left(theme.item_spacing) diff --git a/crates/workspace/src/toolbar.rs b/crates/workspace/src/toolbar.rs index d1d666e031..7443f19003 100644 --- a/crates/workspace/src/toolbar.rs +++ b/crates/workspace/src/toolbar.rs @@ -67,7 +67,7 @@ impl View for Toolbar { match *position { ToolbarItemLocation::Hidden => {} ToolbarItemLocation::PrimaryLeft { flex } => { - let left_item = ChildView::new(item.as_ref()) + let left_item = ChildView::new(item.as_ref(), cx) .aligned() .contained() .with_margin_right(spacing); @@ -78,7 +78,7 @@ impl View for Toolbar { } } ToolbarItemLocation::PrimaryRight { flex } => { - let right_item = ChildView::new(item.as_ref()) + let right_item = ChildView::new(item.as_ref(), cx) .aligned() .contained() .with_margin_left(spacing) @@ -91,7 +91,7 @@ impl View for Toolbar { } ToolbarItemLocation::Secondary => { secondary_item = Some( - ChildView::new(item.as_ref()) + ChildView::new(item.as_ref(), cx) .constrained() .with_height(theme.height) .boxed(), diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 1f2847fd8f..ece8cedfb1 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -2126,7 +2126,7 @@ impl Workspace { enum TitleBar {} ConstrainedBox::new( - MouseEventHandler::::new(0, cx, |_, _| { + MouseEventHandler::::new(0, cx, |_, cx| { Container::new( Stack::new() .with_child( @@ -2138,7 +2138,7 @@ impl Workspace { .with_children( self.titlebar_item .as_ref() - .map(|item| ChildView::new(item).aligned().right().boxed()), + .map(|item| ChildView::new(item, cx).aligned().right().boxed()), ) .boxed(), ) @@ -2231,14 +2231,18 @@ impl Workspace { } } - fn render_notifications(&self, theme: &theme::Workspace) -> Option { + fn render_notifications( + &self, + theme: &theme::Workspace, + cx: &AppContext, + ) -> Option { if self.notifications.is_empty() { None } else { Some( Flex::column() .with_children(self.notifications.iter().map(|(_, _, notification)| { - ChildView::new(notification.as_ref()) + ChildView::new(notification.as_ref(), cx) .contained() .with_style(theme.notification) .boxed() @@ -2570,7 +2574,7 @@ impl View for Workspace { .with_children( if self.left_sidebar.read(cx).active_item().is_some() { Some( - ChildView::new(&self.left_sidebar) + ChildView::new(&self.left_sidebar, cx) .flex(0.8, false) .boxed(), ) @@ -2606,7 +2610,7 @@ impl View for Workspace { .with_children( if self.right_sidebar.read(cx).active_item().is_some() { Some( - ChildView::new(&self.right_sidebar) + ChildView::new(&self.right_sidebar, cx) .flex(0.8, false) .boxed(), ) @@ -2624,15 +2628,17 @@ impl View for Workspace { DockAnchor::Expanded, cx, )) - .with_children(self.modal.as_ref().map(|m| { - ChildView::new(m) + .with_children(self.modal.as_ref().map(|modal| { + ChildView::new(modal, cx) .contained() .with_style(theme.workspace.modal) .aligned() .top() .boxed() })) - .with_children(self.render_notifications(&theme.workspace)) + .with_children( + self.render_notifications(&theme.workspace, cx), + ) .boxed(), ) .boxed(), @@ -2640,7 +2646,7 @@ impl View for Workspace { .flex(1.0, true) .boxed(), ) - .with_child(ChildView::new(&self.status_bar).boxed()) + .with_child(ChildView::new(&self.status_bar, cx).boxed()) .contained() .with_background_color(theme.workspace.background) .boxed(), From 1bec8087eed53c95fb0e513217da8b4dcd3fc555 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 13 Oct 2022 15:59:43 +0200 Subject: [PATCH 296/314] Add unit test for `ChildView` --- crates/gpui/src/app.rs | 69 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 83936c617e..76aae930a1 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -7044,4 +7044,73 @@ mod tests { cx.simulate_window_activation(Some(window_3)); assert_eq!(mem::take(&mut *events.borrow_mut()), []); } + + #[crate::test(self)] + fn test_child_view(cx: &mut MutableAppContext) { + struct Child { + rendered: Rc>, + dropped: Rc>, + } + + impl super::Entity for Child { + type Event = (); + } + + impl super::View for Child { + fn ui_name() -> &'static str { + "child view" + } + + fn render(&mut self, _: &mut RenderContext) -> ElementBox { + self.rendered.set(true); + Empty::new().boxed() + } + } + + impl Drop for Child { + fn drop(&mut self) { + self.dropped.set(true); + } + } + + struct Parent { + child: Option>, + } + + impl super::Entity for Parent { + type Event = (); + } + + impl super::View for Parent { + fn ui_name() -> &'static str { + "parent view" + } + + fn render(&mut self, cx: &mut RenderContext) -> ElementBox { + if let Some(child) = self.child.as_ref() { + ChildView::new(child, cx).boxed() + } else { + Empty::new().boxed() + } + } + } + + let child_rendered = Rc::new(Cell::new(false)); + let child_dropped = Rc::new(Cell::new(false)); + let (_, root_view) = cx.add_window(Default::default(), |cx| Parent { + child: Some(cx.add_view(|_| Child { + rendered: child_rendered.clone(), + dropped: child_dropped.clone(), + })), + }); + assert!(child_rendered.take()); + assert!(!child_dropped.take()); + + root_view.update(cx, |view, cx| { + view.child.take(); + cx.notify(); + }); + assert!(!child_rendered.take()); + assert!(child_dropped.take()); + } } From 9ebd5863505dbdd85cc37e7728a01f69461acb2d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 13 Oct 2022 16:40:52 +0200 Subject: [PATCH 297/314] Improve error message when rendering a child view for a dropped view Co-Authored-By: Nathan Sobo --- crates/gpui/src/presenter.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index 1d59905d7c..4eef9c7398 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -1001,7 +1001,7 @@ impl Element for ChildView { (size, true) } else { log::error!( - "layout called on a view (id: {}, name: {:?}) that was not rendered", + "layout called on a ChildView element whose underlying view was dropped (view_id: {}, name: {:?})", self.view.id(), self.view_name ); @@ -1020,7 +1020,7 @@ impl Element for ChildView { cx.paint(self.view.id(), bounds.origin(), visible_bounds); } else { log::error!( - "paint called on a view (id: {}, name: {:?}) that was not rendered", + "paint called on a ChildView element whose underlying view was dropped (view_id: {}, name: {:?})", self.view.id(), self.view_name ); @@ -1040,7 +1040,7 @@ impl Element for ChildView { cx.dispatch_event(self.view.id(), event) } else { log::error!( - "dispatch_event called on a view (id: {}, name: {:?}) that was not rendered", + "dispatch_event called on a ChildView element whose underlying view was dropped (view_id: {}, name: {:?})", self.view.id(), self.view_name ); @@ -1061,7 +1061,7 @@ impl Element for ChildView { cx.rect_for_text_range(self.view.id(), range_utf16) } else { log::error!( - "rect_for_text_range called on a view (id: {}, name: {:?}) that was not rendered", + "rect_for_text_range called on a ChildView element whose underlying view was dropped (view_id: {}, name: {:?})", self.view.id(), self.view_name ); From b8bc5a282ec151a992bd6ce30c28e4f68af59193 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 13 Oct 2022 18:48:14 +0200 Subject: [PATCH 298/314] Allow inviting users to a project that was shared by someone else Co-Authored-By: Nathan Sobo Co-Authored-By: Max Brunsfeld --- crates/call/src/room.rs | 4 +--- crates/collab/src/integration_tests.rs | 26 +++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 5003479214..09b49716e0 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -374,9 +374,7 @@ impl Room { project: ModelHandle, cx: &mut ModelContext, ) -> Task> { - if project.read(cx).is_remote() { - return Task::ready(Err(anyhow!("can't share remote project"))); - } else if let Some(project_id) = project.read(cx).remote_id() { + if let Some(project_id) = project.read(cx).remote_id() { return Task::ready(Ok(project_id)); } diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 9dddb74977..1f8f1394ce 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -532,17 +532,20 @@ async fn test_share_project( deterministic: Arc, cx_a: &mut TestAppContext, cx_b: &mut TestAppContext, + cx_c: &mut TestAppContext, ) { deterministic.forbid_parking(); let (_, window_b) = cx_b.add_window(|_| EmptyView); let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; + let client_c = server.create_client(cx_c, "user_c").await; server - .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + .make_contacts(&mut [(&client_a, cx_a), (&client_b, cx_b), (&client_c, cx_c)]) .await; let active_call_a = cx_a.read(ActiveCall::global); let active_call_b = cx_b.read(ActiveCall::global); + let active_call_c = cx_c.read(ActiveCall::global); client_a .fs @@ -633,6 +636,27 @@ async fn test_share_project( .condition(cx_a, |buffer, _| buffer.text() == "ok, b-contents") .await; + // Client B can invite client C on a project shared by client A. + active_call_b + .update(cx_b, |call, cx| { + call.invite(client_c.user_id().unwrap(), Some(project_b.clone()), cx) + }) + .await + .unwrap(); + + let incoming_call_c = active_call_c.read_with(cx_c, |call, _| call.incoming()); + deterministic.run_until_parked(); + let call = incoming_call_c.borrow().clone().unwrap(); + assert_eq!(call.caller.github_login, "user_b"); + let initial_project = call.initial_project.unwrap(); + active_call_c + .update(cx_c, |call, cx| call.accept_incoming(cx)) + .await + .unwrap(); + let _project_c = client_c + .build_remote_project(initial_project.id, cx_c) + .await; + // TODO // // Remove the selection set as client B, see those selections disappear as client A. cx_b.update(move |_| drop(editor_b)); From cf499abf31c93bc9b3f15624fd07bf39f79d5fdd Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 13 Oct 2022 10:00:07 -0700 Subject: [PATCH 299/314] v0.60.1 --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c679fd0e19..ebc88db991 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7528,7 +7528,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.60.0" +version = "0.60.1" dependencies = [ "activity_indicator", "anyhow", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 5bfeb681a6..4fbdad8368 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] description = "The fast, collaborative code editor." edition = "2021" name = "zed" -version = "0.60.0" +version = "0.60.1" [lib] name = "zed" From 8d82702da201e71103a217a1eaa771d02d455727 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 13 Oct 2022 15:57:19 -0600 Subject: [PATCH 300/314] Pass the current view id value when painting List's mouse region Previously, a dummy value was being passed. I think this slipped in accidentally. --- crates/gpui/src/elements/list.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/gpui/src/elements/list.rs b/crates/gpui/src/elements/list.rs index a6c76cf643..7711d4497b 100644 --- a/crates/gpui/src/elements/list.rs +++ b/crates/gpui/src/elements/list.rs @@ -264,8 +264,8 @@ impl Element for List { let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default(); cx.scene.push_layer(Some(visible_bounds)); - cx.scene - .push_mouse_region(MouseRegion::new::(10, 0, bounds).on_scroll({ + cx.scene.push_mouse_region( + MouseRegion::new::(cx.current_view_id(), 0, bounds).on_scroll({ let state = self.state.clone(); let height = bounds.height(); let scroll_top = scroll_top.clone(); @@ -278,7 +278,8 @@ impl Element for List { cx, ) } - })); + }), + ); let state = &mut *self.state.0.borrow_mut(); for (mut element, origin) in state.visible_elements(bounds, scroll_top) { From eef086f60f66f7e6536c42cce1a5044dd8fb6e6b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 13 Oct 2022 16:26:26 -0600 Subject: [PATCH 301/314] 0.60.2 --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ebc88db991..203ad95fbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7528,7 +7528,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.60.1" +version = "0.60.2" dependencies = [ "activity_indicator", "anyhow", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 4fbdad8368..c3ddcb9022 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] description = "The fast, collaborative code editor." edition = "2021" name = "zed" -version = "0.60.1" +version = "0.60.2" [lib] name = "zed" From 4b12fb6b3b178c8db48e605ea2d1016837674ba8 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 14 Oct 2022 09:30:30 -0700 Subject: [PATCH 302/314] Avoid skipping over a different closing bracket in autoclose --- crates/editor/src/editor.rs | 7 ++++--- crates/editor/src/editor_tests.rs | 25 ++++++++++++++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 2f7431a6c9..07429a600d 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1938,9 +1938,10 @@ impl Editor { } } else if let Some(region) = autoclose_region { // If the selection is followed by an auto-inserted closing bracket, - // then don't insert anything else; just move the selection past the - // closing bracket. - let should_skip = selection.end == region.range.end.to_point(&snapshot); + // then don't insert that closing bracket again; just move the selection + // past the closing bracket. + let should_skip = selection.end == region.range.end.to_point(&snapshot) + && text.as_ref() == region.pair.end.as_str(); if should_skip { let anchor = snapshot.anchor_after(selection.end); new_selections.push(( diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 406969c8fe..5ad39d2bbd 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -2907,6 +2907,12 @@ async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) { close: true, newline: true, }, + BracketPair { + start: "(".to_string(), + end: ")".to_string(), + close: true, + newline: true, + }, BracketPair { start: "/*".to_string(), end: " */".to_string(), @@ -2957,6 +2963,19 @@ async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) { .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); @@ -2966,9 +2985,9 @@ async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) { }); cx.assert_editor_state( &" - 🏀{{{}}}}ˇ - ε{{{}}}}ˇ - ❤️{{{}}}}ˇ + 🏀{{{)}}}}ˇ + ε{{{)}}}}ˇ + ❤️{{{)}}}}ˇ " .unindent(), ); From ad6f9b249956cebbe09ae888d02cb8b16264b7cf Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 14 Oct 2022 09:35:57 -0700 Subject: [PATCH 303/314] 0.60.3 --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 203ad95fbf..f21a0509bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7528,7 +7528,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.60.2" +version = "0.60.3" dependencies = [ "activity_indicator", "anyhow", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index c3ddcb9022..378747fc15 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] description = "The fast, collaborative code editor." edition = "2021" name = "zed" -version = "0.60.2" +version = "0.60.3" [lib] name = "zed" From 2d3d07d4d7d217a7afafc19c97b35e8c8b5bd5f7 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 14 Oct 2022 10:17:59 -0700 Subject: [PATCH 304/314] Clear project's shared state upon every disconnection Co-authored-by: Nathan Sobo Co-authored-by: Antonio Scandurra --- crates/collab/src/integration_tests.rs | 25 +- crates/project/src/project.rs | 328 ++++++++++++------------- 2 files changed, 188 insertions(+), 165 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 1f8f1394ce..90d7b6d4b5 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -807,7 +807,7 @@ async fn test_host_disconnect( // Drop client A's connection. Collaborators should disappear and the project should not be shown as shared. server.disconnect_client(client_a.current_user_id(cx_a)); - cx_a.foreground().advance_clock(rpc::RECEIVE_TIMEOUT); + deterministic.advance_clock(rpc::RECEIVE_TIMEOUT); project_a .condition(cx_a, |project, _| project.collaborators().is_empty()) .await; @@ -829,6 +829,29 @@ async fn test_host_disconnect( .await .unwrap(); assert!(can_close); + + let active_call_b = cx_b.read(ActiveCall::global); + active_call_b + .update(cx_b, |call, cx| { + call.invite(client_a.user_id().unwrap(), None, cx) + }) + .await + .unwrap(); + deterministic.run_until_parked(); + active_call_a + .update(cx_a, |call, cx| call.accept_incoming(cx)) + .await + .unwrap(); + + active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) + .await + .unwrap(); + + // Drop client A's connection again. We should still unshare it successfully. + server.disconnect_client(client_a.current_user_id(cx_a)); + deterministic.advance_clock(rpc::RECEIVE_TIMEOUT); + project_a.read_with(cx_a, |project, _| assert!(!project.is_shared())); } #[gpui::test(iterations = 10)] diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index ea74dda434..f964726c4c 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -104,7 +104,7 @@ pub struct Project { user_store: ModelHandle, project_store: ModelHandle, fs: Arc, - client_state: ProjectClientState, + client_state: Option, collaborators: HashMap, client_subscriptions: Vec, _subscriptions: Vec, @@ -151,7 +151,7 @@ enum WorktreeHandle { enum ProjectClientState { Local { - remote_id: Option, + remote_id: u64, _detect_unshare: Task>, }, Remote { @@ -418,21 +418,6 @@ impl Project { cx: &mut MutableAppContext, ) -> ModelHandle { cx.add_model(|cx: &mut ModelContext| { - let mut status = client.status(); - let _detect_unshare = cx.spawn_weak(move |this, mut cx| { - async move { - let is_connected = status.next().await.map_or(false, |s| s.is_connected()); - // Even if we're initially connected, any future change of the status means we momentarily disconnected. - if !is_connected || status.next().await.is_some() { - if let Some(this) = this.upgrade(&cx) { - let _ = this.update(&mut cx, |this, cx| this.unshare(cx)); - } - } - Ok(()) - } - .log_err() - }); - let handle = cx.weak_handle(); project_store.update(cx, |store, cx| store.add_project(handle, cx)); @@ -445,10 +430,7 @@ impl Project { loading_buffers: Default::default(), loading_local_worktrees: Default::default(), buffer_snapshots: Default::default(), - client_state: ProjectClientState::Local { - remote_id: None, - _detect_unshare, - }, + client_state: None, opened_buffer: watch::channel(), client_subscriptions: Vec::new(), _subscriptions: vec![cx.observe_global::(Self::on_settings_changed)], @@ -522,7 +504,7 @@ impl Project { client_subscriptions: vec![client.add_model_for_remote_entity(remote_id, cx)], _subscriptions: Default::default(), client: client.clone(), - client_state: ProjectClientState::Remote { + client_state: Some(ProjectClientState::Remote { sharing_has_stopped: false, remote_id, replica_id, @@ -541,7 +523,7 @@ impl Project { } .log_err() }), - }, + }), language_servers: Default::default(), language_server_ids: Default::default(), language_server_settings: Default::default(), @@ -753,21 +735,22 @@ impl Project { } pub fn remote_id(&self) -> Option { - match &self.client_state { - ProjectClientState::Local { remote_id, .. } => *remote_id, - ProjectClientState::Remote { remote_id, .. } => Some(*remote_id), + match self.client_state.as_ref()? { + ProjectClientState::Local { remote_id, .. } + | ProjectClientState::Remote { remote_id, .. } => Some(*remote_id), } } pub fn replica_id(&self) -> ReplicaId { match &self.client_state { - ProjectClientState::Local { .. } => 0, - ProjectClientState::Remote { replica_id, .. } => *replica_id, + Some(ProjectClientState::Remote { replica_id, .. }) => *replica_id, + _ => 0, } } fn metadata_changed(&mut self, cx: &mut ModelContext) { - if let ProjectClientState::Local { remote_id, .. } = &self.client_state { + if let Some(ProjectClientState::Local { remote_id, .. }) = &self.client_state { + let project_id = *remote_id; // Broadcast worktrees only if the project is online. let worktrees = self .worktrees @@ -778,40 +761,40 @@ impl Project { .map(|worktree| worktree.read(cx).as_local().unwrap().metadata_proto()) }) .collect(); - if let Some(project_id) = *remote_id { - self.client - .send(proto::UpdateProject { - project_id, - worktrees, - }) - .log_err(); - - let worktrees = self.visible_worktrees(cx).collect::>(); - let scans_complete = - futures::future::join_all(worktrees.iter().filter_map(|worktree| { - Some(worktree.read(cx).as_local()?.scan_complete()) - })); - - let worktrees = worktrees.into_iter().map(|handle| handle.downgrade()); - cx.spawn_weak(move |_, cx| async move { - scans_complete.await; - cx.read(|cx| { - for worktree in worktrees { - if let Some(worktree) = worktree - .upgrade(cx) - .and_then(|worktree| worktree.read(cx).as_local()) - { - worktree.send_extension_counts(project_id); - } - } - }) + self.client + .send(proto::UpdateProject { + project_id, + worktrees, }) - .detach(); - } + .log_err(); - self.project_store.update(cx, |_, cx| cx.notify()); - cx.notify(); + let worktrees = self.visible_worktrees(cx).collect::>(); + let scans_complete = futures::future::join_all( + worktrees + .iter() + .filter_map(|worktree| Some(worktree.read(cx).as_local()?.scan_complete())), + ); + + let worktrees = worktrees.into_iter().map(|handle| handle.downgrade()); + + cx.spawn_weak(move |_, cx| async move { + scans_complete.await; + cx.read(|cx| { + for worktree in worktrees { + if let Some(worktree) = worktree + .upgrade(cx) + .and_then(|worktree| worktree.read(cx).as_local()) + { + worktree.send_extension_counts(project_id); + } + } + }) + }) + .detach(); } + + self.project_store.update(cx, |_, cx| cx.notify()); + cx.notify(); } pub fn collaborators(&self) -> &HashMap { @@ -1051,113 +1034,129 @@ impl Project { } pub fn shared(&mut self, project_id: u64, cx: &mut ModelContext) -> Task> { - if let ProjectClientState::Local { remote_id, .. } = &mut self.client_state { - if remote_id.is_some() { - return Task::ready(Err(anyhow!("project was already shared"))); - } - - *remote_id = Some(project_id); - - let mut worktree_share_tasks = Vec::new(); - - for open_buffer in self.opened_buffers.values_mut() { - match open_buffer { - OpenBuffer::Strong(_) => {} - OpenBuffer::Weak(buffer) => { - if let Some(buffer) = buffer.upgrade(cx) { - *open_buffer = OpenBuffer::Strong(buffer); - } - } - OpenBuffer::Operations(_) => unreachable!(), - } - } - - for worktree_handle in self.worktrees.iter_mut() { - match worktree_handle { - WorktreeHandle::Strong(_) => {} - WorktreeHandle::Weak(worktree) => { - if let Some(worktree) = worktree.upgrade(cx) { - *worktree_handle = WorktreeHandle::Strong(worktree); - } - } - } - } - - for (server_id, status) in &self.language_server_statuses { - self.client - .send(proto::StartLanguageServer { - project_id, - server: Some(proto::LanguageServer { - id: *server_id as u64, - name: status.name.clone(), - }), - }) - .log_err(); - } - - for worktree in self.worktrees(cx).collect::>() { - worktree.update(cx, |worktree, cx| { - let worktree = worktree.as_local_mut().unwrap(); - worktree_share_tasks.push(worktree.share(project_id, cx)); - }); - } - - self.client_subscriptions - .push(self.client.add_model_for_remote_entity(project_id, cx)); - self.metadata_changed(cx); - cx.emit(Event::RemoteIdChanged(Some(project_id))); - cx.notify(); - - cx.foreground().spawn(async move { - futures::future::try_join_all(worktree_share_tasks).await?; - Ok(()) - }) - } else { - Task::ready(Err(anyhow!("can't share a remote project"))) + if self.client_state.is_some() { + return Task::ready(Err(anyhow!("project was already shared"))); } + + let mut worktree_share_tasks = Vec::new(); + + for open_buffer in self.opened_buffers.values_mut() { + match open_buffer { + OpenBuffer::Strong(_) => {} + OpenBuffer::Weak(buffer) => { + if let Some(buffer) = buffer.upgrade(cx) { + *open_buffer = OpenBuffer::Strong(buffer); + } + } + OpenBuffer::Operations(_) => unreachable!(), + } + } + + for worktree_handle in self.worktrees.iter_mut() { + match worktree_handle { + WorktreeHandle::Strong(_) => {} + WorktreeHandle::Weak(worktree) => { + if let Some(worktree) = worktree.upgrade(cx) { + *worktree_handle = WorktreeHandle::Strong(worktree); + } + } + } + } + + for (server_id, status) in &self.language_server_statuses { + self.client + .send(proto::StartLanguageServer { + project_id, + server: Some(proto::LanguageServer { + id: *server_id as u64, + name: status.name.clone(), + }), + }) + .log_err(); + } + + for worktree in self.worktrees(cx).collect::>() { + worktree.update(cx, |worktree, cx| { + let worktree = worktree.as_local_mut().unwrap(); + worktree_share_tasks.push(worktree.share(project_id, cx)); + }); + } + + self.client_subscriptions + .push(self.client.add_model_for_remote_entity(project_id, cx)); + self.metadata_changed(cx); + cx.emit(Event::RemoteIdChanged(Some(project_id))); + cx.notify(); + + let mut status = self.client.status(); + self.client_state = Some(ProjectClientState::Local { + remote_id: project_id, + _detect_unshare: cx.spawn_weak(move |this, mut cx| { + async move { + let is_connected = status.next().await.map_or(false, |s| s.is_connected()); + // Even if we're initially connected, any future change of the status means we momentarily disconnected. + if !is_connected || status.next().await.is_some() { + if let Some(this) = this.upgrade(&cx) { + let _ = this.update(&mut cx, |this, cx| this.unshare(cx)); + } + } + Ok(()) + } + .log_err() + }), + }); + + cx.foreground().spawn(async move { + futures::future::try_join_all(worktree_share_tasks).await?; + Ok(()) + }) } pub fn unshare(&mut self, cx: &mut ModelContext) -> Result<()> { - if let ProjectClientState::Local { remote_id, .. } = &mut self.client_state { - if let Some(project_id) = remote_id.take() { - self.collaborators.clear(); - self.shared_buffers.clear(); - self.client_subscriptions.clear(); + if self.is_remote() { + return Err(anyhow!("attempted to unshare a remote project")); + } - for worktree_handle in self.worktrees.iter_mut() { - if let WorktreeHandle::Strong(worktree) = worktree_handle { - let is_visible = worktree.update(cx, |worktree, _| { - worktree.as_local_mut().unwrap().unshare(); - worktree.is_visible() - }); - if !is_visible { - *worktree_handle = WorktreeHandle::Weak(worktree.downgrade()); - } + if let Some(ProjectClientState::Local { remote_id, .. }) = self.client_state.take() { + self.collaborators.clear(); + self.shared_buffers.clear(); + self.client_subscriptions.clear(); + + for worktree_handle in self.worktrees.iter_mut() { + if let WorktreeHandle::Strong(worktree) = worktree_handle { + let is_visible = worktree.update(cx, |worktree, _| { + worktree.as_local_mut().unwrap().unshare(); + worktree.is_visible() + }); + if !is_visible { + *worktree_handle = WorktreeHandle::Weak(worktree.downgrade()); } } - - for open_buffer in self.opened_buffers.values_mut() { - if let OpenBuffer::Strong(buffer) = open_buffer { - *open_buffer = OpenBuffer::Weak(buffer.downgrade()); - } - } - - self.metadata_changed(cx); - cx.notify(); - self.client.send(proto::UnshareProject { project_id })?; } + for open_buffer in self.opened_buffers.values_mut() { + if let OpenBuffer::Strong(buffer) = open_buffer { + *open_buffer = OpenBuffer::Weak(buffer.downgrade()); + } + } + + self.metadata_changed(cx); + cx.notify(); + self.client.send(proto::UnshareProject { + project_id: remote_id, + })?; + Ok(()) } else { - Err(anyhow!("attempted to unshare a remote project")) + Err(anyhow!("attempted to unshare an unshared project")) } } fn disconnected_from_host(&mut self, cx: &mut ModelContext) { - if let ProjectClientState::Remote { + if let Some(ProjectClientState::Remote { sharing_has_stopped, .. - } = &mut self.client_state + }) = &mut self.client_state { *sharing_has_stopped = true; self.collaborators.clear(); @@ -1181,18 +1180,18 @@ impl Project { pub fn is_read_only(&self) -> bool { match &self.client_state { - ProjectClientState::Local { .. } => false, - ProjectClientState::Remote { + Some(ProjectClientState::Remote { sharing_has_stopped, .. - } => *sharing_has_stopped, + }) => *sharing_has_stopped, + _ => false, } } pub fn is_local(&self) -> bool { match &self.client_state { - ProjectClientState::Local { .. } => true, - ProjectClientState::Remote { .. } => false, + Some(ProjectClientState::Remote { .. }) => false, + _ => true, } } @@ -4165,8 +4164,8 @@ impl Project { pub fn is_shared(&self) -> bool { match &self.client_state { - ProjectClientState::Local { remote_id, .. } => remote_id.is_some(), - ProjectClientState::Remote { .. } => false, + Some(ProjectClientState::Local { .. }) => true, + _ => false, } } @@ -5958,20 +5957,21 @@ impl Entity for Project { self.project_store.update(cx, ProjectStore::prune_projects); match &self.client_state { - ProjectClientState::Local { remote_id, .. } => { - if let Some(project_id) = *remote_id { - self.client - .send(proto::UnshareProject { project_id }) - .log_err(); - } + Some(ProjectClientState::Local { remote_id, .. }) => { + self.client + .send(proto::UnshareProject { + project_id: *remote_id, + }) + .log_err(); } - ProjectClientState::Remote { remote_id, .. } => { + Some(ProjectClientState::Remote { remote_id, .. }) => { self.client .send(proto::LeaveProject { project_id: *remote_id, }) .log_err(); } + _ => {} } } From 864020463f4850decebd2eaf22ba8eaddbc10097 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 14 Oct 2022 10:37:42 -0700 Subject: [PATCH 305/314] Consolidate calculation of editor's visible row range We think this will fix a panic that was occuring in `paint_highlighted_range` due to an out-of-bounds read into the line layouts. We think doing essentially the same calculation in two different ways with floating point numbers might have caused a different end row to be calculated in 2 different code paths. Co-authored-by: Nathan Sobo --- crates/editor/src/element.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 912cea8c5f..afc9bdd494 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -675,10 +675,8 @@ impl EditorElement { let style = &self.style; let local_replica_id = view.replica_id(cx); let scroll_position = layout.position_map.snapshot.scroll_position(); - let start_row = scroll_position.y() as u32; + let start_row = layout.visible_display_row_range.start; let scroll_top = scroll_position.y() * layout.position_map.line_height; - let end_row = - ((scroll_top + bounds.height()) / layout.position_map.line_height).ceil() as u32 + 1; // Add 1 to ensure selections bleed off screen let max_glyph_width = layout.position_map.em_width; let scroll_left = scroll_position.x() * max_glyph_width; let content_origin = bounds.origin() + vec2f(layout.gutter_margin, 0.); @@ -697,8 +695,6 @@ impl EditorElement { for (range, color) in &layout.highlighted_ranges { self.paint_highlighted_range( range.clone(), - start_row, - end_row, *color, 0., 0.15 * layout.position_map.line_height, @@ -719,8 +715,6 @@ impl EditorElement { for selection in selections { self.paint_highlighted_range( selection.range.clone(), - start_row, - end_row, selection_style.selection, corner_radius, corner_radius * 2., @@ -734,7 +728,10 @@ impl EditorElement { if view.show_local_cursors() || *replica_id != local_replica_id { let cursor_position = selection.head; - if (start_row..end_row).contains(&cursor_position.row()) { + if layout + .visible_display_row_range + .contains(&cursor_position.row()) + { let cursor_row_layout = &layout.position_map.line_layouts [(cursor_position.row() - start_row) as usize]; let cursor_column = cursor_position.column() as usize; @@ -1025,8 +1022,6 @@ impl EditorElement { fn paint_highlighted_range( &self, range: Range, - start_row: u32, - end_row: u32, color: Color, corner_radius: f32, line_end_overshoot: f32, @@ -1037,6 +1032,8 @@ impl EditorElement { bounds: RectF, cx: &mut PaintContext, ) { + let start_row = layout.visible_display_row_range.start; + let end_row = layout.visible_display_row_range.end; if range.start != range.end { let row_range = if range.end.column() == 0 { cmp::max(range.start.row(), start_row)..cmp::min(range.end.row(), end_row) @@ -1826,6 +1823,7 @@ impl Element for EditorElement { em_advance, snapshot, }), + visible_display_row_range: start_row..end_row, gutter_size, gutter_padding, text_size, @@ -1971,6 +1969,7 @@ pub struct LayoutState { gutter_margin: f32, text_size: Vector2F, mode: EditorMode, + visible_display_row_range: Range, active_rows: BTreeMap, highlighted_rows: Option>, line_number_layouts: Vec>, From 137a9cefbdb733a1922fc7ad9cafa3de7c987ee9 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 14 Oct 2022 11:32:22 -0700 Subject: [PATCH 306/314] Enable auto-scroll when moving cursors in Editor::handle_input Co-authored-by: Mikayla Maki --- crates/editor/src/editor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 07429a600d..014787b503 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2024,7 +2024,7 @@ impl Editor { } drop(snapshot); - this.change_selections(None, cx, |s| s.select(new_selections)); + this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(new_selections)); this.trigger_completion_on_input(&text, cx); }); } From 8df84e0341e4fb4386e3ff12f2fb9def5055928d Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 14 Oct 2022 12:36:46 -0700 Subject: [PATCH 307/314] Add MovePageUp and MovePageDown editor commands Co-authored-by: Mikayla Maki --- crates/editor/src/editor.rs | 57 ++++++++++++ crates/editor/src/editor_tests.rs | 114 ++++++++++++++++++++++++ crates/editor/src/movement.rs | 28 +++++- crates/gpui/src/app/test_app_context.rs | 20 ++++- crates/gpui/src/platform/test.rs | 4 +- 5 files changed, 213 insertions(+), 10 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 014787b503..d453d26d0e 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -108,6 +108,18 @@ pub struct SelectToBeginningOfLine { stop_at_soft_wraps: bool, } +#[derive(Clone, Default, Deserialize, PartialEq)] +pub struct MovePageUp { + #[serde(default)] + center_cursor: bool, +} + +#[derive(Clone, Default, Deserialize, PartialEq)] +pub struct MovePageDown { + #[serde(default)] + center_cursor: bool, +} + #[derive(Clone, Deserialize, PartialEq)] pub struct SelectToEndOfLine { #[serde(default)] @@ -222,6 +234,8 @@ impl_actions!( SelectToBeginningOfLine, SelectToEndOfLine, ToggleCodeActions, + MovePageUp, + MovePageDown, ConfirmCompletion, ConfirmCodeAction, ] @@ -5536,6 +5550,49 @@ impl Editor { } } + // Vscode style + emacs style + pub fn move_page_down(&mut self, _: &MovePageDown, cx: &mut ViewContext) { + let row_count = match self.visible_line_count { + Some(row_count) => row_count as u32 - 1, + None => return, + }; + + self.change_selections(Some(Autoscroll::Fit), cx, |s| { + let line_mode = s.line_mode; + s.move_with(|map, selection| { + if !selection.is_empty() && !line_mode { + selection.goal = SelectionGoal::None; + } + let (cursor, goal) = + movement::down_by_rows(map, selection.end, row_count, selection.goal, false); + eprintln!( + "{:?} down by rows {} = {:?}", + selection.end, row_count, cursor + ); + selection.collapse_to(cursor, goal); + }); + }); + } + + pub fn move_page_up(&mut self, _: &MovePageUp, cx: &mut ViewContext) { + let row_count = match self.visible_line_count { + Some(row_count) => row_count as u32 - 1, + None => return, + }; + + self.change_selections(Some(Autoscroll::Fit), cx, |s| { + let line_mode = s.line_mode; + s.move_with(|map, selection| { + if !selection.is_empty() && !line_mode { + selection.goal = SelectionGoal::None; + } + let (cursor, goal) = + movement::up_by_rows(map, selection.end, row_count, selection.goal, false); + selection.collapse_to(cursor, goal); + }); + }); + } + pub fn page_up(&mut self, _: &PageUp, cx: &mut ViewContext) { let lines = match self.visible_line_count { Some(lines) => lines, diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 5ad39d2bbd..46fbfa4fb3 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -1194,6 +1194,120 @@ fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) { }); } +#[gpui::test] +async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) { + let mut cx = EditorTestContext::new(cx); + + let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache())); + cx.simulate_window_resize(cx.window_id, vec2f(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 + tenx + "# + .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) { let mut cx = EditorTestContext::new(cx); diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index 40908d2bb3..96b2065823 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -29,6 +29,25 @@ pub fn up( start: DisplayPoint, goal: SelectionGoal, preserve_column_at_start: bool, +) -> (DisplayPoint, SelectionGoal) { + up_by_rows(map, start, 1, goal, preserve_column_at_start) +} + +pub fn down( + map: &DisplaySnapshot, + start: DisplayPoint, + goal: SelectionGoal, + preserve_column_at_end: bool, +) -> (DisplayPoint, SelectionGoal) { + down_by_rows(map, start, 1, goal, preserve_column_at_end) +} + +pub fn up_by_rows( + map: &DisplaySnapshot, + start: DisplayPoint, + row_count: u32, + goal: SelectionGoal, + preserve_column_at_start: bool, ) -> (DisplayPoint, SelectionGoal) { let mut goal_column = if let SelectionGoal::Column(column) = goal { column @@ -36,7 +55,7 @@ pub fn up( map.column_to_chars(start.row(), start.column()) }; - let prev_row = start.row().saturating_sub(1); + let prev_row = start.row().saturating_sub(row_count); let mut point = map.clip_point( DisplayPoint::new(prev_row, map.line_len(prev_row)), Bias::Left, @@ -62,9 +81,10 @@ pub fn up( ) } -pub fn down( +pub fn down_by_rows( map: &DisplaySnapshot, start: DisplayPoint, + row_count: u32, goal: SelectionGoal, preserve_column_at_end: bool, ) -> (DisplayPoint, SelectionGoal) { @@ -74,8 +94,8 @@ pub fn down( map.column_to_chars(start.row(), start.column()) }; - let next_row = start.row() + 1; - let mut point = map.clip_point(DisplayPoint::new(next_row, 0), Bias::Right); + let new_row = start.row() + row_count; + let mut point = map.clip_point(DisplayPoint::new(new_row, 0), Bias::Right); if point.row() > start.row() { *point.column_mut() = map.column_from_chars(point.row(), goal_column); } else if preserve_column_at_end { diff --git a/crates/gpui/src/app/test_app_context.rs b/crates/gpui/src/app/test_app_context.rs index 477c316f71..72f1f546fb 100644 --- a/crates/gpui/src/app/test_app_context.rs +++ b/crates/gpui/src/app/test_app_context.rs @@ -17,10 +17,11 @@ use parking_lot::{Mutex, RwLock}; use smol::stream::StreamExt; use crate::{ - executor, keymap::Keystroke, platform, Action, AnyViewHandle, AppContext, Appearance, Entity, - Event, FontCache, InputHandler, KeyDownEvent, LeakDetector, ModelContext, ModelHandle, - MutableAppContext, Platform, ReadModelWith, ReadViewWith, RenderContext, Task, UpdateModel, - UpdateView, View, ViewContext, ViewHandle, WeakHandle, WindowInputHandler, + executor, geometry::vector::Vector2F, keymap::Keystroke, platform, Action, AnyViewHandle, + AppContext, Appearance, Entity, Event, FontCache, InputHandler, KeyDownEvent, LeakDetector, + ModelContext, ModelHandle, MutableAppContext, Platform, ReadModelWith, ReadViewWith, + RenderContext, Task, UpdateModel, UpdateView, View, ViewContext, ViewHandle, WeakHandle, + WindowInputHandler, }; use collections::BTreeMap; @@ -275,6 +276,17 @@ impl TestAppContext { } } + pub fn simulate_window_resize(&self, window_id: usize, size: Vector2F) { + let mut window = self.window_mut(window_id); + window.size = size; + let mut handlers = mem::take(&mut window.resize_handlers); + drop(window); + for handler in &mut handlers { + handler(); + } + self.window_mut(window_id).resize_handlers = handlers; + } + pub fn simulate_window_activation(&self, to_activate: Option) { let mut handlers = BTreeMap::new(); { diff --git a/crates/gpui/src/platform/test.rs b/crates/gpui/src/platform/test.rs index 3c2e23bbd3..2a44616cdd 100644 --- a/crates/gpui/src/platform/test.rs +++ b/crates/gpui/src/platform/test.rs @@ -34,11 +34,11 @@ pub struct ForegroundPlatform { struct Dispatcher; pub struct Window { - size: Vector2F, + pub(crate) size: Vector2F, scale_factor: f32, current_scene: Option, event_handlers: Vec bool>>, - resize_handlers: Vec>, + pub(crate) resize_handlers: Vec>, close_handlers: Vec>, fullscreen_handlers: Vec>, pub(crate) active_status_change_handlers: Vec>, From 8044beffc7eb9ef8deb24d786d4996b920730bca Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 14 Oct 2022 12:44:22 -0700 Subject: [PATCH 308/314] v0.60.4 --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f21a0509bb..9ad40dc2e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7528,7 +7528,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.60.3" +version = "0.60.4" dependencies = [ "activity_indicator", "anyhow", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 378747fc15..abf4c21871 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] description = "The fast, collaborative code editor." edition = "2021" name = "zed" -version = "0.60.3" +version = "0.60.4" [lib] name = "zed" From d301a215f7f7d7ed940c8a3e31bc31ba2788998c Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 14 Oct 2022 13:52:30 -0700 Subject: [PATCH 309/314] Finished implementing vscode, emacs, and mac style pageup/down. Added keybindings ctrl-v, alt-v for emacs up/down and shift-pageup, shift-pagedown for vscode style. Also improved incorporated pageup/down into context menus --- assets/keymaps/default.json | 31 ++- crates/editor/src/editor.rs | 272 ++++++++++++++++++++------- crates/editor/src/editor_tests.rs | 2 +- crates/terminal/src/terminal_view.rs | 4 +- 4 files changed, 235 insertions(+), 74 deletions(-) diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index 841d9ab7c8..40d486c161 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -3,8 +3,12 @@ { "bindings": { "up": "menu::SelectPrev", + "pageup": "menu::SelectFirst", + "shift-pageup": "menu::SelectFirst", "ctrl-p": "menu::SelectPrev", "down": "menu::SelectNext", + "pagedown": "menu::SelectLast", + "shift-pagedown": "menu::SelectFirst", "ctrl-n": "menu::SelectNext", "cmd-up": "menu::SelectFirst", "cmd-down": "menu::SelectLast", @@ -60,13 +64,18 @@ "cmd-z": "editor::Undo", "cmd-shift-z": "editor::Redo", "up": "editor::MoveUp", + "pageup": "editor::PageUp", + "shift-pageup": "editor::MovePageUp", "down": "editor::MoveDown", + "pagedown": "editor::PageDown", + "shift-pagedown": "editor::MovePageDown", "left": "editor::MoveLeft", "right": "editor::MoveRight", "ctrl-p": "editor::MoveUp", "ctrl-n": "editor::MoveDown", "ctrl-b": "editor::MoveLeft", "ctrl-f": "editor::MoveRight", + "ctrl-l": "editor::CenterScreen", "alt-left": "editor::MoveToPreviousWordStart", "alt-b": "editor::MoveToPreviousWordStart", "alt-right": "editor::MoveToNextWordEnd", @@ -118,8 +127,18 @@ "stop_at_soft_wraps": true } ], - "pageup": "editor::PageUp", - "pagedown": "editor::PageDown", + "ctrl-v": [ + "editor::MovePageDown", + { + "center_cursor": true + } + ], + "alt-v": [ + "editor::MovePageUp", + { + "center_cursor": true + } + ], "ctrl-cmd-space": "editor::ShowCharacterPalette" } }, @@ -451,10 +470,18 @@ "terminal::SendKeystroke", "up" ], + "pageup": [ + "terminal::SendKeystroke", + "pageup" + ], "down": [ "terminal::SendKeystroke", "down" ], + "pagedown": [ + "terminal::SendKeystroke", + "pagedown" + ], "escape": [ "terminal::SendKeystroke", "escape" diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index d453d26d0e..00436b88d7 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -173,8 +173,11 @@ actions!( Paste, Undo, Redo, + CenterScreen, MoveUp, + PageUp, MoveDown, + PageDown, MoveLeft, MoveRight, MoveToPreviousWordStart, @@ -214,8 +217,6 @@ actions!( FindAllReferences, Rename, ConfirmRename, - PageUp, - PageDown, Fold, UnfoldLines, FoldSelectedRanges, @@ -287,7 +288,12 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action(Editor::undo); cx.add_action(Editor::redo); cx.add_action(Editor::move_up); + cx.add_action(Editor::move_page_up); + cx.add_action(Editor::page_up); cx.add_action(Editor::move_down); + cx.add_action(Editor::move_page_down); + cx.add_action(Editor::page_down); + cx.add_action(Editor::center_screen); cx.add_action(Editor::move_left); cx.add_action(Editor::move_right); cx.add_action(Editor::move_to_previous_word_start); @@ -326,8 +332,6 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action(Editor::go_to_prev_diagnostic); cx.add_action(Editor::go_to_definition); cx.add_action(Editor::go_to_type_definition); - cx.add_action(Editor::page_up); - cx.add_action(Editor::page_down); cx.add_action(Editor::fold); cx.add_action(Editor::unfold_lines); cx.add_action(Editor::fold_selected_ranges); @@ -620,6 +624,18 @@ enum ContextMenu { } impl ContextMenu { + fn select_first(&mut self, cx: &mut ViewContext) -> bool { + if self.visible() { + match self { + ContextMenu::Completions(menu) => menu.select_first(cx), + ContextMenu::CodeActions(menu) => menu.select_first(cx), + } + true + } else { + false + } + } + fn select_prev(&mut self, cx: &mut ViewContext) -> bool { if self.visible() { match self { @@ -644,6 +660,18 @@ impl ContextMenu { } } + fn select_last(&mut self, cx: &mut ViewContext) -> bool { + if self.visible() { + match self { + ContextMenu::Completions(menu) => menu.select_last(cx), + ContextMenu::CodeActions(menu) => menu.select_last(cx), + } + true + } else { + false + } + } + fn visible(&self) -> bool { match self { ContextMenu::Completions(menu) => menu.visible(), @@ -676,6 +704,12 @@ struct CompletionsMenu { } impl CompletionsMenu { + fn select_first(&mut self, cx: &mut ViewContext) { + self.selected_item = 0; + self.list.scroll_to(ScrollTarget::Show(self.selected_item)); + cx.notify(); + } + fn select_prev(&mut self, cx: &mut ViewContext) { if self.selected_item > 0 { self.selected_item -= 1; @@ -692,6 +726,12 @@ impl CompletionsMenu { cx.notify(); } + fn select_last(&mut self, cx: &mut ViewContext) { + self.selected_item = self.matches.len() - 1; + self.list.scroll_to(ScrollTarget::Show(self.selected_item)); + cx.notify(); + } + fn visible(&self) -> bool { !self.matches.is_empty() } @@ -823,6 +863,11 @@ struct CodeActionsMenu { } impl CodeActionsMenu { + fn select_first(&mut self, cx: &mut ViewContext) { + self.selected_item = 0; + cx.notify() + } + fn select_prev(&mut self, cx: &mut ViewContext) { if self.selected_item > 0 { self.selected_item -= 1; @@ -837,6 +882,11 @@ impl CodeActionsMenu { } } + fn select_last(&mut self, cx: &mut ViewContext) { + self.selected_item = self.actions.len() - 1; + cx.notify() + } + fn visible(&self) -> bool { !self.actions.is_empty() } @@ -3863,6 +3913,23 @@ impl Editor { }) } + pub fn center_screen(&mut self, _: &CenterScreen, cx: &mut ViewContext) { + if self.take_rename(true, cx).is_some() { + return; + } + + if let Some(_) = self.context_menu.as_mut() { + return; + } + + if matches!(self.mode, EditorMode::SingleLine) { + cx.propagate_action(); + return; + } + + self.request_autoscroll(Autoscroll::Center, cx); + } + pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext) { if self.take_rename(true, cx).is_some() { return; @@ -3891,6 +3958,72 @@ impl Editor { }) } + pub fn move_page_up(&mut self, action: &MovePageUp, cx: &mut ViewContext) { + if self.take_rename(true, cx).is_some() { + return; + } + + if let Some(context_menu) = self.context_menu.as_mut() { + if context_menu.select_first(cx) { + return; + } + } + + if matches!(self.mode, EditorMode::SingleLine) { + cx.propagate_action(); + return; + } + + let row_count = match self.visible_line_count { + Some(row_count) => row_count as u32 - 1, + None => return, + }; + + let autoscroll = if action.center_cursor { + Autoscroll::Center + } else { + Autoscroll::Fit + }; + + self.change_selections(Some(autoscroll), cx, |s| { + let line_mode = s.line_mode; + s.move_with(|map, selection| { + if !selection.is_empty() && !line_mode { + selection.goal = SelectionGoal::None; + } + let (cursor, goal) = + movement::up_by_rows(map, selection.end, row_count, selection.goal, false); + selection.collapse_to(cursor, goal); + }); + }); + } + + pub fn page_up(&mut self, _: &PageUp, cx: &mut ViewContext) { + if self.take_rename(true, cx).is_some() { + return; + } + + if let Some(context_menu) = self.context_menu.as_mut() { + if context_menu.select_first(cx) { + return; + } + } + + if matches!(self.mode, EditorMode::SingleLine) { + cx.propagate_action(); + return; + } + + let lines = match self.visible_line_count { + Some(lines) => lines, + None => return, + }; + + let cur_position = self.scroll_position(cx); + let new_pos = cur_position - vec2f(0., lines + 1.); + self.set_scroll_position(new_pos, cx); + } + pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext) { self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false)) @@ -3923,6 +4056,72 @@ impl Editor { }); } + pub fn move_page_down(&mut self, action: &MovePageDown, cx: &mut ViewContext) { + if self.take_rename(true, cx).is_some() { + return; + } + + if let Some(context_menu) = self.context_menu.as_mut() { + if context_menu.select_last(cx) { + return; + } + } + + if matches!(self.mode, EditorMode::SingleLine) { + cx.propagate_action(); + return; + } + + let row_count = match self.visible_line_count { + Some(row_count) => row_count as u32 - 1, + None => return, + }; + + let autoscroll = if action.center_cursor { + Autoscroll::Center + } else { + Autoscroll::Fit + }; + + self.change_selections(Some(autoscroll), cx, |s| { + let line_mode = s.line_mode; + s.move_with(|map, selection| { + if !selection.is_empty() && !line_mode { + selection.goal = SelectionGoal::None; + } + let (cursor, goal) = + movement::down_by_rows(map, selection.end, row_count, selection.goal, false); + selection.collapse_to(cursor, goal); + }); + }); + } + + pub fn page_down(&mut self, _: &PageDown, cx: &mut ViewContext) { + if self.take_rename(true, cx).is_some() { + return; + } + + if let Some(context_menu) = self.context_menu.as_mut() { + if context_menu.select_last(cx) { + return; + } + } + + if matches!(self.mode, EditorMode::SingleLine) { + cx.propagate_action(); + return; + } + + let lines = match self.visible_line_count { + Some(lines) => lines, + None => return, + }; + + let cur_position = self.scroll_position(cx); + let new_pos = cur_position + vec2f(0., lines - 1.); + self.set_scroll_position(new_pos, cx); + } + pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext) { self.change_selections(Some(Autoscroll::Fit), cx, |s| { s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false)) @@ -5550,71 +5749,6 @@ impl Editor { } } - // Vscode style + emacs style - pub fn move_page_down(&mut self, _: &MovePageDown, cx: &mut ViewContext) { - let row_count = match self.visible_line_count { - Some(row_count) => row_count as u32 - 1, - None => return, - }; - - self.change_selections(Some(Autoscroll::Fit), cx, |s| { - let line_mode = s.line_mode; - s.move_with(|map, selection| { - if !selection.is_empty() && !line_mode { - selection.goal = SelectionGoal::None; - } - let (cursor, goal) = - movement::down_by_rows(map, selection.end, row_count, selection.goal, false); - eprintln!( - "{:?} down by rows {} = {:?}", - selection.end, row_count, cursor - ); - selection.collapse_to(cursor, goal); - }); - }); - } - - pub fn move_page_up(&mut self, _: &MovePageUp, cx: &mut ViewContext) { - let row_count = match self.visible_line_count { - Some(row_count) => row_count as u32 - 1, - None => return, - }; - - self.change_selections(Some(Autoscroll::Fit), cx, |s| { - let line_mode = s.line_mode; - s.move_with(|map, selection| { - if !selection.is_empty() && !line_mode { - selection.goal = SelectionGoal::None; - } - let (cursor, goal) = - movement::up_by_rows(map, selection.end, row_count, selection.goal, false); - selection.collapse_to(cursor, goal); - }); - }); - } - - pub fn page_up(&mut self, _: &PageUp, cx: &mut ViewContext) { - let lines = match self.visible_line_count { - Some(lines) => lines, - None => return, - }; - - let cur_position = self.scroll_position(cx); - let new_pos = cur_position - vec2f(0., lines + 1.); - self.set_scroll_position(new_pos, cx); - } - - pub fn page_down(&mut self, _: &PageDown, cx: &mut ViewContext) { - let lines = match self.visible_line_count { - Some(lines) => lines, - None => return, - }; - - let cur_position = self.scroll_position(cx); - let new_pos = cur_position + vec2f(0., lines - 1.); - self.set_scroll_position(new_pos, cx); - } - pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext) { let mut fold_ranges = Vec::new(); diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 46fbfa4fb3..58978c51f0 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -1229,7 +1229,7 @@ async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) { seven eight nine - tenx + ten "# .unindent(), ); diff --git a/crates/terminal/src/terminal_view.rs b/crates/terminal/src/terminal_view.rs index 675766b066..732c0a717e 100644 --- a/crates/terminal/src/terminal_view.rs +++ b/crates/terminal/src/terminal_view.rs @@ -142,8 +142,8 @@ impl TerminalView { pub fn deploy_context_menu(&mut self, action: &DeployContextMenu, cx: &mut ViewContext) { let menu_entries = vec![ - ContextMenuItem::item("Clear Buffer", Clear), - ContextMenuItem::item("Close Terminal", pane::CloseActiveItem), + ContextMenuItem::item("Clear", Clear), + ContextMenuItem::item("Close", pane::CloseActiveItem), ]; self.context_menu.update(cx, |menu, cx| { From 3a4e802093f12306ecd1e7ff8585d5a5606b80a8 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 14 Oct 2022 15:20:23 -0700 Subject: [PATCH 310/314] Include waitlist entries w/ unknown platform when summarizing and sending invites --- crates/collab/src/api.rs | 4 ++-- crates/collab/src/db.rs | 40 +++++++++++++++++++++++++++-------- crates/collab/src/db_tests.rs | 29 ++++++++++++++++--------- 3 files changed, 52 insertions(+), 21 deletions(-) diff --git a/crates/collab/src/api.rs b/crates/collab/src/api.rs index 08dfa91ba9..2d09bf3aea 100644 --- a/crates/collab/src/api.rs +++ b/crates/collab/src/api.rs @@ -1,6 +1,6 @@ use crate::{ auth, - db::{Invite, NewUserParams, ProjectId, Signup, User, UserId, WaitlistSummary}, + db::{Invite, NewUserParams, ProjectId, Signup, UnsentInvite, User, UserId, WaitlistSummary}, rpc::{self, ResultExt}, AppState, Error, Result, }; @@ -471,7 +471,7 @@ pub struct GetUnsentInvitesParams { async fn get_unsent_invites( Query(params): Query, Extension(app): Extension>, -) -> Result>> { +) -> Result>> { Ok(Json(app.db.get_unsent_invites(params.count).await?)) } diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs index 4f49c117fd..f241202b22 100644 --- a/crates/collab/src/db.rs +++ b/crates/collab/src/db.rs @@ -45,7 +45,7 @@ pub trait Db: Send + Sync { async fn create_signup(&self, signup: Signup) -> Result<()>; async fn get_waitlist_summary(&self) -> Result; - async fn get_unsent_invites(&self, count: usize) -> Result>; + async fn get_unsent_invites(&self, count: usize) -> Result>; async fn record_sent_invites(&self, invites: &[Invite]) -> Result<()>; async fn create_user_from_invite( &self, @@ -428,7 +428,8 @@ impl Db for PostgresDb { COUNT(*) as count, COALESCE(SUM(CASE WHEN platform_linux THEN 1 ELSE 0 END), 0) as linux_count, COALESCE(SUM(CASE WHEN platform_mac THEN 1 ELSE 0 END), 0) as mac_count, - COALESCE(SUM(CASE WHEN platform_windows THEN 1 ELSE 0 END), 0) as windows_count + COALESCE(SUM(CASE WHEN platform_windows THEN 1 ELSE 0 END), 0) as windows_count, + COALESCE(SUM(CASE WHEN platform_unknown THEN 1 ELSE 0 END), 0) as unknown_count FROM ( SELECT * FROM signups @@ -441,21 +442,33 @@ impl Db for PostgresDb { .await?) } - async fn get_unsent_invites(&self, count: usize) -> Result> { - Ok(sqlx::query_as( + async fn get_unsent_invites(&self, count: usize) -> Result> { + let rows: Vec<(String, String, bool)> = sqlx::query_as( " SELECT - email_address, email_confirmation_code + email_address, email_confirmation_code, platform_unknown FROM signups WHERE NOT email_confirmation_sent AND - platform_mac + (platform_mac OR platform_unknown) LIMIT $1 ", ) .bind(count as i32) .fetch_all(&self.pool) - .await?) + .await?; + Ok(rows + .into_iter() + .map( + |(email_address, email_confirmation_code, platform_unknown)| UnsentInvite { + invite: Invite { + email_address, + email_confirmation_code, + }, + platform_unknown, + }, + ) + .collect()) } async fn record_sent_invites(&self, invites: &[Invite]) -> Result<()> { @@ -1720,14 +1733,23 @@ pub struct WaitlistSummary { pub mac_count: i64, #[sqlx(default)] pub windows_count: i64, + #[sqlx(default)] + pub unknown_count: i64, } -#[derive(FromRow, PartialEq, Debug, Serialize, Deserialize)] +#[derive(Clone, FromRow, PartialEq, Debug, Serialize, Deserialize)] pub struct Invite { pub email_address: String, pub email_confirmation_code: String, } +#[derive(Serialize, Debug, PartialEq)] +pub struct UnsentInvite { + #[serde(flatten)] + pub invite: Invite, + pub platform_unknown: bool, +} + #[derive(Debug, Serialize, Deserialize)] pub struct NewUserParams { pub github_login: String, @@ -1943,7 +1965,7 @@ mod test { unimplemented!() } - async fn get_unsent_invites(&self, _count: usize) -> Result> { + async fn get_unsent_invites(&self, _count: usize) -> Result> { unimplemented!() } diff --git a/crates/collab/src/db_tests.rs b/crates/collab/src/db_tests.rs index 477dcd4ab8..b48a45018b 100644 --- a/crates/collab/src/db_tests.rs +++ b/crates/collab/src/db_tests.rs @@ -1022,6 +1022,7 @@ async fn test_signups() { mac_count: 8, linux_count: 4, windows_count: 2, + unknown_count: 0, } ); @@ -1029,7 +1030,7 @@ async fn test_signups() { let signups_batch1 = db.get_unsent_invites(3).await.unwrap(); let addresses = signups_batch1 .iter() - .map(|s| &s.email_address) + .map(|s| &s.invite.email_address) .collect::>(); assert_eq!( addresses, @@ -1040,8 +1041,8 @@ async fn test_signups() { ] ); assert_ne!( - signups_batch1[0].email_confirmation_code, - signups_batch1[1].email_confirmation_code + signups_batch1[0].invite.email_confirmation_code, + signups_batch1[1].invite.email_confirmation_code ); // the waitlist isn't updated until we record that the emails @@ -1051,11 +1052,18 @@ async fn test_signups() { // once the emails go out, we can retrieve the next batch // of signups. - db.record_sent_invites(&signups_batch1).await.unwrap(); + db.record_sent_invites( + &signups_batch1 + .iter() + .map(|i| i.invite.clone()) + .collect::>(), + ) + .await + .unwrap(); let signups_batch2 = db.get_unsent_invites(3).await.unwrap(); let addresses = signups_batch2 .iter() - .map(|s| &s.email_address) + .map(|s| &s.invite.email_address) .collect::>(); assert_eq!( addresses, @@ -1074,6 +1082,7 @@ async fn test_signups() { mac_count: 5, linux_count: 2, windows_count: 1, + unknown_count: 0, } ); @@ -1087,8 +1096,8 @@ async fn test_signups() { } = db .create_user_from_invite( &Invite { - email_address: signups_batch1[0].email_address.clone(), - email_confirmation_code: signups_batch1[0].email_confirmation_code.clone(), + email_address: signups_batch1[0].invite.email_address.clone(), + email_confirmation_code: signups_batch1[0].invite.email_confirmation_code.clone(), }, NewUserParams { github_login: "person-0".into(), @@ -1108,8 +1117,8 @@ async fn test_signups() { // cannot redeem the same signup again. db.create_user_from_invite( &Invite { - email_address: signups_batch1[0].email_address.clone(), - email_confirmation_code: signups_batch1[0].email_confirmation_code.clone(), + email_address: signups_batch1[0].invite.email_address.clone(), + email_confirmation_code: signups_batch1[0].invite.email_confirmation_code.clone(), }, NewUserParams { github_login: "some-other-github_account".into(), @@ -1123,7 +1132,7 @@ async fn test_signups() { // cannot redeem a signup with the wrong confirmation code. db.create_user_from_invite( &Invite { - email_address: signups_batch1[1].email_address.clone(), + email_address: signups_batch1[1].invite.email_address.clone(), email_confirmation_code: "the-wrong-code".to_string(), }, NewUserParams { From 934474f87e94be94f6db4521b1c1bd55324f45bc Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 14 Oct 2022 12:03:42 -0600 Subject: [PATCH 311/314] Remove unconditional invalidation when calling mouse region handlers We want invalidation to opt-in as much as possible. If you want a view to re-render, you need to call `cx.notify`. --- crates/editor/src/hover_popover.rs | 4 ++-- crates/gpui/src/elements/list.rs | 2 ++ crates/gpui/src/presenter.rs | 3 --- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index 38b28f0630..e4b4da68d3 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -354,7 +354,7 @@ impl InfoPopover { .with_style(style.hover_popover.container) .boxed() }) - .on_move(|_, _| {}) + .on_move(|_, _| {}) // Consume move events so they don't reach regions underneath. .with_cursor_style(CursorStyle::Arrow) .with_padding(Padding { bottom: HOVER_POPOVER_GAP, @@ -400,7 +400,7 @@ impl DiagnosticPopover { bottom: HOVER_POPOVER_GAP, ..Default::default() }) - .on_move(|_, _| {}) + .on_move(|_, _| {}) // Consume move events so they don't reach regions underneath. .on_click(MouseButton::Left, |_, cx| { cx.dispatch_action(GoToDiagnostic) }) diff --git a/crates/gpui/src/elements/list.rs b/crates/gpui/src/elements/list.rs index 7711d4497b..d0f87590fc 100644 --- a/crates/gpui/src/elements/list.rs +++ b/crates/gpui/src/elements/list.rs @@ -558,6 +558,8 @@ impl StateInner { let visible_range = self.visible_range(height, scroll_top); self.scroll_handler.as_mut().unwrap()(visible_range, cx); } + + cx.notify(); } fn scroll_top(&self, logical_scroll_top: &ListOffset) -> f32 { diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index 4eef9c7398..d0d70aae18 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -482,9 +482,6 @@ impl Presenter { if let Some(callback) = valid_region.handlers.get(®ion_event.handler_key()) { event_cx.handled = true; - event_cx - .invalidated_views - .insert(valid_region.id().view_id()); event_cx.with_current_view(valid_region.id().view_id(), { let region_event = region_event.clone(); |cx| { From b541ac313c1d174ab873e2237ea4aced038e1d34 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 14 Oct 2022 16:13:38 -0700 Subject: [PATCH 312/314] Revert unnecessary logic for fetching invites' platform_unknown flag --- crates/collab/src/api.rs | 4 ++-- crates/collab/src/db.rs | 33 +++++++-------------------------- crates/collab/src/db_tests.rs | 27 ++++++++++----------------- 3 files changed, 19 insertions(+), 45 deletions(-) diff --git a/crates/collab/src/api.rs b/crates/collab/src/api.rs index 2d09bf3aea..08dfa91ba9 100644 --- a/crates/collab/src/api.rs +++ b/crates/collab/src/api.rs @@ -1,6 +1,6 @@ use crate::{ auth, - db::{Invite, NewUserParams, ProjectId, Signup, UnsentInvite, User, UserId, WaitlistSummary}, + db::{Invite, NewUserParams, ProjectId, Signup, User, UserId, WaitlistSummary}, rpc::{self, ResultExt}, AppState, Error, Result, }; @@ -471,7 +471,7 @@ pub struct GetUnsentInvitesParams { async fn get_unsent_invites( Query(params): Query, Extension(app): Extension>, -) -> Result>> { +) -> Result>> { Ok(Json(app.db.get_unsent_invites(params.count).await?)) } diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs index f241202b22..9b3dca1f2c 100644 --- a/crates/collab/src/db.rs +++ b/crates/collab/src/db.rs @@ -45,7 +45,7 @@ pub trait Db: Send + Sync { async fn create_signup(&self, signup: Signup) -> Result<()>; async fn get_waitlist_summary(&self) -> Result; - async fn get_unsent_invites(&self, count: usize) -> Result>; + async fn get_unsent_invites(&self, count: usize) -> Result>; async fn record_sent_invites(&self, invites: &[Invite]) -> Result<()>; async fn create_user_from_invite( &self, @@ -442,11 +442,11 @@ impl Db for PostgresDb { .await?) } - async fn get_unsent_invites(&self, count: usize) -> Result> { - let rows: Vec<(String, String, bool)> = sqlx::query_as( + async fn get_unsent_invites(&self, count: usize) -> Result> { + Ok(sqlx::query_as( " SELECT - email_address, email_confirmation_code, platform_unknown + email_address, email_confirmation_code FROM signups WHERE NOT email_confirmation_sent AND @@ -456,19 +456,7 @@ impl Db for PostgresDb { ) .bind(count as i32) .fetch_all(&self.pool) - .await?; - Ok(rows - .into_iter() - .map( - |(email_address, email_confirmation_code, platform_unknown)| UnsentInvite { - invite: Invite { - email_address, - email_confirmation_code, - }, - platform_unknown, - }, - ) - .collect()) + .await?) } async fn record_sent_invites(&self, invites: &[Invite]) -> Result<()> { @@ -1737,19 +1725,12 @@ pub struct WaitlistSummary { pub unknown_count: i64, } -#[derive(Clone, FromRow, PartialEq, Debug, Serialize, Deserialize)] +#[derive(FromRow, PartialEq, Debug, Serialize, Deserialize)] pub struct Invite { pub email_address: String, pub email_confirmation_code: String, } -#[derive(Serialize, Debug, PartialEq)] -pub struct UnsentInvite { - #[serde(flatten)] - pub invite: Invite, - pub platform_unknown: bool, -} - #[derive(Debug, Serialize, Deserialize)] pub struct NewUserParams { pub github_login: String, @@ -1965,7 +1946,7 @@ mod test { unimplemented!() } - async fn get_unsent_invites(&self, _count: usize) -> Result> { + async fn get_unsent_invites(&self, _count: usize) -> Result> { unimplemented!() } diff --git a/crates/collab/src/db_tests.rs b/crates/collab/src/db_tests.rs index b48a45018b..d5ef045e66 100644 --- a/crates/collab/src/db_tests.rs +++ b/crates/collab/src/db_tests.rs @@ -1030,7 +1030,7 @@ async fn test_signups() { let signups_batch1 = db.get_unsent_invites(3).await.unwrap(); let addresses = signups_batch1 .iter() - .map(|s| &s.invite.email_address) + .map(|s| &s.email_address) .collect::>(); assert_eq!( addresses, @@ -1041,8 +1041,8 @@ async fn test_signups() { ] ); assert_ne!( - signups_batch1[0].invite.email_confirmation_code, - signups_batch1[1].invite.email_confirmation_code + signups_batch1[0].email_confirmation_code, + signups_batch1[1].email_confirmation_code ); // the waitlist isn't updated until we record that the emails @@ -1052,18 +1052,11 @@ async fn test_signups() { // once the emails go out, we can retrieve the next batch // of signups. - db.record_sent_invites( - &signups_batch1 - .iter() - .map(|i| i.invite.clone()) - .collect::>(), - ) - .await - .unwrap(); + db.record_sent_invites(&signups_batch1).await.unwrap(); let signups_batch2 = db.get_unsent_invites(3).await.unwrap(); let addresses = signups_batch2 .iter() - .map(|s| &s.invite.email_address) + .map(|s| &s.email_address) .collect::>(); assert_eq!( addresses, @@ -1096,8 +1089,8 @@ async fn test_signups() { } = db .create_user_from_invite( &Invite { - email_address: signups_batch1[0].invite.email_address.clone(), - email_confirmation_code: signups_batch1[0].invite.email_confirmation_code.clone(), + email_address: signups_batch1[0].email_address.clone(), + email_confirmation_code: signups_batch1[0].email_confirmation_code.clone(), }, NewUserParams { github_login: "person-0".into(), @@ -1117,8 +1110,8 @@ async fn test_signups() { // cannot redeem the same signup again. db.create_user_from_invite( &Invite { - email_address: signups_batch1[0].invite.email_address.clone(), - email_confirmation_code: signups_batch1[0].invite.email_confirmation_code.clone(), + email_address: signups_batch1[0].email_address.clone(), + email_confirmation_code: signups_batch1[0].email_confirmation_code.clone(), }, NewUserParams { github_login: "some-other-github_account".into(), @@ -1132,7 +1125,7 @@ async fn test_signups() { // cannot redeem a signup with the wrong confirmation code. db.create_user_from_invite( &Invite { - email_address: signups_batch1[1].invite.email_address.clone(), + email_address: signups_batch1[1].email_address.clone(), email_confirmation_code: "the-wrong-code".to_string(), }, NewUserParams { From bc0359291266af8a74c8efae8cb0cf875d73c417 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 14 Oct 2022 18:09:15 -0600 Subject: [PATCH 313/314] Only invalidate parent view on click/hover if we read that state when rendering Co-Authored-By: Max Brunsfeld --- .../src/activity_indicator.rs | 2 +- crates/chat_panel/src/chat_panel.rs | 2 +- crates/collab_ui/src/contact_finder.rs | 2 +- crates/collab_ui/src/contact_list.rs | 22 ++++++++-- crates/command_palette/src/command_palette.rs | 2 +- crates/context_menu/src/context_menu.rs | 14 +++--- crates/editor/src/editor.rs | 4 +- crates/file_finder/src/file_finder.rs | 2 +- crates/gpui/src/app.rs | 30 +++++++++++-- .../gpui/src/elements/mouse_event_handler.rs | 16 +++++-- crates/gpui/src/presenter.rs | 44 ++++++++++--------- crates/gpui/src/scene/mouse_region.rs | 14 ++++++ crates/gpui/src/views/select.rs | 4 +- crates/outline/src/outline.rs | 2 +- crates/picker/src/picker.rs | 2 +- crates/project_symbols/src/project_symbols.rs | 2 +- crates/theme/src/theme.rs | 6 +-- crates/theme_selector/src/theme_selector.rs | 2 +- crates/workspace/src/pane.rs | 7 +-- 19 files changed, 124 insertions(+), 55 deletions(-) diff --git a/crates/activity_indicator/src/activity_indicator.rs b/crates/activity_indicator/src/activity_indicator.rs index 8f6f4bf627..596e9ba995 100644 --- a/crates/activity_indicator/src/activity_indicator.rs +++ b/crates/activity_indicator/src/activity_indicator.rs @@ -285,7 +285,7 @@ impl View for ActivityIndicator { .workspace .status_bar .lsp_status; - let style = if state.hovered && action.is_some() { + let style = if state.hovered() && action.is_some() { theme.hover.as_ref().unwrap_or(&theme.default) } else { &theme.default diff --git a/crates/chat_panel/src/chat_panel.rs b/crates/chat_panel/src/chat_panel.rs index 60eda235ac..0c22c21c71 100644 --- a/crates/chat_panel/src/chat_panel.rs +++ b/crates/chat_panel/src/chat_panel.rs @@ -311,7 +311,7 @@ impl ChatPanel { MouseEventHandler::::new(0, cx, |mouse_state, _| { Label::new( "Sign in to use chat".to_string(), - if mouse_state.hovered { + if mouse_state.hovered() { theme.chat_panel.hovered_sign_in_prompt.clone() } else { theme.chat_panel.sign_in_prompt.clone() diff --git a/crates/collab_ui/src/contact_finder.rs b/crates/collab_ui/src/contact_finder.rs index 8835dc0b0e..a4ec02d2f0 100644 --- a/crates/collab_ui/src/contact_finder.rs +++ b/crates/collab_ui/src/contact_finder.rs @@ -101,7 +101,7 @@ impl PickerDelegate for ContactFinder { fn render_match( &self, ix: usize, - mouse_state: MouseState, + mouse_state: &mut MouseState, selected: bool, cx: &gpui::AppContext, ) -> ElementBox { diff --git a/crates/collab_ui/src/contact_list.rs b/crates/collab_ui/src/contact_list.rs index e1d96cd1da..cf8a8f8223 100644 --- a/crates/collab_ui/src/contact_list.rs +++ b/crates/collab_ui/src/contact_list.rs @@ -653,7 +653,11 @@ impl ContactList { .constrained() .with_height(theme.row_height) .contained() - .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) + .with_style( + *theme + .contact_row + .style_for(&mut Default::default(), is_selected), + ) .boxed() } @@ -768,7 +772,9 @@ impl ContactList { ) -> ElementBox { enum Header {} - let header_style = theme.header_row.style_for(Default::default(), is_selected); + let header_style = theme + .header_row + .style_for(&mut Default::default(), is_selected); let text = match section { Section::ActiveCall => "Collaborators", Section::Requests => "Contact Requests", @@ -890,7 +896,11 @@ impl ContactList { .constrained() .with_height(theme.row_height) .contained() - .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) + .with_style( + *theme + .contact_row + .style_for(&mut Default::default(), is_selected), + ) .boxed() }) .on_click(MouseButton::Left, move |_, cx| { @@ -1014,7 +1024,11 @@ impl ContactList { row.constrained() .with_height(theme.row_height) .contained() - .with_style(*theme.contact_row.style_for(Default::default(), is_selected)) + .with_style( + *theme + .contact_row + .style_for(&mut Default::default(), is_selected), + ) .boxed() } diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index 125a4eeb02..7702aaaf2a 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -224,7 +224,7 @@ impl PickerDelegate for CommandPalette { fn render_match( &self, ix: usize, - mouse_state: MouseState, + mouse_state: &mut MouseState, selected: bool, cx: &gpui::AppContext, ) -> gpui::ElementBox { diff --git a/crates/context_menu/src/context_menu.rs b/crates/context_menu/src/context_menu.rs index b79e931257..c284375966 100644 --- a/crates/context_menu/src/context_menu.rs +++ b/crates/context_menu/src/context_menu.rs @@ -258,9 +258,10 @@ impl ContextMenu { .with_children(self.items.iter().enumerate().map(|(ix, item)| { match item { ContextMenuItem::Item { label, .. } => { - let style = style - .item - .style_for(Default::default(), Some(ix) == self.selected_index); + let style = style.item.style_for( + &mut Default::default(), + Some(ix) == self.selected_index, + ); Label::new(label.to_string(), style.label.clone()) .contained() @@ -283,9 +284,10 @@ impl ContextMenu { .with_children(self.items.iter().enumerate().map(|(ix, item)| { match item { ContextMenuItem::Item { action, .. } => { - let style = style - .item - .style_for(Default::default(), Some(ix) == self.selected_index); + let style = style.item.style_for( + &mut Default::default(), + Some(ix) == self.selected_index, + ); KeystrokeLabel::new( action.boxed_clone(), style.keystroke.container, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 07429a600d..7f84772a45 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -705,7 +705,7 @@ impl CompletionsMenu { |state, _| { let item_style = if item_ix == selected_item { style.autocomplete.selected_item - } else if state.hovered { + } else if state.hovered() { style.autocomplete.hovered_item } else { style.autocomplete.item @@ -850,7 +850,7 @@ impl CodeActionsMenu { MouseEventHandler::::new(item_ix, cx, |state, _| { let item_style = if item_ix == selected_item { style.autocomplete.selected_item - } else if state.hovered { + } else if state.hovered() { style.autocomplete.hovered_item } else { style.autocomplete.item diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index fe0fa3ae77..e787e3c1a6 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -251,7 +251,7 @@ impl PickerDelegate for FileFinder { fn render_match( &self, ix: usize, - mouse_state: MouseState, + mouse_state: &mut MouseState, selected: bool, cx: &AppContext, ) -> ElementBox { diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 76aae930a1..a9529f3f9f 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -3774,10 +3774,32 @@ pub struct RenderContext<'a, T: View> { pub refreshing: bool, } -#[derive(Clone, Copy, Default)] +#[derive(Clone, Default)] pub struct MouseState { - pub hovered: bool, - pub clicked: Option, + hovered: bool, + clicked: Option, + accessed_hovered: bool, + accessed_clicked: bool, +} + +impl MouseState { + pub fn hovered(&mut self) -> bool { + self.accessed_hovered = true; + self.hovered + } + + pub fn clicked(&mut self) -> Option { + self.accessed_clicked = true; + self.clicked + } + + pub fn accessed_hovered(&self) -> bool { + self.accessed_hovered + } + + pub fn accessed_clicked(&self) -> bool { + self.accessed_clicked + } } impl<'a, V: View> RenderContext<'a, V> { @@ -3818,6 +3840,8 @@ impl<'a, V: View> RenderContext<'a, V> { None } }), + accessed_hovered: false, + accessed_clicked: false, } } diff --git a/crates/gpui/src/elements/mouse_event_handler.rs b/crates/gpui/src/elements/mouse_event_handler.rs index e809c0080f..ab5aeb562b 100644 --- a/crates/gpui/src/elements/mouse_event_handler.rs +++ b/crates/gpui/src/elements/mouse_event_handler.rs @@ -22,6 +22,8 @@ pub struct MouseEventHandler { cursor_style: Option, handlers: HandlerSet, hoverable: bool, + notify_on_hover: bool, + notify_on_click: bool, padding: Padding, _tag: PhantomData, } @@ -30,13 +32,19 @@ impl MouseEventHandler { pub fn new(region_id: usize, cx: &mut RenderContext, render_child: F) -> Self where V: View, - F: FnOnce(MouseState, &mut RenderContext) -> ElementBox, + F: FnOnce(&mut MouseState, &mut RenderContext) -> ElementBox, { + let mut mouse_state = cx.mouse_state::(region_id); + let child = render_child(&mut mouse_state, cx); + let notify_on_hover = mouse_state.accessed_hovered(); + let notify_on_click = mouse_state.accessed_clicked(); Self { - child: render_child(cx.mouse_state::(region_id), cx), + child, region_id, cursor_style: None, handlers: Default::default(), + notify_on_hover, + notify_on_click, hoverable: true, padding: Default::default(), _tag: PhantomData, @@ -185,7 +193,9 @@ impl Element for MouseEventHandler { hit_bounds, self.handlers.clone(), ) - .with_hoverable(self.hoverable), + .with_hoverable(self.hoverable) + .with_notify_on_hover(self.notify_on_hover) + .with_notify_on_click(self.notify_on_click), ); self.child.paint(bounds.origin(), visible_bounds, cx); diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index d0d70aae18..d082ebd095 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -231,7 +231,7 @@ impl Presenter { ) -> bool { if let Some(root_view_id) = cx.root_view_id(self.window_id) { let mut events_to_send = Vec::new(); - let mut invalidated_views: HashSet = Default::default(); + let mut notified_views: HashSet = Default::default(); // 1. Allocate the correct set of GPUI events generated from the platform events // -> These are usually small: [Mouse Down] or [Mouse up, Click] or [Mouse Moved, Mouse Dragged?] @@ -257,11 +257,6 @@ impl Presenter { }) .collect(); - // Clicked status is used when rendering views via the RenderContext. - // So when it changes, these views need to be rerendered - for clicked_region_id in self.clicked_region_ids.iter() { - invalidated_views.insert(clicked_region_id.view_id()); - } self.clicked_button = Some(e.button); } @@ -392,17 +387,31 @@ impl Presenter { //Ensure that hover entrance events aren't sent twice if self.hovered_region_ids.insert(region.id()) { valid_regions.push(region.clone()); - invalidated_views.insert(region.id().view_id()); + if region.notify_on_hover { + notified_views.insert(region.id().view_id()); + } } } else { // Ensure that hover exit events aren't sent twice if self.hovered_region_ids.remove(®ion.id()) { valid_regions.push(region.clone()); - invalidated_views.insert(region.id().view_id()); + if region.notify_on_hover { + notified_views.insert(region.id().view_id()); + } } } } } + MouseRegionEvent::Down(_) | MouseRegionEvent::Up(_) => { + for (region, _) in self.mouse_regions.iter().rev() { + if region.bounds.contains_point(self.mouse_position) { + if region.notify_on_click { + notified_views.insert(region.id().view_id()); + } + valid_regions.push(region.clone()); + } + } + } MouseRegionEvent::Click(e) => { // Only raise click events if the released button is the same as the one stored if self @@ -413,11 +422,6 @@ impl Presenter { // Clear clicked regions and clicked button let clicked_region_ids = std::mem::replace(&mut self.clicked_region_ids, Default::default()); - // Clicked status is used when rendering views via the RenderContext. - // So when it changes, these views need to be rerendered - for clicked_region_id in clicked_region_ids.iter() { - invalidated_views.insert(clicked_region_id.view_id()); - } self.clicked_button = None; // Find regions which still overlap with the mouse since the last MouseDown happened @@ -459,7 +463,7 @@ impl Presenter { //3. Fire region events let hovered_region_ids = self.hovered_region_ids.clone(); for valid_region in valid_regions.into_iter() { - let mut event_cx = self.build_event_context(&mut invalidated_views, cx); + let mut event_cx = self.build_event_context(&mut notified_views, cx); region_event.set_region(valid_region.bounds); if let MouseRegionEvent::Hover(e) = &mut region_event { @@ -500,11 +504,11 @@ impl Presenter { } if !any_event_handled && !event_reused { - let mut event_cx = self.build_event_context(&mut invalidated_views, cx); + let mut event_cx = self.build_event_context(&mut notified_views, cx); any_event_handled = event_cx.dispatch_event(root_view_id, &event); } - for view_id in invalidated_views { + for view_id in notified_views { cx.notify_view(self.window_id, view_id); } @@ -516,7 +520,7 @@ impl Presenter { pub fn build_event_context<'a>( &'a mut self, - invalidated_views: &'a mut HashSet, + notified_views: &'a mut HashSet, cx: &'a mut MutableAppContext, ) -> EventContext<'a> { EventContext { @@ -524,7 +528,7 @@ impl Presenter { font_cache: &self.font_cache, text_layout_cache: &self.text_layout_cache, view_stack: Default::default(), - invalidated_views, + notified_views, notify_count: 0, handled: false, window_id: self.window_id, @@ -747,7 +751,7 @@ pub struct EventContext<'a> { pub notify_count: usize, view_stack: Vec, handled: bool, - invalidated_views: &'a mut HashSet, + notified_views: &'a mut HashSet, } impl<'a> EventContext<'a> { @@ -806,7 +810,7 @@ impl<'a> EventContext<'a> { pub fn notify(&mut self) { self.notify_count += 1; if let Some(view_id) = self.view_stack.last() { - self.invalidated_views.insert(*view_id); + self.notified_views.insert(*view_id); } } diff --git a/crates/gpui/src/scene/mouse_region.rs b/crates/gpui/src/scene/mouse_region.rs index 9bd1ab008b..e84508622b 100644 --- a/crates/gpui/src/scene/mouse_region.rs +++ b/crates/gpui/src/scene/mouse_region.rs @@ -20,6 +20,8 @@ pub struct MouseRegion { pub bounds: RectF, pub handlers: HandlerSet, pub hoverable: bool, + pub notify_on_hover: bool, + pub notify_on_click: bool, } impl MouseRegion { @@ -52,6 +54,8 @@ impl MouseRegion { bounds, handlers, hoverable: true, + notify_on_hover: false, + notify_on_click: false, } } @@ -137,6 +141,16 @@ impl MouseRegion { self.hoverable = is_hoverable; self } + + pub fn with_notify_on_hover(mut self, notify: bool) -> Self { + self.notify_on_hover = notify; + self + } + + pub fn with_notify_on_click(mut self, notify: bool) -> Self { + self.notify_on_click = notify; + self + } } #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] diff --git a/crates/gpui/src/views/select.rs b/crates/gpui/src/views/select.rs index 113fbf5828..216491db0f 100644 --- a/crates/gpui/src/views/select.rs +++ b/crates/gpui/src/views/select.rs @@ -113,7 +113,7 @@ impl View for Select { Container::new((self.render_item)( self.selected_item_ix, ItemType::Header, - mouse_state.hovered, + mouse_state.hovered(), cx, )) .with_style(style.header) @@ -145,7 +145,7 @@ impl View for Select { } else { ItemType::Unselected }, - mouse_state.hovered, + mouse_state.hovered(), cx, ) }) diff --git a/crates/outline/src/outline.rs b/crates/outline/src/outline.rs index afcfa263a8..a677ab5b67 100644 --- a/crates/outline/src/outline.rs +++ b/crates/outline/src/outline.rs @@ -233,7 +233,7 @@ impl PickerDelegate for OutlineView { fn render_match( &self, ix: usize, - mouse_state: MouseState, + mouse_state: &mut MouseState, selected: bool, cx: &AppContext, ) -> ElementBox { diff --git a/crates/picker/src/picker.rs b/crates/picker/src/picker.rs index a093740e25..30ad7827ef 100644 --- a/crates/picker/src/picker.rs +++ b/crates/picker/src/picker.rs @@ -33,7 +33,7 @@ pub trait PickerDelegate: View { fn render_match( &self, ix: usize, - state: MouseState, + state: &mut MouseState, selected: bool, cx: &AppContext, ) -> ElementBox; diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index 6aa866342a..a81231b98f 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -234,7 +234,7 @@ impl PickerDelegate for ProjectSymbolsView { fn render_match( &self, ix: usize, - mouse_state: MouseState, + mouse_state: &mut MouseState, selected: bool, cx: &AppContext, ) -> ElementBox { diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 6e411c010f..51aa3232d4 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -645,12 +645,12 @@ pub struct Interactive { } impl Interactive { - pub fn style_for(&self, state: MouseState, active: bool) -> &T { + pub fn style_for(&self, state: &mut MouseState, active: bool) -> &T { if active { self.active.as_ref().unwrap_or(&self.default) - } else if state.clicked == Some(gpui::MouseButton::Left) && self.clicked.is_some() { + } else if state.clicked() == Some(gpui::MouseButton::Left) && self.clicked.is_some() { self.clicked.as_ref().unwrap() - } else if state.hovered { + } else if state.hovered() { self.hover.as_ref().unwrap_or(&self.default) } else { &self.default diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index a014fc8e92..3236120857 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -230,7 +230,7 @@ impl PickerDelegate for ThemeSelector { fn render_match( &self, ix: usize, - mouse_state: MouseState, + mouse_state: &mut MouseState, selected: bool, cx: &AppContext, ) -> ElementBox { diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index c8019e1123..b7ae7f2ba0 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1088,7 +1088,7 @@ impl Pane { move |mouse_state, cx| { let tab_style = theme.workspace.tab_bar.tab_style(pane_active, tab_active); - let hovered = mouse_state.hovered; + let hovered = mouse_state.hovered(); Self::render_tab( &item, pane, @@ -1161,7 +1161,8 @@ impl Pane { .with_style(filler_style.container) .with_border(filler_style.container.border); - if let Some(overlay) = Self::tab_overlay_color(mouse_state.hovered, &theme, cx) + if let Some(overlay) = + Self::tab_overlay_color(mouse_state.hovered(), &theme, cx) { filler = filler.with_overlay_color(overlay); } @@ -1283,7 +1284,7 @@ impl Pane { enum TabCloseButton {} let icon = Svg::new("icons/x_mark_thin_8.svg"); MouseEventHandler::::new(item_id, cx, |mouse_state, _| { - if mouse_state.hovered { + if mouse_state.hovered() { icon.with_color(tab_style.icon_close_active).boxed() } else { icon.with_color(tab_style.icon_close).boxed() From 646d344a11032c9ad2d32a7624a1254b32249716 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 14 Oct 2022 18:27:55 -0600 Subject: [PATCH 314/314] Avoid re-rendering editor on mouse move Only notify editor when clearing highlights if there were highlights to begin with. Co-Authored-By: Max Brunsfeld --- crates/editor/src/editor.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 7f84772a45..cc8347248b 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -5792,8 +5792,11 @@ impl Editor { &mut self, cx: &mut ViewContext, ) -> Option<(fn(&Theme) -> Color, Vec>)> { - cx.notify(); - self.background_highlights.remove(&TypeId::of::()) + let highlights = self.background_highlights.remove(&TypeId::of::()); + if highlights.is_some() { + cx.notify(); + } + highlights } #[cfg(feature = "test-support")] @@ -5907,9 +5910,13 @@ impl Editor { &mut self, cx: &mut ViewContext, ) -> Option>)>> { - cx.notify(); - self.display_map - .update(cx, |map, _| map.clear_text_highlights(TypeId::of::())) + let highlights = self + .display_map + .update(cx, |map, _| map.clear_text_highlights(TypeId::of::())); + if highlights.is_some() { + cx.notify(); + } + highlights } fn next_blink_epoch(&mut self) -> usize {