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:
Kirill Bulatov 2025-05-01 21:28:05 +03:00 committed by GitHub
parent 50ec26c163
commit 2a319efade
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 276 additions and 64 deletions

View file

@ -2,9 +2,10 @@ use crate::{
LocationLink,
lsp_command::{
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,
make_text_document_identifier,
make_lsp_text_document_position, make_text_document_identifier,
};
use anyhow::{Context as _, Result};
use async_trait::async_trait;
@ -301,6 +302,19 @@ pub struct SwitchSourceHeaderResult(pub String);
#[serde(rename_all = "camelCase")]
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)]
impl LspCommand for SwitchSourceHeader {
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
// Taken from https://github.com/rust-lang/rust-analyzer/blob/a73a37a757a58b43a796d3eb86a1f7dfd0036659/crates/rust-analyzer/src/lsp/ext.rs#L425-L489
pub enum Runnables {}
@ -633,7 +737,7 @@ impl LspCommand for GetLspRunnables {
for lsp_runnable in message.runnables {
let location = match lsp_runnable.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,
};