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