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