lsp_ext_command.rs

  1use crate::{lsp_command::LspCommand, lsp_store::LspStore, make_text_document_identifier};
  2use anyhow::{Context as _, Result};
  3use async_trait::async_trait;
  4use gpui::{App, AsyncApp, Entity};
  5use language::{Buffer, point_to_lsp, proto::deserialize_anchor};
  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 display_name(&self) -> &str {
 51        "Expand macro"
 52    }
 53
 54    fn to_lsp(
 55        &self,
 56        path: &Path,
 57        _: &Buffer,
 58        _: &Arc<LanguageServer>,
 59        _: &App,
 60    ) -> Result<ExpandMacroParams> {
 61        Ok(ExpandMacroParams {
 62            text_document: make_text_document_identifier(path)?,
 63            position: point_to_lsp(self.position),
 64        })
 65    }
 66
 67    async fn response_from_lsp(
 68        self,
 69        message: Option<ExpandedMacro>,
 70        _: Entity<LspStore>,
 71        _: Entity<Buffer>,
 72        _: LanguageServerId,
 73        _: AsyncApp,
 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        _: Entity<LspStore>,
 96        buffer: Entity<Buffer>,
 97        mut cx: AsyncApp,
 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 LspStore,
111        _: PeerId,
112        _: &clock::Global,
113        _: &mut App,
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        _: Entity<LspStore>,
125        _: Entity<Buffer>,
126        _: AsyncApp,
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}
138
139pub enum LspOpenDocs {}
140
141impl lsp::request::Request for LspOpenDocs {
142    type Params = OpenDocsParams;
143    type Result = Option<DocsUrls>;
144    const METHOD: &'static str = "experimental/externalDocs";
145}
146
147#[derive(Serialize, Deserialize, Debug)]
148#[serde(rename_all = "camelCase")]
149pub struct OpenDocsParams {
150    pub text_document: lsp::TextDocumentIdentifier,
151    pub position: lsp::Position,
152}
153
154#[derive(Serialize, Deserialize, Debug, Default)]
155#[serde(rename_all = "camelCase")]
156pub struct DocsUrls {
157    pub web: Option<String>,
158    pub local: Option<String>,
159}
160
161impl DocsUrls {
162    pub fn is_empty(&self) -> bool {
163        self.web.is_none() && self.local.is_none()
164    }
165}
166
167#[derive(Debug)]
168pub struct OpenDocs {
169    pub position: PointUtf16,
170}
171
172#[async_trait(?Send)]
173impl LspCommand for OpenDocs {
174    type Response = DocsUrls;
175    type LspRequest = LspOpenDocs;
176    type ProtoRequest = proto::LspExtOpenDocs;
177
178    fn display_name(&self) -> &str {
179        "Open docs"
180    }
181
182    fn to_lsp(
183        &self,
184        path: &Path,
185        _: &Buffer,
186        _: &Arc<LanguageServer>,
187        _: &App,
188    ) -> Result<OpenDocsParams> {
189        Ok(OpenDocsParams {
190            text_document: lsp::TextDocumentIdentifier {
191                uri: lsp::Url::from_file_path(path).unwrap(),
192            },
193            position: point_to_lsp(self.position),
194        })
195    }
196
197    async fn response_from_lsp(
198        self,
199        message: Option<DocsUrls>,
200        _: Entity<LspStore>,
201        _: Entity<Buffer>,
202        _: LanguageServerId,
203        _: AsyncApp,
204    ) -> anyhow::Result<DocsUrls> {
205        Ok(message
206            .map(|message| DocsUrls {
207                web: message.web,
208                local: message.local,
209            })
210            .unwrap_or_default())
211    }
212
213    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::LspExtOpenDocs {
214        proto::LspExtOpenDocs {
215            project_id,
216            buffer_id: buffer.remote_id().into(),
217            position: Some(language::proto::serialize_anchor(
218                &buffer.anchor_before(self.position),
219            )),
220        }
221    }
222
223    async fn from_proto(
224        message: Self::ProtoRequest,
225        _: Entity<LspStore>,
226        buffer: Entity<Buffer>,
227        mut cx: AsyncApp,
228    ) -> anyhow::Result<Self> {
229        let position = message
230            .position
231            .and_then(deserialize_anchor)
232            .context("invalid position")?;
233        Ok(Self {
234            position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
235        })
236    }
237
238    fn response_to_proto(
239        response: DocsUrls,
240        _: &mut LspStore,
241        _: PeerId,
242        _: &clock::Global,
243        _: &mut App,
244    ) -> proto::LspExtOpenDocsResponse {
245        proto::LspExtOpenDocsResponse {
246            web: response.web,
247            local: response.local,
248        }
249    }
250
251    async fn response_from_proto(
252        self,
253        message: proto::LspExtOpenDocsResponse,
254        _: Entity<LspStore>,
255        _: Entity<Buffer>,
256        _: AsyncApp,
257    ) -> anyhow::Result<DocsUrls> {
258        Ok(DocsUrls {
259            web: message.web,
260            local: message.local,
261        })
262    }
263
264    fn buffer_id_from_proto(message: &proto::LspExtOpenDocs) -> Result<BufferId> {
265        BufferId::new(message.buffer_id)
266    }
267}
268
269pub enum LspSwitchSourceHeader {}
270
271impl lsp::request::Request for LspSwitchSourceHeader {
272    type Params = SwitchSourceHeaderParams;
273    type Result = Option<SwitchSourceHeaderResult>;
274    const METHOD: &'static str = "textDocument/switchSourceHeader";
275}
276
277#[derive(Serialize, Deserialize, Debug)]
278#[serde(rename_all = "camelCase")]
279pub struct SwitchSourceHeaderParams(lsp::TextDocumentIdentifier);
280
281#[derive(Serialize, Deserialize, Debug, Default)]
282#[serde(rename_all = "camelCase")]
283pub struct SwitchSourceHeaderResult(pub String);
284
285#[derive(Default, Deserialize, Serialize, Debug)]
286#[serde(rename_all = "camelCase")]
287pub struct SwitchSourceHeader;
288
289#[async_trait(?Send)]
290impl LspCommand for SwitchSourceHeader {
291    type Response = SwitchSourceHeaderResult;
292    type LspRequest = LspSwitchSourceHeader;
293    type ProtoRequest = proto::LspExtSwitchSourceHeader;
294
295    fn display_name(&self) -> &str {
296        "Switch source header"
297    }
298
299    fn to_lsp(
300        &self,
301        path: &Path,
302        _: &Buffer,
303        _: &Arc<LanguageServer>,
304        _: &App,
305    ) -> Result<SwitchSourceHeaderParams> {
306        Ok(SwitchSourceHeaderParams(make_text_document_identifier(
307            path,
308        )?))
309    }
310
311    async fn response_from_lsp(
312        self,
313        message: Option<SwitchSourceHeaderResult>,
314        _: Entity<LspStore>,
315        _: Entity<Buffer>,
316        _: LanguageServerId,
317        _: AsyncApp,
318    ) -> anyhow::Result<SwitchSourceHeaderResult> {
319        Ok(message
320            .map(|message| SwitchSourceHeaderResult(message.0))
321            .unwrap_or_default())
322    }
323
324    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::LspExtSwitchSourceHeader {
325        proto::LspExtSwitchSourceHeader {
326            project_id,
327            buffer_id: buffer.remote_id().into(),
328        }
329    }
330
331    async fn from_proto(
332        _: Self::ProtoRequest,
333        _: Entity<LspStore>,
334        _: Entity<Buffer>,
335        _: AsyncApp,
336    ) -> anyhow::Result<Self> {
337        Ok(Self {})
338    }
339
340    fn response_to_proto(
341        response: SwitchSourceHeaderResult,
342        _: &mut LspStore,
343        _: PeerId,
344        _: &clock::Global,
345        _: &mut App,
346    ) -> proto::LspExtSwitchSourceHeaderResponse {
347        proto::LspExtSwitchSourceHeaderResponse {
348            target_file: response.0,
349        }
350    }
351
352    async fn response_from_proto(
353        self,
354        message: proto::LspExtSwitchSourceHeaderResponse,
355        _: Entity<LspStore>,
356        _: Entity<Buffer>,
357        _: AsyncApp,
358    ) -> anyhow::Result<SwitchSourceHeaderResult> {
359        Ok(SwitchSourceHeaderResult(message.target_file))
360    }
361
362    fn buffer_id_from_proto(message: &proto::LspExtSwitchSourceHeader) -> Result<BufferId> {
363        BufferId::new(message.buffer_id)
364    }
365}