1use crate::{
2 DocumentHighlight, Hover, HoverBlock, Location, LocationLink, Project, ProjectTransaction,
3};
4use anyhow::{anyhow, Result};
5use async_trait::async_trait;
6use client::proto::{self, 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.0
132 && buffer.clip_point_utf16(end, Bias::Left) == end.0
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 = project
526 .update(&mut cx, |this, cx| {
527 this.wait_for_buffer(origin.buffer_id, cx)
528 })
529 .await?;
530 let start = origin
531 .start
532 .and_then(deserialize_anchor)
533 .ok_or_else(|| anyhow!("missing origin start"))?;
534 let end = origin
535 .end
536 .and_then(deserialize_anchor)
537 .ok_or_else(|| anyhow!("missing origin end"))?;
538 buffer
539 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
540 .await;
541 Some(Location {
542 buffer,
543 range: start..end,
544 })
545 }
546 None => None,
547 };
548
549 let target = link.target.ok_or_else(|| anyhow!("missing target"))?;
550 let buffer = project
551 .update(&mut cx, |this, cx| {
552 this.wait_for_buffer(target.buffer_id, cx)
553 })
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_id = project.create_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_id,
672 }
673 });
674
675 let buffer_id = project.create_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_id,
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_id = project.create_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_id,
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 target_buffer = project
816 .update(&mut cx, |this, cx| {
817 this.wait_for_buffer(location.buffer_id, cx)
818 })
819 .await?;
820 let start = location
821 .start
822 .and_then(deserialize_anchor)
823 .ok_or_else(|| anyhow!("missing target start"))?;
824 let end = location
825 .end
826 .and_then(deserialize_anchor)
827 .ok_or_else(|| anyhow!("missing target end"))?;
828 target_buffer
829 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
830 .await;
831 locations.push(Location {
832 buffer: target_buffer,
833 range: start..end,
834 })
835 }
836 Ok(locations)
837 }
838
839 fn buffer_id_from_proto(message: &proto::GetReferences) -> u64 {
840 message.buffer_id
841 }
842}
843
844#[async_trait(?Send)]
845impl LspCommand for GetDocumentHighlights {
846 type Response = Vec<DocumentHighlight>;
847 type LspRequest = lsp::request::DocumentHighlightRequest;
848 type ProtoRequest = proto::GetDocumentHighlights;
849
850 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
851 capabilities.document_highlight_provider.is_some()
852 }
853
854 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::DocumentHighlightParams {
855 lsp::DocumentHighlightParams {
856 text_document_position_params: lsp::TextDocumentPositionParams {
857 text_document: lsp::TextDocumentIdentifier {
858 uri: lsp::Url::from_file_path(path).unwrap(),
859 },
860 position: point_to_lsp(self.position),
861 },
862 work_done_progress_params: Default::default(),
863 partial_result_params: Default::default(),
864 }
865 }
866
867 async fn response_from_lsp(
868 self,
869 lsp_highlights: Option<Vec<lsp::DocumentHighlight>>,
870 _: ModelHandle<Project>,
871 buffer: ModelHandle<Buffer>,
872 cx: AsyncAppContext,
873 ) -> Result<Vec<DocumentHighlight>> {
874 buffer.read_with(&cx, |buffer, _| {
875 let mut lsp_highlights = lsp_highlights.unwrap_or_default();
876 lsp_highlights.sort_unstable_by_key(|h| (h.range.start, Reverse(h.range.end)));
877 Ok(lsp_highlights
878 .into_iter()
879 .map(|lsp_highlight| {
880 let start = buffer
881 .clip_point_utf16(point_from_lsp(lsp_highlight.range.start), Bias::Left);
882 let end = buffer
883 .clip_point_utf16(point_from_lsp(lsp_highlight.range.end), Bias::Left);
884 DocumentHighlight {
885 range: buffer.anchor_after(start)..buffer.anchor_before(end),
886 kind: lsp_highlight
887 .kind
888 .unwrap_or(lsp::DocumentHighlightKind::READ),
889 }
890 })
891 .collect())
892 })
893 }
894
895 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentHighlights {
896 proto::GetDocumentHighlights {
897 project_id,
898 buffer_id: buffer.remote_id(),
899 position: Some(language::proto::serialize_anchor(
900 &buffer.anchor_before(self.position),
901 )),
902 version: serialize_version(&buffer.version()),
903 }
904 }
905
906 async fn from_proto(
907 message: proto::GetDocumentHighlights,
908 _: ModelHandle<Project>,
909 buffer: ModelHandle<Buffer>,
910 mut cx: AsyncAppContext,
911 ) -> Result<Self> {
912 let position = message
913 .position
914 .and_then(deserialize_anchor)
915 .ok_or_else(|| anyhow!("invalid position"))?;
916 buffer
917 .update(&mut cx, |buffer, _| {
918 buffer.wait_for_version(deserialize_version(message.version))
919 })
920 .await;
921 Ok(Self {
922 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
923 })
924 }
925
926 fn response_to_proto(
927 response: Vec<DocumentHighlight>,
928 _: &mut Project,
929 _: PeerId,
930 _: &clock::Global,
931 _: &AppContext,
932 ) -> proto::GetDocumentHighlightsResponse {
933 let highlights = response
934 .into_iter()
935 .map(|highlight| proto::DocumentHighlight {
936 start: Some(serialize_anchor(&highlight.range.start)),
937 end: Some(serialize_anchor(&highlight.range.end)),
938 kind: match highlight.kind {
939 DocumentHighlightKind::TEXT => proto::document_highlight::Kind::Text.into(),
940 DocumentHighlightKind::WRITE => proto::document_highlight::Kind::Write.into(),
941 DocumentHighlightKind::READ => proto::document_highlight::Kind::Read.into(),
942 _ => proto::document_highlight::Kind::Text.into(),
943 },
944 })
945 .collect();
946 proto::GetDocumentHighlightsResponse { highlights }
947 }
948
949 async fn response_from_proto(
950 self,
951 message: proto::GetDocumentHighlightsResponse,
952 _: ModelHandle<Project>,
953 buffer: ModelHandle<Buffer>,
954 mut cx: AsyncAppContext,
955 ) -> Result<Vec<DocumentHighlight>> {
956 let mut highlights = Vec::new();
957 for highlight in message.highlights {
958 let start = highlight
959 .start
960 .and_then(deserialize_anchor)
961 .ok_or_else(|| anyhow!("missing target start"))?;
962 let end = highlight
963 .end
964 .and_then(deserialize_anchor)
965 .ok_or_else(|| anyhow!("missing target end"))?;
966 buffer
967 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([&start, &end]))
968 .await;
969 let kind = match proto::document_highlight::Kind::from_i32(highlight.kind) {
970 Some(proto::document_highlight::Kind::Text) => DocumentHighlightKind::TEXT,
971 Some(proto::document_highlight::Kind::Read) => DocumentHighlightKind::READ,
972 Some(proto::document_highlight::Kind::Write) => DocumentHighlightKind::WRITE,
973 None => DocumentHighlightKind::TEXT,
974 };
975 highlights.push(DocumentHighlight {
976 range: start..end,
977 kind,
978 });
979 }
980 Ok(highlights)
981 }
982
983 fn buffer_id_from_proto(message: &proto::GetDocumentHighlights) -> u64 {
984 message.buffer_id
985 }
986}
987
988#[async_trait(?Send)]
989impl LspCommand for GetHover {
990 type Response = Option<Hover>;
991 type LspRequest = lsp::request::HoverRequest;
992 type ProtoRequest = proto::GetHover;
993
994 fn to_lsp(&self, path: &Path, _: &AppContext) -> lsp::HoverParams {
995 lsp::HoverParams {
996 text_document_position_params: lsp::TextDocumentPositionParams {
997 text_document: lsp::TextDocumentIdentifier {
998 uri: lsp::Url::from_file_path(path).unwrap(),
999 },
1000 position: point_to_lsp(self.position),
1001 },
1002 work_done_progress_params: Default::default(),
1003 }
1004 }
1005
1006 async fn response_from_lsp(
1007 self,
1008 message: Option<lsp::Hover>,
1009 _: ModelHandle<Project>,
1010 buffer: ModelHandle<Buffer>,
1011 cx: AsyncAppContext,
1012 ) -> Result<Self::Response> {
1013 Ok(message.and_then(|hover| {
1014 let range = hover.range.map(|range| {
1015 cx.read(|cx| {
1016 let buffer = buffer.read(cx);
1017 let token_start =
1018 buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left);
1019 let token_end = buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left);
1020 buffer.anchor_after(token_start)..buffer.anchor_before(token_end)
1021 })
1022 });
1023
1024 let contents = cx.read(|_| match hover.contents {
1025 lsp::HoverContents::Scalar(marked_string) => {
1026 HoverBlock::try_new(marked_string).map(|contents| vec![contents])
1027 }
1028 lsp::HoverContents::Array(marked_strings) => {
1029 let content: Vec<HoverBlock> = marked_strings
1030 .into_iter()
1031 .filter_map(HoverBlock::try_new)
1032 .collect();
1033 if content.is_empty() {
1034 None
1035 } else {
1036 Some(content)
1037 }
1038 }
1039 lsp::HoverContents::Markup(markup_content) => {
1040 let mut contents = Vec::new();
1041 let mut language = None;
1042 let mut current_text = String::new();
1043 for event in Parser::new_ext(&markup_content.value, Options::all()) {
1044 match event {
1045 Event::SoftBreak => {
1046 current_text.push(' ');
1047 }
1048 Event::Text(text) | Event::Code(text) => {
1049 current_text.push_str(&text.to_string());
1050 }
1051 Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(new_language))) => {
1052 if !current_text.is_empty() {
1053 let text = std::mem::take(&mut current_text).trim().to_string();
1054 contents.push(HoverBlock { text, language });
1055 }
1056
1057 language = if new_language.is_empty() {
1058 None
1059 } else {
1060 Some(new_language.to_string())
1061 };
1062 }
1063 Event::End(Tag::CodeBlock(_))
1064 | Event::End(Tag::Paragraph)
1065 | Event::End(Tag::Heading(_, _, _))
1066 | Event::End(Tag::BlockQuote)
1067 | Event::HardBreak => {
1068 if !current_text.is_empty() {
1069 let text = std::mem::take(&mut current_text).trim().to_string();
1070 contents.push(HoverBlock { text, language });
1071 }
1072 language = None;
1073 }
1074 _ => {}
1075 }
1076 }
1077
1078 if !current_text.trim().is_empty() {
1079 contents.push(HoverBlock {
1080 text: current_text,
1081 language,
1082 });
1083 }
1084
1085 if contents.is_empty() {
1086 None
1087 } else {
1088 Some(contents)
1089 }
1090 }
1091 });
1092
1093 contents.map(|contents| Hover { contents, range })
1094 }))
1095 }
1096
1097 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
1098 proto::GetHover {
1099 project_id,
1100 buffer_id: buffer.remote_id(),
1101 position: Some(language::proto::serialize_anchor(
1102 &buffer.anchor_before(self.position),
1103 )),
1104 version: serialize_version(&buffer.version),
1105 }
1106 }
1107
1108 async fn from_proto(
1109 message: Self::ProtoRequest,
1110 _: ModelHandle<Project>,
1111 buffer: ModelHandle<Buffer>,
1112 mut cx: AsyncAppContext,
1113 ) -> Result<Self> {
1114 let position = message
1115 .position
1116 .and_then(deserialize_anchor)
1117 .ok_or_else(|| anyhow!("invalid position"))?;
1118 buffer
1119 .update(&mut cx, |buffer, _| {
1120 buffer.wait_for_version(deserialize_version(message.version))
1121 })
1122 .await;
1123 Ok(Self {
1124 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
1125 })
1126 }
1127
1128 fn response_to_proto(
1129 response: Self::Response,
1130 _: &mut Project,
1131 _: PeerId,
1132 _: &clock::Global,
1133 _: &AppContext,
1134 ) -> proto::GetHoverResponse {
1135 if let Some(response) = response {
1136 let (start, end) = if let Some(range) = response.range {
1137 (
1138 Some(language::proto::serialize_anchor(&range.start)),
1139 Some(language::proto::serialize_anchor(&range.end)),
1140 )
1141 } else {
1142 (None, None)
1143 };
1144
1145 let contents = response
1146 .contents
1147 .into_iter()
1148 .map(|block| proto::HoverBlock {
1149 text: block.text,
1150 language: block.language,
1151 })
1152 .collect();
1153
1154 proto::GetHoverResponse {
1155 start,
1156 end,
1157 contents,
1158 }
1159 } else {
1160 proto::GetHoverResponse {
1161 start: None,
1162 end: None,
1163 contents: Vec::new(),
1164 }
1165 }
1166 }
1167
1168 async fn response_from_proto(
1169 self,
1170 message: proto::GetHoverResponse,
1171 _: ModelHandle<Project>,
1172 _: ModelHandle<Buffer>,
1173 _: AsyncAppContext,
1174 ) -> Result<Self::Response> {
1175 let range = if let (Some(start), Some(end)) = (message.start, message.end) {
1176 language::proto::deserialize_anchor(start)
1177 .and_then(|start| language::proto::deserialize_anchor(end).map(|end| start..end))
1178 } else {
1179 None
1180 };
1181
1182 let contents: Vec<_> = message
1183 .contents
1184 .into_iter()
1185 .map(|block| HoverBlock {
1186 text: block.text,
1187 language: block.language,
1188 })
1189 .collect();
1190
1191 Ok(if contents.is_empty() {
1192 None
1193 } else {
1194 Some(Hover { contents, range })
1195 })
1196 }
1197
1198 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64 {
1199 message.buffer_id
1200 }
1201}