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