Detect LSP startup failure
This commit is contained in:
parent
0a8d67c4ca
commit
ec0409a3d1
3 changed files with 351 additions and 291 deletions
|
@ -887,8 +887,8 @@ impl LanguageRegistry {
|
||||||
})
|
})
|
||||||
.clone();
|
.clone();
|
||||||
drop(lock);
|
drop(lock);
|
||||||
let binary = entry.clone().map_err(|e| anyhow!(e)).await?;
|
|
||||||
|
|
||||||
|
let binary = entry.clone().map_err(|e| anyhow!(e)).await?;
|
||||||
let server = lsp::LanguageServer::new(
|
let server = lsp::LanguageServer::new(
|
||||||
server_id,
|
server_id,
|
||||||
&binary.path,
|
&binary.path,
|
||||||
|
|
|
@ -167,6 +167,7 @@ impl LanguageServer {
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(server)
|
Ok(server)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2383,16 +2383,9 @@ impl Project {
|
||||||
language: Arc<Language>,
|
language: Arc<Language>,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) {
|
) {
|
||||||
if !language_settings(
|
let root_file = worktree.update(cx, |tree, cx| tree.root_file(cx));
|
||||||
Some(&language),
|
let settings = language_settings(Some(&language), root_file.map(|f| f as _).as_ref(), cx);
|
||||||
worktree
|
if !settings.enable_language_server {
|
||||||
.update(cx, |tree, cx| tree.root_file(cx))
|
|
||||||
.map(|f| f as _)
|
|
||||||
.as_ref(),
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
.enable_language_server
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2414,9 +2407,8 @@ impl Project {
|
||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
let lsp = settings::get::<ProjectSettings>(cx)
|
let project_settings = settings::get::<ProjectSettings>(cx);
|
||||||
.lsp
|
let lsp = project_settings.lsp.get(&adapter.name.0);
|
||||||
.get(&adapter.name.0);
|
|
||||||
let override_options = lsp.map(|s| s.initialization_options.clone()).flatten();
|
let override_options = lsp.map(|s| s.initialization_options.clone()).flatten();
|
||||||
|
|
||||||
let mut initialization_options = adapter.initialization_options.clone();
|
let mut initialization_options = adapter.initialization_options.clone();
|
||||||
|
@ -2429,34 +2421,98 @@ impl Project {
|
||||||
}
|
}
|
||||||
|
|
||||||
let server_id = pending_server.server_id;
|
let server_id = pending_server.server_id;
|
||||||
let state = self.setup_pending_language_server(
|
let state = LanguageServerState::Starting({
|
||||||
|
let server_name = adapter.name.0.clone();
|
||||||
|
let adapter = adapter.clone();
|
||||||
|
let language = language.clone();
|
||||||
|
let languages = self.languages.clone();
|
||||||
|
let key = key.clone();
|
||||||
|
|
||||||
|
cx.spawn_weak(|this, cx| async move {
|
||||||
|
let result = Self::setup_and_insert_language_server(
|
||||||
|
this,
|
||||||
initialization_options,
|
initialization_options,
|
||||||
pending_server,
|
pending_server,
|
||||||
adapter.clone(),
|
adapter,
|
||||||
language.clone(),
|
languages,
|
||||||
key.clone(),
|
language,
|
||||||
|
server_id,
|
||||||
|
key,
|
||||||
cx,
|
cx,
|
||||||
);
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(server) => Some(server),
|
||||||
|
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("Error starting language server {:?}: {}", server_name, err);
|
||||||
|
// TODO: Prompt installation validity check
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
self.language_servers.insert(server_id, state);
|
self.language_servers.insert(server_id, state);
|
||||||
self.language_server_ids.insert(key.clone(), server_id);
|
self.language_server_ids.insert(key, server_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_pending_language_server(
|
async fn setup_and_insert_language_server(
|
||||||
&mut self,
|
this: WeakModelHandle<Self>,
|
||||||
initialization_options: Option<serde_json::Value>,
|
initialization_options: Option<serde_json::Value>,
|
||||||
pending_server: PendingLanguageServer,
|
pending_server: PendingLanguageServer,
|
||||||
adapter: Arc<CachedLspAdapter>,
|
adapter: Arc<CachedLspAdapter>,
|
||||||
|
languages: Arc<LanguageRegistry>,
|
||||||
language: Arc<Language>,
|
language: Arc<Language>,
|
||||||
|
server_id: LanguageServerId,
|
||||||
key: (WorktreeId, LanguageServerName),
|
key: (WorktreeId, LanguageServerName),
|
||||||
cx: &mut ModelContext<Project>,
|
mut cx: AsyncAppContext,
|
||||||
) -> LanguageServerState {
|
) -> Result<Arc<LanguageServer>> {
|
||||||
let server_id = pending_server.server_id;
|
let language_server = Self::setup_pending_language_server(
|
||||||
let languages = self.languages.clone();
|
this,
|
||||||
|
initialization_options,
|
||||||
|
pending_server,
|
||||||
|
adapter.clone(),
|
||||||
|
languages,
|
||||||
|
server_id,
|
||||||
|
&mut cx,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
LanguageServerState::Starting(cx.spawn_weak(|this, mut cx| async move {
|
let this = match this.upgrade(&mut cx) {
|
||||||
|
Some(this) => this,
|
||||||
|
None => return Err(anyhow!("failed to upgrade project handle")),
|
||||||
|
};
|
||||||
|
|
||||||
|
this.update(&mut cx, |this, cx| {
|
||||||
|
this.insert_newly_running_language_server(
|
||||||
|
language,
|
||||||
|
adapter,
|
||||||
|
language_server.clone(),
|
||||||
|
server_id,
|
||||||
|
key,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(language_server)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn setup_pending_language_server(
|
||||||
|
this: WeakModelHandle<Self>,
|
||||||
|
initialization_options: Option<serde_json::Value>,
|
||||||
|
pending_server: PendingLanguageServer,
|
||||||
|
adapter: Arc<CachedLspAdapter>,
|
||||||
|
languages: Arc<LanguageRegistry>,
|
||||||
|
server_id: LanguageServerId,
|
||||||
|
cx: &mut AsyncAppContext,
|
||||||
|
) -> Result<Arc<LanguageServer>> {
|
||||||
let workspace_config = cx.update(|cx| languages.workspace_configuration(cx)).await;
|
let workspace_config = cx.update(|cx| languages.workspace_configuration(cx)).await;
|
||||||
let language_server = pending_server.task.await.log_err()?;
|
|
||||||
|
let language_server = pending_server.task.await?;
|
||||||
|
let language_server = language_server.initialize(initialization_options).await?;
|
||||||
|
|
||||||
language_server
|
language_server
|
||||||
.on_notification::<lsp::notification::LogMessage, _>({
|
.on_notification::<lsp::notification::LogMessage, _>({
|
||||||
|
@ -2474,6 +2530,7 @@ impl Project {
|
||||||
.on_notification::<lsp::notification::PublishDiagnostics, _>({
|
.on_notification::<lsp::notification::PublishDiagnostics, _>({
|
||||||
let adapter = adapter.clone();
|
let adapter = adapter.clone();
|
||||||
move |mut params, cx| {
|
move |mut params, cx| {
|
||||||
|
let this = this;
|
||||||
let adapter = adapter.clone();
|
let adapter = adapter.clone();
|
||||||
cx.spawn(|mut cx| async move {
|
cx.spawn(|mut cx| async move {
|
||||||
adapter.process_diagnostics(&mut params).await;
|
adapter.process_diagnostics(&mut params).await;
|
||||||
|
@ -2529,8 +2586,7 @@ impl Project {
|
||||||
move |params, mut cx| async move {
|
move |params, mut cx| async move {
|
||||||
if let Some(this) = this.upgrade(&cx) {
|
if let Some(this) = this.upgrade(&cx) {
|
||||||
this.update(&mut cx, |this, _| {
|
this.update(&mut cx, |this, _| {
|
||||||
if let Some(status) =
|
if let Some(status) = this.language_server_statuses.get_mut(&server_id)
|
||||||
this.language_server_statuses.get_mut(&server_id)
|
|
||||||
{
|
{
|
||||||
if let lsp::NumberOrString::String(token) = params.token {
|
if let lsp::NumberOrString::String(token) = params.token {
|
||||||
status.progress_tokens.insert(token);
|
status.progress_tokens.insert(token);
|
||||||
|
@ -2543,7 +2599,7 @@ impl Project {
|
||||||
)
|
)
|
||||||
.detach();
|
.detach();
|
||||||
language_server
|
language_server
|
||||||
.on_request::<lsp::request::RegisterCapability, _, _>(
|
.on_request::<lsp::request::RegisterCapability, _, _>({
|
||||||
move |params, mut cx| async move {
|
move |params, mut cx| async move {
|
||||||
let this = this
|
let this = this
|
||||||
.upgrade(&cx)
|
.upgrade(&cx)
|
||||||
|
@ -2553,16 +2609,14 @@ impl Project {
|
||||||
if let Some(options) = reg.register_options {
|
if let Some(options) = reg.register_options {
|
||||||
let options = serde_json::from_value(options)?;
|
let options = serde_json::from_value(options)?;
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.on_lsp_did_change_watched_files(
|
this.on_lsp_did_change_watched_files(server_id, options, cx);
|
||||||
server_id, options, cx,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
)
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
language_server
|
language_server
|
||||||
|
@ -2578,8 +2632,7 @@ impl Project {
|
||||||
adapter.disk_based_diagnostics_progress_token.clone();
|
adapter.disk_based_diagnostics_progress_token.clone();
|
||||||
|
|
||||||
language_server
|
language_server
|
||||||
.on_notification::<lsp::notification::Progress, _>({
|
.on_notification::<lsp::notification::Progress, _>(move |params, mut cx| {
|
||||||
move |params, mut cx| {
|
|
||||||
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.on_lsp_progress(
|
this.on_lsp_progress(
|
||||||
|
@ -2590,14 +2643,9 @@ impl Project {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
let language_server = language_server
|
|
||||||
.initialize(initialization_options)
|
|
||||||
.await
|
|
||||||
.log_err()?;
|
|
||||||
language_server
|
language_server
|
||||||
.notify::<lsp::notification::DidChangeConfiguration>(
|
.notify::<lsp::notification::DidChangeConfiguration>(
|
||||||
lsp::DidChangeConfigurationParams {
|
lsp::DidChangeConfigurationParams {
|
||||||
|
@ -2606,22 +2654,32 @@ impl Project {
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
let this = this.upgrade(&cx)?;
|
Ok(language_server)
|
||||||
this.update(&mut cx, |this, cx| {
|
}
|
||||||
|
|
||||||
|
fn insert_newly_running_language_server(
|
||||||
|
&mut self,
|
||||||
|
language: Arc<Language>,
|
||||||
|
adapter: Arc<CachedLspAdapter>,
|
||||||
|
language_server: Arc<LanguageServer>,
|
||||||
|
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
|
// 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
|
// server. Which will cause it to be dropped, killing the process
|
||||||
if this
|
if self
|
||||||
.language_server_ids
|
.language_server_ids
|
||||||
.get(&key)
|
.get(&key)
|
||||||
.map(|id| id != &server_id)
|
.map(|id| id != &server_id)
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
return None;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update language_servers collection with Running variant of LanguageServerState
|
// Update language_servers collection with Running variant of LanguageServerState
|
||||||
// indicating that the server is up and running and ready
|
// indicating that the server is up and running and ready
|
||||||
this.language_servers.insert(
|
self.language_servers.insert(
|
||||||
server_id,
|
server_id,
|
||||||
LanguageServerState::Running {
|
LanguageServerState::Running {
|
||||||
adapter: adapter.clone(),
|
adapter: adapter.clone(),
|
||||||
|
@ -2631,7 +2689,8 @@ impl Project {
|
||||||
simulate_disk_based_diagnostics_completion: None,
|
simulate_disk_based_diagnostics_completion: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
this.language_server_statuses.insert(
|
|
||||||
|
self.language_server_statuses.insert(
|
||||||
server_id,
|
server_id,
|
||||||
LanguageServerStatus {
|
LanguageServerStatus {
|
||||||
name: language_server.name().to_string(),
|
name: language_server.name().to_string(),
|
||||||
|
@ -2643,20 +2702,18 @@ impl Project {
|
||||||
|
|
||||||
cx.emit(Event::LanguageServerAdded(server_id));
|
cx.emit(Event::LanguageServerAdded(server_id));
|
||||||
|
|
||||||
if let Some(project_id) = this.remote_id() {
|
if let Some(project_id) = self.remote_id() {
|
||||||
this.client
|
self.client.send(proto::StartLanguageServer {
|
||||||
.send(proto::StartLanguageServer {
|
|
||||||
project_id,
|
project_id,
|
||||||
server: Some(proto::LanguageServer {
|
server: Some(proto::LanguageServer {
|
||||||
id: server_id.0 as u64,
|
id: server_id.0 as u64,
|
||||||
name: language_server.name().to_string(),
|
name: language_server.name().to_string(),
|
||||||
}),
|
}),
|
||||||
})
|
})?;
|
||||||
.log_err();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell the language server about every open buffer in the worktree that matches the language.
|
// Tell the language server about every open buffer in the worktree that matches the language.
|
||||||
for buffer in this.opened_buffers.values() {
|
for buffer in self.opened_buffers.values() {
|
||||||
if let Some(buffer_handle) = buffer.upgrade(cx) {
|
if let Some(buffer_handle) = buffer.upgrade(cx) {
|
||||||
let buffer = buffer_handle.read(cx);
|
let buffer = buffer_handle.read(cx);
|
||||||
let file = match File::from_dyn(buffer.file()) {
|
let file = match File::from_dyn(buffer.file()) {
|
||||||
|
@ -2674,8 +2731,12 @@ impl Project {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let file = file.as_local()?;
|
let file = match file.as_local() {
|
||||||
let versions = this
|
Some(file) => file,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
let versions = self
|
||||||
.buffer_snapshots
|
.buffer_snapshots
|
||||||
.entry(buffer.remote_id())
|
.entry(buffer.remote_id())
|
||||||
.or_default()
|
.or_default()
|
||||||
|
@ -2691,8 +2752,7 @@ impl Project {
|
||||||
let version = snapshot.version;
|
let version = snapshot.version;
|
||||||
let initial_snapshot = &snapshot.snapshot;
|
let initial_snapshot = &snapshot.snapshot;
|
||||||
let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
|
let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
|
||||||
language_server
|
language_server.notify::<lsp::notification::DidOpenTextDocument>(
|
||||||
.notify::<lsp::notification::DidOpenTextDocument>(
|
|
||||||
lsp::DidOpenTextDocumentParams {
|
lsp::DidOpenTextDocumentParams {
|
||||||
text_document: lsp::TextDocumentItem::new(
|
text_document: lsp::TextDocumentItem::new(
|
||||||
uri,
|
uri,
|
||||||
|
@ -2705,8 +2765,8 @@ impl Project {
|
||||||
initial_snapshot.text(),
|
initial_snapshot.text(),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
)
|
)?;
|
||||||
.log_err()?;
|
|
||||||
buffer_handle.update(cx, |buffer, cx| {
|
buffer_handle.update(cx, |buffer, cx| {
|
||||||
buffer.set_completion_triggers(
|
buffer.set_completion_triggers(
|
||||||
language_server
|
language_server
|
||||||
|
@ -2722,9 +2782,7 @@ impl Project {
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
Some(language_server)
|
Ok(())
|
||||||
})
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a list of all of the worktrees which no longer have a language server and the root path
|
// Returns a list of all of the worktrees which no longer have a language server and the root path
|
||||||
|
@ -2776,6 +2834,7 @@ impl Project {
|
||||||
Some(LanguageServerState::Starting(started_language_server)) => {
|
Some(LanguageServerState::Starting(started_language_server)) => {
|
||||||
started_language_server.await
|
started_language_server.await
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(LanguageServerState::Running { server, .. }) => Some(server),
|
Some(LanguageServerState::Running { server, .. }) => Some(server),
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue