mirror of
https://github.com/encounter/zed.git
synced 2026-07-11 06:19:04 -07:00
Revert "project: Fine-grained language server management" (#23977)
Reverts zed-industries/zed#23805
This commit is contained in:
Generated
-1
@@ -9983,7 +9983,6 @@ dependencies = [
|
||||
"log",
|
||||
"lsp",
|
||||
"node_runtime",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"pathdiff",
|
||||
"paths",
|
||||
|
||||
+3
-4
@@ -375,7 +375,7 @@ async-tungstenite = "0.28"
|
||||
async-watch = "0.3.1"
|
||||
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
|
||||
base64 = "0.22"
|
||||
bitflags = "2.8.0"
|
||||
bitflags = "2.6.0"
|
||||
blade-graphics = { git = "https://github.com/kvark/blade", rev = "b16f5c7bd873c7126f48c82c39e7ae64602ae74f" }
|
||||
blade-macros = { git = "https://github.com/kvark/blade", rev = "b16f5c7bd873c7126f48c82c39e7ae64602ae74f" }
|
||||
blade-util = { git = "https://github.com/kvark/blade", rev = "b16f5c7bd873c7126f48c82c39e7ae64602ae74f" }
|
||||
@@ -425,13 +425,12 @@ libc = "0.2"
|
||||
libsqlite3-sys = { version = "0.30.1", features = ["bundled"] }
|
||||
linkify = "0.10.0"
|
||||
livekit = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev="060964da10574cd9bf06463a53bf6e0769c5c45e", features = ["dispatcher", "services-dispatcher", "rustls-tls-native-roots"], default-features = false }
|
||||
log = { version = "0.4.25", features = ["kv_unstable_serde", "serde"] }
|
||||
log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] }
|
||||
markup5ever_rcdom = "0.3.0"
|
||||
nanoid = "0.4"
|
||||
nbformat = { version = "0.10.0" }
|
||||
nix = "0.29"
|
||||
num-format = "0.4.4"
|
||||
once_cell = "1.20"
|
||||
ordered-float = "2.1.1"
|
||||
palette = { version = "0.7.5", default-features = false, features = ["std"] }
|
||||
parking_lot = "0.12.1"
|
||||
@@ -514,7 +513,7 @@ tree-sitter = { version = "0.23", features = ["wasm"] }
|
||||
tree-sitter-bash = "0.23"
|
||||
tree-sitter-c = "0.23"
|
||||
tree-sitter-cpp = "0.23"
|
||||
tree-sitter-css = "0.23.2"
|
||||
tree-sitter-css = "0.23"
|
||||
tree-sitter-elixir = "0.3"
|
||||
tree-sitter-embedded-template = "0.23.0"
|
||||
tree-sitter-go = "0.23"
|
||||
|
||||
@@ -53,7 +53,7 @@ reqwest_client.workspace = true
|
||||
rpc.workspace = true
|
||||
rustc-demangle.workspace = true
|
||||
scrypt = "0.11"
|
||||
sea-orm = { version = "1.1.4", features = ["sqlx-postgres", "postgres-array", "runtime-tokio-rustls", "with-uuid"] }
|
||||
sea-orm = { version = "1.1.0-rc.1", features = ["sqlx-postgres", "postgres-array", "runtime-tokio-rustls", "with-uuid"] }
|
||||
semantic_version.workspace = true
|
||||
semver.workspace = true
|
||||
serde.workspace = true
|
||||
@@ -116,7 +116,7 @@ release_channel.workspace = true
|
||||
remote = { workspace = true, features = ["test-support"] }
|
||||
remote_server.workspace = true
|
||||
rpc = { workspace = true, features = ["test-support"] }
|
||||
sea-orm = { version = "1.1.4", features = ["sqlx-sqlite"] }
|
||||
sea-orm = { version = "1.1.0-rc.1", features = ["sqlx-sqlite"] }
|
||||
serde_json.workspace = true
|
||||
session = { workspace = true, features = ["test-support"] }
|
||||
settings = { workspace = true, features = ["test-support"] }
|
||||
|
||||
@@ -458,14 +458,12 @@ impl Copilot {
|
||||
.on_notification::<StatusNotification, _>(|_, _| { /* Silence the notification */ })
|
||||
.detach();
|
||||
|
||||
let initialize_params = None;
|
||||
let configuration = lsp::DidChangeConfigurationParams {
|
||||
settings: Default::default(),
|
||||
};
|
||||
let server = cx
|
||||
.update(|cx| {
|
||||
let params = server.default_initialize_params(cx);
|
||||
server.initialize(params, configuration.into(), cx)
|
||||
})?
|
||||
.update(|cx| server.initialize(initialize_params, configuration.into(), cx))?
|
||||
.await?;
|
||||
|
||||
let status = server
|
||||
|
||||
+35
-45
@@ -13306,27 +13306,28 @@ impl Editor {
|
||||
cx.emit(SearchEvent::MatchesInvalidated);
|
||||
if *singleton_buffer_edited {
|
||||
if let Some(project) = &self.project {
|
||||
let project = project.read(cx);
|
||||
#[allow(clippy::mutable_key_type)]
|
||||
let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
|
||||
multibuffer
|
||||
.all_buffers()
|
||||
.into_iter()
|
||||
.filter_map(|buffer| {
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
let language = buffer.language()?;
|
||||
let should_discard = project.update(cx, |project, cx| {
|
||||
project.is_local()
|
||||
&& project.for_language_servers_for_local_buffer(
|
||||
buffer,
|
||||
|it| it.count() == 0,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
should_discard.not().then_some(language.clone())
|
||||
})
|
||||
})
|
||||
.collect::<HashSet<_>>()
|
||||
});
|
||||
let languages_affected = multibuffer
|
||||
.read(cx)
|
||||
.all_buffers()
|
||||
.into_iter()
|
||||
.filter_map(|buffer| {
|
||||
let buffer = buffer.read(cx);
|
||||
let language = buffer.language()?;
|
||||
if project.is_local()
|
||||
&& project
|
||||
.language_servers_for_local_buffer(buffer, cx)
|
||||
.count()
|
||||
== 0
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some(language)
|
||||
}
|
||||
})
|
||||
.cloned()
|
||||
.collect::<HashSet<_>>();
|
||||
if !languages_affected.is_empty() {
|
||||
self.refresh_inlay_hints(
|
||||
InlayHintRefreshReason::BufferEdited(languages_affected),
|
||||
@@ -13916,18 +13917,15 @@ impl Editor {
|
||||
self.handle_input(text, window, cx);
|
||||
}
|
||||
|
||||
pub fn supports_inlay_hints(&self, cx: &mut App) -> bool {
|
||||
pub fn supports_inlay_hints(&self, cx: &App) -> bool {
|
||||
let Some(provider) = self.semantics_provider.as_ref() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let mut supports = false;
|
||||
self.buffer().update(cx, |this, cx| {
|
||||
this.for_each_buffer(|buffer| {
|
||||
supports |= provider.supports_inlay_hints(buffer, cx);
|
||||
})
|
||||
self.buffer().read(cx).for_each_buffer(|buffer| {
|
||||
supports |= provider.supports_inlay_hints(buffer, cx);
|
||||
});
|
||||
|
||||
supports
|
||||
}
|
||||
pub fn is_focused(&self, window: &mut Window) -> bool {
|
||||
@@ -14477,7 +14475,7 @@ pub trait SemanticsProvider {
|
||||
cx: &mut App,
|
||||
) -> Option<Task<anyhow::Result<InlayHint>>>;
|
||||
|
||||
fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool;
|
||||
fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &App) -> bool;
|
||||
|
||||
fn document_highlights(
|
||||
&self,
|
||||
@@ -14868,25 +14866,17 @@ impl SemanticsProvider for Entity<Project> {
|
||||
}))
|
||||
}
|
||||
|
||||
fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
|
||||
fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &App) -> bool {
|
||||
// TODO: make this work for remote projects
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
self.update(cx, |this, cx| {
|
||||
this.for_language_servers_for_local_buffer(
|
||||
buffer,
|
||||
|mut it| {
|
||||
it.any(
|
||||
|(_, server)| match server.capabilities().inlay_hint_provider {
|
||||
Some(lsp::OneOf::Left(enabled)) => enabled,
|
||||
Some(lsp::OneOf::Right(_)) => true,
|
||||
None => false,
|
||||
},
|
||||
)
|
||||
},
|
||||
cx,
|
||||
)
|
||||
})
|
||||
})
|
||||
self.read(cx)
|
||||
.language_servers_for_local_buffer(buffer.read(cx), cx)
|
||||
.any(
|
||||
|(_, server)| match server.capabilities().inlay_hint_provider {
|
||||
Some(lsp::OneOf::Left(enabled)) => enabled,
|
||||
Some(lsp::OneOf::Right(_)) => true,
|
||||
None => false,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn inlay_hints(
|
||||
|
||||
@@ -11111,6 +11111,7 @@ async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::Test
|
||||
0,
|
||||
"Should not restart LSP server on an unrelated LSP settings change"
|
||||
);
|
||||
|
||||
update_test_project_settings(cx, |project_settings| {
|
||||
project_settings.lsp.insert(
|
||||
language_server_name.into(),
|
||||
|
||||
@@ -21,6 +21,7 @@ where
|
||||
let Some(project) = &editor.project else {
|
||||
return None;
|
||||
};
|
||||
let multibuffer = editor.buffer().read(cx);
|
||||
let mut language_servers_for = HashMap::default();
|
||||
editor
|
||||
.selections
|
||||
@@ -28,33 +29,29 @@ where
|
||||
.iter()
|
||||
.filter(|selection| selection.start == selection.end)
|
||||
.filter_map(|selection| Some((selection.start.buffer_id?, selection.start)))
|
||||
.find_map(|(buffer_id, trigger_anchor)| {
|
||||
let buffer = editor.buffer().read(cx).buffer(buffer_id)?;
|
||||
.filter_map(|(buffer_id, trigger_anchor)| {
|
||||
let buffer = multibuffer.buffer(buffer_id)?;
|
||||
let server_id = *match language_servers_for.entry(buffer_id) {
|
||||
Entry::Occupied(occupied_entry) => occupied_entry.into_mut(),
|
||||
Entry::Vacant(vacant_entry) => {
|
||||
let language_server_id = buffer.update(cx, |buffer, cx| {
|
||||
project.update(cx, |project, cx| {
|
||||
project.for_language_servers_for_local_buffer(
|
||||
buffer,
|
||||
|mut it| {
|
||||
it.find_map(|(adapter, server)| {
|
||||
if adapter.name.0.as_ref() == language_server_name {
|
||||
Some(server.server_id())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
},
|
||||
cx,
|
||||
)
|
||||
})
|
||||
});
|
||||
let language_server_id = project
|
||||
.read(cx)
|
||||
.language_servers_for_local_buffer(buffer.read(cx), cx)
|
||||
.find_map(|(adapter, server)| {
|
||||
if adapter.name.0.as_ref() == language_server_name {
|
||||
Some(server.server_id())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
vacant_entry.insert(language_server_id)
|
||||
}
|
||||
}
|
||||
.as_ref()?;
|
||||
|
||||
Some((buffer, trigger_anchor, server_id))
|
||||
})
|
||||
.find_map(|(buffer, trigger_anchor, server_id)| {
|
||||
let language = buffer.read(cx).language_at(trigger_anchor.text_anchor)?;
|
||||
if !filter_language(&language) {
|
||||
return None;
|
||||
|
||||
@@ -472,7 +472,7 @@ impl SemanticsProvider for BranchBufferSemanticsProvider {
|
||||
self.0.resolve_inlay_hint(hint, buffer, server_id, cx)
|
||||
}
|
||||
|
||||
fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &mut App) -> bool {
|
||||
fn supports_inlay_hints(&self, buffer: &Entity<Buffer>, cx: &App) -> bool {
|
||||
if let Some(buffer) = self.to_base(&buffer, &[], cx) {
|
||||
self.0.supports_inlay_hints(&buffer, cx)
|
||||
} else {
|
||||
|
||||
@@ -14,7 +14,7 @@ proc-macro = true
|
||||
doctest = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0.93"
|
||||
proc-macro2 = "1.0.66"
|
||||
quote = "1.0.9"
|
||||
syn = { version = "1.0.72", features = ["full", "extra-traits"] }
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_json::Value;
|
||||
use settings::WorktreeId;
|
||||
use smol::future::FutureExt as _;
|
||||
use std::num::NonZeroU32;
|
||||
use std::{
|
||||
any::Any,
|
||||
ffi::OsStr,
|
||||
@@ -61,7 +62,6 @@ use std::{
|
||||
Arc, LazyLock,
|
||||
},
|
||||
};
|
||||
use std::{num::NonZeroU32, sync::OnceLock};
|
||||
use syntax_map::{QueryCursorHandle, SyntaxSnapshot};
|
||||
use task::RunnableTag;
|
||||
pub use task_context::{ContextProvider, RunnableRange};
|
||||
@@ -164,7 +164,6 @@ pub struct CachedLspAdapter {
|
||||
pub adapter: Arc<dyn LspAdapter>,
|
||||
pub reinstall_attempt_count: AtomicU64,
|
||||
cached_binary: futures::lock::Mutex<Option<LanguageServerBinary>>,
|
||||
attach_kind: OnceLock<Attach>,
|
||||
}
|
||||
|
||||
impl Debug for CachedLspAdapter {
|
||||
@@ -200,7 +199,6 @@ impl CachedLspAdapter {
|
||||
adapter,
|
||||
cached_binary: Default::default(),
|
||||
reinstall_attempt_count: AtomicU64::new(0),
|
||||
attach_kind: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -262,38 +260,6 @@ impl CachedLspAdapter {
|
||||
.cloned()
|
||||
.unwrap_or_else(|| language_name.lsp_id())
|
||||
}
|
||||
pub fn find_project_root(
|
||||
&self,
|
||||
path: &Path,
|
||||
ancestor_depth: usize,
|
||||
delegate: &Arc<dyn LspAdapterDelegate>,
|
||||
) -> Option<Arc<Path>> {
|
||||
self.adapter
|
||||
.find_project_root(path, ancestor_depth, delegate)
|
||||
}
|
||||
pub fn attach_kind(&self) -> Attach {
|
||||
*self.attach_kind.get_or_init(|| self.adapter.attach_kind())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Attach {
|
||||
/// Create a single language server instance per subproject root.
|
||||
InstancePerRoot,
|
||||
/// Use one shared language server instance for all subprojects within a project.
|
||||
Shared,
|
||||
}
|
||||
|
||||
impl Attach {
|
||||
pub fn root_path(
|
||||
&self,
|
||||
root_subproject_path: (WorktreeId, Arc<Path>),
|
||||
) -> (WorktreeId, Arc<Path>) {
|
||||
match self {
|
||||
Attach::InstancePerRoot => root_subproject_path,
|
||||
Attach::Shared => (root_subproject_path.0, Arc::from(Path::new(""))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// [`LspAdapterDelegate`] allows [`LspAdapter]` implementations to interface with the application
|
||||
@@ -304,7 +270,6 @@ pub trait LspAdapterDelegate: Send + Sync {
|
||||
fn http_client(&self) -> Arc<dyn HttpClient>;
|
||||
fn worktree_id(&self) -> WorktreeId;
|
||||
fn worktree_root_path(&self) -> &Path;
|
||||
fn exists(&self, path: &Path, is_dir: Option<bool>) -> bool;
|
||||
fn update_status(&self, language: LanguageServerName, status: LanguageServerBinaryStatus);
|
||||
async fn language_server_download_dir(&self, name: &LanguageServerName) -> Option<Arc<Path>>;
|
||||
|
||||
@@ -543,19 +508,6 @@ pub trait LspAdapter: 'static + Send + Sync {
|
||||
fn prepare_initialize_params(&self, original: InitializeParams) -> Result<InitializeParams> {
|
||||
Ok(original)
|
||||
}
|
||||
fn attach_kind(&self) -> Attach {
|
||||
Attach::Shared
|
||||
}
|
||||
fn find_project_root(
|
||||
&self,
|
||||
|
||||
_path: &Path,
|
||||
_ancestor_depth: usize,
|
||||
_: &Arc<dyn LspAdapterDelegate>,
|
||||
) -> Option<Arc<Path>> {
|
||||
// By default all language servers are rooted at the root of the worktree.
|
||||
Some(Arc::from("".as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
async fn try_fetch_server_binary<L: LspAdapter + 'static + Send + Sync + ?Sized>(
|
||||
|
||||
@@ -108,7 +108,6 @@ struct LanguageRegistryState {
|
||||
available_languages: Vec<AvailableLanguage>,
|
||||
grammars: HashMap<Arc<str>, AvailableGrammar>,
|
||||
lsp_adapters: HashMap<LanguageName, Vec<Arc<CachedLspAdapter>>>,
|
||||
all_lsp_adapters: HashMap<LanguageServerName, Arc<CachedLspAdapter>>,
|
||||
available_lsp_adapters:
|
||||
HashMap<LanguageServerName, Arc<dyn Fn() -> Arc<CachedLspAdapter> + 'static + Send + Sync>>,
|
||||
loading_languages: HashMap<LanguageId, Vec<oneshot::Sender<Result<Arc<Language>>>>>,
|
||||
@@ -235,7 +234,6 @@ impl LanguageRegistry {
|
||||
language_settings: Default::default(),
|
||||
loading_languages: Default::default(),
|
||||
lsp_adapters: Default::default(),
|
||||
all_lsp_adapters: Default::default(),
|
||||
available_lsp_adapters: HashMap::default(),
|
||||
subscription: watch::channel(),
|
||||
theme: Default::default(),
|
||||
@@ -358,16 +356,12 @@ impl LanguageRegistry {
|
||||
adapter: Arc<dyn LspAdapter>,
|
||||
) -> Arc<CachedLspAdapter> {
|
||||
let cached = CachedLspAdapter::new(adapter);
|
||||
let mut state = self.state.write();
|
||||
state
|
||||
self.state
|
||||
.write()
|
||||
.lsp_adapters
|
||||
.entry(language_name)
|
||||
.or_default()
|
||||
.push(cached.clone());
|
||||
state
|
||||
.all_lsp_adapters
|
||||
.insert(cached.name.clone(), cached.clone());
|
||||
|
||||
cached
|
||||
}
|
||||
|
||||
@@ -407,17 +401,12 @@ impl LanguageRegistry {
|
||||
let adapter_name = LanguageServerName(adapter.name.into());
|
||||
let capabilities = adapter.capabilities.clone();
|
||||
let initializer = adapter.initializer.take();
|
||||
let adapter = CachedLspAdapter::new(Arc::new(adapter));
|
||||
{
|
||||
let mut state = self.state.write();
|
||||
state
|
||||
.lsp_adapters
|
||||
.entry(language_name.clone())
|
||||
.or_default()
|
||||
.push(adapter.clone());
|
||||
state.all_lsp_adapters.insert(adapter.name(), adapter);
|
||||
}
|
||||
|
||||
self.state
|
||||
.write()
|
||||
.lsp_adapters
|
||||
.entry(language_name.clone())
|
||||
.or_default()
|
||||
.push(CachedLspAdapter::new(Arc::new(adapter)));
|
||||
self.register_fake_language_server(adapter_name, capabilities, initializer)
|
||||
}
|
||||
|
||||
@@ -430,16 +419,12 @@ impl LanguageRegistry {
|
||||
adapter: crate::FakeLspAdapter,
|
||||
) {
|
||||
let language_name = language_name.into();
|
||||
let mut state = self.state.write();
|
||||
let cached_adapter = CachedLspAdapter::new(Arc::new(adapter));
|
||||
state
|
||||
self.state
|
||||
.write()
|
||||
.lsp_adapters
|
||||
.entry(language_name.clone())
|
||||
.or_default()
|
||||
.push(cached_adapter.clone());
|
||||
state
|
||||
.all_lsp_adapters
|
||||
.insert(cached_adapter.name(), cached_adapter);
|
||||
.push(CachedLspAdapter::new(Arc::new(adapter)));
|
||||
}
|
||||
|
||||
/// Register a fake language server (without the adapter)
|
||||
@@ -907,10 +892,6 @@ impl LanguageRegistry {
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn adapter_for_name(&self, name: &LanguageServerName) -> Option<Arc<CachedLspAdapter>> {
|
||||
self.state.read().all_lsp_adapters.get(name).cloned()
|
||||
}
|
||||
|
||||
pub fn update_lsp_status(
|
||||
&self,
|
||||
server_name: LanguageServerName,
|
||||
|
||||
@@ -735,8 +735,7 @@ impl LspLogView {
|
||||
|
||||
* Binary: {BINARY:#?}
|
||||
|
||||
* Registered workspace folders:
|
||||
{WORKSPACE_FOLDERS}
|
||||
* Running in project: {PATH:?}
|
||||
|
||||
* Capabilities: {CAPABILITIES}
|
||||
|
||||
@@ -744,15 +743,7 @@ impl LspLogView {
|
||||
NAME = server.name(),
|
||||
ID = server.server_id(),
|
||||
BINARY = server.binary(),
|
||||
WORKSPACE_FOLDERS = server
|
||||
.workspace_folders()
|
||||
.iter()
|
||||
.filter_map(|path| path
|
||||
.to_file_path()
|
||||
.ok()
|
||||
.map(|path| path.to_string_lossy().into_owned()))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
PATH = server.root_path(),
|
||||
CAPABILITIES = serde_json::to_string_pretty(&server.capabilities())
|
||||
.unwrap_or_else(|e| format!("Failed to serialize capabilities: {e}")),
|
||||
CONFIGURATION = serde_json::to_string_pretty(server.configuration())
|
||||
|
||||
@@ -74,23 +74,6 @@ impl LspAdapter for RustLspAdapter {
|
||||
Self::SERVER_NAME.clone()
|
||||
}
|
||||
|
||||
fn find_project_root(
|
||||
&self,
|
||||
path: &Path,
|
||||
ancestor_depth: usize,
|
||||
delegate: &Arc<dyn LspAdapterDelegate>,
|
||||
) -> Option<Arc<Path>> {
|
||||
let mut outermost_cargo_toml = None;
|
||||
for path in path.ancestors().take(ancestor_depth) {
|
||||
let p = path.join("Cargo.toml");
|
||||
if delegate.exists(&p, Some(false)) {
|
||||
outermost_cargo_toml = Some(Arc::from(path));
|
||||
}
|
||||
}
|
||||
|
||||
outermost_cargo_toml
|
||||
}
|
||||
|
||||
async fn check_if_user_installed(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
|
||||
+50
-137
@@ -4,10 +4,9 @@ pub use lsp_types::request::*;
|
||||
pub use lsp_types::*;
|
||||
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use collections::{HashMap, HashSet};
|
||||
use collections::HashMap;
|
||||
use futures::{channel::oneshot, io::BufWriter, select, AsyncRead, AsyncWrite, Future, FutureExt};
|
||||
use gpui::{App, AsyncApp, BackgroundExecutor, SharedString, Task};
|
||||
use notification::DidChangeWorkspaceFolders;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use postage::{barrier, prelude::Stream};
|
||||
use schemars::{
|
||||
@@ -24,11 +23,10 @@ use smol::{
|
||||
};
|
||||
|
||||
use std::{
|
||||
collections::BTreeSet,
|
||||
ffi::{OsStr, OsString},
|
||||
fmt,
|
||||
io::Write,
|
||||
ops::{Deref, DerefMut},
|
||||
ops::DerefMut,
|
||||
path::PathBuf,
|
||||
pin::Pin,
|
||||
sync::{
|
||||
@@ -98,9 +96,9 @@ pub struct LanguageServer {
|
||||
#[allow(clippy::type_complexity)]
|
||||
io_tasks: Mutex<Option<(Task<Option<()>>, Task<Option<()>>)>>,
|
||||
output_done_rx: Mutex<Option<barrier::Receiver>>,
|
||||
root_path: PathBuf,
|
||||
working_dir: PathBuf,
|
||||
server: Arc<Mutex<Option<Child>>>,
|
||||
workspace_folders: Arc<Mutex<BTreeSet<Url>>>,
|
||||
registered_buffers: Arc<Mutex<HashSet<Url>>>,
|
||||
}
|
||||
|
||||
/// Identifies a running language server.
|
||||
@@ -378,6 +376,8 @@ impl LanguageServer {
|
||||
Some(stderr),
|
||||
stderr_capture,
|
||||
Some(server),
|
||||
root_path,
|
||||
working_dir,
|
||||
code_action_kinds,
|
||||
binary,
|
||||
cx,
|
||||
@@ -403,6 +403,8 @@ impl LanguageServer {
|
||||
stderr: Option<Stderr>,
|
||||
stderr_capture: Arc<Mutex<Option<String>>>,
|
||||
server: Option<Child>,
|
||||
root_path: &Path,
|
||||
working_dir: &Path,
|
||||
code_action_kinds: Option<Vec<CodeActionKind>>,
|
||||
binary: LanguageServerBinary,
|
||||
cx: AsyncApp,
|
||||
@@ -486,9 +488,9 @@ impl LanguageServer {
|
||||
executor: cx.background_executor().clone(),
|
||||
io_tasks: Mutex::new(Some((input_task, output_task))),
|
||||
output_done_rx: Mutex::new(Some(output_done_rx)),
|
||||
root_path: root_path.to_path_buf(),
|
||||
working_dir: working_dir.to_path_buf(),
|
||||
server: Arc::new(Mutex::new(server)),
|
||||
workspace_folders: Default::default(),
|
||||
registered_buffers: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -613,11 +615,12 @@ impl LanguageServer {
|
||||
}
|
||||
|
||||
pub fn default_initialize_params(&self, cx: &App) -> InitializeParams {
|
||||
let root_uri = Url::from_file_path(&self.working_dir).unwrap();
|
||||
#[allow(deprecated)]
|
||||
InitializeParams {
|
||||
process_id: None,
|
||||
root_path: None,
|
||||
root_uri: None,
|
||||
root_uri: Some(root_uri.clone()),
|
||||
initialization_options: None,
|
||||
capabilities: ClientCapabilities {
|
||||
general: Some(GeneralClientCapabilities {
|
||||
@@ -784,7 +787,10 @@ impl LanguageServer {
|
||||
}),
|
||||
},
|
||||
trace: None,
|
||||
workspace_folders: None,
|
||||
workspace_folders: Some(vec![WorkspaceFolder {
|
||||
uri: root_uri,
|
||||
name: Default::default(),
|
||||
}]),
|
||||
client_info: release_channel::ReleaseChannel::try_global(cx).map(|release_channel| {
|
||||
ClientInfo {
|
||||
name: release_channel.display_name().to_string(),
|
||||
@@ -803,10 +809,16 @@ impl LanguageServer {
|
||||
/// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize)
|
||||
pub fn initialize(
|
||||
mut self,
|
||||
params: InitializeParams,
|
||||
initialize_params: Option<InitializeParams>,
|
||||
configuration: Arc<DidChangeConfigurationParams>,
|
||||
cx: &App,
|
||||
) -> Task<Result<Arc<Self>>> {
|
||||
let params = if let Some(params) = initialize_params {
|
||||
params
|
||||
} else {
|
||||
self.default_initialize_params(cx)
|
||||
};
|
||||
|
||||
cx.spawn(|_| async move {
|
||||
let response = self.request::<request::Initialize>(params).await?;
|
||||
if let Some(info) = response.server_info {
|
||||
@@ -1058,10 +1070,16 @@ impl LanguageServer {
|
||||
self.server_id
|
||||
}
|
||||
|
||||
/// Get the root path of the project the language server is running against.
|
||||
pub fn root_path(&self) -> &PathBuf {
|
||||
&self.root_path
|
||||
}
|
||||
|
||||
/// Language server's binary information.
|
||||
pub fn binary(&self) -> &LanguageServerBinary {
|
||||
&self.binary
|
||||
}
|
||||
|
||||
/// Sends a RPC request to the language server.
|
||||
///
|
||||
/// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#requestMessage)
|
||||
@@ -1189,125 +1207,6 @@ impl LanguageServer {
|
||||
outbound_tx.try_send(message)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add new workspace folder to the list.
|
||||
pub fn add_workspace_folder(&self, uri: Url) {
|
||||
if self
|
||||
.capabilities()
|
||||
.workspace
|
||||
.and_then(|ws| {
|
||||
ws.workspace_folders.and_then(|folders| {
|
||||
folders
|
||||
.change_notifications
|
||||
.map(|caps| matches!(caps, OneOf::Left(false)))
|
||||
})
|
||||
})
|
||||
.unwrap_or(true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let is_new_folder = self.workspace_folders.lock().insert(uri.clone());
|
||||
if is_new_folder {
|
||||
let params = DidChangeWorkspaceFoldersParams {
|
||||
event: WorkspaceFoldersChangeEvent {
|
||||
added: vec![WorkspaceFolder {
|
||||
uri,
|
||||
name: String::default(),
|
||||
}],
|
||||
removed: vec![],
|
||||
},
|
||||
};
|
||||
self.notify::<DidChangeWorkspaceFolders>(¶ms).log_err();
|
||||
}
|
||||
}
|
||||
/// Add new workspace folder to the list.
|
||||
pub fn remove_workspace_folder(&self, uri: Url) {
|
||||
if self
|
||||
.capabilities()
|
||||
.workspace
|
||||
.and_then(|ws| {
|
||||
ws.workspace_folders.and_then(|folders| {
|
||||
folders
|
||||
.change_notifications
|
||||
.map(|caps| !matches!(caps, OneOf::Left(false)))
|
||||
})
|
||||
})
|
||||
.unwrap_or(true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
let was_removed = self.workspace_folders.lock().remove(&uri);
|
||||
if was_removed {
|
||||
let params = DidChangeWorkspaceFoldersParams {
|
||||
event: WorkspaceFoldersChangeEvent {
|
||||
added: vec![],
|
||||
removed: vec![WorkspaceFolder {
|
||||
uri,
|
||||
name: String::default(),
|
||||
}],
|
||||
},
|
||||
};
|
||||
self.notify::<DidChangeWorkspaceFolders>(¶ms).log_err();
|
||||
}
|
||||
}
|
||||
pub fn set_workspace_folders(&self, folders: BTreeSet<Url>) {
|
||||
let mut workspace_folders = self.workspace_folders.lock();
|
||||
let added: Vec<_> = folders
|
||||
.iter()
|
||||
.map(|uri| WorkspaceFolder {
|
||||
uri: uri.clone(),
|
||||
name: String::default(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let removed: Vec<_> = std::mem::replace(&mut *workspace_folders, folders)
|
||||
.into_iter()
|
||||
.map(|uri| WorkspaceFolder {
|
||||
uri: uri.clone(),
|
||||
name: String::default(),
|
||||
})
|
||||
.collect();
|
||||
let should_notify = !added.is_empty() || !removed.is_empty();
|
||||
|
||||
if should_notify {
|
||||
let params = DidChangeWorkspaceFoldersParams {
|
||||
event: WorkspaceFoldersChangeEvent { added, removed },
|
||||
};
|
||||
self.notify::<DidChangeWorkspaceFolders>(¶ms).log_err();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn workspace_folders(&self) -> impl Deref<Target = BTreeSet<Url>> + '_ {
|
||||
self.workspace_folders.lock()
|
||||
}
|
||||
|
||||
pub fn register_buffer(
|
||||
&self,
|
||||
uri: Url,
|
||||
language_id: String,
|
||||
version: i32,
|
||||
initial_text: String,
|
||||
) {
|
||||
let was_already_registered = self.registered_buffers.lock().insert(uri.clone());
|
||||
|
||||
if was_already_registered {
|
||||
self.notify::<notification::DidOpenTextDocument>(&DidOpenTextDocumentParams {
|
||||
text_document: TextDocumentItem::new(uri, language_id, version, initial_text),
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unregister_buffer(&self, uri: &Url) {
|
||||
let was_removed = self.registered_buffers.lock().remove(uri);
|
||||
if was_removed {
|
||||
self.notify::<notification::DidCloseTextDocument>(&DidCloseTextDocumentParams {
|
||||
text_document: TextDocumentIdentifier::new(uri.clone()),
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for LanguageServer {
|
||||
@@ -1389,6 +1288,8 @@ impl FakeLanguageServer {
|
||||
let (stdout_writer, stdout_reader) = async_pipe::pipe();
|
||||
let (notifications_tx, notifications_rx) = channel::unbounded();
|
||||
|
||||
let root = Self::root_path();
|
||||
|
||||
let server_name = LanguageServerName(name.clone().into());
|
||||
let process_name = Arc::from(name.as_str());
|
||||
let mut server = LanguageServer::new_internal(
|
||||
@@ -1399,6 +1300,8 @@ impl FakeLanguageServer {
|
||||
None::<async_pipe::PipeReader>,
|
||||
Arc::new(Mutex::new(None)),
|
||||
None,
|
||||
root,
|
||||
root,
|
||||
None,
|
||||
binary.clone(),
|
||||
cx.clone(),
|
||||
@@ -1416,6 +1319,8 @@ impl FakeLanguageServer {
|
||||
None::<async_pipe::PipeReader>,
|
||||
Arc::new(Mutex::new(None)),
|
||||
None,
|
||||
root,
|
||||
root,
|
||||
None,
|
||||
binary,
|
||||
cx.clone(),
|
||||
@@ -1452,6 +1357,16 @@ impl FakeLanguageServer {
|
||||
|
||||
(server, fake)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn root_path() -> &'static Path {
|
||||
Path::new("C:\\")
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn root_path() -> &'static Path {
|
||||
Path::new("/")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
@@ -1639,14 +1554,12 @@ mod tests {
|
||||
})
|
||||
.detach();
|
||||
|
||||
let initialize_params = None;
|
||||
let configuration = DidChangeConfigurationParams {
|
||||
settings: Default::default(),
|
||||
};
|
||||
let server = cx
|
||||
.update(|cx| {
|
||||
let params = server.default_initialize_params(cx);
|
||||
let configuration = DidChangeConfigurationParams {
|
||||
settings: Default::default(),
|
||||
};
|
||||
server.initialize(params, configuration.into(), cx)
|
||||
})
|
||||
.update(|cx| server.initialize(initialize_params, configuration.into(), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
server
|
||||
|
||||
@@ -283,13 +283,13 @@ impl Prettier {
|
||||
)
|
||||
.context("prettier server creation")?;
|
||||
|
||||
let initialize_params = None;
|
||||
let configuration = lsp::DidChangeConfigurationParams {
|
||||
settings: Default::default(),
|
||||
};
|
||||
let server = cx
|
||||
.update(|cx| {
|
||||
let params = server.default_initialize_params(cx);
|
||||
let configuration = lsp::DidChangeConfigurationParams {
|
||||
settings: Default::default(),
|
||||
};
|
||||
executor.spawn(server.initialize(params, configuration.into(), cx))
|
||||
executor.spawn(server.initialize(initialize_params, configuration.into(), cx))
|
||||
})?
|
||||
.await
|
||||
.context("prettier server initialization")?;
|
||||
|
||||
@@ -43,7 +43,6 @@ log.workspace = true
|
||||
lsp.workspace = true
|
||||
node_runtime.workspace = true
|
||||
image.workspace = true
|
||||
once_cell.workspace = true
|
||||
parking_lot.workspace = true
|
||||
pathdiff.workspace = true
|
||||
paths.workspace = true
|
||||
|
||||
@@ -942,11 +942,9 @@ fn language_server_for_buffer(
|
||||
) -> Result<(Arc<CachedLspAdapter>, Arc<LanguageServer>)> {
|
||||
lsp_store
|
||||
.update(cx, |lsp_store, cx| {
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
lsp_store
|
||||
.language_server_for_local_buffer(buffer, server_id, cx)
|
||||
.map(|(adapter, server)| (adapter.clone(), server.clone()))
|
||||
})
|
||||
lsp_store
|
||||
.language_server_for_local_buffer(buffer.read(cx), server_id, cx)
|
||||
.map(|(adapter, server)| (adapter.clone(), server.clone()))
|
||||
})?
|
||||
.ok_or_else(|| anyhow!("no language server found for buffer"))
|
||||
}
|
||||
|
||||
+753
-881
File diff suppressed because it is too large
Load Diff
@@ -40,7 +40,7 @@ pub struct PrettierStore {
|
||||
prettier_instances: HashMap<PathBuf, PrettierInstance>,
|
||||
}
|
||||
|
||||
pub(crate) enum PrettierStoreEvent {
|
||||
pub enum PrettierStoreEvent {
|
||||
LanguageServerRemoved(LanguageServerId),
|
||||
LanguageServerAdded {
|
||||
new_server_id: LanguageServerId,
|
||||
|
||||
@@ -9,7 +9,6 @@ pub mod lsp_ext_command;
|
||||
pub mod lsp_store;
|
||||
pub mod prettier_store;
|
||||
pub mod project_settings;
|
||||
mod project_tree;
|
||||
pub mod search;
|
||||
mod task_inventory;
|
||||
pub mod task_store;
|
||||
@@ -478,7 +477,6 @@ pub struct DocumentHighlight {
|
||||
pub struct Symbol {
|
||||
pub language_server_name: LanguageServerName,
|
||||
pub source_worktree_id: WorktreeId,
|
||||
pub source_language_server_id: LanguageServerId,
|
||||
pub path: ProjectPath,
|
||||
pub label: CodeLabel,
|
||||
pub name: String,
|
||||
@@ -1931,7 +1929,7 @@ impl Project {
|
||||
pub fn open_buffer(
|
||||
&mut self,
|
||||
path: impl Into<ProjectPath>,
|
||||
cx: &mut App,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<Entity<Buffer>>> {
|
||||
if self.is_disconnected(cx) {
|
||||
return Task::ready(Err(anyhow!(ErrorCode::Disconnected)));
|
||||
@@ -1946,11 +1944,11 @@ impl Project {
|
||||
pub fn open_buffer_with_lsp(
|
||||
&mut self,
|
||||
path: impl Into<ProjectPath>,
|
||||
cx: &mut App,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<(Entity<Buffer>, lsp_store::OpenLspBufferHandle)>> {
|
||||
let buffer = self.open_buffer(path, cx);
|
||||
let lsp_store = self.lsp_store().clone();
|
||||
cx.spawn(|mut cx| async move {
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
let buffer = buffer.await?;
|
||||
let handle = lsp_store.update(&mut cx, |lsp_store, cx| {
|
||||
lsp_store.register_buffer_with_language_servers(&buffer, cx)
|
||||
@@ -4273,25 +4271,14 @@ impl Project {
|
||||
self.lsp_store.read(cx).supplementary_language_servers()
|
||||
}
|
||||
|
||||
pub fn language_server_for_id(
|
||||
&self,
|
||||
id: LanguageServerId,
|
||||
cx: &App,
|
||||
) -> Option<Arc<LanguageServer>> {
|
||||
self.lsp_store.read(cx).language_server_for_id(id)
|
||||
}
|
||||
|
||||
pub fn for_language_servers_for_local_buffer<R: 'static>(
|
||||
&self,
|
||||
buffer: &Buffer,
|
||||
callback: impl FnOnce(
|
||||
Box<dyn Iterator<Item = (&Arc<CachedLspAdapter>, &Arc<LanguageServer>)> + '_>,
|
||||
) -> R,
|
||||
cx: &mut App,
|
||||
) -> R {
|
||||
self.lsp_store.update(cx, |this, cx| {
|
||||
callback(Box::new(this.language_servers_for_local_buffer(buffer, cx)))
|
||||
})
|
||||
pub fn language_servers_for_local_buffer<'a>(
|
||||
&'a self,
|
||||
buffer: &'a Buffer,
|
||||
cx: &'a App,
|
||||
) -> impl Iterator<Item = (&'a Arc<CachedLspAdapter>, &'a Arc<LanguageServer>)> {
|
||||
self.lsp_store
|
||||
.read(cx)
|
||||
.language_servers_for_local_buffer(buffer, cx)
|
||||
}
|
||||
|
||||
pub fn buffer_store(&self) -> &Entity<BufferStore> {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user