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