Compare commits
14 Commits
inspector-
...
v0.124.4-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4be47ba1e | ||
|
|
dae95e03e6 | ||
|
|
4faf3acf67 | ||
|
|
9e9ea6fcb4 | ||
|
|
e3d5a0f649 | ||
|
|
bd317eea49 | ||
|
|
77cdc280c2 | ||
|
|
1ba33763a4 | ||
|
|
292d32eb70 | ||
|
|
10df9dfca1 | ||
|
|
57426b925e | ||
|
|
e12d617264 | ||
|
|
ca1a95e6e2 | ||
|
|
bc8e7e0cc7 |
26
Cargo.lock
generated
26
Cargo.lock
generated
@@ -1349,7 +1349,7 @@ dependencies = [
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn 2.0.48",
|
||||
"which",
|
||||
"which 4.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4340,11 +4340,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.5"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
|
||||
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
||||
dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6915,6 +6915,7 @@ dependencies = [
|
||||
"toml 0.8.10",
|
||||
"unindent",
|
||||
"util",
|
||||
"which 6.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7024,7 +7025,7 @@ dependencies = [
|
||||
"prost-types 0.9.0",
|
||||
"regex",
|
||||
"tempfile",
|
||||
"which",
|
||||
"which 4.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -11396,6 +11397,19 @@ dependencies = [
|
||||
"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]]
|
||||
name = "whoami"
|
||||
version = "1.4.1"
|
||||
@@ -11911,7 +11925,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.124.0"
|
||||
version = "0.124.4"
|
||||
dependencies = [
|
||||
"activity_indicator",
|
||||
"ai",
|
||||
|
||||
@@ -278,6 +278,7 @@ unindent = "0.1.7"
|
||||
url = "2.2"
|
||||
uuid = { version = "1.1.2", features = ["v4"] }
|
||||
wasmtime = "16"
|
||||
which = "6.0.0"
|
||||
sys-locale = "0.3.1"
|
||||
|
||||
[patch.crates-io]
|
||||
|
||||
@@ -122,16 +122,13 @@ impl AssistantPanel {
|
||||
.await
|
||||
.log_err()
|
||||
.unwrap_or_default();
|
||||
let (api_url, model_name) = cx
|
||||
.update(|cx| {
|
||||
let settings = AssistantSettings::get_global(cx);
|
||||
(
|
||||
settings.openai_api_url.clone(),
|
||||
settings.default_open_ai_model.full_name().to_string(),
|
||||
)
|
||||
})
|
||||
.log_err()
|
||||
.unwrap();
|
||||
let (api_url, model_name) = cx.update(|cx| {
|
||||
let settings = AssistantSettings::get_global(cx);
|
||||
(
|
||||
settings.openai_api_url.clone(),
|
||||
settings.default_open_ai_model.full_name().to_string(),
|
||||
)
|
||||
})?;
|
||||
let completion_provider = OpenAiCompletionProvider::new(
|
||||
api_url,
|
||||
model_name,
|
||||
|
||||
@@ -428,6 +428,8 @@ impl Copilot {
|
||||
let binary = LanguageServerBinary {
|
||||
path: node_path,
|
||||
arguments,
|
||||
// TODO: We could set HTTP_PROXY etc here and fix the copilot issue.
|
||||
env: None,
|
||||
};
|
||||
|
||||
let server = LanguageServer::new(
|
||||
|
||||
@@ -406,6 +406,10 @@ impl ExtensionStore {
|
||||
}));
|
||||
|
||||
for language_name in &languages_to_add {
|
||||
if language_name.as_ref() == "Swift" {
|
||||
continue;
|
||||
}
|
||||
|
||||
let language = manifest.languages.get(language_name.as_ref()).unwrap();
|
||||
let mut language_path = self.extensions_dir.clone();
|
||||
language_path.extend([language.extension.as_ref(), language.path.as_path()]);
|
||||
|
||||
@@ -108,6 +108,35 @@ impl Keystroke {
|
||||
ime_key,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a new keystroke with the ime_key filled.
|
||||
/// This is used for dispatch_keystroke where we want users to
|
||||
/// be able to simulate typing "space", etc.
|
||||
pub fn with_simulated_ime(mut self) -> Self {
|
||||
if self.ime_key.is_none()
|
||||
&& !self.modifiers.command
|
||||
&& !self.modifiers.control
|
||||
&& !self.modifiers.function
|
||||
&& !self.modifiers.alt
|
||||
{
|
||||
self.ime_key = match self.key.as_str() {
|
||||
"space" => Some(" ".into()),
|
||||
"tab" => Some("\t".into()),
|
||||
"enter" => Some("\n".into()),
|
||||
"up" | "down" | "left" | "right" | "pageup" | "pagedown" | "home" | "end"
|
||||
| "delete" | "escape" | "backspace" | "f1" | "f2" | "f3" | "f4" | "f5" | "f6"
|
||||
| "f7" | "f8" | "f9" | "f10" | "f11" | "f12" => None,
|
||||
key => {
|
||||
if self.modifiers.shift {
|
||||
Some(key.to_uppercase())
|
||||
} else {
|
||||
Some(key.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Keystroke {
|
||||
|
||||
@@ -1101,18 +1101,8 @@ impl<'a> WindowContext<'a> {
|
||||
|
||||
/// Dispatch a given keystroke as though the user had typed it.
|
||||
/// You can create a keystroke with Keystroke::parse("").
|
||||
pub fn dispatch_keystroke(&mut self, mut keystroke: Keystroke) -> bool {
|
||||
if keystroke.ime_key.is_none()
|
||||
&& !keystroke.modifiers.command
|
||||
&& !keystroke.modifiers.control
|
||||
&& !keystroke.modifiers.function
|
||||
{
|
||||
keystroke.ime_key = Some(if keystroke.modifiers.shift {
|
||||
keystroke.key.to_uppercase().clone()
|
||||
} else {
|
||||
keystroke.key.clone()
|
||||
})
|
||||
}
|
||||
pub fn dispatch_keystroke(&mut self, keystroke: Keystroke) -> bool {
|
||||
let keystroke = keystroke.with_simulated_ime();
|
||||
if self.dispatch_event(PlatformInput::KeyDown(KeyDownEvent {
|
||||
keystroke: keystroke.clone(),
|
||||
is_held: false,
|
||||
|
||||
@@ -38,6 +38,7 @@ use serde_json::Value;
|
||||
use std::{
|
||||
any::Any,
|
||||
cell::RefCell,
|
||||
ffi::OsString,
|
||||
fmt::Debug,
|
||||
hash::Hash,
|
||||
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(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
@@ -240,6 +249,11 @@ impl CachedLspAdapter {
|
||||
pub trait LspAdapterDelegate: Send + Sync {
|
||||
fn show_notification(&self, message: &str, cx: &mut AppContext);
|
||||
fn http_client(&self) -> Arc<dyn HttpClient>;
|
||||
fn which_command(
|
||||
&self,
|
||||
command: OsString,
|
||||
cx: &AppContext,
|
||||
) -> Task<Option<(PathBuf, HashMap<String, String>)>>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -248,6 +262,14 @@ pub trait LspAdapter: 'static + Send + Sync {
|
||||
|
||||
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(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
|
||||
@@ -558,34 +558,41 @@ impl LanguageRegistry {
|
||||
let task = {
|
||||
let container_dir = container_dir.clone();
|
||||
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
|
||||
.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();
|
||||
let user_binary_task = check_user_installed_binary(
|
||||
adapter.clone(),
|
||||
language.clone(),
|
||||
delegate.clone(),
|
||||
&mut cx,
|
||||
);
|
||||
let binary = if let Some(user_binary) = user_binary_task.await {
|
||||
user_binary
|
||||
} else {
|
||||
// If we want to install a binary globally, we need to wait for
|
||||
// the login shell to be set on our process.
|
||||
login_shell_env_loaded.await;
|
||||
|
||||
let binary = match entry.await {
|
||||
Ok(binary) => binary,
|
||||
Err(err) => anyhow::bail!("{err}"),
|
||||
get_or_install_binary(
|
||||
this,
|
||||
&adapter,
|
||||
language,
|
||||
&delegate,
|
||||
&cx,
|
||||
container_dir,
|
||||
lsp_binary_statuses,
|
||||
)
|
||||
.await?
|
||||
};
|
||||
|
||||
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(
|
||||
adapter: Arc<CachedLspAdapter>,
|
||||
language: Arc<Language>,
|
||||
@@ -757,15 +820,20 @@ async fn get_binary(
|
||||
.await
|
||||
{
|
||||
statuses.send(language.clone(), LanguageServerBinaryStatus::Cached);
|
||||
return Ok(binary);
|
||||
} else {
|
||||
statuses.send(
|
||||
language.clone(),
|
||||
LanguageServerBinaryStatus::Failed {
|
||||
error: format!("{:?}", error),
|
||||
},
|
||||
log::info!(
|
||||
"failed to fetch newest version of language server {:?}. falling back to using {:?}",
|
||||
adapter.name,
|
||||
binary.path.display()
|
||||
);
|
||||
return Ok(binary);
|
||||
}
|
||||
|
||||
statuses.send(
|
||||
language.clone(),
|
||||
LanguageServerBinaryStatus::Failed {
|
||||
error: format!("{:?}", error),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
binary
|
||||
@@ -779,14 +847,23 @@ async fn fetch_latest_binary(
|
||||
lsp_binary_statuses_tx: LspBinaryStatusSender,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let container_dir: Arc<Path> = container_dir.into();
|
||||
|
||||
lsp_binary_statuses_tx.send(
|
||||
language.clone(),
|
||||
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?;
|
||||
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
|
||||
.fetch_server_binary(version_info, container_dir.to_path_buf(), delegate)
|
||||
.await?;
|
||||
|
||||
@@ -55,6 +55,7 @@ pub enum IoKind {
|
||||
pub struct LanguageServerBinary {
|
||||
pub path: PathBuf,
|
||||
pub arguments: Vec<OsString>,
|
||||
pub env: Option<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
/// A running language server process.
|
||||
@@ -189,6 +190,7 @@ impl LanguageServer {
|
||||
let mut server = process::Command::new(&binary.path)
|
||||
.current_dir(working_dir)
|
||||
.args(binary.arguments)
|
||||
.envs(binary.env.unwrap_or_default())
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
|
||||
@@ -192,6 +192,7 @@ impl Prettier {
|
||||
LanguageServerBinary {
|
||||
path: node_path,
|
||||
arguments: vec![prettier_server.into(), prettier_dir.as_path().into()],
|
||||
env: None,
|
||||
},
|
||||
Path::new("/"),
|
||||
None,
|
||||
|
||||
@@ -65,6 +65,7 @@ text.workspace = true
|
||||
thiserror.workspace = true
|
||||
toml.workspace = true
|
||||
util.workspace = true
|
||||
which.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
client = { workspace = true, features = ["test-support"] }
|
||||
|
||||
@@ -70,9 +70,14 @@ pub(super) async fn format_with_prettier(
|
||||
match prettier.format(buffer, buffer_path, cx).await {
|
||||
Ok(new_diff) => return Some(FormatOperation::Prettier(new_diff)),
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"Prettier instance from {prettier_path:?} failed to format a buffer: {e:#}"
|
||||
);
|
||||
match prettier_path {
|
||||
Some(prettier_path) => log::error!(
|
||||
"Prettier instance from path {prettier_path:?} failed to format a buffer: {e:#}"
|
||||
),
|
||||
None => log::error!(
|
||||
"Default prettier instance failed to format a buffer: {e:#}"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -366,6 +371,7 @@ fn register_new_prettier(
|
||||
}
|
||||
|
||||
async fn install_prettier_packages(
|
||||
fs: &dyn Fs,
|
||||
plugins_to_install: HashSet<&'static str>,
|
||||
node: Arc<dyn NodeRuntime>,
|
||||
) -> anyhow::Result<()> {
|
||||
@@ -385,18 +391,32 @@ async fn install_prettier_packages(
|
||||
.await
|
||||
.context("fetching latest npm versions")?;
|
||||
|
||||
log::info!("Fetching default prettier and plugins: {packages_to_versions:?}");
|
||||
let default_prettier_dir = DEFAULT_PRETTIER_DIR.as_path();
|
||||
match fs.metadata(default_prettier_dir).await.with_context(|| {
|
||||
format!("fetching FS metadata for default prettier dir {default_prettier_dir:?}")
|
||||
})? {
|
||||
Some(prettier_dir_metadata) => anyhow::ensure!(
|
||||
prettier_dir_metadata.is_dir,
|
||||
"default prettier dir {default_prettier_dir:?} is not a directory"
|
||||
),
|
||||
None => fs
|
||||
.create_dir(default_prettier_dir)
|
||||
.await
|
||||
.with_context(|| format!("creating default prettier dir {default_prettier_dir:?}"))?,
|
||||
}
|
||||
|
||||
log::info!("Installing default prettier and plugins: {packages_to_versions:?}");
|
||||
let borrowed_packages = packages_to_versions
|
||||
.iter()
|
||||
.map(|(package, version)| (package.as_str(), version.as_str()))
|
||||
.collect::<Vec<_>>();
|
||||
node.npm_install_packages(DEFAULT_PRETTIER_DIR.as_path(), &borrowed_packages)
|
||||
node.npm_install_packages(default_prettier_dir, &borrowed_packages)
|
||||
.await
|
||||
.context("fetching formatter packages")?;
|
||||
anyhow::Ok(())
|
||||
}
|
||||
|
||||
async fn save_prettier_server_file(fs: &dyn Fs) -> Result<(), anyhow::Error> {
|
||||
async fn save_prettier_server_file(fs: &dyn Fs) -> anyhow::Result<()> {
|
||||
let prettier_wrapper_path = DEFAULT_PRETTIER_DIR.join(prettier::PRETTIER_SERVER_FILE);
|
||||
fs.save(
|
||||
&prettier_wrapper_path,
|
||||
@@ -413,6 +433,17 @@ async fn save_prettier_server_file(fs: &dyn Fs) -> Result<(), anyhow::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn should_write_prettier_server_file(fs: &dyn Fs) -> bool {
|
||||
let prettier_wrapper_path = DEFAULT_PRETTIER_DIR.join(prettier::PRETTIER_SERVER_FILE);
|
||||
if !fs.is_file(&prettier_wrapper_path).await {
|
||||
return true;
|
||||
}
|
||||
let Ok(prettier_server_file_contents) = fs.load(&prettier_wrapper_path).await else {
|
||||
return true;
|
||||
};
|
||||
prettier_server_file_contents != prettier::PRETTIER_SERVER_JS
|
||||
}
|
||||
|
||||
impl Project {
|
||||
pub fn update_prettier_settings(
|
||||
&self,
|
||||
@@ -623,6 +654,7 @@ impl Project {
|
||||
_cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
// suppress unused code warnings
|
||||
let _ = should_write_prettier_server_file;
|
||||
let _ = install_prettier_packages;
|
||||
let _ = save_prettier_server_file;
|
||||
|
||||
@@ -643,7 +675,6 @@ impl Project {
|
||||
let Some(node) = self.node.as_ref().cloned() else {
|
||||
return;
|
||||
};
|
||||
log::info!("Initializing default prettier with plugins {new_plugins:?}");
|
||||
let fs = Arc::clone(&self.fs);
|
||||
let locate_prettier_installation = match worktree.and_then(|worktree_id| {
|
||||
self.worktree_for_id(worktree_id, cx)
|
||||
@@ -689,6 +720,7 @@ impl Project {
|
||||
}
|
||||
};
|
||||
|
||||
log::info!("Initializing default prettier with plugins {new_plugins:?}");
|
||||
let plugins_to_install = new_plugins.clone();
|
||||
let fs = Arc::clone(&self.fs);
|
||||
let new_installation_task = cx
|
||||
@@ -703,7 +735,7 @@ impl Project {
|
||||
if prettier_path.is_some() {
|
||||
new_plugins.clear();
|
||||
}
|
||||
let mut needs_install = false;
|
||||
let mut needs_install = should_write_prettier_server_file(fs.as_ref()).await;
|
||||
if let Some(previous_installation_task) = previous_installation_task {
|
||||
if let Err(e) = previous_installation_task.await {
|
||||
log::error!("Failed to install default prettier: {e:#}");
|
||||
@@ -744,8 +776,10 @@ impl Project {
|
||||
let installed_plugins = new_plugins.clone();
|
||||
cx.background_executor()
|
||||
.spawn(async move {
|
||||
install_prettier_packages(fs.as_ref(), new_plugins, node).await?;
|
||||
// Save the server file last, so the reinstall need could be determined by the absence of the file.
|
||||
save_prettier_server_file(fs.as_ref()).await?;
|
||||
install_prettier_packages(new_plugins, node).await
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.await
|
||||
.context("prettier & plugins install")
|
||||
|
||||
@@ -71,6 +71,8 @@ use smol::lock::Semaphore;
|
||||
use std::{
|
||||
cmp::{self, Ordering},
|
||||
convert::TryInto,
|
||||
env,
|
||||
ffi::OsString,
|
||||
hash::Hash,
|
||||
mem,
|
||||
num::NonZeroU32,
|
||||
@@ -504,11 +506,6 @@ pub enum FormatTrigger {
|
||||
Manual,
|
||||
}
|
||||
|
||||
struct ProjectLspAdapterDelegate {
|
||||
project: Model<Project>,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
}
|
||||
|
||||
// Currently, formatting operations are represented differently depending on
|
||||
// whether they come from a language server or an external command.
|
||||
enum FormatOperation {
|
||||
@@ -2800,7 +2797,7 @@ impl Project {
|
||||
|
||||
fn start_language_server(
|
||||
&mut self,
|
||||
worktree: &Model<Worktree>,
|
||||
worktree_handle: &Model<Worktree>,
|
||||
adapter: Arc<CachedLspAdapter>,
|
||||
language: Arc<Language>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
@@ -2809,7 +2806,7 @@ impl Project {
|
||||
return;
|
||||
}
|
||||
|
||||
let worktree = worktree.read(cx);
|
||||
let worktree = worktree_handle.read(cx);
|
||||
let worktree_id = worktree.id();
|
||||
let worktree_path = worktree.abs_path();
|
||||
let key = (worktree_id, adapter.name.clone());
|
||||
@@ -2823,7 +2820,7 @@ impl Project {
|
||||
language.clone(),
|
||||
adapter.clone(),
|
||||
Arc::clone(&worktree_path),
|
||||
ProjectLspAdapterDelegate::new(self, cx),
|
||||
ProjectLspAdapterDelegate::new(self, worktree_handle, cx),
|
||||
cx,
|
||||
) {
|
||||
Some(pending_server) => pending_server,
|
||||
@@ -9271,10 +9268,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 {
|
||||
fn new(project: &Project, cx: &ModelContext<Project>) -> Arc<Self> {
|
||||
fn new(project: &Project, worktree: &Model<Worktree>, cx: &ModelContext<Project>) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
project: cx.handle(),
|
||||
worktree: worktree.clone(),
|
||||
http_client: project.client.http_client(),
|
||||
})
|
||||
}
|
||||
@@ -9289,6 +9293,43 @@ impl LspAdapterDelegate for ProjectLspAdapterDelegate {
|
||||
fn http_client(&self) -> Arc<dyn HttpClient> {
|
||||
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 shell PATH {:?}: {error}",
|
||||
command.to_string_lossy(),
|
||||
shell_path.map(String::as_str).unwrap_or("")
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_symbol(symbol: &Symbol) -> proto::Symbol {
|
||||
@@ -9396,3 +9437,55 @@ fn include_text(server: &lsp::LanguageServer) -> bool {
|
||||
})
|
||||
.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)
|
||||
}
|
||||
|
||||
@@ -116,13 +116,20 @@ pub fn update_settings_file<T: Settings>(
|
||||
store.new_text_for_update::<T>(old_text, update)
|
||||
})?;
|
||||
let initial_path = paths::SETTINGS.as_path();
|
||||
let resolved_path = fs
|
||||
.canonicalize(initial_path)
|
||||
.await
|
||||
.with_context(|| format!("Failed to canonicalize settings path {:?}", initial_path))?;
|
||||
fs.atomic_write(resolved_path.clone(), new_text)
|
||||
.await
|
||||
.with_context(|| format!("Failed to write settings to file {:?}", resolved_path))?;
|
||||
if !fs.is_file(initial_path).await {
|
||||
fs.atomic_write(initial_path.to_path_buf(), new_text)
|
||||
.await
|
||||
.with_context(|| format!("Failed to write settings to file {:?}", initial_path))?;
|
||||
} else {
|
||||
let resolved_path = fs.canonicalize(initial_path).await.with_context(|| {
|
||||
format!("Failed to canonicalize settings path {:?}", initial_path)
|
||||
})?;
|
||||
|
||||
fs.atomic_write(resolved_path.clone(), new_text)
|
||||
.await
|
||||
.with_context(|| format!("Failed to write settings to file {:?}", resolved_path))?;
|
||||
}
|
||||
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
|
||||
@@ -828,7 +828,7 @@ fn next_word_end(
|
||||
let mut new_point = point;
|
||||
if new_point.column() < map.line_len(new_point.row()) {
|
||||
*new_point.column_mut() += 1;
|
||||
} else if new_point.row() < map.max_buffer_row() {
|
||||
} else if new_point < map.max_point() {
|
||||
*new_point.row_mut() += 1;
|
||||
*new_point.column_mut() = 0;
|
||||
}
|
||||
@@ -1110,13 +1110,15 @@ fn window_top(
|
||||
|
||||
if let Some(visible_rows) = text_layout_details.visible_rows {
|
||||
let bottom_row = first_visible_line.row() + visible_rows as u32;
|
||||
let new_row = (first_visible_line.row() + (times as u32)).min(bottom_row);
|
||||
let new_row = (first_visible_line.row() + (times as u32))
|
||||
.min(bottom_row)
|
||||
.min(map.max_point().row());
|
||||
let new_col = point.column().min(map.line_len(first_visible_line.row()));
|
||||
|
||||
let new_point = DisplayPoint::new(new_row, new_col);
|
||||
(map.clip_point(new_point, Bias::Left), SelectionGoal::None)
|
||||
} else {
|
||||
let new_row = first_visible_line.row() + (times as u32);
|
||||
let new_row = (first_visible_line.row() + (times as u32)).min(map.max_point().row());
|
||||
let new_col = point.column().min(map.line_len(first_visible_line.row()));
|
||||
|
||||
let new_point = DisplayPoint::new(new_row, new_col);
|
||||
@@ -1134,8 +1136,12 @@ fn window_middle(
|
||||
.scroll_anchor
|
||||
.anchor
|
||||
.to_display_point(map);
|
||||
let max_rows = (visible_rows as u32).min(map.max_buffer_row());
|
||||
let new_row = first_visible_line.row() + (max_rows.div_euclid(2));
|
||||
|
||||
let max_visible_rows =
|
||||
(visible_rows as u32).min(map.max_point().row() - first_visible_line.row());
|
||||
|
||||
let new_row =
|
||||
(first_visible_line.row() + (max_visible_rows / 2) as u32).min(map.max_point().row());
|
||||
let new_col = point.column().min(map.line_len(new_row));
|
||||
let new_point = DisplayPoint::new(new_row, new_col);
|
||||
(map.clip_point(new_point, Bias::Left), SelectionGoal::None)
|
||||
@@ -1157,12 +1163,12 @@ fn window_bottom(
|
||||
.to_display_point(map);
|
||||
let bottom_row = first_visible_line.row()
|
||||
+ (visible_rows + text_layout_details.scroll_anchor.offset.y - 1.).floor() as u32;
|
||||
if bottom_row < map.max_buffer_row()
|
||||
if bottom_row < map.max_point().row()
|
||||
&& text_layout_details.vertical_scroll_margin as usize > times
|
||||
{
|
||||
times = text_layout_details.vertical_scroll_margin.ceil() as usize;
|
||||
}
|
||||
let bottom_row_capped = bottom_row.min(map.max_buffer_row());
|
||||
let bottom_row_capped = bottom_row.min(map.max_point().row());
|
||||
let new_row = if bottom_row_capped.saturating_sub(times as u32) < first_visible_line.row() {
|
||||
first_visible_line.row()
|
||||
} else {
|
||||
|
||||
@@ -950,4 +950,16 @@ async fn test_remap(cx: &mut gpui::TestAppContext) {
|
||||
cx.set_state("ˇ1234\n56789", Mode::Normal);
|
||||
cx.simulate_keystrokes(["g", "u"]);
|
||||
cx.assert_state("1234 567ˇ89", Mode::Normal);
|
||||
|
||||
// test leaving command
|
||||
cx.update(|cx| {
|
||||
cx.bind_keys([KeyBinding::new(
|
||||
"g t",
|
||||
workspace::SendKeystrokes("i space escape".to_string()),
|
||||
None,
|
||||
)])
|
||||
});
|
||||
cx.set_state("12ˇ34", Mode::Normal);
|
||||
cx.simulate_keystrokes(["g", "t"]);
|
||||
cx.assert_state("12ˇ 34", Mode::Normal);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
description = "The fast, collaborative code editor."
|
||||
edition = "2021"
|
||||
name = "zed"
|
||||
version = "0.124.0"
|
||||
version = "0.124.4"
|
||||
publish = false
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
dev
|
||||
preview
|
||||
@@ -71,6 +71,7 @@ impl LspAdapter for AstroLspAdapter {
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
@@ -122,6 +123,7 @@ async fn get_cached_server_binary(
|
||||
if server_path.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -84,6 +84,7 @@ impl super::LspAdapter for CLspAdapter {
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: binary_path,
|
||||
env: None,
|
||||
arguments: vec![],
|
||||
})
|
||||
}
|
||||
@@ -260,6 +261,7 @@ async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServ
|
||||
if clangd_bin.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: clangd_bin,
|
||||
env: None,
|
||||
arguments: vec![],
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -105,6 +105,7 @@ impl super::LspAdapter for ClojureLspAdapter {
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: binary_path,
|
||||
env: None,
|
||||
arguments: vec![],
|
||||
})
|
||||
}
|
||||
@@ -118,6 +119,7 @@ impl super::LspAdapter for ClojureLspAdapter {
|
||||
if binary_path.exists() {
|
||||
Some(LanguageServerBinary {
|
||||
path: binary_path,
|
||||
env: None,
|
||||
arguments: vec![],
|
||||
})
|
||||
} else {
|
||||
@@ -133,6 +135,7 @@ impl super::LspAdapter for ClojureLspAdapter {
|
||||
if binary_path.exists() {
|
||||
Some(LanguageServerBinary {
|
||||
path: binary_path,
|
||||
env: None,
|
||||
arguments: vec!["--version".into()],
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -92,6 +92,7 @@ impl super::LspAdapter for OmniSharpAdapter {
|
||||
}
|
||||
Ok(LanguageServerBinary {
|
||||
path: binary_path,
|
||||
env: None,
|
||||
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 {
|
||||
Ok(LanguageServerBinary {
|
||||
path,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(),
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -72,6 +72,7 @@ impl LspAdapter for CssLspAdapter {
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
@@ -116,6 +117,7 @@ async fn get_cached_server_binary(
|
||||
if server_path.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -39,6 +39,7 @@ impl LspAdapter for DartLanguageServer {
|
||||
) -> Option<LanguageServerBinary> {
|
||||
Some(LanguageServerBinary {
|
||||
path: "dart".into(),
|
||||
env: None,
|
||||
arguments: vec!["language-server".into(), "--protocol=lsp".into()],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -134,6 +134,7 @@ impl LspAdapter for DenoLspAdapter {
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: binary_path,
|
||||
env: None,
|
||||
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() {
|
||||
return Ok(LanguageServerBinary {
|
||||
path: binary,
|
||||
env: None,
|
||||
arguments: deno_server_binary_arguments(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ impl LspAdapter for DockerfileLspAdapter {
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
@@ -110,6 +111,7 @@ async fn get_cached_server_binary(
|
||||
if server_path.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -174,6 +174,7 @@ impl LspAdapter for ElixirLspAdapter {
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: binary_path,
|
||||
env: None,
|
||||
arguments: vec![],
|
||||
})
|
||||
}
|
||||
@@ -284,6 +285,7 @@ async fn get_cached_server_binary_elixir_ls(
|
||||
if server_path.exists() {
|
||||
Some(LanguageServerBinary {
|
||||
path: server_path,
|
||||
env: None,
|
||||
arguments: vec![],
|
||||
})
|
||||
} else {
|
||||
@@ -369,6 +371,7 @@ impl LspAdapter for NextLspAdapter {
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: binary_path,
|
||||
env: None,
|
||||
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 {
|
||||
Ok(LanguageServerBinary {
|
||||
path,
|
||||
env: None,
|
||||
arguments: Vec::new(),
|
||||
})
|
||||
} else {
|
||||
@@ -476,6 +480,7 @@ impl LspAdapter for LocalLspAdapter {
|
||||
let path = shellexpand::full(&self.path)?;
|
||||
Ok(LanguageServerBinary {
|
||||
path: PathBuf::from(path.deref()),
|
||||
env: None,
|
||||
arguments: self.arguments.iter().map(|arg| arg.into()).collect(),
|
||||
})
|
||||
}
|
||||
@@ -488,6 +493,7 @@ impl LspAdapter for LocalLspAdapter {
|
||||
let path = shellexpand::full(&self.path).ok()?;
|
||||
Some(LanguageServerBinary {
|
||||
path: PathBuf::from(path.deref()),
|
||||
env: None,
|
||||
arguments: self.arguments.iter().map(|arg| arg.into()).collect(),
|
||||
})
|
||||
}
|
||||
@@ -496,6 +502,7 @@ impl LspAdapter for LocalLspAdapter {
|
||||
let path = shellexpand::full(&self.path).ok()?;
|
||||
Some(LanguageServerBinary {
|
||||
path: PathBuf::from(path.deref()),
|
||||
env: None,
|
||||
arguments: self.arguments.iter().map(|arg| arg.into()).collect(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ impl LspAdapter for ElmLspAdapter {
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
@@ -134,6 +135,7 @@ async fn get_cached_server_binary(
|
||||
if server_path.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -41,6 +41,7 @@ impl LspAdapter for ErlangLspAdapter {
|
||||
) -> Option<LanguageServerBinary> {
|
||||
Some(LanguageServerBinary {
|
||||
path: "erlang_ls".into(),
|
||||
env: None,
|
||||
arguments: vec![],
|
||||
})
|
||||
}
|
||||
@@ -52,6 +53,7 @@ impl LspAdapter for ErlangLspAdapter {
|
||||
async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
|
||||
Some(LanguageServerBinary {
|
||||
path: "erlang_ls".into(),
|
||||
env: None,
|
||||
arguments: vec!["--version".into()],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ impl LspAdapter for GleamLspAdapter {
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: binary_path,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(),
|
||||
})
|
||||
}
|
||||
@@ -116,6 +117,7 @@ async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServ
|
||||
|
||||
anyhow::Ok(LanguageServerBinary {
|
||||
path: last.ok_or_else(|| anyhow!("no cached binary"))?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(),
|
||||
})
|
||||
})
|
||||
|
||||
@@ -58,6 +58,25 @@ impl super::LspAdapter for GoLspAdapter {
|
||||
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(
|
||||
&self,
|
||||
delegate: &Arc<dyn LspAdapterDelegate>,
|
||||
@@ -107,6 +126,7 @@ impl super::LspAdapter for GoLspAdapter {
|
||||
return Ok(LanguageServerBinary {
|
||||
path: binary_path.to_path_buf(),
|
||||
arguments: server_binary_arguments(),
|
||||
env: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -154,6 +174,7 @@ impl super::LspAdapter for GoLspAdapter {
|
||||
Ok(LanguageServerBinary {
|
||||
path: binary_path.to_path_buf(),
|
||||
arguments: server_binary_arguments(),
|
||||
env: None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -372,6 +393,7 @@ async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServ
|
||||
Ok(LanguageServerBinary {
|
||||
path,
|
||||
arguments: server_binary_arguments(),
|
||||
env: None,
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("no cached binary"))
|
||||
|
||||
@@ -41,6 +41,7 @@ impl LspAdapter for HaskellLanguageServer {
|
||||
) -> Option<LanguageServerBinary> {
|
||||
Some(LanguageServerBinary {
|
||||
path: "haskell-language-server-wrapper".into(),
|
||||
env: None,
|
||||
arguments: vec!["lsp".into()],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ impl LspAdapter for HtmlLspAdapter {
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
@@ -116,6 +117,7 @@ async fn get_cached_server_binary(
|
||||
if server_path.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -122,6 +122,7 @@ impl LspAdapter for JsonLspAdapter {
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
@@ -177,6 +178,7 @@ async fn get_cached_server_binary(
|
||||
if server_path.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -94,6 +94,7 @@ impl super::LspAdapter for LuaLspAdapter {
|
||||
}
|
||||
Ok(LanguageServerBinary {
|
||||
path: binary_path,
|
||||
env: None,
|
||||
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 {
|
||||
Ok(LanguageServerBinary {
|
||||
path,
|
||||
env: None,
|
||||
arguments: Vec::new(),
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -41,6 +41,7 @@ impl LspAdapter for NuLanguageServer {
|
||||
) -> Option<LanguageServerBinary> {
|
||||
Some(LanguageServerBinary {
|
||||
path: "nu".into(),
|
||||
env: None,
|
||||
arguments: vec!["--lsp".into()],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ impl LspAdapter for OCamlLspAdapter {
|
||||
) -> Option<LanguageServerBinary> {
|
||||
Some(LanguageServerBinary {
|
||||
path: "ocamllsp".into(),
|
||||
env: None,
|
||||
arguments: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ impl LspAdapter for IntelephenseLspAdapter {
|
||||
}
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: intelephense_server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
@@ -126,6 +127,7 @@ async fn get_cached_server_binary(
|
||||
if server_path.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: intelephense_server_binary_arguments(&server_path),
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -70,6 +70,7 @@ impl LspAdapter for PrismaLspAdapter {
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
@@ -112,6 +113,7 @@ async fn get_cached_server_binary(
|
||||
if server_path.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -74,6 +74,7 @@ impl LspAdapter for PurescriptLspAdapter {
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
@@ -127,6 +128,7 @@ async fn get_cached_server_binary(
|
||||
if server_path.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -62,6 +62,7 @@ impl LspAdapter for PythonLspAdapter {
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
@@ -167,6 +168,7 @@ async fn get_cached_server_binary(
|
||||
if server_path.exists() {
|
||||
Some(LanguageServerBinary {
|
||||
path: node.binary_path().await.log_err()?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -39,6 +39,7 @@ impl LspAdapter for RubyLanguageServer {
|
||||
) -> Option<LanguageServerBinary> {
|
||||
Some(LanguageServerBinary {
|
||||
path: "solargraph".into(),
|
||||
env: None,
|
||||
arguments: vec!["stdio".into()],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ impl LspAdapter for RustLspAdapter {
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: destination_path,
|
||||
env: None,
|
||||
arguments: Default::default(),
|
||||
})
|
||||
}
|
||||
@@ -296,6 +297,7 @@ async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServ
|
||||
|
||||
anyhow::Ok(LanguageServerBinary {
|
||||
path: last.ok_or_else(|| anyhow!("no cached binary"))?,
|
||||
env: None,
|
||||
arguments: Default::default(),
|
||||
})
|
||||
})
|
||||
|
||||
@@ -71,6 +71,7 @@ impl LspAdapter for SvelteLspAdapter {
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
@@ -148,6 +149,7 @@ async fn get_cached_server_binary(
|
||||
if server_path.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -73,6 +73,7 @@ impl LspAdapter for TailwindLspAdapter {
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
@@ -150,6 +151,7 @@ async fn get_cached_server_binary(
|
||||
if server_path.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -85,6 +85,7 @@ impl LspAdapter for TaploLspAdapter {
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: binary_path,
|
||||
env: None,
|
||||
arguments: vec!["lsp".into(), "stdio".into()],
|
||||
})
|
||||
}
|
||||
@@ -120,6 +121,7 @@ async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServ
|
||||
|
||||
anyhow::Ok(LanguageServerBinary {
|
||||
path: last.context("no cached binary")?,
|
||||
env: None,
|
||||
arguments: Default::default(),
|
||||
})
|
||||
})
|
||||
|
||||
@@ -97,6 +97,7 @@ impl LspAdapter for TypeScriptLspAdapter {
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: typescript_server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
@@ -192,11 +193,13 @@ async fn get_cached_ts_server_binary(
|
||||
if new_server_path.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: typescript_server_binary_arguments(&new_server_path),
|
||||
})
|
||||
} else if old_server_path.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: typescript_server_binary_arguments(&old_server_path),
|
||||
})
|
||||
} else {
|
||||
@@ -259,7 +262,7 @@ impl LspAdapter for EsLintLspAdapter {
|
||||
let release = latest_github_release(
|
||||
"microsoft/vscode-eslint",
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
delegate.http_client(),
|
||||
)
|
||||
.await?;
|
||||
@@ -307,6 +310,7 @@ impl LspAdapter for EsLintLspAdapter {
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: eslint_server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
@@ -354,6 +358,7 @@ async fn get_cached_eslint_server_binary(
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: eslint_server_binary_arguments(&server_path),
|
||||
})
|
||||
})
|
||||
|
||||
@@ -41,6 +41,7 @@ impl LspAdapter for UiuaLanguageServer {
|
||||
) -> Option<LanguageServerBinary> {
|
||||
Some(LanguageServerBinary {
|
||||
path: "uiua".into(),
|
||||
env: None,
|
||||
arguments: vec!["lsp".into()],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -118,6 +118,7 @@ impl super::LspAdapter for VueLspAdapter {
|
||||
*self.typescript_install_path.lock() = Some(ts_path);
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: vue_server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
@@ -204,6 +205,7 @@ async fn get_cached_server_binary(
|
||||
Ok((
|
||||
LanguageServerBinary {
|
||||
path: node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: vue_server_binary_arguments(&server_path),
|
||||
},
|
||||
typescript_path,
|
||||
|
||||
@@ -74,6 +74,7 @@ impl LspAdapter for YamlLspAdapter {
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
@@ -124,6 +125,7 @@ async fn get_cached_server_binary(
|
||||
if server_path.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: node.binary_path().await?,
|
||||
env: None,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -3,10 +3,13 @@ use async_compression::futures::bufread::GzipDecoder;
|
||||
use async_tar::Archive;
|
||||
use async_trait::async_trait;
|
||||
use futures::{io::BufReader, StreamExt};
|
||||
use gpui::{AsyncAppContext, Task};
|
||||
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||
use lsp::LanguageServerBinary;
|
||||
use smol::fs;
|
||||
use std::env::consts::{ARCH, OS};
|
||||
use std::ffi::OsString;
|
||||
use std::sync::Arc;
|
||||
use std::{any::Any, path::PathBuf};
|
||||
use util::async_maybe;
|
||||
use util::github::latest_github_release;
|
||||
@@ -44,6 +47,25 @@ impl LspAdapter for ZlsAdapter {
|
||||
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(
|
||||
&self,
|
||||
version: Box<dyn 'static + Send + Any>,
|
||||
@@ -75,6 +97,7 @@ impl LspAdapter for ZlsAdapter {
|
||||
}
|
||||
Ok(LanguageServerBinary {
|
||||
path: binary_path,
|
||||
env: None,
|
||||
arguments: vec![],
|
||||
})
|
||||
}
|
||||
@@ -119,6 +142,7 @@ async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServ
|
||||
if let Some(path) = last_binary_path {
|
||||
Ok(LanguageServerBinary {
|
||||
path,
|
||||
env: None,
|
||||
arguments: Vec::new(),
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -824,8 +824,29 @@ async fn load_login_shell_environment() -> Result<()> {
|
||||
let shell = env::var("SHELL").context(
|
||||
"SHELL environment variable is not assigned so we can't source login environment variables",
|
||||
)?;
|
||||
|
||||
// If possible, we want to `cd` in the user's `$HOME` to trigger programs
|
||||
// such as direnv, asdf, mise, ... to adjust the PATH. These tools often hook
|
||||
// into shell's `cd` command (and hooks) to manipulate env.
|
||||
// We do this so that we get the env a user would have when spawning a shell
|
||||
// in home directory.
|
||||
let shell_cmd_prefix = std::env::var_os("HOME")
|
||||
.and_then(|home| home.into_string().ok())
|
||||
.map(|home| format!("cd {home};"));
|
||||
|
||||
// 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.
|
||||
let shell_cmd = format!(
|
||||
"{}echo {marker}; /usr/bin/env -0; exit 0;",
|
||||
shell_cmd_prefix.as_deref().unwrap_or("")
|
||||
);
|
||||
|
||||
let output = Command::new(&shell)
|
||||
.args(["-l", "-i", "-c", &format!("echo {marker}; /usr/bin/env -0")])
|
||||
.args(["-l", "-i", "-c", &shell_cmd])
|
||||
.output()
|
||||
.await
|
||||
.context("failed to spawn login shell to source login environment variables")?;
|
||||
|
||||
Reference in New Issue
Block a user