lsp_ext_command.rs

  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}