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 to_lsp(
900 &self,
901 path: &Path,
902 _: &Buffer,
903 _: &Arc<LanguageServer>,
904 _: &AppContext,
905 ) -> lsp::ReferenceParams {
906 lsp::ReferenceParams {
907 text_document_position: lsp::TextDocumentPositionParams {
908 text_document: lsp::TextDocumentIdentifier {
909 uri: lsp::Url::from_file_path(path).unwrap(),
910 },
911 position: point_to_lsp(self.position),
912 },
913 work_done_progress_params: Default::default(),
914 partial_result_params: Default::default(),
915 context: lsp::ReferenceContext {
916 include_declaration: true,
917 },
918 }
919 }
920
921 async fn response_from_lsp(
922 self,
923 locations: Option<Vec<lsp::Location>>,
924 project: Model<Project>,
925 buffer: Model<Buffer>,
926 server_id: LanguageServerId,
927 mut cx: AsyncAppContext,
928 ) -> Result<Vec<Location>> {
929 let mut references = Vec::new();
930 let (lsp_adapter, language_server) =
931 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
932
933 if let Some(locations) = locations {
934 for lsp_location in locations {
935 let target_buffer_handle = project
936 .update(&mut cx, |this, cx| {
937 this.open_local_buffer_via_lsp(
938 lsp_location.uri,
939 language_server.server_id(),
940 lsp_adapter.name.clone(),
941 cx,
942 )
943 })?
944 .await?;
945
946 target_buffer_handle
947 .clone()
948 .update(&mut cx, |target_buffer, _| {
949 let target_start = target_buffer
950 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
951 let target_end = target_buffer
952 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
953 references.push(Location {
954 buffer: target_buffer_handle,
955 range: target_buffer.anchor_after(target_start)
956 ..target_buffer.anchor_before(target_end),
957 });
958 })?;
959 }
960 }
961
962 Ok(references)
963 }
964
965 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetReferences {
966 proto::GetReferences {
967 project_id,
968 buffer_id: buffer.remote_id().into(),
969 position: Some(language::proto::serialize_anchor(
970 &buffer.anchor_before(self.position),
971 )),
972 version: serialize_version(&buffer.version()),
973 }
974 }
975
976 async fn from_proto(
977 message: proto::GetReferences,
978 _: Model<Project>,
979 buffer: Model<Buffer>,
980 mut cx: AsyncAppContext,
981 ) -> Result<Self> {
982 let position = message
983 .position
984 .and_then(deserialize_anchor)
985 .ok_or_else(|| anyhow!("invalid position"))?;
986 buffer
987 .update(&mut cx, |buffer, _| {
988 buffer.wait_for_version(deserialize_version(&message.version))
989 })?
990 .await?;
991 Ok(Self {
992 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
993 })
994 }
995
996 fn response_to_proto(
997 response: Vec<Location>,
998 project: &mut Project,
999 peer_id: PeerId,
1000 _: &clock::Global,
1001 cx: &mut AppContext,
1002 ) -> proto::GetReferencesResponse {
1003 let locations = response
1004 .into_iter()
1005 .map(|definition| {
1006 let buffer_id = project.create_buffer_for_peer(&definition.buffer, peer_id, cx);
1007 proto::Location {
1008 start: Some(serialize_anchor(&definition.range.start)),
1009 end: Some(serialize_anchor(&definition.range.end)),
1010 buffer_id: buffer_id.into(),
1011 }
1012 })
1013 .collect();
1014 proto::GetReferencesResponse { locations }
1015 }
1016
1017 async fn response_from_proto(
1018 self,
1019 message: proto::GetReferencesResponse,
1020 project: Model<Project>,
1021 _: Model<Buffer>,
1022 mut cx: AsyncAppContext,
1023 ) -> Result<Vec<Location>> {
1024 let mut locations = Vec::new();
1025 for location in message.locations {
1026 let buffer_id = BufferId::new(location.buffer_id)?;
1027 let target_buffer = project
1028 .update(&mut cx, |this, cx| {
1029 this.wait_for_remote_buffer(buffer_id, cx)
1030 })?
1031 .await?;
1032 let start = location
1033 .start
1034 .and_then(deserialize_anchor)
1035 .ok_or_else(|| anyhow!("missing target start"))?;
1036 let end = location
1037 .end
1038 .and_then(deserialize_anchor)
1039 .ok_or_else(|| anyhow!("missing target end"))?;
1040 target_buffer
1041 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
1042 .await?;
1043 locations.push(Location {
1044 buffer: target_buffer,
1045 range: start..end,
1046 })
1047 }
1048 Ok(locations)
1049 }
1050
1051 fn buffer_id_from_proto(message: &proto::GetReferences) -> Result<BufferId> {
1052 BufferId::new(message.buffer_id)
1053 }
1054}
1055
1056#[async_trait(?Send)]
1057impl LspCommand for GetDocumentHighlights {
1058 type Response = Vec<DocumentHighlight>;
1059 type LspRequest = lsp::request::DocumentHighlightRequest;
1060 type ProtoRequest = proto::GetDocumentHighlights;
1061
1062 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
1063 capabilities.document_highlight_provider.is_some()
1064 }
1065
1066 fn to_lsp(
1067 &self,
1068 path: &Path,
1069 _: &Buffer,
1070 _: &Arc<LanguageServer>,
1071 _: &AppContext,
1072 ) -> lsp::DocumentHighlightParams {
1073 lsp::DocumentHighlightParams {
1074 text_document_position_params: lsp::TextDocumentPositionParams {
1075 text_document: lsp::TextDocumentIdentifier {
1076 uri: lsp::Url::from_file_path(path).unwrap(),
1077 },
1078 position: point_to_lsp(self.position),
1079 },
1080 work_done_progress_params: Default::default(),
1081 partial_result_params: Default::default(),
1082 }
1083 }
1084
1085 async fn response_from_lsp(
1086 self,
1087 lsp_highlights: Option<Vec<lsp::DocumentHighlight>>,
1088 _: Model<Project>,
1089 buffer: Model<Buffer>,
1090 _: LanguageServerId,
1091 mut cx: AsyncAppContext,
1092 ) -> Result<Vec<DocumentHighlight>> {
1093 buffer.update(&mut cx, |buffer, _| {
1094 let mut lsp_highlights = lsp_highlights.unwrap_or_default();
1095 lsp_highlights.sort_unstable_by_key(|h| (h.range.start, Reverse(h.range.end)));
1096 lsp_highlights
1097 .into_iter()
1098 .map(|lsp_highlight| {
1099 let start = buffer
1100 .clip_point_utf16(point_from_lsp(lsp_highlight.range.start), Bias::Left);
1101 let end = buffer
1102 .clip_point_utf16(point_from_lsp(lsp_highlight.range.end), Bias::Left);
1103 DocumentHighlight {
1104 range: buffer.anchor_after(start)..buffer.anchor_before(end),
1105 kind: lsp_highlight
1106 .kind
1107 .unwrap_or(lsp::DocumentHighlightKind::READ),
1108 }
1109 })
1110 .collect()
1111 })
1112 }
1113
1114 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentHighlights {
1115 proto::GetDocumentHighlights {
1116 project_id,
1117 buffer_id: buffer.remote_id().into(),
1118 position: Some(language::proto::serialize_anchor(
1119 &buffer.anchor_before(self.position),
1120 )),
1121 version: serialize_version(&buffer.version()),
1122 }
1123 }
1124
1125 async fn from_proto(
1126 message: proto::GetDocumentHighlights,
1127 _: Model<Project>,
1128 buffer: Model<Buffer>,
1129 mut cx: AsyncAppContext,
1130 ) -> Result<Self> {
1131 let position = message
1132 .position
1133 .and_then(deserialize_anchor)
1134 .ok_or_else(|| anyhow!("invalid position"))?;
1135 buffer
1136 .update(&mut cx, |buffer, _| {
1137 buffer.wait_for_version(deserialize_version(&message.version))
1138 })?
1139 .await?;
1140 Ok(Self {
1141 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
1142 })
1143 }
1144
1145 fn response_to_proto(
1146 response: Vec<DocumentHighlight>,
1147 _: &mut Project,
1148 _: PeerId,
1149 _: &clock::Global,
1150 _: &mut AppContext,
1151 ) -> proto::GetDocumentHighlightsResponse {
1152 let highlights = response
1153 .into_iter()
1154 .map(|highlight| proto::DocumentHighlight {
1155 start: Some(serialize_anchor(&highlight.range.start)),
1156 end: Some(serialize_anchor(&highlight.range.end)),
1157 kind: match highlight.kind {
1158 DocumentHighlightKind::TEXT => proto::document_highlight::Kind::Text.into(),
1159 DocumentHighlightKind::WRITE => proto::document_highlight::Kind::Write.into(),
1160 DocumentHighlightKind::READ => proto::document_highlight::Kind::Read.into(),
1161 _ => proto::document_highlight::Kind::Text.into(),
1162 },
1163 })
1164 .collect();
1165 proto::GetDocumentHighlightsResponse { highlights }
1166 }
1167
1168 async fn response_from_proto(
1169 self,
1170 message: proto::GetDocumentHighlightsResponse,
1171 _: Model<Project>,
1172 buffer: Model<Buffer>,
1173 mut cx: AsyncAppContext,
1174 ) -> Result<Vec<DocumentHighlight>> {
1175 let mut highlights = Vec::new();
1176 for highlight in message.highlights {
1177 let start = highlight
1178 .start
1179 .and_then(deserialize_anchor)
1180 .ok_or_else(|| anyhow!("missing target start"))?;
1181 let end = highlight
1182 .end
1183 .and_then(deserialize_anchor)
1184 .ok_or_else(|| anyhow!("missing target end"))?;
1185 buffer
1186 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
1187 .await?;
1188 let kind = match proto::document_highlight::Kind::from_i32(highlight.kind) {
1189 Some(proto::document_highlight::Kind::Text) => DocumentHighlightKind::TEXT,
1190 Some(proto::document_highlight::Kind::Read) => DocumentHighlightKind::READ,
1191 Some(proto::document_highlight::Kind::Write) => DocumentHighlightKind::WRITE,
1192 None => DocumentHighlightKind::TEXT,
1193 };
1194 highlights.push(DocumentHighlight {
1195 range: start..end,
1196 kind,
1197 });
1198 }
1199 Ok(highlights)
1200 }
1201
1202 fn buffer_id_from_proto(message: &proto::GetDocumentHighlights) -> Result<BufferId> {
1203 BufferId::new(message.buffer_id)
1204 }
1205}
1206
1207#[async_trait(?Send)]
1208impl LspCommand for GetHover {
1209 type Response = Option<Hover>;
1210 type LspRequest = lsp::request::HoverRequest;
1211 type ProtoRequest = proto::GetHover;
1212
1213 fn to_lsp(
1214 &self,
1215 path: &Path,
1216 _: &Buffer,
1217 _: &Arc<LanguageServer>,
1218 _: &AppContext,
1219 ) -> lsp::HoverParams {
1220 lsp::HoverParams {
1221 text_document_position_params: lsp::TextDocumentPositionParams {
1222 text_document: lsp::TextDocumentIdentifier {
1223 uri: lsp::Url::from_file_path(path).unwrap(),
1224 },
1225 position: point_to_lsp(self.position),
1226 },
1227 work_done_progress_params: Default::default(),
1228 }
1229 }
1230
1231 async fn response_from_lsp(
1232 self,
1233 message: Option<lsp::Hover>,
1234 _: Model<Project>,
1235 buffer: Model<Buffer>,
1236 _: LanguageServerId,
1237 mut cx: AsyncAppContext,
1238 ) -> Result<Self::Response> {
1239 let Some(hover) = message else {
1240 return Ok(None);
1241 };
1242
1243 let (language, range) = buffer.update(&mut cx, |buffer, _| {
1244 (
1245 buffer.language().cloned(),
1246 hover.range.map(|range| {
1247 let token_start =
1248 buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left);
1249 let token_end = buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left);
1250 buffer.anchor_after(token_start)..buffer.anchor_before(token_end)
1251 }),
1252 )
1253 })?;
1254
1255 fn hover_blocks_from_marked_string(marked_string: lsp::MarkedString) -> Option<HoverBlock> {
1256 let block = match marked_string {
1257 lsp::MarkedString::String(content) => HoverBlock {
1258 text: content,
1259 kind: HoverBlockKind::Markdown,
1260 },
1261 lsp::MarkedString::LanguageString(lsp::LanguageString { language, value }) => {
1262 HoverBlock {
1263 text: value,
1264 kind: HoverBlockKind::Code { language },
1265 }
1266 }
1267 };
1268 if block.text.is_empty() {
1269 None
1270 } else {
1271 Some(block)
1272 }
1273 }
1274
1275 let contents = match hover.contents {
1276 lsp::HoverContents::Scalar(marked_string) => {
1277 hover_blocks_from_marked_string(marked_string)
1278 .into_iter()
1279 .collect()
1280 }
1281 lsp::HoverContents::Array(marked_strings) => marked_strings
1282 .into_iter()
1283 .filter_map(hover_blocks_from_marked_string)
1284 .collect(),
1285 lsp::HoverContents::Markup(markup_content) => vec![HoverBlock {
1286 text: markup_content.value,
1287 kind: if markup_content.kind == lsp::MarkupKind::Markdown {
1288 HoverBlockKind::Markdown
1289 } else {
1290 HoverBlockKind::PlainText
1291 },
1292 }],
1293 };
1294
1295 Ok(Some(Hover {
1296 contents,
1297 range,
1298 language,
1299 }))
1300 }
1301
1302 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
1303 proto::GetHover {
1304 project_id,
1305 buffer_id: buffer.remote_id().into(),
1306 position: Some(language::proto::serialize_anchor(
1307 &buffer.anchor_before(self.position),
1308 )),
1309 version: serialize_version(&buffer.version),
1310 }
1311 }
1312
1313 async fn from_proto(
1314 message: Self::ProtoRequest,
1315 _: Model<Project>,
1316 buffer: Model<Buffer>,
1317 mut cx: AsyncAppContext,
1318 ) -> Result<Self> {
1319 let position = message
1320 .position
1321 .and_then(deserialize_anchor)
1322 .ok_or_else(|| anyhow!("invalid position"))?;
1323 buffer
1324 .update(&mut cx, |buffer, _| {
1325 buffer.wait_for_version(deserialize_version(&message.version))
1326 })?
1327 .await?;
1328 Ok(Self {
1329 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
1330 })
1331 }
1332
1333 fn response_to_proto(
1334 response: Self::Response,
1335 _: &mut Project,
1336 _: PeerId,
1337 _: &clock::Global,
1338 _: &mut AppContext,
1339 ) -> proto::GetHoverResponse {
1340 if let Some(response) = response {
1341 let (start, end) = if let Some(range) = response.range {
1342 (
1343 Some(language::proto::serialize_anchor(&range.start)),
1344 Some(language::proto::serialize_anchor(&range.end)),
1345 )
1346 } else {
1347 (None, None)
1348 };
1349
1350 let contents = response
1351 .contents
1352 .into_iter()
1353 .map(|block| proto::HoverBlock {
1354 text: block.text,
1355 is_markdown: block.kind == HoverBlockKind::Markdown,
1356 language: if let HoverBlockKind::Code { language } = block.kind {
1357 Some(language)
1358 } else {
1359 None
1360 },
1361 })
1362 .collect();
1363
1364 proto::GetHoverResponse {
1365 start,
1366 end,
1367 contents,
1368 }
1369 } else {
1370 proto::GetHoverResponse {
1371 start: None,
1372 end: None,
1373 contents: Vec::new(),
1374 }
1375 }
1376 }
1377
1378 async fn response_from_proto(
1379 self,
1380 message: proto::GetHoverResponse,
1381 _: Model<Project>,
1382 buffer: Model<Buffer>,
1383 mut cx: AsyncAppContext,
1384 ) -> Result<Self::Response> {
1385 let contents: Vec<_> = message
1386 .contents
1387 .into_iter()
1388 .map(|block| HoverBlock {
1389 text: block.text,
1390 kind: if let Some(language) = block.language {
1391 HoverBlockKind::Code { language }
1392 } else if block.is_markdown {
1393 HoverBlockKind::Markdown
1394 } else {
1395 HoverBlockKind::PlainText
1396 },
1397 })
1398 .collect();
1399 if contents.is_empty() {
1400 return Ok(None);
1401 }
1402
1403 let language = buffer.update(&mut cx, |buffer, _| buffer.language().cloned())?;
1404 let range = if let (Some(start), Some(end)) = (message.start, message.end) {
1405 language::proto::deserialize_anchor(start)
1406 .and_then(|start| language::proto::deserialize_anchor(end).map(|end| start..end))
1407 } else {
1408 None
1409 };
1410 if let Some(range) = range.as_ref() {
1411 buffer
1412 .update(&mut cx, |buffer, _| {
1413 buffer.wait_for_anchors([range.start, range.end])
1414 })?
1415 .await?;
1416 }
1417
1418 Ok(Some(Hover {
1419 contents,
1420 range,
1421 language,
1422 }))
1423 }
1424
1425 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> Result<BufferId> {
1426 BufferId::new(message.buffer_id)
1427 }
1428}
1429
1430#[async_trait(?Send)]
1431impl LspCommand for GetCompletions {
1432 type Response = Vec<Completion>;
1433 type LspRequest = lsp::request::Completion;
1434 type ProtoRequest = proto::GetCompletions;
1435
1436 fn to_lsp(
1437 &self,
1438 path: &Path,
1439 _: &Buffer,
1440 _: &Arc<LanguageServer>,
1441 _: &AppContext,
1442 ) -> lsp::CompletionParams {
1443 lsp::CompletionParams {
1444 text_document_position: lsp::TextDocumentPositionParams::new(
1445 lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
1446 point_to_lsp(self.position),
1447 ),
1448 context: Default::default(),
1449 work_done_progress_params: Default::default(),
1450 partial_result_params: Default::default(),
1451 }
1452 }
1453
1454 async fn response_from_lsp(
1455 self,
1456 completions: Option<lsp::CompletionResponse>,
1457 project: Model<Project>,
1458 buffer: Model<Buffer>,
1459 server_id: LanguageServerId,
1460 mut cx: AsyncAppContext,
1461 ) -> Result<Vec<Completion>> {
1462 let mut response_list = None;
1463 let completions = if let Some(completions) = completions {
1464 match completions {
1465 lsp::CompletionResponse::Array(completions) => completions,
1466
1467 lsp::CompletionResponse::List(mut list) => {
1468 let items = std::mem::take(&mut list.items);
1469 response_list = Some(list);
1470 items
1471 }
1472 }
1473 } else {
1474 Default::default()
1475 };
1476
1477 let language_server_adapter = project
1478 .update(&mut cx, |project, _cx| {
1479 project.language_server_adapter_for_id(server_id)
1480 })?
1481 .ok_or_else(|| anyhow!("no such language server"))?;
1482
1483 let completions = buffer.update(&mut cx, |buffer, cx| {
1484 let language_registry = project.read(cx).languages().clone();
1485 let language = buffer.language().cloned();
1486 let snapshot = buffer.snapshot();
1487 let clipped_position = buffer.clip_point_utf16(Unclipped(self.position), Bias::Left);
1488
1489 let mut range_for_token = None;
1490 completions
1491 .into_iter()
1492 .filter_map(move |mut lsp_completion| {
1493 let (old_range, mut new_text) = match lsp_completion.text_edit.as_ref() {
1494 // If the language server provides a range to overwrite, then
1495 // check that the range is valid.
1496 Some(lsp::CompletionTextEdit::Edit(edit)) => {
1497 let range = range_from_lsp(edit.range);
1498 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1499 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1500 if start != range.start.0 || end != range.end.0 {
1501 log::info!("completion out of expected range");
1502 return None;
1503 }
1504 (
1505 snapshot.anchor_before(start)..snapshot.anchor_after(end),
1506 edit.new_text.clone(),
1507 )
1508 }
1509
1510 // If the language server does not provide a range, then infer
1511 // the range based on the syntax tree.
1512 None => {
1513 if self.position != clipped_position {
1514 log::info!("completion out of expected range");
1515 return None;
1516 }
1517
1518 let default_edit_range = response_list
1519 .as_ref()
1520 .and_then(|list| list.item_defaults.as_ref())
1521 .and_then(|defaults| defaults.edit_range.as_ref())
1522 .and_then(|range| match range {
1523 CompletionListItemDefaultsEditRange::Range(r) => Some(r),
1524 _ => None,
1525 });
1526
1527 let range = if let Some(range) = default_edit_range {
1528 let range = range_from_lsp(*range);
1529 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1530 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1531 if start != range.start.0 || end != range.end.0 {
1532 log::info!("completion out of expected range");
1533 return None;
1534 }
1535
1536 snapshot.anchor_before(start)..snapshot.anchor_after(end)
1537 } else {
1538 range_for_token
1539 .get_or_insert_with(|| {
1540 let offset = self.position.to_offset(&snapshot);
1541 let (range, kind) = snapshot.surrounding_word(offset);
1542 let range = if kind == Some(CharKind::Word) {
1543 range
1544 } else {
1545 offset..offset
1546 };
1547
1548 snapshot.anchor_before(range.start)
1549 ..snapshot.anchor_after(range.end)
1550 })
1551 .clone()
1552 };
1553
1554 let text = lsp_completion
1555 .insert_text
1556 .as_ref()
1557 .unwrap_or(&lsp_completion.label)
1558 .clone();
1559 (range, text)
1560 }
1561
1562 Some(lsp::CompletionTextEdit::InsertAndReplace(edit)) => {
1563 let range = range_from_lsp(edit.insert);
1564
1565 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1566 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1567 if start != range.start.0 || end != range.end.0 {
1568 log::info!("completion out of expected range");
1569 return None;
1570 }
1571 (
1572 snapshot.anchor_before(start)..snapshot.anchor_after(end),
1573 edit.new_text.clone(),
1574 )
1575 }
1576 };
1577
1578 let language_registry = language_registry.clone();
1579 let language = language.clone();
1580 let language_server_adapter = language_server_adapter.clone();
1581 LineEnding::normalize(&mut new_text);
1582 Some(async move {
1583 let mut label = None;
1584 if let Some(language) = &language {
1585 language_server_adapter
1586 .process_completion(&mut lsp_completion)
1587 .await;
1588 label = language_server_adapter
1589 .label_for_completion(&lsp_completion, language)
1590 .await;
1591 }
1592
1593 let documentation = if let Some(lsp_docs) = &lsp_completion.documentation {
1594 Some(
1595 prepare_completion_documentation(
1596 lsp_docs,
1597 &language_registry,
1598 language.clone(),
1599 )
1600 .await,
1601 )
1602 } else {
1603 None
1604 };
1605
1606 Completion {
1607 old_range,
1608 new_text,
1609 label: label.unwrap_or_else(|| {
1610 language::CodeLabel::plain(
1611 lsp_completion.label.clone(),
1612 lsp_completion.filter_text.as_deref(),
1613 )
1614 }),
1615 documentation,
1616 server_id,
1617 lsp_completion,
1618 }
1619 })
1620 })
1621 })?;
1622
1623 Ok(future::join_all(completions).await)
1624 }
1625
1626 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCompletions {
1627 let anchor = buffer.anchor_after(self.position);
1628 proto::GetCompletions {
1629 project_id,
1630 buffer_id: buffer.remote_id().into(),
1631 position: Some(language::proto::serialize_anchor(&anchor)),
1632 version: serialize_version(&buffer.version()),
1633 }
1634 }
1635
1636 async fn from_proto(
1637 message: proto::GetCompletions,
1638 _: Model<Project>,
1639 buffer: Model<Buffer>,
1640 mut cx: AsyncAppContext,
1641 ) -> Result<Self> {
1642 let version = deserialize_version(&message.version);
1643 buffer
1644 .update(&mut cx, |buffer, _| buffer.wait_for_version(version))?
1645 .await?;
1646 let position = message
1647 .position
1648 .and_then(language::proto::deserialize_anchor)
1649 .map(|p| {
1650 buffer.update(&mut cx, |buffer, _| {
1651 buffer.clip_point_utf16(Unclipped(p.to_point_utf16(buffer)), Bias::Left)
1652 })
1653 })
1654 .ok_or_else(|| anyhow!("invalid position"))??;
1655 Ok(Self { position })
1656 }
1657
1658 fn response_to_proto(
1659 completions: Vec<Completion>,
1660 _: &mut Project,
1661 _: PeerId,
1662 buffer_version: &clock::Global,
1663 _: &mut AppContext,
1664 ) -> proto::GetCompletionsResponse {
1665 proto::GetCompletionsResponse {
1666 completions: completions
1667 .iter()
1668 .map(language::proto::serialize_completion)
1669 .collect(),
1670 version: serialize_version(buffer_version),
1671 }
1672 }
1673
1674 async fn response_from_proto(
1675 self,
1676 message: proto::GetCompletionsResponse,
1677 project: Model<Project>,
1678 buffer: Model<Buffer>,
1679 mut cx: AsyncAppContext,
1680 ) -> Result<Vec<Completion>> {
1681 buffer
1682 .update(&mut cx, |buffer, _| {
1683 buffer.wait_for_version(deserialize_version(&message.version))
1684 })?
1685 .await?;
1686
1687 let language = buffer.update(&mut cx, |buffer, _| buffer.language().cloned())?;
1688 let language_registry = project.update(&mut cx, |project, _| project.languages.clone())?;
1689 let completions = message.completions.into_iter().map(|completion| {
1690 language::proto::deserialize_completion(
1691 completion,
1692 language.clone(),
1693 &language_registry,
1694 )
1695 });
1696 future::try_join_all(completions).await
1697 }
1698
1699 fn buffer_id_from_proto(message: &proto::GetCompletions) -> Result<BufferId> {
1700 BufferId::new(message.buffer_id)
1701 }
1702}
1703
1704#[async_trait(?Send)]
1705impl LspCommand for GetCodeActions {
1706 type Response = Vec<CodeAction>;
1707 type LspRequest = lsp::request::CodeActionRequest;
1708 type ProtoRequest = proto::GetCodeActions;
1709
1710 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
1711 match &capabilities.code_action_provider {
1712 None => false,
1713 Some(lsp::CodeActionProviderCapability::Simple(false)) => false,
1714 _ => true,
1715 }
1716 }
1717
1718 fn to_lsp(
1719 &self,
1720 path: &Path,
1721 buffer: &Buffer,
1722 language_server: &Arc<LanguageServer>,
1723 _: &AppContext,
1724 ) -> lsp::CodeActionParams {
1725 let relevant_diagnostics = buffer
1726 .snapshot()
1727 .diagnostics_in_range::<_, usize>(self.range.clone(), false)
1728 .map(|entry| entry.to_lsp_diagnostic_stub())
1729 .collect();
1730 lsp::CodeActionParams {
1731 text_document: lsp::TextDocumentIdentifier::new(
1732 lsp::Url::from_file_path(path).unwrap(),
1733 ),
1734 range: range_to_lsp(self.range.to_point_utf16(buffer)),
1735 work_done_progress_params: Default::default(),
1736 partial_result_params: Default::default(),
1737 context: lsp::CodeActionContext {
1738 diagnostics: relevant_diagnostics,
1739 only: self
1740 .kinds
1741 .clone()
1742 .or_else(|| language_server.code_action_kinds()),
1743 ..lsp::CodeActionContext::default()
1744 },
1745 }
1746 }
1747
1748 async fn response_from_lsp(
1749 self,
1750 actions: Option<lsp::CodeActionResponse>,
1751 _: Model<Project>,
1752 _: Model<Buffer>,
1753 server_id: LanguageServerId,
1754 _: AsyncAppContext,
1755 ) -> Result<Vec<CodeAction>> {
1756 Ok(actions
1757 .unwrap_or_default()
1758 .into_iter()
1759 .filter_map(|entry| {
1760 if let lsp::CodeActionOrCommand::CodeAction(lsp_action) = entry {
1761 Some(CodeAction {
1762 server_id,
1763 range: self.range.clone(),
1764 lsp_action,
1765 })
1766 } else {
1767 None
1768 }
1769 })
1770 .collect())
1771 }
1772
1773 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCodeActions {
1774 proto::GetCodeActions {
1775 project_id,
1776 buffer_id: buffer.remote_id().into(),
1777 start: Some(language::proto::serialize_anchor(&self.range.start)),
1778 end: Some(language::proto::serialize_anchor(&self.range.end)),
1779 version: serialize_version(&buffer.version()),
1780 }
1781 }
1782
1783 async fn from_proto(
1784 message: proto::GetCodeActions,
1785 _: Model<Project>,
1786 buffer: Model<Buffer>,
1787 mut cx: AsyncAppContext,
1788 ) -> Result<Self> {
1789 let start = message
1790 .start
1791 .and_then(language::proto::deserialize_anchor)
1792 .ok_or_else(|| anyhow!("invalid start"))?;
1793 let end = message
1794 .end
1795 .and_then(language::proto::deserialize_anchor)
1796 .ok_or_else(|| anyhow!("invalid end"))?;
1797 buffer
1798 .update(&mut cx, |buffer, _| {
1799 buffer.wait_for_version(deserialize_version(&message.version))
1800 })?
1801 .await?;
1802
1803 Ok(Self {
1804 range: start..end,
1805 kinds: None,
1806 })
1807 }
1808
1809 fn response_to_proto(
1810 code_actions: Vec<CodeAction>,
1811 _: &mut Project,
1812 _: PeerId,
1813 buffer_version: &clock::Global,
1814 _: &mut AppContext,
1815 ) -> proto::GetCodeActionsResponse {
1816 proto::GetCodeActionsResponse {
1817 actions: code_actions
1818 .iter()
1819 .map(language::proto::serialize_code_action)
1820 .collect(),
1821 version: serialize_version(buffer_version),
1822 }
1823 }
1824
1825 async fn response_from_proto(
1826 self,
1827 message: proto::GetCodeActionsResponse,
1828 _: Model<Project>,
1829 buffer: Model<Buffer>,
1830 mut cx: AsyncAppContext,
1831 ) -> Result<Vec<CodeAction>> {
1832 buffer
1833 .update(&mut cx, |buffer, _| {
1834 buffer.wait_for_version(deserialize_version(&message.version))
1835 })?
1836 .await?;
1837 message
1838 .actions
1839 .into_iter()
1840 .map(language::proto::deserialize_code_action)
1841 .collect()
1842 }
1843
1844 fn buffer_id_from_proto(message: &proto::GetCodeActions) -> Result<BufferId> {
1845 BufferId::new(message.buffer_id)
1846 }
1847}
1848
1849impl GetCodeActions {
1850 pub fn can_resolve_actions(capabilities: &ServerCapabilities) -> bool {
1851 capabilities
1852 .code_action_provider
1853 .as_ref()
1854 .and_then(|options| match options {
1855 lsp::CodeActionProviderCapability::Simple(_is_supported) => None,
1856 lsp::CodeActionProviderCapability::Options(options) => options.resolve_provider,
1857 })
1858 .unwrap_or(false)
1859 }
1860
1861 pub fn supports_code_actions(capabilities: &ServerCapabilities) -> bool {
1862 capabilities
1863 .code_action_provider
1864 .as_ref()
1865 .map(|options| match options {
1866 lsp::CodeActionProviderCapability::Simple(is_supported) => *is_supported,
1867 lsp::CodeActionProviderCapability::Options(_) => true,
1868 })
1869 .unwrap_or(false)
1870 }
1871}
1872
1873#[async_trait(?Send)]
1874impl LspCommand for OnTypeFormatting {
1875 type Response = Option<Transaction>;
1876 type LspRequest = lsp::request::OnTypeFormatting;
1877 type ProtoRequest = proto::OnTypeFormatting;
1878
1879 fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
1880 let Some(on_type_formatting_options) =
1881 &server_capabilities.document_on_type_formatting_provider
1882 else {
1883 return false;
1884 };
1885 on_type_formatting_options
1886 .first_trigger_character
1887 .contains(&self.trigger)
1888 || on_type_formatting_options
1889 .more_trigger_character
1890 .iter()
1891 .flatten()
1892 .any(|chars| chars.contains(&self.trigger))
1893 }
1894
1895 fn to_lsp(
1896 &self,
1897 path: &Path,
1898 _: &Buffer,
1899 _: &Arc<LanguageServer>,
1900 _: &AppContext,
1901 ) -> lsp::DocumentOnTypeFormattingParams {
1902 lsp::DocumentOnTypeFormattingParams {
1903 text_document_position: lsp::TextDocumentPositionParams::new(
1904 lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
1905 point_to_lsp(self.position),
1906 ),
1907 ch: self.trigger.clone(),
1908 options: lsp_formatting_options(self.options.tab_size),
1909 }
1910 }
1911
1912 async fn response_from_lsp(
1913 self,
1914 message: Option<Vec<lsp::TextEdit>>,
1915 project: Model<Project>,
1916 buffer: Model<Buffer>,
1917 server_id: LanguageServerId,
1918 mut cx: AsyncAppContext,
1919 ) -> Result<Option<Transaction>> {
1920 if let Some(edits) = message {
1921 let (lsp_adapter, lsp_server) =
1922 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
1923 Project::deserialize_edits(
1924 project,
1925 buffer,
1926 edits,
1927 self.push_to_history,
1928 lsp_adapter,
1929 lsp_server,
1930 &mut cx,
1931 )
1932 .await
1933 } else {
1934 Ok(None)
1935 }
1936 }
1937
1938 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::OnTypeFormatting {
1939 proto::OnTypeFormatting {
1940 project_id,
1941 buffer_id: buffer.remote_id().into(),
1942 position: Some(language::proto::serialize_anchor(
1943 &buffer.anchor_before(self.position),
1944 )),
1945 trigger: self.trigger.clone(),
1946 version: serialize_version(&buffer.version()),
1947 }
1948 }
1949
1950 async fn from_proto(
1951 message: proto::OnTypeFormatting,
1952 _: Model<Project>,
1953 buffer: Model<Buffer>,
1954 mut cx: AsyncAppContext,
1955 ) -> Result<Self> {
1956 let position = message
1957 .position
1958 .and_then(deserialize_anchor)
1959 .ok_or_else(|| anyhow!("invalid position"))?;
1960 buffer
1961 .update(&mut cx, |buffer, _| {
1962 buffer.wait_for_version(deserialize_version(&message.version))
1963 })?
1964 .await?;
1965
1966 let tab_size = buffer.update(&mut cx, |buffer, cx| {
1967 language_settings(buffer.language(), buffer.file(), cx).tab_size
1968 })?;
1969
1970 Ok(Self {
1971 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
1972 trigger: message.trigger.clone(),
1973 options: lsp_formatting_options(tab_size.get()).into(),
1974 push_to_history: false,
1975 })
1976 }
1977
1978 fn response_to_proto(
1979 response: Option<Transaction>,
1980 _: &mut Project,
1981 _: PeerId,
1982 _: &clock::Global,
1983 _: &mut AppContext,
1984 ) -> proto::OnTypeFormattingResponse {
1985 proto::OnTypeFormattingResponse {
1986 transaction: response
1987 .map(|transaction| language::proto::serialize_transaction(&transaction)),
1988 }
1989 }
1990
1991 async fn response_from_proto(
1992 self,
1993 message: proto::OnTypeFormattingResponse,
1994 _: Model<Project>,
1995 _: Model<Buffer>,
1996 _: AsyncAppContext,
1997 ) -> Result<Option<Transaction>> {
1998 let Some(transaction) = message.transaction else {
1999 return Ok(None);
2000 };
2001 Ok(Some(language::proto::deserialize_transaction(transaction)?))
2002 }
2003
2004 fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> Result<BufferId> {
2005 BufferId::new(message.buffer_id)
2006 }
2007}
2008
2009impl InlayHints {
2010 pub async fn lsp_to_project_hint(
2011 lsp_hint: lsp::InlayHint,
2012 buffer_handle: &Model<Buffer>,
2013 server_id: LanguageServerId,
2014 resolve_state: ResolveState,
2015 force_no_type_left_padding: bool,
2016 cx: &mut AsyncAppContext,
2017 ) -> anyhow::Result<InlayHint> {
2018 let kind = lsp_hint.kind.and_then(|kind| match kind {
2019 lsp::InlayHintKind::TYPE => Some(InlayHintKind::Type),
2020 lsp::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
2021 _ => None,
2022 });
2023
2024 let position = buffer_handle.update(cx, |buffer, _| {
2025 let position = buffer.clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
2026 if kind == Some(InlayHintKind::Parameter) {
2027 buffer.anchor_before(position)
2028 } else {
2029 buffer.anchor_after(position)
2030 }
2031 })?;
2032 let label = Self::lsp_inlay_label_to_project(lsp_hint.label, server_id)
2033 .await
2034 .context("lsp to project inlay hint conversion")?;
2035 let padding_left = if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
2036 false
2037 } else {
2038 lsp_hint.padding_left.unwrap_or(false)
2039 };
2040
2041 Ok(InlayHint {
2042 position,
2043 padding_left,
2044 padding_right: lsp_hint.padding_right.unwrap_or(false),
2045 label,
2046 kind,
2047 tooltip: lsp_hint.tooltip.map(|tooltip| match tooltip {
2048 lsp::InlayHintTooltip::String(s) => InlayHintTooltip::String(s),
2049 lsp::InlayHintTooltip::MarkupContent(markup_content) => {
2050 InlayHintTooltip::MarkupContent(MarkupContent {
2051 kind: match markup_content.kind {
2052 lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
2053 lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
2054 },
2055 value: markup_content.value,
2056 })
2057 }
2058 }),
2059 resolve_state,
2060 })
2061 }
2062
2063 async fn lsp_inlay_label_to_project(
2064 lsp_label: lsp::InlayHintLabel,
2065 server_id: LanguageServerId,
2066 ) -> anyhow::Result<InlayHintLabel> {
2067 let label = match lsp_label {
2068 lsp::InlayHintLabel::String(s) => InlayHintLabel::String(s),
2069 lsp::InlayHintLabel::LabelParts(lsp_parts) => {
2070 let mut parts = Vec::with_capacity(lsp_parts.len());
2071 for lsp_part in lsp_parts {
2072 parts.push(InlayHintLabelPart {
2073 value: lsp_part.value,
2074 tooltip: lsp_part.tooltip.map(|tooltip| match tooltip {
2075 lsp::InlayHintLabelPartTooltip::String(s) => {
2076 InlayHintLabelPartTooltip::String(s)
2077 }
2078 lsp::InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2079 InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2080 kind: match markup_content.kind {
2081 lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
2082 lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
2083 },
2084 value: markup_content.value,
2085 })
2086 }
2087 }),
2088 location: Some(server_id).zip(lsp_part.location),
2089 });
2090 }
2091 InlayHintLabel::LabelParts(parts)
2092 }
2093 };
2094
2095 Ok(label)
2096 }
2097
2098 pub fn project_to_proto_hint(response_hint: InlayHint) -> proto::InlayHint {
2099 let (state, lsp_resolve_state) = match response_hint.resolve_state {
2100 ResolveState::Resolved => (0, None),
2101 ResolveState::CanResolve(server_id, resolve_data) => (
2102 1,
2103 resolve_data
2104 .map(|json_data| {
2105 serde_json::to_string(&json_data)
2106 .expect("failed to serialize resolve json data")
2107 })
2108 .map(|value| proto::resolve_state::LspResolveState {
2109 server_id: server_id.0 as u64,
2110 value,
2111 }),
2112 ),
2113 ResolveState::Resolving => (2, None),
2114 };
2115 let resolve_state = Some(proto::ResolveState {
2116 state,
2117 lsp_resolve_state,
2118 });
2119 proto::InlayHint {
2120 position: Some(language::proto::serialize_anchor(&response_hint.position)),
2121 padding_left: response_hint.padding_left,
2122 padding_right: response_hint.padding_right,
2123 label: Some(proto::InlayHintLabel {
2124 label: Some(match response_hint.label {
2125 InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s),
2126 InlayHintLabel::LabelParts(label_parts) => {
2127 proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts {
2128 parts: label_parts.into_iter().map(|label_part| {
2129 let location_url = label_part.location.as_ref().map(|(_, location)| location.uri.to_string());
2130 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 });
2131 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 });
2132 proto::InlayHintLabelPart {
2133 value: label_part.value,
2134 tooltip: label_part.tooltip.map(|tooltip| {
2135 let proto_tooltip = match tooltip {
2136 InlayHintLabelPartTooltip::String(s) => proto::inlay_hint_label_part_tooltip::Content::Value(s),
2137 InlayHintLabelPartTooltip::MarkupContent(markup_content) => proto::inlay_hint_label_part_tooltip::Content::MarkupContent(proto::MarkupContent {
2138 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
2139 value: markup_content.value,
2140 }),
2141 };
2142 proto::InlayHintLabelPartTooltip {content: Some(proto_tooltip)}
2143 }),
2144 location_url,
2145 location_range_start,
2146 location_range_end,
2147 language_server_id: label_part.location.as_ref().map(|(server_id, _)| server_id.0 as u64),
2148 }}).collect()
2149 })
2150 }
2151 }),
2152 }),
2153 kind: response_hint.kind.map(|kind| kind.name().to_string()),
2154 tooltip: response_hint.tooltip.map(|response_tooltip| {
2155 let proto_tooltip = match response_tooltip {
2156 InlayHintTooltip::String(s) => proto::inlay_hint_tooltip::Content::Value(s),
2157 InlayHintTooltip::MarkupContent(markup_content) => {
2158 proto::inlay_hint_tooltip::Content::MarkupContent(proto::MarkupContent {
2159 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
2160 value: markup_content.value,
2161 })
2162 }
2163 };
2164 proto::InlayHintTooltip {
2165 content: Some(proto_tooltip),
2166 }
2167 }),
2168 resolve_state,
2169 }
2170 }
2171
2172 pub fn proto_to_project_hint(message_hint: proto::InlayHint) -> anyhow::Result<InlayHint> {
2173 let resolve_state = message_hint.resolve_state.as_ref().unwrap_or_else(|| {
2174 panic!("incorrect proto inlay hint message: no resolve state in hint {message_hint:?}",)
2175 });
2176 let resolve_state_data = resolve_state
2177 .lsp_resolve_state.as_ref()
2178 .map(|lsp_resolve_state| {
2179 serde_json::from_str::<Option<lsp::LSPAny>>(&lsp_resolve_state.value)
2180 .with_context(|| format!("incorrect proto inlay hint message: non-json resolve state {lsp_resolve_state:?}"))
2181 .map(|state| (LanguageServerId(lsp_resolve_state.server_id as usize), state))
2182 })
2183 .transpose()?;
2184 let resolve_state = match resolve_state.state {
2185 0 => ResolveState::Resolved,
2186 1 => {
2187 let (server_id, lsp_resolve_state) = resolve_state_data.with_context(|| {
2188 format!(
2189 "No lsp resolve data for the hint that can be resolved: {message_hint:?}"
2190 )
2191 })?;
2192 ResolveState::CanResolve(server_id, lsp_resolve_state)
2193 }
2194 2 => ResolveState::Resolving,
2195 invalid => {
2196 anyhow::bail!("Unexpected resolve state {invalid} for hint {message_hint:?}")
2197 }
2198 };
2199 Ok(InlayHint {
2200 position: message_hint
2201 .position
2202 .and_then(language::proto::deserialize_anchor)
2203 .context("invalid position")?,
2204 label: match message_hint
2205 .label
2206 .and_then(|label| label.label)
2207 .context("missing label")?
2208 {
2209 proto::inlay_hint_label::Label::Value(s) => InlayHintLabel::String(s),
2210 proto::inlay_hint_label::Label::LabelParts(parts) => {
2211 let mut label_parts = Vec::new();
2212 for part in parts.parts {
2213 label_parts.push(InlayHintLabelPart {
2214 value: part.value,
2215 tooltip: part.tooltip.map(|tooltip| match tooltip.content {
2216 Some(proto::inlay_hint_label_part_tooltip::Content::Value(s)) => {
2217 InlayHintLabelPartTooltip::String(s)
2218 }
2219 Some(
2220 proto::inlay_hint_label_part_tooltip::Content::MarkupContent(
2221 markup_content,
2222 ),
2223 ) => InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2224 kind: if markup_content.is_markdown {
2225 HoverBlockKind::Markdown
2226 } else {
2227 HoverBlockKind::PlainText
2228 },
2229 value: markup_content.value,
2230 }),
2231 None => InlayHintLabelPartTooltip::String(String::new()),
2232 }),
2233 location: {
2234 match part
2235 .location_url
2236 .zip(
2237 part.location_range_start.and_then(|start| {
2238 Some(start..part.location_range_end?)
2239 }),
2240 )
2241 .zip(part.language_server_id)
2242 {
2243 Some(((uri, range), server_id)) => Some((
2244 LanguageServerId(server_id as usize),
2245 lsp::Location {
2246 uri: lsp::Url::parse(&uri)
2247 .context("invalid uri in hint part {part:?}")?,
2248 range: lsp::Range::new(
2249 point_to_lsp(PointUtf16::new(
2250 range.start.row,
2251 range.start.column,
2252 )),
2253 point_to_lsp(PointUtf16::new(
2254 range.end.row,
2255 range.end.column,
2256 )),
2257 ),
2258 },
2259 )),
2260 None => None,
2261 }
2262 },
2263 });
2264 }
2265
2266 InlayHintLabel::LabelParts(label_parts)
2267 }
2268 },
2269 padding_left: message_hint.padding_left,
2270 padding_right: message_hint.padding_right,
2271 kind: message_hint
2272 .kind
2273 .as_deref()
2274 .and_then(InlayHintKind::from_name),
2275 tooltip: message_hint.tooltip.and_then(|tooltip| {
2276 Some(match tooltip.content? {
2277 proto::inlay_hint_tooltip::Content::Value(s) => InlayHintTooltip::String(s),
2278 proto::inlay_hint_tooltip::Content::MarkupContent(markup_content) => {
2279 InlayHintTooltip::MarkupContent(MarkupContent {
2280 kind: if markup_content.is_markdown {
2281 HoverBlockKind::Markdown
2282 } else {
2283 HoverBlockKind::PlainText
2284 },
2285 value: markup_content.value,
2286 })
2287 }
2288 })
2289 }),
2290 resolve_state,
2291 })
2292 }
2293
2294 pub fn project_to_lsp_hint(hint: InlayHint, snapshot: &BufferSnapshot) -> lsp::InlayHint {
2295 lsp::InlayHint {
2296 position: point_to_lsp(hint.position.to_point_utf16(snapshot)),
2297 kind: hint.kind.map(|kind| match kind {
2298 InlayHintKind::Type => lsp::InlayHintKind::TYPE,
2299 InlayHintKind::Parameter => lsp::InlayHintKind::PARAMETER,
2300 }),
2301 text_edits: None,
2302 tooltip: hint.tooltip.and_then(|tooltip| {
2303 Some(match tooltip {
2304 InlayHintTooltip::String(s) => lsp::InlayHintTooltip::String(s),
2305 InlayHintTooltip::MarkupContent(markup_content) => {
2306 lsp::InlayHintTooltip::MarkupContent(lsp::MarkupContent {
2307 kind: match markup_content.kind {
2308 HoverBlockKind::PlainText => lsp::MarkupKind::PlainText,
2309 HoverBlockKind::Markdown => lsp::MarkupKind::Markdown,
2310 HoverBlockKind::Code { .. } => return None,
2311 },
2312 value: markup_content.value,
2313 })
2314 }
2315 })
2316 }),
2317 label: match hint.label {
2318 InlayHintLabel::String(s) => lsp::InlayHintLabel::String(s),
2319 InlayHintLabel::LabelParts(label_parts) => lsp::InlayHintLabel::LabelParts(
2320 label_parts
2321 .into_iter()
2322 .map(|part| lsp::InlayHintLabelPart {
2323 value: part.value,
2324 tooltip: part.tooltip.and_then(|tooltip| {
2325 Some(match tooltip {
2326 InlayHintLabelPartTooltip::String(s) => {
2327 lsp::InlayHintLabelPartTooltip::String(s)
2328 }
2329 InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2330 lsp::InlayHintLabelPartTooltip::MarkupContent(
2331 lsp::MarkupContent {
2332 kind: match markup_content.kind {
2333 HoverBlockKind::PlainText => {
2334 lsp::MarkupKind::PlainText
2335 }
2336 HoverBlockKind::Markdown => {
2337 lsp::MarkupKind::Markdown
2338 }
2339 HoverBlockKind::Code { .. } => return None,
2340 },
2341 value: markup_content.value,
2342 },
2343 )
2344 }
2345 })
2346 }),
2347 location: part.location.map(|(_, location)| location),
2348 command: None,
2349 })
2350 .collect(),
2351 ),
2352 },
2353 padding_left: Some(hint.padding_left),
2354 padding_right: Some(hint.padding_right),
2355 data: match hint.resolve_state {
2356 ResolveState::CanResolve(_, data) => data,
2357 ResolveState::Resolving | ResolveState::Resolved => None,
2358 },
2359 }
2360 }
2361
2362 pub fn can_resolve_inlays(capabilities: &ServerCapabilities) -> bool {
2363 capabilities
2364 .inlay_hint_provider
2365 .as_ref()
2366 .and_then(|options| match options {
2367 OneOf::Left(_is_supported) => None,
2368 OneOf::Right(capabilities) => match capabilities {
2369 lsp::InlayHintServerCapabilities::Options(o) => o.resolve_provider,
2370 lsp::InlayHintServerCapabilities::RegistrationOptions(o) => {
2371 o.inlay_hint_options.resolve_provider
2372 }
2373 },
2374 })
2375 .unwrap_or(false)
2376 }
2377}
2378
2379#[async_trait(?Send)]
2380impl LspCommand for InlayHints {
2381 type Response = Vec<InlayHint>;
2382 type LspRequest = lsp::InlayHintRequest;
2383 type ProtoRequest = proto::InlayHints;
2384
2385 fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
2386 let Some(inlay_hint_provider) = &server_capabilities.inlay_hint_provider else {
2387 return false;
2388 };
2389 match inlay_hint_provider {
2390 lsp::OneOf::Left(enabled) => *enabled,
2391 lsp::OneOf::Right(inlay_hint_capabilities) => match inlay_hint_capabilities {
2392 lsp::InlayHintServerCapabilities::Options(_) => true,
2393 lsp::InlayHintServerCapabilities::RegistrationOptions(_) => false,
2394 },
2395 }
2396 }
2397
2398 fn to_lsp(
2399 &self,
2400 path: &Path,
2401 buffer: &Buffer,
2402 _: &Arc<LanguageServer>,
2403 _: &AppContext,
2404 ) -> lsp::InlayHintParams {
2405 lsp::InlayHintParams {
2406 text_document: lsp::TextDocumentIdentifier {
2407 uri: lsp::Url::from_file_path(path).unwrap(),
2408 },
2409 range: range_to_lsp(self.range.to_point_utf16(buffer)),
2410 work_done_progress_params: Default::default(),
2411 }
2412 }
2413
2414 async fn response_from_lsp(
2415 self,
2416 message: Option<Vec<lsp::InlayHint>>,
2417 project: Model<Project>,
2418 buffer: Model<Buffer>,
2419 server_id: LanguageServerId,
2420 mut cx: AsyncAppContext,
2421 ) -> anyhow::Result<Vec<InlayHint>> {
2422 let (lsp_adapter, lsp_server) =
2423 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
2424 // `typescript-language-server` adds padding to the left for type hints, turning
2425 // `const foo: boolean` into `const foo : boolean` which looks odd.
2426 // `rust-analyzer` does not have the padding for this case, and we have to accommodate both.
2427 //
2428 // We could trim the whole string, but being pessimistic on par with the situation above,
2429 // there might be a hint with multiple whitespaces at the end(s) which we need to display properly.
2430 // Hence let's use a heuristic first to handle the most awkward case and look for more.
2431 let force_no_type_left_padding =
2432 lsp_adapter.name.0.as_ref() == "typescript-language-server";
2433
2434 let hints = message.unwrap_or_default().into_iter().map(|lsp_hint| {
2435 let resolve_state = if InlayHints::can_resolve_inlays(lsp_server.capabilities()) {
2436 ResolveState::CanResolve(lsp_server.server_id(), lsp_hint.data.clone())
2437 } else {
2438 ResolveState::Resolved
2439 };
2440
2441 let buffer = buffer.clone();
2442 cx.spawn(move |mut cx| async move {
2443 InlayHints::lsp_to_project_hint(
2444 lsp_hint,
2445 &buffer,
2446 server_id,
2447 resolve_state,
2448 force_no_type_left_padding,
2449 &mut cx,
2450 )
2451 .await
2452 })
2453 });
2454 future::join_all(hints)
2455 .await
2456 .into_iter()
2457 .collect::<anyhow::Result<_>>()
2458 .context("lsp to project inlay hints conversion")
2459 }
2460
2461 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints {
2462 proto::InlayHints {
2463 project_id,
2464 buffer_id: buffer.remote_id().into(),
2465 start: Some(language::proto::serialize_anchor(&self.range.start)),
2466 end: Some(language::proto::serialize_anchor(&self.range.end)),
2467 version: serialize_version(&buffer.version()),
2468 }
2469 }
2470
2471 async fn from_proto(
2472 message: proto::InlayHints,
2473 _: Model<Project>,
2474 buffer: Model<Buffer>,
2475 mut cx: AsyncAppContext,
2476 ) -> Result<Self> {
2477 let start = message
2478 .start
2479 .and_then(language::proto::deserialize_anchor)
2480 .context("invalid start")?;
2481 let end = message
2482 .end
2483 .and_then(language::proto::deserialize_anchor)
2484 .context("invalid end")?;
2485 buffer
2486 .update(&mut cx, |buffer, _| {
2487 buffer.wait_for_version(deserialize_version(&message.version))
2488 })?
2489 .await?;
2490
2491 Ok(Self { range: start..end })
2492 }
2493
2494 fn response_to_proto(
2495 response: Vec<InlayHint>,
2496 _: &mut Project,
2497 _: PeerId,
2498 buffer_version: &clock::Global,
2499 _: &mut AppContext,
2500 ) -> proto::InlayHintsResponse {
2501 proto::InlayHintsResponse {
2502 hints: response
2503 .into_iter()
2504 .map(|response_hint| InlayHints::project_to_proto_hint(response_hint))
2505 .collect(),
2506 version: serialize_version(buffer_version),
2507 }
2508 }
2509
2510 async fn response_from_proto(
2511 self,
2512 message: proto::InlayHintsResponse,
2513 _: Model<Project>,
2514 buffer: Model<Buffer>,
2515 mut cx: AsyncAppContext,
2516 ) -> anyhow::Result<Vec<InlayHint>> {
2517 buffer
2518 .update(&mut cx, |buffer, _| {
2519 buffer.wait_for_version(deserialize_version(&message.version))
2520 })?
2521 .await?;
2522
2523 let mut hints = Vec::new();
2524 for message_hint in message.hints {
2525 hints.push(InlayHints::proto_to_project_hint(message_hint)?);
2526 }
2527
2528 Ok(hints)
2529 }
2530
2531 fn buffer_id_from_proto(message: &proto::InlayHints) -> Result<BufferId> {
2532 BufferId::new(message.buffer_id)
2533 }
2534}