Provide wasm extensions with APIs needed for using pre-installed LSP binaries (#9085)

In this PR, we've added two new methods that LSP extensions can call:
* `shell_env()`, for retrieving the environment variables set in the
user's default shell in the worktree
* `which(command)`, for looking up paths to an executable (accounting
for the user's shell env in the worktree)

To test this out, we moved the `uiua` language support into an
extension. We went ahead and removed the built-in support, since this
language is extremely obscure. Sorry @mikayla-maki. To continue coding
in Uiua in Zed, for now you can `Add Dev Extension` from the extensions
pane, and select the `extensions/uiua` directory in the Zed repo. Very
soon, we'll support publishing these extensions so that you'll be able
to just install it normally.

Release Notes:

- N/A

---------

Co-authored-by: Marshall <marshall@zed.dev>
This commit is contained in:
Max Brunsfeld 2024-03-08 14:18:06 -08:00 committed by GitHub
parent 5abcc1c3c5
commit 8a6264d933
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 235 additions and 256 deletions

View file

@ -72,7 +72,7 @@ use std::{
cmp::{self, Ordering},
convert::TryInto,
env,
ffi::OsString,
ffi::OsStr,
hash::Hash,
mem,
num::NonZeroU32,
@ -9390,6 +9390,7 @@ struct ProjectLspAdapterDelegate {
fs: Arc<dyn Fs>,
http_client: Arc<dyn HttpClient>,
language_registry: Arc<LanguageRegistry>,
shell_env: Mutex<Option<HashMap<String, String>>>,
}
impl ProjectLspAdapterDelegate {
@ -9400,8 +9401,21 @@ impl ProjectLspAdapterDelegate {
fs: project.fs.clone(),
http_client: project.client.http_client(),
language_registry: project.languages.clone(),
shell_env: Default::default(),
})
}
async fn load_shell_env(&self) {
let worktree_abs_path = self.worktree.abs_path();
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()
.unwrap_or_default();
*self.shell_env.lock() = Some(shell_env);
}
}
#[async_trait]
@ -9416,32 +9430,20 @@ impl LspAdapterDelegate for ProjectLspAdapterDelegate {
self.http_client.clone()
}
async fn which_command(&self, command: OsString) -> Option<(PathBuf, HashMap<String, String>)> {
async fn shell_env(&self) -> HashMap<String, String> {
self.load_shell_env().await;
self.shell_env.lock().as_ref().cloned().unwrap_or_default()
}
async fn which(&self, command: &OsStr) -> Option<PathBuf> {
let worktree_abs_path = self.worktree.abs_path();
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 shell PATH {:?}: {error}",
command.to_string_lossy(),
shell_path.map(String::as_str).unwrap_or("")
);
None
}
}
} else {
None
}
self.load_shell_env().await;
let shell_path = self
.shell_env
.lock()
.as_ref()
.and_then(|shell_env| shell_env.get("PATH").cloned());
which::which_in(command, shell_path.as_ref(), &worktree_abs_path).ok()
}
fn update_status(