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