1use crate::{lsp_command::LspCommand, lsp_store::LspStore, make_text_document_identifier};
2use anyhow::{Context, Result};
3use async_trait::async_trait;
4use gpui::{AppContext, AsyncAppContext, Model};
5use language::{point_to_lsp, proto::deserialize_anchor, Buffer};
6use lsp::{LanguageServer, LanguageServerId};
7use rpc::proto::{self, PeerId};
8use serde::{Deserialize, Serialize};
9use std::{path::Path, sync::Arc};
10use text::{BufferId, PointUtf16, ToPointUtf16};
11
12pub enum LspExpandMacro {}
13
14impl lsp::request::Request for LspExpandMacro {
15 type Params = ExpandMacroParams;
16 type Result = Option<ExpandedMacro>;
17 const METHOD: &'static str = "rust-analyzer/expandMacro";
18}
19
20#[derive(Deserialize, Serialize, Debug)]
21#[serde(rename_all = "camelCase")]
22pub struct ExpandMacroParams {
23 pub text_document: lsp::TextDocumentIdentifier,
24 pub position: lsp::Position,
25}
26
27#[derive(Default, Deserialize, Serialize, Debug)]
28#[serde(rename_all = "camelCase")]
29pub struct ExpandedMacro {
30 pub name: String,
31 pub expansion: String,
32}
33
34impl ExpandedMacro {
35 pub fn is_empty(&self) -> bool {
36 self.name.is_empty() && self.expansion.is_empty()
37 }
38}
39#[derive(Debug)]
40pub struct ExpandMacro {
41 pub position: PointUtf16,
42}
43
44#[async_trait(?Send)]
45impl LspCommand for ExpandMacro {
46 type Response = ExpandedMacro;
47 type LspRequest = LspExpandMacro;
48 type ProtoRequest = proto::LspExtExpandMacro;
49
50 fn to_lsp(
51 &self,
52 path: &Path,
53 _: &Buffer,
54 _: &Arc<LanguageServer>,
55 _: &AppContext,
56 ) -> Result<ExpandMacroParams> {
57 Ok(ExpandMacroParams {
58 text_document: make_text_document_identifier(path)?,
59 position: point_to_lsp(self.position),
60 })
61 }
62
63 async fn response_from_lsp(
64 self,
65 message: Option<ExpandedMacro>,
66 _: Model<LspStore>,
67 _: Model<Buffer>,
68 _: LanguageServerId,
69 _: AsyncAppContext,
70 ) -> anyhow::Result<ExpandedMacro> {
71 Ok(message
72 .map(|message| ExpandedMacro {
73 name: message.name,
74 expansion: message.expansion,
75 })
76 .unwrap_or_default())
77 }
78
79 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::LspExtExpandMacro {
80 proto::LspExtExpandMacro {
81 project_id,
82 buffer_id: buffer.remote_id().into(),
83 position: Some(language::proto::serialize_anchor(
84 &buffer.anchor_before(self.position),
85 )),
86 }
87 }
88
89 async fn from_proto(
90 message: Self::ProtoRequest,
91 _: Model<LspStore>,
92 buffer: Model<Buffer>,
93 mut cx: AsyncAppContext,
94 ) -> anyhow::Result<Self> {
95 let position = message
96 .position
97 .and_then(deserialize_anchor)
98 .context("invalid position")?;
99 Ok(Self {
100 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
101 })
102 }
103
104 fn response_to_proto(
105 response: ExpandedMacro,
106 _: &mut LspStore,
107 _: PeerId,
108 _: &clock::Global,
109 _: &mut AppContext,
110 ) -> proto::LspExtExpandMacroResponse {
111 proto::LspExtExpandMacroResponse {
112 name: response.name,
113 expansion: response.expansion,
114 }
115 }
116
117 async fn response_from_proto(
118 self,
119 message: proto::LspExtExpandMacroResponse,
120 _: Model<LspStore>,
121 _: Model<Buffer>,
122 _: AsyncAppContext,
123 ) -> anyhow::Result<ExpandedMacro> {
124 Ok(ExpandedMacro {
125 name: message.name,
126 expansion: message.expansion,
127 })
128 }
129
130 fn buffer_id_from_proto(message: &proto::LspExtExpandMacro) -> Result<BufferId> {
131 BufferId::new(message.buffer_id)
132 }
133}
134
135pub enum LspOpenDocs {}
136
137impl lsp::request::Request for LspOpenDocs {
138 type Params = OpenDocsParams;
139 type Result = Option<DocsUrls>;
140 const METHOD: &'static str = "experimental/externalDocs";
141}
142
143#[derive(Serialize, Deserialize, Debug)]
144#[serde(rename_all = "camelCase")]
145pub struct OpenDocsParams {
146 pub text_document: lsp::TextDocumentIdentifier,
147 pub position: lsp::Position,
148}
149
150#[derive(Serialize, Deserialize, Debug, Default)]
151#[serde(rename_all = "camelCase")]
152pub struct DocsUrls {
153 pub web: Option<String>,
154 pub local: Option<String>,
155}
156
157impl DocsUrls {
158 pub fn is_empty(&self) -> bool {
159 self.web.is_none() && self.local.is_none()
160 }
161}
162
163#[derive(Debug)]
164pub struct OpenDocs {
165 pub position: PointUtf16,
166}
167
168#[async_trait(?Send)]
169impl LspCommand for OpenDocs {
170 type Response = DocsUrls;
171 type LspRequest = LspOpenDocs;
172 type ProtoRequest = proto::LspExtOpenDocs;
173
174 fn to_lsp(
175 &self,
176 path: &Path,
177 _: &Buffer,
178 _: &Arc<LanguageServer>,
179 _: &AppContext,
180 ) -> Result<OpenDocsParams> {
181 Ok(OpenDocsParams {
182 text_document: lsp::TextDocumentIdentifier {
183 uri: lsp::Url::from_file_path(path).unwrap(),
184 },
185 position: point_to_lsp(self.position),
186 })
187 }
188
189 async fn response_from_lsp(
190 self,
191 message: Option<DocsUrls>,
192 _: Model<LspStore>,
193 _: Model<Buffer>,
194 _: LanguageServerId,
195 _: AsyncAppContext,
196 ) -> anyhow::Result<DocsUrls> {
197 Ok(message
198 .map(|message| DocsUrls {
199 web: message.web,
200 local: message.local,
201 })
202 .unwrap_or_default())
203 }
204
205 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::LspExtOpenDocs {
206 proto::LspExtOpenDocs {
207 project_id,
208 buffer_id: buffer.remote_id().into(),
209 position: Some(language::proto::serialize_anchor(
210 &buffer.anchor_before(self.position),
211 )),
212 }
213 }
214
215 async fn from_proto(
216 message: Self::ProtoRequest,
217 _: Model<LspStore>,
218 buffer: Model<Buffer>,
219 mut cx: AsyncAppContext,
220 ) -> anyhow::Result<Self> {
221 let position = message
222 .position
223 .and_then(deserialize_anchor)
224 .context("invalid position")?;
225 Ok(Self {
226 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
227 })
228 }
229
230 fn response_to_proto(
231 response: DocsUrls,
232 _: &mut LspStore,
233 _: PeerId,
234 _: &clock::Global,
235 _: &mut AppContext,
236 ) -> proto::LspExtOpenDocsResponse {
237 proto::LspExtOpenDocsResponse {
238 web: response.web,
239 local: response.local,
240 }
241 }
242
243 async fn response_from_proto(
244 self,
245 message: proto::LspExtOpenDocsResponse,
246 _: Model<LspStore>,
247 _: Model<Buffer>,
248 _: AsyncAppContext,
249 ) -> anyhow::Result<DocsUrls> {
250 Ok(DocsUrls {
251 web: message.web,
252 local: message.local,
253 })
254 }
255
256 fn buffer_id_from_proto(message: &proto::LspExtOpenDocs) -> Result<BufferId> {
257 BufferId::new(message.buffer_id)
258 }
259}
260
261pub enum LspSwitchSourceHeader {}
262
263impl lsp::request::Request for LspSwitchSourceHeader {
264 type Params = SwitchSourceHeaderParams;
265 type Result = Option<SwitchSourceHeaderResult>;
266 const METHOD: &'static str = "textDocument/switchSourceHeader";
267}
268
269#[derive(Serialize, Deserialize, Debug)]
270#[serde(rename_all = "camelCase")]
271pub struct SwitchSourceHeaderParams(lsp::TextDocumentIdentifier);
272
273#[derive(Serialize, Deserialize, Debug, Default)]
274#[serde(rename_all = "camelCase")]
275pub struct SwitchSourceHeaderResult(pub String);
276
277#[derive(Default, Deserialize, Serialize, Debug)]
278#[serde(rename_all = "camelCase")]
279pub struct SwitchSourceHeader;
280
281#[async_trait(?Send)]
282impl LspCommand for SwitchSourceHeader {
283 type Response = SwitchSourceHeaderResult;
284 type LspRequest = LspSwitchSourceHeader;
285 type ProtoRequest = proto::LspExtSwitchSourceHeader;
286
287 fn to_lsp(
288 &self,
289 path: &Path,
290 _: &Buffer,
291 _: &Arc<LanguageServer>,
292 _: &AppContext,
293 ) -> Result<SwitchSourceHeaderParams> {
294 Ok(SwitchSourceHeaderParams(make_text_document_identifier(
295 path,
296 )?))
297 }
298
299 async fn response_from_lsp(
300 self,
301 message: Option<SwitchSourceHeaderResult>,
302 _: Model<LspStore>,
303 _: Model<Buffer>,
304 _: LanguageServerId,
305 _: AsyncAppContext,
306 ) -> anyhow::Result<SwitchSourceHeaderResult> {
307 Ok(message
308 .map(|message| SwitchSourceHeaderResult(message.0))
309 .unwrap_or_default())
310 }
311
312 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::LspExtSwitchSourceHeader {
313 proto::LspExtSwitchSourceHeader {
314 project_id,
315 buffer_id: buffer.remote_id().into(),
316 }
317 }
318
319 async fn from_proto(
320 _: Self::ProtoRequest,
321 _: Model<LspStore>,
322 _: Model<Buffer>,
323 _: AsyncAppContext,
324 ) -> anyhow::Result<Self> {
325 Ok(Self {})
326 }
327
328 fn response_to_proto(
329 response: SwitchSourceHeaderResult,
330 _: &mut LspStore,
331 _: PeerId,
332 _: &clock::Global,
333 _: &mut AppContext,
334 ) -> proto::LspExtSwitchSourceHeaderResponse {
335 proto::LspExtSwitchSourceHeaderResponse {
336 target_file: response.0,
337 }
338 }
339
340 async fn response_from_proto(
341 self,
342 message: proto::LspExtSwitchSourceHeaderResponse,
343 _: Model<LspStore>,
344 _: Model<Buffer>,
345 _: AsyncAppContext,
346 ) -> anyhow::Result<SwitchSourceHeaderResult> {
347 Ok(SwitchSourceHeaderResult(message.target_file))
348 }
349
350 fn buffer_id_from_proto(message: &proto::LspExtSwitchSourceHeader) -> Result<BufferId> {
351 BufferId::new(message.buffer_id)
352 }
353}