1use std::{path::Path, sync::Arc};
2
3use anyhow::{Context, Result};
4use async_trait::async_trait;
5use gpui::{AppContext, AsyncAppContext, Model};
6use language::{point_to_lsp, proto::deserialize_anchor, Buffer};
7use lsp::{LanguageServer, LanguageServerId};
8use rpc::proto::{self, PeerId};
9use serde::{Deserialize, Serialize};
10use text::{BufferId, PointUtf16, ToPointUtf16};
11
12use crate::{lsp_command::LspCommand, Project};
13
14pub enum LspExpandMacro {}
15
16impl lsp::request::Request for LspExpandMacro {
17 type Params = ExpandMacroParams;
18 type Result = Option<ExpandedMacro>;
19 const METHOD: &'static str = "rust-analyzer/expandMacro";
20}
21
22#[derive(Deserialize, Serialize, Debug)]
23#[serde(rename_all = "camelCase")]
24pub struct ExpandMacroParams {
25 pub text_document: lsp::TextDocumentIdentifier,
26 pub position: lsp::Position,
27}
28
29#[derive(Default, Deserialize, Serialize, Debug)]
30#[serde(rename_all = "camelCase")]
31pub struct ExpandedMacro {
32 pub name: String,
33 pub expansion: String,
34}
35
36impl ExpandedMacro {
37 pub fn is_empty(&self) -> bool {
38 self.name.is_empty() && self.expansion.is_empty()
39 }
40}
41
42pub struct ExpandMacro {
43 pub position: PointUtf16,
44}
45
46#[async_trait(?Send)]
47impl LspCommand for ExpandMacro {
48 type Response = ExpandedMacro;
49 type LspRequest = LspExpandMacro;
50 type ProtoRequest = proto::LspExtExpandMacro;
51
52 fn to_lsp(
53 &self,
54 path: &Path,
55 _: &Buffer,
56 _: &Arc<LanguageServer>,
57 _: &AppContext,
58 ) -> ExpandMacroParams {
59 ExpandMacroParams {
60 text_document: lsp::TextDocumentIdentifier {
61 uri: lsp::Url::from_file_path(path).unwrap(),
62 },
63 position: point_to_lsp(self.position),
64 }
65 }
66
67 async fn response_from_lsp(
68 self,
69 message: Option<ExpandedMacro>,
70 _: Model<Project>,
71 _: Model<Buffer>,
72 _: LanguageServerId,
73 _: AsyncAppContext,
74 ) -> anyhow::Result<ExpandedMacro> {
75 Ok(message
76 .map(|message| ExpandedMacro {
77 name: message.name,
78 expansion: message.expansion,
79 })
80 .unwrap_or_default())
81 }
82
83 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::LspExtExpandMacro {
84 proto::LspExtExpandMacro {
85 project_id,
86 buffer_id: buffer.remote_id().into(),
87 position: Some(language::proto::serialize_anchor(
88 &buffer.anchor_before(self.position),
89 )),
90 }
91 }
92
93 async fn from_proto(
94 message: Self::ProtoRequest,
95 _: Model<Project>,
96 buffer: Model<Buffer>,
97 mut cx: AsyncAppContext,
98 ) -> anyhow::Result<Self> {
99 let position = message
100 .position
101 .and_then(deserialize_anchor)
102 .context("invalid position")?;
103 Ok(Self {
104 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
105 })
106 }
107
108 fn response_to_proto(
109 response: ExpandedMacro,
110 _: &mut Project,
111 _: PeerId,
112 _: &clock::Global,
113 _: &mut AppContext,
114 ) -> proto::LspExtExpandMacroResponse {
115 proto::LspExtExpandMacroResponse {
116 name: response.name,
117 expansion: response.expansion,
118 }
119 }
120
121 async fn response_from_proto(
122 self,
123 message: proto::LspExtExpandMacroResponse,
124 _: Model<Project>,
125 _: Model<Buffer>,
126 _: AsyncAppContext,
127 ) -> anyhow::Result<ExpandedMacro> {
128 Ok(ExpandedMacro {
129 name: message.name,
130 expansion: message.expansion,
131 })
132 }
133
134 fn buffer_id_from_proto(message: &proto::LspExtExpandMacro) -> Result<BufferId> {
135 BufferId::new(message.buffer_id)
136 }
137}