lsp_command.rs

  1use crate::{Location, Project, ProjectTransaction};
  2use anyhow::{anyhow, Result};
  3use async_trait::async_trait;
  4use client::{proto, PeerId};
  5use gpui::{AppContext, AsyncAppContext, ModelHandle};
  6use language::{
  7    point_from_lsp,
  8    proto::{deserialize_anchor, serialize_anchor},
  9    range_from_lsp, Anchor, Bias, Buffer, PointUtf16, ToLspPosition, ToPointUtf16,
 10};
 11use std::{ops::Range, path::Path};
 12
 13#[async_trait(?Send)]
 14pub(crate) trait LspCommand: 'static + Sized {
 15    type Response: 'static + Default + Send;
 16    type LspRequest: 'static + Send + lsp::request::Request;
 17    type ProtoRequest: 'static + Send + proto::RequestMessage;
 18
 19    fn to_lsp(
 20        &self,
 21        path: &Path,
 22        cx: &AppContext,
 23    ) -> <Self::LspRequest as lsp::request::Request>::Params;
 24    async fn response_from_lsp(
 25        self,
 26        message: <Self::LspRequest as lsp::request::Request>::Result,
 27        project: ModelHandle<Project>,
 28        buffer: ModelHandle<Buffer>,
 29        cx: AsyncAppContext,
 30    ) -> Result<Self::Response>;
 31
 32    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest;
 33    fn from_proto(
 34        message: Self::ProtoRequest,
 35        project: &mut Project,
 36        buffer: &Buffer,
 37    ) -> Result<Self>;
 38    fn response_to_proto(
 39        response: Self::Response,
 40        project: &mut Project,
 41        peer_id: PeerId,
 42        buffer_version: &clock::Global,
 43        cx: &AppContext,
 44    ) -> <Self::ProtoRequest as proto::RequestMessage>::Response;
 45    async fn response_from_proto(
 46        self,
 47        message: <Self::ProtoRequest as proto::RequestMessage>::Response,
 48        project: ModelHandle<Project>,
 49        buffer: ModelHandle<Buffer>,
 50        cx: AsyncAppContext,
 51    ) -> Result<Self::Response>;
 52    fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64;
 53}
 54
 55pub(crate) struct PrepareRename {
 56    pub position: PointUtf16,
 57}
 58
 59pub(crate) struct PerformRename {
 60    pub position: PointUtf16,
 61    pub new_name: String,
 62    pub push_to_history: bool,
 63}
 64
 65pub(crate) struct GetDefinition {
 66    pub position: PointUtf16,
 67}
 68
 69pub(crate) struct GetReferences {
 70    pub position: PointUtf16,
 71}
 72
 73#[async_trait(?Send)]
 74impl LspCommand for PrepareRename {
 75    type Response = Option<Range<Anchor>>;
 76    type LspRequest = lsp::request::PrepareRenameRequest;
 77    type ProtoRequest = proto::PrepareRename;
 78
 79    fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::TextDocumentPositionParams {
 80        lsp::TextDocumentPositionParams {
 81            text_document: lsp::TextDocumentIdentifier {
 82                uri: lsp::Url::from_file_path(path).unwrap(),
 83            },
 84            position: self.position.to_lsp_position(),
 85        }
 86    }
 87
 88    async fn response_from_lsp(
 89        self,
 90        message: Option<lsp::PrepareRenameResponse>,
 91        _: ModelHandle<Project>,
 92        buffer: ModelHandle<Buffer>,
 93        cx: AsyncAppContext,
 94    ) -> Result<Option<Range<Anchor>>> {
 95        buffer.read_with(&cx, |buffer, _| {
 96            if let Some(
 97                lsp::PrepareRenameResponse::Range(range)
 98                | lsp::PrepareRenameResponse::RangeWithPlaceholder { range, .. },
 99            ) = message
100            {
101                let Range { start, end } = range_from_lsp(range);
102                if buffer.clip_point_utf16(start, Bias::Left) == start
103                    && buffer.clip_point_utf16(end, Bias::Left) == end
104                {
105                    return Ok(Some(buffer.anchor_after(start)..buffer.anchor_before(end)));
106                }
107            }
108            Ok(None)
109        })
110    }
111
112    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PrepareRename {
113        proto::PrepareRename {
114            project_id,
115            buffer_id: buffer.remote_id(),
116            position: Some(language::proto::serialize_anchor(
117                &buffer.anchor_before(self.position),
118            )),
119        }
120    }
121
122    fn from_proto(message: proto::PrepareRename, _: &mut Project, buffer: &Buffer) -> Result<Self> {
123        let position = message
124            .position
125            .and_then(deserialize_anchor)
126            .ok_or_else(|| anyhow!("invalid position"))?;
127        if !buffer.can_resolve(&position) {
128            Err(anyhow!("cannot resolve position"))?;
129        }
130        Ok(Self {
131            position: position.to_point_utf16(buffer),
132        })
133    }
134
135    fn response_to_proto(
136        range: Option<Range<Anchor>>,
137        _: &mut Project,
138        _: PeerId,
139        buffer_version: &clock::Global,
140        _: &AppContext,
141    ) -> proto::PrepareRenameResponse {
142        proto::PrepareRenameResponse {
143            can_rename: range.is_some(),
144            start: range
145                .as_ref()
146                .map(|range| language::proto::serialize_anchor(&range.start)),
147            end: range
148                .as_ref()
149                .map(|range| language::proto::serialize_anchor(&range.end)),
150            version: buffer_version.into(),
151        }
152    }
153
154    async fn response_from_proto(
155        self,
156        message: proto::PrepareRenameResponse,
157        _: ModelHandle<Project>,
158        buffer: ModelHandle<Buffer>,
159        mut cx: AsyncAppContext,
160    ) -> Result<Option<Range<Anchor>>> {
161        if message.can_rename {
162            buffer
163                .update(&mut cx, |buffer, _| {
164                    buffer.wait_for_version(message.version.into())
165                })
166                .await;
167            let start = message.start.and_then(deserialize_anchor);
168            let end = message.end.and_then(deserialize_anchor);
169            Ok(start.zip(end).map(|(start, end)| start..end))
170        } else {
171            Ok(None)
172        }
173    }
174
175    fn buffer_id_from_proto(message: &proto::PrepareRename) -> u64 {
176        message.buffer_id
177    }
178}
179
180#[async_trait(?Send)]
181impl LspCommand for PerformRename {
182    type Response = ProjectTransaction;
183    type LspRequest = lsp::request::Rename;
184    type ProtoRequest = proto::PerformRename;
185
186    fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::RenameParams {
187        lsp::RenameParams {
188            text_document_position: lsp::TextDocumentPositionParams {
189                text_document: lsp::TextDocumentIdentifier {
190                    uri: lsp::Url::from_file_path(path).unwrap(),
191                },
192                position: self.position.to_lsp_position(),
193            },
194            new_name: self.new_name.clone(),
195            work_done_progress_params: Default::default(),
196        }
197    }
198
199    async fn response_from_lsp(
200        self,
201        message: Option<lsp::WorkspaceEdit>,
202        project: ModelHandle<Project>,
203        buffer: ModelHandle<Buffer>,
204        mut cx: AsyncAppContext,
205    ) -> Result<ProjectTransaction> {
206        if let Some(edit) = message {
207            let (language_name, language_server) = buffer.read_with(&cx, |buffer, _| {
208                let language = buffer
209                    .language()
210                    .ok_or_else(|| anyhow!("buffer's language was removed"))?;
211                let language_server = buffer
212                    .language_server()
213                    .cloned()
214                    .ok_or_else(|| anyhow!("buffer's language server was removed"))?;
215                Ok::<_, anyhow::Error>((language.name().to_string(), language_server))
216            })?;
217            Project::deserialize_workspace_edit(
218                project,
219                edit,
220                self.push_to_history,
221                language_name,
222                language_server,
223                &mut cx,
224            )
225            .await
226        } else {
227            Ok(ProjectTransaction::default())
228        }
229    }
230
231    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PerformRename {
232        proto::PerformRename {
233            project_id,
234            buffer_id: buffer.remote_id(),
235            position: Some(language::proto::serialize_anchor(
236                &buffer.anchor_before(self.position),
237            )),
238            new_name: self.new_name.clone(),
239        }
240    }
241
242    fn from_proto(message: proto::PerformRename, _: &mut Project, buffer: &Buffer) -> Result<Self> {
243        let position = message
244            .position
245            .and_then(deserialize_anchor)
246            .ok_or_else(|| anyhow!("invalid position"))?;
247        if !buffer.can_resolve(&position) {
248            Err(anyhow!("cannot resolve position"))?;
249        }
250        Ok(Self {
251            position: position.to_point_utf16(buffer),
252            new_name: message.new_name,
253            push_to_history: false,
254        })
255    }
256
257    fn response_to_proto(
258        response: ProjectTransaction,
259        project: &mut Project,
260        peer_id: PeerId,
261        _: &clock::Global,
262        cx: &AppContext,
263    ) -> proto::PerformRenameResponse {
264        let transaction = project.serialize_project_transaction_for_peer(response, peer_id, cx);
265        proto::PerformRenameResponse {
266            transaction: Some(transaction),
267        }
268    }
269
270    async fn response_from_proto(
271        self,
272        message: proto::PerformRenameResponse,
273        project: ModelHandle<Project>,
274        _: ModelHandle<Buffer>,
275        mut cx: AsyncAppContext,
276    ) -> Result<ProjectTransaction> {
277        let message = message
278            .transaction
279            .ok_or_else(|| anyhow!("missing transaction"))?;
280        project
281            .update(&mut cx, |project, cx| {
282                project.deserialize_project_transaction(message, self.push_to_history, cx)
283            })
284            .await
285    }
286
287    fn buffer_id_from_proto(message: &proto::PerformRename) -> u64 {
288        message.buffer_id
289    }
290}
291
292#[async_trait(?Send)]
293impl LspCommand for GetDefinition {
294    type Response = Vec<Location>;
295    type LspRequest = lsp::request::GotoDefinition;
296    type ProtoRequest = proto::GetDefinition;
297
298    fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::GotoDefinitionParams {
299        lsp::GotoDefinitionParams {
300            text_document_position_params: lsp::TextDocumentPositionParams {
301                text_document: lsp::TextDocumentIdentifier {
302                    uri: lsp::Url::from_file_path(path).unwrap(),
303                },
304                position: self.position.to_lsp_position(),
305            },
306            work_done_progress_params: Default::default(),
307            partial_result_params: Default::default(),
308        }
309    }
310
311    async fn response_from_lsp(
312        self,
313        message: Option<lsp::GotoDefinitionResponse>,
314        project: ModelHandle<Project>,
315        buffer: ModelHandle<Buffer>,
316        mut cx: AsyncAppContext,
317    ) -> Result<Vec<Location>> {
318        let mut definitions = Vec::new();
319        let (language, language_server) = buffer
320            .read_with(&cx, |buffer, _| {
321                buffer
322                    .language()
323                    .cloned()
324                    .zip(buffer.language_server().cloned())
325            })
326            .ok_or_else(|| anyhow!("buffer no longer has language server"))?;
327
328        if let Some(message) = message {
329            let mut unresolved_locations = Vec::new();
330            match message {
331                lsp::GotoDefinitionResponse::Scalar(loc) => {
332                    unresolved_locations.push((loc.uri, loc.range));
333                }
334                lsp::GotoDefinitionResponse::Array(locs) => {
335                    unresolved_locations.extend(locs.into_iter().map(|l| (l.uri, l.range)));
336                }
337                lsp::GotoDefinitionResponse::Link(links) => {
338                    unresolved_locations.extend(
339                        links
340                            .into_iter()
341                            .map(|l| (l.target_uri, l.target_selection_range)),
342                    );
343                }
344            }
345
346            for (target_uri, target_range) in unresolved_locations {
347                let target_buffer_handle = project
348                    .update(&mut cx, |this, cx| {
349                        this.open_local_buffer_via_lsp(
350                            target_uri,
351                            language.name().to_string(),
352                            language_server.clone(),
353                            cx,
354                        )
355                    })
356                    .await?;
357
358                cx.read(|cx| {
359                    let target_buffer = target_buffer_handle.read(cx);
360                    let target_start = target_buffer
361                        .clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
362                    let target_end = target_buffer
363                        .clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
364                    definitions.push(Location {
365                        buffer: target_buffer_handle,
366                        range: target_buffer.anchor_after(target_start)
367                            ..target_buffer.anchor_before(target_end),
368                    });
369                });
370            }
371        }
372
373        Ok(definitions)
374    }
375
376    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDefinition {
377        proto::GetDefinition {
378            project_id,
379            buffer_id: buffer.remote_id(),
380            position: Some(language::proto::serialize_anchor(
381                &buffer.anchor_before(self.position),
382            )),
383        }
384    }
385
386    fn from_proto(message: proto::GetDefinition, _: &mut Project, buffer: &Buffer) -> Result<Self> {
387        let position = message
388            .position
389            .and_then(deserialize_anchor)
390            .ok_or_else(|| anyhow!("invalid position"))?;
391        if !buffer.can_resolve(&position) {
392            Err(anyhow!("cannot resolve position"))?;
393        }
394        Ok(Self {
395            position: position.to_point_utf16(buffer),
396        })
397    }
398
399    fn response_to_proto(
400        response: Vec<Location>,
401        project: &mut Project,
402        peer_id: PeerId,
403        _: &clock::Global,
404        cx: &AppContext,
405    ) -> proto::GetDefinitionResponse {
406        let locations = response
407            .into_iter()
408            .map(|definition| {
409                let buffer = project.serialize_buffer_for_peer(&definition.buffer, peer_id, cx);
410                proto::Location {
411                    start: Some(serialize_anchor(&definition.range.start)),
412                    end: Some(serialize_anchor(&definition.range.end)),
413                    buffer: Some(buffer),
414                }
415            })
416            .collect();
417        proto::GetDefinitionResponse { locations }
418    }
419
420    async fn response_from_proto(
421        self,
422        message: proto::GetDefinitionResponse,
423        project: ModelHandle<Project>,
424        _: ModelHandle<Buffer>,
425        mut cx: AsyncAppContext,
426    ) -> Result<Vec<Location>> {
427        let mut locations = Vec::new();
428        for location in message.locations {
429            let buffer = location.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
430            let buffer = project
431                .update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
432                .await?;
433            let start = location
434                .start
435                .and_then(deserialize_anchor)
436                .ok_or_else(|| anyhow!("missing target start"))?;
437            let end = location
438                .end
439                .and_then(deserialize_anchor)
440                .ok_or_else(|| anyhow!("missing target end"))?;
441            locations.push(Location {
442                buffer,
443                range: start..end,
444            })
445        }
446        Ok(locations)
447    }
448
449    fn buffer_id_from_proto(message: &proto::GetDefinition) -> u64 {
450        message.buffer_id
451    }
452}
453
454#[async_trait(?Send)]
455impl LspCommand for GetReferences {
456    type Response = Vec<Location>;
457    type LspRequest = lsp::request::References;
458    type ProtoRequest = proto::GetReferences;
459
460    fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::ReferenceParams {
461        lsp::ReferenceParams {
462            text_document_position: lsp::TextDocumentPositionParams {
463                text_document: lsp::TextDocumentIdentifier {
464                    uri: lsp::Url::from_file_path(path).unwrap(),
465                },
466                position: self.position.to_lsp_position(),
467            },
468            work_done_progress_params: Default::default(),
469            partial_result_params: Default::default(),
470            context: lsp::ReferenceContext {
471                include_declaration: true,
472            },
473        }
474    }
475
476    async fn response_from_lsp(
477        self,
478        locations: Option<Vec<lsp::Location>>,
479        project: ModelHandle<Project>,
480        buffer: ModelHandle<Buffer>,
481        mut cx: AsyncAppContext,
482    ) -> Result<Vec<Location>> {
483        let mut references = Vec::new();
484        let (language, language_server) = buffer
485            .read_with(&cx, |buffer, _| {
486                buffer
487                    .language()
488                    .cloned()
489                    .zip(buffer.language_server().cloned())
490            })
491            .ok_or_else(|| anyhow!("buffer no longer has language server"))?;
492
493        if let Some(locations) = locations {
494            for lsp_location in locations {
495                let target_buffer_handle = project
496                    .update(&mut cx, |this, cx| {
497                        this.open_local_buffer_via_lsp(
498                            lsp_location.uri,
499                            language.name().to_string(),
500                            language_server.clone(),
501                            cx,
502                        )
503                    })
504                    .await?;
505
506                cx.read(|cx| {
507                    let target_buffer = target_buffer_handle.read(cx);
508                    let target_start = target_buffer
509                        .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
510                    let target_end = target_buffer
511                        .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
512                    references.push(Location {
513                        buffer: target_buffer_handle,
514                        range: target_buffer.anchor_after(target_start)
515                            ..target_buffer.anchor_before(target_end),
516                    });
517                });
518            }
519        }
520
521        Ok(references)
522    }
523
524    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetReferences {
525        proto::GetReferences {
526            project_id,
527            buffer_id: buffer.remote_id(),
528            position: Some(language::proto::serialize_anchor(
529                &buffer.anchor_before(self.position),
530            )),
531        }
532    }
533
534    fn from_proto(message: proto::GetReferences, _: &mut Project, buffer: &Buffer) -> Result<Self> {
535        let position = message
536            .position
537            .and_then(deserialize_anchor)
538            .ok_or_else(|| anyhow!("invalid position"))?;
539        if !buffer.can_resolve(&position) {
540            Err(anyhow!("cannot resolve position"))?;
541        }
542        Ok(Self {
543            position: position.to_point_utf16(buffer),
544        })
545    }
546
547    fn response_to_proto(
548        response: Vec<Location>,
549        project: &mut Project,
550        peer_id: PeerId,
551        _: &clock::Global,
552        cx: &AppContext,
553    ) -> proto::GetReferencesResponse {
554        let locations = response
555            .into_iter()
556            .map(|definition| {
557                let buffer = project.serialize_buffer_for_peer(&definition.buffer, peer_id, cx);
558                proto::Location {
559                    start: Some(serialize_anchor(&definition.range.start)),
560                    end: Some(serialize_anchor(&definition.range.end)),
561                    buffer: Some(buffer),
562                }
563            })
564            .collect();
565        proto::GetReferencesResponse { locations }
566    }
567
568    async fn response_from_proto(
569        self,
570        message: proto::GetReferencesResponse,
571        project: ModelHandle<Project>,
572        _: ModelHandle<Buffer>,
573        mut cx: AsyncAppContext,
574    ) -> Result<Vec<Location>> {
575        let mut locations = Vec::new();
576        for location in message.locations {
577            let buffer = location.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
578            let target_buffer = project
579                .update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
580                .await?;
581            let start = location
582                .start
583                .and_then(deserialize_anchor)
584                .ok_or_else(|| anyhow!("missing target start"))?;
585            let end = location
586                .end
587                .and_then(deserialize_anchor)
588                .ok_or_else(|| anyhow!("missing target end"))?;
589            locations.push(Location {
590                buffer: target_buffer,
591                range: start..end,
592            })
593        }
594        Ok(locations)
595    }
596
597    fn buffer_id_from_proto(message: &proto::GetReferences) -> u64 {
598        message.buffer_id
599    }
600}