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