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