Extract worktree, rpc_client, and util crates

Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
Nathan Sobo 2021-10-04 13:07:35 -06:00
parent 154620233b
commit c236b0828c
29 changed files with 489 additions and 307 deletions

66
Cargo.lock generated
View File

@ -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",
]

View File

@ -6,8 +6,11 @@ members = [
"fuzzy",
"gpui",
"gpui_macros",
"rpc_client",
"server",
"sum_tree",
"util",
"worktree",
"zed",
"zrpc"
]

View File

@ -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::*;

55
rpc_client/Cargo.toml Normal file
View File

@ -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" }

View File

@ -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},

156
rpc_client/src/test.rs Normal file
View File

@ -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")
}
}

15
util/Cargo.toml Normal file
View File

@ -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 }

View File

@ -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;

37
util/src/test.rs Normal file
View File

@ -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")
}
}

61
worktree/Cargo.toml Normal file
View File

@ -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"] }

View File

@ -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) {

View File

@ -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"]

View File

@ -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));

View File

@ -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>,

View File

@ -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;

View File

@ -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};

View File

@ -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};

View File

@ -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>);

View File

@ -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;

View File

@ -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) {

View File

@ -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::*;

View File

@ -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);

View File

@ -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) {

View File

@ -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>,

View File

@ -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>>>,

View File

@ -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();
}
_ => {}

View File

@ -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) {