1use crate::{
2 DocumentHighlight, Hover, HoverBlock, Location, LocationLink, Project, ProjectTransaction,
3};
4use anyhow::{anyhow, Result};
5use async_trait::async_trait;
6use client::{proto, PeerId};
7use gpui::{AppContext, AsyncAppContext, ModelHandle};
8use language::{
9 point_from_lsp, point_to_lsp,
10 proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version},
11 range_from_lsp, Anchor, Bias, Buffer, CachedLspAdapter, PointUtf16, ToPointUtf16,
12};
13use lsp::{DocumentHighlightKind, LanguageServer, ServerCapabilities};
14use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag};
15use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc};
16
17#[async_trait(?Send)]
18pub(crate) trait LspCommand: 'static + Sized {
19 type Response: 'static + Default + Send;
20 type LspRequest: 'static + Send + lsp::request::Request;
21 type ProtoRequest: 'static + Send + proto::RequestMessage;
22
23 fn check_capabilities(&self, _: &lsp::ServerCapabilities) -> bool {
24 true
25 }
26
27 fn to_lsp(
28 &self,
29 path: &Path,
30 cx: &AppContext,
31 ) -> <Self::LspRequest as lsp::request::Request>::Params;
32 async fn response_from_lsp(
33 self,
34 message: <Self::LspRequest as lsp::request::Request>::Result,
35 project: ModelHandle<Project>,
36 buffer: ModelHandle<Buffer>,
37 cx: AsyncAppContext,
38 ) -> Result<Self::Response>;
39
40 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest;
41 async fn from_proto(
42 message: Self::ProtoRequest,
43 project: ModelHandle<Project>,
44 buffer: ModelHandle<Buffer>,
45 cx: AsyncAppContext,
46 ) -> Result<Self>;
47 fn response_to_proto(
48 response: Self::Response,
49 project: &mut Project,
50 peer_id: PeerId,
51 buffer_version: &clock::Global,
52 cx: &AppContext,
53 ) -> <Self::ProtoRequest as proto::RequestMessage>::Response;
54 async fn response_from_proto(
55 self,
56 message: <Self::ProtoRequest as proto::RequestMessage>::Response,
57 project: ModelHandle<Project>,
58 buffer: ModelHandle<Buffer>,
59 cx: AsyncAppContext,
60 ) -> Result<Self::Response>;
61 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64;
62}
63
64pub(crate) struct PrepareRename {
65 pub position: PointUtf16,
66}
67
68pub(crate) struct PerformRename {
69 pub position: PointUtf16,
70 pub new_name: String,
71 pub push_to_history: bool,
72}
73
74pub(crate) struct GetDefinition {
75 pub position: PointUtf16,
76}
77
78pub(crate) struct GetTypeDefinition {
79 pub position: PointUtf16,
80}
81
82pub(crate) struct GetReferences {
83 pub position: PointUtf16,
84}
85
86pub(crate) struct GetDocumentHighlights {
87 pub position: PointUtf16,
88}
89
90pub(crate) struct GetHover {
91 pub position: PointUtf16,
92}
93
94#[async_trait(?Send)]
95impl LspCommand for PrepareRename {
96 type Response = Option<Range<Anchor>>;
97 type LspRequest = lsp::request::PrepareRenameRequest;
98 type ProtoRequest = proto::PrepareRename;
99
100 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
101 if let Some(lsp::OneOf::Right(rename)) = &capabilities.rename_provider {
102 rename.prepare_provider == Some(true)
103 } else {
104 false
105 }
106 }
107
108 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::TextDocumentPositionParams {
109 lsp::TextDocumentPositionParams {
110 text_document: lsp::TextDocumentIdentifier {
111 uri: lsp::Url::from_file_path(path).unwrap(),
112 },
113 position: point_to_lsp(self.position),
114 }
115 }
116
117 async fn response_from_lsp(
118 self,
119 message: Option<lsp::PrepareRenameResponse>,
120 _: ModelHandle<Project>,
121 buffer: ModelHandle<Buffer>,
122 cx: AsyncAppContext,
123 ) -> Result<Option<Range<Anchor>>> {
124 buffer.read_with(&cx, |buffer, _| {
125 if let Some(
126 lsp::PrepareRenameResponse::Range(range)
127 | lsp::PrepareRenameResponse::RangeWithPlaceholder { range, .. },
128 ) = message
129 {
130 let Range { start, end } = range_from_lsp(range);
131 if buffer.clip_point_utf16(start, Bias::Left) == start
132 && buffer.clip_point_utf16(end, Bias::Left) == end
133 {
134 return Ok(Some(
135 buffer.clamped_anchor_after(start)..buffer.clamped_anchor_before(end),
136 ));
137 }
138 }
139 Ok(None)
140 })
141 }
142
143 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PrepareRename {
144 proto::PrepareRename {
145 project_id,
146 buffer_id: buffer.remote_id(),
147 position: Some(language::proto::serialize_anchor(
148 &buffer.clamped_anchor_before(self.position),
149 )),
150 version: serialize_version(&buffer.version()),
151 }
152 }
153
154 async fn from_proto(
155 message: proto::PrepareRename,
156 _: ModelHandle<Project>,
157 buffer: ModelHandle<Buffer>,
158 mut cx: AsyncAppContext,
159 ) -> Result<Self> {
160 let position = message
161 .position
162 .and_then(deserialize_anchor)
163 .ok_or_else(|| anyhow!("invalid position"))?;
164 buffer
165 .update(&mut cx, |buffer, _| {
166 buffer.wait_for_version(deserialize_version(message.version))
167 })
168 .await;
169
170 Ok(Self {
171 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
172 })
173 }
174
175 fn response_to_proto(
176 range: Option<Range<Anchor>>,
177 _: &mut Project,
178 _: PeerId,
179 buffer_version: &clock::Global,
180 _: &AppContext,
181 ) -> proto::PrepareRenameResponse {
182 proto::PrepareRenameResponse {
183 can_rename: range.is_some(),
184 start: range
185 .as_ref()
186 .map(|range| language::proto::serialize_anchor(&range.start)),
187 end: range
188 .as_ref()
189 .map(|range| language::proto::serialize_anchor(&range.end)),
190 version: serialize_version(buffer_version),
191 }
192 }
193
194 async fn response_from_proto(
195 self,
196 message: proto::PrepareRenameResponse,
197 _: ModelHandle<Project>,
198 buffer: ModelHandle<Buffer>,
199 mut cx: AsyncAppContext,
200 ) -> Result<Option<Range<Anchor>>> {
201 if message.can_rename {
202 buffer
203 .update(&mut cx, |buffer, _| {
204 buffer.wait_for_version(deserialize_version(message.version))
205 })
206 .await;
207 let start = message.start.and_then(deserialize_anchor);
208 let end = message.end.and_then(deserialize_anchor);
209 Ok(start.zip(end).map(|(start, end)| start..end))
210 } else {
211 Ok(None)
212 }
213 }
214
215 fn buffer_id_from_proto(message: &proto::PrepareRename) -> u64 {
216 message.buffer_id
217 }
218}
219
220#[async_trait(?Send)]
221impl LspCommand for PerformRename {
222 type Response = ProjectTransaction;
223 type LspRequest = lsp::request::Rename;
224 type ProtoRequest = proto::PerformRename;
225
226 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::RenameParams {
227 lsp::RenameParams {
228 text_document_position: lsp::TextDocumentPositionParams {
229 text_document: lsp::TextDocumentIdentifier {
230 uri: lsp::Url::from_file_path(path).unwrap(),
231 },
232 position: point_to_lsp(self.position),
233 },
234 new_name: self.new_name.clone(),
235 work_done_progress_params: Default::default(),
236 }
237 }
238
239 async fn response_from_lsp(
240 self,
241 message: Option<lsp::WorkspaceEdit>,
242 project: ModelHandle<Project>,
243 buffer: ModelHandle<Buffer>,
244 mut cx: AsyncAppContext,
245 ) -> Result<ProjectTransaction> {
246 if let Some(edit) = message {
247 let (lsp_adapter, lsp_server) = language_server_for_buffer(&project, &buffer, &mut cx)?;
248 Project::deserialize_workspace_edit(
249 project,
250 edit,
251 self.push_to_history,
252 lsp_adapter,
253 lsp_server,
254 &mut cx,
255 )
256 .await
257 } else {
258 Ok(ProjectTransaction::default())
259 }
260 }
261
262 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PerformRename {
263 proto::PerformRename {
264 project_id,
265 buffer_id: buffer.remote_id(),
266 position: Some(language::proto::serialize_anchor(
267 &buffer.clamped_anchor_before(self.position),
268 )),
269 new_name: self.new_name.clone(),
270 version: serialize_version(&buffer.version()),
271 }
272 }
273
274 async fn from_proto(
275 message: proto::PerformRename,
276 _: ModelHandle<Project>,
277 buffer: ModelHandle<Buffer>,
278 mut cx: AsyncAppContext,
279 ) -> Result<Self> {
280 let position = message
281 .position
282 .and_then(deserialize_anchor)
283 .ok_or_else(|| anyhow!("invalid position"))?;
284 buffer
285 .update(&mut cx, |buffer, _| {
286 buffer.wait_for_version(deserialize_version(message.version))
287 })
288 .await;
289 Ok(Self {
290 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
291 new_name: message.new_name,
292 push_to_history: false,
293 })
294 }
295
296 fn response_to_proto(
297 response: ProjectTransaction,
298 project: &mut Project,
299 peer_id: PeerId,
300 _: &clock::Global,
301 cx: &AppContext,
302 ) -> proto::PerformRenameResponse {
303 let transaction = project.serialize_project_transaction_for_peer(response, peer_id, cx);
304 proto::PerformRenameResponse {
305 transaction: Some(transaction),
306 }
307 }
308
309 async fn response_from_proto(
310 self,
311 message: proto::PerformRenameResponse,
312 project: ModelHandle<Project>,
313 _: ModelHandle<Buffer>,
314 mut cx: AsyncAppContext,
315 ) -> Result<ProjectTransaction> {
316 let message = message
317 .transaction
318 .ok_or_else(|| anyhow!("missing transaction"))?;
319 project
320 .update(&mut cx, |project, cx| {
321 project.deserialize_project_transaction(message, self.push_to_history, cx)
322 })
323 .await
324 }
325
326 fn buffer_id_from_proto(message: &proto::PerformRename) -> u64 {
327 message.buffer_id
328 }
329}
330
331#[async_trait(?Send)]
332impl LspCommand for GetDefinition {
333 type Response = Vec<LocationLink>;
334 type LspRequest = lsp::request::GotoDefinition;
335 type ProtoRequest = proto::GetDefinition;
336
337 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::GotoDefinitionParams {
338 lsp::GotoDefinitionParams {
339 text_document_position_params: lsp::TextDocumentPositionParams {
340 text_document: lsp::TextDocumentIdentifier {
341 uri: lsp::Url::from_file_path(path).unwrap(),
342 },
343 position: point_to_lsp(self.position),
344 },
345 work_done_progress_params: Default::default(),
346 partial_result_params: Default::default(),
347 }
348 }
349
350 async fn response_from_lsp(
351 self,
352 message: Option<lsp::GotoDefinitionResponse>,
353 project: ModelHandle<Project>,
354 buffer: ModelHandle<Buffer>,
355 cx: AsyncAppContext,
356 ) -> Result<Vec<LocationLink>> {
357 location_links_from_lsp(message, project, buffer, cx).await
358 }
359
360 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDefinition {
361 proto::GetDefinition {
362 project_id,
363 buffer_id: buffer.remote_id(),
364 position: Some(language::proto::serialize_anchor(
365 &buffer.clamped_anchor_before(self.position),
366 )),
367 version: serialize_version(&buffer.version()),
368 }
369 }
370
371 async fn from_proto(
372 message: proto::GetDefinition,
373 _: ModelHandle<Project>,
374 buffer: ModelHandle<Buffer>,
375 mut cx: AsyncAppContext,
376 ) -> Result<Self> {
377 let position = message
378 .position
379 .and_then(deserialize_anchor)
380 .ok_or_else(|| anyhow!("invalid position"))?;
381 buffer
382 .update(&mut cx, |buffer, _| {
383 buffer.wait_for_version(deserialize_version(message.version))
384 })
385 .await;
386 Ok(Self {
387 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
388 })
389 }
390
391 fn response_to_proto(
392 response: Vec<LocationLink>,
393 project: &mut Project,
394 peer_id: PeerId,
395 _: &clock::Global,
396 cx: &AppContext,
397 ) -> proto::GetDefinitionResponse {
398 let links = location_links_to_proto(response, project, peer_id, cx);
399 proto::GetDefinitionResponse { links }
400 }
401
402 async fn response_from_proto(
403 self,
404 message: proto::GetDefinitionResponse,
405 project: ModelHandle<Project>,
406 _: ModelHandle<Buffer>,
407 cx: AsyncAppContext,
408 ) -> Result<Vec<LocationLink>> {
409 location_links_from_proto(message.links, project, cx).await
410 }
411
412 fn buffer_id_from_proto(message: &proto::GetDefinition) -> u64 {
413 message.buffer_id
414 }
415}
416
417#[async_trait(?Send)]
418impl LspCommand for GetTypeDefinition {
419 type Response = Vec<LocationLink>;
420 type LspRequest = lsp::request::GotoTypeDefinition;
421 type ProtoRequest = proto::GetTypeDefinition;
422
423 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::GotoTypeDefinitionParams {
424 lsp::GotoTypeDefinitionParams {
425 text_document_position_params: lsp::TextDocumentPositionParams {
426 text_document: lsp::TextDocumentIdentifier {
427 uri: lsp::Url::from_file_path(path).unwrap(),
428 },
429 position: point_to_lsp(self.position),
430 },
431 work_done_progress_params: Default::default(),
432 partial_result_params: Default::default(),
433 }
434 }
435
436 async fn response_from_lsp(
437 self,
438 message: Option<lsp::GotoTypeDefinitionResponse>,
439 project: ModelHandle<Project>,
440 buffer: ModelHandle<Buffer>,
441 cx: AsyncAppContext,
442 ) -> Result<Vec<LocationLink>> {
443 location_links_from_lsp(message, project, buffer, cx).await
444 }
445
446 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetTypeDefinition {
447 proto::GetTypeDefinition {
448 project_id,
449 buffer_id: buffer.remote_id(),
450 position: Some(language::proto::serialize_anchor(
451 &buffer.clamped_anchor_before(self.position),
452 )),
453 version: serialize_version(&buffer.version()),
454 }
455 }
456
457 async fn from_proto(
458 message: proto::GetTypeDefinition,
459 _: ModelHandle<Project>,
460 buffer: ModelHandle<Buffer>,
461 mut cx: AsyncAppContext,
462 ) -> Result<Self> {
463 let position = message
464 .position
465 .and_then(deserialize_anchor)
466 .ok_or_else(|| anyhow!("invalid position"))?;
467 buffer
468 .update(&mut cx, |buffer, _| {
469 buffer.wait_for_version(deserialize_version(message.version))
470 })
471 .await;
472 Ok(Self {
473 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
474 })
475 }
476
477 fn response_to_proto(
478 response: Vec<LocationLink>,
479 project: &mut Project,
480 peer_id: PeerId,
481 _: &clock::Global,
482 cx: &AppContext,
483 ) -> proto::GetTypeDefinitionResponse {
484 let links = location_links_to_proto(response, project, peer_id, cx);
485 proto::GetTypeDefinitionResponse { links }
486 }
487
488 async fn response_from_proto(
489 self,
490 message: proto::GetTypeDefinitionResponse,
491 project: ModelHandle<Project>,
492 _: ModelHandle<Buffer>,
493 cx: AsyncAppContext,
494 ) -> Result<Vec<LocationLink>> {
495 location_links_from_proto(message.links, project, cx).await
496 }
497
498 fn buffer_id_from_proto(message: &proto::GetTypeDefinition) -> u64 {
499 message.buffer_id
500 }
501}
502
503fn language_server_for_buffer(
504 project: &ModelHandle<Project>,
505 buffer: &ModelHandle<Buffer>,
506 cx: &mut AsyncAppContext,
507) -> Result<(Arc<CachedLspAdapter>, Arc<LanguageServer>)> {
508 project
509 .read_with(cx, |project, cx| {
510 project
511 .language_server_for_buffer(buffer.read(cx), cx)
512 .map(|(adapter, server)| (adapter.clone(), server.clone()))
513 })
514 .ok_or_else(|| anyhow!("no language server found for buffer"))
515}
516
517async fn location_links_from_proto(
518 proto_links: Vec<proto::LocationLink>,
519 project: ModelHandle<Project>,
520 mut cx: AsyncAppContext,
521) -> Result<Vec<LocationLink>> {
522 let mut links = Vec::new();
523
524 for link in proto_links {
525 let origin = match link.origin {
526 Some(origin) => {
527 let buffer = project
528 .update(&mut cx, |this, cx| {
529 this.wait_for_buffer(origin.buffer_id, cx)
530 })
531 .await?;
532 let start = origin
533 .start
534 .and_then(deserialize_anchor)
535 .ok_or_else(|| anyhow!("missing origin start"))?;
536 let end = origin
537 .end
538 .and_then(deserialize_anchor)
539 .ok_or_else(|| anyhow!("missing origin end"))?;
540 buffer
541 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
542 .await;
543 Some(Location {
544 buffer,
545 range: start..end,
546 })
547 }
548 None => None,
549 };
550
551 let target = link.target.ok_or_else(|| anyhow!("missing target"))?;
552 let buffer = project
553 .update(&mut cx, |this, cx| {
554 this.wait_for_buffer(target.buffer_id, cx)
555 })
556 .await?;
557 let start = target
558 .start
559 .and_then(deserialize_anchor)
560 .ok_or_else(|| anyhow!("missing target start"))?;
561 let end = target
562 .end
563 .and_then(deserialize_anchor)
564 .ok_or_else(|| anyhow!("missing target end"))?;
565 buffer
566 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
567 .await;
568 let target = Location {
569 buffer,
570 range: start..end,
571 };
572
573 links.push(LocationLink { origin, target })
574 }
575
576 Ok(links)
577}
578
579async fn location_links_from_lsp(
580 message: Option<lsp::GotoDefinitionResponse>,
581 project: ModelHandle<Project>,
582 buffer: ModelHandle<Buffer>,
583 mut cx: AsyncAppContext,
584) -> Result<Vec<LocationLink>> {
585 let message = match message {
586 Some(message) => message,
587 None => return Ok(Vec::new()),
588 };
589
590 let mut unresolved_links = Vec::new();
591 match message {
592 lsp::GotoDefinitionResponse::Scalar(loc) => {
593 unresolved_links.push((None, loc.uri, loc.range));
594 }
595
596 lsp::GotoDefinitionResponse::Array(locs) => {
597 unresolved_links.extend(locs.into_iter().map(|l| (None, l.uri, l.range)));
598 }
599
600 lsp::GotoDefinitionResponse::Link(links) => {
601 unresolved_links.extend(links.into_iter().map(|l| {
602 (
603 l.origin_selection_range,
604 l.target_uri,
605 l.target_selection_range,
606 )
607 }));
608 }
609 }
610
611 let (lsp_adapter, language_server) = language_server_for_buffer(&project, &buffer, &mut cx)?;
612 let mut definitions = Vec::new();
613 for (origin_range, target_uri, target_range) in unresolved_links {
614 let target_buffer_handle = project
615 .update(&mut cx, |this, cx| {
616 this.open_local_buffer_via_lsp(
617 target_uri,
618 language_server.server_id(),
619 lsp_adapter.name.clone(),
620 cx,
621 )
622 })
623 .await?;
624
625 cx.read(|cx| {
626 let origin_location = origin_range.map(|origin_range| {
627 let origin_buffer = buffer.read(cx);
628 let origin_start =
629 origin_buffer.clip_point_utf16(point_from_lsp(origin_range.start), Bias::Left);
630 let origin_end =
631 origin_buffer.clip_point_utf16(point_from_lsp(origin_range.end), Bias::Left);
632 Location {
633 buffer: buffer.clone(),
634 range: origin_buffer.clamped_anchor_after(origin_start)
635 ..origin_buffer.clamped_anchor_before(origin_end),
636 }
637 });
638
639 let target_buffer = target_buffer_handle.read(cx);
640 let target_start =
641 target_buffer.clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
642 let target_end =
643 target_buffer.clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
644 let target_location = Location {
645 buffer: target_buffer_handle,
646 range: target_buffer.clamped_anchor_after(target_start)
647 ..target_buffer.clamped_anchor_before(target_end),
648 };
649
650 definitions.push(LocationLink {
651 origin: origin_location,
652 target: target_location,
653 })
654 });
655 }
656 Ok(definitions)
657}
658
659fn location_links_to_proto(
660 links: Vec<LocationLink>,
661 project: &mut Project,
662 peer_id: PeerId,
663 cx: &AppContext,
664) -> Vec<proto::LocationLink> {
665 links
666 .into_iter()
667 .map(|definition| {
668 let origin = definition.origin.map(|origin| {
669 let buffer_id = project.create_buffer_for_peer(&origin.buffer, peer_id, cx);
670 proto::Location {
671 start: Some(serialize_anchor(&origin.range.start)),
672 end: Some(serialize_anchor(&origin.range.end)),
673 buffer_id,
674 }
675 });
676
677 let buffer_id = project.create_buffer_for_peer(&definition.target.buffer, peer_id, cx);
678 let target = proto::Location {
679 start: Some(serialize_anchor(&definition.target.range.start)),
680 end: Some(serialize_anchor(&definition.target.range.end)),
681 buffer_id,
682 };
683
684 proto::LocationLink {
685 origin,
686 target: Some(target),
687 }
688 })
689 .collect()
690}
691
692#[async_trait(?Send)]
693impl LspCommand for GetReferences {
694 type Response = Vec<Location>;
695 type LspRequest = lsp::request::References;
696 type ProtoRequest = proto::GetReferences;
697
698 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::ReferenceParams {
699 lsp::ReferenceParams {
700 text_document_position: lsp::TextDocumentPositionParams {
701 text_document: lsp::TextDocumentIdentifier {
702 uri: lsp::Url::from_file_path(path).unwrap(),
703 },
704 position: point_to_lsp(self.position),
705 },
706 work_done_progress_params: Default::default(),
707 partial_result_params: Default::default(),
708 context: lsp::ReferenceContext {
709 include_declaration: true,
710 },
711 }
712 }
713
714 async fn response_from_lsp(
715 self,
716 locations: Option<Vec<lsp::Location>>,
717 project: ModelHandle<Project>,
718 buffer: ModelHandle<Buffer>,
719 mut cx: AsyncAppContext,
720 ) -> Result<Vec<Location>> {
721 let mut references = Vec::new();
722 let (lsp_adapter, language_server) =
723 language_server_for_buffer(&project, &buffer, &mut cx)?;
724
725 if let Some(locations) = locations {
726 for lsp_location in locations {
727 let target_buffer_handle = project
728 .update(&mut cx, |this, cx| {
729 this.open_local_buffer_via_lsp(
730 lsp_location.uri,
731 language_server.server_id(),
732 lsp_adapter.name.clone(),
733 cx,
734 )
735 })
736 .await?;
737
738 cx.read(|cx| {
739 let target_buffer = target_buffer_handle.read(cx);
740 let target_start = target_buffer
741 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
742 let target_end = target_buffer
743 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
744 references.push(Location {
745 buffer: target_buffer_handle,
746 range: target_buffer.clamped_anchor_after(target_start)
747 ..target_buffer.clamped_anchor_before(target_end),
748 });
749 });
750 }
751 }
752
753 Ok(references)
754 }
755
756 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetReferences {
757 proto::GetReferences {
758 project_id,
759 buffer_id: buffer.remote_id(),
760 position: Some(language::proto::serialize_anchor(
761 &buffer.clamped_anchor_before(self.position),
762 )),
763 version: serialize_version(&buffer.version()),
764 }
765 }
766
767 async fn from_proto(
768 message: proto::GetReferences,
769 _: ModelHandle<Project>,
770 buffer: ModelHandle<Buffer>,
771 mut cx: AsyncAppContext,
772 ) -> Result<Self> {
773 let position = message
774 .position
775 .and_then(deserialize_anchor)
776 .ok_or_else(|| anyhow!("invalid position"))?;
777 buffer
778 .update(&mut cx, |buffer, _| {
779 buffer.wait_for_version(deserialize_version(message.version))
780 })
781 .await;
782 Ok(Self {
783 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
784 })
785 }
786
787 fn response_to_proto(
788 response: Vec<Location>,
789 project: &mut Project,
790 peer_id: PeerId,
791 _: &clock::Global,
792 cx: &AppContext,
793 ) -> proto::GetReferencesResponse {
794 let locations = response
795 .into_iter()
796 .map(|definition| {
797 let buffer_id = project.create_buffer_for_peer(&definition.buffer, peer_id, cx);
798 proto::Location {
799 start: Some(serialize_anchor(&definition.range.start)),
800 end: Some(serialize_anchor(&definition.range.end)),
801 buffer_id,
802 }
803 })
804 .collect();
805 proto::GetReferencesResponse { locations }
806 }
807
808 async fn response_from_proto(
809 self,
810 message: proto::GetReferencesResponse,
811 project: ModelHandle<Project>,
812 _: ModelHandle<Buffer>,
813 mut cx: AsyncAppContext,
814 ) -> Result<Vec<Location>> {
815 let mut locations = Vec::new();
816 for location in message.locations {
817 let target_buffer = project
818 .update(&mut cx, |this, cx| {
819 this.wait_for_buffer(location.buffer_id, cx)
820 })
821 .await?;
822 let start = location
823 .start
824 .and_then(deserialize_anchor)
825 .ok_or_else(|| anyhow!("missing target start"))?;
826 let end = location
827 .end
828 .and_then(deserialize_anchor)
829 .ok_or_else(|| anyhow!("missing target end"))?;
830 target_buffer
831 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
832 .await;
833 locations.push(Location {
834 buffer: target_buffer,
835 range: start..end,
836 })
837 }
838 Ok(locations)
839 }
840
841 fn buffer_id_from_proto(message: &proto::GetReferences) -> u64 {
842 message.buffer_id
843 }
844}
845
846#[async_trait(?Send)]
847impl LspCommand for GetDocumentHighlights {
848 type Response = Vec<DocumentHighlight>;
849 type LspRequest = lsp::request::DocumentHighlightRequest;
850 type ProtoRequest = proto::GetDocumentHighlights;
851
852 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
853 capabilities.document_highlight_provider.is_some()
854 }
855
856 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::DocumentHighlightParams {
857 lsp::DocumentHighlightParams {
858 text_document_position_params: lsp::TextDocumentPositionParams {
859 text_document: lsp::TextDocumentIdentifier {
860 uri: lsp::Url::from_file_path(path).unwrap(),
861 },
862 position: point_to_lsp(self.position),
863 },
864 work_done_progress_params: Default::default(),
865 partial_result_params: Default::default(),
866 }
867 }
868
869 async fn response_from_lsp(
870 self,
871 lsp_highlights: Option<Vec<lsp::DocumentHighlight>>,
872 _: ModelHandle<Project>,
873 buffer: ModelHandle<Buffer>,
874 cx: AsyncAppContext,
875 ) -> Result<Vec<DocumentHighlight>> {
876 buffer.read_with(&cx, |buffer, _| {
877 let mut lsp_highlights = lsp_highlights.unwrap_or_default();
878 lsp_highlights.sort_unstable_by_key(|h| (h.range.start, Reverse(h.range.end)));
879 Ok(lsp_highlights
880 .into_iter()
881 .map(|lsp_highlight| {
882 let start = buffer
883 .clip_point_utf16(point_from_lsp(lsp_highlight.range.start), Bias::Left);
884 let end = buffer
885 .clip_point_utf16(point_from_lsp(lsp_highlight.range.end), Bias::Left);
886 DocumentHighlight {
887 range: buffer.clamped_anchor_after(start)
888 ..buffer.clamped_anchor_before(end),
889 kind: lsp_highlight
890 .kind
891 .unwrap_or(lsp::DocumentHighlightKind::READ),
892 }
893 })
894 .collect())
895 })
896 }
897
898 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentHighlights {
899 proto::GetDocumentHighlights {
900 project_id,
901 buffer_id: buffer.remote_id(),
902 position: Some(language::proto::serialize_anchor(
903 &buffer.clamped_anchor_before(self.position),
904 )),
905 version: serialize_version(&buffer.version()),
906 }
907 }
908
909 async fn from_proto(
910 message: proto::GetDocumentHighlights,
911 _: ModelHandle<Project>,
912 buffer: ModelHandle<Buffer>,
913 mut cx: AsyncAppContext,
914 ) -> Result<Self> {
915 let position = message
916 .position
917 .and_then(deserialize_anchor)
918 .ok_or_else(|| anyhow!("invalid position"))?;
919 buffer
920 .update(&mut cx, |buffer, _| {
921 buffer.wait_for_version(deserialize_version(message.version))
922 })
923 .await;
924 Ok(Self {
925 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
926 })
927 }
928
929 fn response_to_proto(
930 response: Vec<DocumentHighlight>,
931 _: &mut Project,
932 _: PeerId,
933 _: &clock::Global,
934 _: &AppContext,
935 ) -> proto::GetDocumentHighlightsResponse {
936 let highlights = response
937 .into_iter()
938 .map(|highlight| proto::DocumentHighlight {
939 start: Some(serialize_anchor(&highlight.range.start)),
940 end: Some(serialize_anchor(&highlight.range.end)),
941 kind: match highlight.kind {
942 DocumentHighlightKind::TEXT => proto::document_highlight::Kind::Text.into(),
943 DocumentHighlightKind::WRITE => proto::document_highlight::Kind::Write.into(),
944 DocumentHighlightKind::READ => proto::document_highlight::Kind::Read.into(),
945 _ => proto::document_highlight::Kind::Text.into(),
946 },
947 })
948 .collect();
949 proto::GetDocumentHighlightsResponse { highlights }
950 }
951
952 async fn response_from_proto(
953 self,
954 message: proto::GetDocumentHighlightsResponse,
955 _: ModelHandle<Project>,
956 buffer: ModelHandle<Buffer>,
957 mut cx: AsyncAppContext,
958 ) -> Result<Vec<DocumentHighlight>> {
959 let mut highlights = Vec::new();
960 for highlight in message.highlights {
961 let start = highlight
962 .start
963 .and_then(deserialize_anchor)
964 .ok_or_else(|| anyhow!("missing target start"))?;
965 let end = highlight
966 .end
967 .and_then(deserialize_anchor)
968 .ok_or_else(|| anyhow!("missing target end"))?;
969 buffer
970 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
971 .await;
972 let kind = match proto::document_highlight::Kind::from_i32(highlight.kind) {
973 Some(proto::document_highlight::Kind::Text) => DocumentHighlightKind::TEXT,
974 Some(proto::document_highlight::Kind::Read) => DocumentHighlightKind::READ,
975 Some(proto::document_highlight::Kind::Write) => DocumentHighlightKind::WRITE,
976 None => DocumentHighlightKind::TEXT,
977 };
978 highlights.push(DocumentHighlight {
979 range: start..end,
980 kind,
981 });
982 }
983 Ok(highlights)
984 }
985
986 fn buffer_id_from_proto(message: &proto::GetDocumentHighlights) -> u64 {
987 message.buffer_id
988 }
989}
990
991#[async_trait(?Send)]
992impl LspCommand for GetHover {
993 type Response = Option<Hover>;
994 type LspRequest = lsp::request::HoverRequest;
995 type ProtoRequest = proto::GetHover;
996
997 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::HoverParams {
998 lsp::HoverParams {
999 text_document_position_params: lsp::TextDocumentPositionParams {
1000 text_document: lsp::TextDocumentIdentifier {
1001 uri: lsp::Url::from_file_path(path).unwrap(),
1002 },
1003 position: point_to_lsp(self.position),
1004 },
1005 work_done_progress_params: Default::default(),
1006 }
1007 }
1008
1009 async fn response_from_lsp(
1010 self,
1011 message: Option<lsp::Hover>,
1012 _: ModelHandle<Project>,
1013 buffer: ModelHandle<Buffer>,
1014 cx: AsyncAppContext,
1015 ) -> Result<Self::Response> {
1016 Ok(message.and_then(|hover| {
1017 let range = hover.range.map(|range| {
1018 cx.read(|cx| {
1019 let buffer = buffer.read(cx);
1020 let token_start =
1021 buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left);
1022 let token_end = buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left);
1023 buffer.clamped_anchor_after(token_start)
1024 ..buffer.clamped_anchor_before(token_end)
1025 })
1026 });
1027
1028 let contents = cx.read(|_| match hover.contents {
1029 lsp::HoverContents::Scalar(marked_string) => {
1030 HoverBlock::try_new(marked_string).map(|contents| vec![contents])
1031 }
1032 lsp::HoverContents::Array(marked_strings) => {
1033 let content: Vec<HoverBlock> = marked_strings
1034 .into_iter()
1035 .filter_map(HoverBlock::try_new)
1036 .collect();
1037 if content.is_empty() {
1038 None
1039 } else {
1040 Some(content)
1041 }
1042 }
1043 lsp::HoverContents::Markup(markup_content) => {
1044 let mut contents = Vec::new();
1045 let mut language = None;
1046 let mut current_text = String::new();
1047 for event in Parser::new_ext(&markup_content.value, Options::all()) {
1048 match event {
1049 Event::SoftBreak => {
1050 current_text.push(' ');
1051 }
1052 Event::Text(text) | Event::Code(text) => {
1053 current_text.push_str(&text.to_string());
1054 }
1055 Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(new_language))) => {
1056 if !current_text.is_empty() {
1057 let text = std::mem::take(&mut current_text).trim().to_string();
1058 contents.push(HoverBlock { text, language });
1059 }
1060
1061 language = if new_language.is_empty() {
1062 None
1063 } else {
1064 Some(new_language.to_string())
1065 };
1066 }
1067 Event::End(Tag::CodeBlock(_))
1068 | Event::End(Tag::Paragraph)
1069 | Event::End(Tag::Heading(_, _, _))
1070 | Event::End(Tag::BlockQuote)
1071 | Event::HardBreak => {
1072 if !current_text.is_empty() {
1073 let text = std::mem::take(&mut current_text).trim().to_string();
1074 contents.push(HoverBlock { text, language });
1075 }
1076 language = None;
1077 }
1078 _ => {}
1079 }
1080 }
1081
1082 if !current_text.trim().is_empty() {
1083 contents.push(HoverBlock {
1084 text: current_text,
1085 language,
1086 });
1087 }
1088
1089 if contents.is_empty() {
1090 None
1091 } else {
1092 Some(contents)
1093 }
1094 }
1095 });
1096
1097 contents.map(|contents| Hover { contents, range })
1098 }))
1099 }
1100
1101 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
1102 proto::GetHover {
1103 project_id,
1104 buffer_id: buffer.remote_id(),
1105 position: Some(language::proto::serialize_anchor(
1106 &buffer.clamped_anchor_before(self.position),
1107 )),
1108 version: serialize_version(&buffer.version),
1109 }
1110 }
1111
1112 async fn from_proto(
1113 message: Self::ProtoRequest,
1114 _: ModelHandle<Project>,
1115 buffer: ModelHandle<Buffer>,
1116 mut cx: AsyncAppContext,
1117 ) -> Result<Self> {
1118 let position = message
1119 .position
1120 .and_then(deserialize_anchor)
1121 .ok_or_else(|| anyhow!("invalid position"))?;
1122 buffer
1123 .update(&mut cx, |buffer, _| {
1124 buffer.wait_for_version(deserialize_version(message.version))
1125 })
1126 .await;
1127 Ok(Self {
1128 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
1129 })
1130 }
1131
1132 fn response_to_proto(
1133 response: Self::Response,
1134 _: &mut Project,
1135 _: PeerId,
1136 _: &clock::Global,
1137 _: &AppContext,
1138 ) -> proto::GetHoverResponse {
1139 if let Some(response) = response {
1140 let (start, end) = if let Some(range) = response.range {
1141 (
1142 Some(language::proto::serialize_anchor(&range.start)),
1143 Some(language::proto::serialize_anchor(&range.end)),
1144 )
1145 } else {
1146 (None, None)
1147 };
1148
1149 let contents = response
1150 .contents
1151 .into_iter()
1152 .map(|block| proto::HoverBlock {
1153 text: block.text,
1154 language: block.language,
1155 })
1156 .collect();
1157
1158 proto::GetHoverResponse {
1159 start,
1160 end,
1161 contents,
1162 }
1163 } else {
1164 proto::GetHoverResponse {
1165 start: None,
1166 end: None,
1167 contents: Vec::new(),
1168 }
1169 }
1170 }
1171
1172 async fn response_from_proto(
1173 self,
1174 message: proto::GetHoverResponse,
1175 _: ModelHandle<Project>,
1176 _: ModelHandle<Buffer>,
1177 _: AsyncAppContext,
1178 ) -> Result<Self::Response> {
1179 let range = if let (Some(start), Some(end)) = (message.start, message.end) {
1180 language::proto::deserialize_anchor(start)
1181 .and_then(|start| language::proto::deserialize_anchor(end).map(|end| start..end))
1182 } else {
1183 None
1184 };
1185
1186 let contents: Vec<_> = message
1187 .contents
1188 .into_iter()
1189 .map(|block| HoverBlock {
1190 text: block.text,
1191 language: block.language,
1192 })
1193 .collect();
1194
1195 Ok(if contents.is_empty() {
1196 None
1197 } else {
1198 Some(Hover { contents, range })
1199 })
1200 }
1201
1202 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64 {
1203 message.buffer_id
1204 }
1205}