Add editor::GoToParentModule
for rust-analyzer backed projects (#29755)
Support rust-analyzer's "go to parent module" action https://rust-analyzer.github.io/book/contributing/lsp-extensions.html#parent-module Release Notes: - Added `editor::GoToParentModule` for rust-analyzer backed projects --------- Co-authored-by: Julia Ryan <juliaryan3.14@gmail.com>
This commit is contained in:
parent
50ec26c163
commit
2a319efade
8 changed files with 276 additions and 64 deletions
|
@ -327,6 +327,7 @@ impl Server {
|
||||||
.add_request_handler(
|
.add_request_handler(
|
||||||
forward_read_only_project_request::<proto::LspExtSwitchSourceHeader>,
|
forward_read_only_project_request::<proto::LspExtSwitchSourceHeader>,
|
||||||
)
|
)
|
||||||
|
.add_request_handler(forward_read_only_project_request::<proto::LspExtGoToParentModule>)
|
||||||
.add_request_handler(
|
.add_request_handler(
|
||||||
forward_read_only_project_request::<proto::LanguageServerIdForName>,
|
forward_read_only_project_request::<proto::LanguageServerIdForName>,
|
||||||
)
|
)
|
||||||
|
|
|
@ -308,6 +308,7 @@ actions!(
|
||||||
GoToImplementation,
|
GoToImplementation,
|
||||||
GoToImplementationSplit,
|
GoToImplementationSplit,
|
||||||
GoToNextChange,
|
GoToNextChange,
|
||||||
|
GoToParentModule,
|
||||||
GoToPreviousChange,
|
GoToPreviousChange,
|
||||||
GoToPreviousDiagnostic,
|
GoToPreviousDiagnostic,
|
||||||
GoToTypeDefinition,
|
GoToTypeDefinition,
|
||||||
|
|
|
@ -4,15 +4,19 @@ use anyhow::Context as _;
|
||||||
use gpui::{App, AppContext as _, Context, Entity, Window};
|
use gpui::{App, AppContext as _, Context, Entity, Window};
|
||||||
use language::{Capability, Language, proto::serialize_anchor};
|
use language::{Capability, Language, proto::serialize_anchor};
|
||||||
use multi_buffer::MultiBuffer;
|
use multi_buffer::MultiBuffer;
|
||||||
use project::lsp_store::{
|
use project::{
|
||||||
|
lsp_command::location_link_from_proto,
|
||||||
|
lsp_store::{
|
||||||
lsp_ext_command::{DocsUrls, ExpandMacro, ExpandedMacro},
|
lsp_ext_command::{DocsUrls, ExpandMacro, ExpandedMacro},
|
||||||
rust_analyzer_ext::RUST_ANALYZER_NAME,
|
rust_analyzer_ext::RUST_ANALYZER_NAME,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use rpc::proto;
|
use rpc::proto;
|
||||||
use text::ToPointUtf16;
|
use text::ToPointUtf16;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Editor, ExpandMacroRecursively, OpenDocs, element::register_action,
|
Editor, ExpandMacroRecursively, GoToParentModule, GotoDefinitionKind, OpenDocs,
|
||||||
|
element::register_action, hover_links::HoverLink,
|
||||||
lsp_ext::find_specific_language_server_in_selection,
|
lsp_ext::find_specific_language_server_in_selection,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -30,11 +34,94 @@ pub fn apply_related_actions(editor: &Entity<Editor>, window: &mut Window, cx: &
|
||||||
.filter_map(|buffer| buffer.read(cx).language())
|
.filter_map(|buffer| buffer.read(cx).language())
|
||||||
.any(|language| is_rust_language(language))
|
.any(|language| is_rust_language(language))
|
||||||
{
|
{
|
||||||
|
register_action(&editor, window, go_to_parent_module);
|
||||||
register_action(&editor, window, expand_macro_recursively);
|
register_action(&editor, window, expand_macro_recursively);
|
||||||
register_action(&editor, window, open_docs);
|
register_action(&editor, window, open_docs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn go_to_parent_module(
|
||||||
|
editor: &mut Editor,
|
||||||
|
_: &GoToParentModule,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Editor>,
|
||||||
|
) {
|
||||||
|
if editor.selections.count() == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let Some(project) = &editor.project else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let server_lookup = find_specific_language_server_in_selection(
|
||||||
|
editor,
|
||||||
|
cx,
|
||||||
|
is_rust_language,
|
||||||
|
RUST_ANALYZER_NAME,
|
||||||
|
);
|
||||||
|
|
||||||
|
let project = project.clone();
|
||||||
|
let lsp_store = project.read(cx).lsp_store();
|
||||||
|
let upstream_client = lsp_store.read(cx).upstream_client();
|
||||||
|
cx.spawn_in(window, async move |editor, cx| {
|
||||||
|
let Some((trigger_anchor, _, server_to_query, buffer)) = server_lookup.await else {
|
||||||
|
return anyhow::Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
let location_links = if let Some((client, project_id)) = upstream_client {
|
||||||
|
let buffer_id = buffer.update(cx, |buffer, _| buffer.remote_id())?;
|
||||||
|
|
||||||
|
let request = proto::LspExtGoToParentModule {
|
||||||
|
project_id,
|
||||||
|
buffer_id: buffer_id.to_proto(),
|
||||||
|
position: Some(serialize_anchor(&trigger_anchor.text_anchor)),
|
||||||
|
};
|
||||||
|
let response = client
|
||||||
|
.request(request)
|
||||||
|
.await
|
||||||
|
.context("lsp ext go to parent module proto request")?;
|
||||||
|
futures::future::join_all(
|
||||||
|
response
|
||||||
|
.links
|
||||||
|
.into_iter()
|
||||||
|
.map(|link| location_link_from_proto(link, lsp_store.clone(), cx)),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.collect::<anyhow::Result<_>>()
|
||||||
|
.context("go to parent module via collab")?
|
||||||
|
} else {
|
||||||
|
let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot())?;
|
||||||
|
let position = trigger_anchor.text_anchor.to_point_utf16(&buffer_snapshot);
|
||||||
|
project
|
||||||
|
.update(cx, |project, cx| {
|
||||||
|
project.request_lsp(
|
||||||
|
buffer,
|
||||||
|
project::LanguageServerToQuery::Other(server_to_query),
|
||||||
|
project::lsp_store::lsp_ext_command::GoToParentModule { position },
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
})?
|
||||||
|
.await
|
||||||
|
.context("go to parent module")?
|
||||||
|
};
|
||||||
|
|
||||||
|
editor
|
||||||
|
.update_in(cx, |editor, window, cx| {
|
||||||
|
editor.navigate_to_hover_links(
|
||||||
|
Some(GotoDefinitionKind::Declaration),
|
||||||
|
location_links.into_iter().map(HoverLink::Text).collect(),
|
||||||
|
false,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
})?
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.detach_and_log_err(cx);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn expand_macro_recursively(
|
pub fn expand_macro_recursively(
|
||||||
editor: &mut Editor,
|
editor: &mut Editor,
|
||||||
_: &ExpandMacroRecursively,
|
_: &ExpandMacroRecursively,
|
||||||
|
|
|
@ -13,7 +13,7 @@ use client::proto::{self, PeerId};
|
||||||
use clock::Global;
|
use clock::Global;
|
||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
use futures::future;
|
use futures::future;
|
||||||
use gpui::{App, AsyncApp, Entity};
|
use gpui::{App, AsyncApp, Entity, Task};
|
||||||
use language::{
|
use language::{
|
||||||
Anchor, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CharKind, OffsetRangeExt, PointUtf16,
|
Anchor, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CharKind, OffsetRangeExt, PointUtf16,
|
||||||
ToOffset, ToPointUtf16, Transaction, Unclipped,
|
ToOffset, ToPointUtf16, Transaction, Unclipped,
|
||||||
|
@ -966,7 +966,7 @@ fn language_server_for_buffer(
|
||||||
.ok_or_else(|| anyhow!("no language server found for buffer"))
|
.ok_or_else(|| anyhow!("no language server found for buffer"))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn location_links_from_proto(
|
pub async fn location_links_from_proto(
|
||||||
proto_links: Vec<proto::LocationLink>,
|
proto_links: Vec<proto::LocationLink>,
|
||||||
lsp_store: Entity<LspStore>,
|
lsp_store: Entity<LspStore>,
|
||||||
mut cx: AsyncApp,
|
mut cx: AsyncApp,
|
||||||
|
@ -974,17 +974,18 @@ async fn location_links_from_proto(
|
||||||
let mut links = Vec::new();
|
let mut links = Vec::new();
|
||||||
|
|
||||||
for link in proto_links {
|
for link in proto_links {
|
||||||
links.push(location_link_from_proto(link, &lsp_store, &mut cx).await?)
|
links.push(location_link_from_proto(link, lsp_store.clone(), &mut cx).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(links)
|
Ok(links)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn location_link_from_proto(
|
pub fn location_link_from_proto(
|
||||||
link: proto::LocationLink,
|
link: proto::LocationLink,
|
||||||
lsp_store: &Entity<LspStore>,
|
lsp_store: Entity<LspStore>,
|
||||||
cx: &mut AsyncApp,
|
cx: &mut AsyncApp,
|
||||||
) -> Result<LocationLink> {
|
) -> Task<Result<LocationLink>> {
|
||||||
|
cx.spawn(async move |cx| {
|
||||||
let origin = match link.origin {
|
let origin = match link.origin {
|
||||||
Some(origin) => {
|
Some(origin) => {
|
||||||
let buffer_id = BufferId::new(origin.buffer_id)?;
|
let buffer_id = BufferId::new(origin.buffer_id)?;
|
||||||
|
@ -1035,9 +1036,10 @@ pub async fn location_link_from_proto(
|
||||||
range: start..end,
|
range: start..end,
|
||||||
};
|
};
|
||||||
Ok(LocationLink { origin, target })
|
Ok(LocationLink { origin, target })
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn location_links_from_lsp(
|
pub async fn location_links_from_lsp(
|
||||||
message: Option<lsp::GotoDefinitionResponse>,
|
message: Option<lsp::GotoDefinitionResponse>,
|
||||||
lsp_store: Entity<LspStore>,
|
lsp_store: Entity<LspStore>,
|
||||||
buffer: Entity<Buffer>,
|
buffer: Entity<Buffer>,
|
||||||
|
@ -1178,7 +1180,7 @@ pub async fn location_link_from_lsp(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn location_links_to_proto(
|
pub fn location_links_to_proto(
|
||||||
links: Vec<LocationLink>,
|
links: Vec<LocationLink>,
|
||||||
lsp_store: &mut LspStore,
|
lsp_store: &mut LspStore,
|
||||||
peer_id: PeerId,
|
peer_id: PeerId,
|
||||||
|
|
|
@ -2,9 +2,10 @@ use crate::{
|
||||||
LocationLink,
|
LocationLink,
|
||||||
lsp_command::{
|
lsp_command::{
|
||||||
LspCommand, location_link_from_lsp, location_link_from_proto, location_link_to_proto,
|
LspCommand, location_link_from_lsp, location_link_from_proto, location_link_to_proto,
|
||||||
|
location_links_from_lsp, location_links_from_proto, location_links_to_proto,
|
||||||
},
|
},
|
||||||
lsp_store::LspStore,
|
lsp_store::LspStore,
|
||||||
make_text_document_identifier,
|
make_lsp_text_document_position, make_text_document_identifier,
|
||||||
};
|
};
|
||||||
use anyhow::{Context as _, Result};
|
use anyhow::{Context as _, Result};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
@ -301,6 +302,19 @@ pub struct SwitchSourceHeaderResult(pub String);
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct SwitchSourceHeader;
|
pub struct SwitchSourceHeader;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GoToParentModule {
|
||||||
|
pub position: PointUtf16,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LspGoToParentModule {}
|
||||||
|
|
||||||
|
impl lsp::request::Request for LspGoToParentModule {
|
||||||
|
type Params = lsp::TextDocumentPositionParams;
|
||||||
|
type Result = Option<Vec<lsp::LocationLink>>;
|
||||||
|
const METHOD: &'static str = "experimental/parentModule";
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
impl LspCommand for SwitchSourceHeader {
|
impl LspCommand for SwitchSourceHeader {
|
||||||
type Response = SwitchSourceHeaderResult;
|
type Response = SwitchSourceHeaderResult;
|
||||||
|
@ -379,6 +393,96 @@ impl LspCommand for SwitchSourceHeader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl LspCommand for GoToParentModule {
|
||||||
|
type Response = Vec<LocationLink>;
|
||||||
|
type LspRequest = LspGoToParentModule;
|
||||||
|
type ProtoRequest = proto::LspExtGoToParentModule;
|
||||||
|
|
||||||
|
fn display_name(&self) -> &str {
|
||||||
|
"Go to parent module"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_lsp(
|
||||||
|
&self,
|
||||||
|
path: &Path,
|
||||||
|
_: &Buffer,
|
||||||
|
_: &Arc<LanguageServer>,
|
||||||
|
_: &App,
|
||||||
|
) -> Result<lsp::TextDocumentPositionParams> {
|
||||||
|
make_lsp_text_document_position(path, self.position)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn response_from_lsp(
|
||||||
|
self,
|
||||||
|
links: Option<Vec<lsp::LocationLink>>,
|
||||||
|
lsp_store: Entity<LspStore>,
|
||||||
|
buffer: Entity<Buffer>,
|
||||||
|
server_id: LanguageServerId,
|
||||||
|
cx: AsyncApp,
|
||||||
|
) -> anyhow::Result<Vec<LocationLink>> {
|
||||||
|
location_links_from_lsp(
|
||||||
|
links.map(lsp::GotoDefinitionResponse::Link),
|
||||||
|
lsp_store,
|
||||||
|
buffer,
|
||||||
|
server_id,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::LspExtGoToParentModule {
|
||||||
|
proto::LspExtGoToParentModule {
|
||||||
|
project_id,
|
||||||
|
buffer_id: buffer.remote_id().to_proto(),
|
||||||
|
position: Some(language::proto::serialize_anchor(
|
||||||
|
&buffer.anchor_before(self.position),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn from_proto(
|
||||||
|
request: Self::ProtoRequest,
|
||||||
|
_: Entity<LspStore>,
|
||||||
|
buffer: Entity<Buffer>,
|
||||||
|
mut cx: AsyncApp,
|
||||||
|
) -> anyhow::Result<Self> {
|
||||||
|
let position = request
|
||||||
|
.position
|
||||||
|
.and_then(deserialize_anchor)
|
||||||
|
.context("bad request with bad position")?;
|
||||||
|
Ok(Self {
|
||||||
|
position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn response_to_proto(
|
||||||
|
links: Vec<LocationLink>,
|
||||||
|
lsp_store: &mut LspStore,
|
||||||
|
peer_id: PeerId,
|
||||||
|
_: &clock::Global,
|
||||||
|
cx: &mut App,
|
||||||
|
) -> proto::LspExtGoToParentModuleResponse {
|
||||||
|
proto::LspExtGoToParentModuleResponse {
|
||||||
|
links: location_links_to_proto(links, lsp_store, peer_id, cx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn response_from_proto(
|
||||||
|
self,
|
||||||
|
message: proto::LspExtGoToParentModuleResponse,
|
||||||
|
lsp_store: Entity<LspStore>,
|
||||||
|
_: Entity<Buffer>,
|
||||||
|
cx: AsyncApp,
|
||||||
|
) -> anyhow::Result<Vec<LocationLink>> {
|
||||||
|
location_links_from_proto(message.links, lsp_store, cx).await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn buffer_id_from_proto(message: &proto::LspExtGoToParentModule) -> Result<BufferId> {
|
||||||
|
BufferId::new(message.buffer_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// https://rust-analyzer.github.io/book/contributing/lsp-extensions.html#runnables
|
// https://rust-analyzer.github.io/book/contributing/lsp-extensions.html#runnables
|
||||||
// Taken from https://github.com/rust-lang/rust-analyzer/blob/a73a37a757a58b43a796d3eb86a1f7dfd0036659/crates/rust-analyzer/src/lsp/ext.rs#L425-L489
|
// Taken from https://github.com/rust-lang/rust-analyzer/blob/a73a37a757a58b43a796d3eb86a1f7dfd0036659/crates/rust-analyzer/src/lsp/ext.rs#L425-L489
|
||||||
pub enum Runnables {}
|
pub enum Runnables {}
|
||||||
|
@ -633,7 +737,7 @@ impl LspCommand for GetLspRunnables {
|
||||||
for lsp_runnable in message.runnables {
|
for lsp_runnable in message.runnables {
|
||||||
let location = match lsp_runnable.location {
|
let location = match lsp_runnable.location {
|
||||||
Some(location) => {
|
Some(location) => {
|
||||||
Some(location_link_from_proto(location, &lsp_store, &mut cx).await?)
|
Some(location_link_from_proto(location, lsp_store.clone(), &mut cx).await?)
|
||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
|
@ -182,6 +182,16 @@ message LspExtSwitchSourceHeaderResponse {
|
||||||
string target_file = 1;
|
string target_file = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message LspExtGoToParentModule {
|
||||||
|
uint64 project_id = 1;
|
||||||
|
uint64 buffer_id = 2;
|
||||||
|
Anchor position = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LspExtGoToParentModuleResponse {
|
||||||
|
repeated LocationLink links = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message GetCompletionsResponse {
|
message GetCompletionsResponse {
|
||||||
repeated Completion completions = 1;
|
repeated Completion completions = 1;
|
||||||
repeated VectorClockEntry version = 2;
|
repeated VectorClockEntry version = 2;
|
||||||
|
|
|
@ -378,7 +378,10 @@ message Envelope {
|
||||||
GetDebugAdapterBinary get_debug_adapter_binary = 339;
|
GetDebugAdapterBinary get_debug_adapter_binary = 339;
|
||||||
DebugAdapterBinary debug_adapter_binary = 340;
|
DebugAdapterBinary debug_adapter_binary = 340;
|
||||||
RunDebugLocators run_debug_locators = 341;
|
RunDebugLocators run_debug_locators = 341;
|
||||||
DebugRequest debug_request = 342; // current max
|
DebugRequest debug_request = 342;
|
||||||
|
|
||||||
|
LspExtGoToParentModule lsp_ext_go_to_parent_module = 343;
|
||||||
|
LspExtGoToParentModuleResponse lsp_ext_go_to_parent_module_response = 344;// current max
|
||||||
}
|
}
|
||||||
|
|
||||||
reserved 87 to 88;
|
reserved 87 to 88;
|
||||||
|
|
|
@ -169,6 +169,8 @@ messages!(
|
||||||
(LspExtRunnablesResponse, Background),
|
(LspExtRunnablesResponse, Background),
|
||||||
(LspExtSwitchSourceHeader, Background),
|
(LspExtSwitchSourceHeader, Background),
|
||||||
(LspExtSwitchSourceHeaderResponse, Background),
|
(LspExtSwitchSourceHeaderResponse, Background),
|
||||||
|
(LspExtGoToParentModule, Background),
|
||||||
|
(LspExtGoToParentModuleResponse, Background),
|
||||||
(MarkNotificationRead, Foreground),
|
(MarkNotificationRead, Foreground),
|
||||||
(MoveChannel, Foreground),
|
(MoveChannel, Foreground),
|
||||||
(MultiLspQuery, Background),
|
(MultiLspQuery, Background),
|
||||||
|
@ -422,6 +424,7 @@ request_messages!(
|
||||||
(CreateContext, CreateContextResponse),
|
(CreateContext, CreateContextResponse),
|
||||||
(SynchronizeContexts, SynchronizeContextsResponse),
|
(SynchronizeContexts, SynchronizeContextsResponse),
|
||||||
(LspExtSwitchSourceHeader, LspExtSwitchSourceHeaderResponse),
|
(LspExtSwitchSourceHeader, LspExtSwitchSourceHeaderResponse),
|
||||||
|
(LspExtGoToParentModule, LspExtGoToParentModuleResponse),
|
||||||
(AddWorktree, AddWorktreeResponse),
|
(AddWorktree, AddWorktreeResponse),
|
||||||
(ShutdownRemoteServer, Ack),
|
(ShutdownRemoteServer, Ack),
|
||||||
(RemoveWorktree, Ack),
|
(RemoveWorktree, Ack),
|
||||||
|
@ -544,6 +547,7 @@ entity_messages!(
|
||||||
UpdateContext,
|
UpdateContext,
|
||||||
SynchronizeContexts,
|
SynchronizeContexts,
|
||||||
LspExtSwitchSourceHeader,
|
LspExtSwitchSourceHeader,
|
||||||
|
LspExtGoToParentModule,
|
||||||
LanguageServerLog,
|
LanguageServerLog,
|
||||||
Toast,
|
Toast,
|
||||||
HideToast,
|
HideToast,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue