use std::{path::Path, sync::Arc}; use anyhow::Context; use async_trait::async_trait; use gpui::{AppContext, AsyncAppContext, Model}; use language::{point_to_lsp, proto::deserialize_anchor, Buffer}; use lsp::{LanguageServer, LanguageServerId}; use rpc::proto::{self, PeerId}; use serde::{Deserialize, Serialize}; use text::{PointUtf16, ToPointUtf16}; use crate::{lsp_command::LspCommand, Project}; pub enum LspExpandMacro {} impl lsp::request::Request for LspExpandMacro { type Params = ExpandMacroParams; type Result = Option; const METHOD: &'static str = "rust-analyzer/expandMacro"; } #[derive(Deserialize, Serialize, Debug)] #[serde(rename_all = "camelCase")] pub struct ExpandMacroParams { pub text_document: lsp::TextDocumentIdentifier, pub position: lsp::Position, } #[derive(Default, Deserialize, Serialize, Debug)] #[serde(rename_all = "camelCase")] pub struct ExpandedMacro { pub name: String, pub expansion: String, } impl ExpandedMacro { pub fn is_empty(&self) -> bool { self.name.is_empty() && self.expansion.is_empty() } } pub struct ExpandMacro { pub position: PointUtf16, } #[async_trait(?Send)] impl LspCommand for ExpandMacro { type Response = ExpandedMacro; type LspRequest = LspExpandMacro; type ProtoRequest = proto::LspExtExpandMacro; fn to_lsp( &self, path: &Path, _: &Buffer, _: &Arc, _: &AppContext, ) -> ExpandMacroParams { ExpandMacroParams { text_document: lsp::TextDocumentIdentifier { uri: lsp::Url::from_file_path(path).unwrap(), }, position: point_to_lsp(self.position), } } async fn response_from_lsp( self, message: Option, _: Model, _: Model, _: LanguageServerId, _: AsyncAppContext, ) -> anyhow::Result { Ok(message .map(|message| ExpandedMacro { name: message.name, expansion: message.expansion, }) .unwrap_or_default()) } fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::LspExtExpandMacro { proto::LspExtExpandMacro { project_id, buffer_id: buffer.remote_id(), position: Some(language::proto::serialize_anchor( &buffer.anchor_before(self.position), )), } } async fn from_proto( message: Self::ProtoRequest, _: Model, buffer: Model, mut cx: AsyncAppContext, ) -> anyhow::Result { let position = message .position .and_then(deserialize_anchor) .context("invalid position")?; Ok(Self { position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?, }) } fn response_to_proto( response: ExpandedMacro, _: &mut Project, _: PeerId, _: &clock::Global, _: &mut AppContext, ) -> proto::LspExtExpandMacroResponse { proto::LspExtExpandMacroResponse { name: response.name, expansion: response.expansion, } } async fn response_from_proto( self, message: proto::LspExtExpandMacroResponse, _: Model, _: Model, _: AsyncAppContext, ) -> anyhow::Result { Ok(ExpandedMacro { name: message.name, expansion: message.expansion, }) } fn buffer_id_from_proto(message: &proto::LspExtExpandMacro) -> u64 { message.buffer_id } }