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