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