1use crate::{DocumentHighlight, Hover, HoverContents, 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, LanguageString, MarkedString, 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 project: 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 fn text_and_language(marked_string: MarkedString) -> (String, Option<String>) {
840 match marked_string {
841 MarkedString::LanguageString(LanguageString { language, value }) => {
842 (value, Some(language))
843 }
844 MarkedString::String(text) => (text, None),
845 }
846 }
847
848 fn highlight(
849 text: String,
850 language: Option<String>,
851 project: &Project,
852 ) -> Option<HoverContents> {
853 let text = text.trim();
854 if text.is_empty() {
855 return None;
856 }
857
858 if let Some(language) =
859 language.and_then(|language| project.languages().get_language(&language))
860 {
861 let runs = language.highlight_text(&text.into(), 0..text.len());
862 Some(HoverContents {
863 text: text.to_string(),
864 runs,
865 })
866 } else {
867 Some(HoverContents {
868 text: text.to_string(),
869 runs: Vec::new(),
870 })
871 }
872 }
873
874 let contents = cx.read(|cx| {
875 let project = project.read(cx);
876 match hover.contents {
877 lsp::HoverContents::Scalar(marked_string) => {
878 let (text, language) = text_and_language(marked_string);
879 highlight(text, language, project).map(|content| vec![content])
880 }
881 lsp::HoverContents::Array(marked_strings) => {
882 let content: Vec<HoverContents> = marked_strings
883 .into_iter()
884 .filter_map(|marked_string| {
885 let (text, language) = text_and_language(marked_string);
886 highlight(text, language, project)
887 })
888 .collect();
889 if content.is_empty() {
890 None
891 } else {
892 Some(content)
893 }
894 }
895 lsp::HoverContents::Markup(markup_content) => {
896 let mut contents = Vec::new();
897 let mut language = None;
898 let mut current_text = String::new();
899 for event in Parser::new_ext(&markup_content.value, Options::all()) {
900 match event {
901 Event::Text(text) | Event::Code(text) => {
902 current_text.push_str(&text.to_string());
903 }
904 Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(
905 new_language,
906 ))) => {
907 if let Some(content) =
908 highlight(current_text.clone(), language, project)
909 {
910 contents.push(content);
911 current_text.clear();
912 }
913
914 language = if new_language.is_empty() {
915 None
916 } else {
917 Some(new_language.to_string())
918 };
919 }
920 Event::End(Tag::CodeBlock(_)) => {
921 if let Some(content) =
922 highlight(current_text.clone(), language.clone(), project)
923 {
924 contents.push(content);
925 current_text.clear();
926 language = None;
927 }
928 }
929 _ => {}
930 }
931 }
932
933 if let Some(content) =
934 highlight(current_text.clone(), language.clone(), project)
935 {
936 contents.push(content);
937 }
938
939 if contents.is_empty() {
940 None
941 } else {
942 Some(contents)
943 }
944 }
945 }
946 });
947
948 contents.map(|contents| Hover { contents, range })
949 }))
950 }
951
952 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
953 proto::GetHover {
954 project_id,
955 buffer_id: buffer.remote_id(),
956 position: Some(language::proto::serialize_anchor(
957 &buffer.anchor_before(self.position),
958 )),
959 version: serialize_version(&buffer.version),
960 }
961 }
962
963 async fn from_proto(
964 message: Self::ProtoRequest,
965 _: ModelHandle<Project>,
966 buffer: ModelHandle<Buffer>,
967 mut cx: AsyncAppContext,
968 ) -> Result<Self> {
969 let position = message
970 .position
971 .and_then(deserialize_anchor)
972 .ok_or_else(|| anyhow!("invalid position"))?;
973 buffer
974 .update(&mut cx, |buffer, _| {
975 buffer.wait_for_version(deserialize_version(message.version))
976 })
977 .await;
978 Ok(Self {
979 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
980 })
981 }
982
983 fn response_to_proto(
984 response: Self::Response,
985 project: &mut Project,
986 peer_id: PeerId,
987 buffer_version: &clock::Global,
988 cx: &AppContext,
989 ) -> proto::GetHoverResponse {
990 todo!()
991 }
992
993 async fn response_from_proto(
994 self,
995 message: <Self::ProtoRequest as proto::RequestMessage>::Response,
996 project: ModelHandle<Project>,
997 buffer: ModelHandle<Buffer>,
998 cx: AsyncAppContext,
999 ) -> Result<Self::Response> {
1000 todo!()
1001 }
1002
1003 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64 {
1004 message.buffer_id
1005 }
1006}