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