Stop waiting for part of LSP responses on remote Collab clients' part (#36557)
Instead of holding a connection for potentially long LSP queries (e.g. rust-analyzer might take minutes to look up a definition), disconnect right after sending the initial request and handle the follow-up responses later. As a bonus, this allows to cancel previously sent request on the local Collab clients' side due to this, as instead of holding and serving the old connection, local clients now can stop previous requests, if needed. Current PR does not convert all LSP requests to the new paradigm, but the problematic ones, deprecating `MultiLspQuery` and moving all its requests to the new paradigm. Release Notes: - Improved resource usage when querying LSP over Collab --------- Co-authored-by: David Kleingeld <git@davidsk.dev> Co-authored-by: Mikayla Maki <mikayla@zed.dev> Co-authored-by: David Kleingeld <davidsk@zed.dev>
This commit is contained in:
parent
c731bb6d91
commit
5dcb90858e
20 changed files with 1395 additions and 681 deletions
|
@ -1691,7 +1691,7 @@ impl SemanticsProvider for SlashCommandSemanticsProvider {
|
||||||
buffer: &Entity<Buffer>,
|
buffer: &Entity<Buffer>,
|
||||||
position: text::Anchor,
|
position: text::Anchor,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Option<Task<Vec<project::Hover>>> {
|
) -> Option<Task<Option<Vec<project::Hover>>>> {
|
||||||
let snapshot = buffer.read(cx).snapshot();
|
let snapshot = buffer.read(cx).snapshot();
|
||||||
let offset = position.to_offset(&snapshot);
|
let offset = position.to_offset(&snapshot);
|
||||||
let (start, end) = self.range.get()?;
|
let (start, end) = self.range.get()?;
|
||||||
|
@ -1699,14 +1699,14 @@ impl SemanticsProvider for SlashCommandSemanticsProvider {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let range = snapshot.anchor_after(start)..snapshot.anchor_after(end);
|
let range = snapshot.anchor_after(start)..snapshot.anchor_after(end);
|
||||||
Some(Task::ready(vec![project::Hover {
|
Some(Task::ready(Some(vec![project::Hover {
|
||||||
contents: vec![project::HoverBlock {
|
contents: vec![project::HoverBlock {
|
||||||
text: "Slash commands are not supported".into(),
|
text: "Slash commands are not supported".into(),
|
||||||
kind: project::HoverBlockKind::PlainText,
|
kind: project::HoverBlockKind::PlainText,
|
||||||
}],
|
}],
|
||||||
range: Some(range),
|
range: Some(range),
|
||||||
language: None,
|
language: None,
|
||||||
}]))
|
}])))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inline_values(
|
fn inline_values(
|
||||||
|
@ -1756,7 +1756,7 @@ impl SemanticsProvider for SlashCommandSemanticsProvider {
|
||||||
_position: text::Anchor,
|
_position: text::Anchor,
|
||||||
_kind: editor::GotoDefinitionKind,
|
_kind: editor::GotoDefinitionKind,
|
||||||
_cx: &mut App,
|
_cx: &mut App,
|
||||||
) -> Option<Task<Result<Vec<project::LocationLink>>>> {
|
) -> Option<Task<Result<Option<Vec<project::LocationLink>>>>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -400,6 +400,8 @@ impl Server {
|
||||||
.add_request_handler(forward_mutating_project_request::<proto::SaveBuffer>)
|
.add_request_handler(forward_mutating_project_request::<proto::SaveBuffer>)
|
||||||
.add_request_handler(forward_mutating_project_request::<proto::BlameBuffer>)
|
.add_request_handler(forward_mutating_project_request::<proto::BlameBuffer>)
|
||||||
.add_request_handler(multi_lsp_query)
|
.add_request_handler(multi_lsp_query)
|
||||||
|
.add_request_handler(lsp_query)
|
||||||
|
.add_message_handler(broadcast_project_message_from_host::<proto::LspQueryResponse>)
|
||||||
.add_request_handler(forward_mutating_project_request::<proto::RestartLanguageServers>)
|
.add_request_handler(forward_mutating_project_request::<proto::RestartLanguageServers>)
|
||||||
.add_request_handler(forward_mutating_project_request::<proto::StopLanguageServers>)
|
.add_request_handler(forward_mutating_project_request::<proto::StopLanguageServers>)
|
||||||
.add_request_handler(forward_mutating_project_request::<proto::LinkedEditingRange>)
|
.add_request_handler(forward_mutating_project_request::<proto::LinkedEditingRange>)
|
||||||
|
@ -910,7 +912,9 @@ impl Server {
|
||||||
user_id=field::Empty,
|
user_id=field::Empty,
|
||||||
login=field::Empty,
|
login=field::Empty,
|
||||||
impersonator=field::Empty,
|
impersonator=field::Empty,
|
||||||
|
// todo(lsp) remove after Zed Stable hits v0.204.x
|
||||||
multi_lsp_query_request=field::Empty,
|
multi_lsp_query_request=field::Empty,
|
||||||
|
lsp_query_request=field::Empty,
|
||||||
release_channel=field::Empty,
|
release_channel=field::Empty,
|
||||||
{ TOTAL_DURATION_MS }=field::Empty,
|
{ TOTAL_DURATION_MS }=field::Empty,
|
||||||
{ PROCESSING_DURATION_MS }=field::Empty,
|
{ PROCESSING_DURATION_MS }=field::Empty,
|
||||||
|
@ -2356,6 +2360,7 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo(lsp) remove after Zed Stable hits v0.204.x
|
||||||
async fn multi_lsp_query(
|
async fn multi_lsp_query(
|
||||||
request: MultiLspQuery,
|
request: MultiLspQuery,
|
||||||
response: Response<MultiLspQuery>,
|
response: Response<MultiLspQuery>,
|
||||||
|
@ -2366,6 +2371,21 @@ async fn multi_lsp_query(
|
||||||
forward_mutating_project_request(request, response, session).await
|
forward_mutating_project_request(request, response, session).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn lsp_query(
|
||||||
|
request: proto::LspQuery,
|
||||||
|
response: Response<proto::LspQuery>,
|
||||||
|
session: MessageContext,
|
||||||
|
) -> Result<()> {
|
||||||
|
let (name, should_write) = request.query_name_and_write_permissions();
|
||||||
|
tracing::Span::current().record("lsp_query_request", name);
|
||||||
|
tracing::info!("lsp_query message received");
|
||||||
|
if should_write {
|
||||||
|
forward_mutating_project_request(request, response, session).await
|
||||||
|
} else {
|
||||||
|
forward_read_only_project_request(request, response, session).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Notify other participants that a new buffer has been created
|
/// Notify other participants that a new buffer has been created
|
||||||
async fn create_buffer_for_peer(
|
async fn create_buffer_for_peer(
|
||||||
request: proto::CreateBufferForPeer,
|
request: proto::CreateBufferForPeer,
|
||||||
|
|
|
@ -15,13 +15,14 @@ use editor::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use futures::{StreamExt, lock::Mutex};
|
use futures::{SinkExt, StreamExt, channel::mpsc, lock::Mutex};
|
||||||
use gpui::{App, Rgba, TestAppContext, UpdateGlobal, VisualContext, VisualTestContext};
|
use gpui::{App, Rgba, TestAppContext, UpdateGlobal, VisualContext, VisualTestContext};
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use language::{
|
use language::{
|
||||||
FakeLspAdapter,
|
FakeLspAdapter,
|
||||||
language_settings::{AllLanguageSettings, InlayHintSettings},
|
language_settings::{AllLanguageSettings, InlayHintSettings},
|
||||||
};
|
};
|
||||||
|
use lsp::LSP_REQUEST_TIMEOUT;
|
||||||
use project::{
|
use project::{
|
||||||
ProjectPath, SERVER_PROGRESS_THROTTLE_TIMEOUT,
|
ProjectPath, SERVER_PROGRESS_THROTTLE_TIMEOUT,
|
||||||
lsp_store::lsp_ext_command::{ExpandedMacro, LspExtExpandMacro},
|
lsp_store::lsp_ext_command::{ExpandedMacro, LspExtExpandMacro},
|
||||||
|
@ -1017,6 +1018,211 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_slow_lsp_server(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
||||||
|
let mut server = TestServer::start(cx_a.executor()).await;
|
||||||
|
let client_a = server.create_client(cx_a, "user_a").await;
|
||||||
|
let client_b = server.create_client(cx_b, "user_b").await;
|
||||||
|
server
|
||||||
|
.create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
|
||||||
|
.await;
|
||||||
|
let active_call_a = cx_a.read(ActiveCall::global);
|
||||||
|
cx_b.update(editor::init);
|
||||||
|
|
||||||
|
let command_name = "test_command";
|
||||||
|
let capabilities = lsp::ServerCapabilities {
|
||||||
|
code_lens_provider: Some(lsp::CodeLensOptions {
|
||||||
|
resolve_provider: None,
|
||||||
|
}),
|
||||||
|
execute_command_provider: Some(lsp::ExecuteCommandOptions {
|
||||||
|
commands: vec![command_name.to_string()],
|
||||||
|
..lsp::ExecuteCommandOptions::default()
|
||||||
|
}),
|
||||||
|
..lsp::ServerCapabilities::default()
|
||||||
|
};
|
||||||
|
client_a.language_registry().add(rust_lang());
|
||||||
|
let mut fake_language_servers = client_a.language_registry().register_fake_lsp(
|
||||||
|
"Rust",
|
||||||
|
FakeLspAdapter {
|
||||||
|
capabilities: capabilities.clone(),
|
||||||
|
..FakeLspAdapter::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
client_b.language_registry().add(rust_lang());
|
||||||
|
client_b.language_registry().register_fake_lsp_adapter(
|
||||||
|
"Rust",
|
||||||
|
FakeLspAdapter {
|
||||||
|
capabilities,
|
||||||
|
..FakeLspAdapter::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
client_a
|
||||||
|
.fs()
|
||||||
|
.insert_tree(
|
||||||
|
path!("/dir"),
|
||||||
|
json!({
|
||||||
|
"one.rs": "const ONE: usize = 1;"
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let (project_a, worktree_id) = client_a.build_local_project(path!("/dir"), cx_a).await;
|
||||||
|
let project_id = active_call_a
|
||||||
|
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let project_b = client_b.join_remote_project(project_id, cx_b).await;
|
||||||
|
|
||||||
|
let (workspace_b, cx_b) = client_b.build_workspace(&project_b, cx_b);
|
||||||
|
let editor_b = workspace_b
|
||||||
|
.update_in(cx_b, |workspace, window, cx| {
|
||||||
|
workspace.open_path((worktree_id, "one.rs"), None, true, window, cx)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.downcast::<Editor>()
|
||||||
|
.unwrap();
|
||||||
|
let (lsp_store_b, buffer_b) = editor_b.update(cx_b, |editor, cx| {
|
||||||
|
let lsp_store = editor.project().unwrap().read(cx).lsp_store();
|
||||||
|
let buffer = editor.buffer().read(cx).as_singleton().unwrap();
|
||||||
|
(lsp_store, buffer)
|
||||||
|
});
|
||||||
|
let fake_language_server = fake_language_servers.next().await.unwrap();
|
||||||
|
cx_a.run_until_parked();
|
||||||
|
cx_b.run_until_parked();
|
||||||
|
|
||||||
|
let long_request_time = LSP_REQUEST_TIMEOUT / 2;
|
||||||
|
let (request_started_tx, mut request_started_rx) = mpsc::unbounded();
|
||||||
|
let requests_started = Arc::new(AtomicUsize::new(0));
|
||||||
|
let requests_completed = Arc::new(AtomicUsize::new(0));
|
||||||
|
let _lens_requests = fake_language_server
|
||||||
|
.set_request_handler::<lsp::request::CodeLensRequest, _, _>({
|
||||||
|
let request_started_tx = request_started_tx.clone();
|
||||||
|
let requests_started = requests_started.clone();
|
||||||
|
let requests_completed = requests_completed.clone();
|
||||||
|
move |params, cx| {
|
||||||
|
let mut request_started_tx = request_started_tx.clone();
|
||||||
|
let requests_started = requests_started.clone();
|
||||||
|
let requests_completed = requests_completed.clone();
|
||||||
|
async move {
|
||||||
|
assert_eq!(
|
||||||
|
params.text_document.uri.as_str(),
|
||||||
|
uri!("file:///dir/one.rs")
|
||||||
|
);
|
||||||
|
requests_started.fetch_add(1, atomic::Ordering::Release);
|
||||||
|
request_started_tx.send(()).await.unwrap();
|
||||||
|
cx.background_executor().timer(long_request_time).await;
|
||||||
|
let i = requests_completed.fetch_add(1, atomic::Ordering::Release) + 1;
|
||||||
|
Ok(Some(vec![lsp::CodeLens {
|
||||||
|
range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 9)),
|
||||||
|
command: Some(lsp::Command {
|
||||||
|
title: format!("LSP Command {i}"),
|
||||||
|
command: command_name.to_string(),
|
||||||
|
arguments: None,
|
||||||
|
}),
|
||||||
|
data: None,
|
||||||
|
}]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Move cursor to a location, this should trigger the code lens call.
|
||||||
|
editor_b.update_in(cx_b, |editor, window, cx| {
|
||||||
|
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||||
|
s.select_ranges([7..7])
|
||||||
|
});
|
||||||
|
});
|
||||||
|
let () = request_started_rx.next().await.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
requests_started.load(atomic::Ordering::Acquire),
|
||||||
|
1,
|
||||||
|
"Selection change should have initiated the first request"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
requests_completed.load(atomic::Ordering::Acquire),
|
||||||
|
0,
|
||||||
|
"Slow requests should be running still"
|
||||||
|
);
|
||||||
|
let _first_task = lsp_store_b.update(cx_b, |lsp_store, cx| {
|
||||||
|
lsp_store
|
||||||
|
.forget_code_lens_task(buffer_b.read(cx).remote_id())
|
||||||
|
.expect("Should have the fetch task started")
|
||||||
|
});
|
||||||
|
|
||||||
|
editor_b.update_in(cx_b, |editor, window, cx| {
|
||||||
|
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||||
|
s.select_ranges([1..1])
|
||||||
|
});
|
||||||
|
});
|
||||||
|
let () = request_started_rx.next().await.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
requests_started.load(atomic::Ordering::Acquire),
|
||||||
|
2,
|
||||||
|
"Selection change should have initiated the second request"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
requests_completed.load(atomic::Ordering::Acquire),
|
||||||
|
0,
|
||||||
|
"Slow requests should be running still"
|
||||||
|
);
|
||||||
|
let _second_task = lsp_store_b.update(cx_b, |lsp_store, cx| {
|
||||||
|
lsp_store
|
||||||
|
.forget_code_lens_task(buffer_b.read(cx).remote_id())
|
||||||
|
.expect("Should have the fetch task started for the 2nd time")
|
||||||
|
});
|
||||||
|
|
||||||
|
editor_b.update_in(cx_b, |editor, window, cx| {
|
||||||
|
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||||
|
s.select_ranges([2..2])
|
||||||
|
});
|
||||||
|
});
|
||||||
|
let () = request_started_rx.next().await.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
requests_started.load(atomic::Ordering::Acquire),
|
||||||
|
3,
|
||||||
|
"Selection change should have initiated the third request"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
requests_completed.load(atomic::Ordering::Acquire),
|
||||||
|
0,
|
||||||
|
"Slow requests should be running still"
|
||||||
|
);
|
||||||
|
|
||||||
|
_first_task.await.unwrap();
|
||||||
|
_second_task.await.unwrap();
|
||||||
|
cx_b.run_until_parked();
|
||||||
|
assert_eq!(
|
||||||
|
requests_started.load(atomic::Ordering::Acquire),
|
||||||
|
3,
|
||||||
|
"No selection changes should trigger no more code lens requests"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
requests_completed.load(atomic::Ordering::Acquire),
|
||||||
|
3,
|
||||||
|
"After enough time, all 3 LSP requests should have been served by the language server"
|
||||||
|
);
|
||||||
|
let resulting_lens_actions = editor_b
|
||||||
|
.update(cx_b, |editor, cx| {
|
||||||
|
let lsp_store = editor.project().unwrap().read(cx).lsp_store();
|
||||||
|
lsp_store.update(cx, |lsp_store, cx| {
|
||||||
|
lsp_store.code_lens_actions(&buffer_b, cx)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
resulting_lens_actions.len(),
|
||||||
|
1,
|
||||||
|
"Should have fetched one code lens action, but got: {resulting_lens_actions:?}"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
resulting_lens_actions.first().unwrap().lsp_action.title(),
|
||||||
|
"LSP Command 3",
|
||||||
|
"Only the final code lens action should be in the data"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test(iterations = 10)]
|
#[gpui::test(iterations = 10)]
|
||||||
async fn test_language_server_statuses(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
async fn test_language_server_statuses(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
||||||
let mut server = TestServer::start(cx_a.executor()).await;
|
let mut server = TestServer::start(cx_a.executor()).await;
|
||||||
|
|
|
@ -4850,6 +4850,7 @@ async fn test_definition(
|
||||||
let definitions_1 = project_b
|
let definitions_1 = project_b
|
||||||
.update(cx_b, |p, cx| p.definitions(&buffer_b, 23, cx))
|
.update(cx_b, |p, cx| p.definitions(&buffer_b, 23, cx))
|
||||||
.await
|
.await
|
||||||
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
cx_b.read(|cx| {
|
cx_b.read(|cx| {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -4885,6 +4886,7 @@ async fn test_definition(
|
||||||
let definitions_2 = project_b
|
let definitions_2 = project_b
|
||||||
.update(cx_b, |p, cx| p.definitions(&buffer_b, 33, cx))
|
.update(cx_b, |p, cx| p.definitions(&buffer_b, 33, cx))
|
||||||
.await
|
.await
|
||||||
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
cx_b.read(|cx| {
|
cx_b.read(|cx| {
|
||||||
assert_eq!(definitions_2.len(), 1);
|
assert_eq!(definitions_2.len(), 1);
|
||||||
|
@ -4922,6 +4924,7 @@ async fn test_definition(
|
||||||
let type_definitions = project_b
|
let type_definitions = project_b
|
||||||
.update(cx_b, |p, cx| p.type_definitions(&buffer_b, 7, cx))
|
.update(cx_b, |p, cx| p.type_definitions(&buffer_b, 7, cx))
|
||||||
.await
|
.await
|
||||||
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
cx_b.read(|cx| {
|
cx_b.read(|cx| {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -5060,7 +5063,7 @@ async fn test_references(
|
||||||
])))
|
])))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let references = references.await.unwrap();
|
let references = references.await.unwrap().unwrap();
|
||||||
executor.run_until_parked();
|
executor.run_until_parked();
|
||||||
project_b.read_with(cx_b, |project, cx| {
|
project_b.read_with(cx_b, |project, cx| {
|
||||||
// User is informed that a request is no longer pending.
|
// User is informed that a request is no longer pending.
|
||||||
|
@ -5104,7 +5107,7 @@ async fn test_references(
|
||||||
lsp_response_tx
|
lsp_response_tx
|
||||||
.unbounded_send(Err(anyhow!("can't find references")))
|
.unbounded_send(Err(anyhow!("can't find references")))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(references.await.unwrap(), []);
|
assert_eq!(references.await.unwrap().unwrap(), []);
|
||||||
|
|
||||||
// User is informed that the request is no longer pending.
|
// User is informed that the request is no longer pending.
|
||||||
executor.run_until_parked();
|
executor.run_until_parked();
|
||||||
|
@ -5505,7 +5508,8 @@ async fn test_lsp_hover(
|
||||||
// Request hover information as the guest.
|
// Request hover information as the guest.
|
||||||
let mut hovers = project_b
|
let mut hovers = project_b
|
||||||
.update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
|
.update(cx_b, |p, cx| p.hover(&buffer_b, 22, cx))
|
||||||
.await;
|
.await
|
||||||
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
hovers.len(),
|
hovers.len(),
|
||||||
2,
|
2,
|
||||||
|
@ -5764,7 +5768,7 @@ async fn test_open_buffer_while_getting_definition_pointing_to_it(
|
||||||
definitions = project_b.update(cx_b, |p, cx| p.definitions(&buffer_b1, 23, cx));
|
definitions = project_b.update(cx_b, |p, cx| p.definitions(&buffer_b1, 23, cx));
|
||||||
}
|
}
|
||||||
|
|
||||||
let definitions = definitions.await.unwrap();
|
let definitions = definitions.await.unwrap().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
definitions.len(),
|
definitions.len(),
|
||||||
1,
|
1,
|
||||||
|
|
|
@ -15710,7 +15710,9 @@ impl Editor {
|
||||||
};
|
};
|
||||||
|
|
||||||
cx.spawn_in(window, async move |editor, cx| {
|
cx.spawn_in(window, async move |editor, cx| {
|
||||||
let definitions = definitions.await?;
|
let Some(definitions) = definitions.await? else {
|
||||||
|
return Ok(Navigated::No);
|
||||||
|
};
|
||||||
let navigated = editor
|
let navigated = editor
|
||||||
.update_in(cx, |editor, window, cx| {
|
.update_in(cx, |editor, window, cx| {
|
||||||
editor.navigate_to_hover_links(
|
editor.navigate_to_hover_links(
|
||||||
|
@ -16052,7 +16054,9 @@ impl Editor {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let locations = references.await?;
|
let Some(locations) = references.await? else {
|
||||||
|
return anyhow::Ok(Navigated::No);
|
||||||
|
};
|
||||||
if locations.is_empty() {
|
if locations.is_empty() {
|
||||||
return anyhow::Ok(Navigated::No);
|
return anyhow::Ok(Navigated::No);
|
||||||
}
|
}
|
||||||
|
@ -21837,7 +21841,7 @@ pub trait SemanticsProvider {
|
||||||
buffer: &Entity<Buffer>,
|
buffer: &Entity<Buffer>,
|
||||||
position: text::Anchor,
|
position: text::Anchor,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Option<Task<Vec<project::Hover>>>;
|
) -> Option<Task<Option<Vec<project::Hover>>>>;
|
||||||
|
|
||||||
fn inline_values(
|
fn inline_values(
|
||||||
&self,
|
&self,
|
||||||
|
@ -21876,7 +21880,7 @@ pub trait SemanticsProvider {
|
||||||
position: text::Anchor,
|
position: text::Anchor,
|
||||||
kind: GotoDefinitionKind,
|
kind: GotoDefinitionKind,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Option<Task<Result<Vec<LocationLink>>>>;
|
) -> Option<Task<Result<Option<Vec<LocationLink>>>>>;
|
||||||
|
|
||||||
fn range_for_rename(
|
fn range_for_rename(
|
||||||
&self,
|
&self,
|
||||||
|
@ -21989,7 +21993,13 @@ impl CodeActionProvider for Entity<Project> {
|
||||||
Ok(code_lens_actions
|
Ok(code_lens_actions
|
||||||
.context("code lens fetch")?
|
.context("code lens fetch")?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(code_actions.context("code action fetch")?)
|
.flatten()
|
||||||
|
.chain(
|
||||||
|
code_actions
|
||||||
|
.context("code action fetch")?
|
||||||
|
.into_iter()
|
||||||
|
.flatten(),
|
||||||
|
)
|
||||||
.collect())
|
.collect())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -22284,7 +22294,7 @@ impl SemanticsProvider for Entity<Project> {
|
||||||
buffer: &Entity<Buffer>,
|
buffer: &Entity<Buffer>,
|
||||||
position: text::Anchor,
|
position: text::Anchor,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Option<Task<Vec<project::Hover>>> {
|
) -> Option<Task<Option<Vec<project::Hover>>>> {
|
||||||
Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
|
Some(self.update(cx, |project, cx| project.hover(buffer, position, cx)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22305,7 +22315,7 @@ impl SemanticsProvider for Entity<Project> {
|
||||||
position: text::Anchor,
|
position: text::Anchor,
|
||||||
kind: GotoDefinitionKind,
|
kind: GotoDefinitionKind,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Option<Task<Result<Vec<LocationLink>>>> {
|
) -> Option<Task<Result<Option<Vec<LocationLink>>>>> {
|
||||||
Some(self.update(cx, |project, cx| match kind {
|
Some(self.update(cx, |project, cx| match kind {
|
||||||
GotoDefinitionKind::Symbol => project.definitions(buffer, position, cx),
|
GotoDefinitionKind::Symbol => project.definitions(buffer, position, cx),
|
||||||
GotoDefinitionKind::Declaration => project.declarations(buffer, position, cx),
|
GotoDefinitionKind::Declaration => project.declarations(buffer, position, cx),
|
||||||
|
|
|
@ -559,7 +559,7 @@ pub fn show_link_definition(
|
||||||
provider.definitions(&buffer, buffer_position, preferred_kind, cx)
|
provider.definitions(&buffer, buffer_position, preferred_kind, cx)
|
||||||
})?;
|
})?;
|
||||||
if let Some(task) = task {
|
if let Some(task) = task {
|
||||||
task.await.ok().map(|definition_result| {
|
task.await.ok().flatten().map(|definition_result| {
|
||||||
(
|
(
|
||||||
definition_result.iter().find_map(|link| {
|
definition_result.iter().find_map(|link| {
|
||||||
link.origin.as_ref().and_then(|origin| {
|
link.origin.as_ref().and_then(|origin| {
|
||||||
|
|
|
@ -428,7 +428,7 @@ fn show_hover(
|
||||||
};
|
};
|
||||||
|
|
||||||
let hovers_response = if let Some(hover_request) = hover_request {
|
let hovers_response = if let Some(hover_request) = hover_request {
|
||||||
hover_request.await
|
hover_request.await.unwrap_or_default()
|
||||||
} else {
|
} else {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
};
|
};
|
||||||
|
|
|
@ -431,7 +431,7 @@ impl SemanticsProvider for BranchBufferSemanticsProvider {
|
||||||
buffer: &Entity<Buffer>,
|
buffer: &Entity<Buffer>,
|
||||||
position: text::Anchor,
|
position: text::Anchor,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Option<Task<Vec<project::Hover>>> {
|
) -> Option<Task<Option<Vec<project::Hover>>>> {
|
||||||
let buffer = self.to_base(buffer, &[position], cx)?;
|
let buffer = self.to_base(buffer, &[position], cx)?;
|
||||||
self.0.hover(&buffer, position, cx)
|
self.0.hover(&buffer, position, cx)
|
||||||
}
|
}
|
||||||
|
@ -490,7 +490,7 @@ impl SemanticsProvider for BranchBufferSemanticsProvider {
|
||||||
position: text::Anchor,
|
position: text::Anchor,
|
||||||
kind: crate::GotoDefinitionKind,
|
kind: crate::GotoDefinitionKind,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Option<Task<anyhow::Result<Vec<project::LocationLink>>>> {
|
) -> Option<Task<anyhow::Result<Option<Vec<project::LocationLink>>>>> {
|
||||||
let buffer = self.to_base(buffer, &[position], cx)?;
|
let buffer = self.to_base(buffer, &[position], cx)?;
|
||||||
self.0.definitions(&buffer, position, kind, cx)
|
self.0.definitions(&buffer, position, kind, cx)
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,7 +182,9 @@ impl Editor {
|
||||||
let signature_help = task.await;
|
let signature_help = task.await;
|
||||||
editor
|
editor
|
||||||
.update(cx, |editor, cx| {
|
.update(cx, |editor, cx| {
|
||||||
let Some(mut signature_help) = signature_help.into_iter().next() else {
|
let Some(mut signature_help) =
|
||||||
|
signature_help.unwrap_or_default().into_iter().next()
|
||||||
|
else {
|
||||||
editor
|
editor
|
||||||
.signature_help_state
|
.signature_help_state
|
||||||
.hide(SignatureHelpHiddenBy::AutoClose);
|
.hide(SignatureHelpHiddenBy::AutoClose);
|
||||||
|
|
|
@ -45,7 +45,7 @@ use util::{ConnectionResult, ResultExt, TryFutureExt, redact};
|
||||||
const JSON_RPC_VERSION: &str = "2.0";
|
const JSON_RPC_VERSION: &str = "2.0";
|
||||||
const CONTENT_LEN_HEADER: &str = "Content-Length: ";
|
const CONTENT_LEN_HEADER: &str = "Content-Length: ";
|
||||||
|
|
||||||
const LSP_REQUEST_TIMEOUT: Duration = Duration::from_secs(60 * 2);
|
pub const LSP_REQUEST_TIMEOUT: Duration = Duration::from_secs(60 * 2);
|
||||||
const SERVER_SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(5);
|
const SERVER_SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(5);
|
||||||
|
|
||||||
type NotificationHandler = Box<dyn Send + FnMut(Option<RequestId>, Value, &mut AsyncApp)>;
|
type NotificationHandler = Box<dyn Send + FnMut(Option<RequestId>, Value, &mut AsyncApp)>;
|
||||||
|
|
|
@ -3444,8 +3444,7 @@ impl LspCommand for GetCodeLens {
|
||||||
capabilities
|
capabilities
|
||||||
.server_capabilities
|
.server_capabilities
|
||||||
.code_lens_provider
|
.code_lens_provider
|
||||||
.as_ref()
|
.is_some()
|
||||||
.is_some_and(|code_lens_options| code_lens_options.resolve_provider.unwrap_or(false))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_lsp(
|
fn to_lsp(
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3415,7 +3415,7 @@ impl Project {
|
||||||
buffer: &Entity<Buffer>,
|
buffer: &Entity<Buffer>,
|
||||||
position: T,
|
position: T,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Result<Vec<LocationLink>>> {
|
) -> Task<Result<Option<Vec<LocationLink>>>> {
|
||||||
let position = position.to_point_utf16(buffer.read(cx));
|
let position = position.to_point_utf16(buffer.read(cx));
|
||||||
let guard = self.retain_remotely_created_models(cx);
|
let guard = self.retain_remotely_created_models(cx);
|
||||||
let task = self.lsp_store.update(cx, |lsp_store, cx| {
|
let task = self.lsp_store.update(cx, |lsp_store, cx| {
|
||||||
|
@ -3433,7 +3433,7 @@ impl Project {
|
||||||
buffer: &Entity<Buffer>,
|
buffer: &Entity<Buffer>,
|
||||||
position: T,
|
position: T,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Result<Vec<LocationLink>>> {
|
) -> Task<Result<Option<Vec<LocationLink>>>> {
|
||||||
let position = position.to_point_utf16(buffer.read(cx));
|
let position = position.to_point_utf16(buffer.read(cx));
|
||||||
let guard = self.retain_remotely_created_models(cx);
|
let guard = self.retain_remotely_created_models(cx);
|
||||||
let task = self.lsp_store.update(cx, |lsp_store, cx| {
|
let task = self.lsp_store.update(cx, |lsp_store, cx| {
|
||||||
|
@ -3451,7 +3451,7 @@ impl Project {
|
||||||
buffer: &Entity<Buffer>,
|
buffer: &Entity<Buffer>,
|
||||||
position: T,
|
position: T,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Result<Vec<LocationLink>>> {
|
) -> Task<Result<Option<Vec<LocationLink>>>> {
|
||||||
let position = position.to_point_utf16(buffer.read(cx));
|
let position = position.to_point_utf16(buffer.read(cx));
|
||||||
let guard = self.retain_remotely_created_models(cx);
|
let guard = self.retain_remotely_created_models(cx);
|
||||||
let task = self.lsp_store.update(cx, |lsp_store, cx| {
|
let task = self.lsp_store.update(cx, |lsp_store, cx| {
|
||||||
|
@ -3469,7 +3469,7 @@ impl Project {
|
||||||
buffer: &Entity<Buffer>,
|
buffer: &Entity<Buffer>,
|
||||||
position: T,
|
position: T,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Result<Vec<LocationLink>>> {
|
) -> Task<Result<Option<Vec<LocationLink>>>> {
|
||||||
let position = position.to_point_utf16(buffer.read(cx));
|
let position = position.to_point_utf16(buffer.read(cx));
|
||||||
let guard = self.retain_remotely_created_models(cx);
|
let guard = self.retain_remotely_created_models(cx);
|
||||||
let task = self.lsp_store.update(cx, |lsp_store, cx| {
|
let task = self.lsp_store.update(cx, |lsp_store, cx| {
|
||||||
|
@ -3487,7 +3487,7 @@ impl Project {
|
||||||
buffer: &Entity<Buffer>,
|
buffer: &Entity<Buffer>,
|
||||||
position: T,
|
position: T,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Result<Vec<Location>>> {
|
) -> Task<Result<Option<Vec<Location>>>> {
|
||||||
let position = position.to_point_utf16(buffer.read(cx));
|
let position = position.to_point_utf16(buffer.read(cx));
|
||||||
let guard = self.retain_remotely_created_models(cx);
|
let guard = self.retain_remotely_created_models(cx);
|
||||||
let task = self.lsp_store.update(cx, |lsp_store, cx| {
|
let task = self.lsp_store.update(cx, |lsp_store, cx| {
|
||||||
|
@ -3585,23 +3585,12 @@ impl Project {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn signature_help<T: ToPointUtf16>(
|
|
||||||
&self,
|
|
||||||
buffer: &Entity<Buffer>,
|
|
||||||
position: T,
|
|
||||||
cx: &mut Context<Self>,
|
|
||||||
) -> Task<Vec<SignatureHelp>> {
|
|
||||||
self.lsp_store.update(cx, |lsp_store, cx| {
|
|
||||||
lsp_store.signature_help(buffer, position, cx)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hover<T: ToPointUtf16>(
|
pub fn hover<T: ToPointUtf16>(
|
||||||
&self,
|
&self,
|
||||||
buffer: &Entity<Buffer>,
|
buffer: &Entity<Buffer>,
|
||||||
position: T,
|
position: T,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Vec<Hover>> {
|
) -> Task<Option<Vec<Hover>>> {
|
||||||
let position = position.to_point_utf16(buffer.read(cx));
|
let position = position.to_point_utf16(buffer.read(cx));
|
||||||
self.lsp_store
|
self.lsp_store
|
||||||
.update(cx, |lsp_store, cx| lsp_store.hover(buffer, position, cx))
|
.update(cx, |lsp_store, cx| lsp_store.hover(buffer, position, cx))
|
||||||
|
@ -3637,7 +3626,7 @@ impl Project {
|
||||||
range: Range<T>,
|
range: Range<T>,
|
||||||
kinds: Option<Vec<CodeActionKind>>,
|
kinds: Option<Vec<CodeActionKind>>,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Result<Vec<CodeAction>>> {
|
) -> Task<Result<Option<Vec<CodeAction>>>> {
|
||||||
let buffer = buffer_handle.read(cx);
|
let buffer = buffer_handle.read(cx);
|
||||||
let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
|
let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
|
||||||
self.lsp_store.update(cx, |lsp_store, cx| {
|
self.lsp_store.update(cx, |lsp_store, cx| {
|
||||||
|
@ -3650,7 +3639,7 @@ impl Project {
|
||||||
buffer: &Entity<Buffer>,
|
buffer: &Entity<Buffer>,
|
||||||
range: Range<T>,
|
range: Range<T>,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Result<Vec<CodeAction>>> {
|
) -> Task<Result<Option<Vec<CodeAction>>>> {
|
||||||
let snapshot = buffer.read(cx).snapshot();
|
let snapshot = buffer.read(cx).snapshot();
|
||||||
let range = range.to_point(&snapshot);
|
let range = range.to_point(&snapshot);
|
||||||
let range_start = snapshot.anchor_before(range.start);
|
let range_start = snapshot.anchor_before(range.start);
|
||||||
|
@ -3668,16 +3657,18 @@ impl Project {
|
||||||
let mut code_lens_actions = code_lens_actions
|
let mut code_lens_actions = code_lens_actions
|
||||||
.await
|
.await
|
||||||
.map_err(|e| anyhow!("code lens fetch failed: {e:#}"))?;
|
.map_err(|e| anyhow!("code lens fetch failed: {e:#}"))?;
|
||||||
code_lens_actions.retain(|code_lens_action| {
|
if let Some(code_lens_actions) = &mut code_lens_actions {
|
||||||
range
|
code_lens_actions.retain(|code_lens_action| {
|
||||||
.start
|
range
|
||||||
.cmp(&code_lens_action.range.start, &snapshot)
|
.start
|
||||||
.is_ge()
|
.cmp(&code_lens_action.range.start, &snapshot)
|
||||||
&& range
|
.is_ge()
|
||||||
.end
|
&& range
|
||||||
.cmp(&code_lens_action.range.end, &snapshot)
|
.end
|
||||||
.is_le()
|
.cmp(&code_lens_action.range.end, &snapshot)
|
||||||
});
|
.is_le()
|
||||||
|
});
|
||||||
|
}
|
||||||
Ok(code_lens_actions)
|
Ok(code_lens_actions)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3005,6 +3005,7 @@ async fn test_definition(cx: &mut gpui::TestAppContext) {
|
||||||
let mut definitions = project
|
let mut definitions = project
|
||||||
.update(cx, |project, cx| project.definitions(&buffer, 22, cx))
|
.update(cx, |project, cx| project.definitions(&buffer, 22, cx))
|
||||||
.await
|
.await
|
||||||
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Assert no new language server started
|
// Assert no new language server started
|
||||||
|
@ -3519,7 +3520,7 @@ async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) {
|
||||||
.next()
|
.next()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let action = actions.await.unwrap()[0].clone();
|
let action = actions.await.unwrap().unwrap()[0].clone();
|
||||||
let apply = project.update(cx, |project, cx| {
|
let apply = project.update(cx, |project, cx| {
|
||||||
project.apply_code_action(buffer.clone(), action, true, cx)
|
project.apply_code_action(buffer.clone(), action, true, cx)
|
||||||
});
|
});
|
||||||
|
@ -6110,6 +6111,7 @@ async fn test_multiple_language_server_hovers(cx: &mut gpui::TestAppContext) {
|
||||||
hover_task
|
hover_task
|
||||||
.await
|
.await
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
.map(|hover| hover.contents.iter().map(|block| &block.text).join("|"))
|
.map(|hover| hover.contents.iter().map(|block| &block.text).join("|"))
|
||||||
.sorted()
|
.sorted()
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
|
@ -6183,6 +6185,7 @@ async fn test_hovers_with_empty_parts(cx: &mut gpui::TestAppContext) {
|
||||||
hover_task
|
hover_task
|
||||||
.await
|
.await
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
.map(|hover| hover.contents.iter().map(|block| &block.text).join("|"))
|
.map(|hover| hover.contents.iter().map(|block| &block.text).join("|"))
|
||||||
.sorted()
|
.sorted()
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
|
@ -6261,7 +6264,7 @@ async fn test_code_actions_only_kinds(cx: &mut gpui::TestAppContext) {
|
||||||
.await
|
.await
|
||||||
.expect("The code action request should have been triggered");
|
.expect("The code action request should have been triggered");
|
||||||
|
|
||||||
let code_actions = code_actions_task.await.unwrap();
|
let code_actions = code_actions_task.await.unwrap().unwrap();
|
||||||
assert_eq!(code_actions.len(), 1);
|
assert_eq!(code_actions.len(), 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
code_actions[0].lsp_action.action_kind(),
|
code_actions[0].lsp_action.action_kind(),
|
||||||
|
@ -6420,6 +6423,7 @@ async fn test_multiple_language_server_actions(cx: &mut gpui::TestAppContext) {
|
||||||
code_actions_task
|
code_actions_task
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|code_action| code_action.lsp_action.title().to_owned())
|
.map(|code_action| code_action.lsp_action.title().to_owned())
|
||||||
.sorted()
|
.sorted()
|
||||||
|
|
|
@ -753,28 +753,47 @@ message TextEdit {
|
||||||
PointUtf16 lsp_range_end = 3;
|
PointUtf16 lsp_range_end = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message MultiLspQuery {
|
message LspQuery {
|
||||||
uint64 project_id = 1;
|
uint64 project_id = 1;
|
||||||
uint64 buffer_id = 2;
|
uint64 lsp_request_id = 2;
|
||||||
repeated VectorClockEntry version = 3;
|
|
||||||
oneof strategy {
|
|
||||||
AllLanguageServers all = 4;
|
|
||||||
}
|
|
||||||
oneof request {
|
oneof request {
|
||||||
|
GetReferences get_references = 3;
|
||||||
|
GetDocumentColor get_document_color = 4;
|
||||||
GetHover get_hover = 5;
|
GetHover get_hover = 5;
|
||||||
GetCodeActions get_code_actions = 6;
|
GetCodeActions get_code_actions = 6;
|
||||||
GetSignatureHelp get_signature_help = 7;
|
GetSignatureHelp get_signature_help = 7;
|
||||||
GetCodeLens get_code_lens = 8;
|
GetCodeLens get_code_lens = 8;
|
||||||
GetDocumentDiagnostics get_document_diagnostics = 9;
|
GetDocumentDiagnostics get_document_diagnostics = 9;
|
||||||
GetDocumentColor get_document_color = 10;
|
GetDefinition get_definition = 10;
|
||||||
GetDefinition get_definition = 11;
|
GetDeclaration get_declaration = 11;
|
||||||
GetDeclaration get_declaration = 12;
|
GetTypeDefinition get_type_definition = 12;
|
||||||
GetTypeDefinition get_type_definition = 13;
|
GetImplementation get_implementation = 13;
|
||||||
GetImplementation get_implementation = 14;
|
|
||||||
GetReferences get_references = 15;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message LspQueryResponse {
|
||||||
|
uint64 project_id = 1;
|
||||||
|
uint64 lsp_request_id = 2;
|
||||||
|
repeated LspResponse responses = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LspResponse {
|
||||||
|
oneof response {
|
||||||
|
GetHoverResponse get_hover_response = 1;
|
||||||
|
GetCodeActionsResponse get_code_actions_response = 2;
|
||||||
|
GetSignatureHelpResponse get_signature_help_response = 3;
|
||||||
|
GetCodeLensResponse get_code_lens_response = 4;
|
||||||
|
GetDocumentDiagnosticsResponse get_document_diagnostics_response = 5;
|
||||||
|
GetDocumentColorResponse get_document_color_response = 6;
|
||||||
|
GetDefinitionResponse get_definition_response = 8;
|
||||||
|
GetDeclarationResponse get_declaration_response = 9;
|
||||||
|
GetTypeDefinitionResponse get_type_definition_response = 10;
|
||||||
|
GetImplementationResponse get_implementation_response = 11;
|
||||||
|
GetReferencesResponse get_references_response = 12;
|
||||||
|
}
|
||||||
|
uint64 server_id = 7;
|
||||||
|
}
|
||||||
|
|
||||||
message AllLanguageServers {}
|
message AllLanguageServers {}
|
||||||
|
|
||||||
message LanguageServerSelector {
|
message LanguageServerSelector {
|
||||||
|
@ -798,27 +817,6 @@ message StopLanguageServers {
|
||||||
bool all = 4;
|
bool all = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message MultiLspQueryResponse {
|
|
||||||
repeated LspResponse responses = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message LspResponse {
|
|
||||||
oneof response {
|
|
||||||
GetHoverResponse get_hover_response = 1;
|
|
||||||
GetCodeActionsResponse get_code_actions_response = 2;
|
|
||||||
GetSignatureHelpResponse get_signature_help_response = 3;
|
|
||||||
GetCodeLensResponse get_code_lens_response = 4;
|
|
||||||
GetDocumentDiagnosticsResponse get_document_diagnostics_response = 5;
|
|
||||||
GetDocumentColorResponse get_document_color_response = 6;
|
|
||||||
GetDefinitionResponse get_definition_response = 8;
|
|
||||||
GetDeclarationResponse get_declaration_response = 9;
|
|
||||||
GetTypeDefinitionResponse get_type_definition_response = 10;
|
|
||||||
GetImplementationResponse get_implementation_response = 11;
|
|
||||||
GetReferencesResponse get_references_response = 12;
|
|
||||||
}
|
|
||||||
uint64 server_id = 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
message LspExtRunnables {
|
message LspExtRunnables {
|
||||||
uint64 project_id = 1;
|
uint64 project_id = 1;
|
||||||
uint64 buffer_id = 2;
|
uint64 buffer_id = 2;
|
||||||
|
@ -909,3 +907,30 @@ message PullWorkspaceDiagnostics {
|
||||||
uint64 project_id = 1;
|
uint64 project_id = 1;
|
||||||
uint64 server_id = 2;
|
uint64 server_id = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo(lsp) remove after Zed Stable hits v0.204.x
|
||||||
|
message MultiLspQuery {
|
||||||
|
uint64 project_id = 1;
|
||||||
|
uint64 buffer_id = 2;
|
||||||
|
repeated VectorClockEntry version = 3;
|
||||||
|
oneof strategy {
|
||||||
|
AllLanguageServers all = 4;
|
||||||
|
}
|
||||||
|
oneof request {
|
||||||
|
GetHover get_hover = 5;
|
||||||
|
GetCodeActions get_code_actions = 6;
|
||||||
|
GetSignatureHelp get_signature_help = 7;
|
||||||
|
GetCodeLens get_code_lens = 8;
|
||||||
|
GetDocumentDiagnostics get_document_diagnostics = 9;
|
||||||
|
GetDocumentColor get_document_color = 10;
|
||||||
|
GetDefinition get_definition = 11;
|
||||||
|
GetDeclaration get_declaration = 12;
|
||||||
|
GetTypeDefinition get_type_definition = 13;
|
||||||
|
GetImplementation get_implementation = 14;
|
||||||
|
GetReferences get_references = 15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message MultiLspQueryResponse {
|
||||||
|
repeated LspResponse responses = 1;
|
||||||
|
}
|
||||||
|
|
|
@ -393,7 +393,10 @@ message Envelope {
|
||||||
GetCrashFilesResponse get_crash_files_response = 362;
|
GetCrashFilesResponse get_crash_files_response = 362;
|
||||||
|
|
||||||
GitClone git_clone = 363;
|
GitClone git_clone = 363;
|
||||||
GitCloneResponse git_clone_response = 364; // current max
|
GitCloneResponse git_clone_response = 364;
|
||||||
|
|
||||||
|
LspQuery lsp_query = 365;
|
||||||
|
LspQueryResponse lsp_query_response = 366; // current max
|
||||||
}
|
}
|
||||||
|
|
||||||
reserved 87 to 88;
|
reserved 87 to 88;
|
||||||
|
|
|
@ -69,3 +69,32 @@ macro_rules! entity_messages {
|
||||||
})*
|
})*
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! lsp_messages {
|
||||||
|
($(($request_name:ident, $response_name:ident, $stop_previous_requests:expr)),* $(,)?) => {
|
||||||
|
$(impl LspRequestMessage for $request_name {
|
||||||
|
type Response = $response_name;
|
||||||
|
|
||||||
|
fn to_proto_query(self) -> $crate::lsp_query::Request {
|
||||||
|
$crate::lsp_query::Request::$request_name(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn response_to_proto_query(response: Self::Response) -> $crate::lsp_response::Response {
|
||||||
|
$crate::lsp_response::Response::$response_name(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn buffer_id(&self) -> u64 {
|
||||||
|
self.buffer_id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn buffer_version(&self) -> &[$crate::VectorClockEntry] {
|
||||||
|
&self.version
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop_previous_requests() -> bool {
|
||||||
|
$stop_previous_requests
|
||||||
|
}
|
||||||
|
})*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -169,6 +169,9 @@ messages!(
|
||||||
(MarkNotificationRead, Foreground),
|
(MarkNotificationRead, Foreground),
|
||||||
(MoveChannel, Foreground),
|
(MoveChannel, Foreground),
|
||||||
(ReorderChannel, Foreground),
|
(ReorderChannel, Foreground),
|
||||||
|
(LspQuery, Background),
|
||||||
|
(LspQueryResponse, Background),
|
||||||
|
// todo(lsp) remove after Zed Stable hits v0.204.x
|
||||||
(MultiLspQuery, Background),
|
(MultiLspQuery, Background),
|
||||||
(MultiLspQueryResponse, Background),
|
(MultiLspQueryResponse, Background),
|
||||||
(OnTypeFormatting, Background),
|
(OnTypeFormatting, Background),
|
||||||
|
@ -426,7 +429,10 @@ request_messages!(
|
||||||
(SetRoomParticipantRole, Ack),
|
(SetRoomParticipantRole, Ack),
|
||||||
(BlameBuffer, BlameBufferResponse),
|
(BlameBuffer, BlameBufferResponse),
|
||||||
(RejoinRemoteProjects, RejoinRemoteProjectsResponse),
|
(RejoinRemoteProjects, RejoinRemoteProjectsResponse),
|
||||||
|
// todo(lsp) remove after Zed Stable hits v0.204.x
|
||||||
(MultiLspQuery, MultiLspQueryResponse),
|
(MultiLspQuery, MultiLspQueryResponse),
|
||||||
|
(LspQuery, Ack),
|
||||||
|
(LspQueryResponse, Ack),
|
||||||
(RestartLanguageServers, Ack),
|
(RestartLanguageServers, Ack),
|
||||||
(StopLanguageServers, Ack),
|
(StopLanguageServers, Ack),
|
||||||
(OpenContext, OpenContextResponse),
|
(OpenContext, OpenContextResponse),
|
||||||
|
@ -478,6 +484,20 @@ request_messages!(
|
||||||
(GitClone, GitCloneResponse)
|
(GitClone, GitCloneResponse)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
lsp_messages!(
|
||||||
|
(GetReferences, GetReferencesResponse, true),
|
||||||
|
(GetDocumentColor, GetDocumentColorResponse, true),
|
||||||
|
(GetHover, GetHoverResponse, true),
|
||||||
|
(GetCodeActions, GetCodeActionsResponse, true),
|
||||||
|
(GetSignatureHelp, GetSignatureHelpResponse, true),
|
||||||
|
(GetCodeLens, GetCodeLensResponse, true),
|
||||||
|
(GetDocumentDiagnostics, GetDocumentDiagnosticsResponse, true),
|
||||||
|
(GetDefinition, GetDefinitionResponse, true),
|
||||||
|
(GetDeclaration, GetDeclarationResponse, true),
|
||||||
|
(GetTypeDefinition, GetTypeDefinitionResponse, true),
|
||||||
|
(GetImplementation, GetImplementationResponse, true),
|
||||||
|
);
|
||||||
|
|
||||||
entity_messages!(
|
entity_messages!(
|
||||||
{project_id, ShareProject},
|
{project_id, ShareProject},
|
||||||
AddProjectCollaborator,
|
AddProjectCollaborator,
|
||||||
|
@ -520,6 +540,9 @@ entity_messages!(
|
||||||
LeaveProject,
|
LeaveProject,
|
||||||
LinkedEditingRange,
|
LinkedEditingRange,
|
||||||
LoadCommitDiff,
|
LoadCommitDiff,
|
||||||
|
LspQuery,
|
||||||
|
LspQueryResponse,
|
||||||
|
// todo(lsp) remove after Zed Stable hits v0.204.x
|
||||||
MultiLspQuery,
|
MultiLspQuery,
|
||||||
RestartLanguageServers,
|
RestartLanguageServers,
|
||||||
StopLanguageServers,
|
StopLanguageServers,
|
||||||
|
@ -777,6 +800,28 @@ pub fn split_repository_update(
|
||||||
}])
|
}])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl LspQuery {
|
||||||
|
pub fn query_name_and_write_permissions(&self) -> (&str, bool) {
|
||||||
|
match self.request {
|
||||||
|
Some(lsp_query::Request::GetHover(_)) => ("GetHover", false),
|
||||||
|
Some(lsp_query::Request::GetCodeActions(_)) => ("GetCodeActions", true),
|
||||||
|
Some(lsp_query::Request::GetSignatureHelp(_)) => ("GetSignatureHelp", false),
|
||||||
|
Some(lsp_query::Request::GetCodeLens(_)) => ("GetCodeLens", true),
|
||||||
|
Some(lsp_query::Request::GetDocumentDiagnostics(_)) => {
|
||||||
|
("GetDocumentDiagnostics", false)
|
||||||
|
}
|
||||||
|
Some(lsp_query::Request::GetDefinition(_)) => ("GetDefinition", false),
|
||||||
|
Some(lsp_query::Request::GetDeclaration(_)) => ("GetDeclaration", false),
|
||||||
|
Some(lsp_query::Request::GetTypeDefinition(_)) => ("GetTypeDefinition", false),
|
||||||
|
Some(lsp_query::Request::GetImplementation(_)) => ("GetImplementation", false),
|
||||||
|
Some(lsp_query::Request::GetReferences(_)) => ("GetReferences", false),
|
||||||
|
Some(lsp_query::Request::GetDocumentColor(_)) => ("GetDocumentColor", false),
|
||||||
|
None => ("<unknown>", true),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo(lsp) remove after Zed Stable hits v0.204.x
|
||||||
impl MultiLspQuery {
|
impl MultiLspQuery {
|
||||||
pub fn request_str(&self) -> &str {
|
pub fn request_str(&self) -> &str {
|
||||||
match self.request {
|
match self.request {
|
||||||
|
|
|
@ -31,6 +31,58 @@ pub trait RequestMessage: EnvelopedMessage {
|
||||||
type Response: EnvelopedMessage;
|
type Response: EnvelopedMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A trait to bind LSP request and responses for the proto layer.
|
||||||
|
/// Should be used for every LSP request that has to traverse through the proto layer.
|
||||||
|
///
|
||||||
|
/// `lsp_messages` macro in the same crate provides a convenient way to implement this.
|
||||||
|
pub trait LspRequestMessage: EnvelopedMessage {
|
||||||
|
type Response: EnvelopedMessage;
|
||||||
|
|
||||||
|
fn to_proto_query(self) -> crate::lsp_query::Request;
|
||||||
|
|
||||||
|
fn response_to_proto_query(response: Self::Response) -> crate::lsp_response::Response;
|
||||||
|
|
||||||
|
fn buffer_id(&self) -> u64;
|
||||||
|
|
||||||
|
fn buffer_version(&self) -> &[crate::VectorClockEntry];
|
||||||
|
|
||||||
|
/// Whether to deduplicate the requests, or keep the previous ones running when another
|
||||||
|
/// request of the same kind is processed.
|
||||||
|
fn stop_previous_requests() -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct LspRequestId(pub u64);
|
||||||
|
|
||||||
|
/// A response from a single language server.
|
||||||
|
/// There could be multiple responses for a single LSP request,
|
||||||
|
/// from different servers.
|
||||||
|
pub struct ProtoLspResponse<R> {
|
||||||
|
pub server_id: u64,
|
||||||
|
pub response: R,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProtoLspResponse<Box<dyn AnyTypedEnvelope>> {
|
||||||
|
pub fn into_response<T: LspRequestMessage>(self) -> Result<ProtoLspResponse<T::Response>> {
|
||||||
|
let envelope = self
|
||||||
|
.response
|
||||||
|
.into_any()
|
||||||
|
.downcast::<TypedEnvelope<T::Response>>()
|
||||||
|
.map_err(|_| {
|
||||||
|
anyhow::anyhow!(
|
||||||
|
"cannot downcast LspResponse to {} for message {}",
|
||||||
|
T::Response::NAME,
|
||||||
|
T::NAME,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(ProtoLspResponse {
|
||||||
|
server_id: self.server_id,
|
||||||
|
response: envelope.payload,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait AnyTypedEnvelope: Any + Send + Sync {
|
pub trait AnyTypedEnvelope: Any + Send + Sync {
|
||||||
fn payload_type_id(&self) -> TypeId;
|
fn payload_type_id(&self) -> TypeId;
|
||||||
fn payload_type_name(&self) -> &'static str;
|
fn payload_type_name(&self) -> &'static str;
|
||||||
|
|
|
@ -1,35 +1,48 @@
|
||||||
use anyhow::Context;
|
use anyhow::{Context, Result};
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use futures::{
|
use futures::{
|
||||||
Future, FutureExt as _,
|
Future, FutureExt as _,
|
||||||
|
channel::oneshot,
|
||||||
future::{BoxFuture, LocalBoxFuture},
|
future::{BoxFuture, LocalBoxFuture},
|
||||||
};
|
};
|
||||||
use gpui::{AnyEntity, AnyWeakEntity, AsyncApp, Entity};
|
use gpui::{AnyEntity, AnyWeakEntity, AsyncApp, BackgroundExecutor, Entity, FutureExt as _};
|
||||||
|
use parking_lot::Mutex;
|
||||||
use proto::{
|
use proto::{
|
||||||
AnyTypedEnvelope, EntityMessage, Envelope, EnvelopedMessage, RequestMessage, TypedEnvelope,
|
AnyTypedEnvelope, EntityMessage, Envelope, EnvelopedMessage, LspRequestId, LspRequestMessage,
|
||||||
error::ErrorExt as _,
|
RequestMessage, TypedEnvelope, error::ErrorExt as _,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
sync::{Arc, Weak},
|
sync::{
|
||||||
|
Arc, OnceLock,
|
||||||
|
atomic::{self, AtomicU64},
|
||||||
|
},
|
||||||
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AnyProtoClient(Arc<dyn ProtoClient>);
|
pub struct AnyProtoClient(Arc<State>);
|
||||||
|
|
||||||
impl AnyProtoClient {
|
type RequestIds = Arc<
|
||||||
pub fn downgrade(&self) -> AnyWeakProtoClient {
|
Mutex<
|
||||||
AnyWeakProtoClient(Arc::downgrade(&self.0))
|
HashMap<
|
||||||
}
|
LspRequestId,
|
||||||
}
|
oneshot::Sender<
|
||||||
|
Result<
|
||||||
|
Option<TypedEnvelope<Vec<proto::ProtoLspResponse<Box<dyn AnyTypedEnvelope>>>>>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
>;
|
||||||
|
|
||||||
#[derive(Clone)]
|
static NEXT_LSP_REQUEST_ID: OnceLock<Arc<AtomicU64>> = OnceLock::new();
|
||||||
pub struct AnyWeakProtoClient(Weak<dyn ProtoClient>);
|
static REQUEST_IDS: OnceLock<RequestIds> = OnceLock::new();
|
||||||
|
|
||||||
impl AnyWeakProtoClient {
|
struct State {
|
||||||
pub fn upgrade(&self) -> Option<AnyProtoClient> {
|
client: Arc<dyn ProtoClient>,
|
||||||
self.0.upgrade().map(AnyProtoClient)
|
next_lsp_request_id: Arc<AtomicU64>,
|
||||||
}
|
request_ids: RequestIds,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ProtoClient: Send + Sync {
|
pub trait ProtoClient: Send + Sync {
|
||||||
|
@ -37,11 +50,11 @@ pub trait ProtoClient: Send + Sync {
|
||||||
&self,
|
&self,
|
||||||
envelope: Envelope,
|
envelope: Envelope,
|
||||||
request_type: &'static str,
|
request_type: &'static str,
|
||||||
) -> BoxFuture<'static, anyhow::Result<Envelope>>;
|
) -> BoxFuture<'static, Result<Envelope>>;
|
||||||
|
|
||||||
fn send(&self, envelope: Envelope, message_type: &'static str) -> anyhow::Result<()>;
|
fn send(&self, envelope: Envelope, message_type: &'static str) -> Result<()>;
|
||||||
|
|
||||||
fn send_response(&self, envelope: Envelope, message_type: &'static str) -> anyhow::Result<()>;
|
fn send_response(&self, envelope: Envelope, message_type: &'static str) -> Result<()>;
|
||||||
|
|
||||||
fn message_handler_set(&self) -> &parking_lot::Mutex<ProtoMessageHandlerSet>;
|
fn message_handler_set(&self) -> &parking_lot::Mutex<ProtoMessageHandlerSet>;
|
||||||
|
|
||||||
|
@ -65,7 +78,7 @@ pub type ProtoMessageHandler = Arc<
|
||||||
Box<dyn AnyTypedEnvelope>,
|
Box<dyn AnyTypedEnvelope>,
|
||||||
AnyProtoClient,
|
AnyProtoClient,
|
||||||
AsyncApp,
|
AsyncApp,
|
||||||
) -> LocalBoxFuture<'static, anyhow::Result<()>>,
|
) -> LocalBoxFuture<'static, Result<()>>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
impl ProtoMessageHandlerSet {
|
impl ProtoMessageHandlerSet {
|
||||||
|
@ -113,7 +126,7 @@ impl ProtoMessageHandlerSet {
|
||||||
message: Box<dyn AnyTypedEnvelope>,
|
message: Box<dyn AnyTypedEnvelope>,
|
||||||
client: AnyProtoClient,
|
client: AnyProtoClient,
|
||||||
cx: AsyncApp,
|
cx: AsyncApp,
|
||||||
) -> Option<LocalBoxFuture<'static, anyhow::Result<()>>> {
|
) -> Option<LocalBoxFuture<'static, Result<()>>> {
|
||||||
let payload_type_id = message.payload_type_id();
|
let payload_type_id = message.payload_type_id();
|
||||||
let mut this = this.lock();
|
let mut this = this.lock();
|
||||||
let handler = this.message_handlers.get(&payload_type_id)?.clone();
|
let handler = this.message_handlers.get(&payload_type_id)?.clone();
|
||||||
|
@ -169,43 +182,195 @@ where
|
||||||
T: ProtoClient + 'static,
|
T: ProtoClient + 'static,
|
||||||
{
|
{
|
||||||
fn from(client: Arc<T>) -> Self {
|
fn from(client: Arc<T>) -> Self {
|
||||||
Self(client)
|
Self::new(client)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnyProtoClient {
|
impl AnyProtoClient {
|
||||||
pub fn new<T: ProtoClient + 'static>(client: Arc<T>) -> Self {
|
pub fn new<T: ProtoClient + 'static>(client: Arc<T>) -> Self {
|
||||||
Self(client)
|
Self(Arc::new(State {
|
||||||
|
client,
|
||||||
|
next_lsp_request_id: NEXT_LSP_REQUEST_ID
|
||||||
|
.get_or_init(|| Arc::new(AtomicU64::new(0)))
|
||||||
|
.clone(),
|
||||||
|
request_ids: REQUEST_IDS.get_or_init(RequestIds::default).clone(),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_via_collab(&self) -> bool {
|
pub fn is_via_collab(&self) -> bool {
|
||||||
self.0.is_via_collab()
|
self.0.client.is_via_collab()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn request<T: RequestMessage>(
|
pub fn request<T: RequestMessage>(
|
||||||
&self,
|
&self,
|
||||||
request: T,
|
request: T,
|
||||||
) -> impl Future<Output = anyhow::Result<T::Response>> + use<T> {
|
) -> impl Future<Output = Result<T::Response>> + use<T> {
|
||||||
let envelope = request.into_envelope(0, None, None);
|
let envelope = request.into_envelope(0, None, None);
|
||||||
let response = self.0.request(envelope, T::NAME);
|
let response = self.0.client.request(envelope, T::NAME);
|
||||||
async move {
|
async move {
|
||||||
T::Response::from_envelope(response.await?)
|
T::Response::from_envelope(response.await?)
|
||||||
.context("received response of the wrong type")
|
.context("received response of the wrong type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send<T: EnvelopedMessage>(&self, request: T) -> anyhow::Result<()> {
|
pub fn send<T: EnvelopedMessage>(&self, request: T) -> Result<()> {
|
||||||
let envelope = request.into_envelope(0, None, None);
|
let envelope = request.into_envelope(0, None, None);
|
||||||
self.0.send(envelope, T::NAME)
|
self.0.client.send(envelope, T::NAME)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_response<T: EnvelopedMessage>(
|
pub fn send_response<T: EnvelopedMessage>(&self, request_id: u32, request: T) -> Result<()> {
|
||||||
&self,
|
|
||||||
request_id: u32,
|
|
||||||
request: T,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let envelope = request.into_envelope(0, Some(request_id), None);
|
let envelope = request.into_envelope(0, Some(request_id), None);
|
||||||
self.0.send(envelope, T::NAME)
|
self.0.client.send(envelope, T::NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn request_lsp<T>(
|
||||||
|
&self,
|
||||||
|
project_id: u64,
|
||||||
|
timeout: Duration,
|
||||||
|
executor: BackgroundExecutor,
|
||||||
|
request: T,
|
||||||
|
) -> impl Future<
|
||||||
|
Output = Result<Option<TypedEnvelope<Vec<proto::ProtoLspResponse<T::Response>>>>>,
|
||||||
|
> + use<T>
|
||||||
|
where
|
||||||
|
T: LspRequestMessage,
|
||||||
|
{
|
||||||
|
let new_id = LspRequestId(
|
||||||
|
self.0
|
||||||
|
.next_lsp_request_id
|
||||||
|
.fetch_add(1, atomic::Ordering::Acquire),
|
||||||
|
);
|
||||||
|
let (tx, rx) = oneshot::channel();
|
||||||
|
{
|
||||||
|
self.0.request_ids.lock().insert(new_id, tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
let query = proto::LspQuery {
|
||||||
|
project_id,
|
||||||
|
lsp_request_id: new_id.0,
|
||||||
|
request: Some(request.clone().to_proto_query()),
|
||||||
|
};
|
||||||
|
let request = self.request(query);
|
||||||
|
let request_ids = self.0.request_ids.clone();
|
||||||
|
async move {
|
||||||
|
match request.await {
|
||||||
|
Ok(_request_enqueued) => {}
|
||||||
|
Err(e) => {
|
||||||
|
request_ids.lock().remove(&new_id);
|
||||||
|
return Err(e).context("sending LSP proto request");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = rx.with_timeout(timeout, &executor).await;
|
||||||
|
{
|
||||||
|
request_ids.lock().remove(&new_id);
|
||||||
|
}
|
||||||
|
match response {
|
||||||
|
Ok(Ok(response)) => {
|
||||||
|
let response = response
|
||||||
|
.context("waiting for LSP proto response")?
|
||||||
|
.map(|response| {
|
||||||
|
anyhow::Ok(TypedEnvelope {
|
||||||
|
payload: response
|
||||||
|
.payload
|
||||||
|
.into_iter()
|
||||||
|
.map(|lsp_response| lsp_response.into_response::<T>())
|
||||||
|
.collect::<Result<Vec<_>>>()?,
|
||||||
|
sender_id: response.sender_id,
|
||||||
|
original_sender_id: response.original_sender_id,
|
||||||
|
message_id: response.message_id,
|
||||||
|
received_at: response.received_at,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.transpose()
|
||||||
|
.context("converting LSP proto response")?;
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
|
Err(_cancelled_due_timeout) => Ok(None),
|
||||||
|
Ok(Err(_channel_dropped)) => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_lsp_response<T: LspRequestMessage>(
|
||||||
|
&self,
|
||||||
|
project_id: u64,
|
||||||
|
lsp_request_id: LspRequestId,
|
||||||
|
server_responses: HashMap<u64, T::Response>,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.send(proto::LspQueryResponse {
|
||||||
|
project_id,
|
||||||
|
lsp_request_id: lsp_request_id.0,
|
||||||
|
responses: server_responses
|
||||||
|
.into_iter()
|
||||||
|
.map(|(server_id, response)| proto::LspResponse {
|
||||||
|
server_id,
|
||||||
|
response: Some(T::response_to_proto_query(response)),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_lsp_response(&self, mut envelope: TypedEnvelope<proto::LspQueryResponse>) {
|
||||||
|
let request_id = LspRequestId(envelope.payload.lsp_request_id);
|
||||||
|
let mut response_senders = self.0.request_ids.lock();
|
||||||
|
if let Some(tx) = response_senders.remove(&request_id) {
|
||||||
|
let responses = envelope.payload.responses.drain(..).collect::<Vec<_>>();
|
||||||
|
tx.send(Ok(Some(proto::TypedEnvelope {
|
||||||
|
sender_id: envelope.sender_id,
|
||||||
|
original_sender_id: envelope.original_sender_id,
|
||||||
|
message_id: envelope.message_id,
|
||||||
|
received_at: envelope.received_at,
|
||||||
|
payload: responses
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|response| {
|
||||||
|
use proto::lsp_response::Response;
|
||||||
|
|
||||||
|
let server_id = response.server_id;
|
||||||
|
let response = match response.response? {
|
||||||
|
Response::GetReferencesResponse(response) => {
|
||||||
|
to_any_envelope(&envelope, response)
|
||||||
|
}
|
||||||
|
Response::GetDocumentColorResponse(response) => {
|
||||||
|
to_any_envelope(&envelope, response)
|
||||||
|
}
|
||||||
|
Response::GetHoverResponse(response) => {
|
||||||
|
to_any_envelope(&envelope, response)
|
||||||
|
}
|
||||||
|
Response::GetCodeActionsResponse(response) => {
|
||||||
|
to_any_envelope(&envelope, response)
|
||||||
|
}
|
||||||
|
Response::GetSignatureHelpResponse(response) => {
|
||||||
|
to_any_envelope(&envelope, response)
|
||||||
|
}
|
||||||
|
Response::GetCodeLensResponse(response) => {
|
||||||
|
to_any_envelope(&envelope, response)
|
||||||
|
}
|
||||||
|
Response::GetDocumentDiagnosticsResponse(response) => {
|
||||||
|
to_any_envelope(&envelope, response)
|
||||||
|
}
|
||||||
|
Response::GetDefinitionResponse(response) => {
|
||||||
|
to_any_envelope(&envelope, response)
|
||||||
|
}
|
||||||
|
Response::GetDeclarationResponse(response) => {
|
||||||
|
to_any_envelope(&envelope, response)
|
||||||
|
}
|
||||||
|
Response::GetTypeDefinitionResponse(response) => {
|
||||||
|
to_any_envelope(&envelope, response)
|
||||||
|
}
|
||||||
|
Response::GetImplementationResponse(response) => {
|
||||||
|
to_any_envelope(&envelope, response)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Some(proto::ProtoLspResponse {
|
||||||
|
server_id,
|
||||||
|
response,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
})))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_request_handler<M, E, H, F>(&self, entity: gpui::WeakEntity<E>, handler: H)
|
pub fn add_request_handler<M, E, H, F>(&self, entity: gpui::WeakEntity<E>, handler: H)
|
||||||
|
@ -213,31 +378,35 @@ impl AnyProtoClient {
|
||||||
M: RequestMessage,
|
M: RequestMessage,
|
||||||
E: 'static,
|
E: 'static,
|
||||||
H: 'static + Sync + Fn(Entity<E>, TypedEnvelope<M>, AsyncApp) -> F + Send + Sync,
|
H: 'static + Sync + Fn(Entity<E>, TypedEnvelope<M>, AsyncApp) -> F + Send + Sync,
|
||||||
F: 'static + Future<Output = anyhow::Result<M::Response>>,
|
F: 'static + Future<Output = Result<M::Response>>,
|
||||||
{
|
{
|
||||||
self.0.message_handler_set().lock().add_message_handler(
|
self.0
|
||||||
TypeId::of::<M>(),
|
.client
|
||||||
entity.into(),
|
.message_handler_set()
|
||||||
Arc::new(move |entity, envelope, client, cx| {
|
.lock()
|
||||||
let entity = entity.downcast::<E>().unwrap();
|
.add_message_handler(
|
||||||
let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap();
|
TypeId::of::<M>(),
|
||||||
let request_id = envelope.message_id();
|
entity.into(),
|
||||||
handler(entity, *envelope, cx)
|
Arc::new(move |entity, envelope, client, cx| {
|
||||||
.then(move |result| async move {
|
let entity = entity.downcast::<E>().unwrap();
|
||||||
match result {
|
let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap();
|
||||||
Ok(response) => {
|
let request_id = envelope.message_id();
|
||||||
client.send_response(request_id, response)?;
|
handler(entity, *envelope, cx)
|
||||||
Ok(())
|
.then(move |result| async move {
|
||||||
|
match result {
|
||||||
|
Ok(response) => {
|
||||||
|
client.send_response(request_id, response)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
client.send_response(request_id, error.to_proto())?;
|
||||||
|
Err(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(error) => {
|
})
|
||||||
client.send_response(request_id, error.to_proto())?;
|
.boxed_local()
|
||||||
Err(error)
|
}),
|
||||||
}
|
)
|
||||||
}
|
|
||||||
})
|
|
||||||
.boxed_local()
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_entity_request_handler<M, E, H, F>(&self, handler: H)
|
pub fn add_entity_request_handler<M, E, H, F>(&self, handler: H)
|
||||||
|
@ -245,7 +414,7 @@ impl AnyProtoClient {
|
||||||
M: EnvelopedMessage + RequestMessage + EntityMessage,
|
M: EnvelopedMessage + RequestMessage + EntityMessage,
|
||||||
E: 'static,
|
E: 'static,
|
||||||
H: 'static + Sync + Send + Fn(gpui::Entity<E>, TypedEnvelope<M>, AsyncApp) -> F,
|
H: 'static + Sync + Send + Fn(gpui::Entity<E>, TypedEnvelope<M>, AsyncApp) -> F,
|
||||||
F: 'static + Future<Output = anyhow::Result<M::Response>>,
|
F: 'static + Future<Output = Result<M::Response>>,
|
||||||
{
|
{
|
||||||
let message_type_id = TypeId::of::<M>();
|
let message_type_id = TypeId::of::<M>();
|
||||||
let entity_type_id = TypeId::of::<E>();
|
let entity_type_id = TypeId::of::<E>();
|
||||||
|
@ -257,6 +426,7 @@ impl AnyProtoClient {
|
||||||
.remote_entity_id()
|
.remote_entity_id()
|
||||||
};
|
};
|
||||||
self.0
|
self.0
|
||||||
|
.client
|
||||||
.message_handler_set()
|
.message_handler_set()
|
||||||
.lock()
|
.lock()
|
||||||
.add_entity_message_handler(
|
.add_entity_message_handler(
|
||||||
|
@ -290,7 +460,7 @@ impl AnyProtoClient {
|
||||||
M: EnvelopedMessage + EntityMessage,
|
M: EnvelopedMessage + EntityMessage,
|
||||||
E: 'static,
|
E: 'static,
|
||||||
H: 'static + Sync + Send + Fn(gpui::Entity<E>, TypedEnvelope<M>, AsyncApp) -> F,
|
H: 'static + Sync + Send + Fn(gpui::Entity<E>, TypedEnvelope<M>, AsyncApp) -> F,
|
||||||
F: 'static + Future<Output = anyhow::Result<()>>,
|
F: 'static + Future<Output = Result<()>>,
|
||||||
{
|
{
|
||||||
let message_type_id = TypeId::of::<M>();
|
let message_type_id = TypeId::of::<M>();
|
||||||
let entity_type_id = TypeId::of::<E>();
|
let entity_type_id = TypeId::of::<E>();
|
||||||
|
@ -302,6 +472,7 @@ impl AnyProtoClient {
|
||||||
.remote_entity_id()
|
.remote_entity_id()
|
||||||
};
|
};
|
||||||
self.0
|
self.0
|
||||||
|
.client
|
||||||
.message_handler_set()
|
.message_handler_set()
|
||||||
.lock()
|
.lock()
|
||||||
.add_entity_message_handler(
|
.add_entity_message_handler(
|
||||||
|
@ -319,7 +490,7 @@ impl AnyProtoClient {
|
||||||
pub fn subscribe_to_entity<E: 'static>(&self, remote_id: u64, entity: &Entity<E>) {
|
pub fn subscribe_to_entity<E: 'static>(&self, remote_id: u64, entity: &Entity<E>) {
|
||||||
let id = (TypeId::of::<E>(), remote_id);
|
let id = (TypeId::of::<E>(), remote_id);
|
||||||
|
|
||||||
let mut message_handlers = self.0.message_handler_set().lock();
|
let mut message_handlers = self.0.client.message_handler_set().lock();
|
||||||
if message_handlers
|
if message_handlers
|
||||||
.entities_by_type_and_remote_id
|
.entities_by_type_and_remote_id
|
||||||
.contains_key(&id)
|
.contains_key(&id)
|
||||||
|
@ -335,3 +506,16 @@ impl AnyProtoClient {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_any_envelope<T: EnvelopedMessage>(
|
||||||
|
envelope: &TypedEnvelope<proto::LspQueryResponse>,
|
||||||
|
response: T,
|
||||||
|
) -> Box<dyn AnyTypedEnvelope> {
|
||||||
|
Box::new(proto::TypedEnvelope {
|
||||||
|
sender_id: envelope.sender_id,
|
||||||
|
original_sender_id: envelope.original_sender_id,
|
||||||
|
message_id: envelope.message_id,
|
||||||
|
received_at: envelope.received_at,
|
||||||
|
payload: response,
|
||||||
|
}) as Box<_>
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue