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
|
@ -13,7 +13,7 @@ use client::proto::{self, PeerId};
|
|||
use clock::Global;
|
||||
use collections::HashSet;
|
||||
use futures::future;
|
||||
use gpui::{App, AsyncApp, Entity};
|
||||
use gpui::{App, AsyncApp, Entity, Task};
|
||||
use language::{
|
||||
Anchor, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CharKind, OffsetRangeExt, PointUtf16,
|
||||
ToOffset, ToPointUtf16, Transaction, Unclipped,
|
||||
|
@ -966,7 +966,7 @@ fn language_server_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>,
|
||||
lsp_store: Entity<LspStore>,
|
||||
mut cx: AsyncApp,
|
||||
|
@ -974,70 +974,72 @@ async fn location_links_from_proto(
|
|||
let mut links = Vec::new();
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
pub async fn location_link_from_proto(
|
||||
pub fn location_link_from_proto(
|
||||
link: proto::LocationLink,
|
||||
lsp_store: &Entity<LspStore>,
|
||||
lsp_store: Entity<LspStore>,
|
||||
cx: &mut AsyncApp,
|
||||
) -> Result<LocationLink> {
|
||||
let origin = match link.origin {
|
||||
Some(origin) => {
|
||||
let buffer_id = BufferId::new(origin.buffer_id)?;
|
||||
let buffer = lsp_store
|
||||
.update(cx, |lsp_store, cx| {
|
||||
lsp_store.wait_for_remote_buffer(buffer_id, cx)
|
||||
})?
|
||||
.await?;
|
||||
let start = origin
|
||||
.start
|
||||
.and_then(deserialize_anchor)
|
||||
.ok_or_else(|| anyhow!("missing origin start"))?;
|
||||
let end = origin
|
||||
.end
|
||||
.and_then(deserialize_anchor)
|
||||
.ok_or_else(|| anyhow!("missing origin end"))?;
|
||||
buffer
|
||||
.update(cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
|
||||
.await?;
|
||||
Some(Location {
|
||||
buffer,
|
||||
range: start..end,
|
||||
})
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
) -> Task<Result<LocationLink>> {
|
||||
cx.spawn(async move |cx| {
|
||||
let origin = match link.origin {
|
||||
Some(origin) => {
|
||||
let buffer_id = BufferId::new(origin.buffer_id)?;
|
||||
let buffer = lsp_store
|
||||
.update(cx, |lsp_store, cx| {
|
||||
lsp_store.wait_for_remote_buffer(buffer_id, cx)
|
||||
})?
|
||||
.await?;
|
||||
let start = origin
|
||||
.start
|
||||
.and_then(deserialize_anchor)
|
||||
.ok_or_else(|| anyhow!("missing origin start"))?;
|
||||
let end = origin
|
||||
.end
|
||||
.and_then(deserialize_anchor)
|
||||
.ok_or_else(|| anyhow!("missing origin end"))?;
|
||||
buffer
|
||||
.update(cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
|
||||
.await?;
|
||||
Some(Location {
|
||||
buffer,
|
||||
range: start..end,
|
||||
})
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
let target = link.target.ok_or_else(|| anyhow!("missing target"))?;
|
||||
let buffer_id = BufferId::new(target.buffer_id)?;
|
||||
let buffer = lsp_store
|
||||
.update(cx, |lsp_store, cx| {
|
||||
lsp_store.wait_for_remote_buffer(buffer_id, cx)
|
||||
})?
|
||||
.await?;
|
||||
let start = target
|
||||
.start
|
||||
.and_then(deserialize_anchor)
|
||||
.ok_or_else(|| anyhow!("missing target start"))?;
|
||||
let end = target
|
||||
.end
|
||||
.and_then(deserialize_anchor)
|
||||
.ok_or_else(|| anyhow!("missing target end"))?;
|
||||
buffer
|
||||
.update(cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
|
||||
.await?;
|
||||
let target = Location {
|
||||
buffer,
|
||||
range: start..end,
|
||||
};
|
||||
Ok(LocationLink { origin, target })
|
||||
let target = link.target.ok_or_else(|| anyhow!("missing target"))?;
|
||||
let buffer_id = BufferId::new(target.buffer_id)?;
|
||||
let buffer = lsp_store
|
||||
.update(cx, |lsp_store, cx| {
|
||||
lsp_store.wait_for_remote_buffer(buffer_id, cx)
|
||||
})?
|
||||
.await?;
|
||||
let start = target
|
||||
.start
|
||||
.and_then(deserialize_anchor)
|
||||
.ok_or_else(|| anyhow!("missing target start"))?;
|
||||
let end = target
|
||||
.end
|
||||
.and_then(deserialize_anchor)
|
||||
.ok_or_else(|| anyhow!("missing target end"))?;
|
||||
buffer
|
||||
.update(cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
|
||||
.await?;
|
||||
let target = Location {
|
||||
buffer,
|
||||
range: start..end,
|
||||
};
|
||||
Ok(LocationLink { origin, target })
|
||||
})
|
||||
}
|
||||
|
||||
async fn location_links_from_lsp(
|
||||
pub async fn location_links_from_lsp(
|
||||
message: Option<lsp::GotoDefinitionResponse>,
|
||||
lsp_store: Entity<LspStore>,
|
||||
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>,
|
||||
lsp_store: &mut LspStore,
|
||||
peer_id: PeerId,
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue