mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-07 20:39:04 +03:00
Add copilot crate
Refactor HTTP and github release downloading into util Lazily download / upgrade the copilot LSP from Zed Co-authored-by: Max <max@zed.dev> Co-Authored-By: Antonio <antonio@zed.dev>
This commit is contained in:
parent
35b2aceffb
commit
455cdc8b37
22
Cargo.lock
generated
22
Cargo.lock
generated
@ -1113,7 +1113,6 @@ dependencies = [
|
||||
"futures 0.3.25",
|
||||
"gpui",
|
||||
"image",
|
||||
"isahc",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"parking_lot 0.11.2",
|
||||
@ -1332,6 +1331,22 @@ dependencies = [
|
||||
"theme",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "copilot"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-compression",
|
||||
"client",
|
||||
"futures 0.3.25",
|
||||
"gpui",
|
||||
"lsp",
|
||||
"settings",
|
||||
"smol",
|
||||
"util",
|
||||
"workspace",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.3"
|
||||
@ -7500,11 +7515,15 @@ dependencies = [
|
||||
"dirs 3.0.2",
|
||||
"futures 0.3.25",
|
||||
"git2",
|
||||
"isahc",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smol",
|
||||
"tempdir",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -8460,6 +8479,7 @@ dependencies = [
|
||||
"collections",
|
||||
"command_palette",
|
||||
"context_menu",
|
||||
"copilot",
|
||||
"ctor",
|
||||
"db",
|
||||
"diagnostics",
|
||||
|
@ -13,6 +13,7 @@ members = [
|
||||
"crates/collections",
|
||||
"crates/command_palette",
|
||||
"crates/context_menu",
|
||||
"crates/copilot",
|
||||
"crates/db",
|
||||
"crates/diagnostics",
|
||||
"crates/drag_and_drop",
|
||||
|
@ -1,8 +1,7 @@
|
||||
mod update_notification;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use client::{http::HttpClient, ZED_SECRET_CLIENT_TOKEN};
|
||||
use client::{ZED_APP_PATH, ZED_APP_VERSION};
|
||||
use client::{ZED_APP_PATH, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN};
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use gpui::{
|
||||
actions, platform::AppVersion, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle,
|
||||
@ -14,6 +13,7 @@ use smol::{fs::File, io::AsyncReadExt, process::Command};
|
||||
use std::{ffi::OsString, sync::Arc, time::Duration};
|
||||
use update_notification::UpdateNotification;
|
||||
use util::channel::ReleaseChannel;
|
||||
use util::http::HttpClient;
|
||||
use workspace::Workspace;
|
||||
|
||||
const SHOULD_SHOW_UPDATE_NOTIFICATION_KEY: &str = "auto-updater-should-show-updated-notification";
|
||||
|
@ -23,7 +23,6 @@ async-recursion = "0.3"
|
||||
async-tungstenite = { version = "0.16", features = ["async-tls"] }
|
||||
futures = "0.3"
|
||||
image = "0.23"
|
||||
isahc = "1.7"
|
||||
lazy_static = "1.4.0"
|
||||
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
|
||||
parking_lot = "0.11.1"
|
||||
|
@ -1,7 +1,6 @@
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub mod test;
|
||||
|
||||
pub mod http;
|
||||
pub mod telemetry;
|
||||
pub mod user;
|
||||
|
||||
@ -18,7 +17,6 @@ use gpui::{
|
||||
AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AnyWeakViewHandle, AppContext, AppVersion,
|
||||
AsyncAppContext, Entity, ModelHandle, MutableAppContext, Task, View, ViewContext, ViewHandle,
|
||||
};
|
||||
use http::HttpClient;
|
||||
use lazy_static::lazy_static;
|
||||
use parking_lot::RwLock;
|
||||
use postage::watch;
|
||||
@ -41,6 +39,7 @@ use telemetry::Telemetry;
|
||||
use thiserror::Error;
|
||||
use url::Url;
|
||||
use util::channel::ReleaseChannel;
|
||||
use util::http::HttpClient;
|
||||
use util::{ResultExt, TryFutureExt};
|
||||
|
||||
pub use rpc::*;
|
||||
@ -130,7 +129,7 @@ pub enum EstablishConnectionError {
|
||||
#[error("{0}")]
|
||||
Other(#[from] anyhow::Error),
|
||||
#[error("{0}")]
|
||||
Http(#[from] http::Error),
|
||||
Http(#[from] util::http::Error),
|
||||
#[error("{0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
#[error("{0}")]
|
||||
@ -1396,10 +1395,11 @@ pub fn decode_worktree_url(url: &str) -> Option<(u64, String)> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test::{FakeHttpClient, FakeServer};
|
||||
use crate::test::FakeServer;
|
||||
use gpui::{executor::Deterministic, TestAppContext};
|
||||
use parking_lot::Mutex;
|
||||
use std::future;
|
||||
use util::http::FakeHttpClient;
|
||||
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_reconnection(cx: &mut TestAppContext) {
|
||||
|
@ -1,57 +0,0 @@
|
||||
pub use anyhow::{anyhow, Result};
|
||||
use futures::future::BoxFuture;
|
||||
use isahc::{
|
||||
config::{Configurable, RedirectPolicy},
|
||||
AsyncBody,
|
||||
};
|
||||
pub use isahc::{
|
||||
http::{Method, Uri},
|
||||
Error,
|
||||
};
|
||||
use smol::future::FutureExt;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
pub use url::Url;
|
||||
|
||||
pub type Request = isahc::Request<AsyncBody>;
|
||||
pub type Response = isahc::Response<AsyncBody>;
|
||||
|
||||
pub trait HttpClient: Send + Sync {
|
||||
fn send(&self, req: Request) -> BoxFuture<Result<Response, Error>>;
|
||||
|
||||
fn get<'a>(
|
||||
&'a self,
|
||||
uri: &str,
|
||||
body: AsyncBody,
|
||||
follow_redirects: bool,
|
||||
) -> BoxFuture<'a, Result<Response, Error>> {
|
||||
let request = isahc::Request::builder()
|
||||
.redirect_policy(if follow_redirects {
|
||||
RedirectPolicy::Follow
|
||||
} else {
|
||||
RedirectPolicy::None
|
||||
})
|
||||
.method(Method::GET)
|
||||
.uri(uri)
|
||||
.body(body);
|
||||
match request {
|
||||
Ok(request) => self.send(request),
|
||||
Err(error) => async move { Err(error.into()) }.boxed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client() -> Arc<dyn HttpClient> {
|
||||
Arc::new(
|
||||
isahc::HttpClient::builder()
|
||||
.connect_timeout(Duration::from_secs(5))
|
||||
.low_speed_timeout(100, Duration::from_secs(5))
|
||||
.build()
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
impl HttpClient for isahc::HttpClient {
|
||||
fn send(&self, req: Request) -> BoxFuture<Result<Response, Error>> {
|
||||
Box::pin(async move { self.send_async(req).await })
|
||||
}
|
||||
}
|
@ -1,11 +1,9 @@
|
||||
use crate::http::HttpClient;
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use gpui::{
|
||||
executor::Background,
|
||||
serde_json::{self, value::Map, Value},
|
||||
AppContext, Task,
|
||||
};
|
||||
use isahc::Request;
|
||||
use lazy_static::lazy_static;
|
||||
use parking_lot::Mutex;
|
||||
use serde::Serialize;
|
||||
@ -19,6 +17,7 @@ use std::{
|
||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
use tempfile::NamedTempFile;
|
||||
use util::http::HttpClient;
|
||||
use util::{channel::ReleaseChannel, post_inc, ResultExt, TryFutureExt};
|
||||
use uuid::Uuid;
|
||||
|
||||
@ -220,10 +219,10 @@ impl Telemetry {
|
||||
"App": true
|
||||
}),
|
||||
}])?;
|
||||
let request = Request::post(MIXPANEL_ENGAGE_URL)
|
||||
.header("Content-Type", "application/json")
|
||||
.body(json_bytes.into())?;
|
||||
this.http_client.send(request).await?;
|
||||
|
||||
this.http_client
|
||||
.post_json(MIXPANEL_ENGAGE_URL, json_bytes.into())
|
||||
.await?;
|
||||
anyhow::Ok(())
|
||||
}
|
||||
.log_err(),
|
||||
@ -316,10 +315,9 @@ impl Telemetry {
|
||||
|
||||
json_bytes.clear();
|
||||
serde_json::to_writer(&mut json_bytes, &events)?;
|
||||
let request = Request::post(MIXPANEL_EVENTS_URL)
|
||||
.header("Content-Type", "application/json")
|
||||
.body(json_bytes.into())?;
|
||||
this.http_client.send(request).await?;
|
||||
this.http_client
|
||||
.post_json(MIXPANEL_EVENTS_URL, json_bytes.into())
|
||||
.await?;
|
||||
anyhow::Ok(())
|
||||
}
|
||||
.log_err(),
|
||||
|
@ -1,16 +1,14 @@
|
||||
use crate::{
|
||||
http::{self, HttpClient, Request, Response},
|
||||
Client, Connection, Credentials, EstablishConnectionError, UserStore,
|
||||
};
|
||||
use crate::{Client, Connection, Credentials, EstablishConnectionError, UserStore};
|
||||
use anyhow::{anyhow, Result};
|
||||
use futures::{future::BoxFuture, stream::BoxStream, Future, StreamExt};
|
||||
use futures::{stream::BoxStream, StreamExt};
|
||||
use gpui::{executor, ModelHandle, TestAppContext};
|
||||
use parking_lot::Mutex;
|
||||
use rpc::{
|
||||
proto::{self, GetPrivateUserInfo, GetPrivateUserInfoResponse},
|
||||
ConnectionId, Peer, Receipt, TypedEnvelope,
|
||||
};
|
||||
use std::{fmt, rc::Rc, sync::Arc};
|
||||
use std::{rc::Rc, sync::Arc};
|
||||
use util::http::FakeHttpClient;
|
||||
|
||||
pub struct FakeServer {
|
||||
peer: Arc<Peer>,
|
||||
@ -219,46 +217,3 @@ impl Drop for FakeServer {
|
||||
self.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FakeHttpClient {
|
||||
handler: Box<
|
||||
dyn 'static
|
||||
+ Send
|
||||
+ Sync
|
||||
+ Fn(Request) -> BoxFuture<'static, Result<Response, http::Error>>,
|
||||
>,
|
||||
}
|
||||
|
||||
impl FakeHttpClient {
|
||||
pub fn create<Fut, F>(handler: F) -> Arc<dyn HttpClient>
|
||||
where
|
||||
Fut: 'static + Send + Future<Output = Result<Response, http::Error>>,
|
||||
F: 'static + Send + Sync + Fn(Request) -> Fut,
|
||||
{
|
||||
Arc::new(Self {
|
||||
handler: Box::new(move |req| Box::pin(handler(req))),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_404_response() -> Arc<dyn HttpClient> {
|
||||
Self::create(|_| async move {
|
||||
Ok(isahc::Response::builder()
|
||||
.status(404)
|
||||
.body(Default::default())
|
||||
.unwrap())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for FakeHttpClient {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("FakeHttpClient").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpClient for FakeHttpClient {
|
||||
fn send(&self, req: Request) -> BoxFuture<Result<Response, crate::http::Error>> {
|
||||
let future = (self.handler)(req);
|
||||
Box::pin(async move { future.await.map(Into::into) })
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{http::HttpClient, proto, Client, Status, TypedEnvelope};
|
||||
use super::{proto, Client, Status, TypedEnvelope};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use collections::{hash_map::Entry, HashMap, HashSet};
|
||||
use futures::{channel::mpsc, future, AsyncReadExt, Future, StreamExt};
|
||||
@ -7,6 +7,7 @@ use postage::{sink::Sink, watch};
|
||||
use rpc::proto::{RequestMessage, UsersResponse};
|
||||
use settings::Settings;
|
||||
use std::sync::{Arc, Weak};
|
||||
use util::http::HttpClient;
|
||||
use util::{StaffMode, TryFutureExt as _};
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
|
@ -7,8 +7,7 @@ use crate::{
|
||||
use anyhow::anyhow;
|
||||
use call::ActiveCall;
|
||||
use client::{
|
||||
self, proto::PeerId, test::FakeHttpClient, Client, Connection, Credentials,
|
||||
EstablishConnectionError, UserStore,
|
||||
self, proto::PeerId, Client, Connection, Credentials, EstablishConnectionError, UserStore,
|
||||
};
|
||||
use collections::{HashMap, HashSet};
|
||||
use fs::FakeFs;
|
||||
@ -28,6 +27,7 @@ use std::{
|
||||
},
|
||||
};
|
||||
use theme::ThemeRegistry;
|
||||
use util::http::FakeHttpClient;
|
||||
use workspace::Workspace;
|
||||
|
||||
mod integration_tests;
|
||||
|
21
crates/copilot/Cargo.toml
Normal file
21
crates/copilot/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "copilot"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
path = "src/copilot.rs"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
gpui = { path = "../gpui" }
|
||||
settings = { path = "../settings" }
|
||||
lsp = { path = "../lsp" }
|
||||
util = { path = "../util" }
|
||||
client = { path = "../client" }
|
||||
workspace = { path = "../workspace" }
|
||||
async-compression = { version = "0.3", features = ["gzip", "futures-bufread"] }
|
||||
anyhow = "1.0"
|
||||
smol = "1.2.5"
|
||||
futures = "0.3"
|
21
crates/copilot/readme.md
Normal file
21
crates/copilot/readme.md
Normal file
@ -0,0 +1,21 @@
|
||||
Basic idea:
|
||||
|
||||
Run the `copilot-node-server` as an LSP
|
||||
Reuse our LSP code to use it
|
||||
|
||||
Issues:
|
||||
- Re-use our github authentication for copilot - ??
|
||||
- Integrate Copilot suggestions with `SuggestionMap`
|
||||
|
||||
|
||||
|
||||
THE PLAN:
|
||||
- Copilot crate.
|
||||
- Instantiated with a project / listens to them
|
||||
- Listens to events from the project about adding worktrees
|
||||
- Manages the copilot language servers per worktree
|
||||
- Editor <-?-> Copilot
|
||||
|
||||
|
||||
From anotonio in Slack:
|
||||
- soooo regarding copilot i was thinking… if it doesn’t really behave like a language server (but they implemented like that because of the protocol, etc.), it might be nice to just have a singleton that is not even set when we’re signed out. when we sign in, we set the global. then, the editor can access the global (e.g. cx.global::<Option<Copilot>>) after typing some character (and with some debouncing mechanism). the Copilot struct could hold a lsp::LanguageServer and then our job is to write an adapter that can then be used to start the language server, but it’s kinda orthogonal to the language servers we store in the project. what do you think?
|
97
crates/copilot/src/copilot.rs
Normal file
97
crates/copilot/src/copilot.rs
Normal file
@ -0,0 +1,97 @@
|
||||
use anyhow::{anyhow, Ok};
|
||||
use async_compression::futures::bufread::GzipDecoder;
|
||||
use client::Client;
|
||||
use gpui::{actions, MutableAppContext};
|
||||
use smol::{fs, io::BufReader, stream::StreamExt};
|
||||
use std::{env::consts, path::PathBuf, sync::Arc};
|
||||
use util::{
|
||||
fs::remove_matching, github::latest_github_release, http::HttpClient, paths, ResultExt,
|
||||
};
|
||||
|
||||
actions!(copilot, [SignIn]);
|
||||
|
||||
pub fn init(client: Arc<Client>, cx: &mut MutableAppContext) {
|
||||
cx.add_global_action(move |_: &SignIn, cx: &mut MutableAppContext| {
|
||||
Copilot::sign_in(client.http_client(), cx)
|
||||
});
|
||||
}
|
||||
|
||||
struct Copilot {
|
||||
copilot_server: PathBuf,
|
||||
}
|
||||
|
||||
impl Copilot {
|
||||
fn sign_in(http: Arc<dyn HttpClient>, cx: &mut MutableAppContext) {
|
||||
let copilot = cx.global::<Option<Arc<Copilot>>>().clone();
|
||||
|
||||
cx.spawn(|mut cx| async move {
|
||||
// Lazily download / initialize copilot LSP
|
||||
let copilot = if let Some(copilot) = copilot {
|
||||
copilot
|
||||
} else {
|
||||
let copilot_server = get_lsp_binary(http).await?; // TODO: Make this error user visible
|
||||
let new_copilot = Arc::new(Copilot { copilot_server });
|
||||
cx.update({
|
||||
let new_copilot = new_copilot.clone();
|
||||
move |cx| cx.set_global(Some(new_copilot.clone()))
|
||||
});
|
||||
new_copilot
|
||||
};
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_lsp_binary(http: Arc<dyn HttpClient>) -> anyhow::Result<PathBuf> {
|
||||
///Check for the latest copilot language server and download it if we haven't already
|
||||
async fn fetch_latest(http: Arc<dyn HttpClient>) -> anyhow::Result<PathBuf> {
|
||||
let release = latest_github_release("zed-industries/copilotserver", http.clone()).await?;
|
||||
let asset_name = format!("copilot-darwin-{}.gz", consts::ARCH);
|
||||
let asset = release
|
||||
.assets
|
||||
.iter()
|
||||
.find(|asset| asset.name == asset_name)
|
||||
.ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?;
|
||||
|
||||
let destination_path =
|
||||
paths::COPILOT_DIR.join(format!("copilot-{}-{}", release.name, consts::ARCH));
|
||||
|
||||
if fs::metadata(&destination_path).await.is_err() {
|
||||
let mut response = http
|
||||
.get(&asset.browser_download_url, Default::default(), true)
|
||||
.await
|
||||
.map_err(|err| anyhow!("error downloading release: {}", err))?;
|
||||
let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut()));
|
||||
let mut file = fs::File::create(&destination_path).await?;
|
||||
futures::io::copy(decompressed_bytes, &mut file).await?;
|
||||
fs::set_permissions(
|
||||
&destination_path,
|
||||
<fs::Permissions as fs::unix::PermissionsExt>::from_mode(0o755),
|
||||
)
|
||||
.await?;
|
||||
|
||||
remove_matching(&paths::COPILOT_DIR, |entry| entry != destination_path).await;
|
||||
}
|
||||
|
||||
Ok(destination_path)
|
||||
}
|
||||
|
||||
match fetch_latest(http).await {
|
||||
ok @ Result::Ok(..) => ok,
|
||||
e @ Err(..) => {
|
||||
e.log_err();
|
||||
// Fetch a cached binary, if it exists
|
||||
(|| async move {
|
||||
let mut last = None;
|
||||
let mut entries = fs::read_dir(paths::COPILOT_DIR.as_path()).await?;
|
||||
while let Some(entry) = entries.next().await {
|
||||
last = Some(entry?.path());
|
||||
}
|
||||
last.ok_or_else(|| anyhow!("no cached binary"))
|
||||
})()
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
@ -10,7 +10,6 @@ mod buffer_tests;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use client::http::HttpClient;
|
||||
use collections::HashMap;
|
||||
use futures::{
|
||||
channel::oneshot,
|
||||
@ -45,6 +44,7 @@ use syntax_map::SyntaxSnapshot;
|
||||
use theme::{SyntaxTheme, Theme};
|
||||
use tree_sitter::{self, Query};
|
||||
use unicase::UniCase;
|
||||
use util::http::HttpClient;
|
||||
use util::{merge_json_value_into, post_inc, ResultExt, TryFutureExt as _, UnwrapFuture};
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
|
@ -568,7 +568,7 @@ impl Project {
|
||||
|
||||
let mut languages = LanguageRegistry::test();
|
||||
languages.set_executor(cx.background());
|
||||
let http_client = client::test::FakeHttpClient::with_404_response();
|
||||
let http_client = util::http::FakeHttpClient::with_404_response();
|
||||
let client = cx.update(|cx| client::Client::new(http_client.clone(), cx));
|
||||
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
|
||||
let project =
|
||||
|
@ -3114,13 +3114,14 @@ impl<'a> TryFrom<(&'a CharBag, proto::Entry)> for Entry {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use client::test::FakeHttpClient;
|
||||
use fs::repository::FakeGitRepository;
|
||||
use fs::{FakeFs, RealFs};
|
||||
use gpui::{executor::Deterministic, TestAppContext};
|
||||
use rand::prelude::*;
|
||||
use serde_json::json;
|
||||
use std::{env, fmt::Write};
|
||||
use util::http::FakeHttpClient;
|
||||
|
||||
use util::test::temp_tree;
|
||||
|
||||
#[gpui::test]
|
||||
|
@ -14,11 +14,15 @@ test-support = ["tempdir", "git2"]
|
||||
[dependencies]
|
||||
anyhow = "1.0.38"
|
||||
backtrace = "0.3"
|
||||
futures = "0.3"
|
||||
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
|
||||
lazy_static = "1.4.0"
|
||||
futures = "0.3"
|
||||
isahc = "1.7"
|
||||
smol = "1.2.5"
|
||||
url = "2.2"
|
||||
rand = { workspace = true }
|
||||
tempdir = { version = "0.3.7", optional = true }
|
||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
||||
serde_json = { version = "1.0", features = ["preserve_order"] }
|
||||
git2 = { version = "0.15", default-features = false, optional = true }
|
||||
dirs = "3.0"
|
||||
|
28
crates/util/src/fs.rs
Normal file
28
crates/util/src/fs.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use std::path::Path;
|
||||
|
||||
use smol::{fs, stream::StreamExt};
|
||||
|
||||
use crate::ResultExt;
|
||||
|
||||
// Removes all files and directories matching the given predicate
|
||||
pub async fn remove_matching<F>(dir: &Path, predicate: F)
|
||||
where
|
||||
F: Fn(&Path) -> bool,
|
||||
{
|
||||
if let Some(mut entries) = fs::read_dir(dir).await.log_err() {
|
||||
while let Some(entry) = entries.next().await {
|
||||
if let Some(entry) = entry.log_err() {
|
||||
let entry_path = entry.path();
|
||||
if predicate(entry_path.as_path()) {
|
||||
if let Ok(metadata) = fs::metadata(&entry_path).await {
|
||||
if metadata.is_file() {
|
||||
fs::remove_file(&entry_path).await.log_err();
|
||||
} else {
|
||||
fs::remove_dir_all(&entry_path).await.log_err();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
40
crates/util/src/github.rs
Normal file
40
crates/util/src/github.rs
Normal file
@ -0,0 +1,40 @@
|
||||
use crate::http::HttpClient;
|
||||
use anyhow::{Context, Result};
|
||||
use futures::AsyncReadExt;
|
||||
use serde::Deserialize;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct GithubRelease {
|
||||
pub name: String,
|
||||
pub assets: Vec<GithubReleaseAsset>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct GithubReleaseAsset {
|
||||
pub name: String,
|
||||
pub browser_download_url: String,
|
||||
}
|
||||
|
||||
pub async fn latest_github_release(
|
||||
repo_name_with_owner: &str,
|
||||
http: Arc<dyn HttpClient>,
|
||||
) -> Result<GithubRelease, anyhow::Error> {
|
||||
let mut response = http
|
||||
.get(
|
||||
&format!("https://api.github.com/repos/{repo_name_with_owner}/releases/latest"),
|
||||
Default::default(),
|
||||
true,
|
||||
)
|
||||
.await
|
||||
.context("error fetching latest release")?;
|
||||
let mut body = Vec::new();
|
||||
response
|
||||
.body_mut()
|
||||
.read_to_end(&mut body)
|
||||
.await
|
||||
.context("error reading latest release")?;
|
||||
let release: GithubRelease =
|
||||
serde_json::from_slice(body.as_slice()).context("error deserializing latest release")?;
|
||||
Ok(release)
|
||||
}
|
117
crates/util/src/http.rs
Normal file
117
crates/util/src/http.rs
Normal file
@ -0,0 +1,117 @@
|
||||
pub use anyhow::{anyhow, Result};
|
||||
use futures::future::BoxFuture;
|
||||
use isahc::config::{Configurable, RedirectPolicy};
|
||||
pub use isahc::{
|
||||
http::{Method, Uri},
|
||||
Error,
|
||||
};
|
||||
pub use isahc::{AsyncBody, Request, Response};
|
||||
use smol::future::FutureExt;
|
||||
#[cfg(feature = "test-support")]
|
||||
use std::fmt;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
pub use url::Url;
|
||||
|
||||
pub trait HttpClient: Send + Sync {
|
||||
fn send(&self, req: Request<AsyncBody>) -> BoxFuture<Result<Response<AsyncBody>, Error>>;
|
||||
|
||||
fn get<'a>(
|
||||
&'a self,
|
||||
uri: &str,
|
||||
body: AsyncBody,
|
||||
follow_redirects: bool,
|
||||
) -> BoxFuture<'a, Result<Response<AsyncBody>, Error>> {
|
||||
let request = isahc::Request::builder()
|
||||
.redirect_policy(if follow_redirects {
|
||||
RedirectPolicy::Follow
|
||||
} else {
|
||||
RedirectPolicy::None
|
||||
})
|
||||
.method(Method::GET)
|
||||
.uri(uri)
|
||||
.body(body);
|
||||
match request {
|
||||
Ok(request) => self.send(request),
|
||||
Err(error) => async move { Err(error.into()) }.boxed(),
|
||||
}
|
||||
}
|
||||
|
||||
fn post_json<'a>(
|
||||
&'a self,
|
||||
uri: &str,
|
||||
body: AsyncBody,
|
||||
) -> BoxFuture<'a, Result<Response<AsyncBody>, Error>> {
|
||||
let request = isahc::Request::builder()
|
||||
.method(Method::POST)
|
||||
.uri(uri)
|
||||
.header("Content-Type", "application/json")
|
||||
.body(body);
|
||||
match request {
|
||||
Ok(request) => self.send(request),
|
||||
Err(error) => async move { Err(error.into()) }.boxed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client() -> Arc<dyn HttpClient> {
|
||||
Arc::new(
|
||||
isahc::HttpClient::builder()
|
||||
.connect_timeout(Duration::from_secs(5))
|
||||
.low_speed_timeout(100, Duration::from_secs(5))
|
||||
.build()
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
impl HttpClient for isahc::HttpClient {
|
||||
fn send(&self, req: Request<AsyncBody>) -> BoxFuture<Result<Response<AsyncBody>, Error>> {
|
||||
Box::pin(async move { self.send_async(req).await })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "test-support")]
|
||||
pub struct FakeHttpClient {
|
||||
handler: Box<
|
||||
dyn 'static
|
||||
+ Send
|
||||
+ Sync
|
||||
+ Fn(Request<AsyncBody>) -> BoxFuture<'static, Result<Response<AsyncBody>, Error>>,
|
||||
>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "test-support")]
|
||||
impl FakeHttpClient {
|
||||
pub fn create<Fut, F>(handler: F) -> Arc<dyn HttpClient>
|
||||
where
|
||||
Fut: 'static + Send + futures::Future<Output = Result<Response<AsyncBody>, Error>>,
|
||||
F: 'static + Send + Sync + Fn(Request<AsyncBody>) -> Fut,
|
||||
{
|
||||
Arc::new(Self {
|
||||
handler: Box::new(move |req| Box::pin(handler(req))),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_404_response() -> Arc<dyn HttpClient> {
|
||||
Self::create(|_| async move {
|
||||
Ok(Response::builder()
|
||||
.status(404)
|
||||
.body(Default::default())
|
||||
.unwrap())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "test-support")]
|
||||
impl fmt::Debug for FakeHttpClient {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("FakeHttpClient").finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "test-support")]
|
||||
impl HttpClient for FakeHttpClient {
|
||||
fn send(&self, req: Request<AsyncBody>) -> BoxFuture<Result<Response<AsyncBody>, Error>> {
|
||||
let future = (self.handler)(req);
|
||||
Box::pin(async move { future.await.map(Into::into) })
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ lazy_static::lazy_static! {
|
||||
pub static ref LOGS_DIR: PathBuf = HOME.join("Library/Logs/Zed");
|
||||
pub static ref SUPPORT_DIR: PathBuf = HOME.join("Library/Application Support/Zed");
|
||||
pub static ref LANGUAGES_DIR: PathBuf = HOME.join("Library/Application Support/Zed/languages");
|
||||
pub static ref COPILOT_DIR: PathBuf = HOME.join("Library/Application Support/Zed/copilot");
|
||||
pub static ref DB_DIR: PathBuf = HOME.join("Library/Application Support/Zed/db");
|
||||
pub static ref SETTINGS: PathBuf = CONFIG_DIR.join("settings.json");
|
||||
pub static ref KEYMAP: PathBuf = CONFIG_DIR.join("keymap.json");
|
||||
|
@ -1,4 +1,7 @@
|
||||
pub mod channel;
|
||||
pub mod fs;
|
||||
pub mod github;
|
||||
pub mod http;
|
||||
pub mod paths;
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub mod test;
|
||||
|
@ -449,7 +449,7 @@ impl AppState {
|
||||
|
||||
let fs = fs::FakeFs::new(cx.background().clone());
|
||||
let languages = Arc::new(LanguageRegistry::test());
|
||||
let http_client = client::test::FakeHttpClient::with_404_response();
|
||||
let http_client = util::http::FakeHttpClient::with_404_response();
|
||||
let client = Client::new(http_client.clone(), cx);
|
||||
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
|
||||
let themes = ThemeRegistry::new((), cx.font_cache().clone());
|
||||
|
@ -28,6 +28,7 @@ command_palette = { path = "../command_palette" }
|
||||
context_menu = { path = "../context_menu" }
|
||||
client = { path = "../client" }
|
||||
clock = { path = "../clock" }
|
||||
copilot = { path = "../copilot" }
|
||||
diagnostics = { path = "../diagnostics" }
|
||||
db = { path = "../db" }
|
||||
editor = { path = "../editor" }
|
||||
|
@ -1,11 +1,11 @@
|
||||
use anyhow::Context;
|
||||
use client::http::HttpClient;
|
||||
use gpui::executor::Background;
|
||||
pub use language::*;
|
||||
use node_runtime::NodeRuntime;
|
||||
use rust_embed::RustEmbed;
|
||||
use std::{borrow::Cow, str, sync::Arc};
|
||||
use theme::ThemeRegistry;
|
||||
use util::http::HttpClient;
|
||||
|
||||
mod c;
|
||||
mod elixir;
|
||||
|
@ -1,13 +1,16 @@
|
||||
use super::github::{latest_github_release, GitHubLspBinaryVersion};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use client::http::HttpClient;
|
||||
use futures::StreamExt;
|
||||
pub use language::*;
|
||||
use smol::fs::{self, File};
|
||||
use std::{any::Any, path::PathBuf, sync::Arc};
|
||||
use util::fs::remove_matching;
|
||||
use util::github::latest_github_release;
|
||||
use util::http::HttpClient;
|
||||
use util::ResultExt;
|
||||
|
||||
use super::github::GitHubLspBinaryVersion;
|
||||
|
||||
pub struct CLspAdapter;
|
||||
|
||||
#[async_trait]
|
||||
@ -69,16 +72,7 @@ impl super::LspAdapter for CLspAdapter {
|
||||
Err(anyhow!("failed to unzip clangd archive"))?;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
remove_matching(&container_dir, |entry| entry != version_dir).await;
|
||||
}
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
|
@ -1,14 +1,17 @@
|
||||
use super::github::{latest_github_release, GitHubLspBinaryVersion};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use client::http::HttpClient;
|
||||
use futures::StreamExt;
|
||||
pub use language::*;
|
||||
use lsp::{CompletionItemKind, SymbolKind};
|
||||
use smol::fs::{self, File};
|
||||
use std::{any::Any, path::PathBuf, sync::Arc};
|
||||
use util::fs::remove_matching;
|
||||
use util::github::latest_github_release;
|
||||
use util::http::HttpClient;
|
||||
use util::ResultExt;
|
||||
|
||||
use super::github::GitHubLspBinaryVersion;
|
||||
|
||||
pub struct ElixirLspAdapter;
|
||||
|
||||
#[async_trait]
|
||||
@ -76,22 +79,7 @@ impl LspAdapter for ElixirLspAdapter {
|
||||
Err(anyhow!("failed to unzip clangd archive"))?;
|
||||
}
|
||||
|
||||
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 {
|
||||
if let Ok(metadata) = fs::metadata(&entry_path).await {
|
||||
if metadata.is_file() {
|
||||
fs::remove_file(&entry_path).await.log_err();
|
||||
} else {
|
||||
fs::remove_dir_all(&entry_path).await.log_err();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
remove_matching(&container_dir, |entry| entry != version_dir).await;
|
||||
}
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
|
@ -1,8 +1,8 @@
|
||||
use anyhow::{Context, Result};
|
||||
use client::http::HttpClient;
|
||||
use serde::Deserialize;
|
||||
use smol::io::AsyncReadExt;
|
||||
use std::sync::Arc;
|
||||
use util::http::HttpClient;
|
||||
|
||||
pub struct GitHubLspBinaryVersion {
|
||||
pub name: String,
|
||||
|
@ -1,13 +1,15 @@
|
||||
use super::github::latest_github_release;
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use client::http::HttpClient;
|
||||
use futures::StreamExt;
|
||||
pub use language::*;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use smol::{fs, process};
|
||||
use std::{any::Any, ffi::OsString, ops::Range, path::PathBuf, str, sync::Arc};
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::{any::Any, ops::Range, path::PathBuf, str, sync::Arc};
|
||||
use util::fs::remove_matching;
|
||||
use util::github::latest_github_release;
|
||||
use util::http::HttpClient;
|
||||
use util::ResultExt;
|
||||
|
||||
fn server_binary_arguments() -> Vec<OsString> {
|
||||
@ -55,18 +57,10 @@ impl super::LspAdapter for GoLspAdapter {
|
||||
let binary_path = container_dir.join(&format!("gopls_{version}"));
|
||||
if let Ok(metadata) = fs::metadata(&binary_path).await {
|
||||
if metadata.is_file() {
|
||||
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() != binary_path
|
||||
&& entry.file_name() != "gobin"
|
||||
{
|
||||
fs::remove_file(&entry_path).await.log_err();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
remove_matching(&container_dir, |entry| {
|
||||
entry != binary_path && entry.file_name() != Some(OsStr::new("gobin"))
|
||||
})
|
||||
.await;
|
||||
|
||||
return Ok(LanguageServerBinary {
|
||||
path: binary_path.to_path_buf(),
|
||||
|
@ -1,17 +1,15 @@
|
||||
use super::node_runtime::NodeRuntime;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use client::http::HttpClient;
|
||||
use futures::StreamExt;
|
||||
use language::{LanguageServerBinary, LanguageServerName, LspAdapter};
|
||||
use serde_json::json;
|
||||
use smol::fs;
|
||||
use std::{
|
||||
any::Any,
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use std::ffi::OsString;
|
||||
use std::path::Path;
|
||||
use std::{any::Any, path::PathBuf, sync::Arc};
|
||||
use util::fs::remove_matching;
|
||||
use util::http::HttpClient;
|
||||
use util::ResultExt;
|
||||
|
||||
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||
@ -69,16 +67,7 @@ impl LspAdapter for HtmlLspAdapter {
|
||||
)
|
||||
.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
remove_matching(container_dir.as_path(), |entry| entry != version_dir).await;
|
||||
}
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
|
@ -1,7 +1,6 @@
|
||||
use super::node_runtime::NodeRuntime;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use client::http::HttpClient;
|
||||
use collections::HashMap;
|
||||
use futures::{future::BoxFuture, FutureExt, StreamExt};
|
||||
use gpui::MutableAppContext;
|
||||
@ -17,6 +16,7 @@ use std::{
|
||||
sync::Arc,
|
||||
};
|
||||
use theme::ThemeRegistry;
|
||||
use util::{fs::remove_matching, http::HttpClient};
|
||||
use util::{paths, ResultExt, StaffMode};
|
||||
|
||||
const SERVER_PATH: &'static str =
|
||||
@ -84,16 +84,7 @@ impl LspAdapter for JsonLspAdapter {
|
||||
)
|
||||
.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
remove_matching(&container_dir, |entry| entry != server_path).await;
|
||||
}
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
|
@ -1,12 +1,12 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use client::http::HttpClient;
|
||||
use collections::HashMap;
|
||||
use futures::lock::Mutex;
|
||||
use gpui::executor::Background;
|
||||
use language::{LanguageServerBinary, LanguageServerName, LspAdapter};
|
||||
use plugin_runtime::{Plugin, PluginBinary, PluginBuilder, WasiFn};
|
||||
use std::{any::Any, path::PathBuf, sync::Arc};
|
||||
use util::http::HttpClient;
|
||||
use util::ResultExt;
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
@ -1,16 +1,14 @@
|
||||
use std::{any::Any, env::consts, ffi::OsString, path::PathBuf, sync::Arc};
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use async_compression::futures::bufread::GzipDecoder;
|
||||
use async_tar::Archive;
|
||||
use async_trait::async_trait;
|
||||
use client::http::HttpClient;
|
||||
use futures::{io::BufReader, StreamExt};
|
||||
use language::{LanguageServerBinary, LanguageServerName};
|
||||
use smol::fs;
|
||||
use util::{async_iife, ResultExt};
|
||||
use std::{any::Any, env::consts, ffi::OsString, path::PathBuf, sync::Arc};
|
||||
use util::{async_iife, github::latest_github_release, http::HttpClient, ResultExt};
|
||||
|
||||
use super::github::{latest_github_release, GitHubLspBinaryVersion};
|
||||
use super::github::GitHubLspBinaryVersion;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct LuaLspAdapter;
|
||||
|
@ -1,7 +1,6 @@
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use async_compression::futures::bufread::GzipDecoder;
|
||||
use async_tar::Archive;
|
||||
use client::http::HttpClient;
|
||||
use futures::{future::Shared, FutureExt};
|
||||
use gpui::{executor::Background, Task};
|
||||
use parking_lot::Mutex;
|
||||
@ -12,6 +11,7 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use util::http::HttpClient;
|
||||
|
||||
const VERSION: &str = "v18.15.0";
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
use super::node_runtime::NodeRuntime;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use client::http::HttpClient;
|
||||
use futures::StreamExt;
|
||||
use language::{LanguageServerBinary, LanguageServerName, LspAdapter};
|
||||
use smol::fs;
|
||||
@ -11,6 +10,8 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use util::fs::remove_matching;
|
||||
use util::http::HttpClient;
|
||||
use util::ResultExt;
|
||||
|
||||
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||
@ -60,16 +61,7 @@ impl LspAdapter for PythonLspAdapter {
|
||||
.npm_install_packages([("pyright", 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
remove_matching(&container_dir, |entry| entry != version_dir).await;
|
||||
}
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
|
@ -1,8 +1,8 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use client::http::HttpClient;
|
||||
use language::{LanguageServerBinary, LanguageServerName, LspAdapter};
|
||||
use std::{any::Any, path::PathBuf, sync::Arc};
|
||||
use util::http::HttpClient;
|
||||
|
||||
pub struct RubyLanguageServer;
|
||||
|
||||
|
@ -2,13 +2,14 @@ use super::github::{latest_github_release, GitHubLspBinaryVersion};
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_compression::futures::bufread::GzipDecoder;
|
||||
use async_trait::async_trait;
|
||||
use client::http::HttpClient;
|
||||
use futures::{io::BufReader, StreamExt};
|
||||
pub use language::*;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use smol::fs::{self, File};
|
||||
use std::{any::Any, borrow::Cow, env::consts, path::PathBuf, str, sync::Arc};
|
||||
use util::fs::remove_matching;
|
||||
use util::http::HttpClient;
|
||||
use util::ResultExt;
|
||||
|
||||
pub struct RustLspAdapter;
|
||||
@ -60,16 +61,7 @@ impl LspAdapter for RustLspAdapter {
|
||||
)
|
||||
.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() != destination_path {
|
||||
fs::remove_file(&entry_path).await.log_err();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
remove_matching(&container_dir, |entry| entry != destination_path).await;
|
||||
}
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
|
@ -1,7 +1,6 @@
|
||||
use super::node_runtime::NodeRuntime;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use client::http::HttpClient;
|
||||
use futures::StreamExt;
|
||||
use language::{LanguageServerBinary, LanguageServerName, LspAdapter};
|
||||
use serde_json::json;
|
||||
@ -12,6 +11,8 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use util::fs::remove_matching;
|
||||
use util::http::HttpClient;
|
||||
use util::ResultExt;
|
||||
|
||||
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||
@ -90,16 +91,7 @@ impl LspAdapter for TypeScriptLspAdapter {
|
||||
)
|
||||
.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
remove_matching(&container_dir, |entry| entry != version_dir).await;
|
||||
}
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
|
@ -1,7 +1,6 @@
|
||||
use super::node_runtime::NodeRuntime;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use client::http::HttpClient;
|
||||
use futures::{future::BoxFuture, FutureExt, StreamExt};
|
||||
use gpui::MutableAppContext;
|
||||
use language::{LanguageServerBinary, LanguageServerName, LspAdapter};
|
||||
@ -16,6 +15,7 @@ use std::{
|
||||
sync::Arc,
|
||||
};
|
||||
use util::ResultExt;
|
||||
use util::{fs::remove_matching, http::HttpClient};
|
||||
|
||||
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||
vec![server_path.into(), "--stdio".into()]
|
||||
@ -68,16 +68,7 @@ impl LspAdapter for YamlLspAdapter {
|
||||
.npm_install_packages([("yaml-language-server", 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
remove_matching(&container_dir, |entry| entry != version_dir).await;
|
||||
}
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
|
@ -8,11 +8,7 @@ use cli::{
|
||||
ipc::{self, IpcSender},
|
||||
CliRequest, CliResponse, IpcHandshake,
|
||||
};
|
||||
use client::{
|
||||
self,
|
||||
http::{self, HttpClient},
|
||||
UserStore, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN,
|
||||
};
|
||||
use client::{self, UserStore, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN};
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use futures::{
|
||||
channel::{mpsc, oneshot},
|
||||
@ -36,6 +32,7 @@ use std::{
|
||||
path::PathBuf, sync::Arc, thread, time::Duration,
|
||||
};
|
||||
use terminal_view::{get_working_directory, TerminalView};
|
||||
use util::http::{self, HttpClient};
|
||||
use welcome::{show_welcome_experience, FIRST_OPEN};
|
||||
|
||||
use fs::RealFs;
|
||||
@ -165,6 +162,7 @@ fn main() {
|
||||
terminal_view::init(cx);
|
||||
theme_testbench::init(cx);
|
||||
recent_projects::init(cx);
|
||||
copilot::init(client.clone(), cx);
|
||||
|
||||
cx.spawn(|cx| watch_themes(fs.clone(), themes.clone(), cx))
|
||||
.detach();
|
||||
|
@ -652,7 +652,6 @@ fn open_bundled_file(
|
||||
mod tests {
|
||||
use super::*;
|
||||
use assets::Assets;
|
||||
use client::test::FakeHttpClient;
|
||||
use editor::{scroll::autoscroll::Autoscroll, DisplayPoint, Editor};
|
||||
use gpui::{
|
||||
executor::Deterministic, AssetSource, MutableAppContext, TestAppContext, ViewHandle,
|
||||
@ -665,6 +664,7 @@ mod tests {
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use theme::ThemeRegistry;
|
||||
use util::http::FakeHttpClient;
|
||||
use workspace::{
|
||||
item::{Item, ItemHandle},
|
||||
open_new, open_paths, pane, NewFile, Pane, SplitDirection, WorkspaceHandle,
|
||||
|
Loading…
Reference in New Issue
Block a user