lsp_command.rs

  1use crate::{Definition, 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
 69#[async_trait(?Send)]
 70impl LspCommand for PrepareRename {
 71    type Response = Option<Range<Anchor>>;
 72    type LspRequest = lsp::request::PrepareRenameRequest;
 73    type ProtoRequest = proto::PrepareRename;
 74
 75    fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::TextDocumentPositionParams {
 76        lsp::TextDocumentPositionParams {
 77            text_document: lsp::TextDocumentIdentifier {
 78                uri: lsp::Url::from_file_path(path).unwrap(),
 79            },
 80            position: self.position.to_lsp_position(),
 81        }
 82    }
 83
 84    async fn response_from_lsp(
 85        self,
 86        message: Option<lsp::PrepareRenameResponse>,
 87        _: ModelHandle<Project>,
 88        buffer: ModelHandle<Buffer>,
 89        cx: AsyncAppContext,
 90    ) -> Result<Option<Range<Anchor>>> {
 91        buffer.read_with(&cx, |buffer, _| {
 92            if let Some(
 93                lsp::PrepareRenameResponse::Range(range)
 94                | lsp::PrepareRenameResponse::RangeWithPlaceholder { range, .. },
 95            ) = message
 96            {
 97                let Range { start, end } = range_from_lsp(range);
 98                if buffer.clip_point_utf16(start, Bias::Left) == start
 99                    && buffer.clip_point_utf16(end, Bias::Left) == end
100                {
101                    return Ok(Some(buffer.anchor_after(start)..buffer.anchor_before(end)));
102                }
103            }
104            Ok(None)
105        })
106    }
107
108    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PrepareRename {
109        proto::PrepareRename {
110            project_id,
111            buffer_id: buffer.remote_id(),
112            position: Some(language::proto::serialize_anchor(
113                &buffer.anchor_before(self.position),
114            )),
115        }
116    }
117
118    fn from_proto(message: proto::PrepareRename, _: &mut Project, buffer: &Buffer) -> Result<Self> {
119        let position = message
120            .position
121            .and_then(deserialize_anchor)
122            .ok_or_else(|| anyhow!("invalid position"))?;
123        if !buffer.can_resolve(&position) {
124            Err(anyhow!("cannot resolve position"))?;
125        }
126        Ok(Self {
127            position: position.to_point_utf16(buffer),
128        })
129    }
130
131    fn response_to_proto(
132        range: Option<Range<Anchor>>,
133        _: &mut Project,
134        _: PeerId,
135        buffer_version: &clock::Global,
136        _: &AppContext,
137    ) -> proto::PrepareRenameResponse {
138        proto::PrepareRenameResponse {
139            can_rename: range.is_some(),
140            start: range
141                .as_ref()
142                .map(|range| language::proto::serialize_anchor(&range.start)),
143            end: range
144                .as_ref()
145                .map(|range| language::proto::serialize_anchor(&range.end)),
146            version: buffer_version.into(),
147        }
148    }
149
150    async fn response_from_proto(
151        self,
152        message: proto::PrepareRenameResponse,
153        _: ModelHandle<Project>,
154        buffer: ModelHandle<Buffer>,
155        mut cx: AsyncAppContext,
156    ) -> Result<Option<Range<Anchor>>> {
157        if message.can_rename {
158            buffer
159                .update(&mut cx, |buffer, _| {
160                    buffer.wait_for_version(message.version.into())
161                })
162                .await;
163            let start = message.start.and_then(deserialize_anchor);
164            let end = message.end.and_then(deserialize_anchor);
165            Ok(start.zip(end).map(|(start, end)| start..end))
166        } else {
167            Ok(None)
168        }
169    }
170
171    fn buffer_id_from_proto(message: &proto::PrepareRename) -> u64 {
172        message.buffer_id
173    }
174}
175
176#[async_trait(?Send)]
177impl LspCommand for PerformRename {
178    type Response = ProjectTransaction;
179    type LspRequest = lsp::request::Rename;
180    type ProtoRequest = proto::PerformRename;
181
182    fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::RenameParams {
183        lsp::RenameParams {
184            text_document_position: lsp::TextDocumentPositionParams {
185                text_document: lsp::TextDocumentIdentifier {
186                    uri: lsp::Url::from_file_path(path).unwrap(),
187                },
188                position: self.position.to_lsp_position(),
189            },
190            new_name: self.new_name.clone(),
191            work_done_progress_params: Default::default(),
192        }
193    }
194
195    async fn response_from_lsp(
196        self,
197        message: Option<lsp::WorkspaceEdit>,
198        project: ModelHandle<Project>,
199        buffer: ModelHandle<Buffer>,
200        mut cx: AsyncAppContext,
201    ) -> Result<ProjectTransaction> {
202        if let Some(edit) = message {
203            let (language_name, language_server) = buffer.read_with(&cx, |buffer, _| {
204                let language = buffer
205                    .language()
206                    .ok_or_else(|| anyhow!("buffer's language was removed"))?;
207                let language_server = buffer
208                    .language_server()
209                    .cloned()
210                    .ok_or_else(|| anyhow!("buffer's language server was removed"))?;
211                Ok::<_, anyhow::Error>((language.name().to_string(), language_server))
212            })?;
213            Project::deserialize_workspace_edit(
214                project,
215                edit,
216                self.push_to_history,
217                language_name,
218                language_server,
219                &mut cx,
220            )
221            .await
222        } else {
223            Ok(ProjectTransaction::default())
224        }
225    }
226
227    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PerformRename {
228        proto::PerformRename {
229            project_id,
230            buffer_id: buffer.remote_id(),
231            position: Some(language::proto::serialize_anchor(
232                &buffer.anchor_before(self.position),
233            )),
234            new_name: self.new_name.clone(),
235        }
236    }
237
238    fn from_proto(message: proto::PerformRename, _: &mut Project, buffer: &Buffer) -> Result<Self> {
239        let position = message
240            .position
241            .and_then(deserialize_anchor)
242            .ok_or_else(|| anyhow!("invalid position"))?;
243        if !buffer.can_resolve(&position) {
244            Err(anyhow!("cannot resolve position"))?;
245        }
246        Ok(Self {
247            position: position.to_point_utf16(buffer),
248            new_name: message.new_name,
249            push_to_history: false,
250        })
251    }
252
253    fn response_to_proto(
254        response: ProjectTransaction,
255        project: &mut Project,
256        peer_id: PeerId,
257        _: &clock::Global,
258        cx: &AppContext,
259    ) -> proto::PerformRenameResponse {
260        let transaction = project.serialize_project_transaction_for_peer(response, peer_id, cx);
261        proto::PerformRenameResponse {
262            transaction: Some(transaction),
263        }
264    }
265
266    async fn response_from_proto(
267        self,
268        message: proto::PerformRenameResponse,
269        project: ModelHandle<Project>,
270        _: ModelHandle<Buffer>,
271        mut cx: AsyncAppContext,
272    ) -> Result<ProjectTransaction> {
273        let message = message
274            .transaction
275            .ok_or_else(|| anyhow!("missing transaction"))?;
276        project
277            .update(&mut cx, |project, cx| {
278                project.deserialize_project_transaction(message, self.push_to_history, cx)
279            })
280            .await
281    }
282
283    fn buffer_id_from_proto(message: &proto::PerformRename) -> u64 {
284        message.buffer_id
285    }
286}
287
288#[async_trait(?Send)]
289impl LspCommand for GetDefinition {
290    type Response = Vec<Definition>;
291    type LspRequest = lsp::request::GotoDefinition;
292    type ProtoRequest = proto::GetDefinition;
293
294    fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::GotoDefinitionParams {
295        lsp::GotoDefinitionParams {
296            text_document_position_params: lsp::TextDocumentPositionParams {
297                text_document: lsp::TextDocumentIdentifier {
298                    uri: lsp::Url::from_file_path(path).unwrap(),
299                },
300                position: self.position.to_lsp_position(),
301            },
302            work_done_progress_params: Default::default(),
303            partial_result_params: Default::default(),
304        }
305    }
306
307    async fn response_from_lsp(
308        self,
309        message: Option<lsp::GotoDefinitionResponse>,
310        project: ModelHandle<Project>,
311        buffer: ModelHandle<Buffer>,
312        mut cx: AsyncAppContext,
313    ) -> Result<Vec<Definition>> {
314        let mut definitions = Vec::new();
315        let (language, language_server) = buffer
316            .read_with(&cx, |buffer, _| {
317                buffer
318                    .language()
319                    .cloned()
320                    .zip(buffer.language_server().cloned())
321            })
322            .ok_or_else(|| anyhow!("buffer no longer has language server"))?;
323
324        if let Some(message) = message {
325            let mut unresolved_locations = Vec::new();
326            match message {
327                lsp::GotoDefinitionResponse::Scalar(loc) => {
328                    unresolved_locations.push((loc.uri, loc.range));
329                }
330                lsp::GotoDefinitionResponse::Array(locs) => {
331                    unresolved_locations.extend(locs.into_iter().map(|l| (l.uri, l.range)));
332                }
333                lsp::GotoDefinitionResponse::Link(links) => {
334                    unresolved_locations.extend(
335                        links
336                            .into_iter()
337                            .map(|l| (l.target_uri, l.target_selection_range)),
338                    );
339                }
340            }
341
342            for (target_uri, target_range) in unresolved_locations {
343                let target_buffer_handle = project
344                    .update(&mut cx, |this, cx| {
345                        this.open_local_buffer_from_lsp_path(
346                            target_uri,
347                            language.name().to_string(),
348                            language_server.clone(),
349                            cx,
350                        )
351                    })
352                    .await?;
353
354                cx.read(|cx| {
355                    let target_buffer = target_buffer_handle.read(cx);
356                    let target_start = target_buffer
357                        .clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
358                    let target_end = target_buffer
359                        .clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
360                    definitions.push(Definition {
361                        target_buffer: target_buffer_handle,
362                        target_range: target_buffer.anchor_after(target_start)
363                            ..target_buffer.anchor_before(target_end),
364                    });
365                });
366            }
367        }
368
369        Ok(definitions)
370    }
371
372    fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDefinition {
373        proto::GetDefinition {
374            project_id,
375            buffer_id: buffer.remote_id(),
376            position: Some(language::proto::serialize_anchor(
377                &buffer.anchor_before(self.position),
378            )),
379        }
380    }
381
382    fn from_proto(message: proto::GetDefinition, _: &mut Project, buffer: &Buffer) -> Result<Self> {
383        let position = message
384            .position
385            .and_then(deserialize_anchor)
386            .ok_or_else(|| anyhow!("invalid position"))?;
387        if !buffer.can_resolve(&position) {
388            Err(anyhow!("cannot resolve position"))?;
389        }
390        Ok(Self {
391            position: position.to_point_utf16(buffer),
392        })
393    }
394
395    fn response_to_proto(
396        response: Vec<Definition>,
397        project: &mut Project,
398        peer_id: PeerId,
399        _: &clock::Global,
400        cx: &AppContext,
401    ) -> proto::GetDefinitionResponse {
402        let definitions = response
403            .into_iter()
404            .map(|definition| {
405                let buffer =
406                    project.serialize_buffer_for_peer(&definition.target_buffer, peer_id, cx);
407                proto::Definition {
408                    target_start: Some(serialize_anchor(&definition.target_range.start)),
409                    target_end: Some(serialize_anchor(&definition.target_range.end)),
410                    buffer: Some(buffer),
411                }
412            })
413            .collect();
414        proto::GetDefinitionResponse { definitions }
415    }
416
417    async fn response_from_proto(
418        self,
419        message: proto::GetDefinitionResponse,
420        project: ModelHandle<Project>,
421        _: ModelHandle<Buffer>,
422        mut cx: AsyncAppContext,
423    ) -> Result<Vec<Definition>> {
424        let mut definitions = Vec::new();
425        for definition in message.definitions {
426            let buffer = definition.buffer.ok_or_else(|| anyhow!("missing buffer"))?;
427            let target_buffer = project
428                .update(&mut cx, |this, cx| this.deserialize_buffer(buffer, cx))
429                .await?;
430            let target_start = definition
431                .target_start
432                .and_then(deserialize_anchor)
433                .ok_or_else(|| anyhow!("missing target start"))?;
434            let target_end = definition
435                .target_end
436                .and_then(deserialize_anchor)
437                .ok_or_else(|| anyhow!("missing target end"))?;
438            definitions.push(Definition {
439                target_buffer,
440                target_range: target_start..target_end,
441            })
442        }
443        Ok(definitions)
444    }
445
446    fn buffer_id_from_proto(message: &proto::GetDefinition) -> u64 {
447        message.buffer_id
448    }
449}