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