1use crate::{
2 DocumentHighlight, Hover, HoverBlock, HoverBlockKind, InlayHint, InlayHintLabel,
3 InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Item, Location, LocationLink,
4 MarkupContent, Project, ProjectTransaction, ResolveState,
5};
6use anyhow::{anyhow, Context, Result};
7use async_trait::async_trait;
8use client::proto::{self, PeerId};
9use fs::LineEnding;
10use gpui::{AppContext, AsyncAppContext, ModelHandle};
11use language::{
12 language_settings::{language_settings, InlayHintKind},
13 point_from_lsp, point_to_lsp,
14 proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version},
15 range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CharKind,
16 CodeAction, Completion, OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Transaction,
17 Unclipped,
18};
19use lsp::{DocumentHighlightKind, LanguageServer, LanguageServerId, ServerCapabilities};
20use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc};
21
22pub fn lsp_formatting_options(tab_size: u32) -> lsp::FormattingOptions {
23 lsp::FormattingOptions {
24 tab_size,
25 insert_spaces: true,
26 insert_final_newline: Some(true),
27 ..lsp::FormattingOptions::default()
28 }
29}
30
31#[async_trait(?Send)]
32pub(crate) trait LspCommand: 'static + Sized {
33 type Response: 'static + Default + Send;
34 type LspRequest: 'static + Send + lsp::request::Request;
35 type ProtoRequest: 'static + Send + proto::RequestMessage;
36
37 fn check_capabilities(&self, _: &lsp::ServerCapabilities) -> bool {
38 true
39 }
40
41 fn to_lsp(
42 &self,
43 path: &Path,
44 buffer: &Buffer,
45 language_server: &Arc<LanguageServer>,
46 cx: &AppContext,
47 ) -> <Self::LspRequest as lsp::request::Request>::Params;
48
49 async fn response_from_lsp(
50 self,
51 message: <Self::LspRequest as lsp::request::Request>::Result,
52 project: ModelHandle<Project>,
53 buffer: ModelHandle<Buffer>,
54 server_id: LanguageServerId,
55 cx: AsyncAppContext,
56 ) -> Result<Self::Response>;
57
58 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest;
59
60 async fn from_proto(
61 message: Self::ProtoRequest,
62 project: ModelHandle<Project>,
63 buffer: ModelHandle<Buffer>,
64 cx: AsyncAppContext,
65 ) -> Result<Self>;
66
67 fn response_to_proto(
68 response: Self::Response,
69 project: &mut Project,
70 peer_id: PeerId,
71 buffer_version: &clock::Global,
72 cx: &mut AppContext,
73 ) -> <Self::ProtoRequest as proto::RequestMessage>::Response;
74
75 async fn response_from_proto(
76 self,
77 message: <Self::ProtoRequest as proto::RequestMessage>::Response,
78 project: ModelHandle<Project>,
79 buffer: ModelHandle<Buffer>,
80 cx: AsyncAppContext,
81 ) -> Result<Self::Response>;
82
83 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64;
84}
85
86pub(crate) struct PrepareRename {
87 pub position: PointUtf16,
88}
89
90pub(crate) struct PerformRename {
91 pub position: PointUtf16,
92 pub new_name: String,
93 pub push_to_history: bool,
94}
95
96pub(crate) struct GetDefinition {
97 pub position: PointUtf16,
98}
99
100pub(crate) struct GetTypeDefinition {
101 pub position: PointUtf16,
102}
103
104pub(crate) struct GetReferences {
105 pub position: PointUtf16,
106}
107
108pub(crate) struct GetDocumentHighlights {
109 pub position: PointUtf16,
110}
111
112pub(crate) struct GetHover {
113 pub position: PointUtf16,
114}
115
116pub(crate) struct GetCompletions {
117 pub position: PointUtf16,
118}
119
120pub(crate) struct GetCodeActions {
121 pub range: Range<Anchor>,
122}
123
124pub(crate) struct OnTypeFormatting {
125 pub position: PointUtf16,
126 pub trigger: String,
127 pub options: FormattingOptions,
128 pub push_to_history: bool,
129}
130
131pub(crate) struct InlayHints {
132 pub range: Range<Anchor>,
133}
134
135pub(crate) struct FormattingOptions {
136 tab_size: u32,
137}
138
139impl From<lsp::FormattingOptions> for FormattingOptions {
140 fn from(value: lsp::FormattingOptions) -> Self {
141 Self {
142 tab_size: value.tab_size,
143 }
144 }
145}
146
147#[async_trait(?Send)]
148impl LspCommand for PrepareRename {
149 type Response = Option<Range<Anchor>>;
150 type LspRequest = lsp::request::PrepareRenameRequest;
151 type ProtoRequest = proto::PrepareRename;
152
153 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
154 if let Some(lsp::OneOf::Right(rename)) = &capabilities.rename_provider {
155 rename.prepare_provider == Some(true)
156 } else {
157 false
158 }
159 }
160
161 fn to_lsp(
162 &self,
163 path: &Path,
164 _: &Buffer,
165 _: &Arc<LanguageServer>,
166 _: &AppContext,
167 ) -> lsp::TextDocumentPositionParams {
168 lsp::TextDocumentPositionParams {
169 text_document: lsp::TextDocumentIdentifier {
170 uri: lsp::Url::from_file_path(path).unwrap(),
171 },
172 position: point_to_lsp(self.position),
173 }
174 }
175
176 async fn response_from_lsp(
177 self,
178 message: Option<lsp::PrepareRenameResponse>,
179 _: ModelHandle<Project>,
180 buffer: ModelHandle<Buffer>,
181 _: LanguageServerId,
182 cx: AsyncAppContext,
183 ) -> Result<Option<Range<Anchor>>> {
184 buffer.read_with(&cx, |buffer, _| {
185 if let Some(
186 lsp::PrepareRenameResponse::Range(range)
187 | lsp::PrepareRenameResponse::RangeWithPlaceholder { range, .. },
188 ) = message
189 {
190 let Range { start, end } = range_from_lsp(range);
191 if buffer.clip_point_utf16(start, Bias::Left) == start.0
192 && buffer.clip_point_utf16(end, Bias::Left) == end.0
193 {
194 return Ok(Some(buffer.anchor_after(start)..buffer.anchor_before(end)));
195 }
196 }
197 Ok(None)
198 })
199 }
200
201 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PrepareRename {
202 proto::PrepareRename {
203 project_id,
204 buffer_id: buffer.remote_id(),
205 position: Some(language::proto::serialize_anchor(
206 &buffer.anchor_before(self.position),
207 )),
208 version: serialize_version(&buffer.version()),
209 }
210 }
211
212 async fn from_proto(
213 message: proto::PrepareRename,
214 _: ModelHandle<Project>,
215 buffer: ModelHandle<Buffer>,
216 mut cx: AsyncAppContext,
217 ) -> Result<Self> {
218 let position = message
219 .position
220 .and_then(deserialize_anchor)
221 .ok_or_else(|| anyhow!("invalid position"))?;
222 buffer
223 .update(&mut cx, |buffer, _| {
224 buffer.wait_for_version(deserialize_version(&message.version))
225 })
226 .await?;
227
228 Ok(Self {
229 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
230 })
231 }
232
233 fn response_to_proto(
234 range: Option<Range<Anchor>>,
235 _: &mut Project,
236 _: PeerId,
237 buffer_version: &clock::Global,
238 _: &mut AppContext,
239 ) -> proto::PrepareRenameResponse {
240 proto::PrepareRenameResponse {
241 can_rename: range.is_some(),
242 start: range
243 .as_ref()
244 .map(|range| language::proto::serialize_anchor(&range.start)),
245 end: range
246 .as_ref()
247 .map(|range| language::proto::serialize_anchor(&range.end)),
248 version: serialize_version(buffer_version),
249 }
250 }
251
252 async fn response_from_proto(
253 self,
254 message: proto::PrepareRenameResponse,
255 _: ModelHandle<Project>,
256 buffer: ModelHandle<Buffer>,
257 mut cx: AsyncAppContext,
258 ) -> Result<Option<Range<Anchor>>> {
259 if message.can_rename {
260 buffer
261 .update(&mut cx, |buffer, _| {
262 buffer.wait_for_version(deserialize_version(&message.version))
263 })
264 .await?;
265 let start = message.start.and_then(deserialize_anchor);
266 let end = message.end.and_then(deserialize_anchor);
267 Ok(start.zip(end).map(|(start, end)| start..end))
268 } else {
269 Ok(None)
270 }
271 }
272
273 fn buffer_id_from_proto(message: &proto::PrepareRename) -> u64 {
274 message.buffer_id
275 }
276}
277
278#[async_trait(?Send)]
279impl LspCommand for PerformRename {
280 type Response = ProjectTransaction;
281 type LspRequest = lsp::request::Rename;
282 type ProtoRequest = proto::PerformRename;
283
284 fn to_lsp(
285 &self,
286 path: &Path,
287 _: &Buffer,
288 _: &Arc<LanguageServer>,
289 _: &AppContext,
290 ) -> lsp::RenameParams {
291 lsp::RenameParams {
292 text_document_position: lsp::TextDocumentPositionParams {
293 text_document: lsp::TextDocumentIdentifier {
294 uri: lsp::Url::from_file_path(path).unwrap(),
295 },
296 position: point_to_lsp(self.position),
297 },
298 new_name: self.new_name.clone(),
299 work_done_progress_params: Default::default(),
300 }
301 }
302
303 async fn response_from_lsp(
304 self,
305 message: Option<lsp::WorkspaceEdit>,
306 project: ModelHandle<Project>,
307 buffer: ModelHandle<Buffer>,
308 server_id: LanguageServerId,
309 mut cx: AsyncAppContext,
310 ) -> Result<ProjectTransaction> {
311 if let Some(edit) = message {
312 let (lsp_adapter, lsp_server) =
313 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
314 Project::deserialize_workspace_edit(
315 project,
316 edit,
317 self.push_to_history,
318 lsp_adapter,
319 lsp_server,
320 &mut cx,
321 )
322 .await
323 } else {
324 Ok(ProjectTransaction::default())
325 }
326 }
327
328 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PerformRename {
329 proto::PerformRename {
330 project_id,
331 buffer_id: buffer.remote_id(),
332 position: Some(language::proto::serialize_anchor(
333 &buffer.anchor_before(self.position),
334 )),
335 new_name: self.new_name.clone(),
336 version: serialize_version(&buffer.version()),
337 }
338 }
339
340 async fn from_proto(
341 message: proto::PerformRename,
342 _: ModelHandle<Project>,
343 buffer: ModelHandle<Buffer>,
344 mut cx: AsyncAppContext,
345 ) -> Result<Self> {
346 let position = message
347 .position
348 .and_then(deserialize_anchor)
349 .ok_or_else(|| anyhow!("invalid position"))?;
350 buffer
351 .update(&mut cx, |buffer, _| {
352 buffer.wait_for_version(deserialize_version(&message.version))
353 })
354 .await?;
355 Ok(Self {
356 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
357 new_name: message.new_name,
358 push_to_history: false,
359 })
360 }
361
362 fn response_to_proto(
363 response: ProjectTransaction,
364 project: &mut Project,
365 peer_id: PeerId,
366 _: &clock::Global,
367 cx: &mut AppContext,
368 ) -> proto::PerformRenameResponse {
369 let transaction = project.serialize_project_transaction_for_peer(response, peer_id, cx);
370 proto::PerformRenameResponse {
371 transaction: Some(transaction),
372 }
373 }
374
375 async fn response_from_proto(
376 self,
377 message: proto::PerformRenameResponse,
378 project: ModelHandle<Project>,
379 _: ModelHandle<Buffer>,
380 mut cx: AsyncAppContext,
381 ) -> Result<ProjectTransaction> {
382 let message = message
383 .transaction
384 .ok_or_else(|| anyhow!("missing transaction"))?;
385 project
386 .update(&mut cx, |project, cx| {
387 project.deserialize_project_transaction(message, self.push_to_history, cx)
388 })
389 .await
390 }
391
392 fn buffer_id_from_proto(message: &proto::PerformRename) -> u64 {
393 message.buffer_id
394 }
395}
396
397#[async_trait(?Send)]
398impl LspCommand for GetDefinition {
399 type Response = Vec<LocationLink>;
400 type LspRequest = lsp::request::GotoDefinition;
401 type ProtoRequest = proto::GetDefinition;
402
403 fn to_lsp(
404 &self,
405 path: &Path,
406 _: &Buffer,
407 _: &Arc<LanguageServer>,
408 _: &AppContext,
409 ) -> lsp::GotoDefinitionParams {
410 lsp::GotoDefinitionParams {
411 text_document_position_params: lsp::TextDocumentPositionParams {
412 text_document: lsp::TextDocumentIdentifier {
413 uri: lsp::Url::from_file_path(path).unwrap(),
414 },
415 position: point_to_lsp(self.position),
416 },
417 work_done_progress_params: Default::default(),
418 partial_result_params: Default::default(),
419 }
420 }
421
422 async fn response_from_lsp(
423 self,
424 message: Option<lsp::GotoDefinitionResponse>,
425 project: ModelHandle<Project>,
426 buffer: ModelHandle<Buffer>,
427 server_id: LanguageServerId,
428 cx: AsyncAppContext,
429 ) -> Result<Vec<LocationLink>> {
430 location_links_from_lsp(message, project, buffer, server_id, cx).await
431 }
432
433 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDefinition {
434 proto::GetDefinition {
435 project_id,
436 buffer_id: buffer.remote_id(),
437 position: Some(language::proto::serialize_anchor(
438 &buffer.anchor_before(self.position),
439 )),
440 version: serialize_version(&buffer.version()),
441 }
442 }
443
444 async fn from_proto(
445 message: proto::GetDefinition,
446 _: ModelHandle<Project>,
447 buffer: ModelHandle<Buffer>,
448 mut cx: AsyncAppContext,
449 ) -> Result<Self> {
450 let position = message
451 .position
452 .and_then(deserialize_anchor)
453 .ok_or_else(|| anyhow!("invalid position"))?;
454 buffer
455 .update(&mut cx, |buffer, _| {
456 buffer.wait_for_version(deserialize_version(&message.version))
457 })
458 .await?;
459 Ok(Self {
460 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
461 })
462 }
463
464 fn response_to_proto(
465 response: Vec<LocationLink>,
466 project: &mut Project,
467 peer_id: PeerId,
468 _: &clock::Global,
469 cx: &mut AppContext,
470 ) -> proto::GetDefinitionResponse {
471 let links = location_links_to_proto(response, project, peer_id, cx);
472 proto::GetDefinitionResponse { links }
473 }
474
475 async fn response_from_proto(
476 self,
477 message: proto::GetDefinitionResponse,
478 project: ModelHandle<Project>,
479 _: ModelHandle<Buffer>,
480 cx: AsyncAppContext,
481 ) -> Result<Vec<LocationLink>> {
482 location_links_from_proto(message.links, project, cx).await
483 }
484
485 fn buffer_id_from_proto(message: &proto::GetDefinition) -> u64 {
486 message.buffer_id
487 }
488}
489
490#[async_trait(?Send)]
491impl LspCommand for GetTypeDefinition {
492 type Response = Vec<LocationLink>;
493 type LspRequest = lsp::request::GotoTypeDefinition;
494 type ProtoRequest = proto::GetTypeDefinition;
495
496 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
497 match &capabilities.type_definition_provider {
498 None => false,
499 Some(lsp::TypeDefinitionProviderCapability::Simple(false)) => false,
500 _ => true,
501 }
502 }
503
504 fn to_lsp(
505 &self,
506 path: &Path,
507 _: &Buffer,
508 _: &Arc<LanguageServer>,
509 _: &AppContext,
510 ) -> lsp::GotoTypeDefinitionParams {
511 lsp::GotoTypeDefinitionParams {
512 text_document_position_params: lsp::TextDocumentPositionParams {
513 text_document: lsp::TextDocumentIdentifier {
514 uri: lsp::Url::from_file_path(path).unwrap(),
515 },
516 position: point_to_lsp(self.position),
517 },
518 work_done_progress_params: Default::default(),
519 partial_result_params: Default::default(),
520 }
521 }
522
523 async fn response_from_lsp(
524 self,
525 message: Option<lsp::GotoTypeDefinitionResponse>,
526 project: ModelHandle<Project>,
527 buffer: ModelHandle<Buffer>,
528 server_id: LanguageServerId,
529 cx: AsyncAppContext,
530 ) -> Result<Vec<LocationLink>> {
531 location_links_from_lsp(message, project, buffer, server_id, cx).await
532 }
533
534 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetTypeDefinition {
535 proto::GetTypeDefinition {
536 project_id,
537 buffer_id: buffer.remote_id(),
538 position: Some(language::proto::serialize_anchor(
539 &buffer.anchor_before(self.position),
540 )),
541 version: serialize_version(&buffer.version()),
542 }
543 }
544
545 async fn from_proto(
546 message: proto::GetTypeDefinition,
547 _: ModelHandle<Project>,
548 buffer: ModelHandle<Buffer>,
549 mut cx: AsyncAppContext,
550 ) -> Result<Self> {
551 let position = message
552 .position
553 .and_then(deserialize_anchor)
554 .ok_or_else(|| anyhow!("invalid position"))?;
555 buffer
556 .update(&mut cx, |buffer, _| {
557 buffer.wait_for_version(deserialize_version(&message.version))
558 })
559 .await?;
560 Ok(Self {
561 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
562 })
563 }
564
565 fn response_to_proto(
566 response: Vec<LocationLink>,
567 project: &mut Project,
568 peer_id: PeerId,
569 _: &clock::Global,
570 cx: &mut AppContext,
571 ) -> proto::GetTypeDefinitionResponse {
572 let links = location_links_to_proto(response, project, peer_id, cx);
573 proto::GetTypeDefinitionResponse { links }
574 }
575
576 async fn response_from_proto(
577 self,
578 message: proto::GetTypeDefinitionResponse,
579 project: ModelHandle<Project>,
580 _: ModelHandle<Buffer>,
581 cx: AsyncAppContext,
582 ) -> Result<Vec<LocationLink>> {
583 location_links_from_proto(message.links, project, cx).await
584 }
585
586 fn buffer_id_from_proto(message: &proto::GetTypeDefinition) -> u64 {
587 message.buffer_id
588 }
589}
590
591fn language_server_for_buffer(
592 project: &ModelHandle<Project>,
593 buffer: &ModelHandle<Buffer>,
594 server_id: LanguageServerId,
595 cx: &mut AsyncAppContext,
596) -> Result<(Arc<CachedLspAdapter>, Arc<LanguageServer>)> {
597 project
598 .read_with(cx, |project, cx| {
599 project
600 .language_server_for_buffer(buffer.read(cx), server_id, cx)
601 .map(|(adapter, server)| (adapter.clone(), server.clone()))
602 })
603 .ok_or_else(|| anyhow!("no language server found for buffer"))
604}
605
606async fn location_links_from_proto(
607 proto_links: Vec<proto::LocationLink>,
608 project: ModelHandle<Project>,
609 mut cx: AsyncAppContext,
610) -> Result<Vec<LocationLink>> {
611 let mut links = Vec::new();
612
613 for link in proto_links {
614 let origin = match link.origin {
615 Some(origin) => {
616 let buffer = project
617 .update(&mut cx, |this, cx| {
618 this.wait_for_remote_buffer(origin.buffer_id, cx)
619 })
620 .await?;
621 let start = origin
622 .start
623 .and_then(deserialize_anchor)
624 .ok_or_else(|| anyhow!("missing origin start"))?;
625 let end = origin
626 .end
627 .and_then(deserialize_anchor)
628 .ok_or_else(|| anyhow!("missing origin end"))?;
629 buffer
630 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))
631 .await?;
632 Some(Location {
633 buffer,
634 range: start..end,
635 })
636 }
637 None => None,
638 };
639
640 let target = link.target.ok_or_else(|| anyhow!("missing target"))?;
641 let buffer = project
642 .update(&mut cx, |this, cx| {
643 this.wait_for_remote_buffer(target.buffer_id, cx)
644 })
645 .await?;
646 let start = target
647 .start
648 .and_then(deserialize_anchor)
649 .ok_or_else(|| anyhow!("missing target start"))?;
650 let end = target
651 .end
652 .and_then(deserialize_anchor)
653 .ok_or_else(|| anyhow!("missing target end"))?;
654 buffer
655 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))
656 .await?;
657 let target = Location {
658 buffer,
659 range: start..end,
660 };
661
662 links.push(LocationLink { origin, target })
663 }
664
665 Ok(links)
666}
667
668async fn location_links_from_lsp(
669 message: Option<lsp::GotoDefinitionResponse>,
670 project: ModelHandle<Project>,
671 buffer: ModelHandle<Buffer>,
672 server_id: LanguageServerId,
673 mut cx: AsyncAppContext,
674) -> Result<Vec<LocationLink>> {
675 let message = match message {
676 Some(message) => message,
677 None => return Ok(Vec::new()),
678 };
679
680 let mut unresolved_links = Vec::new();
681 match message {
682 lsp::GotoDefinitionResponse::Scalar(loc) => {
683 unresolved_links.push((None, loc.uri, loc.range));
684 }
685
686 lsp::GotoDefinitionResponse::Array(locs) => {
687 unresolved_links.extend(locs.into_iter().map(|l| (None, l.uri, l.range)));
688 }
689
690 lsp::GotoDefinitionResponse::Link(links) => {
691 unresolved_links.extend(links.into_iter().map(|l| {
692 (
693 l.origin_selection_range,
694 l.target_uri,
695 l.target_selection_range,
696 )
697 }));
698 }
699 }
700
701 let (lsp_adapter, language_server) =
702 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
703 let mut definitions = Vec::new();
704 for (origin_range, target_uri, target_range) in unresolved_links {
705 let target_buffer_handle = project
706 .update(&mut cx, |this, cx| {
707 this.open_local_buffer_via_lsp(
708 target_uri,
709 language_server.server_id(),
710 lsp_adapter.name.clone(),
711 cx,
712 )
713 })
714 .await?;
715
716 cx.read(|cx| {
717 let origin_location = origin_range.map(|origin_range| {
718 let origin_buffer = buffer.read(cx);
719 let origin_start =
720 origin_buffer.clip_point_utf16(point_from_lsp(origin_range.start), Bias::Left);
721 let origin_end =
722 origin_buffer.clip_point_utf16(point_from_lsp(origin_range.end), Bias::Left);
723 Location {
724 buffer: buffer.clone(),
725 range: origin_buffer.anchor_after(origin_start)
726 ..origin_buffer.anchor_before(origin_end),
727 }
728 });
729
730 let target_buffer = target_buffer_handle.read(cx);
731 let target_start =
732 target_buffer.clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
733 let target_end =
734 target_buffer.clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
735 let target_location = Location {
736 buffer: target_buffer_handle,
737 range: target_buffer.anchor_after(target_start)
738 ..target_buffer.anchor_before(target_end),
739 };
740
741 definitions.push(LocationLink {
742 origin: origin_location,
743 target: target_location,
744 })
745 });
746 }
747 Ok(definitions)
748}
749
750fn location_links_to_proto(
751 links: Vec<LocationLink>,
752 project: &mut Project,
753 peer_id: PeerId,
754 cx: &mut AppContext,
755) -> Vec<proto::LocationLink> {
756 links
757 .into_iter()
758 .map(|definition| {
759 let origin = definition.origin.map(|origin| {
760 let buffer_id = project.create_buffer_for_peer(&origin.buffer, peer_id, cx);
761 proto::Location {
762 start: Some(serialize_anchor(&origin.range.start)),
763 end: Some(serialize_anchor(&origin.range.end)),
764 buffer_id,
765 }
766 });
767
768 let buffer_id = project.create_buffer_for_peer(&definition.target.buffer, peer_id, cx);
769 let target = proto::Location {
770 start: Some(serialize_anchor(&definition.target.range.start)),
771 end: Some(serialize_anchor(&definition.target.range.end)),
772 buffer_id,
773 };
774
775 proto::LocationLink {
776 origin,
777 target: Some(target),
778 }
779 })
780 .collect()
781}
782
783#[async_trait(?Send)]
784impl LspCommand for GetReferences {
785 type Response = Vec<Location>;
786 type LspRequest = lsp::request::References;
787 type ProtoRequest = proto::GetReferences;
788
789 fn to_lsp(
790 &self,
791 path: &Path,
792 _: &Buffer,
793 _: &Arc<LanguageServer>,
794 _: &AppContext,
795 ) -> lsp::ReferenceParams {
796 lsp::ReferenceParams {
797 text_document_position: lsp::TextDocumentPositionParams {
798 text_document: lsp::TextDocumentIdentifier {
799 uri: lsp::Url::from_file_path(path).unwrap(),
800 },
801 position: point_to_lsp(self.position),
802 },
803 work_done_progress_params: Default::default(),
804 partial_result_params: Default::default(),
805 context: lsp::ReferenceContext {
806 include_declaration: true,
807 },
808 }
809 }
810
811 async fn response_from_lsp(
812 self,
813 locations: Option<Vec<lsp::Location>>,
814 project: ModelHandle<Project>,
815 buffer: ModelHandle<Buffer>,
816 server_id: LanguageServerId,
817 mut cx: AsyncAppContext,
818 ) -> Result<Vec<Location>> {
819 let mut references = Vec::new();
820 let (lsp_adapter, language_server) =
821 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
822
823 if let Some(locations) = locations {
824 for lsp_location in locations {
825 let target_buffer_handle = project
826 .update(&mut cx, |this, cx| {
827 this.open_local_buffer_via_lsp(
828 lsp_location.uri,
829 language_server.server_id(),
830 lsp_adapter.name.clone(),
831 cx,
832 )
833 })
834 .await?;
835
836 cx.read(|cx| {
837 let target_buffer = target_buffer_handle.read(cx);
838 let target_start = target_buffer
839 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
840 let target_end = target_buffer
841 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
842 references.push(Location {
843 buffer: target_buffer_handle,
844 range: target_buffer.anchor_after(target_start)
845 ..target_buffer.anchor_before(target_end),
846 });
847 });
848 }
849 }
850
851 Ok(references)
852 }
853
854 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetReferences {
855 proto::GetReferences {
856 project_id,
857 buffer_id: buffer.remote_id(),
858 position: Some(language::proto::serialize_anchor(
859 &buffer.anchor_before(self.position),
860 )),
861 version: serialize_version(&buffer.version()),
862 }
863 }
864
865 async fn from_proto(
866 message: proto::GetReferences,
867 _: ModelHandle<Project>,
868 buffer: ModelHandle<Buffer>,
869 mut cx: AsyncAppContext,
870 ) -> Result<Self> {
871 let position = message
872 .position
873 .and_then(deserialize_anchor)
874 .ok_or_else(|| anyhow!("invalid position"))?;
875 buffer
876 .update(&mut cx, |buffer, _| {
877 buffer.wait_for_version(deserialize_version(&message.version))
878 })
879 .await?;
880 Ok(Self {
881 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
882 })
883 }
884
885 fn response_to_proto(
886 response: Vec<Location>,
887 project: &mut Project,
888 peer_id: PeerId,
889 _: &clock::Global,
890 cx: &mut AppContext,
891 ) -> proto::GetReferencesResponse {
892 let locations = response
893 .into_iter()
894 .map(|definition| {
895 let buffer_id = project.create_buffer_for_peer(&definition.buffer, peer_id, cx);
896 proto::Location {
897 start: Some(serialize_anchor(&definition.range.start)),
898 end: Some(serialize_anchor(&definition.range.end)),
899 buffer_id,
900 }
901 })
902 .collect();
903 proto::GetReferencesResponse { locations }
904 }
905
906 async fn response_from_proto(
907 self,
908 message: proto::GetReferencesResponse,
909 project: ModelHandle<Project>,
910 _: ModelHandle<Buffer>,
911 mut cx: AsyncAppContext,
912 ) -> Result<Vec<Location>> {
913 let mut locations = Vec::new();
914 for location in message.locations {
915 let target_buffer = project
916 .update(&mut cx, |this, cx| {
917 this.wait_for_remote_buffer(location.buffer_id, cx)
918 })
919 .await?;
920 let start = location
921 .start
922 .and_then(deserialize_anchor)
923 .ok_or_else(|| anyhow!("missing target start"))?;
924 let end = location
925 .end
926 .and_then(deserialize_anchor)
927 .ok_or_else(|| anyhow!("missing target end"))?;
928 target_buffer
929 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))
930 .await?;
931 locations.push(Location {
932 buffer: target_buffer,
933 range: start..end,
934 })
935 }
936 Ok(locations)
937 }
938
939 fn buffer_id_from_proto(message: &proto::GetReferences) -> u64 {
940 message.buffer_id
941 }
942}
943
944#[async_trait(?Send)]
945impl LspCommand for GetDocumentHighlights {
946 type Response = Vec<DocumentHighlight>;
947 type LspRequest = lsp::request::DocumentHighlightRequest;
948 type ProtoRequest = proto::GetDocumentHighlights;
949
950 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
951 capabilities.document_highlight_provider.is_some()
952 }
953
954 fn to_lsp(
955 &self,
956 path: &Path,
957 _: &Buffer,
958 _: &Arc<LanguageServer>,
959 _: &AppContext,
960 ) -> lsp::DocumentHighlightParams {
961 lsp::DocumentHighlightParams {
962 text_document_position_params: lsp::TextDocumentPositionParams {
963 text_document: lsp::TextDocumentIdentifier {
964 uri: lsp::Url::from_file_path(path).unwrap(),
965 },
966 position: point_to_lsp(self.position),
967 },
968 work_done_progress_params: Default::default(),
969 partial_result_params: Default::default(),
970 }
971 }
972
973 async fn response_from_lsp(
974 self,
975 lsp_highlights: Option<Vec<lsp::DocumentHighlight>>,
976 _: ModelHandle<Project>,
977 buffer: ModelHandle<Buffer>,
978 _: LanguageServerId,
979 cx: AsyncAppContext,
980 ) -> Result<Vec<DocumentHighlight>> {
981 buffer.read_with(&cx, |buffer, _| {
982 let mut lsp_highlights = lsp_highlights.unwrap_or_default();
983 lsp_highlights.sort_unstable_by_key(|h| (h.range.start, Reverse(h.range.end)));
984 Ok(lsp_highlights
985 .into_iter()
986 .map(|lsp_highlight| {
987 let start = buffer
988 .clip_point_utf16(point_from_lsp(lsp_highlight.range.start), Bias::Left);
989 let end = buffer
990 .clip_point_utf16(point_from_lsp(lsp_highlight.range.end), Bias::Left);
991 DocumentHighlight {
992 range: buffer.anchor_after(start)..buffer.anchor_before(end),
993 kind: lsp_highlight
994 .kind
995 .unwrap_or(lsp::DocumentHighlightKind::READ),
996 }
997 })
998 .collect())
999 })
1000 }
1001
1002 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentHighlights {
1003 proto::GetDocumentHighlights {
1004 project_id,
1005 buffer_id: buffer.remote_id(),
1006 position: Some(language::proto::serialize_anchor(
1007 &buffer.anchor_before(self.position),
1008 )),
1009 version: serialize_version(&buffer.version()),
1010 }
1011 }
1012
1013 async fn from_proto(
1014 message: proto::GetDocumentHighlights,
1015 _: ModelHandle<Project>,
1016 buffer: ModelHandle<Buffer>,
1017 mut cx: AsyncAppContext,
1018 ) -> Result<Self> {
1019 let position = message
1020 .position
1021 .and_then(deserialize_anchor)
1022 .ok_or_else(|| anyhow!("invalid position"))?;
1023 buffer
1024 .update(&mut cx, |buffer, _| {
1025 buffer.wait_for_version(deserialize_version(&message.version))
1026 })
1027 .await?;
1028 Ok(Self {
1029 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
1030 })
1031 }
1032
1033 fn response_to_proto(
1034 response: Vec<DocumentHighlight>,
1035 _: &mut Project,
1036 _: PeerId,
1037 _: &clock::Global,
1038 _: &mut AppContext,
1039 ) -> proto::GetDocumentHighlightsResponse {
1040 let highlights = response
1041 .into_iter()
1042 .map(|highlight| proto::DocumentHighlight {
1043 start: Some(serialize_anchor(&highlight.range.start)),
1044 end: Some(serialize_anchor(&highlight.range.end)),
1045 kind: match highlight.kind {
1046 DocumentHighlightKind::TEXT => proto::document_highlight::Kind::Text.into(),
1047 DocumentHighlightKind::WRITE => proto::document_highlight::Kind::Write.into(),
1048 DocumentHighlightKind::READ => proto::document_highlight::Kind::Read.into(),
1049 _ => proto::document_highlight::Kind::Text.into(),
1050 },
1051 })
1052 .collect();
1053 proto::GetDocumentHighlightsResponse { highlights }
1054 }
1055
1056 async fn response_from_proto(
1057 self,
1058 message: proto::GetDocumentHighlightsResponse,
1059 _: ModelHandle<Project>,
1060 buffer: ModelHandle<Buffer>,
1061 mut cx: AsyncAppContext,
1062 ) -> Result<Vec<DocumentHighlight>> {
1063 let mut highlights = Vec::new();
1064 for highlight in message.highlights {
1065 let start = highlight
1066 .start
1067 .and_then(deserialize_anchor)
1068 .ok_or_else(|| anyhow!("missing target start"))?;
1069 let end = highlight
1070 .end
1071 .and_then(deserialize_anchor)
1072 .ok_or_else(|| anyhow!("missing target end"))?;
1073 buffer
1074 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))
1075 .await?;
1076 let kind = match proto::document_highlight::Kind::from_i32(highlight.kind) {
1077 Some(proto::document_highlight::Kind::Text) => DocumentHighlightKind::TEXT,
1078 Some(proto::document_highlight::Kind::Read) => DocumentHighlightKind::READ,
1079 Some(proto::document_highlight::Kind::Write) => DocumentHighlightKind::WRITE,
1080 None => DocumentHighlightKind::TEXT,
1081 };
1082 highlights.push(DocumentHighlight {
1083 range: start..end,
1084 kind,
1085 });
1086 }
1087 Ok(highlights)
1088 }
1089
1090 fn buffer_id_from_proto(message: &proto::GetDocumentHighlights) -> u64 {
1091 message.buffer_id
1092 }
1093}
1094
1095#[async_trait(?Send)]
1096impl LspCommand for GetHover {
1097 type Response = Option<Hover>;
1098 type LspRequest = lsp::request::HoverRequest;
1099 type ProtoRequest = proto::GetHover;
1100
1101 fn to_lsp(
1102 &self,
1103 path: &Path,
1104 _: &Buffer,
1105 _: &Arc<LanguageServer>,
1106 _: &AppContext,
1107 ) -> lsp::HoverParams {
1108 lsp::HoverParams {
1109 text_document_position_params: lsp::TextDocumentPositionParams {
1110 text_document: lsp::TextDocumentIdentifier {
1111 uri: lsp::Url::from_file_path(path).unwrap(),
1112 },
1113 position: point_to_lsp(self.position),
1114 },
1115 work_done_progress_params: Default::default(),
1116 }
1117 }
1118
1119 async fn response_from_lsp(
1120 self,
1121 message: Option<lsp::Hover>,
1122 _: ModelHandle<Project>,
1123 buffer: ModelHandle<Buffer>,
1124 _: LanguageServerId,
1125 cx: AsyncAppContext,
1126 ) -> Result<Self::Response> {
1127 Ok(message.and_then(|hover| {
1128 let (language, range) = cx.read(|cx| {
1129 let buffer = buffer.read(cx);
1130 (
1131 buffer.language().cloned(),
1132 hover.range.map(|range| {
1133 let token_start =
1134 buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left);
1135 let token_end =
1136 buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left);
1137 buffer.anchor_after(token_start)..buffer.anchor_before(token_end)
1138 }),
1139 )
1140 });
1141
1142 fn hover_blocks_from_marked_string(
1143 marked_string: lsp::MarkedString,
1144 ) -> Option<HoverBlock> {
1145 let block = match marked_string {
1146 lsp::MarkedString::String(content) => HoverBlock {
1147 text: content,
1148 kind: HoverBlockKind::Markdown,
1149 },
1150 lsp::MarkedString::LanguageString(lsp::LanguageString { language, value }) => {
1151 HoverBlock {
1152 text: value,
1153 kind: HoverBlockKind::Code { language },
1154 }
1155 }
1156 };
1157 if block.text.is_empty() {
1158 None
1159 } else {
1160 Some(block)
1161 }
1162 }
1163
1164 let contents = cx.read(|_| match hover.contents {
1165 lsp::HoverContents::Scalar(marked_string) => {
1166 hover_blocks_from_marked_string(marked_string)
1167 .into_iter()
1168 .collect()
1169 }
1170 lsp::HoverContents::Array(marked_strings) => marked_strings
1171 .into_iter()
1172 .filter_map(hover_blocks_from_marked_string)
1173 .collect(),
1174 lsp::HoverContents::Markup(markup_content) => vec![HoverBlock {
1175 text: markup_content.value,
1176 kind: if markup_content.kind == lsp::MarkupKind::Markdown {
1177 HoverBlockKind::Markdown
1178 } else {
1179 HoverBlockKind::PlainText
1180 },
1181 }],
1182 });
1183
1184 Some(Hover {
1185 contents,
1186 range,
1187 language,
1188 })
1189 }))
1190 }
1191
1192 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
1193 proto::GetHover {
1194 project_id,
1195 buffer_id: buffer.remote_id(),
1196 position: Some(language::proto::serialize_anchor(
1197 &buffer.anchor_before(self.position),
1198 )),
1199 version: serialize_version(&buffer.version),
1200 }
1201 }
1202
1203 async fn from_proto(
1204 message: Self::ProtoRequest,
1205 _: ModelHandle<Project>,
1206 buffer: ModelHandle<Buffer>,
1207 mut cx: AsyncAppContext,
1208 ) -> Result<Self> {
1209 let position = message
1210 .position
1211 .and_then(deserialize_anchor)
1212 .ok_or_else(|| anyhow!("invalid position"))?;
1213 buffer
1214 .update(&mut cx, |buffer, _| {
1215 buffer.wait_for_version(deserialize_version(&message.version))
1216 })
1217 .await?;
1218 Ok(Self {
1219 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
1220 })
1221 }
1222
1223 fn response_to_proto(
1224 response: Self::Response,
1225 _: &mut Project,
1226 _: PeerId,
1227 _: &clock::Global,
1228 _: &mut AppContext,
1229 ) -> proto::GetHoverResponse {
1230 if let Some(response) = response {
1231 let (start, end) = if let Some(range) = response.range {
1232 (
1233 Some(language::proto::serialize_anchor(&range.start)),
1234 Some(language::proto::serialize_anchor(&range.end)),
1235 )
1236 } else {
1237 (None, None)
1238 };
1239
1240 let contents = response
1241 .contents
1242 .into_iter()
1243 .map(|block| proto::HoverBlock {
1244 text: block.text,
1245 is_markdown: block.kind == HoverBlockKind::Markdown,
1246 language: if let HoverBlockKind::Code { language } = block.kind {
1247 Some(language)
1248 } else {
1249 None
1250 },
1251 })
1252 .collect();
1253
1254 proto::GetHoverResponse {
1255 start,
1256 end,
1257 contents,
1258 }
1259 } else {
1260 proto::GetHoverResponse {
1261 start: None,
1262 end: None,
1263 contents: Vec::new(),
1264 }
1265 }
1266 }
1267
1268 async fn response_from_proto(
1269 self,
1270 message: proto::GetHoverResponse,
1271 _: ModelHandle<Project>,
1272 buffer: ModelHandle<Buffer>,
1273 cx: AsyncAppContext,
1274 ) -> Result<Self::Response> {
1275 let contents: Vec<_> = message
1276 .contents
1277 .into_iter()
1278 .map(|block| HoverBlock {
1279 text: block.text,
1280 kind: if let Some(language) = block.language {
1281 HoverBlockKind::Code { language }
1282 } else if block.is_markdown {
1283 HoverBlockKind::Markdown
1284 } else {
1285 HoverBlockKind::PlainText
1286 },
1287 })
1288 .collect();
1289 if contents.is_empty() {
1290 return Ok(None);
1291 }
1292
1293 let language = buffer.read_with(&cx, |buffer, _| buffer.language().cloned());
1294 let range = if let (Some(start), Some(end)) = (message.start, message.end) {
1295 language::proto::deserialize_anchor(start)
1296 .and_then(|start| language::proto::deserialize_anchor(end).map(|end| start..end))
1297 } else {
1298 None
1299 };
1300
1301 Ok(Some(Hover {
1302 contents,
1303 range,
1304 language,
1305 }))
1306 }
1307
1308 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64 {
1309 message.buffer_id
1310 }
1311}
1312
1313#[async_trait(?Send)]
1314impl LspCommand for GetCompletions {
1315 type Response = Vec<Completion>;
1316 type LspRequest = lsp::request::Completion;
1317 type ProtoRequest = proto::GetCompletions;
1318
1319 fn to_lsp(
1320 &self,
1321 path: &Path,
1322 _: &Buffer,
1323 _: &Arc<LanguageServer>,
1324 _: &AppContext,
1325 ) -> lsp::CompletionParams {
1326 lsp::CompletionParams {
1327 text_document_position: lsp::TextDocumentPositionParams::new(
1328 lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
1329 point_to_lsp(self.position),
1330 ),
1331 context: Default::default(),
1332 work_done_progress_params: Default::default(),
1333 partial_result_params: Default::default(),
1334 }
1335 }
1336
1337 async fn response_from_lsp(
1338 self,
1339 completions: Option<lsp::CompletionResponse>,
1340 _: ModelHandle<Project>,
1341 buffer: ModelHandle<Buffer>,
1342 _: LanguageServerId,
1343 cx: AsyncAppContext,
1344 ) -> Result<Vec<Completion>> {
1345 let completions = if let Some(completions) = completions {
1346 match completions {
1347 lsp::CompletionResponse::Array(completions) => completions,
1348 lsp::CompletionResponse::List(list) => list.items,
1349 }
1350 } else {
1351 Default::default()
1352 };
1353
1354 let completions = buffer.read_with(&cx, |buffer, _| {
1355 let language = buffer.language().cloned();
1356 let snapshot = buffer.snapshot();
1357 let clipped_position = buffer.clip_point_utf16(Unclipped(self.position), Bias::Left);
1358 let mut range_for_token = None;
1359 completions
1360 .into_iter()
1361 .filter_map(move |mut lsp_completion| {
1362 let (old_range, mut new_text) = match lsp_completion.text_edit.as_ref() {
1363 // If the language server provides a range to overwrite, then
1364 // check that the range is valid.
1365 Some(lsp::CompletionTextEdit::Edit(edit)) => {
1366 let range = range_from_lsp(edit.range);
1367 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1368 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1369 if start != range.start.0 || end != range.end.0 {
1370 log::info!("completion out of expected range");
1371 return None;
1372 }
1373 (
1374 snapshot.anchor_before(start)..snapshot.anchor_after(end),
1375 edit.new_text.clone(),
1376 )
1377 }
1378 // If the language server does not provide a range, then infer
1379 // the range based on the syntax tree.
1380 None => {
1381 if self.position != clipped_position {
1382 log::info!("completion out of expected range");
1383 return None;
1384 }
1385 let Range { start, end } = range_for_token
1386 .get_or_insert_with(|| {
1387 let offset = self.position.to_offset(&snapshot);
1388 let (range, kind) = snapshot.surrounding_word(offset);
1389 if kind == Some(CharKind::Word) {
1390 range
1391 } else {
1392 offset..offset
1393 }
1394 })
1395 .clone();
1396 let text = lsp_completion
1397 .insert_text
1398 .as_ref()
1399 .unwrap_or(&lsp_completion.label)
1400 .clone();
1401 (
1402 snapshot.anchor_before(start)..snapshot.anchor_after(end),
1403 text,
1404 )
1405 }
1406 Some(lsp::CompletionTextEdit::InsertAndReplace(_)) => {
1407 log::info!("unsupported insert/replace completion");
1408 return None;
1409 }
1410 };
1411
1412 let language = language.clone();
1413 LineEnding::normalize(&mut new_text);
1414 Some(async move {
1415 let mut label = None;
1416 if let Some(language) = language {
1417 language.process_completion(&mut lsp_completion).await;
1418 label = language.label_for_completion(&lsp_completion).await;
1419 }
1420 Completion {
1421 old_range,
1422 new_text,
1423 label: label.unwrap_or_else(|| {
1424 language::CodeLabel::plain(
1425 lsp_completion.label.clone(),
1426 lsp_completion.filter_text.as_deref(),
1427 )
1428 }),
1429 lsp_completion,
1430 }
1431 })
1432 })
1433 });
1434
1435 Ok(futures::future::join_all(completions).await)
1436 }
1437
1438 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCompletions {
1439 let anchor = buffer.anchor_after(self.position);
1440 proto::GetCompletions {
1441 project_id,
1442 buffer_id: buffer.remote_id(),
1443 position: Some(language::proto::serialize_anchor(&anchor)),
1444 version: serialize_version(&buffer.version()),
1445 }
1446 }
1447
1448 async fn from_proto(
1449 message: proto::GetCompletions,
1450 _: ModelHandle<Project>,
1451 buffer: ModelHandle<Buffer>,
1452 mut cx: AsyncAppContext,
1453 ) -> Result<Self> {
1454 let version = deserialize_version(&message.version);
1455 buffer
1456 .update(&mut cx, |buffer, _| buffer.wait_for_version(version))
1457 .await?;
1458 let position = message
1459 .position
1460 .and_then(language::proto::deserialize_anchor)
1461 .map(|p| {
1462 buffer.read_with(&cx, |buffer, _| {
1463 buffer.clip_point_utf16(Unclipped(p.to_point_utf16(buffer)), Bias::Left)
1464 })
1465 })
1466 .ok_or_else(|| anyhow!("invalid position"))?;
1467 Ok(Self { position })
1468 }
1469
1470 fn response_to_proto(
1471 completions: Vec<Completion>,
1472 _: &mut Project,
1473 _: PeerId,
1474 buffer_version: &clock::Global,
1475 _: &mut AppContext,
1476 ) -> proto::GetCompletionsResponse {
1477 proto::GetCompletionsResponse {
1478 completions: completions
1479 .iter()
1480 .map(language::proto::serialize_completion)
1481 .collect(),
1482 version: serialize_version(&buffer_version),
1483 }
1484 }
1485
1486 async fn response_from_proto(
1487 self,
1488 message: proto::GetCompletionsResponse,
1489 _: ModelHandle<Project>,
1490 buffer: ModelHandle<Buffer>,
1491 mut cx: AsyncAppContext,
1492 ) -> Result<Vec<Completion>> {
1493 buffer
1494 .update(&mut cx, |buffer, _| {
1495 buffer.wait_for_version(deserialize_version(&message.version))
1496 })
1497 .await?;
1498
1499 let language = buffer.read_with(&cx, |buffer, _| buffer.language().cloned());
1500 let completions = message.completions.into_iter().map(|completion| {
1501 language::proto::deserialize_completion(completion, language.clone())
1502 });
1503 futures::future::try_join_all(completions).await
1504 }
1505
1506 fn buffer_id_from_proto(message: &proto::GetCompletions) -> u64 {
1507 message.buffer_id
1508 }
1509}
1510
1511#[async_trait(?Send)]
1512impl LspCommand for GetCodeActions {
1513 type Response = Vec<CodeAction>;
1514 type LspRequest = lsp::request::CodeActionRequest;
1515 type ProtoRequest = proto::GetCodeActions;
1516
1517 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
1518 match &capabilities.code_action_provider {
1519 None => false,
1520 Some(lsp::CodeActionProviderCapability::Simple(false)) => false,
1521 _ => true,
1522 }
1523 }
1524
1525 fn to_lsp(
1526 &self,
1527 path: &Path,
1528 buffer: &Buffer,
1529 language_server: &Arc<LanguageServer>,
1530 _: &AppContext,
1531 ) -> lsp::CodeActionParams {
1532 let relevant_diagnostics = buffer
1533 .snapshot()
1534 .diagnostics_in_range::<_, usize>(self.range.clone(), false)
1535 .map(|entry| entry.to_lsp_diagnostic_stub())
1536 .collect();
1537 lsp::CodeActionParams {
1538 text_document: lsp::TextDocumentIdentifier::new(
1539 lsp::Url::from_file_path(path).unwrap(),
1540 ),
1541 range: range_to_lsp(self.range.to_point_utf16(buffer)),
1542 work_done_progress_params: Default::default(),
1543 partial_result_params: Default::default(),
1544 context: lsp::CodeActionContext {
1545 diagnostics: relevant_diagnostics,
1546 only: language_server.code_action_kinds(),
1547 ..lsp::CodeActionContext::default()
1548 },
1549 }
1550 }
1551
1552 async fn response_from_lsp(
1553 self,
1554 actions: Option<lsp::CodeActionResponse>,
1555 _: ModelHandle<Project>,
1556 _: ModelHandle<Buffer>,
1557 server_id: LanguageServerId,
1558 _: AsyncAppContext,
1559 ) -> Result<Vec<CodeAction>> {
1560 Ok(actions
1561 .unwrap_or_default()
1562 .into_iter()
1563 .filter_map(|entry| {
1564 if let lsp::CodeActionOrCommand::CodeAction(lsp_action) = entry {
1565 Some(CodeAction {
1566 server_id,
1567 range: self.range.clone(),
1568 lsp_action,
1569 })
1570 } else {
1571 None
1572 }
1573 })
1574 .collect())
1575 }
1576
1577 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCodeActions {
1578 proto::GetCodeActions {
1579 project_id,
1580 buffer_id: buffer.remote_id(),
1581 start: Some(language::proto::serialize_anchor(&self.range.start)),
1582 end: Some(language::proto::serialize_anchor(&self.range.end)),
1583 version: serialize_version(&buffer.version()),
1584 }
1585 }
1586
1587 async fn from_proto(
1588 message: proto::GetCodeActions,
1589 _: ModelHandle<Project>,
1590 buffer: ModelHandle<Buffer>,
1591 mut cx: AsyncAppContext,
1592 ) -> Result<Self> {
1593 let start = message
1594 .start
1595 .and_then(language::proto::deserialize_anchor)
1596 .ok_or_else(|| anyhow!("invalid start"))?;
1597 let end = message
1598 .end
1599 .and_then(language::proto::deserialize_anchor)
1600 .ok_or_else(|| anyhow!("invalid end"))?;
1601 buffer
1602 .update(&mut cx, |buffer, _| {
1603 buffer.wait_for_version(deserialize_version(&message.version))
1604 })
1605 .await?;
1606
1607 Ok(Self { range: start..end })
1608 }
1609
1610 fn response_to_proto(
1611 code_actions: Vec<CodeAction>,
1612 _: &mut Project,
1613 _: PeerId,
1614 buffer_version: &clock::Global,
1615 _: &mut AppContext,
1616 ) -> proto::GetCodeActionsResponse {
1617 proto::GetCodeActionsResponse {
1618 actions: code_actions
1619 .iter()
1620 .map(language::proto::serialize_code_action)
1621 .collect(),
1622 version: serialize_version(&buffer_version),
1623 }
1624 }
1625
1626 async fn response_from_proto(
1627 self,
1628 message: proto::GetCodeActionsResponse,
1629 _: ModelHandle<Project>,
1630 buffer: ModelHandle<Buffer>,
1631 mut cx: AsyncAppContext,
1632 ) -> Result<Vec<CodeAction>> {
1633 buffer
1634 .update(&mut cx, |buffer, _| {
1635 buffer.wait_for_version(deserialize_version(&message.version))
1636 })
1637 .await?;
1638 message
1639 .actions
1640 .into_iter()
1641 .map(language::proto::deserialize_code_action)
1642 .collect()
1643 }
1644
1645 fn buffer_id_from_proto(message: &proto::GetCodeActions) -> u64 {
1646 message.buffer_id
1647 }
1648}
1649
1650#[async_trait(?Send)]
1651impl LspCommand for OnTypeFormatting {
1652 type Response = Option<Transaction>;
1653 type LspRequest = lsp::request::OnTypeFormatting;
1654 type ProtoRequest = proto::OnTypeFormatting;
1655
1656 fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
1657 let Some(on_type_formatting_options) = &server_capabilities.document_on_type_formatting_provider else { return false };
1658 on_type_formatting_options
1659 .first_trigger_character
1660 .contains(&self.trigger)
1661 || on_type_formatting_options
1662 .more_trigger_character
1663 .iter()
1664 .flatten()
1665 .any(|chars| chars.contains(&self.trigger))
1666 }
1667
1668 fn to_lsp(
1669 &self,
1670 path: &Path,
1671 _: &Buffer,
1672 _: &Arc<LanguageServer>,
1673 _: &AppContext,
1674 ) -> lsp::DocumentOnTypeFormattingParams {
1675 lsp::DocumentOnTypeFormattingParams {
1676 text_document_position: lsp::TextDocumentPositionParams::new(
1677 lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
1678 point_to_lsp(self.position),
1679 ),
1680 ch: self.trigger.clone(),
1681 options: lsp_formatting_options(self.options.tab_size),
1682 }
1683 }
1684
1685 async fn response_from_lsp(
1686 self,
1687 message: Option<Vec<lsp::TextEdit>>,
1688 project: ModelHandle<Project>,
1689 buffer: ModelHandle<Buffer>,
1690 server_id: LanguageServerId,
1691 mut cx: AsyncAppContext,
1692 ) -> Result<Option<Transaction>> {
1693 if let Some(edits) = message {
1694 let (lsp_adapter, lsp_server) =
1695 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
1696 Project::deserialize_edits(
1697 project,
1698 buffer,
1699 edits,
1700 self.push_to_history,
1701 lsp_adapter,
1702 lsp_server,
1703 &mut cx,
1704 )
1705 .await
1706 } else {
1707 Ok(None)
1708 }
1709 }
1710
1711 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::OnTypeFormatting {
1712 proto::OnTypeFormatting {
1713 project_id,
1714 buffer_id: buffer.remote_id(),
1715 position: Some(language::proto::serialize_anchor(
1716 &buffer.anchor_before(self.position),
1717 )),
1718 trigger: self.trigger.clone(),
1719 version: serialize_version(&buffer.version()),
1720 }
1721 }
1722
1723 async fn from_proto(
1724 message: proto::OnTypeFormatting,
1725 _: ModelHandle<Project>,
1726 buffer: ModelHandle<Buffer>,
1727 mut cx: AsyncAppContext,
1728 ) -> Result<Self> {
1729 let position = message
1730 .position
1731 .and_then(deserialize_anchor)
1732 .ok_or_else(|| anyhow!("invalid position"))?;
1733 buffer
1734 .update(&mut cx, |buffer, _| {
1735 buffer.wait_for_version(deserialize_version(&message.version))
1736 })
1737 .await?;
1738
1739 let tab_size = buffer.read_with(&cx, |buffer, cx| {
1740 language_settings(buffer.language(), buffer.file(), cx).tab_size
1741 });
1742
1743 Ok(Self {
1744 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
1745 trigger: message.trigger.clone(),
1746 options: lsp_formatting_options(tab_size.get()).into(),
1747 push_to_history: false,
1748 })
1749 }
1750
1751 fn response_to_proto(
1752 response: Option<Transaction>,
1753 _: &mut Project,
1754 _: PeerId,
1755 _: &clock::Global,
1756 _: &mut AppContext,
1757 ) -> proto::OnTypeFormattingResponse {
1758 proto::OnTypeFormattingResponse {
1759 transaction: response
1760 .map(|transaction| language::proto::serialize_transaction(&transaction)),
1761 }
1762 }
1763
1764 async fn response_from_proto(
1765 self,
1766 message: proto::OnTypeFormattingResponse,
1767 _: ModelHandle<Project>,
1768 _: ModelHandle<Buffer>,
1769 _: AsyncAppContext,
1770 ) -> Result<Option<Transaction>> {
1771 let Some(transaction) = message.transaction else { return Ok(None) };
1772 Ok(Some(language::proto::deserialize_transaction(transaction)?))
1773 }
1774
1775 fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> u64 {
1776 message.buffer_id
1777 }
1778}
1779
1780impl InlayHints {
1781 pub fn lsp_to_project_hint(
1782 lsp_hint: lsp::InlayHint,
1783 buffer_handle: &ModelHandle<Buffer>,
1784 resolve_state: ResolveState,
1785 force_no_type_left_padding: bool,
1786 cx: &AppContext,
1787 ) -> InlayHint {
1788 let kind = lsp_hint.kind.and_then(|kind| match kind {
1789 lsp::InlayHintKind::TYPE => Some(InlayHintKind::Type),
1790 lsp::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
1791 _ => None,
1792 });
1793 let buffer = buffer_handle.read(cx);
1794 let position = buffer.clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
1795 let padding_left = if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
1796 false
1797 } else {
1798 lsp_hint.padding_left.unwrap_or(false)
1799 };
1800 InlayHint {
1801 position: if kind == Some(InlayHintKind::Parameter) {
1802 buffer.anchor_before(position)
1803 } else {
1804 buffer.anchor_after(position)
1805 },
1806 padding_left,
1807 padding_right: lsp_hint.padding_right.unwrap_or(false),
1808 label: match lsp_hint.label {
1809 lsp::InlayHintLabel::String(s) => InlayHintLabel::String(s),
1810 lsp::InlayHintLabel::LabelParts(lsp_parts) => InlayHintLabel::LabelParts(
1811 lsp_parts
1812 .into_iter()
1813 .map(|label_part| InlayHintLabelPart {
1814 value: label_part.value,
1815 tooltip: label_part.tooltip.map(|tooltip| match tooltip {
1816 lsp::InlayHintLabelPartTooltip::String(s) => {
1817 InlayHintLabelPartTooltip::String(s)
1818 }
1819 lsp::InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
1820 InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
1821 kind: match markup_content.kind {
1822 lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
1823 lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
1824 },
1825 value: markup_content.value,
1826 })
1827 }
1828 }),
1829 location: label_part.location.map(|lsp_location| {
1830 let target_start = buffer.clip_point_utf16(
1831 point_from_lsp(lsp_location.range.start),
1832 Bias::Left,
1833 );
1834 let target_end = buffer.clip_point_utf16(
1835 point_from_lsp(lsp_location.range.end),
1836 Bias::Left,
1837 );
1838 Location {
1839 buffer: buffer_handle.clone(),
1840 range: buffer.anchor_after(target_start)
1841 ..buffer.anchor_before(target_end),
1842 }
1843 }),
1844 })
1845 .collect(),
1846 ),
1847 },
1848 kind,
1849 tooltip: lsp_hint.tooltip.map(|tooltip| match tooltip {
1850 lsp::InlayHintTooltip::String(s) => InlayHintTooltip::String(s),
1851 lsp::InlayHintTooltip::MarkupContent(markup_content) => {
1852 InlayHintTooltip::MarkupContent(MarkupContent {
1853 kind: match markup_content.kind {
1854 lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
1855 lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
1856 },
1857 value: markup_content.value,
1858 })
1859 }
1860 }),
1861 resolve_state,
1862 }
1863 }
1864
1865 pub fn project_to_proto_hint(response_hint: InlayHint, cx: &AppContext) -> proto::InlayHint {
1866 let (state, lsp_resolve_state) = match response_hint.resolve_state {
1867 ResolveState::CanResolve(server_id, resolve_data) => (
1868 0,
1869 resolve_data
1870 .map(|json_data| {
1871 serde_json::to_string(&json_data)
1872 .expect("failed to serialize resolve json data")
1873 })
1874 .map(|value| proto::resolve_state::LspResolveState {
1875 server_id: server_id.0 as u64,
1876 value,
1877 }),
1878 ),
1879 ResolveState::Resolved => (1, None),
1880 ResolveState::Resolving => (2, None),
1881 };
1882 let resolve_state = Some(proto::ResolveState {
1883 state,
1884 lsp_resolve_state,
1885 });
1886 proto::InlayHint {
1887 position: Some(language::proto::serialize_anchor(&response_hint.position)),
1888 padding_left: response_hint.padding_left,
1889 padding_right: response_hint.padding_right,
1890 label: Some(proto::InlayHintLabel {
1891 label: Some(match response_hint.label {
1892 InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s),
1893 InlayHintLabel::LabelParts(label_parts) => {
1894 proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts {
1895 parts: label_parts.into_iter().map(|label_part| proto::InlayHintLabelPart {
1896 value: label_part.value,
1897 tooltip: label_part.tooltip.map(|tooltip| {
1898 let proto_tooltip = match tooltip {
1899 InlayHintLabelPartTooltip::String(s) => proto::inlay_hint_label_part_tooltip::Content::Value(s),
1900 InlayHintLabelPartTooltip::MarkupContent(markup_content) => proto::inlay_hint_label_part_tooltip::Content::MarkupContent(proto::MarkupContent {
1901 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
1902 value: markup_content.value,
1903 }),
1904 };
1905 proto::InlayHintLabelPartTooltip {content: Some(proto_tooltip)}
1906 }),
1907 location: label_part.location.map(|location| proto::Location {
1908 start: Some(serialize_anchor(&location.range.start)),
1909 end: Some(serialize_anchor(&location.range.end)),
1910 buffer_id: location.buffer.read(cx).remote_id(),
1911 }),
1912 }).collect()
1913 })
1914 }
1915 }),
1916 }),
1917 kind: response_hint.kind.map(|kind| kind.name().to_string()),
1918 tooltip: response_hint.tooltip.map(|response_tooltip| {
1919 let proto_tooltip = match response_tooltip {
1920 InlayHintTooltip::String(s) => {
1921 proto::inlay_hint_tooltip::Content::Value(s)
1922 }
1923 InlayHintTooltip::MarkupContent(markup_content) => {
1924 proto::inlay_hint_tooltip::Content::MarkupContent(
1925 proto::MarkupContent {
1926 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
1927 value: markup_content.value,
1928 },
1929 )
1930 }
1931 };
1932 proto::InlayHintTooltip {
1933 content: Some(proto_tooltip),
1934 }
1935 }),
1936 resolve_state,
1937 }
1938 }
1939
1940 pub async fn proto_to_project_hint(
1941 message_hint: proto::InlayHint,
1942 project: &ModelHandle<Project>,
1943 cx: &mut AsyncAppContext,
1944 ) -> anyhow::Result<InlayHint> {
1945 let buffer_id = message_hint
1946 .position
1947 .as_ref()
1948 .and_then(|location| location.buffer_id)
1949 .context("missing buffer id")?;
1950 let resolve_state = message_hint.resolve_state.as_ref().unwrap_or_else(|| {
1951 panic!("incorrect proto inlay hint message: no resolve state in hint {message_hint:?}",)
1952 });
1953 let resolve_state_data = resolve_state
1954 .lsp_resolve_state.as_ref()
1955 .map(|lsp_resolve_state| {
1956 serde_json::from_str::<Option<lsp::LSPAny>>(&lsp_resolve_state.value)
1957 .with_context(|| format!("incorrect proto inlay hint message: non-json resolve state {lsp_resolve_state:?}"))
1958 .map(|state| (LanguageServerId(lsp_resolve_state.server_id as usize), state))
1959 })
1960 .transpose()?;
1961 let resolve_state = match resolve_state.state {
1962 0 => ResolveState::Resolved,
1963 1 => {
1964 let (server_id, lsp_resolve_state) = resolve_state_data.with_context(|| {
1965 format!(
1966 "No lsp resolve data for the hint that can be resolved: {message_hint:?}"
1967 )
1968 })?;
1969 ResolveState::CanResolve(server_id, lsp_resolve_state)
1970 }
1971 2 => ResolveState::Resolving,
1972 invalid => {
1973 anyhow::bail!("Unexpected resolve state {invalid} for hint {message_hint:?}")
1974 }
1975 };
1976 Ok(InlayHint {
1977 position: message_hint
1978 .position
1979 .and_then(language::proto::deserialize_anchor)
1980 .context("invalid position")?,
1981 label: match message_hint
1982 .label
1983 .and_then(|label| label.label)
1984 .context("missing label")?
1985 {
1986 proto::inlay_hint_label::Label::Value(s) => InlayHintLabel::String(s),
1987 proto::inlay_hint_label::Label::LabelParts(parts) => {
1988 let mut label_parts = Vec::new();
1989 for part in parts.parts {
1990 let buffer = project
1991 .update(cx, |this, cx| this.wait_for_remote_buffer(buffer_id, cx))
1992 .await?;
1993 label_parts.push(InlayHintLabelPart {
1994 value: part.value,
1995 tooltip: part.tooltip.map(|tooltip| match tooltip.content {
1996 Some(proto::inlay_hint_label_part_tooltip::Content::Value(s)) => {
1997 InlayHintLabelPartTooltip::String(s)
1998 }
1999 Some(
2000 proto::inlay_hint_label_part_tooltip::Content::MarkupContent(
2001 markup_content,
2002 ),
2003 ) => InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2004 kind: if markup_content.is_markdown {
2005 HoverBlockKind::Markdown
2006 } else {
2007 HoverBlockKind::PlainText
2008 },
2009 value: markup_content.value,
2010 }),
2011 None => InlayHintLabelPartTooltip::String(String::new()),
2012 }),
2013 location: match part.location {
2014 Some(location) => Some(Location {
2015 range: location
2016 .start
2017 .and_then(language::proto::deserialize_anchor)
2018 .context("invalid start")?
2019 ..location
2020 .end
2021 .and_then(language::proto::deserialize_anchor)
2022 .context("invalid end")?,
2023 buffer,
2024 }),
2025 None => None,
2026 },
2027 });
2028 }
2029
2030 InlayHintLabel::LabelParts(label_parts)
2031 }
2032 },
2033 padding_left: message_hint.padding_left,
2034 padding_right: message_hint.padding_right,
2035 kind: message_hint
2036 .kind
2037 .as_deref()
2038 .and_then(InlayHintKind::from_name),
2039 tooltip: message_hint.tooltip.and_then(|tooltip| {
2040 Some(match tooltip.content? {
2041 proto::inlay_hint_tooltip::Content::Value(s) => InlayHintTooltip::String(s),
2042 proto::inlay_hint_tooltip::Content::MarkupContent(markup_content) => {
2043 InlayHintTooltip::MarkupContent(MarkupContent {
2044 kind: if markup_content.is_markdown {
2045 HoverBlockKind::Markdown
2046 } else {
2047 HoverBlockKind::PlainText
2048 },
2049 value: markup_content.value,
2050 })
2051 }
2052 })
2053 }),
2054 resolve_state,
2055 })
2056 }
2057
2058 // TODO kb instead, store all LSP data inside the project::InlayHint?
2059 pub fn project_to_lsp_hint(
2060 hint: InlayHint,
2061 project: &ModelHandle<Project>,
2062 snapshot: &BufferSnapshot,
2063 cx: &AsyncAppContext,
2064 ) -> lsp::InlayHint {
2065 lsp::InlayHint {
2066 position: point_to_lsp(hint.position.to_point_utf16(snapshot)),
2067 kind: hint.kind.map(|kind| match kind {
2068 InlayHintKind::Type => lsp::InlayHintKind::TYPE,
2069 InlayHintKind::Parameter => lsp::InlayHintKind::PARAMETER,
2070 }),
2071 text_edits: None,
2072 tooltip: hint.tooltip.and_then(|tooltip| {
2073 Some(match tooltip {
2074 InlayHintTooltip::String(s) => lsp::InlayHintTooltip::String(s),
2075 InlayHintTooltip::MarkupContent(markup_content) => {
2076 lsp::InlayHintTooltip::MarkupContent(lsp::MarkupContent {
2077 kind: match markup_content.kind {
2078 HoverBlockKind::PlainText => lsp::MarkupKind::PlainText,
2079 HoverBlockKind::Markdown => lsp::MarkupKind::Markdown,
2080 HoverBlockKind::Code { .. } => return None,
2081 },
2082 value: markup_content.value,
2083 })
2084 }
2085 })
2086 }),
2087 label: match hint.label {
2088 InlayHintLabel::String(s) => lsp::InlayHintLabel::String(s),
2089 InlayHintLabel::LabelParts(label_parts) => lsp::InlayHintLabel::LabelParts(
2090 label_parts
2091 .into_iter()
2092 .map(|part| lsp::InlayHintLabelPart {
2093 value: part.value,
2094 tooltip: part.tooltip.and_then(|tooltip| {
2095 Some(match tooltip {
2096 InlayHintLabelPartTooltip::String(s) => {
2097 lsp::InlayHintLabelPartTooltip::String(s)
2098 }
2099 InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2100 lsp::InlayHintLabelPartTooltip::MarkupContent(
2101 lsp::MarkupContent {
2102 kind: match markup_content.kind {
2103 HoverBlockKind::PlainText => {
2104 lsp::MarkupKind::PlainText
2105 }
2106 HoverBlockKind::Markdown => {
2107 lsp::MarkupKind::Markdown
2108 }
2109 HoverBlockKind::Code { .. } => return None,
2110 },
2111 value: markup_content.value,
2112 },
2113 )
2114 }
2115 })
2116 }),
2117 location: part.location.and_then(|location| {
2118 let path = cx.read(|cx| {
2119 let project_path = location.buffer.read(cx).project_path(cx)?;
2120 project.read(cx).absolute_path(&project_path, cx)
2121 })?;
2122 Some(lsp::Location::new(
2123 lsp::Url::from_file_path(path).unwrap(),
2124 range_to_lsp(
2125 location.range.start.to_point_utf16(snapshot)
2126 ..location.range.end.to_point_utf16(snapshot),
2127 ),
2128 ))
2129 }),
2130 command: None,
2131 })
2132 .collect(),
2133 ),
2134 },
2135 padding_left: Some(hint.padding_left),
2136 padding_right: Some(hint.padding_right),
2137 data: match hint.resolve_state {
2138 ResolveState::CanResolve(_, data) => data,
2139 ResolveState::Resolving | ResolveState::Resolved => None,
2140 },
2141 }
2142 }
2143}
2144
2145#[async_trait(?Send)]
2146impl LspCommand for InlayHints {
2147 type Response = Vec<InlayHint>;
2148 type LspRequest = lsp::InlayHintRequest;
2149 type ProtoRequest = proto::InlayHints;
2150
2151 fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
2152 let Some(inlay_hint_provider) = &server_capabilities.inlay_hint_provider else { return false };
2153 match inlay_hint_provider {
2154 lsp::OneOf::Left(enabled) => *enabled,
2155 lsp::OneOf::Right(inlay_hint_capabilities) => match inlay_hint_capabilities {
2156 lsp::InlayHintServerCapabilities::Options(_) => true,
2157 lsp::InlayHintServerCapabilities::RegistrationOptions(_) => false,
2158 },
2159 }
2160 }
2161
2162 fn to_lsp(
2163 &self,
2164 path: &Path,
2165 buffer: &Buffer,
2166 _: &Arc<LanguageServer>,
2167 _: &AppContext,
2168 ) -> lsp::InlayHintParams {
2169 lsp::InlayHintParams {
2170 text_document: lsp::TextDocumentIdentifier {
2171 uri: lsp::Url::from_file_path(path).unwrap(),
2172 },
2173 range: range_to_lsp(self.range.to_point_utf16(buffer)),
2174 work_done_progress_params: Default::default(),
2175 }
2176 }
2177
2178 async fn response_from_lsp(
2179 self,
2180 message: Option<Vec<lsp::InlayHint>>,
2181 project: ModelHandle<Project>,
2182 buffer: ModelHandle<Buffer>,
2183 server_id: LanguageServerId,
2184 mut cx: AsyncAppContext,
2185 ) -> Result<Vec<InlayHint>> {
2186 let (lsp_adapter, lsp_server) =
2187 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
2188 // `typescript-language-server` adds padding to the left for type hints, turning
2189 // `const foo: boolean` into `const foo : boolean` which looks odd.
2190 // `rust-analyzer` does not have the padding for this case, and we have to accomodate both.
2191 //
2192 // We could trim the whole string, but being pessimistic on par with the situation above,
2193 // there might be a hint with multiple whitespaces at the end(s) which we need to display properly.
2194 // Hence let's use a heuristic first to handle the most awkward case and look for more.
2195 let force_no_type_left_padding =
2196 lsp_adapter.name.0.as_ref() == "typescript-language-server";
2197 cx.read(|cx| {
2198 Ok(message
2199 .unwrap_or_default()
2200 .into_iter()
2201 .map(|lsp_hint| {
2202 let resolve_state = match lsp_server.capabilities().inlay_hint_provider {
2203 Some(lsp::OneOf::Right(lsp::InlayHintServerCapabilities::Options(
2204 lsp::InlayHintOptions {
2205 resolve_provider: Some(true),
2206 ..
2207 },
2208 ))) => {
2209 ResolveState::CanResolve(lsp_server.server_id(), lsp_hint.data.clone())
2210 }
2211 _ => ResolveState::Resolved,
2212 };
2213 InlayHints::lsp_to_project_hint(
2214 lsp_hint,
2215 &buffer,
2216 resolve_state,
2217 force_no_type_left_padding,
2218 cx,
2219 )
2220 })
2221 .collect())
2222 })
2223 }
2224
2225 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints {
2226 proto::InlayHints {
2227 project_id,
2228 buffer_id: buffer.remote_id(),
2229 start: Some(language::proto::serialize_anchor(&self.range.start)),
2230 end: Some(language::proto::serialize_anchor(&self.range.end)),
2231 version: serialize_version(&buffer.version()),
2232 }
2233 }
2234
2235 async fn from_proto(
2236 message: proto::InlayHints,
2237 _: ModelHandle<Project>,
2238 buffer: ModelHandle<Buffer>,
2239 mut cx: AsyncAppContext,
2240 ) -> Result<Self> {
2241 let start = message
2242 .start
2243 .and_then(language::proto::deserialize_anchor)
2244 .context("invalid start")?;
2245 let end = message
2246 .end
2247 .and_then(language::proto::deserialize_anchor)
2248 .context("invalid end")?;
2249 buffer
2250 .update(&mut cx, |buffer, _| {
2251 buffer.wait_for_version(deserialize_version(&message.version))
2252 })
2253 .await?;
2254
2255 Ok(Self { range: start..end })
2256 }
2257
2258 fn response_to_proto(
2259 response: Vec<InlayHint>,
2260 _: &mut Project,
2261 _: PeerId,
2262 buffer_version: &clock::Global,
2263 cx: &mut AppContext,
2264 ) -> proto::InlayHintsResponse {
2265 proto::InlayHintsResponse {
2266 hints: response
2267 .into_iter()
2268 .map(|response_hint| InlayHints::project_to_proto_hint(response_hint, cx))
2269 .collect(),
2270 version: serialize_version(buffer_version),
2271 }
2272 }
2273
2274 async fn response_from_proto(
2275 self,
2276 message: proto::InlayHintsResponse,
2277 project: ModelHandle<Project>,
2278 buffer: ModelHandle<Buffer>,
2279 mut cx: AsyncAppContext,
2280 ) -> anyhow::Result<Vec<InlayHint>> {
2281 buffer
2282 .update(&mut cx, |buffer, _| {
2283 buffer.wait_for_version(deserialize_version(&message.version))
2284 })
2285 .await?;
2286
2287 let mut hints = Vec::new();
2288 for message_hint in message.hints {
2289 hints.push(InlayHints::proto_to_project_hint(message_hint, &project, &mut cx).await?);
2290 }
2291
2292 Ok(hints)
2293 }
2294
2295 fn buffer_id_from_proto(message: &proto::InlayHints) -> u64 {
2296 message.buffer_id
2297 }
2298}