lsp: Remove reinstall, update config (#18318)
Release Notes: - Fixed overriding the path of a language server binary for all language servers. `{"lsp":{"<lsp-name>":{"binary":{"path": "_"}}}}` will now work for all language servers including those defined by extensions. - (breaking change) To disable finding lsp adapters in your path, you must now specify `{"lsp":{"<lsp-name>":{"binary":{"ignore_system_version": true}}}}`. Previously this was `{"lsp":{"<lsp-name>":{"binary":{"path_lookup": false}}}}`. Note that this setting still does not apply to extensions. - Removed automatic reinstallation of language servers. (It mostly didn't work) --------- Co-authored-by: Mikayla <mikayla@zed.dev>
This commit is contained in:
parent
1f54fde4d2
commit
dc48af0ca1
21 changed files with 405 additions and 940 deletions
|
@ -783,7 +783,7 @@
|
|||
/// or to ensure Zed always downloads and installs an isolated version of node:
|
||||
/// {
|
||||
/// "node": {
|
||||
/// "disable_path_lookup": true
|
||||
/// "ignore_system_version": true,
|
||||
/// }
|
||||
/// NOTE: changing this setting currently requires restarting Zed.
|
||||
"node": {},
|
||||
|
|
|
@ -299,7 +299,7 @@ impl ActivityIndicator {
|
|||
.into_any_element(),
|
||||
),
|
||||
message: format!(
|
||||
"Failed to download {}. Click to show error.",
|
||||
"Failed to run {}. Click to show error.",
|
||||
failed
|
||||
.iter()
|
||||
.map(|name| name.0.as_ref())
|
||||
|
|
|
@ -10,16 +10,11 @@ use gpui::AsyncAppContext;
|
|||
use language::{
|
||||
CodeLabel, HighlightId, Language, LanguageServerName, LspAdapter, LspAdapterDelegate,
|
||||
};
|
||||
use lsp::{CodeActionKind, LanguageServerBinary};
|
||||
use lsp::{CodeActionKind, LanguageServerBinary, LanguageServerBinaryOptions};
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
use std::ops::Range;
|
||||
use std::{
|
||||
any::Any,
|
||||
path::{Path, PathBuf},
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
};
|
||||
use std::{any::Any, path::PathBuf, pin::Pin, sync::Arc};
|
||||
use util::{maybe, ResultExt};
|
||||
use wasmtime_wasi::WasiView as _;
|
||||
|
||||
|
@ -38,8 +33,8 @@ impl LspAdapter for ExtensionLspAdapter {
|
|||
|
||||
fn get_language_server_command<'a>(
|
||||
self: Arc<Self>,
|
||||
_: Option<Arc<Path>>,
|
||||
delegate: Arc<dyn LspAdapterDelegate>,
|
||||
_: LanguageServerBinaryOptions,
|
||||
_: futures::lock::MutexGuard<'a, Option<LanguageServerBinary>>,
|
||||
_: &'a mut AsyncAppContext,
|
||||
) -> Pin<Box<dyn 'a + Future<Output = Result<LanguageServerBinary>>>> {
|
||||
|
@ -124,10 +119,6 @@ impl LspAdapter for ExtensionLspAdapter {
|
|||
unreachable!("get_language_server_command is overridden")
|
||||
}
|
||||
|
||||
async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
|
||||
None
|
||||
}
|
||||
|
||||
fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
|
||||
let code_action_kinds = self
|
||||
.extension
|
||||
|
|
|
@ -29,7 +29,7 @@ use gpui::{AppContext, AsyncAppContext, Model, SharedString, Task};
|
|||
pub use highlight_map::HighlightMap;
|
||||
use http_client::HttpClient;
|
||||
pub use language_registry::LanguageName;
|
||||
use lsp::{CodeActionKind, LanguageServerBinary};
|
||||
use lsp::{CodeActionKind, LanguageServerBinary, LanguageServerBinaryOptions};
|
||||
use parking_lot::Mutex;
|
||||
use regex::Regex;
|
||||
use schemars::{
|
||||
|
@ -69,7 +69,7 @@ pub use buffer::*;
|
|||
pub use diagnostic_set::DiagnosticEntry;
|
||||
pub use language_registry::{
|
||||
AvailableLanguage, LanguageNotFound, LanguageQueries, LanguageRegistry,
|
||||
LanguageServerBinaryStatus, PendingLanguageServer, QUERY_FILENAME_PREFIXES,
|
||||
LanguageServerBinaryStatus, QUERY_FILENAME_PREFIXES,
|
||||
};
|
||||
pub use lsp::LanguageServerId;
|
||||
pub use outline::*;
|
||||
|
@ -249,28 +249,17 @@ impl CachedLspAdapter {
|
|||
|
||||
pub async fn get_language_server_command(
|
||||
self: Arc<Self>,
|
||||
container_dir: Option<Arc<Path>>,
|
||||
delegate: Arc<dyn LspAdapterDelegate>,
|
||||
binary_options: LanguageServerBinaryOptions,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let cached_binary = self.cached_binary.lock().await;
|
||||
self.adapter
|
||||
.clone()
|
||||
.get_language_server_command(container_dir, delegate, cached_binary, cx)
|
||||
.get_language_server_command(delegate, binary_options, cached_binary, cx)
|
||||
.await
|
||||
}
|
||||
|
||||
pub fn can_be_reinstalled(&self) -> bool {
|
||||
self.adapter.can_be_reinstalled()
|
||||
}
|
||||
|
||||
pub async fn installation_test_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
self.adapter.installation_test_binary(container_dir).await
|
||||
}
|
||||
|
||||
pub fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
|
||||
self.adapter.code_action_kinds()
|
||||
}
|
||||
|
@ -322,6 +311,7 @@ pub trait LspAdapterDelegate: Send + Sync {
|
|||
fn worktree_id(&self) -> WorktreeId;
|
||||
fn worktree_root_path(&self) -> &Path;
|
||||
fn update_status(&self, language: LanguageServerName, status: LanguageServerBinaryStatus);
|
||||
async fn language_server_download_dir(&self, name: &LanguageServerName) -> Option<Arc<Path>>;
|
||||
|
||||
async fn which(&self, command: &OsStr) -> Option<PathBuf>;
|
||||
async fn shell_env(&self) -> HashMap<String, String>;
|
||||
|
@ -335,8 +325,8 @@ pub trait LspAdapter: 'static + Send + Sync {
|
|||
|
||||
fn get_language_server_command<'a>(
|
||||
self: Arc<Self>,
|
||||
container_dir: Option<Arc<Path>>,
|
||||
delegate: Arc<dyn LspAdapterDelegate>,
|
||||
binary_options: LanguageServerBinaryOptions,
|
||||
mut cached_binary: futures::lock::MutexGuard<'a, Option<LanguageServerBinary>>,
|
||||
cx: &'a mut AsyncAppContext,
|
||||
) -> Pin<Box<dyn 'a + Future<Output = Result<LanguageServerBinary>>>> {
|
||||
|
@ -352,30 +342,30 @@ pub trait LspAdapter: 'static + Send + Sync {
|
|||
// 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.
|
||||
if let Some(binary) = self.check_if_user_installed(delegate.as_ref(), cx).await {
|
||||
log::info!(
|
||||
"found user-installed language server for {}. path: {:?}, arguments: {:?}",
|
||||
self.name().0,
|
||||
binary.path,
|
||||
binary.arguments
|
||||
);
|
||||
return Ok(binary);
|
||||
if binary_options.allow_path_lookup {
|
||||
if let Some(binary) = self.check_if_user_installed(delegate.as_ref(), cx).await {
|
||||
log::info!(
|
||||
"found user-installed language server for {}. path: {:?}, arguments: {:?}",
|
||||
self.name().0,
|
||||
binary.path,
|
||||
binary.arguments
|
||||
);
|
||||
return Ok(binary);
|
||||
}
|
||||
}
|
||||
|
||||
if !binary_options.allow_binary_download {
|
||||
return Err(anyhow!("downloading language servers disabled"));
|
||||
}
|
||||
|
||||
if let Some(cached_binary) = cached_binary.as_ref() {
|
||||
return Ok(cached_binary.clone());
|
||||
}
|
||||
|
||||
let Some(container_dir) = container_dir else {
|
||||
let Some(container_dir) = delegate.language_server_download_dir(&self.name()).await else {
|
||||
anyhow::bail!("cannot download language servers for remotes (yet)")
|
||||
};
|
||||
|
||||
if !container_dir.exists() {
|
||||
smol::fs::create_dir_all(&container_dir)
|
||||
.await
|
||||
.context("failed to create container directory")?;
|
||||
}
|
||||
|
||||
let mut binary = try_fetch_server_binary(self.as_ref(), &delegate, container_dir.to_path_buf(), cx).await;
|
||||
|
||||
if let Err(error) = binary.as_ref() {
|
||||
|
@ -443,21 +433,6 @@ pub trait LspAdapter: 'static + Send + Sync {
|
|||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary>;
|
||||
|
||||
/// Returns `true` if a language server can be reinstalled.
|
||||
///
|
||||
/// If language server initialization fails, a reinstallation will be attempted unless the value returned from this method is `false`.
|
||||
///
|
||||
/// Implementations that rely on software already installed on user's system
|
||||
/// should have [`can_be_reinstalled`](Self::can_be_reinstalled) return `false`.
|
||||
fn can_be_reinstalled(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
async fn installation_test_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary>;
|
||||
|
||||
fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
|
||||
|
||||
/// Post-processes completions provided by the language server.
|
||||
|
@ -1711,8 +1686,8 @@ impl LspAdapter for FakeLspAdapter {
|
|||
|
||||
fn get_language_server_command<'a>(
|
||||
self: Arc<Self>,
|
||||
_: Option<Arc<Path>>,
|
||||
_: Arc<dyn LspAdapterDelegate>,
|
||||
_: LanguageServerBinaryOptions,
|
||||
_: futures::lock::MutexGuard<'a, Option<LanguageServerBinary>>,
|
||||
_: &'a mut AsyncAppContext,
|
||||
) -> Pin<Box<dyn 'a + Future<Output = Result<LanguageServerBinary>>>> {
|
||||
|
@ -1743,10 +1718,6 @@ impl LspAdapter for FakeLspAdapter {
|
|||
unreachable!();
|
||||
}
|
||||
|
||||
async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
|
||||
|
||||
fn disk_based_diagnostic_sources(&self) -> Vec<String> {
|
||||
|
|
|
@ -4,18 +4,17 @@ use crate::{
|
|||
},
|
||||
task_context::ContextProvider,
|
||||
with_parser, CachedLspAdapter, File, Language, LanguageConfig, LanguageId, LanguageMatcher,
|
||||
LanguageServerName, LspAdapter, LspAdapterDelegate, PLAIN_TEXT,
|
||||
LanguageServerName, LspAdapter, PLAIN_TEXT,
|
||||
};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use collections::{hash_map, HashMap, HashSet};
|
||||
|
||||
use futures::{
|
||||
channel::{mpsc, oneshot},
|
||||
future::Shared,
|
||||
Future,
|
||||
};
|
||||
use globset::GlobSet;
|
||||
use gpui::{AppContext, BackgroundExecutor, Task};
|
||||
use gpui::{AppContext, BackgroundExecutor};
|
||||
use lsp::LanguageServerId;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use postage::watch;
|
||||
|
@ -118,12 +117,6 @@ pub enum LanguageServerBinaryStatus {
|
|||
Failed { error: String },
|
||||
}
|
||||
|
||||
pub struct PendingLanguageServer {
|
||||
pub server_id: LanguageServerId,
|
||||
pub task: Task<Result<(lsp::LanguageServer, Option<serde_json::Value>)>>,
|
||||
pub container_dir: Option<Arc<Path>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AvailableLanguage {
|
||||
id: LanguageId,
|
||||
|
@ -882,123 +875,53 @@ impl LanguageRegistry {
|
|||
self.lsp_binary_status_tx.send(server_name, status);
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn create_pending_language_server(
|
||||
self: &Arc<Self>,
|
||||
stderr_capture: Arc<Mutex<Option<String>>>,
|
||||
_language_name_for_tests: LanguageName,
|
||||
adapter: Arc<CachedLspAdapter>,
|
||||
root_path: Arc<Path>,
|
||||
delegate: Arc<dyn LspAdapterDelegate>,
|
||||
project_environment: Shared<Task<Option<HashMap<String, String>>>>,
|
||||
cx: &mut AppContext,
|
||||
) -> Option<PendingLanguageServer> {
|
||||
let server_id = self.state.write().next_language_server_id();
|
||||
log::info!(
|
||||
"attempting to start language server {:?}, path: {root_path:?}, id: {server_id}",
|
||||
adapter.name.0
|
||||
);
|
||||
pub fn next_language_server_id(&self) -> LanguageServerId {
|
||||
self.state.write().next_language_server_id()
|
||||
}
|
||||
|
||||
let container_dir: Option<Arc<Path>> = self
|
||||
.language_server_download_dir
|
||||
pub fn language_server_download_dir(&self, name: &LanguageServerName) -> Option<Arc<Path>> {
|
||||
self.language_server_download_dir
|
||||
.as_ref()
|
||||
.map(|dir| Arc::from(dir.join(adapter.name.0.as_ref())));
|
||||
let root_path = root_path.clone();
|
||||
let this = Arc::downgrade(self);
|
||||
.map(|dir| Arc::from(dir.join(name.0.as_ref())))
|
||||
}
|
||||
|
||||
let task = cx.spawn({
|
||||
let container_dir = container_dir.clone();
|
||||
move |mut cx| async move {
|
||||
let project_environment = project_environment.await;
|
||||
|
||||
let binary_result = adapter
|
||||
.clone()
|
||||
.get_language_server_command(container_dir, delegate.clone(), &mut cx)
|
||||
.await;
|
||||
|
||||
delegate.update_status(adapter.name.clone(), LanguageServerBinaryStatus::None);
|
||||
|
||||
let mut binary = binary_result?;
|
||||
|
||||
// If we do have a project environment (either by spawning a shell in in the project directory
|
||||
// or by getting it from the CLI) and the language server command itself
|
||||
// doesn't have an environment (which it would have, if it was found in $PATH), then
|
||||
// we use the project environment.
|
||||
if binary.env.is_none() && project_environment.is_some() {
|
||||
log::info!(
|
||||
"using project environment for language server {:?}, id: {server_id}",
|
||||
adapter.name.0
|
||||
);
|
||||
binary.env = project_environment.clone();
|
||||
}
|
||||
|
||||
let options = adapter
|
||||
.adapter
|
||||
.clone()
|
||||
.initialization_options(&delegate)
|
||||
.await?;
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
if true {
|
||||
if let Some(this) = this.upgrade() {
|
||||
if let Some(fake_entry) = this
|
||||
.state
|
||||
.write()
|
||||
.fake_server_entries
|
||||
.get_mut(&adapter.name)
|
||||
{
|
||||
let (server, mut fake_server) = lsp::FakeLanguageServer::new(
|
||||
server_id,
|
||||
binary,
|
||||
adapter.name.0.to_string(),
|
||||
fake_entry.capabilities.clone(),
|
||||
cx.clone(),
|
||||
);
|
||||
fake_entry._server = Some(fake_server.clone());
|
||||
|
||||
if let Some(initializer) = &fake_entry.initializer {
|
||||
initializer(&mut fake_server);
|
||||
}
|
||||
|
||||
let tx = fake_entry.tx.clone();
|
||||
cx.background_executor()
|
||||
.spawn(async move {
|
||||
if fake_server
|
||||
.try_receive_notification::<lsp::notification::Initialized>(
|
||||
)
|
||||
.await
|
||||
.is_some()
|
||||
{
|
||||
tx.unbounded_send(fake_server.clone()).ok();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
return Ok((server, options));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drop(this);
|
||||
Ok((
|
||||
lsp::LanguageServer::new(
|
||||
stderr_capture,
|
||||
server_id,
|
||||
binary,
|
||||
&root_path,
|
||||
adapter.code_action_kinds(),
|
||||
cx,
|
||||
)?,
|
||||
options,
|
||||
))
|
||||
}
|
||||
});
|
||||
|
||||
Some(PendingLanguageServer {
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn create_fake_language_server(
|
||||
&self,
|
||||
server_id: LanguageServerId,
|
||||
name: &LanguageServerName,
|
||||
binary: lsp::LanguageServerBinary,
|
||||
cx: gpui::AsyncAppContext,
|
||||
) -> Option<lsp::LanguageServer> {
|
||||
let mut state = self.state.write();
|
||||
let fake_entry = state.fake_server_entries.get_mut(&name)?;
|
||||
let (server, mut fake_server) = lsp::FakeLanguageServer::new(
|
||||
server_id,
|
||||
task,
|
||||
container_dir,
|
||||
})
|
||||
binary,
|
||||
name.0.to_string(),
|
||||
fake_entry.capabilities.clone(),
|
||||
cx.clone(),
|
||||
);
|
||||
fake_entry._server = Some(fake_server.clone());
|
||||
|
||||
if let Some(initializer) = &fake_entry.initializer {
|
||||
initializer(&mut fake_server);
|
||||
}
|
||||
|
||||
let tx = fake_entry.tx.clone();
|
||||
cx.background_executor()
|
||||
.spawn(async move {
|
||||
if fake_server
|
||||
.try_receive_notification::<lsp::notification::Initialized>()
|
||||
.await
|
||||
.is_some()
|
||||
{
|
||||
tx.unbounded_send(fake_server.clone()).ok();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
Some(server)
|
||||
}
|
||||
|
||||
pub fn language_server_binary_statuses(
|
||||
|
@ -1007,29 +930,16 @@ impl LanguageRegistry {
|
|||
self.lsp_binary_status_tx.subscribe()
|
||||
}
|
||||
|
||||
pub fn delete_server_container(
|
||||
&self,
|
||||
adapter: Arc<CachedLspAdapter>,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<()> {
|
||||
pub async fn delete_server_container(&self, name: LanguageServerName) {
|
||||
log::info!("deleting server container");
|
||||
let Some(dir) = self.language_server_download_dir(&name) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let download_dir = self
|
||||
.language_server_download_dir
|
||||
.clone()
|
||||
.expect("language server download directory has not been assigned before deleting server container");
|
||||
|
||||
cx.spawn(|_| async move {
|
||||
let container_dir = download_dir.join(adapter.name.0.as_ref());
|
||||
smol::fs::remove_dir_all(container_dir)
|
||||
.await
|
||||
.context("server container removal")
|
||||
.log_err();
|
||||
})
|
||||
}
|
||||
|
||||
pub fn next_language_server_id(&self) -> LanguageServerId {
|
||||
self.state.write().next_language_server_id()
|
||||
smol::fs::remove_dir_all(dir)
|
||||
.await
|
||||
.context("server container removal")
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ use gpui::AsyncAppContext;
|
|||
use http_client::github::{latest_github_release, GitHubLspBinaryVersion};
|
||||
pub use language::*;
|
||||
use lsp::LanguageServerBinary;
|
||||
use project::{lsp_store::language_server_settings, project_settings::BinarySettings};
|
||||
use smol::fs::{self, File};
|
||||
use std::{any::Any, env::consts, path::PathBuf, sync::Arc};
|
||||
use util::{fs::remove_matching, maybe, ResultExt};
|
||||
|
@ -25,41 +24,14 @@ impl super::LspAdapter for CLspAdapter {
|
|||
async fn check_if_user_installed(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
cx: &AsyncAppContext,
|
||||
_: &AsyncAppContext,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let configured_binary = cx.update(|cx| {
|
||||
language_server_settings(delegate, &Self::SERVER_NAME, cx)
|
||||
.and_then(|s| s.binary.clone())
|
||||
});
|
||||
|
||||
match configured_binary {
|
||||
Ok(Some(BinarySettings {
|
||||
path: Some(path),
|
||||
arguments,
|
||||
..
|
||||
})) => Some(LanguageServerBinary {
|
||||
path: path.into(),
|
||||
arguments: arguments
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.map(|arg| arg.into())
|
||||
.collect(),
|
||||
env: None,
|
||||
}),
|
||||
Ok(Some(BinarySettings {
|
||||
path_lookup: Some(false),
|
||||
..
|
||||
})) => None,
|
||||
_ => {
|
||||
let env = delegate.shell_env().await;
|
||||
let path = delegate.which(Self::SERVER_NAME.as_ref()).await?;
|
||||
Some(LanguageServerBinary {
|
||||
path,
|
||||
arguments: vec![],
|
||||
env: Some(env),
|
||||
})
|
||||
}
|
||||
}
|
||||
let path = delegate.which(Self::SERVER_NAME.as_ref()).await?;
|
||||
Some(LanguageServerBinary {
|
||||
path,
|
||||
arguments: vec![],
|
||||
env: None,
|
||||
})
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
|
@ -141,18 +113,6 @@ impl super::LspAdapter for CLspAdapter {
|
|||
get_cached_server_binary(container_dir).await
|
||||
}
|
||||
|
||||
async fn installation_test_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir)
|
||||
.await
|
||||
.map(|mut binary| {
|
||||
binary.arguments = vec!["--help".into()];
|
||||
binary
|
||||
})
|
||||
}
|
||||
|
||||
async fn label_for_completion(
|
||||
&self,
|
||||
completion: &lsp::CompletionItem,
|
||||
|
|
|
@ -84,13 +84,6 @@ impl LspAdapter for CssLspAdapter {
|
|||
get_cached_server_binary(container_dir, &self.node).await
|
||||
}
|
||||
|
||||
async fn installation_test_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir, &self.node).await
|
||||
}
|
||||
|
||||
async fn initialization_options(
|
||||
self: Arc<Self>,
|
||||
_: &Arc<dyn LspAdapterDelegate>,
|
||||
|
|
|
@ -6,7 +6,6 @@ use gpui::{AppContext, AsyncAppContext, Task};
|
|||
use http_client::github::latest_github_release;
|
||||
pub use language::*;
|
||||
use lsp::LanguageServerBinary;
|
||||
use project::{lsp_store::language_server_settings, project_settings::BinarySettings};
|
||||
use regex::Regex;
|
||||
use serde_json::json;
|
||||
use smol::{fs, process};
|
||||
|
@ -68,41 +67,14 @@ impl super::LspAdapter for GoLspAdapter {
|
|||
async fn check_if_user_installed(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
cx: &AsyncAppContext,
|
||||
_: &AsyncAppContext,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let configured_binary = cx.update(|cx| {
|
||||
language_server_settings(delegate, &Self::SERVER_NAME, cx)
|
||||
.and_then(|s| s.binary.clone())
|
||||
});
|
||||
|
||||
match configured_binary {
|
||||
Ok(Some(BinarySettings {
|
||||
path: Some(path),
|
||||
arguments,
|
||||
..
|
||||
})) => Some(LanguageServerBinary {
|
||||
path: path.into(),
|
||||
arguments: arguments
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.map(|arg| arg.into())
|
||||
.collect(),
|
||||
env: None,
|
||||
}),
|
||||
Ok(Some(BinarySettings {
|
||||
path_lookup: Some(false),
|
||||
..
|
||||
})) => None,
|
||||
_ => {
|
||||
let env = delegate.shell_env().await;
|
||||
let path = delegate.which(Self::SERVER_NAME.as_ref()).await?;
|
||||
Some(LanguageServerBinary {
|
||||
path,
|
||||
arguments: server_binary_arguments(),
|
||||
env: Some(env),
|
||||
})
|
||||
}
|
||||
}
|
||||
let path = delegate.which(Self::SERVER_NAME.as_ref()).await?;
|
||||
Some(LanguageServerBinary {
|
||||
path,
|
||||
arguments: server_binary_arguments(),
|
||||
env: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn will_fetch_server(
|
||||
|
@ -214,18 +186,6 @@ impl super::LspAdapter for GoLspAdapter {
|
|||
get_cached_server_binary(container_dir).await
|
||||
}
|
||||
|
||||
async fn installation_test_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir)
|
||||
.await
|
||||
.map(|mut binary| {
|
||||
binary.arguments = vec!["--help".into()];
|
||||
binary
|
||||
})
|
||||
}
|
||||
|
||||
async fn initialization_options(
|
||||
self: Arc<Self>,
|
||||
_: &Arc<dyn LspAdapterDelegate>,
|
||||
|
|
|
@ -186,13 +186,6 @@ impl LspAdapter for JsonLspAdapter {
|
|||
get_cached_server_binary(container_dir, &self.node).await
|
||||
}
|
||||
|
||||
async fn installation_test_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir, &self.node).await
|
||||
}
|
||||
|
||||
async fn initialization_options(
|
||||
self: Arc<Self>,
|
||||
_: &Arc<dyn LspAdapterDelegate>,
|
||||
|
@ -374,18 +367,6 @@ impl LspAdapter for NodeVersionAdapter {
|
|||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_version_server_binary(container_dir).await
|
||||
}
|
||||
|
||||
async fn installation_test_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_version_server_binary(container_dir)
|
||||
.await
|
||||
.map(|mut binary| {
|
||||
binary.arguments = vec!["--version".into()];
|
||||
binary
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_cached_version_server_binary(container_dir: PathBuf) -> Option<LanguageServerBinary> {
|
||||
|
|
|
@ -97,13 +97,6 @@ impl LspAdapter for PythonLspAdapter {
|
|||
get_cached_server_binary(container_dir, &self.node).await
|
||||
}
|
||||
|
||||
async fn installation_test_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir, &self.node).await
|
||||
}
|
||||
|
||||
async fn process_completions(&self, items: &mut [lsp::CompletionItem]) {
|
||||
// Pyright assigns each completion item a `sortText` of the form `XX.YYYY.name`.
|
||||
// Where `XX` is the sorting category, `YYYY` is based on most recent usage,
|
||||
|
|
|
@ -8,7 +8,6 @@ use http_client::github::{latest_github_release, GitHubLspBinaryVersion};
|
|||
pub use language::*;
|
||||
use language_settings::all_language_settings;
|
||||
use lsp::LanguageServerBinary;
|
||||
use project::{lsp_store::language_server_settings, project_settings::BinarySettings};
|
||||
use regex::Regex;
|
||||
use smol::fs::{self, File};
|
||||
use std::{
|
||||
|
@ -37,77 +36,34 @@ impl LspAdapter for RustLspAdapter {
|
|||
async fn check_if_user_installed(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
cx: &AsyncAppContext,
|
||||
_: &AsyncAppContext,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let configured_binary = cx
|
||||
.update(|cx| {
|
||||
language_server_settings(delegate, &Self::SERVER_NAME, cx)
|
||||
.and_then(|s| s.binary.clone())
|
||||
let path = delegate.which("rust-analyzer".as_ref()).await?;
|
||||
let env = delegate.shell_env().await;
|
||||
|
||||
// It is surprisingly common for ~/.cargo/bin/rust-analyzer to be a symlink to
|
||||
// /usr/bin/rust-analyzer that fails when you run it; so we need to test it.
|
||||
log::info!("found rust-analyzer in PATH. trying to run `rust-analyzer --help`");
|
||||
let result = delegate
|
||||
.try_exec(LanguageServerBinary {
|
||||
path: path.clone(),
|
||||
arguments: vec!["--help".into()],
|
||||
env: Some(env.clone()),
|
||||
})
|
||||
.ok()?;
|
||||
.await;
|
||||
if let Err(err) = result {
|
||||
log::error!(
|
||||
"failed to run rust-analyzer after detecting it in PATH: binary: {:?}: {}",
|
||||
path,
|
||||
err
|
||||
);
|
||||
return None;
|
||||
}
|
||||
|
||||
let (path, env, arguments) = match configured_binary {
|
||||
// If nothing is configured, or path_lookup explicitly enabled,
|
||||
// we lookup the binary in the path.
|
||||
None
|
||||
| Some(BinarySettings {
|
||||
path: None,
|
||||
path_lookup: Some(true),
|
||||
..
|
||||
})
|
||||
| Some(BinarySettings {
|
||||
path: None,
|
||||
path_lookup: None,
|
||||
..
|
||||
}) => {
|
||||
let path = delegate.which("rust-analyzer".as_ref()).await;
|
||||
let env = delegate.shell_env().await;
|
||||
|
||||
if let Some(path) = path {
|
||||
// It is surprisingly common for ~/.cargo/bin/rust-analyzer to be a symlink to
|
||||
// /usr/bin/rust-analyzer that fails when you run it; so we need to test it.
|
||||
log::info!("found rust-analyzer in PATH. trying to run `rust-analyzer --help`");
|
||||
match delegate
|
||||
.try_exec(LanguageServerBinary {
|
||||
path: path.clone(),
|
||||
arguments: vec!["--help".into()],
|
||||
env: Some(env.clone()),
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(()) => (Some(path), Some(env), None),
|
||||
Err(err) => {
|
||||
log::error!("failed to run rust-analyzer after detecting it in PATH: binary: {:?}: {}", path, err);
|
||||
(None, None, None)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
(None, None, None)
|
||||
}
|
||||
}
|
||||
// Otherwise, we use the configured binary.
|
||||
Some(BinarySettings {
|
||||
path: Some(path),
|
||||
arguments,
|
||||
path_lookup,
|
||||
}) => {
|
||||
if path_lookup.is_some() {
|
||||
log::warn!("Both `path` and `path_lookup` are set, ignoring `path_lookup`");
|
||||
}
|
||||
(Some(path.into()), None, arguments)
|
||||
}
|
||||
|
||||
_ => (None, None, None),
|
||||
};
|
||||
|
||||
path.map(|path| LanguageServerBinary {
|
||||
Some(LanguageServerBinary {
|
||||
path,
|
||||
env,
|
||||
arguments: arguments
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.map(|arg| arg.into())
|
||||
.collect(),
|
||||
env: Some(env),
|
||||
arguments: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -186,18 +142,6 @@ impl LspAdapter for RustLspAdapter {
|
|||
get_cached_server_binary(container_dir).await
|
||||
}
|
||||
|
||||
async fn installation_test_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir)
|
||||
.await
|
||||
.map(|mut binary| {
|
||||
binary.arguments = vec!["--help".into()];
|
||||
binary
|
||||
})
|
||||
}
|
||||
|
||||
fn disk_based_diagnostic_sources(&self) -> Vec<String> {
|
||||
vec!["rustc".into()]
|
||||
}
|
||||
|
|
|
@ -46,38 +46,6 @@ impl LspAdapter for TailwindLspAdapter {
|
|||
Self::SERVER_NAME.clone()
|
||||
}
|
||||
|
||||
async fn check_if_user_installed(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
cx: &AsyncAppContext,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let configured_binary = cx
|
||||
.update(|cx| {
|
||||
language_server_settings(delegate, &Self::SERVER_NAME, cx)
|
||||
.and_then(|s| s.binary.clone())
|
||||
})
|
||||
.ok()??;
|
||||
|
||||
let path = if let Some(configured_path) = configured_binary.path.map(PathBuf::from) {
|
||||
configured_path
|
||||
} else {
|
||||
self.node.binary_path().await.ok()?
|
||||
};
|
||||
|
||||
let arguments = configured_binary
|
||||
.arguments
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.map(|arg| arg.into())
|
||||
.collect();
|
||||
|
||||
Some(LanguageServerBinary {
|
||||
path,
|
||||
arguments,
|
||||
env: None,
|
||||
})
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
|
@ -125,13 +93,6 @@ impl LspAdapter for TailwindLspAdapter {
|
|||
get_cached_server_binary(container_dir, &self.node).await
|
||||
}
|
||||
|
||||
async fn installation_test_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir, &self.node).await
|
||||
}
|
||||
|
||||
async fn initialization_options(
|
||||
self: Arc<Self>,
|
||||
_: &Arc<dyn LspAdapterDelegate>,
|
||||
|
|
|
@ -164,13 +164,6 @@ impl LspAdapter for TypeScriptLspAdapter {
|
|||
get_cached_ts_server_binary(container_dir, &self.node).await
|
||||
}
|
||||
|
||||
async fn installation_test_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_ts_server_binary(container_dir, &self.node).await
|
||||
}
|
||||
|
||||
fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
|
||||
Some(vec![
|
||||
CodeActionKind::QUICKFIX,
|
||||
|
@ -509,19 +502,6 @@ impl LspAdapter for EsLintLspAdapter {
|
|||
arguments: eslint_server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
|
||||
async fn installation_test_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let server_path =
|
||||
Self::build_destination_path(&container_dir).join(EsLintLspAdapter::SERVER_PATH);
|
||||
Some(LanguageServerBinary {
|
||||
path: self.node.binary_path().await.ok()?,
|
||||
env: None,
|
||||
arguments: eslint_server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
|
|
|
@ -5,7 +5,7 @@ use gpui::AsyncAppContext;
|
|||
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||
use lsp::{CodeActionKind, LanguageServerBinary};
|
||||
use node_runtime::NodeRuntime;
|
||||
use project::{lsp_store::language_server_settings, project_settings::BinarySettings};
|
||||
use project::lsp_store::language_server_settings;
|
||||
use serde_json::Value;
|
||||
use std::{
|
||||
any::Any,
|
||||
|
@ -71,40 +71,15 @@ impl LspAdapter for VtslsLspAdapter {
|
|||
async fn check_if_user_installed(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
cx: &AsyncAppContext,
|
||||
_: &AsyncAppContext,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let configured_binary = cx.update(|cx| {
|
||||
language_server_settings(delegate, &SERVER_NAME, cx).and_then(|s| s.binary.clone())
|
||||
});
|
||||
|
||||
match configured_binary {
|
||||
Ok(Some(BinarySettings {
|
||||
path: Some(path),
|
||||
arguments,
|
||||
..
|
||||
})) => Some(LanguageServerBinary {
|
||||
path: path.into(),
|
||||
arguments: arguments
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.map(|arg| arg.into())
|
||||
.collect(),
|
||||
env: None,
|
||||
}),
|
||||
Ok(Some(BinarySettings {
|
||||
path_lookup: Some(false),
|
||||
..
|
||||
})) => None,
|
||||
_ => {
|
||||
let env = delegate.shell_env().await;
|
||||
let path = delegate.which(SERVER_NAME.as_ref()).await?;
|
||||
Some(LanguageServerBinary {
|
||||
path: path.clone(),
|
||||
arguments: typescript_server_binary_arguments(&path),
|
||||
env: Some(env),
|
||||
})
|
||||
}
|
||||
}
|
||||
let env = delegate.shell_env().await;
|
||||
let path = delegate.which(SERVER_NAME.as_ref()).await?;
|
||||
Some(LanguageServerBinary {
|
||||
path: path.clone(),
|
||||
arguments: typescript_server_binary_arguments(&path),
|
||||
env: Some(env),
|
||||
})
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
|
@ -157,13 +132,6 @@ impl LspAdapter for VtslsLspAdapter {
|
|||
get_cached_ts_server_binary(container_dir, &self.node).await
|
||||
}
|
||||
|
||||
async fn installation_test_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_ts_server_binary(container_dir, &self.node).await
|
||||
}
|
||||
|
||||
fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
|
||||
Some(vec![
|
||||
CodeActionKind::QUICKFIX,
|
||||
|
|
|
@ -42,37 +42,6 @@ impl LspAdapter for YamlLspAdapter {
|
|||
Self::SERVER_NAME.clone()
|
||||
}
|
||||
|
||||
async fn check_if_user_installed(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
cx: &AsyncAppContext,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let configured_binary = cx
|
||||
.update(|cx| {
|
||||
language_server_settings(delegate, &Self::SERVER_NAME, cx)
|
||||
.and_then(|s| s.binary.clone())
|
||||
})
|
||||
.ok()??;
|
||||
|
||||
let path = if let Some(configured_path) = configured_binary.path.map(PathBuf::from) {
|
||||
configured_path
|
||||
} else {
|
||||
self.node.binary_path().await.ok()?
|
||||
};
|
||||
|
||||
let arguments = configured_binary
|
||||
.arguments
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.map(|arg| arg.into())
|
||||
.collect();
|
||||
Some(LanguageServerBinary {
|
||||
path,
|
||||
arguments,
|
||||
env: None,
|
||||
})
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
|
@ -120,13 +89,6 @@ impl LspAdapter for YamlLspAdapter {
|
|||
get_cached_server_binary(container_dir, &self.node).await
|
||||
}
|
||||
|
||||
async fn installation_test_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir, &self.node).await
|
||||
}
|
||||
|
||||
async fn workspace_configuration(
|
||||
self: Arc<Self>,
|
||||
delegate: &Arc<dyn LspAdapterDelegate>,
|
||||
|
|
|
@ -64,6 +64,15 @@ pub struct LanguageServerBinary {
|
|||
pub env: Option<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
/// Configures the search (and installation) of language servers.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct LanguageServerBinaryOptions {
|
||||
/// Whether the adapter should look at the users system
|
||||
pub allow_path_lookup: bool,
|
||||
/// Whether the adapter should download its own version
|
||||
pub allow_binary_download: bool,
|
||||
}
|
||||
|
||||
/// A running language server process.
|
||||
pub struct LanguageServer {
|
||||
server_id: LanguageServerId,
|
||||
|
|
|
@ -37,16 +37,16 @@ use language::{
|
|||
proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version},
|
||||
range_from_lsp, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CodeLabel, Diagnostic,
|
||||
DiagnosticEntry, DiagnosticSet, Diff, Documentation, File as _, Language, LanguageConfig,
|
||||
LanguageMatcher, LanguageName, LanguageRegistry, LanguageServerName, LocalFile, LspAdapter,
|
||||
LspAdapterDelegate, Patch, PendingLanguageServer, PointUtf16, TextBufferSnapshot, ToOffset,
|
||||
ToPointUtf16, Transaction, Unclipped,
|
||||
LanguageMatcher, LanguageName, LanguageRegistry, LanguageServerBinaryStatus,
|
||||
LanguageServerName, LocalFile, LspAdapter, LspAdapterDelegate, Patch, PointUtf16,
|
||||
TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, Unclipped,
|
||||
};
|
||||
use lsp::{
|
||||
CodeActionKind, CompletionContext, DiagnosticSeverity, DiagnosticTag,
|
||||
DidChangeWatchedFilesRegistrationOptions, Edit, FileSystemWatcher, InsertTextFormat,
|
||||
LanguageServer, LanguageServerBinary, LanguageServerId, LspRequestFuture, MessageActionItem,
|
||||
MessageType, OneOf, ServerHealthStatus, ServerStatus, SymbolKind, TextEdit, Url,
|
||||
WorkDoneProgressCancelParams, WorkspaceFolder,
|
||||
LanguageServer, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerId,
|
||||
LspRequestFuture, MessageActionItem, MessageType, OneOf, ServerHealthStatus, ServerStatus,
|
||||
SymbolKind, TextEdit, Url, WorkDoneProgressCancelParams, WorkspaceFolder,
|
||||
};
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use postage::watch;
|
||||
|
@ -67,9 +67,8 @@ use std::{
|
|||
iter, mem,
|
||||
ops::{ControlFlow, Range},
|
||||
path::{self, Path, PathBuf},
|
||||
process::Stdio,
|
||||
str,
|
||||
sync::{atomic::Ordering::SeqCst, Arc},
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use text::{Anchor, BufferId, LineEnding};
|
||||
|
@ -87,8 +86,6 @@ pub use worktree::{
|
|||
FS_WATCH_LATENCY,
|
||||
};
|
||||
|
||||
const MAX_SERVER_REINSTALL_ATTEMPT_COUNT: u64 = 4;
|
||||
const SERVER_REINSTALL_DEBOUNCE_TIMEOUT: Duration = Duration::from_secs(1);
|
||||
const SERVER_LAUNCHING_BEFORE_SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
pub const SERVER_PROGRESS_THROTTLE_TIMEOUT: Duration = Duration::from_millis(100);
|
||||
|
||||
|
@ -157,6 +154,7 @@ impl LocalLspStore {
|
|||
futures::future::join_all(shutdown_futures).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn format_locally(
|
||||
lsp_store: WeakModel<LspStore>,
|
||||
mut buffers_with_paths: Vec<(Model<Buffer>, Option<PathBuf>)>,
|
||||
|
@ -1471,7 +1469,7 @@ impl LspStore {
|
|||
}
|
||||
|
||||
for (worktree_id, adapter_name) in language_servers_to_stop {
|
||||
self.stop_language_server(worktree_id, adapter_name, cx)
|
||||
self.stop_local_language_server(worktree_id, adapter_name, cx)
|
||||
.detach();
|
||||
}
|
||||
|
||||
|
@ -1488,7 +1486,7 @@ impl LspStore {
|
|||
|
||||
// Restart all language servers with changed initialization options.
|
||||
for (worktree, language) in language_servers_to_restart {
|
||||
self.restart_language_servers(worktree, language, cx);
|
||||
self.restart_local_language_servers(worktree, language, cx);
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
|
@ -3028,7 +3026,7 @@ impl LspStore {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn primary_language_server_for_buffer<'a>(
|
||||
fn primary_language_server_for_buffer<'a>(
|
||||
&'a self,
|
||||
buffer: &'a Buffer,
|
||||
cx: &'a AppContext,
|
||||
|
@ -3328,7 +3326,7 @@ impl LspStore {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_worktree_diagnostics(
|
||||
fn update_worktree_diagnostics(
|
||||
&mut self,
|
||||
worktree_id: WorktreeId,
|
||||
server_id: LanguageServerId,
|
||||
|
@ -5405,9 +5403,6 @@ impl LspStore {
|
|||
language_registry: self.languages.clone(),
|
||||
}) as Arc<dyn LspAdapterDelegate>;
|
||||
|
||||
// TODO: We should use `adapter` here instead of reaching through the `CachedLspAdapter`.
|
||||
let lsp_adapter = adapter.adapter.clone();
|
||||
|
||||
let Some((upstream_client, project_id)) = self.upstream_client() else {
|
||||
return;
|
||||
};
|
||||
|
@ -5419,17 +5414,11 @@ impl LspStore {
|
|||
return;
|
||||
};
|
||||
|
||||
let task = cx.spawn(|_, cx| async move {
|
||||
let user_binary_task = lsp_adapter.check_if_user_installed(delegate.as_ref(), &cx);
|
||||
let binary = match user_binary_task.await {
|
||||
Some(binary) => binary,
|
||||
None => {
|
||||
return Err(anyhow!(
|
||||
"Downloading language server for ssh host is not supported yet"
|
||||
))
|
||||
}
|
||||
};
|
||||
let user_binary_task =
|
||||
self.get_language_server_binary(adapter.clone(), delegate.clone(), false, cx);
|
||||
|
||||
let task = cx.spawn(|_, _| async move {
|
||||
let binary = user_binary_task.await?;
|
||||
let name = adapter.name();
|
||||
let code_action_kinds = adapter
|
||||
.adapter
|
||||
|
@ -5481,6 +5470,73 @@ impl LspStore {
|
|||
.detach();
|
||||
}
|
||||
|
||||
fn get_language_server_binary(
|
||||
&self,
|
||||
adapter: Arc<CachedLspAdapter>,
|
||||
delegate: Arc<dyn LspAdapterDelegate>,
|
||||
allow_binary_download: bool,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<LanguageServerBinary>> {
|
||||
let settings = ProjectSettings::get(
|
||||
Some(SettingsLocation {
|
||||
worktree_id: delegate.worktree_id(),
|
||||
path: Path::new(""),
|
||||
}),
|
||||
cx,
|
||||
)
|
||||
.lsp
|
||||
.get(&adapter.name)
|
||||
.and_then(|s| s.binary.clone());
|
||||
|
||||
if settings.as_ref().is_some_and(|b| b.path.is_some()) {
|
||||
let settings = settings.unwrap();
|
||||
return cx.spawn(|_, _| async move {
|
||||
Ok(LanguageServerBinary {
|
||||
path: PathBuf::from(&settings.path.unwrap()),
|
||||
env: Some(delegate.shell_env().await),
|
||||
arguments: settings
|
||||
.arguments
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.map(Into::into)
|
||||
.collect(),
|
||||
})
|
||||
});
|
||||
}
|
||||
let lsp_binary_options = LanguageServerBinaryOptions {
|
||||
allow_path_lookup: !settings
|
||||
.as_ref()
|
||||
.and_then(|b| b.ignore_system_version)
|
||||
.unwrap_or_default(),
|
||||
allow_binary_download,
|
||||
};
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
let binary_result = adapter
|
||||
.clone()
|
||||
.get_language_server_command(delegate.clone(), lsp_binary_options, &mut cx)
|
||||
.await;
|
||||
|
||||
delegate.update_status(adapter.name.clone(), LanguageServerBinaryStatus::None);
|
||||
|
||||
let mut binary = binary_result?;
|
||||
if let Some(arguments) = settings.and_then(|b| b.arguments) {
|
||||
binary.arguments = arguments.into_iter().map(Into::into).collect();
|
||||
}
|
||||
|
||||
// If we do have a project environment (either by spawning a shell in in the project directory
|
||||
// or by getting it from the CLI) and the language server command itself
|
||||
// doesn't have an environment, then we use the project environment.
|
||||
if binary.env.is_none() {
|
||||
log::info!(
|
||||
"using project environment for language server {:?}",
|
||||
adapter.name()
|
||||
);
|
||||
binary.env = Some(delegate.shell_env().await);
|
||||
}
|
||||
Ok(binary)
|
||||
})
|
||||
}
|
||||
|
||||
fn start_language_server(
|
||||
&mut self,
|
||||
worktree_handle: &Model<Worktree>,
|
||||
|
@ -5496,6 +5552,7 @@ impl LspStore {
|
|||
let worktree_id = worktree.id();
|
||||
let worktree_path = worktree.abs_path();
|
||||
let key = (worktree_id, adapter.name.clone());
|
||||
|
||||
if self.language_server_ids.contains_key(&key) {
|
||||
return;
|
||||
}
|
||||
|
@ -5505,31 +5562,6 @@ impl LspStore {
|
|||
return;
|
||||
}
|
||||
|
||||
if adapter.reinstall_attempt_count.load(SeqCst) > MAX_SERVER_REINSTALL_ATTEMPT_COUNT {
|
||||
return;
|
||||
}
|
||||
|
||||
let local = self.as_local().unwrap();
|
||||
|
||||
let stderr_capture = Arc::new(Mutex::new(Some(String::new())));
|
||||
let lsp_adapter_delegate = LocalLspAdapterDelegate::for_local(self, worktree_handle, cx);
|
||||
let project_environment = local.environment.update(cx, |environment, cx| {
|
||||
environment.get_environment(Some(worktree_id), Some(worktree_path.clone()), cx)
|
||||
});
|
||||
|
||||
let pending_server = match self.languages.create_pending_language_server(
|
||||
stderr_capture.clone(),
|
||||
language.clone(),
|
||||
adapter.clone(),
|
||||
Arc::clone(&worktree_path),
|
||||
lsp_adapter_delegate.clone(),
|
||||
project_environment,
|
||||
cx,
|
||||
) {
|
||||
Some(pending_server) => pending_server,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let project_settings = ProjectSettings::get(
|
||||
Some(SettingsLocation {
|
||||
worktree_id,
|
||||
|
@ -5537,76 +5569,146 @@ impl LspStore {
|
|||
}),
|
||||
cx,
|
||||
);
|
||||
|
||||
// We need some on the SSH client, and some on SSH host
|
||||
let lsp = project_settings.lsp.get(&adapter.name);
|
||||
let override_options = lsp.and_then(|s| s.initialization_options.clone());
|
||||
|
||||
let server_id = pending_server.server_id;
|
||||
let container_dir = pending_server.container_dir.clone();
|
||||
let state = LanguageServerState::Starting({
|
||||
let stderr_capture = Arc::new(Mutex::new(Some(String::new())));
|
||||
let delegate = LocalLspAdapterDelegate::for_local(self, worktree_handle, cx)
|
||||
as Arc<dyn LspAdapterDelegate>;
|
||||
|
||||
let server_id = self.languages.next_language_server_id();
|
||||
let root_path = worktree_path.clone();
|
||||
log::info!(
|
||||
"attempting to start language server {:?}, path: {root_path:?}, id: {server_id}",
|
||||
adapter.name.0
|
||||
);
|
||||
|
||||
let binary = self.get_language_server_binary(adapter.clone(), delegate.clone(), true, cx);
|
||||
|
||||
let pending_server = cx.spawn({
|
||||
let adapter = adapter.clone();
|
||||
let stderr_capture = stderr_capture.clone();
|
||||
|
||||
move |_lsp_store, cx| async move {
|
||||
let binary = binary.await?;
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
if let Some(server) = _lsp_store
|
||||
.update(&mut cx.clone(), |this, cx| {
|
||||
this.languages.create_fake_language_server(
|
||||
server_id,
|
||||
&adapter.name,
|
||||
binary.clone(),
|
||||
cx.to_async(),
|
||||
)
|
||||
})
|
||||
.ok()
|
||||
.flatten()
|
||||
{
|
||||
return Ok(server);
|
||||
}
|
||||
|
||||
lsp::LanguageServer::new(
|
||||
stderr_capture,
|
||||
server_id,
|
||||
binary,
|
||||
&root_path,
|
||||
adapter.code_action_kinds(),
|
||||
cx,
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
let state = LanguageServerState::Starting({
|
||||
let server_name = adapter.name.0.clone();
|
||||
let delegate = delegate as Arc<dyn LspAdapterDelegate>;
|
||||
let language = language.clone();
|
||||
let key = key.clone();
|
||||
let adapter = adapter.clone();
|
||||
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
let result = Self::setup_and_insert_language_server(
|
||||
this.clone(),
|
||||
lsp_adapter_delegate,
|
||||
override_options,
|
||||
pending_server,
|
||||
adapter.clone(),
|
||||
language.clone(),
|
||||
server_id,
|
||||
key,
|
||||
&mut cx,
|
||||
)
|
||||
let result = {
|
||||
let delegate = delegate.clone();
|
||||
let adapter = adapter.clone();
|
||||
let this = this.clone();
|
||||
let mut cx = cx.clone();
|
||||
async move {
|
||||
let language_server = pending_server.await?;
|
||||
|
||||
let workspace_config = adapter
|
||||
.adapter
|
||||
.clone()
|
||||
.workspace_configuration(&delegate, &mut cx)
|
||||
.await?;
|
||||
|
||||
let mut initialization_options = adapter
|
||||
.adapter
|
||||
.clone()
|
||||
.initialization_options(&(delegate))
|
||||
.await?;
|
||||
|
||||
Self::setup_lsp_messages(this.clone(), &language_server, delegate, adapter);
|
||||
|
||||
match (&mut initialization_options, override_options) {
|
||||
(Some(initialization_options), Some(override_options)) => {
|
||||
merge_json_value_into(override_options, initialization_options);
|
||||
}
|
||||
(None, override_options) => initialization_options = override_options,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let language_server = cx
|
||||
.update(|cx| language_server.initialize(initialization_options, cx))?
|
||||
.await
|
||||
.inspect_err(|_| {
|
||||
if let Some(this) = this.upgrade() {
|
||||
this.update(&mut cx, |_, cx| {
|
||||
cx.emit(LspStoreEvent::LanguageServerRemoved(server_id))
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
})?;
|
||||
|
||||
language_server
|
||||
.notify::<lsp::notification::DidChangeConfiguration>(
|
||||
lsp::DidChangeConfigurationParams {
|
||||
settings: workspace_config,
|
||||
},
|
||||
)
|
||||
.ok();
|
||||
|
||||
anyhow::Ok(language_server)
|
||||
}
|
||||
}
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(server) => {
|
||||
this.update(&mut cx, |this, mut cx| {
|
||||
this.insert_newly_running_language_server(
|
||||
language,
|
||||
adapter,
|
||||
server.clone(),
|
||||
server_id,
|
||||
key,
|
||||
&mut cx,
|
||||
);
|
||||
})
|
||||
.ok();
|
||||
stderr_capture.lock().take();
|
||||
server
|
||||
Some(server)
|
||||
}
|
||||
|
||||
Err(err) => {
|
||||
log::error!("failed to start language server {server_name:?}: {err}");
|
||||
log::error!("server stderr: {:?}", stderr_capture.lock().take());
|
||||
|
||||
let this = this.upgrade()?;
|
||||
let container_dir = container_dir?;
|
||||
|
||||
let attempt_count = adapter.reinstall_attempt_count.fetch_add(1, SeqCst);
|
||||
if attempt_count >= MAX_SERVER_REINSTALL_ATTEMPT_COUNT {
|
||||
let max = MAX_SERVER_REINSTALL_ATTEMPT_COUNT;
|
||||
log::error!("Hit {max} reinstallation attempts for {server_name:?}");
|
||||
return None;
|
||||
}
|
||||
|
||||
log::info!(
|
||||
"retrying installation of language server {server_name:?} in {}s",
|
||||
SERVER_REINSTALL_DEBOUNCE_TIMEOUT.as_secs()
|
||||
let log = stderr_capture.lock().take().unwrap_or_default();
|
||||
delegate.update_status(
|
||||
adapter.name(),
|
||||
LanguageServerBinaryStatus::Failed {
|
||||
error: format!("{err}\n-- stderr--\n{}", log),
|
||||
},
|
||||
);
|
||||
cx.background_executor()
|
||||
.timer(SERVER_REINSTALL_DEBOUNCE_TIMEOUT)
|
||||
.await;
|
||||
|
||||
let installation_test_binary = adapter
|
||||
.installation_test_binary(container_dir.to_path_buf())
|
||||
.await;
|
||||
|
||||
this.update(&mut cx, |_, cx| {
|
||||
Self::check_errored_server(
|
||||
language,
|
||||
adapter,
|
||||
server_id,
|
||||
installation_test_binary,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.ok();
|
||||
|
||||
log::error!("Failed to start language server {server_name:?}: {err}");
|
||||
log::error!("server stderr: {:?}", log);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
@ -5620,109 +5722,6 @@ impl LspStore {
|
|||
self.language_server_ids.insert(key, server_id);
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn setup_and_insert_language_server(
|
||||
this: WeakModel<Self>,
|
||||
delegate: Arc<dyn LspAdapterDelegate>,
|
||||
override_initialization_options: Option<serde_json::Value>,
|
||||
pending_server: PendingLanguageServer,
|
||||
adapter: Arc<CachedLspAdapter>,
|
||||
language: LanguageName,
|
||||
server_id: LanguageServerId,
|
||||
key: (WorktreeId, LanguageServerName),
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<Option<Arc<LanguageServer>>> {
|
||||
let language_server = Self::setup_pending_language_server(
|
||||
this.clone(),
|
||||
override_initialization_options,
|
||||
pending_server,
|
||||
delegate,
|
||||
adapter.clone(),
|
||||
server_id,
|
||||
cx,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let this = match this.upgrade() {
|
||||
Some(this) => this,
|
||||
None => return Err(anyhow!("failed to upgrade project handle")),
|
||||
};
|
||||
|
||||
this.update(cx, |this, cx| {
|
||||
this.insert_newly_running_language_server(
|
||||
language,
|
||||
adapter,
|
||||
language_server.clone(),
|
||||
server_id,
|
||||
key,
|
||||
cx,
|
||||
)
|
||||
})??;
|
||||
|
||||
Ok(Some(language_server))
|
||||
}
|
||||
|
||||
fn reinstall_language_server(
|
||||
&mut self,
|
||||
language: LanguageName,
|
||||
adapter: Arc<CachedLspAdapter>,
|
||||
server_id: LanguageServerId,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Option<Task<()>> {
|
||||
log::info!("beginning to reinstall server");
|
||||
|
||||
if let Some(local) = self.as_local_mut() {
|
||||
let existing_server = match local.language_servers.remove(&server_id) {
|
||||
Some(LanguageServerState::Running { server, .. }) => Some(server),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
self.worktree_store.update(cx, |store, cx| {
|
||||
for worktree in store.worktrees() {
|
||||
let key = (worktree.read(cx).id(), adapter.name.clone());
|
||||
self.language_server_ids.remove(&key);
|
||||
}
|
||||
});
|
||||
|
||||
Some(cx.spawn(move |this, mut cx| async move {
|
||||
if let Some(task) = existing_server.and_then(|server| server.shutdown()) {
|
||||
log::info!("shutting down existing server");
|
||||
task.await;
|
||||
}
|
||||
|
||||
// TODO: This is race-safe with regards to preventing new instances from
|
||||
// starting while deleting, but existing instances in other projects are going
|
||||
// to be very confused and messed up
|
||||
let Some(task) = this
|
||||
.update(&mut cx, |this, cx| {
|
||||
this.languages.delete_server_container(adapter.clone(), cx)
|
||||
})
|
||||
.log_err()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
task.await;
|
||||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
for worktree in this.worktree_store.read(cx).worktrees().collect::<Vec<_>>() {
|
||||
this.start_language_server(
|
||||
&worktree,
|
||||
adapter.clone(),
|
||||
language.clone(),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
}))
|
||||
} else if let Some(_ssh_store) = self.as_ssh() {
|
||||
// TODO
|
||||
None
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
async fn shutdown_language_server(
|
||||
server_state: Option<LanguageServerState>,
|
||||
name: LanguageServerName,
|
||||
|
@ -5761,7 +5760,7 @@ impl LspStore {
|
|||
|
||||
// Returns a list of all of the worktrees which no longer have a language server and the root path
|
||||
// for the stopped server
|
||||
pub fn stop_language_server(
|
||||
fn stop_local_language_server(
|
||||
&mut self,
|
||||
worktree_id: WorktreeId,
|
||||
adapter_name: LanguageServerName,
|
||||
|
@ -5877,7 +5876,6 @@ impl LspStore {
|
|||
.spawn(request)
|
||||
.detach_and_log_err(cx);
|
||||
} else {
|
||||
#[allow(clippy::mutable_key_type)]
|
||||
let language_server_lookup_info: HashSet<(Model<Worktree>, LanguageName)> = buffers
|
||||
.into_iter()
|
||||
.filter_map(|buffer| {
|
||||
|
@ -5893,12 +5891,12 @@ impl LspStore {
|
|||
.collect();
|
||||
|
||||
for (worktree, language) in language_server_lookup_info {
|
||||
self.restart_language_servers(worktree, language, cx);
|
||||
self.restart_local_language_servers(worktree, language, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn restart_language_servers(
|
||||
fn restart_local_language_servers(
|
||||
&mut self,
|
||||
worktree: Model<Worktree>,
|
||||
language: LanguageName,
|
||||
|
@ -5912,7 +5910,8 @@ impl LspStore {
|
|||
.lsp_adapters(&language)
|
||||
.iter()
|
||||
.map(|adapter| {
|
||||
let stop_task = self.stop_language_server(worktree_id, adapter.name.clone(), cx);
|
||||
let stop_task =
|
||||
self.stop_local_language_server(worktree_id, adapter.name.clone(), cx);
|
||||
(stop_task, adapter.name.clone())
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
@ -5951,93 +5950,14 @@ impl LspStore {
|
|||
.detach();
|
||||
}
|
||||
|
||||
fn check_errored_server(
|
||||
language: LanguageName,
|
||||
adapter: Arc<CachedLspAdapter>,
|
||||
server_id: LanguageServerId,
|
||||
installation_test_binary: Option<LanguageServerBinary>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
if !adapter.can_be_reinstalled() {
|
||||
log::info!(
|
||||
"Validation check requested for {:?} but it cannot be reinstalled",
|
||||
adapter.name.0
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
log::info!("About to spawn test binary");
|
||||
|
||||
// A lack of test binary counts as a failure
|
||||
let process = installation_test_binary.and_then(|binary| {
|
||||
smol::process::Command::new(&binary.path)
|
||||
.current_dir(&binary.path)
|
||||
.args(binary.arguments)
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::inherit())
|
||||
.kill_on_drop(true)
|
||||
.spawn()
|
||||
.ok()
|
||||
});
|
||||
|
||||
const PROCESS_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
let mut timeout = cx.background_executor().timer(PROCESS_TIMEOUT).fuse();
|
||||
|
||||
let mut errored = false;
|
||||
if let Some(mut process) = process {
|
||||
futures::select! {
|
||||
status = process.status().fuse() => match status {
|
||||
Ok(status) => errored = !status.success(),
|
||||
Err(_) => errored = true,
|
||||
},
|
||||
|
||||
_ = timeout => {
|
||||
log::info!("test binary time-ed out, this counts as a success");
|
||||
_ = process.kill();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::warn!("test binary failed to launch");
|
||||
errored = true;
|
||||
}
|
||||
|
||||
if errored {
|
||||
log::warn!("test binary check failed");
|
||||
let task = this
|
||||
.update(&mut cx, move |this, cx| {
|
||||
this.reinstall_language_server(language, adapter, server_id, cx)
|
||||
})
|
||||
.ok()
|
||||
.flatten();
|
||||
|
||||
if let Some(task) = task {
|
||||
task.await;
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
async fn setup_pending_language_server(
|
||||
fn setup_lsp_messages(
|
||||
this: WeakModel<Self>,
|
||||
override_options: Option<serde_json::Value>,
|
||||
pending_server: PendingLanguageServer,
|
||||
language_server: &LanguageServer,
|
||||
delegate: Arc<dyn LspAdapterDelegate>,
|
||||
adapter: Arc<CachedLspAdapter>,
|
||||
server_id: LanguageServerId,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<Arc<LanguageServer>> {
|
||||
let workspace_config = adapter
|
||||
.adapter
|
||||
.clone()
|
||||
.workspace_configuration(&delegate, cx)
|
||||
.await?;
|
||||
// This has to come from the server
|
||||
let (language_server, mut initialization_options) = pending_server.task.await?;
|
||||
|
||||
) {
|
||||
let name = language_server.name();
|
||||
let server_id = language_server.server_id();
|
||||
language_server
|
||||
.on_notification::<lsp::notification::PublishDiagnostics, _>({
|
||||
let adapter = adapter.clone();
|
||||
|
@ -6091,7 +6011,6 @@ impl LspStore {
|
|||
})
|
||||
.detach();
|
||||
|
||||
let id = language_server.server_id();
|
||||
language_server
|
||||
.on_request::<lsp::request::WorkspaceFoldersRequest, _, _>({
|
||||
let this = this.clone();
|
||||
|
@ -6099,7 +6018,7 @@ impl LspStore {
|
|||
let this = this.clone();
|
||||
async move {
|
||||
let Some(server) =
|
||||
this.update(&mut cx, |this, _| this.language_server_for_id(id))?
|
||||
this.update(&mut cx, |this, _| this.language_server_for_id(server_id))?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
@ -6375,9 +6294,6 @@ impl LspStore {
|
|||
})
|
||||
.detach();
|
||||
|
||||
let disk_based_diagnostics_progress_token =
|
||||
adapter.disk_based_diagnostics_progress_token.clone();
|
||||
|
||||
language_server
|
||||
.on_notification::<ServerStatus, _>({
|
||||
let this = this.clone();
|
||||
|
@ -6448,6 +6364,10 @@ impl LspStore {
|
|||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
let disk_based_diagnostics_progress_token =
|
||||
adapter.disk_based_diagnostics_progress_token.clone();
|
||||
|
||||
language_server
|
||||
.on_notification::<lsp::notification::Progress, _>({
|
||||
let this = this.clone();
|
||||
|
@ -6502,36 +6422,6 @@ impl LspStore {
|
|||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
match (&mut initialization_options, override_options) {
|
||||
(Some(initialization_options), Some(override_options)) => {
|
||||
merge_json_value_into(override_options, initialization_options);
|
||||
}
|
||||
(None, override_options) => initialization_options = override_options,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let language_server = cx
|
||||
.update(|cx| language_server.initialize(initialization_options, cx))?
|
||||
.await
|
||||
.inspect_err(|_| {
|
||||
if let Some(this) = this.upgrade() {
|
||||
this.update(cx, |_, cx| {
|
||||
cx.emit(LspStoreEvent::LanguageServerRemoved(server_id))
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
})?;
|
||||
|
||||
language_server
|
||||
.notify::<lsp::notification::DidChangeConfiguration>(
|
||||
lsp::DidChangeConfigurationParams {
|
||||
settings: workspace_config,
|
||||
},
|
||||
)
|
||||
.ok();
|
||||
|
||||
Ok(language_server)
|
||||
}
|
||||
|
||||
pub fn update_diagnostics(
|
||||
|
@ -6664,7 +6554,7 @@ impl LspStore {
|
|||
server_id: LanguageServerId,
|
||||
key: (WorktreeId, LanguageServerName),
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Result<()> {
|
||||
) {
|
||||
// If the language server for this key doesn't match the server id, don't store the
|
||||
// server. Which will cause it to be dropped, killing the process
|
||||
if self
|
||||
|
@ -6673,7 +6563,7 @@ impl LspStore {
|
|||
.map(|id| id != &server_id)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return Ok(());
|
||||
return;
|
||||
}
|
||||
|
||||
// Update language_servers collection with Running variant of LanguageServerState
|
||||
|
@ -6703,13 +6593,15 @@ impl LspStore {
|
|||
cx.emit(LspStoreEvent::LanguageServerAdded(server_id));
|
||||
|
||||
if let Some((downstream_client, project_id)) = self.downstream_client.as_ref() {
|
||||
downstream_client.send(proto::StartLanguageServer {
|
||||
project_id: *project_id,
|
||||
server: Some(proto::LanguageServer {
|
||||
id: server_id.0 as u64,
|
||||
name: language_server.name().to_string(),
|
||||
}),
|
||||
})?;
|
||||
downstream_client
|
||||
.send(proto::StartLanguageServer {
|
||||
project_id: *project_id,
|
||||
server: Some(proto::LanguageServer {
|
||||
id: server_id.0 as u64,
|
||||
name: language_server.name().to_string(),
|
||||
}),
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
|
||||
// Tell the language server about every open buffer in the worktree that matches the language.
|
||||
|
@ -6756,16 +6648,18 @@ impl LspStore {
|
|||
let version = snapshot.version;
|
||||
let initial_snapshot = &snapshot.snapshot;
|
||||
let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
|
||||
language_server.notify::<lsp::notification::DidOpenTextDocument>(
|
||||
lsp::DidOpenTextDocumentParams {
|
||||
text_document: lsp::TextDocumentItem::new(
|
||||
uri,
|
||||
adapter.language_id(&language.name()),
|
||||
version,
|
||||
initial_snapshot.text(),
|
||||
),
|
||||
},
|
||||
)?;
|
||||
language_server
|
||||
.notify::<lsp::notification::DidOpenTextDocument>(
|
||||
lsp::DidOpenTextDocumentParams {
|
||||
text_document: lsp::TextDocumentItem::new(
|
||||
uri,
|
||||
adapter.language_id(&language.name()),
|
||||
version,
|
||||
initial_snapshot.text(),
|
||||
),
|
||||
},
|
||||
)
|
||||
.log_err();
|
||||
|
||||
buffer_handle.update(cx, |buffer, cx| {
|
||||
buffer.set_completion_triggers(
|
||||
|
@ -6779,11 +6673,9 @@ impl LspStore {
|
|||
)
|
||||
});
|
||||
}
|
||||
anyhow::Ok(())
|
||||
})?;
|
||||
});
|
||||
|
||||
cx.notify();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn buffer_snapshot_for_lsp_version(
|
||||
|
@ -6878,7 +6770,7 @@ impl LspStore {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn register_supplementary_language_server(
|
||||
fn register_supplementary_language_server(
|
||||
&mut self,
|
||||
id: LanguageServerId,
|
||||
name: LanguageServerName,
|
||||
|
@ -6893,7 +6785,7 @@ impl LspStore {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn unregister_supplementary_language_server(
|
||||
fn unregister_supplementary_language_server(
|
||||
&mut self,
|
||||
id: LanguageServerId,
|
||||
cx: &mut ModelContext<Self>,
|
||||
|
@ -7807,11 +7699,8 @@ impl LspAdapter for SshLspAdapter {
|
|||
) -> Result<LanguageServerBinary> {
|
||||
anyhow::bail!("SshLspAdapter does not support fetch_server_binary")
|
||||
}
|
||||
|
||||
async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn language_server_settings<'a, 'b: 'a>(
|
||||
delegate: &'a dyn LspAdapterDelegate,
|
||||
language: &LanguageServerName,
|
||||
|
@ -7855,22 +7744,6 @@ impl LocalLspAdapterDelegate {
|
|||
Self::new(lsp_store, worktree, http_client, local.fs.clone(), cx)
|
||||
}
|
||||
|
||||
// fn for_ssh(
|
||||
// lsp_store: &LspStore,
|
||||
// worktree: &Model<Worktree>,
|
||||
// upstream_client: AnyProtoClient,
|
||||
// cx: &mut ModelContext<LspStore>,
|
||||
// ) -> Arc<Self> {
|
||||
// Self::new(
|
||||
// lsp_store,
|
||||
// worktree,
|
||||
// Arc::new(BlockedHttpClient),
|
||||
// None,
|
||||
// Some(upstream_client),
|
||||
// cx,
|
||||
// )
|
||||
// }
|
||||
|
||||
pub fn new(
|
||||
lsp_store: &LspStore,
|
||||
worktree: &Model<Worktree>,
|
||||
|
@ -7972,6 +7845,19 @@ impl LspAdapterDelegate for LocalLspAdapterDelegate {
|
|||
.update_lsp_status(server_name, status);
|
||||
}
|
||||
|
||||
async fn language_server_download_dir(&self, name: &LanguageServerName) -> Option<Arc<Path>> {
|
||||
let dir = self.language_registry.language_server_download_dir(name)?;
|
||||
|
||||
if !dir.exists() {
|
||||
smol::fs::create_dir_all(&dir)
|
||||
.await
|
||||
.context("failed to create container directory")
|
||||
.log_err()?;
|
||||
}
|
||||
|
||||
Some(dir)
|
||||
}
|
||||
|
||||
async fn read_text_file(&self, path: PathBuf) -> Result<String> {
|
||||
if self.worktree.entry_for_path(&path).is_none() {
|
||||
return Err(anyhow!("no such path {path:?}"));
|
||||
|
@ -8056,6 +7942,10 @@ impl LspAdapterDelegate for SshLspAdapterDelegate {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn language_server_download_dir(&self, _: &LanguageServerName) -> Option<Arc<Path>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn update_status(
|
||||
&self,
|
||||
server_name: LanguageServerName,
|
||||
|
|
|
@ -3958,14 +3958,6 @@ impl Project {
|
|||
self.lsp_store.read(cx).supplementary_language_servers()
|
||||
}
|
||||
|
||||
pub fn language_server_adapter_for_id(
|
||||
&self,
|
||||
id: LanguageServerId,
|
||||
cx: &AppContext,
|
||||
) -> Option<Arc<CachedLspAdapter>> {
|
||||
self.lsp_store.read(cx).language_server_adapter_for_id(id)
|
||||
}
|
||||
|
||||
pub fn language_server_for_id(
|
||||
&self,
|
||||
id: LanguageServerId,
|
||||
|
|
|
@ -55,7 +55,7 @@ pub struct NodeBinarySettings {
|
|||
pub npm_path: Option<String>,
|
||||
/// If disabled, zed will download its own copy of node.
|
||||
#[serde(default)]
|
||||
pub disable_path_lookup: Option<bool>,
|
||||
pub ignore_system_version: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
|
@ -143,7 +143,7 @@ const fn true_value() -> bool {
|
|||
pub struct BinarySettings {
|
||||
pub path: Option<String>,
|
||||
pub arguments: Option<Vec<String>>,
|
||||
pub path_lookup: Option<bool>,
|
||||
pub ignore_system_version: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
|
|
|
@ -481,7 +481,7 @@ fn main() {
|
|||
cx.observe_global::<SettingsStore>(move |cx| {
|
||||
let settings = &ProjectSettings::get_global(cx).node;
|
||||
let options = NodeBinaryOptions {
|
||||
allow_path_lookup: !settings.disable_path_lookup.unwrap_or_default(),
|
||||
allow_path_lookup: !settings.ignore_system_version.unwrap_or_default(),
|
||||
// TODO: Expose this setting
|
||||
allow_binary_download: true,
|
||||
use_paths: settings.path.as_ref().map(|node_path| {
|
||||
|
|
|
@ -64,14 +64,14 @@ You can configure which `rust-analyzer` binary Zed should use.
|
|||
|
||||
By default, Zed will try to find a `rust-analyzer` in your `$PATH` and try to use that. If that binary successfully executes `rust-analyzer --help`, it's used. Otherwise, Zed will fall back to installing its own `rust-analyzer` version and using that.
|
||||
|
||||
If you want to disable Zed looking for a `rust-analyzer` binary, you can set `path_lookup` to `false` in your `settings.json`:
|
||||
If you want to disable Zed looking for a `rust-analyzer` binary, you can set `ignore_system_version` to `true` in your `settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"lsp": {
|
||||
"rust-analyzer": {
|
||||
"binary": {
|
||||
"path_lookup": false
|
||||
"ignore_system_version": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue