Prevent creating extra language server instances if there already exists one for that workspace
This commit is contained in:
parent
2ee57c1512
commit
db05e32389
3 changed files with 391 additions and 306 deletions
|
@ -11,7 +11,7 @@ use serde_json::{json, value::RawValue, Value};
|
||||||
use smol::{
|
use smol::{
|
||||||
channel,
|
channel,
|
||||||
io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader},
|
io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader},
|
||||||
process,
|
process::{self, Child},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
future::Future,
|
future::Future,
|
||||||
|
@ -44,6 +44,7 @@ pub struct LanguageServer {
|
||||||
io_tasks: Mutex<Option<(Task<Option<()>>, Task<Option<()>>)>>,
|
io_tasks: Mutex<Option<(Task<Option<()>>, Task<Option<()>>)>>,
|
||||||
output_done_rx: Mutex<Option<barrier::Receiver>>,
|
output_done_rx: Mutex<Option<barrier::Receiver>>,
|
||||||
root_path: PathBuf,
|
root_path: PathBuf,
|
||||||
|
_server: Option<Child>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Subscription {
|
pub struct Subscription {
|
||||||
|
@ -118,11 +119,20 @@ impl LanguageServer {
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.stderr(Stdio::inherit())
|
.stderr(Stdio::inherit())
|
||||||
|
.kill_on_drop(true)
|
||||||
.spawn()?;
|
.spawn()?;
|
||||||
|
|
||||||
let stdin = server.stdin.take().unwrap();
|
let stdin = server.stdin.take().unwrap();
|
||||||
let stdout = server.stdout.take().unwrap();
|
let stout = server.stdout.take().unwrap();
|
||||||
let mut server =
|
|
||||||
Self::new_internal(server_id, stdin, stdout, root_path, cx, |notification| {
|
let mut server = Self::new_internal(
|
||||||
|
server_id,
|
||||||
|
stdin,
|
||||||
|
stout,
|
||||||
|
Some(server),
|
||||||
|
root_path,
|
||||||
|
cx,
|
||||||
|
|notification| {
|
||||||
log::info!(
|
log::info!(
|
||||||
"unhandled notification {}:\n{}",
|
"unhandled notification {}:\n{}",
|
||||||
notification.method,
|
notification.method,
|
||||||
|
@ -131,7 +141,8 @@ impl LanguageServer {
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
if let Some(name) = binary_path.file_name() {
|
if let Some(name) = binary_path.file_name() {
|
||||||
server.name = name.to_string_lossy().to_string();
|
server.name = name.to_string_lossy().to_string();
|
||||||
}
|
}
|
||||||
|
@ -142,6 +153,7 @@ impl LanguageServer {
|
||||||
server_id: usize,
|
server_id: usize,
|
||||||
stdin: Stdin,
|
stdin: Stdin,
|
||||||
stdout: Stdout,
|
stdout: Stdout,
|
||||||
|
server: Option<Child>,
|
||||||
root_path: &Path,
|
root_path: &Path,
|
||||||
cx: AsyncAppContext,
|
cx: AsyncAppContext,
|
||||||
mut on_unhandled_notification: F,
|
mut on_unhandled_notification: F,
|
||||||
|
@ -242,6 +254,7 @@ impl LanguageServer {
|
||||||
io_tasks: Mutex::new(Some((input_task, output_task))),
|
io_tasks: Mutex::new(Some((input_task, output_task))),
|
||||||
output_done_rx: Mutex::new(Some(output_done_rx)),
|
output_done_rx: Mutex::new(Some(output_done_rx)),
|
||||||
root_path: root_path.to_path_buf(),
|
root_path: root_path.to_path_buf(),
|
||||||
|
_server: server,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -608,6 +621,7 @@ impl LanguageServer {
|
||||||
0,
|
0,
|
||||||
stdin_writer,
|
stdin_writer,
|
||||||
stdout_reader,
|
stdout_reader,
|
||||||
|
None,
|
||||||
Path::new("/"),
|
Path::new("/"),
|
||||||
cx.clone(),
|
cx.clone(),
|
||||||
|_| {},
|
|_| {},
|
||||||
|
@ -617,6 +631,7 @@ impl LanguageServer {
|
||||||
0,
|
0,
|
||||||
stdout_writer,
|
stdout_writer,
|
||||||
stdin_reader,
|
stdin_reader,
|
||||||
|
None,
|
||||||
Path::new("/"),
|
Path::new("/"),
|
||||||
cx.clone(),
|
cx.clone(),
|
||||||
move |msg| {
|
move |msg| {
|
||||||
|
|
|
@ -242,7 +242,7 @@ impl LspCommand for PerformRename {
|
||||||
.read_with(&cx, |project, cx| {
|
.read_with(&cx, |project, cx| {
|
||||||
project
|
project
|
||||||
.language_server_for_buffer(buffer.read(cx), cx)
|
.language_server_for_buffer(buffer.read(cx), cx)
|
||||||
.cloned()
|
.map(|(adapter, server)| (adapter.clone(), server.clone()))
|
||||||
})
|
})
|
||||||
.ok_or_else(|| anyhow!("no language server found for buffer"))?;
|
.ok_or_else(|| anyhow!("no language server found for buffer"))?;
|
||||||
Project::deserialize_workspace_edit(
|
Project::deserialize_workspace_edit(
|
||||||
|
@ -359,7 +359,7 @@ impl LspCommand for GetDefinition {
|
||||||
.read_with(&cx, |project, cx| {
|
.read_with(&cx, |project, cx| {
|
||||||
project
|
project
|
||||||
.language_server_for_buffer(buffer.read(cx), cx)
|
.language_server_for_buffer(buffer.read(cx), cx)
|
||||||
.cloned()
|
.map(|(adapter, server)| (adapter.clone(), server.clone()))
|
||||||
})
|
})
|
||||||
.ok_or_else(|| anyhow!("no language server found for buffer"))?;
|
.ok_or_else(|| anyhow!("no language server found for buffer"))?;
|
||||||
|
|
||||||
|
@ -388,8 +388,8 @@ impl LspCommand for GetDefinition {
|
||||||
.update(&mut cx, |this, cx| {
|
.update(&mut cx, |this, cx| {
|
||||||
this.open_local_buffer_via_lsp(
|
this.open_local_buffer_via_lsp(
|
||||||
target_uri,
|
target_uri,
|
||||||
lsp_adapter.clone(),
|
language_server.server_id(),
|
||||||
language_server.clone(),
|
lsp_adapter.name(),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -599,7 +599,7 @@ impl LspCommand for GetReferences {
|
||||||
.read_with(&cx, |project, cx| {
|
.read_with(&cx, |project, cx| {
|
||||||
project
|
project
|
||||||
.language_server_for_buffer(buffer.read(cx), cx)
|
.language_server_for_buffer(buffer.read(cx), cx)
|
||||||
.cloned()
|
.map(|(adapter, server)| (adapter.clone(), server.clone()))
|
||||||
})
|
})
|
||||||
.ok_or_else(|| anyhow!("no language server found for buffer"))?;
|
.ok_or_else(|| anyhow!("no language server found for buffer"))?;
|
||||||
|
|
||||||
|
@ -609,8 +609,8 @@ impl LspCommand for GetReferences {
|
||||||
.update(&mut cx, |this, cx| {
|
.update(&mut cx, |this, cx| {
|
||||||
this.open_local_buffer_via_lsp(
|
this.open_local_buffer_via_lsp(
|
||||||
lsp_location.uri,
|
lsp_location.uri,
|
||||||
lsp_adapter.clone(),
|
language_server.server_id(),
|
||||||
language_server.clone(),
|
lsp_adapter.name(),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -65,6 +65,14 @@ pub trait Item: Entity {
|
||||||
fn entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId>;
|
fn entry_id(&self, cx: &AppContext) -> Option<ProjectEntryId>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum LanguageServerState {
|
||||||
|
Starting(Task<Option<Arc<LanguageServer>>>),
|
||||||
|
Running {
|
||||||
|
adapter: Arc<dyn LspAdapter>,
|
||||||
|
server: Arc<LanguageServer>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ProjectStore {
|
pub struct ProjectStore {
|
||||||
db: Arc<Db>,
|
db: Arc<Db>,
|
||||||
projects: Vec<WeakModelHandle<Project>>,
|
projects: Vec<WeakModelHandle<Project>>,
|
||||||
|
@ -74,10 +82,8 @@ pub struct Project {
|
||||||
worktrees: Vec<WorktreeHandle>,
|
worktrees: Vec<WorktreeHandle>,
|
||||||
active_entry: Option<ProjectEntryId>,
|
active_entry: Option<ProjectEntryId>,
|
||||||
languages: Arc<LanguageRegistry>,
|
languages: Arc<LanguageRegistry>,
|
||||||
language_servers:
|
language_servers: HashMap<usize, LanguageServerState>,
|
||||||
HashMap<(WorktreeId, LanguageServerName), (Arc<dyn LspAdapter>, Arc<LanguageServer>)>,
|
language_server_ids: HashMap<(WorktreeId, LanguageServerName), usize>,
|
||||||
started_language_servers:
|
|
||||||
HashMap<(WorktreeId, LanguageServerName), Task<Option<Arc<LanguageServer>>>>,
|
|
||||||
language_server_statuses: BTreeMap<usize, LanguageServerStatus>,
|
language_server_statuses: BTreeMap<usize, LanguageServerStatus>,
|
||||||
language_server_settings: Arc<Mutex<serde_json::Value>>,
|
language_server_settings: Arc<Mutex<serde_json::Value>>,
|
||||||
last_workspace_edits_by_language_server: HashMap<usize, ProjectTransaction>,
|
last_workspace_edits_by_language_server: HashMap<usize, ProjectTransaction>,
|
||||||
|
@ -179,7 +185,7 @@ pub struct LanguageServerStatus {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub pending_work: BTreeMap<String, LanguageServerProgress>,
|
pub pending_work: BTreeMap<String, LanguageServerProgress>,
|
||||||
pub has_pending_diagnostic_updates: bool,
|
pub has_pending_diagnostic_updates: bool,
|
||||||
progress_tokens: HashSet<String>,
|
pub progress_tokens: HashSet<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
|
@ -437,7 +443,7 @@ impl Project {
|
||||||
next_entry_id: Default::default(),
|
next_entry_id: Default::default(),
|
||||||
next_diagnostic_group_id: Default::default(),
|
next_diagnostic_group_id: Default::default(),
|
||||||
language_servers: Default::default(),
|
language_servers: Default::default(),
|
||||||
started_language_servers: Default::default(),
|
language_server_ids: Default::default(),
|
||||||
language_server_statuses: Default::default(),
|
language_server_statuses: Default::default(),
|
||||||
last_workspace_edits_by_language_server: Default::default(),
|
last_workspace_edits_by_language_server: Default::default(),
|
||||||
language_server_settings: Default::default(),
|
language_server_settings: Default::default(),
|
||||||
|
@ -536,7 +542,7 @@ impl Project {
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
language_servers: Default::default(),
|
language_servers: Default::default(),
|
||||||
started_language_servers: Default::default(),
|
language_server_ids: Default::default(),
|
||||||
language_server_settings: Default::default(),
|
language_server_settings: Default::default(),
|
||||||
language_server_statuses: response
|
language_server_statuses: response
|
||||||
.language_servers
|
.language_servers
|
||||||
|
@ -691,7 +697,7 @@ impl Project {
|
||||||
if let Some(lsp_adapter) = language.lsp_adapter() {
|
if let Some(lsp_adapter) = language.lsp_adapter() {
|
||||||
if !settings.enable_language_server(Some(&language.name())) {
|
if !settings.enable_language_server(Some(&language.name())) {
|
||||||
let lsp_name = lsp_adapter.name();
|
let lsp_name = lsp_adapter.name();
|
||||||
for (worktree_id, started_lsp_name) in self.started_language_servers.keys() {
|
for (worktree_id, started_lsp_name) in self.language_server_ids.keys() {
|
||||||
if lsp_name == *started_lsp_name {
|
if lsp_name == *started_lsp_name {
|
||||||
language_servers_to_stop.push((*worktree_id, started_lsp_name.clone()));
|
language_servers_to_stop.push((*worktree_id, started_lsp_name.clone()));
|
||||||
}
|
}
|
||||||
|
@ -1538,8 +1544,8 @@ impl Project {
|
||||||
fn open_local_buffer_via_lsp(
|
fn open_local_buffer_via_lsp(
|
||||||
&mut self,
|
&mut self,
|
||||||
abs_path: lsp::Url,
|
abs_path: lsp::Url,
|
||||||
lsp_adapter: Arc<dyn LspAdapter>,
|
language_server_id: usize,
|
||||||
lsp_server: Arc<LanguageServer>,
|
language_server_name: LanguageServerName,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Task<Result<ModelHandle<Buffer>>> {
|
) -> Task<Result<ModelHandle<Buffer>>> {
|
||||||
cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
|
@ -1557,9 +1563,9 @@ impl Project {
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.language_servers.insert(
|
this.language_server_ids.insert(
|
||||||
(worktree.read(cx).id(), lsp_adapter.name()),
|
(worktree.read(cx).id(), language_server_name),
|
||||||
(lsp_adapter, lsp_server),
|
language_server_id,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
(worktree, PathBuf::new())
|
(worktree, PathBuf::new())
|
||||||
|
@ -1726,9 +1732,16 @@ impl Project {
|
||||||
if let Some(adapter) = language.lsp_adapter() {
|
if let Some(adapter) = language.lsp_adapter() {
|
||||||
language_id = adapter.id_for_language(language.name().as_ref());
|
language_id = adapter.id_for_language(language.name().as_ref());
|
||||||
language_server = self
|
language_server = self
|
||||||
.language_servers
|
.language_server_ids
|
||||||
.get(&(worktree_id, adapter.name()))
|
.get(&(worktree_id, adapter.name()))
|
||||||
.cloned();
|
.and_then(|id| self.language_servers.get(&id))
|
||||||
|
.and_then(|server_state| {
|
||||||
|
if let LanguageServerState::Running { server, .. } = server_state {
|
||||||
|
Some(server.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1739,7 +1752,7 @@ impl Project {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((_, server)) = language_server {
|
if let Some(server) = language_server {
|
||||||
server
|
server
|
||||||
.notify::<lsp::notification::DidOpenTextDocument>(
|
.notify::<lsp::notification::DidOpenTextDocument>(
|
||||||
lsp::DidOpenTextDocumentParams {
|
lsp::DidOpenTextDocumentParams {
|
||||||
|
@ -1816,9 +1829,9 @@ impl Project {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BufferEvent::Edited { .. } => {
|
BufferEvent::Edited { .. } => {
|
||||||
let (_, language_server) = self
|
let language_server = self
|
||||||
.language_server_for_buffer(buffer.read(cx), cx)?
|
.language_server_for_buffer(buffer.read(cx), cx)
|
||||||
.clone();
|
.map(|(_, server)| server.clone())?;
|
||||||
let buffer = buffer.read(cx);
|
let buffer = buffer.read(cx);
|
||||||
let file = File::from_dyn(buffer.file())?;
|
let file = File::from_dyn(buffer.file())?;
|
||||||
let abs_path = file.as_local()?.abs_path(cx);
|
let abs_path = file.as_local()?.abs_path(cx);
|
||||||
|
@ -1907,16 +1920,19 @@ impl Project {
|
||||||
fn language_servers_for_worktree(
|
fn language_servers_for_worktree(
|
||||||
&self,
|
&self,
|
||||||
worktree_id: WorktreeId,
|
worktree_id: WorktreeId,
|
||||||
) -> impl Iterator<Item = &(Arc<dyn LspAdapter>, Arc<LanguageServer>)> {
|
) -> impl Iterator<Item = (&Arc<dyn LspAdapter>, &Arc<LanguageServer>)> {
|
||||||
self.language_servers.iter().filter_map(
|
self.language_server_ids
|
||||||
move |((language_server_worktree_id, _), server)| {
|
.iter()
|
||||||
|
.filter_map(move |((language_server_worktree_id, _), id)| {
|
||||||
if *language_server_worktree_id == worktree_id {
|
if *language_server_worktree_id == worktree_id {
|
||||||
Some(server)
|
if let Some(LanguageServerState::Running { adapter, server }) =
|
||||||
} else {
|
self.language_servers.get(&id)
|
||||||
None
|
{
|
||||||
|
return Some((adapter, server));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
None
|
||||||
)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assign_language_to_buffer(
|
fn assign_language_to_buffer(
|
||||||
|
@ -1960,7 +1976,8 @@ impl Project {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let key = (worktree_id, adapter.name());
|
let key = (worktree_id, adapter.name());
|
||||||
self.started_language_servers
|
|
||||||
|
self.language_server_ids
|
||||||
.entry(key.clone())
|
.entry(key.clone())
|
||||||
.or_insert_with(|| {
|
.or_insert_with(|| {
|
||||||
let server_id = post_inc(&mut self.next_language_server_id);
|
let server_id = post_inc(&mut self.next_language_server_id);
|
||||||
|
@ -1971,218 +1988,240 @@ impl Project {
|
||||||
self.client.http_client(),
|
self.client.http_client(),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
cx.spawn_weak(|this, mut cx| async move {
|
self.language_servers.insert(
|
||||||
let language_server = language_server?.await.log_err()?;
|
server_id,
|
||||||
let language_server = language_server
|
LanguageServerState::Starting(cx.spawn_weak(|this, mut cx| async move {
|
||||||
.initialize(adapter.initialization_options())
|
let language_server = language_server?.await.log_err()?;
|
||||||
.await
|
let language_server = language_server
|
||||||
.log_err()?;
|
.initialize(adapter.initialization_options())
|
||||||
let this = this.upgrade(&cx)?;
|
.await
|
||||||
let disk_based_diagnostics_progress_token =
|
.log_err()?;
|
||||||
adapter.disk_based_diagnostics_progress_token();
|
let this = this.upgrade(&cx)?;
|
||||||
|
let disk_based_diagnostics_progress_token =
|
||||||
|
adapter.disk_based_diagnostics_progress_token();
|
||||||
|
|
||||||
language_server
|
|
||||||
.on_notification::<lsp::notification::PublishDiagnostics, _>({
|
|
||||||
let this = this.downgrade();
|
|
||||||
let adapter = adapter.clone();
|
|
||||||
move |params, mut cx| {
|
|
||||||
if let Some(this) = this.upgrade(&cx) {
|
|
||||||
this.update(&mut cx, |this, cx| {
|
|
||||||
this.on_lsp_diagnostics_published(
|
|
||||||
server_id, params, &adapter, cx,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
|
|
||||||
language_server
|
|
||||||
.on_request::<lsp::request::WorkspaceConfiguration, _, _>({
|
|
||||||
let settings = this
|
|
||||||
.read_with(&cx, |this, _| this.language_server_settings.clone());
|
|
||||||
move |params, _| {
|
|
||||||
let settings = settings.lock().clone();
|
|
||||||
async move {
|
|
||||||
Ok(params
|
|
||||||
.items
|
|
||||||
.into_iter()
|
|
||||||
.map(|item| {
|
|
||||||
if let Some(section) = &item.section {
|
|
||||||
settings
|
|
||||||
.get(section)
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or(serde_json::Value::Null)
|
|
||||||
} else {
|
|
||||||
settings.clone()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
|
|
||||||
// Even though we don't have handling for these requests, respond to them to
|
|
||||||
// avoid stalling any language server like `gopls` which waits for a response
|
|
||||||
// to these requests when initializing.
|
|
||||||
language_server
|
|
||||||
.on_request::<lsp::request::WorkDoneProgressCreate, _, _>({
|
|
||||||
let this = this.downgrade();
|
|
||||||
move |params, mut cx| async move {
|
|
||||||
if let Some(this) = this.upgrade(&cx) {
|
|
||||||
this.update(&mut cx, |this, _| {
|
|
||||||
if let Some(status) =
|
|
||||||
this.language_server_statuses.get_mut(&server_id)
|
|
||||||
{
|
|
||||||
if let lsp::NumberOrString::String(token) = params.token
|
|
||||||
{
|
|
||||||
status.progress_tokens.insert(token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
language_server
|
|
||||||
.on_request::<lsp::request::RegisterCapability, _, _>(|_, _| async {
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
|
|
||||||
language_server
|
|
||||||
.on_request::<lsp::request::ApplyWorkspaceEdit, _, _>({
|
|
||||||
let this = this.downgrade();
|
|
||||||
let adapter = adapter.clone();
|
|
||||||
let language_server = language_server.clone();
|
|
||||||
move |params, cx| {
|
|
||||||
Self::on_lsp_workspace_edit(
|
|
||||||
this,
|
|
||||||
params,
|
|
||||||
server_id,
|
|
||||||
adapter.clone(),
|
|
||||||
language_server.clone(),
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
|
|
||||||
language_server
|
|
||||||
.on_notification::<lsp::notification::Progress, _>({
|
|
||||||
let this = this.downgrade();
|
|
||||||
move |params, mut cx| {
|
|
||||||
if let Some(this) = this.upgrade(&cx) {
|
|
||||||
this.update(&mut cx, |this, cx| {
|
|
||||||
this.on_lsp_progress(
|
|
||||||
params,
|
|
||||||
server_id,
|
|
||||||
disk_based_diagnostics_progress_token,
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
|
|
||||||
this.update(&mut cx, |this, cx| {
|
|
||||||
this.language_servers
|
|
||||||
.insert(key.clone(), (adapter.clone(), language_server.clone()));
|
|
||||||
this.language_server_statuses.insert(
|
|
||||||
server_id,
|
|
||||||
LanguageServerStatus {
|
|
||||||
name: language_server.name().to_string(),
|
|
||||||
pending_work: Default::default(),
|
|
||||||
has_pending_diagnostic_updates: false,
|
|
||||||
progress_tokens: Default::default(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
language_server
|
language_server
|
||||||
.notify::<lsp::notification::DidChangeConfiguration>(
|
.on_notification::<lsp::notification::PublishDiagnostics, _>({
|
||||||
lsp::DidChangeConfigurationParams {
|
let this = this.downgrade();
|
||||||
settings: this.language_server_settings.lock().clone(),
|
let adapter = adapter.clone();
|
||||||
},
|
move |params, mut cx| {
|
||||||
)
|
if let Some(this) = this.upgrade(&cx) {
|
||||||
.ok();
|
this.update(&mut cx, |this, cx| {
|
||||||
|
this.on_lsp_diagnostics_published(
|
||||||
if let Some(project_id) = this.shared_remote_id() {
|
server_id, params, &adapter, cx,
|
||||||
this.client
|
);
|
||||||
.send(proto::StartLanguageServer {
|
});
|
||||||
project_id,
|
}
|
||||||
server: Some(proto::LanguageServer {
|
|
||||||
id: server_id 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.
|
|
||||||
for buffer in this.opened_buffers.values() {
|
|
||||||
if let Some(buffer_handle) = buffer.upgrade(cx) {
|
|
||||||
let buffer = buffer_handle.read(cx);
|
|
||||||
let file = if let Some(file) = File::from_dyn(buffer.file()) {
|
|
||||||
file
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let language = if let Some(language) = buffer.language() {
|
|
||||||
language
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
if file.worktree.read(cx).id() != key.0
|
|
||||||
|| language.lsp_adapter().map(|a| a.name())
|
|
||||||
!= Some(key.1.clone())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
let file = file.as_local()?;
|
language_server
|
||||||
let versions = this
|
.on_request::<lsp::request::WorkspaceConfiguration, _, _>({
|
||||||
.buffer_snapshots
|
let settings = this.read_with(&cx, |this, _| {
|
||||||
.entry(buffer.remote_id())
|
this.language_server_settings.clone()
|
||||||
.or_insert_with(|| vec![(0, buffer.text_snapshot())]);
|
});
|
||||||
let (version, initial_snapshot) = versions.last().unwrap();
|
move |params, _| {
|
||||||
let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
|
let settings = settings.lock().clone();
|
||||||
let language_id = adapter.id_for_language(language.name().as_ref());
|
async move {
|
||||||
language_server
|
Ok(params
|
||||||
.notify::<lsp::notification::DidOpenTextDocument>(
|
.items
|
||||||
lsp::DidOpenTextDocumentParams {
|
.into_iter()
|
||||||
text_document: lsp::TextDocumentItem::new(
|
.map(|item| {
|
||||||
uri,
|
if let Some(section) = &item.section {
|
||||||
language_id.unwrap_or_default(),
|
settings
|
||||||
*version,
|
.get(section)
|
||||||
initial_snapshot.text(),
|
.cloned()
|
||||||
),
|
.unwrap_or(serde_json::Value::Null)
|
||||||
},
|
} else {
|
||||||
)
|
settings.clone()
|
||||||
.log_err()?;
|
}
|
||||||
buffer_handle.update(cx, |buffer, cx| {
|
|
||||||
buffer.set_completion_triggers(
|
|
||||||
language_server
|
|
||||||
.capabilities()
|
|
||||||
.completion_provider
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|provider| {
|
|
||||||
provider.trigger_characters.clone()
|
|
||||||
})
|
})
|
||||||
.unwrap_or(Vec::new()),
|
.collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
// Even though we don't have handling for these requests, respond to them to
|
||||||
|
// avoid stalling any language server like `gopls` which waits for a response
|
||||||
|
// to these requests when initializing.
|
||||||
|
language_server
|
||||||
|
.on_request::<lsp::request::WorkDoneProgressCreate, _, _>({
|
||||||
|
let this = this.downgrade();
|
||||||
|
move |params, mut cx| async move {
|
||||||
|
if let Some(this) = this.upgrade(&cx) {
|
||||||
|
this.update(&mut cx, |this, _| {
|
||||||
|
if let Some(status) =
|
||||||
|
this.language_server_statuses.get_mut(&server_id)
|
||||||
|
{
|
||||||
|
if let lsp::NumberOrString::String(token) =
|
||||||
|
params.token
|
||||||
|
{
|
||||||
|
status.progress_tokens.insert(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
language_server
|
||||||
|
.on_request::<lsp::request::RegisterCapability, _, _>(|_, _| async {
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
language_server
|
||||||
|
.on_request::<lsp::request::ApplyWorkspaceEdit, _, _>({
|
||||||
|
let this = this.downgrade();
|
||||||
|
let adapter = adapter.clone();
|
||||||
|
let language_server = language_server.clone();
|
||||||
|
move |params, cx| {
|
||||||
|
Self::on_lsp_workspace_edit(
|
||||||
|
this,
|
||||||
|
params,
|
||||||
|
server_id,
|
||||||
|
adapter.clone(),
|
||||||
|
language_server.clone(),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
language_server
|
||||||
|
.on_notification::<lsp::notification::Progress, _>({
|
||||||
|
let this = this.downgrade();
|
||||||
|
move |params, mut cx| {
|
||||||
|
if let Some(this) = this.upgrade(&cx) {
|
||||||
|
this.update(&mut cx, |this, cx| {
|
||||||
|
this.on_lsp_progress(
|
||||||
|
params,
|
||||||
|
server_id,
|
||||||
|
disk_based_diagnostics_progress_token,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
this.update(&mut cx, |this, cx| {
|
||||||
|
// If the language server for this key doesn't match the server id, don't store the
|
||||||
|
// server.
|
||||||
|
if this
|
||||||
|
.language_server_ids
|
||||||
|
.get(&key)
|
||||||
|
.map(|id| id != &server_id)
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
cx.notify();
|
this.language_servers.insert(
|
||||||
Some(())
|
server_id,
|
||||||
});
|
LanguageServerState::Running {
|
||||||
|
adapter: adapter.clone(),
|
||||||
|
server: language_server.clone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
this.language_server_statuses.insert(
|
||||||
|
server_id,
|
||||||
|
LanguageServerStatus {
|
||||||
|
name: language_server.name().to_string(),
|
||||||
|
pending_work: Default::default(),
|
||||||
|
has_pending_diagnostic_updates: false,
|
||||||
|
progress_tokens: Default::default(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
language_server
|
||||||
|
.notify::<lsp::notification::DidChangeConfiguration>(
|
||||||
|
lsp::DidChangeConfigurationParams {
|
||||||
|
settings: this.language_server_settings.lock().clone(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
|
||||||
Some(language_server)
|
if let Some(project_id) = this.shared_remote_id() {
|
||||||
})
|
this.client
|
||||||
|
.send(proto::StartLanguageServer {
|
||||||
|
project_id,
|
||||||
|
server: Some(proto::LanguageServer {
|
||||||
|
id: server_id 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.
|
||||||
|
for buffer in this.opened_buffers.values() {
|
||||||
|
if let Some(buffer_handle) = buffer.upgrade(cx) {
|
||||||
|
let buffer = buffer_handle.read(cx);
|
||||||
|
let file = if let Some(file) = File::from_dyn(buffer.file()) {
|
||||||
|
file
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let language = if let Some(language) = buffer.language() {
|
||||||
|
language
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if file.worktree.read(cx).id() != key.0
|
||||||
|
|| language.lsp_adapter().map(|a| a.name())
|
||||||
|
!= Some(key.1.clone())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = file.as_local()?;
|
||||||
|
let versions = this
|
||||||
|
.buffer_snapshots
|
||||||
|
.entry(buffer.remote_id())
|
||||||
|
.or_insert_with(|| vec![(0, buffer.text_snapshot())]);
|
||||||
|
let (version, initial_snapshot) = versions.last().unwrap();
|
||||||
|
let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
|
||||||
|
let language_id =
|
||||||
|
adapter.id_for_language(language.name().as_ref());
|
||||||
|
language_server
|
||||||
|
.notify::<lsp::notification::DidOpenTextDocument>(
|
||||||
|
lsp::DidOpenTextDocumentParams {
|
||||||
|
text_document: lsp::TextDocumentItem::new(
|
||||||
|
uri,
|
||||||
|
language_id.unwrap_or_default(),
|
||||||
|
*version,
|
||||||
|
initial_snapshot.text(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.log_err()?;
|
||||||
|
buffer_handle.update(cx, |buffer, cx| {
|
||||||
|
buffer.set_completion_triggers(
|
||||||
|
language_server
|
||||||
|
.capabilities()
|
||||||
|
.completion_provider
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|provider| {
|
||||||
|
provider.trigger_characters.clone()
|
||||||
|
})
|
||||||
|
.unwrap_or(Vec::new()),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cx.notify();
|
||||||
|
Some(language_server)
|
||||||
|
})
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
|
server_id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2193,26 +2232,28 @@ impl Project {
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Task<()> {
|
) -> Task<()> {
|
||||||
let key = (worktree_id, adapter_name);
|
let key = (worktree_id, adapter_name);
|
||||||
if let Some((_, language_server)) = self.language_servers.remove(&key) {
|
if let Some(server_id) = self.language_server_ids.remove(&key) {
|
||||||
self.language_server_statuses
|
let server_state = self.language_servers.remove(&server_id);
|
||||||
.remove(&language_server.server_id());
|
|
||||||
cx.notify();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(started_language_server) = self.started_language_servers.remove(&key) {
|
|
||||||
cx.spawn_weak(|this, mut cx| async move {
|
cx.spawn_weak(|this, mut cx| async move {
|
||||||
if let Some(language_server) = started_language_server.await {
|
let server = match server_state {
|
||||||
if let Some(shutdown) = language_server.shutdown() {
|
Some(LanguageServerState::Starting(started_language_server)) => {
|
||||||
|
started_language_server.await
|
||||||
|
}
|
||||||
|
Some(LanguageServerState::Running { server, .. }) => Some(server),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(server) = server {
|
||||||
|
if let Some(shutdown) = server.shutdown() {
|
||||||
shutdown.await;
|
shutdown.await;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(this) = this.upgrade(&cx) {
|
if let Some(this) = this.upgrade(&cx) {
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.language_server_statuses
|
this.language_server_statuses.remove(&server_id);
|
||||||
.remove(&language_server.server_id());
|
cx.notify();
|
||||||
cx.notify();
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -2498,14 +2539,16 @@ impl Project {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_language_server_settings(&mut self, settings: serde_json::Value) {
|
pub fn set_language_server_settings(&mut self, settings: serde_json::Value) {
|
||||||
for (_, server) in self.language_servers.values() {
|
for server_state in self.language_servers.values() {
|
||||||
server
|
if let LanguageServerState::Running { server, .. } = server_state {
|
||||||
.notify::<lsp::notification::DidChangeConfiguration>(
|
server
|
||||||
lsp::DidChangeConfigurationParams {
|
.notify::<lsp::notification::DidChangeConfiguration>(
|
||||||
settings: settings.clone(),
|
lsp::DidChangeConfigurationParams {
|
||||||
},
|
settings: settings.clone(),
|
||||||
)
|
},
|
||||||
.ok();
|
)
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*self.language_server_settings.lock() = settings;
|
*self.language_server_settings.lock() = settings;
|
||||||
}
|
}
|
||||||
|
@ -2968,30 +3011,36 @@ impl Project {
|
||||||
pub fn symbols(&self, query: &str, cx: &mut ModelContext<Self>) -> Task<Result<Vec<Symbol>>> {
|
pub fn symbols(&self, query: &str, cx: &mut ModelContext<Self>) -> Task<Result<Vec<Symbol>>> {
|
||||||
if self.is_local() {
|
if self.is_local() {
|
||||||
let mut requests = Vec::new();
|
let mut requests = Vec::new();
|
||||||
for ((worktree_id, _), (lsp_adapter, language_server)) in self.language_servers.iter() {
|
for ((worktree_id, _), server_id) in self.language_server_ids.iter() {
|
||||||
let worktree_id = *worktree_id;
|
let worktree_id = *worktree_id;
|
||||||
if let Some(worktree) = self
|
if let Some(worktree) = self
|
||||||
.worktree_for_id(worktree_id, cx)
|
.worktree_for_id(worktree_id, cx)
|
||||||
.and_then(|worktree| worktree.read(cx).as_local())
|
.and_then(|worktree| worktree.read(cx).as_local())
|
||||||
{
|
{
|
||||||
let lsp_adapter = lsp_adapter.clone();
|
if let Some(LanguageServerState::Running { adapter, server }) =
|
||||||
let worktree_abs_path = worktree.abs_path().clone();
|
self.language_servers.get(server_id)
|
||||||
requests.push(
|
{
|
||||||
language_server
|
let adapter = adapter.clone();
|
||||||
.request::<lsp::request::WorkspaceSymbol>(lsp::WorkspaceSymbolParams {
|
let worktree_abs_path = worktree.abs_path().clone();
|
||||||
query: query.to_string(),
|
requests.push(
|
||||||
..Default::default()
|
server
|
||||||
})
|
.request::<lsp::request::WorkspaceSymbol>(
|
||||||
.log_err()
|
lsp::WorkspaceSymbolParams {
|
||||||
.map(move |response| {
|
query: query.to_string(),
|
||||||
(
|
..Default::default()
|
||||||
lsp_adapter,
|
},
|
||||||
worktree_id,
|
|
||||||
worktree_abs_path,
|
|
||||||
response.unwrap_or_default(),
|
|
||||||
)
|
)
|
||||||
}),
|
.log_err()
|
||||||
);
|
.map(move |response| {
|
||||||
|
(
|
||||||
|
adapter,
|
||||||
|
worktree_id,
|
||||||
|
worktree_abs_path,
|
||||||
|
response.unwrap_or_default(),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3074,11 +3123,11 @@ impl Project {
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Task<Result<ModelHandle<Buffer>>> {
|
) -> Task<Result<ModelHandle<Buffer>>> {
|
||||||
if self.is_local() {
|
if self.is_local() {
|
||||||
let (lsp_adapter, language_server) = if let Some(server) = self.language_servers.get(&(
|
let language_server_id = if let Some(id) = self.language_server_ids.get(&(
|
||||||
symbol.source_worktree_id,
|
symbol.source_worktree_id,
|
||||||
symbol.language_server_name.clone(),
|
symbol.language_server_name.clone(),
|
||||||
)) {
|
)) {
|
||||||
server.clone()
|
*id
|
||||||
} else {
|
} else {
|
||||||
return Task::ready(Err(anyhow!(
|
return Task::ready(Err(anyhow!(
|
||||||
"language server for worktree and language not found"
|
"language server for worktree and language not found"
|
||||||
|
@ -3101,7 +3150,12 @@ impl Project {
|
||||||
return Task::ready(Err(anyhow!("invalid symbol path")));
|
return Task::ready(Err(anyhow!("invalid symbol path")));
|
||||||
};
|
};
|
||||||
|
|
||||||
self.open_local_buffer_via_lsp(symbol_uri, lsp_adapter, language_server, cx)
|
self.open_local_buffer_via_lsp(
|
||||||
|
symbol_uri,
|
||||||
|
language_server_id,
|
||||||
|
symbol.language_server_name.clone(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
} else if let Some(project_id) = self.remote_id() {
|
} else if let Some(project_id) = self.remote_id() {
|
||||||
let request = self.client.request(proto::OpenBufferForSymbol {
|
let request = self.client.request(proto::OpenBufferForSymbol {
|
||||||
project_id,
|
project_id,
|
||||||
|
@ -3152,8 +3206,8 @@ impl Project {
|
||||||
|
|
||||||
if worktree.read(cx).as_local().is_some() {
|
if worktree.read(cx).as_local().is_some() {
|
||||||
let buffer_abs_path = buffer_abs_path.unwrap();
|
let buffer_abs_path = buffer_abs_path.unwrap();
|
||||||
let (_, lang_server) =
|
let lang_server =
|
||||||
if let Some(server) = self.language_server_for_buffer(source_buffer, cx) {
|
if let Some((_, server)) = self.language_server_for_buffer(source_buffer, cx) {
|
||||||
server.clone()
|
server.clone()
|
||||||
} else {
|
} else {
|
||||||
return Task::ready(Ok(Default::default()));
|
return Task::ready(Ok(Default::default()));
|
||||||
|
@ -3310,7 +3364,7 @@ impl Project {
|
||||||
let buffer_id = buffer.remote_id();
|
let buffer_id = buffer.remote_id();
|
||||||
|
|
||||||
if self.is_local() {
|
if self.is_local() {
|
||||||
let (_, lang_server) = if let Some(server) = self.language_server_for_buffer(buffer, cx)
|
let lang_server = if let Some((_, server)) = self.language_server_for_buffer(buffer, cx)
|
||||||
{
|
{
|
||||||
server.clone()
|
server.clone()
|
||||||
} else {
|
} else {
|
||||||
|
@ -3407,7 +3461,7 @@ impl Project {
|
||||||
|
|
||||||
if worktree.read(cx).as_local().is_some() {
|
if worktree.read(cx).as_local().is_some() {
|
||||||
let buffer_abs_path = buffer_abs_path.unwrap();
|
let buffer_abs_path = buffer_abs_path.unwrap();
|
||||||
let (_, lang_server) = if let Some(server) = self.language_server_for_buffer(buffer, cx)
|
let lang_server = if let Some((_, server)) = self.language_server_for_buffer(buffer, cx)
|
||||||
{
|
{
|
||||||
server.clone()
|
server.clone()
|
||||||
} else {
|
} else {
|
||||||
|
@ -3494,8 +3548,8 @@ impl Project {
|
||||||
if self.is_local() {
|
if self.is_local() {
|
||||||
let buffer = buffer_handle.read(cx);
|
let buffer = buffer_handle.read(cx);
|
||||||
let (lsp_adapter, lang_server) =
|
let (lsp_adapter, lang_server) =
|
||||||
if let Some(server) = self.language_server_for_buffer(buffer, cx) {
|
if let Some((adapter, server)) = self.language_server_for_buffer(buffer, cx) {
|
||||||
server.clone()
|
(adapter.clone(), server.clone())
|
||||||
} else {
|
} else {
|
||||||
return Task::ready(Ok(Default::default()));
|
return Task::ready(Ok(Default::default()));
|
||||||
};
|
};
|
||||||
|
@ -3531,8 +3585,8 @@ impl Project {
|
||||||
this,
|
this,
|
||||||
edit,
|
edit,
|
||||||
push_to_history,
|
push_to_history,
|
||||||
lsp_adapter,
|
lsp_adapter.clone(),
|
||||||
lang_server,
|
lang_server.clone(),
|
||||||
&mut cx,
|
&mut cx,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -3661,8 +3715,8 @@ impl Project {
|
||||||
.update(cx, |this, cx| {
|
.update(cx, |this, cx| {
|
||||||
this.open_local_buffer_via_lsp(
|
this.open_local_buffer_via_lsp(
|
||||||
op.text_document.uri,
|
op.text_document.uri,
|
||||||
lsp_adapter.clone(),
|
language_server.server_id(),
|
||||||
language_server.clone(),
|
lsp_adapter.name(),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -3956,9 +4010,10 @@ impl Project {
|
||||||
let buffer = buffer_handle.read(cx);
|
let buffer = buffer_handle.read(cx);
|
||||||
if self.is_local() {
|
if self.is_local() {
|
||||||
let file = File::from_dyn(buffer.file()).and_then(File::as_local);
|
let file = File::from_dyn(buffer.file()).and_then(File::as_local);
|
||||||
if let Some((file, (_, language_server))) =
|
if let Some((file, language_server)) = file.zip(
|
||||||
file.zip(self.language_server_for_buffer(buffer, cx).cloned())
|
self.language_server_for_buffer(buffer, cx)
|
||||||
{
|
.map(|(_, server)| server.clone()),
|
||||||
|
) {
|
||||||
let lsp_params = request.to_lsp(&file.abs_path(cx), cx);
|
let lsp_params = request.to_lsp(&file.abs_path(cx), cx);
|
||||||
return cx.spawn(|this, cx| async move {
|
return cx.spawn(|this, cx| async move {
|
||||||
if !request.check_capabilities(&language_server.capabilities()) {
|
if !request.check_capabilities(&language_server.capabilities()) {
|
||||||
|
@ -5574,14 +5629,21 @@ impl Project {
|
||||||
&self,
|
&self,
|
||||||
buffer: &Buffer,
|
buffer: &Buffer,
|
||||||
cx: &AppContext,
|
cx: &AppContext,
|
||||||
) -> Option<&(Arc<dyn LspAdapter>, Arc<LanguageServer>)> {
|
) -> Option<(&Arc<dyn LspAdapter>, &Arc<LanguageServer>)> {
|
||||||
if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) {
|
if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) {
|
||||||
let worktree_id = file.worktree_id(cx);
|
let worktree_id = file.worktree_id(cx);
|
||||||
self.language_servers
|
let key = (worktree_id, language.lsp_adapter()?.name());
|
||||||
.get(&(worktree_id, language.lsp_adapter()?.name()))
|
|
||||||
} else {
|
if let Some(server_id) = self.language_server_ids.get(&key) {
|
||||||
None
|
if let Some(LanguageServerState::Running { adapter, server }) =
|
||||||
|
self.language_servers.get(&server_id)
|
||||||
|
{
|
||||||
|
return Some((adapter, server));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5741,8 +5803,16 @@ impl Entity for Project {
|
||||||
let shutdown_futures = self
|
let shutdown_futures = self
|
||||||
.language_servers
|
.language_servers
|
||||||
.drain()
|
.drain()
|
||||||
.filter_map(|(_, (_, server))| server.shutdown())
|
.filter_map(|(_, server_state)| {
|
||||||
|
// TODO: Handle starting servers?
|
||||||
|
if let LanguageServerState::Running { server, .. } = server_state {
|
||||||
|
server.shutdown()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
Some(
|
Some(
|
||||||
async move {
|
async move {
|
||||||
futures::future::join_all(shutdown_futures).await;
|
futures::future::join_all(shutdown_futures).await;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue