Detect and possibly use user-installed gopls
/ zls
language servers (#8188)
After a lot of back-and-forth, this is a small attempt to implement solutions (1) and (3) in https://github.com/zed-industries/zed/issues/7902. The goal is to have a minimal change that helps users get started with Zed, until we have extensions ready. Release Notes: - Added detection of user-installed `gopls` to Go language server adapter. If a user has `gopls` in `$PATH` when opening a worktree, it will be used. - Added detection of user-installed `zls` to Zig language server adapter. If a user has `zls` in `$PATH` when opening a worktree, it will be used. Example: I don't have `go` installed globally, but I do have `gopls`: ``` ~ $ which go go not found ~ $ which gopls /Users/thorstenball/code/go/bin/gopls ``` But I do have `go` in a project's directory: ``` ~/tmp/go-testing φ which go /Users/thorstenball/.local/share/mise/installs/go/1.21.5/go/bin/go ~/tmp/go-testing φ which gopls /Users/thorstenball/code/go/bin/gopls ``` With current Zed when I run `zed ~/tmp/go-testing`, I'd get the dreaded error:  But with the changes in this PR, it works: ``` [2024-02-23T11:14:42+01:00 INFO language::language_registry] starting language server "gopls", path: "/Users/thorstenball/tmp/go-testing", id: 1 [2024-02-23T11:14:42+01:00 INFO language::language_registry] found user-installed language server for Go. path: "/Users/thorstenball/code/go/bin/gopls", arguments: ["-mode=stdio"] [2024-02-23T11:14:42+01:00 INFO lsp] starting language server. binary path: "/Users/thorstenball/code/go/bin/gopls", working directory: "/Users/thorstenball/tmp/go-testing", args: ["-mode=stdio"] ``` --------- Co-authored-by: Antonio <antonio@zed.dev>
This commit is contained in:
parent
65318cb6ac
commit
42ac9880c6
42 changed files with 369 additions and 47 deletions
24
Cargo.lock
generated
24
Cargo.lock
generated
|
@ -1351,7 +1351,7 @@ dependencies = [
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"shlex",
|
"shlex",
|
||||||
"syn 2.0.48",
|
"syn 2.0.48",
|
||||||
"which",
|
"which 4.4.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -4348,11 +4348,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "home"
|
name = "home"
|
||||||
version = "0.5.5"
|
version = "0.5.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
|
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -6942,6 +6942,7 @@ dependencies = [
|
||||||
"toml 0.8.10",
|
"toml 0.8.10",
|
||||||
"unindent",
|
"unindent",
|
||||||
"util",
|
"util",
|
||||||
|
"which 6.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -7051,7 +7052,7 @@ dependencies = [
|
||||||
"prost-types 0.9.0",
|
"prost-types 0.9.0",
|
||||||
"regex",
|
"regex",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"which",
|
"which 4.4.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -11425,6 +11426,19 @@ dependencies = [
|
||||||
"rustix 0.38.30",
|
"rustix 0.38.30",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "which"
|
||||||
|
version = "6.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fa5e0c10bf77f44aac573e498d1a82d5fbd5e91f6fc0a99e7be4b38e85e101c"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"home",
|
||||||
|
"once_cell",
|
||||||
|
"rustix 0.38.30",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "whoami"
|
name = "whoami"
|
||||||
version = "1.4.1"
|
version = "1.4.1"
|
||||||
|
|
|
@ -279,6 +279,7 @@ unindent = "0.1.7"
|
||||||
url = "2.2"
|
url = "2.2"
|
||||||
uuid = { version = "1.1.2", features = ["v4"] }
|
uuid = { version = "1.1.2", features = ["v4"] }
|
||||||
wasmtime = "16"
|
wasmtime = "16"
|
||||||
|
which = "6.0.0"
|
||||||
sys-locale = "0.3.1"
|
sys-locale = "0.3.1"
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
|
|
|
@ -428,6 +428,8 @@ impl Copilot {
|
||||||
let binary = LanguageServerBinary {
|
let binary = LanguageServerBinary {
|
||||||
path: node_path,
|
path: node_path,
|
||||||
arguments,
|
arguments,
|
||||||
|
// TODO: We could set HTTP_PROXY etc here and fix the copilot issue.
|
||||||
|
env: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let server = LanguageServer::new(
|
let server = LanguageServer::new(
|
||||||
|
|
|
@ -38,6 +38,7 @@ use serde_json::Value;
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
|
ffi::OsString,
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
mem,
|
mem,
|
||||||
|
@ -140,6 +141,14 @@ impl CachedLspAdapter {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn check_if_user_installed(
|
||||||
|
&self,
|
||||||
|
delegate: &Arc<dyn LspAdapterDelegate>,
|
||||||
|
cx: &mut AsyncAppContext,
|
||||||
|
) -> Option<Task<Option<LanguageServerBinary>>> {
|
||||||
|
self.adapter.check_if_user_installed(delegate, cx)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn fetch_latest_server_version(
|
pub async fn fetch_latest_server_version(
|
||||||
&self,
|
&self,
|
||||||
delegate: &dyn LspAdapterDelegate,
|
delegate: &dyn LspAdapterDelegate,
|
||||||
|
@ -240,6 +249,11 @@ impl CachedLspAdapter {
|
||||||
pub trait LspAdapterDelegate: Send + Sync {
|
pub trait LspAdapterDelegate: Send + Sync {
|
||||||
fn show_notification(&self, message: &str, cx: &mut AppContext);
|
fn show_notification(&self, message: &str, cx: &mut AppContext);
|
||||||
fn http_client(&self) -> Arc<dyn HttpClient>;
|
fn http_client(&self) -> Arc<dyn HttpClient>;
|
||||||
|
fn which_command(
|
||||||
|
&self,
|
||||||
|
command: OsString,
|
||||||
|
cx: &AppContext,
|
||||||
|
) -> Task<Option<(PathBuf, HashMap<String, String>)>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
@ -248,6 +262,14 @@ pub trait LspAdapter: 'static + Send + Sync {
|
||||||
|
|
||||||
fn short_name(&self) -> &'static str;
|
fn short_name(&self) -> &'static str;
|
||||||
|
|
||||||
|
fn check_if_user_installed(
|
||||||
|
&self,
|
||||||
|
_: &Arc<dyn LspAdapterDelegate>,
|
||||||
|
_: &mut AsyncAppContext,
|
||||||
|
) -> Option<Task<Option<LanguageServerBinary>>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
async fn fetch_latest_server_version(
|
async fn fetch_latest_server_version(
|
||||||
&self,
|
&self,
|
||||||
delegate: &dyn LspAdapterDelegate,
|
delegate: &dyn LspAdapterDelegate,
|
||||||
|
|
|
@ -558,34 +558,41 @@ impl LanguageRegistry {
|
||||||
let task = {
|
let task = {
|
||||||
let container_dir = container_dir.clone();
|
let container_dir = container_dir.clone();
|
||||||
cx.spawn(move |mut cx| async move {
|
cx.spawn(move |mut cx| async move {
|
||||||
login_shell_env_loaded.await;
|
// First we check whether the adapter can give us a user-installed binary.
|
||||||
|
// If so, we do *not* want to cache that, because each worktree might give us a different
|
||||||
|
// binary:
|
||||||
|
//
|
||||||
|
// worktree 1: user-installed at `.bin/gopls`
|
||||||
|
// worktree 2: user-installed at `~/bin/gopls`
|
||||||
|
// worktree 3: no gopls found in PATH -> fallback to Zed installation
|
||||||
|
//
|
||||||
|
// We only want to cache when we fall back to the global one,
|
||||||
|
// because we don't want to download and overwrite our global one
|
||||||
|
// for each worktree we might have open.
|
||||||
|
|
||||||
let entry = this
|
let user_binary_task = check_user_installed_binary(
|
||||||
.lsp_binary_paths
|
adapter.clone(),
|
||||||
.lock()
|
language.clone(),
|
||||||
.entry(adapter.name.clone())
|
delegate.clone(),
|
||||||
.or_insert_with(|| {
|
&mut cx,
|
||||||
let adapter = adapter.clone();
|
);
|
||||||
let language = language.clone();
|
let binary = if let Some(user_binary) = user_binary_task.await {
|
||||||
let delegate = delegate.clone();
|
user_binary
|
||||||
cx.spawn(|cx| {
|
} else {
|
||||||
get_binary(
|
// If we want to install a binary globally, we need to wait for
|
||||||
adapter,
|
// the login shell to be set on our process.
|
||||||
language,
|
login_shell_env_loaded.await;
|
||||||
delegate,
|
|
||||||
container_dir,
|
|
||||||
lsp_binary_statuses,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
.map_err(Arc::new)
|
|
||||||
})
|
|
||||||
.shared()
|
|
||||||
})
|
|
||||||
.clone();
|
|
||||||
|
|
||||||
let binary = match entry.await {
|
get_or_install_binary(
|
||||||
Ok(binary) => binary,
|
this,
|
||||||
Err(err) => anyhow::bail!("{err}"),
|
&adapter,
|
||||||
|
language,
|
||||||
|
&delegate,
|
||||||
|
&cx,
|
||||||
|
container_dir,
|
||||||
|
lsp_binary_statuses,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(task) = adapter.will_start_server(&delegate, &mut cx) {
|
if let Some(task) = adapter.will_start_server(&delegate, &mut cx) {
|
||||||
|
@ -724,6 +731,62 @@ impl LspBinaryStatusSender {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn check_user_installed_binary(
|
||||||
|
adapter: Arc<CachedLspAdapter>,
|
||||||
|
language: Arc<Language>,
|
||||||
|
delegate: Arc<dyn LspAdapterDelegate>,
|
||||||
|
cx: &mut AsyncAppContext,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
let Some(task) = adapter.check_if_user_installed(&delegate, cx) else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
task.await.and_then(|binary| {
|
||||||
|
log::info!(
|
||||||
|
"found user-installed language server for {}. path: {:?}, arguments: {:?}",
|
||||||
|
language.name(),
|
||||||
|
binary.path,
|
||||||
|
binary.arguments
|
||||||
|
);
|
||||||
|
Some(binary)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_or_install_binary(
|
||||||
|
registry: Arc<LanguageRegistry>,
|
||||||
|
adapter: &Arc<CachedLspAdapter>,
|
||||||
|
language: Arc<Language>,
|
||||||
|
delegate: &Arc<dyn LspAdapterDelegate>,
|
||||||
|
cx: &AsyncAppContext,
|
||||||
|
container_dir: Arc<Path>,
|
||||||
|
lsp_binary_statuses: LspBinaryStatusSender,
|
||||||
|
) -> Result<LanguageServerBinary> {
|
||||||
|
let entry = registry
|
||||||
|
.lsp_binary_paths
|
||||||
|
.lock()
|
||||||
|
.entry(adapter.name.clone())
|
||||||
|
.or_insert_with(|| {
|
||||||
|
let adapter = adapter.clone();
|
||||||
|
let language = language.clone();
|
||||||
|
let delegate = delegate.clone();
|
||||||
|
cx.spawn(|cx| {
|
||||||
|
get_binary(
|
||||||
|
adapter,
|
||||||
|
language,
|
||||||
|
delegate,
|
||||||
|
container_dir,
|
||||||
|
lsp_binary_statuses,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.map_err(Arc::new)
|
||||||
|
})
|
||||||
|
.shared()
|
||||||
|
})
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
entry.await.map_err(|err| anyhow!("{:?}", err))
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_binary(
|
async fn get_binary(
|
||||||
adapter: Arc<CachedLspAdapter>,
|
adapter: Arc<CachedLspAdapter>,
|
||||||
language: Arc<Language>,
|
language: Arc<Language>,
|
||||||
|
@ -757,15 +820,20 @@ async fn get_binary(
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
statuses.send(language.clone(), LanguageServerBinaryStatus::Cached);
|
statuses.send(language.clone(), LanguageServerBinaryStatus::Cached);
|
||||||
return Ok(binary);
|
log::info!(
|
||||||
} else {
|
"failed to fetch newest version of language server {:?}. falling back to using {:?}",
|
||||||
statuses.send(
|
adapter.name,
|
||||||
language.clone(),
|
binary.path.display()
|
||||||
LanguageServerBinaryStatus::Failed {
|
|
||||||
error: format!("{:?}", error),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
return Ok(binary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
statuses.send(
|
||||||
|
language.clone(),
|
||||||
|
LanguageServerBinaryStatus::Failed {
|
||||||
|
error: format!("{:?}", error),
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
binary
|
binary
|
||||||
|
@ -779,14 +847,23 @@ async fn fetch_latest_binary(
|
||||||
lsp_binary_statuses_tx: LspBinaryStatusSender,
|
lsp_binary_statuses_tx: LspBinaryStatusSender,
|
||||||
) -> Result<LanguageServerBinary> {
|
) -> Result<LanguageServerBinary> {
|
||||||
let container_dir: Arc<Path> = container_dir.into();
|
let container_dir: Arc<Path> = container_dir.into();
|
||||||
|
|
||||||
lsp_binary_statuses_tx.send(
|
lsp_binary_statuses_tx.send(
|
||||||
language.clone(),
|
language.clone(),
|
||||||
LanguageServerBinaryStatus::CheckingForUpdate,
|
LanguageServerBinaryStatus::CheckingForUpdate,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
log::info!(
|
||||||
|
"querying GitHub for latest version of language server {:?}",
|
||||||
|
adapter.name.0
|
||||||
|
);
|
||||||
let version_info = adapter.fetch_latest_server_version(delegate).await?;
|
let version_info = adapter.fetch_latest_server_version(delegate).await?;
|
||||||
lsp_binary_statuses_tx.send(language.clone(), LanguageServerBinaryStatus::Downloading);
|
lsp_binary_statuses_tx.send(language.clone(), LanguageServerBinaryStatus::Downloading);
|
||||||
|
|
||||||
|
log::info!(
|
||||||
|
"checking if Zed already installed or fetching version for language server {:?}",
|
||||||
|
adapter.name.0
|
||||||
|
);
|
||||||
let binary = adapter
|
let binary = adapter
|
||||||
.fetch_server_binary(version_info, container_dir.to_path_buf(), delegate)
|
.fetch_server_binary(version_info, container_dir.to_path_buf(), delegate)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -55,6 +55,7 @@ pub enum IoKind {
|
||||||
pub struct LanguageServerBinary {
|
pub struct LanguageServerBinary {
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
pub arguments: Vec<OsString>,
|
pub arguments: Vec<OsString>,
|
||||||
|
pub env: Option<HashMap<String, String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A running language server process.
|
/// A running language server process.
|
||||||
|
@ -189,6 +190,7 @@ impl LanguageServer {
|
||||||
let mut server = process::Command::new(&binary.path)
|
let mut server = process::Command::new(&binary.path)
|
||||||
.current_dir(working_dir)
|
.current_dir(working_dir)
|
||||||
.args(binary.arguments)
|
.args(binary.arguments)
|
||||||
|
.envs(binary.env.unwrap_or_default())
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.stderr(Stdio::piped())
|
.stderr(Stdio::piped())
|
||||||
|
|
|
@ -192,6 +192,7 @@ impl Prettier {
|
||||||
LanguageServerBinary {
|
LanguageServerBinary {
|
||||||
path: node_path,
|
path: node_path,
|
||||||
arguments: vec![prettier_server.into(), prettier_dir.as_path().into()],
|
arguments: vec![prettier_server.into(), prettier_dir.as_path().into()],
|
||||||
|
env: None,
|
||||||
},
|
},
|
||||||
Path::new("/"),
|
Path::new("/"),
|
||||||
None,
|
None,
|
||||||
|
|
|
@ -65,6 +65,7 @@ text.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
toml.workspace = true
|
toml.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
|
which.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
client = { workspace = true, features = ["test-support"] }
|
client = { workspace = true, features = ["test-support"] }
|
||||||
|
|
|
@ -71,6 +71,8 @@ use smol::lock::Semaphore;
|
||||||
use std::{
|
use std::{
|
||||||
cmp::{self, Ordering},
|
cmp::{self, Ordering},
|
||||||
convert::TryInto,
|
convert::TryInto,
|
||||||
|
env,
|
||||||
|
ffi::OsString,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
mem,
|
mem,
|
||||||
num::NonZeroU32,
|
num::NonZeroU32,
|
||||||
|
@ -504,11 +506,6 @@ pub enum FormatTrigger {
|
||||||
Manual,
|
Manual,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ProjectLspAdapterDelegate {
|
|
||||||
project: Model<Project>,
|
|
||||||
http_client: Arc<dyn HttpClient>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Currently, formatting operations are represented differently depending on
|
// Currently, formatting operations are represented differently depending on
|
||||||
// whether they come from a language server or an external command.
|
// whether they come from a language server or an external command.
|
||||||
enum FormatOperation {
|
enum FormatOperation {
|
||||||
|
@ -2803,7 +2800,7 @@ impl Project {
|
||||||
|
|
||||||
fn start_language_server(
|
fn start_language_server(
|
||||||
&mut self,
|
&mut self,
|
||||||
worktree: &Model<Worktree>,
|
worktree_handle: &Model<Worktree>,
|
||||||
adapter: Arc<CachedLspAdapter>,
|
adapter: Arc<CachedLspAdapter>,
|
||||||
language: Arc<Language>,
|
language: Arc<Language>,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
|
@ -2812,7 +2809,7 @@ impl Project {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let worktree = worktree.read(cx);
|
let worktree = worktree_handle.read(cx);
|
||||||
let worktree_id = worktree.id();
|
let worktree_id = worktree.id();
|
||||||
let worktree_path = worktree.abs_path();
|
let worktree_path = worktree.abs_path();
|
||||||
let key = (worktree_id, adapter.name.clone());
|
let key = (worktree_id, adapter.name.clone());
|
||||||
|
@ -2826,7 +2823,7 @@ impl Project {
|
||||||
language.clone(),
|
language.clone(),
|
||||||
adapter.clone(),
|
adapter.clone(),
|
||||||
Arc::clone(&worktree_path),
|
Arc::clone(&worktree_path),
|
||||||
ProjectLspAdapterDelegate::new(self, cx),
|
ProjectLspAdapterDelegate::new(self, worktree_handle, cx),
|
||||||
cx,
|
cx,
|
||||||
) {
|
) {
|
||||||
Some(pending_server) => pending_server,
|
Some(pending_server) => pending_server,
|
||||||
|
@ -9298,10 +9295,17 @@ impl<P: AsRef<Path>> From<(WorktreeId, P)> for ProjectPath {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ProjectLspAdapterDelegate {
|
||||||
|
project: Model<Project>,
|
||||||
|
worktree: Model<Worktree>,
|
||||||
|
http_client: Arc<dyn HttpClient>,
|
||||||
|
}
|
||||||
|
|
||||||
impl ProjectLspAdapterDelegate {
|
impl ProjectLspAdapterDelegate {
|
||||||
fn new(project: &Project, cx: &ModelContext<Project>) -> Arc<Self> {
|
fn new(project: &Project, worktree: &Model<Worktree>, cx: &ModelContext<Project>) -> Arc<Self> {
|
||||||
Arc::new(Self {
|
Arc::new(Self {
|
||||||
project: cx.handle(),
|
project: cx.handle(),
|
||||||
|
worktree: worktree.clone(),
|
||||||
http_client: project.client.http_client(),
|
http_client: project.client.http_client(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -9316,6 +9320,41 @@ impl LspAdapterDelegate for ProjectLspAdapterDelegate {
|
||||||
fn http_client(&self) -> Arc<dyn HttpClient> {
|
fn http_client(&self) -> Arc<dyn HttpClient> {
|
||||||
self.http_client.clone()
|
self.http_client.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn which_command(
|
||||||
|
&self,
|
||||||
|
command: OsString,
|
||||||
|
cx: &AppContext,
|
||||||
|
) -> Task<Option<(PathBuf, HashMap<String, String>)>> {
|
||||||
|
let worktree_abs_path = self.worktree.read(cx).abs_path();
|
||||||
|
let command = command.to_owned();
|
||||||
|
|
||||||
|
cx.background_executor().spawn(async move {
|
||||||
|
let shell_env = load_shell_environment(&worktree_abs_path)
|
||||||
|
.await
|
||||||
|
.with_context(|| {
|
||||||
|
format!(
|
||||||
|
"failed to determine load login shell environment in {worktree_abs_path:?}"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.log_err();
|
||||||
|
|
||||||
|
if let Some(shell_env) = shell_env {
|
||||||
|
let shell_path = shell_env.get("PATH");
|
||||||
|
match which::which_in(&command, shell_path, &worktree_abs_path) {
|
||||||
|
Ok(command_path) => Some((command_path, shell_env)),
|
||||||
|
Err(error) => {
|
||||||
|
log::warn!(
|
||||||
|
"failed to determine path for command {:?} in env {shell_env:?}: {error}", command.to_string_lossy()
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_symbol(symbol: &Symbol) -> proto::Symbol {
|
fn serialize_symbol(symbol: &Symbol) -> proto::Symbol {
|
||||||
|
@ -9423,3 +9462,55 @@ fn include_text(server: &lsp::LanguageServer) -> bool {
|
||||||
})
|
})
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn load_shell_environment(dir: &Path) -> Result<HashMap<String, String>> {
|
||||||
|
let marker = "ZED_SHELL_START";
|
||||||
|
let shell = env::var("SHELL").context(
|
||||||
|
"SHELL environment variable is not assigned so we can't source login environment variables",
|
||||||
|
)?;
|
||||||
|
let output = smol::process::Command::new(&shell)
|
||||||
|
.args([
|
||||||
|
"-i",
|
||||||
|
"-c",
|
||||||
|
// What we're doing here is to spawn a shell and then `cd` into
|
||||||
|
// the project directory to get the env in there as if the user
|
||||||
|
// `cd`'d into it. We do that because tools like direnv, asdf, ...
|
||||||
|
// hook into `cd` and only set up the env after that.
|
||||||
|
//
|
||||||
|
// The `exit 0` is the result of hours of debugging, trying to find out
|
||||||
|
// why running this command here, without `exit 0`, would mess
|
||||||
|
// up signal process for our process so that `ctrl-c` doesn't work
|
||||||
|
// anymore.
|
||||||
|
// We still don't know why `$SHELL -l -i -c '/usr/bin/env -0'` would
|
||||||
|
// do that, but it does, and `exit 0` helps.
|
||||||
|
&format!("cd {dir:?}; echo {marker}; /usr/bin/env -0; exit 0;"),
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.context("failed to spawn login shell to source login environment variables")?;
|
||||||
|
|
||||||
|
anyhow::ensure!(
|
||||||
|
output.status.success(),
|
||||||
|
"login shell exited with error {:?}",
|
||||||
|
output.status
|
||||||
|
);
|
||||||
|
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
let env_output_start = stdout.find(marker).ok_or_else(|| {
|
||||||
|
anyhow!(
|
||||||
|
"failed to parse output of `env` command in login shell: {}",
|
||||||
|
stdout
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut parsed_env = HashMap::default();
|
||||||
|
let env_output = &stdout[env_output_start + marker.len()..];
|
||||||
|
for line in env_output.split_terminator('\0') {
|
||||||
|
if let Some(separator_index) = line.find('=') {
|
||||||
|
let key = line[..separator_index].to_string();
|
||||||
|
let value = line[separator_index + 1..].to_string();
|
||||||
|
parsed_env.insert(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(parsed_env)
|
||||||
|
}
|
||||||
|
|
|
@ -71,6 +71,7 @@ impl LspAdapter for AstroLspAdapter {
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: self.node.binary_path().await?,
|
path: self.node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -122,6 +123,7 @@ async fn get_cached_server_binary(
|
||||||
if server_path.exists() {
|
if server_path.exists() {
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: node.binary_path().await?,
|
path: node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -84,6 +84,7 @@ impl super::LspAdapter for CLspAdapter {
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: binary_path,
|
path: binary_path,
|
||||||
|
env: None,
|
||||||
arguments: vec![],
|
arguments: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -260,6 +261,7 @@ async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServ
|
||||||
if clangd_bin.exists() {
|
if clangd_bin.exists() {
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: clangd_bin,
|
path: clangd_bin,
|
||||||
|
env: None,
|
||||||
arguments: vec![],
|
arguments: vec![],
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -105,6 +105,7 @@ impl super::LspAdapter for ClojureLspAdapter {
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: binary_path,
|
path: binary_path,
|
||||||
|
env: None,
|
||||||
arguments: vec![],
|
arguments: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -118,6 +119,7 @@ impl super::LspAdapter for ClojureLspAdapter {
|
||||||
if binary_path.exists() {
|
if binary_path.exists() {
|
||||||
Some(LanguageServerBinary {
|
Some(LanguageServerBinary {
|
||||||
path: binary_path,
|
path: binary_path,
|
||||||
|
env: None,
|
||||||
arguments: vec![],
|
arguments: vec![],
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -133,6 +135,7 @@ impl super::LspAdapter for ClojureLspAdapter {
|
||||||
if binary_path.exists() {
|
if binary_path.exists() {
|
||||||
Some(LanguageServerBinary {
|
Some(LanguageServerBinary {
|
||||||
path: binary_path,
|
path: binary_path,
|
||||||
|
env: None,
|
||||||
arguments: vec!["--version".into()],
|
arguments: vec!["--version".into()],
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -92,6 +92,7 @@ impl super::LspAdapter for OmniSharpAdapter {
|
||||||
}
|
}
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: binary_path,
|
path: binary_path,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(),
|
arguments: server_binary_arguments(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -136,6 +137,7 @@ async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServ
|
||||||
if let Some(path) = last_binary_path {
|
if let Some(path) = last_binary_path {
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path,
|
path,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(),
|
arguments: server_binary_arguments(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -72,6 +72,7 @@ impl LspAdapter for CssLspAdapter {
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: self.node.binary_path().await?,
|
path: self.node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -116,6 +117,7 @@ async fn get_cached_server_binary(
|
||||||
if server_path.exists() {
|
if server_path.exists() {
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: node.binary_path().await?,
|
path: node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -39,6 +39,7 @@ impl LspAdapter for DartLanguageServer {
|
||||||
) -> Option<LanguageServerBinary> {
|
) -> Option<LanguageServerBinary> {
|
||||||
Some(LanguageServerBinary {
|
Some(LanguageServerBinary {
|
||||||
path: "dart".into(),
|
path: "dart".into(),
|
||||||
|
env: None,
|
||||||
arguments: vec!["language-server".into(), "--protocol=lsp".into()],
|
arguments: vec!["language-server".into(), "--protocol=lsp".into()],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,6 +134,7 @@ impl LspAdapter for DenoLspAdapter {
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: binary_path,
|
path: binary_path,
|
||||||
|
env: None,
|
||||||
arguments: deno_server_binary_arguments(),
|
arguments: deno_server_binary_arguments(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -220,6 +221,7 @@ async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServ
|
||||||
if fs::metadata(&binary).await.is_ok() {
|
if fs::metadata(&binary).await.is_ok() {
|
||||||
return Ok(LanguageServerBinary {
|
return Ok(LanguageServerBinary {
|
||||||
path: binary,
|
path: binary,
|
||||||
|
env: None,
|
||||||
arguments: deno_server_binary_arguments(),
|
arguments: deno_server_binary_arguments(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,7 @@ impl LspAdapter for DockerfileLspAdapter {
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: self.node.binary_path().await?,
|
path: self.node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -110,6 +111,7 @@ async fn get_cached_server_binary(
|
||||||
if server_path.exists() {
|
if server_path.exists() {
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: node.binary_path().await?,
|
path: node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -174,6 +174,7 @@ impl LspAdapter for ElixirLspAdapter {
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: binary_path,
|
path: binary_path,
|
||||||
|
env: None,
|
||||||
arguments: vec![],
|
arguments: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -284,6 +285,7 @@ async fn get_cached_server_binary_elixir_ls(
|
||||||
if server_path.exists() {
|
if server_path.exists() {
|
||||||
Some(LanguageServerBinary {
|
Some(LanguageServerBinary {
|
||||||
path: server_path,
|
path: server_path,
|
||||||
|
env: None,
|
||||||
arguments: vec![],
|
arguments: vec![],
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -369,6 +371,7 @@ impl LspAdapter for NextLspAdapter {
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: binary_path,
|
path: binary_path,
|
||||||
|
env: None,
|
||||||
arguments: vec!["--stdio".into()],
|
arguments: vec!["--stdio".into()],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -435,6 +438,7 @@ async fn get_cached_server_binary_next(container_dir: PathBuf) -> Option<Languag
|
||||||
if let Some(path) = last_binary_path {
|
if let Some(path) = last_binary_path {
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path,
|
path,
|
||||||
|
env: None,
|
||||||
arguments: Vec::new(),
|
arguments: Vec::new(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -476,6 +480,7 @@ impl LspAdapter for LocalLspAdapter {
|
||||||
let path = shellexpand::full(&self.path)?;
|
let path = shellexpand::full(&self.path)?;
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: PathBuf::from(path.deref()),
|
path: PathBuf::from(path.deref()),
|
||||||
|
env: None,
|
||||||
arguments: self.arguments.iter().map(|arg| arg.into()).collect(),
|
arguments: self.arguments.iter().map(|arg| arg.into()).collect(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -488,6 +493,7 @@ impl LspAdapter for LocalLspAdapter {
|
||||||
let path = shellexpand::full(&self.path).ok()?;
|
let path = shellexpand::full(&self.path).ok()?;
|
||||||
Some(LanguageServerBinary {
|
Some(LanguageServerBinary {
|
||||||
path: PathBuf::from(path.deref()),
|
path: PathBuf::from(path.deref()),
|
||||||
|
env: None,
|
||||||
arguments: self.arguments.iter().map(|arg| arg.into()).collect(),
|
arguments: self.arguments.iter().map(|arg| arg.into()).collect(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -496,6 +502,7 @@ impl LspAdapter for LocalLspAdapter {
|
||||||
let path = shellexpand::full(&self.path).ok()?;
|
let path = shellexpand::full(&self.path).ok()?;
|
||||||
Some(LanguageServerBinary {
|
Some(LanguageServerBinary {
|
||||||
path: PathBuf::from(path.deref()),
|
path: PathBuf::from(path.deref()),
|
||||||
|
env: None,
|
||||||
arguments: self.arguments.iter().map(|arg| arg.into()).collect(),
|
arguments: self.arguments.iter().map(|arg| arg.into()).collect(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,7 @@ impl LspAdapter for ElmLspAdapter {
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: self.node.binary_path().await?,
|
path: self.node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -134,6 +135,7 @@ async fn get_cached_server_binary(
|
||||||
if server_path.exists() {
|
if server_path.exists() {
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: node.binary_path().await?,
|
path: node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -41,6 +41,7 @@ impl LspAdapter for ErlangLspAdapter {
|
||||||
) -> Option<LanguageServerBinary> {
|
) -> Option<LanguageServerBinary> {
|
||||||
Some(LanguageServerBinary {
|
Some(LanguageServerBinary {
|
||||||
path: "erlang_ls".into(),
|
path: "erlang_ls".into(),
|
||||||
|
env: None,
|
||||||
arguments: vec![],
|
arguments: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -52,6 +53,7 @@ impl LspAdapter for ErlangLspAdapter {
|
||||||
async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
|
async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
|
||||||
Some(LanguageServerBinary {
|
Some(LanguageServerBinary {
|
||||||
path: "erlang_ls".into(),
|
path: "erlang_ls".into(),
|
||||||
|
env: None,
|
||||||
arguments: vec!["--version".into()],
|
arguments: vec!["--version".into()],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,7 @@ impl LspAdapter for GleamLspAdapter {
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: binary_path,
|
path: binary_path,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(),
|
arguments: server_binary_arguments(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -116,6 +117,7 @@ async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServ
|
||||||
|
|
||||||
anyhow::Ok(LanguageServerBinary {
|
anyhow::Ok(LanguageServerBinary {
|
||||||
path: last.ok_or_else(|| anyhow!("no cached binary"))?,
|
path: last.ok_or_else(|| anyhow!("no cached binary"))?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(),
|
arguments: server_binary_arguments(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -58,6 +58,25 @@ impl super::LspAdapter for GoLspAdapter {
|
||||||
Ok(Box::new(version) as Box<_>)
|
Ok(Box::new(version) as Box<_>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_if_user_installed(
|
||||||
|
&self,
|
||||||
|
delegate: &Arc<dyn LspAdapterDelegate>,
|
||||||
|
cx: &mut AsyncAppContext,
|
||||||
|
) -> Option<Task<Option<LanguageServerBinary>>> {
|
||||||
|
let delegate = delegate.clone();
|
||||||
|
|
||||||
|
Some(cx.spawn(|cx| async move {
|
||||||
|
match cx.update(|cx| delegate.which_command(OsString::from("gopls"), cx)) {
|
||||||
|
Ok(task) => task.await.map(|(path, env)| LanguageServerBinary {
|
||||||
|
path,
|
||||||
|
arguments: server_binary_arguments(),
|
||||||
|
env: Some(env),
|
||||||
|
}),
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
fn will_fetch_server(
|
fn will_fetch_server(
|
||||||
&self,
|
&self,
|
||||||
delegate: &Arc<dyn LspAdapterDelegate>,
|
delegate: &Arc<dyn LspAdapterDelegate>,
|
||||||
|
@ -107,6 +126,7 @@ impl super::LspAdapter for GoLspAdapter {
|
||||||
return Ok(LanguageServerBinary {
|
return Ok(LanguageServerBinary {
|
||||||
path: binary_path.to_path_buf(),
|
path: binary_path.to_path_buf(),
|
||||||
arguments: server_binary_arguments(),
|
arguments: server_binary_arguments(),
|
||||||
|
env: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,6 +174,7 @@ impl super::LspAdapter for GoLspAdapter {
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: binary_path.to_path_buf(),
|
path: binary_path.to_path_buf(),
|
||||||
arguments: server_binary_arguments(),
|
arguments: server_binary_arguments(),
|
||||||
|
env: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,6 +393,7 @@ async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServ
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path,
|
path,
|
||||||
arguments: server_binary_arguments(),
|
arguments: server_binary_arguments(),
|
||||||
|
env: None,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("no cached binary"))
|
Err(anyhow!("no cached binary"))
|
||||||
|
|
|
@ -41,6 +41,7 @@ impl LspAdapter for HaskellLanguageServer {
|
||||||
) -> Option<LanguageServerBinary> {
|
) -> Option<LanguageServerBinary> {
|
||||||
Some(LanguageServerBinary {
|
Some(LanguageServerBinary {
|
||||||
path: "haskell-language-server-wrapper".into(),
|
path: "haskell-language-server-wrapper".into(),
|
||||||
|
env: None,
|
||||||
arguments: vec!["lsp".into()],
|
arguments: vec!["lsp".into()],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,7 @@ impl LspAdapter for HtmlLspAdapter {
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: self.node.binary_path().await?,
|
path: self.node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -116,6 +117,7 @@ async fn get_cached_server_binary(
|
||||||
if server_path.exists() {
|
if server_path.exists() {
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: node.binary_path().await?,
|
path: node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -122,6 +122,7 @@ impl LspAdapter for JsonLspAdapter {
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: self.node.binary_path().await?,
|
path: self.node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -177,6 +178,7 @@ async fn get_cached_server_binary(
|
||||||
if server_path.exists() {
|
if server_path.exists() {
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: node.binary_path().await?,
|
path: node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -94,6 +94,7 @@ impl super::LspAdapter for LuaLspAdapter {
|
||||||
}
|
}
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: binary_path,
|
path: binary_path,
|
||||||
|
env: None,
|
||||||
arguments: Vec::new(),
|
arguments: Vec::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -138,6 +139,7 @@ async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServ
|
||||||
if let Some(path) = last_binary_path {
|
if let Some(path) = last_binary_path {
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path,
|
path,
|
||||||
|
env: None,
|
||||||
arguments: Vec::new(),
|
arguments: Vec::new(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -41,6 +41,7 @@ impl LspAdapter for NuLanguageServer {
|
||||||
) -> Option<LanguageServerBinary> {
|
) -> Option<LanguageServerBinary> {
|
||||||
Some(LanguageServerBinary {
|
Some(LanguageServerBinary {
|
||||||
path: "nu".into(),
|
path: "nu".into(),
|
||||||
|
env: None,
|
||||||
arguments: vec!["--lsp".into()],
|
arguments: vec!["--lsp".into()],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ impl LspAdapter for OCamlLspAdapter {
|
||||||
) -> Option<LanguageServerBinary> {
|
) -> Option<LanguageServerBinary> {
|
||||||
Some(LanguageServerBinary {
|
Some(LanguageServerBinary {
|
||||||
path: "ocamllsp".into(),
|
path: "ocamllsp".into(),
|
||||||
|
env: None,
|
||||||
arguments: vec![],
|
arguments: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,7 @@ impl LspAdapter for IntelephenseLspAdapter {
|
||||||
}
|
}
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: self.node.binary_path().await?,
|
path: self.node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: intelephense_server_binary_arguments(&server_path),
|
arguments: intelephense_server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -126,6 +127,7 @@ async fn get_cached_server_binary(
|
||||||
if server_path.exists() {
|
if server_path.exists() {
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: node.binary_path().await?,
|
path: node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: intelephense_server_binary_arguments(&server_path),
|
arguments: intelephense_server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -70,6 +70,7 @@ impl LspAdapter for PrismaLspAdapter {
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: self.node.binary_path().await?,
|
path: self.node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -112,6 +113,7 @@ async fn get_cached_server_binary(
|
||||||
if server_path.exists() {
|
if server_path.exists() {
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: node.binary_path().await?,
|
path: node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -74,6 +74,7 @@ impl LspAdapter for PurescriptLspAdapter {
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: self.node.binary_path().await?,
|
path: self.node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -127,6 +128,7 @@ async fn get_cached_server_binary(
|
||||||
if server_path.exists() {
|
if server_path.exists() {
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: node.binary_path().await?,
|
path: node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -62,6 +62,7 @@ impl LspAdapter for PythonLspAdapter {
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: self.node.binary_path().await?,
|
path: self.node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -167,6 +168,7 @@ async fn get_cached_server_binary(
|
||||||
if server_path.exists() {
|
if server_path.exists() {
|
||||||
Some(LanguageServerBinary {
|
Some(LanguageServerBinary {
|
||||||
path: node.binary_path().await.log_err()?,
|
path: node.binary_path().await.log_err()?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -39,6 +39,7 @@ impl LspAdapter for RubyLanguageServer {
|
||||||
) -> Option<LanguageServerBinary> {
|
) -> Option<LanguageServerBinary> {
|
||||||
Some(LanguageServerBinary {
|
Some(LanguageServerBinary {
|
||||||
path: "solargraph".into(),
|
path: "solargraph".into(),
|
||||||
|
env: None,
|
||||||
arguments: vec!["stdio".into()],
|
arguments: vec!["stdio".into()],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,6 +89,7 @@ impl LspAdapter for RustLspAdapter {
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: destination_path,
|
path: destination_path,
|
||||||
|
env: None,
|
||||||
arguments: Default::default(),
|
arguments: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -296,6 +297,7 @@ async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServ
|
||||||
|
|
||||||
anyhow::Ok(LanguageServerBinary {
|
anyhow::Ok(LanguageServerBinary {
|
||||||
path: last.ok_or_else(|| anyhow!("no cached binary"))?,
|
path: last.ok_or_else(|| anyhow!("no cached binary"))?,
|
||||||
|
env: None,
|
||||||
arguments: Default::default(),
|
arguments: Default::default(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -71,6 +71,7 @@ impl LspAdapter for SvelteLspAdapter {
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: self.node.binary_path().await?,
|
path: self.node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -148,6 +149,7 @@ async fn get_cached_server_binary(
|
||||||
if server_path.exists() {
|
if server_path.exists() {
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: node.binary_path().await?,
|
path: node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -73,6 +73,7 @@ impl LspAdapter for TailwindLspAdapter {
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: self.node.binary_path().await?,
|
path: self.node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -150,6 +151,7 @@ async fn get_cached_server_binary(
|
||||||
if server_path.exists() {
|
if server_path.exists() {
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: node.binary_path().await?,
|
path: node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -85,6 +85,7 @@ impl LspAdapter for TaploLspAdapter {
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: binary_path,
|
path: binary_path,
|
||||||
|
env: None,
|
||||||
arguments: vec!["lsp".into(), "stdio".into()],
|
arguments: vec!["lsp".into(), "stdio".into()],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -120,6 +121,7 @@ async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServ
|
||||||
|
|
||||||
anyhow::Ok(LanguageServerBinary {
|
anyhow::Ok(LanguageServerBinary {
|
||||||
path: last.context("no cached binary")?,
|
path: last.context("no cached binary")?,
|
||||||
|
env: None,
|
||||||
arguments: Default::default(),
|
arguments: Default::default(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -97,6 +97,7 @@ impl LspAdapter for TypeScriptLspAdapter {
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: self.node.binary_path().await?,
|
path: self.node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: typescript_server_binary_arguments(&server_path),
|
arguments: typescript_server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -192,11 +193,13 @@ async fn get_cached_ts_server_binary(
|
||||||
if new_server_path.exists() {
|
if new_server_path.exists() {
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: node.binary_path().await?,
|
path: node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: typescript_server_binary_arguments(&new_server_path),
|
arguments: typescript_server_binary_arguments(&new_server_path),
|
||||||
})
|
})
|
||||||
} else if old_server_path.exists() {
|
} else if old_server_path.exists() {
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: node.binary_path().await?,
|
path: node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: typescript_server_binary_arguments(&old_server_path),
|
arguments: typescript_server_binary_arguments(&old_server_path),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -311,6 +314,7 @@ impl LspAdapter for EsLintLspAdapter {
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: self.node.binary_path().await?,
|
path: self.node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: eslint_server_binary_arguments(&server_path),
|
arguments: eslint_server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -358,6 +362,7 @@ async fn get_cached_eslint_server_binary(
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: node.binary_path().await?,
|
path: node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: eslint_server_binary_arguments(&server_path),
|
arguments: eslint_server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -41,6 +41,7 @@ impl LspAdapter for UiuaLanguageServer {
|
||||||
) -> Option<LanguageServerBinary> {
|
) -> Option<LanguageServerBinary> {
|
||||||
Some(LanguageServerBinary {
|
Some(LanguageServerBinary {
|
||||||
path: "uiua".into(),
|
path: "uiua".into(),
|
||||||
|
env: None,
|
||||||
arguments: vec!["lsp".into()],
|
arguments: vec!["lsp".into()],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,6 +118,7 @@ impl super::LspAdapter for VueLspAdapter {
|
||||||
*self.typescript_install_path.lock() = Some(ts_path);
|
*self.typescript_install_path.lock() = Some(ts_path);
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: self.node.binary_path().await?,
|
path: self.node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: vue_server_binary_arguments(&server_path),
|
arguments: vue_server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -204,6 +205,7 @@ async fn get_cached_server_binary(
|
||||||
Ok((
|
Ok((
|
||||||
LanguageServerBinary {
|
LanguageServerBinary {
|
||||||
path: node.binary_path().await?,
|
path: node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: vue_server_binary_arguments(&server_path),
|
arguments: vue_server_binary_arguments(&server_path),
|
||||||
},
|
},
|
||||||
typescript_path,
|
typescript_path,
|
||||||
|
|
|
@ -74,6 +74,7 @@ impl LspAdapter for YamlLspAdapter {
|
||||||
|
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: self.node.binary_path().await?,
|
path: self.node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -124,6 +125,7 @@ async fn get_cached_server_binary(
|
||||||
if server_path.exists() {
|
if server_path.exists() {
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: node.binary_path().await?,
|
path: node.binary_path().await?,
|
||||||
|
env: None,
|
||||||
arguments: server_binary_arguments(&server_path),
|
arguments: server_binary_arguments(&server_path),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -3,10 +3,13 @@ use async_compression::futures::bufread::GzipDecoder;
|
||||||
use async_tar::Archive;
|
use async_tar::Archive;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use futures::{io::BufReader, StreamExt};
|
use futures::{io::BufReader, StreamExt};
|
||||||
|
use gpui::{AsyncAppContext, Task};
|
||||||
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||||
use lsp::LanguageServerBinary;
|
use lsp::LanguageServerBinary;
|
||||||
use smol::fs;
|
use smol::fs;
|
||||||
use std::env::consts::{ARCH, OS};
|
use std::env::consts::{ARCH, OS};
|
||||||
|
use std::ffi::OsString;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::{any::Any, path::PathBuf};
|
use std::{any::Any, path::PathBuf};
|
||||||
use util::async_maybe;
|
use util::async_maybe;
|
||||||
use util::github::latest_github_release;
|
use util::github::latest_github_release;
|
||||||
|
@ -44,6 +47,25 @@ impl LspAdapter for ZlsAdapter {
|
||||||
Ok(Box::new(version) as Box<_>)
|
Ok(Box::new(version) as Box<_>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_if_user_installed(
|
||||||
|
&self,
|
||||||
|
delegate: &Arc<dyn LspAdapterDelegate>,
|
||||||
|
cx: &mut AsyncAppContext,
|
||||||
|
) -> Option<Task<Option<LanguageServerBinary>>> {
|
||||||
|
let delegate = delegate.clone();
|
||||||
|
|
||||||
|
Some(cx.spawn(|cx| async move {
|
||||||
|
match cx.update(|cx| delegate.which_command(OsString::from("zls"), cx)) {
|
||||||
|
Ok(task) => task.await.map(|(path, env)| LanguageServerBinary {
|
||||||
|
path,
|
||||||
|
arguments: vec![],
|
||||||
|
env: Some(env),
|
||||||
|
}),
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
async fn fetch_server_binary(
|
async fn fetch_server_binary(
|
||||||
&self,
|
&self,
|
||||||
version: Box<dyn 'static + Send + Any>,
|
version: Box<dyn 'static + Send + Any>,
|
||||||
|
@ -75,6 +97,7 @@ impl LspAdapter for ZlsAdapter {
|
||||||
}
|
}
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path: binary_path,
|
path: binary_path,
|
||||||
|
env: None,
|
||||||
arguments: vec![],
|
arguments: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -119,6 +142,7 @@ async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServ
|
||||||
if let Some(path) = last_binary_path {
|
if let Some(path) = last_binary_path {
|
||||||
Ok(LanguageServerBinary {
|
Ok(LanguageServerBinary {
|
||||||
path,
|
path,
|
||||||
|
env: None,
|
||||||
arguments: Vec::new(),
|
arguments: Vec::new(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue