Detailed changes
@@ -4087,6 +4087,27 @@ dependencies = [
"xmlparser",
]
+[[package]]
+name = "rpc_client"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "async-recursion",
+ "async-tungstenite",
+ "gpui",
+ "lazy_static",
+ "log",
+ "parking_lot",
+ "postage",
+ "rand 0.8.3",
+ "smol",
+ "surf",
+ "thiserror",
+ "tiny_http",
+ "util",
+ "zrpc",
+]
+
[[package]]
name = "rsa"
version = "0.4.0"
@@ -5625,6 +5646,18 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
+[[package]]
+name = "util"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "futures",
+ "log",
+ "serde_json 1.0.64",
+ "surf",
+ "tempdir",
+]
+
[[package]]
name = "uuid"
version = "0.5.1"
@@ -5897,6 +5930,36 @@ dependencies = [
"winapi 0.3.9",
]
+[[package]]
+name = "worktree"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "buffer",
+ "clock",
+ "fsevent",
+ "futures",
+ "fuzzy",
+ "gpui",
+ "ignore",
+ "lazy_static",
+ "libc",
+ "log",
+ "parking_lot",
+ "postage",
+ "rand 0.8.3",
+ "rpc_client",
+ "serde 1.0.125",
+ "serde_json 1.0.64",
+ "smol",
+ "sum_tree",
+ "tempdir",
+ "toml 0.5.8",
+ "util",
+ "zrpc",
+]
+
[[package]]
name = "wyz"
version = "0.2.0"
@@ -5962,6 +6025,7 @@ dependencies = [
"parking_lot",
"postage",
"rand 0.8.3",
+ "rpc_client",
"rsa",
"rust-embed",
"serde 1.0.125",
@@ -5981,6 +6045,8 @@ dependencies = [
"tree-sitter-rust",
"unindent",
"url",
+ "util",
+ "worktree",
"zrpc",
]
@@ -6,8 +6,11 @@ members = [
"fuzzy",
"gpui",
"gpui_macros",
+ "rpc_client",
"server",
"sum_tree",
+ "util",
+ "worktree",
"zed",
"zrpc"
]
@@ -20,6 +20,8 @@ use lazy_static::lazy_static;
use operation_queue::OperationQueue;
use parking_lot::Mutex;
pub use point::*;
+#[cfg(any(test, feature = "test-support"))]
+pub use random_char_iter::*;
pub use rope::{Chunks, Rope, TextSummary};
use seahash::SeaHasher;
pub use selection::*;
@@ -0,0 +1,55 @@
+[package]
+name = "rpc_client"
+version = "0.1.0"
+edition = "2018"
+
+[features]
+test-support = []
+
+[dependencies]
+anyhow = "1.0.38"
+async-recursion = "0.3"
+# async-trait = "0.1"
+async-tungstenite = { version = "0.14", features = ["async-tls"] }
+# buffer = { path = "../buffer" }
+# clock = { path = "../clock" }
+# crossbeam-channel = "0.5.0"
+# ctor = "0.1.20"
+# dirs = "3.0"
+# easy-parallel = "3.1.0"
+# fsevent = { path = "../fsevent" }
+# futures = "0.3"
+# fuzzy = { path = "../fuzzy" }
+gpui = { path = "../gpui" }
+# http-auth-basic = "0.1.3"
+# ignore = "0.4"
+# image = "0.23"
+# indexmap = "1.6.2"
+lazy_static = "1.4.0"
+# libc = "0.2"
+log = "0.4"
+# log-panics = { version = "2.0", features = ["with-backtrace"] }
+# num_cpus = "1.13.0"
+parking_lot = "0.11.1"
+postage = { version = "0.4.1", features = ["futures-traits"] }
+rand = "0.8.3"
+# rsa = "0.4"
+# rust-embed = { version = "6.2", features = ["include-exclude"] }
+# serde = { version = "1", features = ["derive"] }
+# serde_json = { version = "1.0.64", features = ["preserve_order"] }
+# serde_path_to_error = "0.1.4"
+# simplelog = "0.9"
+# smallvec = { version = "1.6", features = ["union"] }
+smol = "1.2.5"
+# sum_tree = { path = "../sum_tree" }
+surf = "2.2"
+# tempdir = { version = "0.3.7", optional = true }
+thiserror = "1.0.29"
+# time = { version = "0.3" }
+tiny_http = "0.8"
+# toml = "0.5"
+# tree-sitter = "0.19.5"
+# tree-sitter-rust = "0.19.0"
+util = { path = "../util" }
+# url = "2.2"
+zrpc = { path = "../zrpc" }
@@ -1,4 +1,6 @@
-use crate::util::ResultExt;
+#[cfg(any(test, feature = "test-support"))]
+pub mod test;
+
use anyhow::{anyhow, Context, Result};
use async_recursion::async_recursion;
use async_tungstenite::tungstenite::{
@@ -21,6 +23,7 @@ use std::{
};
use surf::Url;
use thiserror::Error;
+use util::ResultExt;
pub use zrpc::{proto, ConnectionId, PeerId, TypedEnvelope};
use zrpc::{
proto::{AnyTypedEnvelope, EntityMessage, EnvelopedMessage, RequestMessage},
@@ -0,0 +1,156 @@
+use super::*;
+use std::sync::atomic::Ordering::SeqCst;
+
+use super::Client;
+use gpui::TestAppContext;
+use parking_lot::Mutex;
+use postage::{mpsc, prelude::Stream};
+use std::sync::{
+ atomic::{AtomicBool, AtomicUsize},
+ Arc,
+};
+use zrpc::{proto, ConnectionId, Peer, Receipt, TypedEnvelope};
+
+pub struct FakeServer {
+ peer: Arc<Peer>,
+ incoming: Mutex<Option<mpsc::Receiver<Box<dyn proto::AnyTypedEnvelope>>>>,
+ connection_id: Mutex<Option<ConnectionId>>,
+ forbid_connections: AtomicBool,
+ auth_count: AtomicUsize,
+ access_token: AtomicUsize,
+ user_id: u64,
+}
+
+impl FakeServer {
+ pub async fn for_client(
+ client_user_id: u64,
+ client: &mut Arc<Client>,
+ cx: &TestAppContext,
+ ) -> Arc<Self> {
+ let server = Arc::new(Self {
+ peer: Peer::new(),
+ incoming: Default::default(),
+ connection_id: Default::default(),
+ forbid_connections: Default::default(),
+ auth_count: Default::default(),
+ access_token: Default::default(),
+ user_id: client_user_id,
+ });
+
+ Arc::get_mut(client)
+ .unwrap()
+ .override_authenticate({
+ let server = server.clone();
+ move |cx| {
+ server.auth_count.fetch_add(1, SeqCst);
+ let access_token = server.access_token.load(SeqCst).to_string();
+ cx.spawn(move |_| async move {
+ Ok(Credentials {
+ user_id: client_user_id,
+ access_token,
+ })
+ })
+ }
+ })
+ .override_establish_connection({
+ let server = server.clone();
+ move |credentials, cx| {
+ let credentials = credentials.clone();
+ cx.spawn({
+ let server = server.clone();
+ move |cx| async move { server.establish_connection(&credentials, &cx).await }
+ })
+ }
+ });
+
+ client
+ .authenticate_and_connect(&cx.to_async())
+ .await
+ .unwrap();
+ server
+ }
+
+ pub async fn disconnect(&self) {
+ self.peer.disconnect(self.connection_id()).await;
+ self.connection_id.lock().take();
+ self.incoming.lock().take();
+ }
+
+ async fn establish_connection(
+ &self,
+ credentials: &Credentials,
+ cx: &AsyncAppContext,
+ ) -> Result<Connection, EstablishConnectionError> {
+ assert_eq!(credentials.user_id, self.user_id);
+
+ if self.forbid_connections.load(SeqCst) {
+ Err(EstablishConnectionError::Other(anyhow!(
+ "server is forbidding connections"
+ )))?
+ }
+
+ if credentials.access_token != self.access_token.load(SeqCst).to_string() {
+ Err(EstablishConnectionError::Unauthorized)?
+ }
+
+ let (client_conn, server_conn, _) = Connection::in_memory();
+ let (connection_id, io, incoming) = self.peer.add_connection(server_conn).await;
+ cx.background().spawn(io).detach();
+ *self.incoming.lock() = Some(incoming);
+ *self.connection_id.lock() = Some(connection_id);
+ Ok(client_conn)
+ }
+
+ pub fn auth_count(&self) -> usize {
+ self.auth_count.load(SeqCst)
+ }
+
+ pub fn roll_access_token(&self) {
+ self.access_token.fetch_add(1, SeqCst);
+ }
+
+ pub fn forbid_connections(&self) {
+ self.forbid_connections.store(true, SeqCst);
+ }
+
+ pub fn allow_connections(&self) {
+ self.forbid_connections.store(false, SeqCst);
+ }
+
+ pub async fn send<T: proto::EnvelopedMessage>(&self, message: T) {
+ self.peer.send(self.connection_id(), message).await.unwrap();
+ }
+
+ pub async fn receive<M: proto::EnvelopedMessage>(&self) -> Result<TypedEnvelope<M>> {
+ let message = self
+ .incoming
+ .lock()
+ .as_mut()
+ .expect("not connected")
+ .recv()
+ .await
+ .ok_or_else(|| anyhow!("other half hung up"))?;
+ let type_name = message.payload_type_name();
+ Ok(*message
+ .into_any()
+ .downcast::<TypedEnvelope<M>>()
+ .unwrap_or_else(|_| {
+ panic!(
+ "fake server received unexpected message type: {:?}",
+ type_name
+ );
+ }))
+ }
+
+ pub async fn respond<T: proto::RequestMessage>(
+ &self,
+ receipt: Receipt<T>,
+ response: T::Response,
+ ) {
+ self.peer.respond(receipt, response).await.unwrap()
+ }
+
+ fn connection_id(&self) -> ConnectionId {
+ self.connection_id.lock().expect("not connected")
+ }
+}
@@ -0,0 +1,15 @@
+[package]
+name = "util"
+version = "0.1.0"
+edition = "2018"
+
+[features]
+test-support = ["serde_json", "tempdir"]
+
+[dependencies]
+anyhow = "1.0.38"
+futures = "0.3"
+log = "0.4"
+surf = "2.2"
+tempdir = { version = "0.3.7", optional = true }
+serde_json = { version = "1.0.64", features = ["preserve_order"], optional = true }
@@ -1,11 +1,12 @@
+#[cfg(feature = "test-support")]
+pub mod test;
+
use futures::Future;
-use rand::prelude::*;
use std::{
cmp::Ordering,
pin::Pin,
task::{Context, Poll},
};
-pub use sum_tree::Bias;
pub fn post_inc(value: &mut usize) -> usize {
let prev = *value;
@@ -35,35 +36,6 @@ where
}
}
}
-
-pub struct RandomCharIter<T: Rng>(T);
-
-impl<T: Rng> RandomCharIter<T> {
- #[cfg(test)]
- pub fn new(rng: T) -> Self {
- Self(rng)
- }
-}
-
-impl<T: Rng> Iterator for RandomCharIter<T> {
- type Item = char;
-
- fn next(&mut self) -> Option<Self::Item> {
- match self.0.gen_range(0..100) {
- // whitespace
- 0..=19 => [' ', '\n', '\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()),
- }
- }
-}
-
pub trait ResultExt {
type Ok;
@@ -0,0 +1,37 @@
+use std::path::{Path, PathBuf};
+use tempdir::TempDir;
+
+pub fn temp_tree(tree: serde_json::Value) -> TempDir {
+ let dir = TempDir::new("").unwrap();
+ write_tree(dir.path(), tree);
+ dir
+}
+
+fn write_tree(path: &Path, tree: serde_json::Value) {
+ use serde_json::Value;
+ use std::fs;
+
+ if let Value::Object(map) = tree {
+ for (name, contents) in map {
+ let mut path = PathBuf::from(path);
+ path.push(name);
+ match contents {
+ Value::Object(_) => {
+ fs::create_dir(&path).unwrap();
+ write_tree(&path, contents);
+ }
+ Value::Null => {
+ fs::create_dir(&path).unwrap();
+ }
+ Value::String(contents) => {
+ fs::write(&path, contents).unwrap();
+ }
+ _ => {
+ panic!("JSON object must contain only objects, strings, or null");
+ }
+ }
+ }
+ } else {
+ panic!("You must pass a JSON object to this helper")
+ }
+}
@@ -0,0 +1,61 @@
+[package]
+name = "worktree"
+version = "0.1.0"
+edition = "2018"
+
+[features]
+test-support = []
+
+[dependencies]
+anyhow = "1.0.38"
+# async-recursion = "0.3"
+async-trait = "0.1"
+# async-tungstenite = { version = "0.14", features = ["async-tls"] }
+buffer = { path = "../buffer" }
+clock = { path = "../clock" }
+# crossbeam-channel = "0.5.0"
+# ctor = "0.1.20"
+# dirs = "3.0"
+# easy-parallel = "3.1.0"
+fsevent = { path = "../fsevent" }
+futures = "0.3"
+fuzzy = { path = "../fuzzy" }
+gpui = { path = "../gpui" }
+# http-auth-basic = "0.1.3"
+ignore = "0.4"
+# image = "0.23"
+# indexmap = "1.6.2"
+lazy_static = "1.4.0"
+libc = "0.2"
+log = "0.4"
+# log-panics = { version = "2.0", features = ["with-backtrace"] }
+# num_cpus = "1.13.0"
+parking_lot = "0.11.1"
+postage = { version = "0.4.1", features = ["futures-traits"] }
+rpc_client = { path = "../rpc_client" }
+# rand = "0.8.3"
+# rsa = "0.4"
+# rust-embed = { version = "6.2", features = ["include-exclude"] }
+serde = { version = "1", features = ["derive"] }
+serde_json = { version = "1.0.64", features = ["preserve_order"] }
+# serde_path_to_error = "0.1.4"
+# simplelog = "0.9"
+# smallvec = { version = "1.6", features = ["union"] }
+smol = "1.2.5"
+sum_tree = { path = "../sum_tree" }
+util = { path = "../util" }
+# surf = "2.2"
+# tempdir = { version = "0.3.7", optional = true }
+# thiserror = "1.0.29"
+# time = { version = "0.3" }
+# tiny_http = "0.8"
+toml = "0.5"
+# tree-sitter = "0.19.5"
+# tree-sitter-rust = "0.19.0"
+# url = "2.2"
+zrpc = { path = "../zrpc" }
+
+[dev-dependencies]
+rand = "0.8.3"
+tempdir = { version = "0.3.7" }
+util = { path = "../util", features = ["test-support"] }
@@ -1,17 +1,14 @@
+pub mod fs;
mod ignore;
use self::ignore::IgnoreStack;
-use crate::{
- fs::{self, Fs},
- fuzzy::CharBag,
- rpc::{self, proto, Status},
- util::{Bias, TryFutureExt},
-};
use ::ignore::gitignore::{Gitignore, GitignoreBuilder};
use anyhow::{anyhow, Result};
use buffer::{self, Buffer, History, LanguageRegistry, Operation, Rope};
use clock::ReplicaId;
+pub use fs::*;
use futures::{Stream, StreamExt};
+use fuzzy::CharBag;
use gpui::{
executor, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext,
Task, UpgradeModelHandle, WeakModelHandle,
@@ -22,6 +19,7 @@ use postage::{
prelude::{Sink as _, Stream as _},
watch,
};
+use rpc_client as rpc;
use serde::Deserialize;
use smol::channel::{self, Sender};
use std::{
@@ -40,8 +38,10 @@ use std::{
},
time::{Duration, SystemTime},
};
+use sum_tree::Bias;
use sum_tree::{self, Edit, SeekTarget, SumTree};
-use zrpc::{PeerId, TypedEnvelope};
+use util::TryFutureExt;
+use zrpc::{proto, PeerId, TypedEnvelope};
lazy_static! {
static ref GITIGNORE: &'static OsStr = OsStr::new(".gitignore");
@@ -755,7 +755,7 @@ impl LocalWorktree {
let mut status = rpc.status();
while let Some(status) = status.recv().await {
if let Some(this) = this.upgrade(&cx) {
- let remote_id = if let Status::Connected { .. } = status {
+ let remote_id = if let rpc::Status::Connected { .. } = status {
let collaborator_logins = this.read_with(&cx, |this, _| {
this.as_local().unwrap().config.collaborators.clone()
});
@@ -2832,16 +2832,19 @@ impl<'a> TryFrom<(&'a CharBag, proto::Entry)> for Entry {
#[cfg(test)]
mod tests {
use super::*;
+ use rpc_client::test::FakeServer;
use crate::fs::FakeFs;
- use crate::test::*;
use anyhow::Result;
use fs::RealFs;
use rand::prelude::*;
use serde_json::json;
- use std::cell::RefCell;
- use std::rc::Rc;
- use std::time::UNIX_EPOCH;
- use std::{env, fmt::Write, time::SystemTime};
+ use std::{cell::RefCell, rc::Rc};
+ use std::{
+ env,
+ fmt::Write,
+ time::{SystemTime, UNIX_EPOCH},
+ };
+ use util::test::temp_tree;
#[gpui::test]
async fn test_traversal(cx: gpui::TestAppContext) {
@@ -17,7 +17,9 @@ path = "src/main.rs"
test-support = [
"buffer/test-support",
"gpui/test-support",
+ "rpc_client/test-support",
"tempdir",
+ "worktree/test-support",
"zrpc/test-support",
]
@@ -48,6 +50,7 @@ num_cpus = "1.13.0"
parking_lot = "0.11.1"
postage = { version = "0.4.1", features = ["futures-traits"] }
rand = "0.8.3"
+rpc_client = { path = "../rpc_client" }
rsa = "0.4"
rust-embed = { version = "6.2", features = ["include-exclude"] }
serde = { version = "1", features = ["derive"] }
@@ -66,6 +69,8 @@ toml = "0.5"
tree-sitter = "0.19.5"
tree-sitter-rust = "0.19.0"
url = "2.2"
+util = { path = "../util" }
+worktree = { path = "../worktree" }
zrpc = { path = "../zrpc" }
[dev-dependencies]
@@ -75,8 +80,11 @@ serde_json = { version = "1.0.64", features = ["preserve_order"] }
tempdir = { version = "0.3.7" }
unindent = "0.1.7"
buffer = { path = "../buffer", features = ["test-support"] }
-zrpc = { path = "../zrpc", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
+rpc_client = { path = "../rpc_client", features = ["test-support"] }
+util = { path = "../util", features = ["test-support"] }
+worktree = { path = "../worktree", features = ["test-support"] }
+zrpc = { path = "../zrpc", features = ["test-support"] }
[package.metadata.bundle]
icon = ["app-icon@2x.png", "app-icon.png"]
@@ -1,14 +1,11 @@
-use crate::{
- rpc::{self, Client},
- user::{User, UserStore},
- util::{post_inc, TryFutureExt},
-};
+use crate::user::{User, UserStore};
use anyhow::{anyhow, Context, Result};
use gpui::{
AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task, WeakModelHandle,
};
use postage::prelude::Stream;
use rand::prelude::*;
+use rpc_client as rpc;
use std::{
collections::{HashMap, HashSet},
mem,
@@ -17,6 +14,7 @@ use std::{
};
use sum_tree::{self, Bias, SumTree};
use time::OffsetDateTime;
+use util::{post_inc, TryFutureExt};
use zrpc::{
proto::{self, ChannelMessageSent},
TypedEnvelope,
@@ -25,7 +23,7 @@ use zrpc::{
pub struct ChannelList {
available_channels: Option<Vec<ChannelDetails>>,
channels: HashMap<u64, WeakModelHandle<Channel>>,
- rpc: Arc<Client>,
+ rpc: Arc<rpc::Client>,
user_store: ModelHandle<UserStore>,
_task: Task<Option<()>>,
}
@@ -42,7 +40,7 @@ pub struct Channel {
loaded_all_messages: bool,
next_pending_message_id: usize,
user_store: ModelHandle<UserStore>,
- rpc: Arc<Client>,
+ rpc: Arc<rpc::Client>,
rng: StdRng,
_subscription: rpc::Subscription,
}
@@ -187,7 +185,7 @@ impl Channel {
pub fn new(
details: ChannelDetails,
user_store: ModelHandle<UserStore>,
- rpc: Arc<Client>,
+ rpc: Arc<rpc::Client>,
cx: &mut ModelContext<Self>,
) -> Self {
let _subscription = rpc.subscribe_to_entity(details.id, cx, Self::handle_message_sent);
@@ -594,14 +592,15 @@ impl<'a> sum_tree::Dimension<'a, ChannelMessageSummary> for Count {
#[cfg(test)]
mod tests {
use super::*;
- use crate::test::{FakeHttpClient, FakeServer};
+ use crate::test::FakeHttpClient;
use gpui::TestAppContext;
+ use rpc_client::test::FakeServer;
use surf::http::Response;
#[gpui::test]
async fn test_channel_messages(mut cx: TestAppContext) {
let user_id = 5;
- let mut client = Client::new();
+ let mut client = rpc::Client::new();
let http_client = FakeHttpClient::new(|_| async move { Ok(Response::new(404)) });
let server = FakeServer::for_client(user_id, &mut client, &cx).await;
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
@@ -3,10 +3,7 @@ use std::sync::Arc;
use crate::{
channel::{Channel, ChannelEvent, ChannelList, ChannelMessage},
editor::Editor,
- rpc::{self, Client},
- theme,
- util::{ResultExt, TryFutureExt},
- Settings,
+ theme, Settings,
};
use gpui::{
action,
@@ -18,12 +15,14 @@ use gpui::{
ViewContext, ViewHandle,
};
use postage::{prelude::Stream, watch};
+use rpc_client as rpc;
use time::{OffsetDateTime, UtcOffset};
+use util::{ResultExt, TryFutureExt};
const MESSAGE_LOADING_THRESHOLD: usize = 50;
pub struct ChatPanel {
- rpc: Arc<Client>,
+ rpc: Arc<rpc::Client>,
channel_list: ModelHandle<ChannelList>,
active_channel: Option<(ModelHandle<Channel>, Subscription)>,
message_list: ListState,
@@ -48,7 +47,7 @@ pub fn init(cx: &mut MutableAppContext) {
impl ChatPanel {
pub fn new(
- rpc: Arc<Client>,
+ rpc: Arc<rpc::Client>,
channel_list: ModelHandle<ChannelList>,
settings: watch::Receiver<Settings>,
cx: &mut ViewContext<Self>,
@@ -2,14 +2,7 @@ pub mod display_map;
mod element;
pub mod movement;
-use crate::{
- project::ProjectPath,
- settings::Settings,
- theme::Theme,
- util::{post_inc, Bias},
- workspace,
- worktree::Worktree,
-};
+use crate::{project::ProjectPath, settings::Settings, theme::Theme, workspace};
use anyhow::Result;
use buffer::*;
use clock::ReplicaId;
@@ -35,6 +28,9 @@ use std::{
sync::Arc,
time::Duration,
};
+use sum_tree::Bias;
+use util::post_inc;
+use worktree::Worktree;
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
const MAX_LINE_LEN: usize = 1024;
@@ -357,8 +357,8 @@ impl ToDisplayPoint for Anchor {
#[cfg(test)]
mod tests {
use super::*;
- use crate::{editor::movement, test::*, util::RandomCharIter};
- use buffer::{History, Language, LanguageConfig, SelectionGoal, SyntaxTheme};
+ use crate::{editor::movement, test::*};
+ use buffer::{History, Language, LanguageConfig, RandomCharIter, SelectionGoal, SyntaxTheme};
use gpui::{color::Color, MutableAppContext};
use rand::{prelude::StdRng, Rng};
use std::{env, sync::Arc};
@@ -1128,7 +1128,8 @@ impl FoldEdit {
#[cfg(test)]
mod tests {
use super::*;
- use crate::{editor::ToPoint, test::sample_text, util::RandomCharIter};
+ use crate::{editor::ToPoint, test::sample_text};
+ use buffer::RandomCharIter;
use rand::prelude::*;
use std::{env, mem};
use Bias::{Left, Right};
@@ -1,8 +1,8 @@
use super::fold_map::{self, FoldEdit, FoldPoint, Snapshot as FoldSnapshot};
-use crate::util::Bias;
use buffer::{rope, HighlightId};
use parking_lot::Mutex;
use std::{mem, ops::Range};
+use sum_tree::Bias;
pub struct TabMap(Mutex<Snapshot>);
@@ -2,13 +2,13 @@ use super::{
fold_map,
tab_map::{self, Edit as TabEdit, Snapshot as TabSnapshot, TabPoint, TextSummary},
};
-use crate::{editor::Point, util::Bias};
+use crate::editor::Point;
use buffer::HighlightId;
use gpui::{fonts::FontId, text_layout::LineWrapper, Entity, ModelContext, Task};
use lazy_static::lazy_static;
use smol::future::yield_now;
use std::{collections::VecDeque, ops::Range, time::Duration};
-use sum_tree::{self, Cursor, SumTree};
+use sum_tree::{self, Bias, Cursor, SumTree};
pub struct WrapMap {
snapshot: Snapshot,
@@ -902,8 +902,8 @@ mod tests {
Buffer,
},
test::Observer,
- util::RandomCharIter,
};
+ use buffer::RandomCharIter;
use rand::prelude::*;
use std::env;
@@ -3,7 +3,6 @@ use crate::{
fuzzy::PathMatch,
project::{Project, ProjectPath},
settings::Settings,
- util,
workspace::Workspace,
};
use gpui::{
@@ -26,6 +25,7 @@ use std::{
Arc,
},
};
+use util::post_inc;
pub struct FileFinder {
handle: WeakViewHandle<Self>,
@@ -315,7 +315,7 @@ impl FileFinder {
editor::Event::Edited => {
let query = self.query_editor.update(cx, |buffer, cx| buffer.text(cx));
if query.is_empty() {
- self.latest_search_id = util::post_inc(&mut self.search_count);
+ self.latest_search_id = post_inc(&mut self.search_count);
self.matches.clear();
cx.notify();
} else {
@@ -422,12 +422,12 @@ mod tests {
use super::*;
use crate::{
editor::{self, Insert},
- fs::FakeFs,
test::test_app_state,
workspace::Workspace,
};
use serde_json::json;
use std::path::PathBuf;
+ use worktree::fs::FakeFs;
#[gpui::test]
async fn test_matching_paths(mut cx: gpui::TestAppContext) {
@@ -1,12 +1,10 @@
-use crate::{
- util,
- worktree::{EntryKind, Snapshot},
-};
use gpui::executor;
use std::{
cmp,
sync::{atomic::AtomicBool, Arc},
};
+use util;
+use worktree::{EntryKind, Snapshot};
pub use fuzzy::*;
@@ -3,7 +3,6 @@ pub mod channel;
pub mod chat_panel;
pub mod editor;
pub mod file_finder;
-pub mod fs;
mod fuzzy;
pub mod http;
pub mod language;
@@ -11,27 +10,25 @@ pub mod menus;
pub mod people_panel;
pub mod project;
pub mod project_panel;
-pub mod rpc;
pub mod settings;
#[cfg(any(test, feature = "test-support"))]
pub mod test;
pub mod theme;
pub mod theme_selector;
pub mod user;
-mod util;
pub mod workspace;
-pub mod worktree;
-use crate::util::TryFutureExt;
pub use buffer;
use buffer::LanguageRegistry;
use channel::ChannelList;
use gpui::{action, keymap::Binding, ModelHandle};
use parking_lot::Mutex;
use postage::watch;
-use std::sync::Arc;
-
+pub use rpc_client as rpc;
pub use settings::Settings;
+use std::sync::Arc;
+use util::TryFutureExt;
+pub use worktree::{self, fs};
action!(About);
action!(Quit);
@@ -1,25 +1,24 @@
use crate::{
- fs::Fs,
fuzzy::{self, PathMatch},
- rpc::Client,
- util::TryFutureExt as _,
- worktree::{self, Worktree},
AppState,
};
use anyhow::Result;
use buffer::LanguageRegistry;
use futures::Future;
use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task};
+use rpc_client as rpc;
use std::{
path::Path,
sync::{atomic::AtomicBool, Arc},
};
+use util::TryFutureExt as _;
+use worktree::{fs::Fs, Worktree};
pub struct Project {
worktrees: Vec<ModelHandle<Worktree>>,
active_entry: Option<ProjectEntry>,
languages: Arc<LanguageRegistry>,
- rpc: Arc<Client>,
+ rpc: Arc<rpc::Client>,
fs: Arc<dyn Fs>,
}
@@ -237,12 +236,11 @@ impl Entity for Project {
#[cfg(test)]
mod tests {
use super::*;
- use crate::{
- fs::RealFs,
- test::{temp_tree, test_app_state},
- };
+ use crate::test::test_app_state;
use serde_json::json;
use std::{os::unix, path::PathBuf};
+ use util::test::temp_tree;
+ use worktree::fs::RealFs;
#[gpui::test]
async fn test_populate_and_search(mut cx: gpui::TestAppContext) {
@@ -2,7 +2,6 @@ use crate::{
project::{self, Project, ProjectEntry, ProjectPath},
theme,
workspace::Workspace,
- worktree::{self, Worktree},
Settings,
};
use gpui::{
@@ -26,6 +25,7 @@ use std::{
ffi::OsStr,
ops::Range,
};
+use worktree::Worktree;
pub struct ProjectPanel {
project: ModelHandle<Project>,
@@ -1,32 +1,21 @@
use crate::{
assets::Assets,
channel::ChannelList,
- fs::FakeFs,
http::{HttpClient, Request, Response, ServerResponse},
language,
- rpc::{self, Client, Credentials, EstablishConnectionError},
settings::{self, ThemeRegistry},
user::UserStore,
AppState,
};
-use anyhow::{anyhow, Result};
+use anyhow::Result;
use buffer::LanguageRegistry;
use futures::{future::BoxFuture, Future};
-use gpui::{AsyncAppContext, Entity, ModelHandle, MutableAppContext, TestAppContext};
+use gpui::{Entity, ModelHandle, MutableAppContext};
use parking_lot::Mutex;
-use postage::{mpsc, prelude::Stream as _};
+use rpc_client as rpc;
use smol::channel;
-use std::{
- fmt,
- marker::PhantomData,
- path::{Path, PathBuf},
- sync::{
- atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst},
- Arc,
- },
-};
-use tempdir::TempDir;
-use zrpc::{proto, Connection, ConnectionId, Peer, Receipt, TypedEnvelope};
+use std::{fmt, marker::PhantomData, sync::Arc};
+use worktree::fs::FakeFs;
#[cfg(test)]
#[ctor::ctor]
@@ -47,41 +36,6 @@ pub fn sample_text(rows: usize, cols: usize) -> String {
text
}
-pub fn temp_tree(tree: serde_json::Value) -> TempDir {
- let dir = TempDir::new("").unwrap();
- write_tree(dir.path(), tree);
- dir
-}
-
-fn write_tree(path: &Path, tree: serde_json::Value) {
- use serde_json::Value;
- use std::fs;
-
- if let Value::Object(map) = tree {
- for (name, contents) in map {
- let mut path = PathBuf::from(path);
- path.push(name);
- match contents {
- Value::Object(_) => {
- fs::create_dir(&path).unwrap();
- write_tree(&path, contents);
- }
- Value::Null => {
- fs::create_dir(&path).unwrap();
- }
- Value::String(contents) => {
- fs::write(&path, contents).unwrap();
- }
- _ => {
- panic!("JSON object must contain only objects, strings, or null");
- }
- }
- }
- } else {
- panic!("You must pass a JSON object to this helper")
- }
-}
-
pub fn test_app_state(cx: &mut MutableAppContext) -> Arc<AppState> {
let (settings_tx, settings) = settings::test(cx);
let mut languages = LanguageRegistry::new();
@@ -125,150 +79,6 @@ impl<T: Entity> Observer<T> {
}
}
-pub struct FakeServer {
- peer: Arc<Peer>,
- incoming: Mutex<Option<mpsc::Receiver<Box<dyn proto::AnyTypedEnvelope>>>>,
- connection_id: Mutex<Option<ConnectionId>>,
- forbid_connections: AtomicBool,
- auth_count: AtomicUsize,
- access_token: AtomicUsize,
- user_id: u64,
-}
-
-impl FakeServer {
- pub async fn for_client(
- client_user_id: u64,
- client: &mut Arc<Client>,
- cx: &TestAppContext,
- ) -> Arc<Self> {
- let server = Arc::new(Self {
- peer: Peer::new(),
- incoming: Default::default(),
- connection_id: Default::default(),
- forbid_connections: Default::default(),
- auth_count: Default::default(),
- access_token: Default::default(),
- user_id: client_user_id,
- });
-
- Arc::get_mut(client)
- .unwrap()
- .override_authenticate({
- let server = server.clone();
- move |cx| {
- server.auth_count.fetch_add(1, SeqCst);
- let access_token = server.access_token.load(SeqCst).to_string();
- cx.spawn(move |_| async move {
- Ok(Credentials {
- user_id: client_user_id,
- access_token,
- })
- })
- }
- })
- .override_establish_connection({
- let server = server.clone();
- move |credentials, cx| {
- let credentials = credentials.clone();
- cx.spawn({
- let server = server.clone();
- move |cx| async move { server.establish_connection(&credentials, &cx).await }
- })
- }
- });
-
- client
- .authenticate_and_connect(&cx.to_async())
- .await
- .unwrap();
- server
- }
-
- pub async fn disconnect(&self) {
- self.peer.disconnect(self.connection_id()).await;
- self.connection_id.lock().take();
- self.incoming.lock().take();
- }
-
- async fn establish_connection(
- &self,
- credentials: &Credentials,
- cx: &AsyncAppContext,
- ) -> Result<Connection, EstablishConnectionError> {
- assert_eq!(credentials.user_id, self.user_id);
-
- if self.forbid_connections.load(SeqCst) {
- Err(EstablishConnectionError::Other(anyhow!(
- "server is forbidding connections"
- )))?
- }
-
- if credentials.access_token != self.access_token.load(SeqCst).to_string() {
- Err(EstablishConnectionError::Unauthorized)?
- }
-
- let (client_conn, server_conn, _) = Connection::in_memory();
- let (connection_id, io, incoming) = self.peer.add_connection(server_conn).await;
- cx.background().spawn(io).detach();
- *self.incoming.lock() = Some(incoming);
- *self.connection_id.lock() = Some(connection_id);
- Ok(client_conn)
- }
-
- pub fn auth_count(&self) -> usize {
- self.auth_count.load(SeqCst)
- }
-
- pub fn roll_access_token(&self) {
- self.access_token.fetch_add(1, SeqCst);
- }
-
- pub fn forbid_connections(&self) {
- self.forbid_connections.store(true, SeqCst);
- }
-
- pub fn allow_connections(&self) {
- self.forbid_connections.store(false, SeqCst);
- }
-
- pub async fn send<T: proto::EnvelopedMessage>(&self, message: T) {
- self.peer.send(self.connection_id(), message).await.unwrap();
- }
-
- pub async fn receive<M: proto::EnvelopedMessage>(&self) -> Result<TypedEnvelope<M>> {
- let message = self
- .incoming
- .lock()
- .as_mut()
- .expect("not connected")
- .recv()
- .await
- .ok_or_else(|| anyhow!("other half hung up"))?;
- let type_name = message.payload_type_name();
- Ok(*message
- .into_any()
- .downcast::<TypedEnvelope<M>>()
- .unwrap_or_else(|_| {
- panic!(
- "fake server received unexpected message type: {:?}",
- type_name
- );
- }))
- }
-
- pub async fn respond<T: proto::RequestMessage>(
- &self,
- receipt: Receipt<T>,
- response: T::Response,
- ) {
- self.peer.respond(receipt, response).await.unwrap()
- }
-
- fn connection_id(&self) -> ConnectionId {
- self.connection_id.lock().expect("not connected")
- }
-}
-
pub struct FakeHttpClient {
handler:
Box<dyn 'static + Send + Sync + Fn(Request) -> BoxFuture<'static, Result<ServerResponse>>>,
@@ -1,16 +1,14 @@
-use crate::{
- http::{HttpClient, Method, Request, Url},
- rpc::{Client, Status},
- util::TryFutureExt,
-};
+use crate::http::{HttpClient, Method, Request, Url};
use anyhow::{anyhow, Context, Result};
use futures::future;
use gpui::{AsyncAppContext, Entity, ImageData, ModelContext, ModelHandle, Task};
use postage::{prelude::Stream, sink::Sink, watch};
+use rpc_client as rpc;
use std::{
collections::{HashMap, HashSet},
sync::Arc,
};
+use util::TryFutureExt as _;
use zrpc::{proto, TypedEnvelope};
#[derive(Debug)]
@@ -38,7 +36,7 @@ pub struct UserStore {
users: HashMap<u64, Arc<User>>,
current_user: watch::Receiver<Option<Arc<User>>>,
collaborators: Arc<[Collaborator]>,
- rpc: Arc<Client>,
+ rpc: Arc<rpc::Client>,
http: Arc<dyn HttpClient>,
_maintain_collaborators: Task<()>,
_maintain_current_user: Task<()>,
@@ -51,7 +49,11 @@ impl Entity for UserStore {
}
impl UserStore {
- pub fn new(rpc: Arc<Client>, http: Arc<dyn HttpClient>, cx: &mut ModelContext<Self>) -> Self {
+ pub fn new(
+ rpc: Arc<rpc::Client>,
+ http: Arc<dyn HttpClient>,
+ cx: &mut ModelContext<Self>,
+ ) -> Self {
let (mut current_user_tx, current_user_rx) = watch::channel();
let (mut update_collaborators_tx, mut update_collaborators_rx) =
watch::channel::<Option<proto::UpdateCollaborators>>();
@@ -82,7 +84,7 @@ impl UserStore {
let mut status = rpc.status();
while let Some(status) = status.recv().await {
match status {
- Status::Connected { .. } => {
+ rpc::Status::Connected { .. } => {
if let Some((this, user_id)) = this.upgrade(&cx).zip(rpc.user_id()) {
let user = this
.update(&mut cx, |this, cx| this.fetch_user(user_id, cx))
@@ -91,7 +93,7 @@ impl UserStore {
current_user_tx.send(user).await.ok();
}
}
- Status::SignedOut => {
+ rpc::Status::SignedOut => {
current_user_tx.send(None).await.ok();
}
_ => {}
@@ -12,7 +12,6 @@ use crate::{
settings::Settings,
user,
workspace::sidebar::{Side, Sidebar, SidebarItemId, ToggleSidebarItem, ToggleSidebarItemFocus},
- worktree::Worktree,
AppState, Authenticate,
};
use anyhow::Result;
@@ -38,6 +37,7 @@ use std::{
path::{Path, PathBuf},
sync::Arc,
};
+use worktree::Worktree;
action!(Open, Arc<AppState>);
action!(OpenPaths, OpenParams);
@@ -1159,10 +1159,11 @@ mod tests {
use crate::{
editor::{Editor, Insert},
fs::FakeFs,
- test::{temp_tree, test_app_state},
+ test::test_app_state,
};
use serde_json::json;
use std::collections::HashSet;
+ use util::test::temp_tree;
#[gpui::test]
async fn test_open_paths_action(mut cx: gpui::TestAppContext) {