lsp_ext_command.rs

  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}