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 mut completion_edits = Vec::new();
1499 buffer.update(&mut cx, |buffer, _cx| {
1500 let snapshot = buffer.snapshot();
1501 let clipped_position = buffer.clip_point_utf16(Unclipped(self.position), Bias::Left);
1502
1503 let mut range_for_token = None;
1504 completions.retain_mut(|lsp_completion| {
1505 let edit = match lsp_completion.text_edit.as_ref() {
1506 // If the language server provides a range to overwrite, then
1507 // check that the range is valid.
1508 Some(lsp::CompletionTextEdit::Edit(edit)) => {
1509 let range = range_from_lsp(edit.range);
1510 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1511 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1512 if start != range.start.0 || end != range.end.0 {
1513 log::info!("completion out of expected range");
1514 return false;
1515 }
1516 (
1517 snapshot.anchor_before(start)..snapshot.anchor_after(end),
1518 edit.new_text.clone(),
1519 )
1520 }
1521
1522 // If the language server does not provide a range, then infer
1523 // the range based on the syntax tree.
1524 None => {
1525 if self.position != clipped_position {
1526 log::info!("completion out of expected range");
1527 return false;
1528 }
1529
1530 let default_edit_range = response_list
1531 .as_ref()
1532 .and_then(|list| list.item_defaults.as_ref())
1533 .and_then(|defaults| defaults.edit_range.as_ref())
1534 .and_then(|range| match range {
1535 CompletionListItemDefaultsEditRange::Range(r) => Some(r),
1536 _ => None,
1537 });
1538
1539 let range = if let Some(range) = default_edit_range {
1540 let range = range_from_lsp(*range);
1541 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1542 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1543 if start != range.start.0 || end != range.end.0 {
1544 log::info!("completion out of expected range");
1545 return false;
1546 }
1547
1548 snapshot.anchor_before(start)..snapshot.anchor_after(end)
1549 } else {
1550 range_for_token
1551 .get_or_insert_with(|| {
1552 let offset = self.position.to_offset(&snapshot);
1553 let (range, kind) = snapshot.surrounding_word(offset);
1554 let range = if kind == Some(CharKind::Word) {
1555 range
1556 } else {
1557 offset..offset
1558 };
1559
1560 snapshot.anchor_before(range.start)
1561 ..snapshot.anchor_after(range.end)
1562 })
1563 .clone()
1564 };
1565
1566 let text = lsp_completion
1567 .insert_text
1568 .as_ref()
1569 .unwrap_or(&lsp_completion.label)
1570 .clone();
1571 (range, text)
1572 }
1573
1574 Some(lsp::CompletionTextEdit::InsertAndReplace(edit)) => {
1575 let range = range_from_lsp(edit.insert);
1576
1577 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1578 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1579 if start != range.start.0 || end != range.end.0 {
1580 log::info!("completion out of expected range");
1581 return false;
1582 }
1583 (
1584 snapshot.anchor_before(start)..snapshot.anchor_after(end),
1585 edit.new_text.clone(),
1586 )
1587 }
1588 };
1589
1590 completion_edits.push(edit);
1591 true
1592 });
1593 })?;
1594
1595 language_server_adapter
1596 .process_completions(&mut completions)
1597 .await;
1598
1599 Ok(completions
1600 .into_iter()
1601 .zip(completion_edits)
1602 .map(|(lsp_completion, (old_range, mut new_text))| {
1603 LineEnding::normalize(&mut new_text);
1604 CoreCompletion {
1605 old_range,
1606 new_text,
1607 server_id,
1608 lsp_completion,
1609 }
1610 })
1611 .collect())
1612 }
1613
1614 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCompletions {
1615 let anchor = buffer.anchor_after(self.position);
1616 proto::GetCompletions {
1617 project_id,
1618 buffer_id: buffer.remote_id().into(),
1619 position: Some(language::proto::serialize_anchor(&anchor)),
1620 version: serialize_version(&buffer.version()),
1621 }
1622 }
1623
1624 async fn from_proto(
1625 message: proto::GetCompletions,
1626 _: Model<Project>,
1627 buffer: Model<Buffer>,
1628 mut cx: AsyncAppContext,
1629 ) -> Result<Self> {
1630 let version = deserialize_version(&message.version);
1631 buffer
1632 .update(&mut cx, |buffer, _| buffer.wait_for_version(version))?
1633 .await?;
1634 let position = message
1635 .position
1636 .and_then(language::proto::deserialize_anchor)
1637 .map(|p| {
1638 buffer.update(&mut cx, |buffer, _| {
1639 buffer.clip_point_utf16(Unclipped(p.to_point_utf16(buffer)), Bias::Left)
1640 })
1641 })
1642 .ok_or_else(|| anyhow!("invalid position"))??;
1643 Ok(Self { position })
1644 }
1645
1646 fn response_to_proto(
1647 completions: Vec<CoreCompletion>,
1648 _: &mut Project,
1649 _: PeerId,
1650 buffer_version: &clock::Global,
1651 _: &mut AppContext,
1652 ) -> proto::GetCompletionsResponse {
1653 proto::GetCompletionsResponse {
1654 completions: completions
1655 .iter()
1656 .map(Project::serialize_completion)
1657 .collect(),
1658 version: serialize_version(buffer_version),
1659 }
1660 }
1661
1662 async fn response_from_proto(
1663 self,
1664 message: proto::GetCompletionsResponse,
1665 _project: Model<Project>,
1666 buffer: Model<Buffer>,
1667 mut cx: AsyncAppContext,
1668 ) -> Result<Self::Response> {
1669 buffer
1670 .update(&mut cx, |buffer, _| {
1671 buffer.wait_for_version(deserialize_version(&message.version))
1672 })?
1673 .await?;
1674
1675 message
1676 .completions
1677 .into_iter()
1678 .map(Project::deserialize_completion)
1679 .collect()
1680 }
1681
1682 fn buffer_id_from_proto(message: &proto::GetCompletions) -> Result<BufferId> {
1683 BufferId::new(message.buffer_id)
1684 }
1685}
1686
1687#[async_trait(?Send)]
1688impl LspCommand for GetCodeActions {
1689 type Response = Vec<CodeAction>;
1690 type LspRequest = lsp::request::CodeActionRequest;
1691 type ProtoRequest = proto::GetCodeActions;
1692
1693 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
1694 match &capabilities.code_action_provider {
1695 None => false,
1696 Some(lsp::CodeActionProviderCapability::Simple(false)) => false,
1697 _ => true,
1698 }
1699 }
1700
1701 fn to_lsp(
1702 &self,
1703 path: &Path,
1704 buffer: &Buffer,
1705 language_server: &Arc<LanguageServer>,
1706 _: &AppContext,
1707 ) -> lsp::CodeActionParams {
1708 let relevant_diagnostics = buffer
1709 .snapshot()
1710 .diagnostics_in_range::<_, language::PointUtf16>(self.range.clone(), false)
1711 .map(|entry| entry.to_lsp_diagnostic_stub())
1712 .collect::<Vec<_>>();
1713
1714 lsp::CodeActionParams {
1715 text_document: lsp::TextDocumentIdentifier::new(
1716 lsp::Url::from_file_path(path).unwrap(),
1717 ),
1718 range: range_to_lsp(self.range.to_point_utf16(buffer)),
1719 work_done_progress_params: Default::default(),
1720 partial_result_params: Default::default(),
1721 context: lsp::CodeActionContext {
1722 diagnostics: relevant_diagnostics,
1723 only: self
1724 .kinds
1725 .clone()
1726 .or_else(|| language_server.code_action_kinds()),
1727 ..lsp::CodeActionContext::default()
1728 },
1729 }
1730 }
1731
1732 async fn response_from_lsp(
1733 self,
1734 actions: Option<lsp::CodeActionResponse>,
1735 _: Model<Project>,
1736 _: Model<Buffer>,
1737 server_id: LanguageServerId,
1738 _: AsyncAppContext,
1739 ) -> Result<Vec<CodeAction>> {
1740 Ok(actions
1741 .unwrap_or_default()
1742 .into_iter()
1743 .filter_map(|entry| {
1744 if let lsp::CodeActionOrCommand::CodeAction(lsp_action) = entry {
1745 Some(CodeAction {
1746 server_id,
1747 range: self.range.clone(),
1748 lsp_action,
1749 })
1750 } else {
1751 None
1752 }
1753 })
1754 .collect())
1755 }
1756
1757 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCodeActions {
1758 proto::GetCodeActions {
1759 project_id,
1760 buffer_id: buffer.remote_id().into(),
1761 start: Some(language::proto::serialize_anchor(&self.range.start)),
1762 end: Some(language::proto::serialize_anchor(&self.range.end)),
1763 version: serialize_version(&buffer.version()),
1764 }
1765 }
1766
1767 async fn from_proto(
1768 message: proto::GetCodeActions,
1769 _: Model<Project>,
1770 buffer: Model<Buffer>,
1771 mut cx: AsyncAppContext,
1772 ) -> Result<Self> {
1773 let start = message
1774 .start
1775 .and_then(language::proto::deserialize_anchor)
1776 .ok_or_else(|| anyhow!("invalid start"))?;
1777 let end = message
1778 .end
1779 .and_then(language::proto::deserialize_anchor)
1780 .ok_or_else(|| anyhow!("invalid end"))?;
1781 buffer
1782 .update(&mut cx, |buffer, _| {
1783 buffer.wait_for_version(deserialize_version(&message.version))
1784 })?
1785 .await?;
1786
1787 Ok(Self {
1788 range: start..end,
1789 kinds: None,
1790 })
1791 }
1792
1793 fn response_to_proto(
1794 code_actions: Vec<CodeAction>,
1795 _: &mut Project,
1796 _: PeerId,
1797 buffer_version: &clock::Global,
1798 _: &mut AppContext,
1799 ) -> proto::GetCodeActionsResponse {
1800 proto::GetCodeActionsResponse {
1801 actions: code_actions
1802 .iter()
1803 .map(Project::serialize_code_action)
1804 .collect(),
1805 version: serialize_version(buffer_version),
1806 }
1807 }
1808
1809 async fn response_from_proto(
1810 self,
1811 message: proto::GetCodeActionsResponse,
1812 _: Model<Project>,
1813 buffer: Model<Buffer>,
1814 mut cx: AsyncAppContext,
1815 ) -> Result<Vec<CodeAction>> {
1816 buffer
1817 .update(&mut cx, |buffer, _| {
1818 buffer.wait_for_version(deserialize_version(&message.version))
1819 })?
1820 .await?;
1821 message
1822 .actions
1823 .into_iter()
1824 .map(Project::deserialize_code_action)
1825 .collect()
1826 }
1827
1828 fn buffer_id_from_proto(message: &proto::GetCodeActions) -> Result<BufferId> {
1829 BufferId::new(message.buffer_id)
1830 }
1831}
1832
1833impl GetCodeActions {
1834 pub fn can_resolve_actions(capabilities: &ServerCapabilities) -> bool {
1835 capabilities
1836 .code_action_provider
1837 .as_ref()
1838 .and_then(|options| match options {
1839 lsp::CodeActionProviderCapability::Simple(_is_supported) => None,
1840 lsp::CodeActionProviderCapability::Options(options) => options.resolve_provider,
1841 })
1842 .unwrap_or(false)
1843 }
1844
1845 pub fn supports_code_actions(capabilities: &ServerCapabilities) -> bool {
1846 capabilities
1847 .code_action_provider
1848 .as_ref()
1849 .map(|options| match options {
1850 lsp::CodeActionProviderCapability::Simple(is_supported) => *is_supported,
1851 lsp::CodeActionProviderCapability::Options(_) => true,
1852 })
1853 .unwrap_or(false)
1854 }
1855}
1856
1857#[async_trait(?Send)]
1858impl LspCommand for OnTypeFormatting {
1859 type Response = Option<Transaction>;
1860 type LspRequest = lsp::request::OnTypeFormatting;
1861 type ProtoRequest = proto::OnTypeFormatting;
1862
1863 fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
1864 let Some(on_type_formatting_options) =
1865 &server_capabilities.document_on_type_formatting_provider
1866 else {
1867 return false;
1868 };
1869 on_type_formatting_options
1870 .first_trigger_character
1871 .contains(&self.trigger)
1872 || on_type_formatting_options
1873 .more_trigger_character
1874 .iter()
1875 .flatten()
1876 .any(|chars| chars.contains(&self.trigger))
1877 }
1878
1879 fn to_lsp(
1880 &self,
1881 path: &Path,
1882 _: &Buffer,
1883 _: &Arc<LanguageServer>,
1884 _: &AppContext,
1885 ) -> lsp::DocumentOnTypeFormattingParams {
1886 lsp::DocumentOnTypeFormattingParams {
1887 text_document_position: lsp::TextDocumentPositionParams::new(
1888 lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
1889 point_to_lsp(self.position),
1890 ),
1891 ch: self.trigger.clone(),
1892 options: lsp_formatting_options(self.options.tab_size),
1893 }
1894 }
1895
1896 async fn response_from_lsp(
1897 self,
1898 message: Option<Vec<lsp::TextEdit>>,
1899 project: Model<Project>,
1900 buffer: Model<Buffer>,
1901 server_id: LanguageServerId,
1902 mut cx: AsyncAppContext,
1903 ) -> Result<Option<Transaction>> {
1904 if let Some(edits) = message {
1905 let (lsp_adapter, lsp_server) =
1906 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
1907 Project::deserialize_edits(
1908 project,
1909 buffer,
1910 edits,
1911 self.push_to_history,
1912 lsp_adapter,
1913 lsp_server,
1914 &mut cx,
1915 )
1916 .await
1917 } else {
1918 Ok(None)
1919 }
1920 }
1921
1922 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::OnTypeFormatting {
1923 proto::OnTypeFormatting {
1924 project_id,
1925 buffer_id: buffer.remote_id().into(),
1926 position: Some(language::proto::serialize_anchor(
1927 &buffer.anchor_before(self.position),
1928 )),
1929 trigger: self.trigger.clone(),
1930 version: serialize_version(&buffer.version()),
1931 }
1932 }
1933
1934 async fn from_proto(
1935 message: proto::OnTypeFormatting,
1936 _: Model<Project>,
1937 buffer: Model<Buffer>,
1938 mut cx: AsyncAppContext,
1939 ) -> Result<Self> {
1940 let position = message
1941 .position
1942 .and_then(deserialize_anchor)
1943 .ok_or_else(|| anyhow!("invalid position"))?;
1944 buffer
1945 .update(&mut cx, |buffer, _| {
1946 buffer.wait_for_version(deserialize_version(&message.version))
1947 })?
1948 .await?;
1949
1950 let tab_size = buffer.update(&mut cx, |buffer, cx| {
1951 language_settings(buffer.language(), buffer.file(), cx).tab_size
1952 })?;
1953
1954 Ok(Self {
1955 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
1956 trigger: message.trigger.clone(),
1957 options: lsp_formatting_options(tab_size.get()).into(),
1958 push_to_history: false,
1959 })
1960 }
1961
1962 fn response_to_proto(
1963 response: Option<Transaction>,
1964 _: &mut Project,
1965 _: PeerId,
1966 _: &clock::Global,
1967 _: &mut AppContext,
1968 ) -> proto::OnTypeFormattingResponse {
1969 proto::OnTypeFormattingResponse {
1970 transaction: response
1971 .map(|transaction| language::proto::serialize_transaction(&transaction)),
1972 }
1973 }
1974
1975 async fn response_from_proto(
1976 self,
1977 message: proto::OnTypeFormattingResponse,
1978 _: Model<Project>,
1979 _: Model<Buffer>,
1980 _: AsyncAppContext,
1981 ) -> Result<Option<Transaction>> {
1982 let Some(transaction) = message.transaction else {
1983 return Ok(None);
1984 };
1985 Ok(Some(language::proto::deserialize_transaction(transaction)?))
1986 }
1987
1988 fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> Result<BufferId> {
1989 BufferId::new(message.buffer_id)
1990 }
1991}
1992
1993impl InlayHints {
1994 pub async fn lsp_to_project_hint(
1995 lsp_hint: lsp::InlayHint,
1996 buffer_handle: &Model<Buffer>,
1997 server_id: LanguageServerId,
1998 resolve_state: ResolveState,
1999 force_no_type_left_padding: bool,
2000 cx: &mut AsyncAppContext,
2001 ) -> anyhow::Result<InlayHint> {
2002 let kind = lsp_hint.kind.and_then(|kind| match kind {
2003 lsp::InlayHintKind::TYPE => Some(InlayHintKind::Type),
2004 lsp::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
2005 _ => None,
2006 });
2007
2008 let position = buffer_handle.update(cx, |buffer, _| {
2009 let position = buffer.clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
2010 if kind == Some(InlayHintKind::Parameter) {
2011 buffer.anchor_before(position)
2012 } else {
2013 buffer.anchor_after(position)
2014 }
2015 })?;
2016 let label = Self::lsp_inlay_label_to_project(lsp_hint.label, server_id)
2017 .await
2018 .context("lsp to project inlay hint conversion")?;
2019 let padding_left = if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
2020 false
2021 } else {
2022 lsp_hint.padding_left.unwrap_or(false)
2023 };
2024
2025 Ok(InlayHint {
2026 position,
2027 padding_left,
2028 padding_right: lsp_hint.padding_right.unwrap_or(false),
2029 label,
2030 kind,
2031 tooltip: lsp_hint.tooltip.map(|tooltip| match tooltip {
2032 lsp::InlayHintTooltip::String(s) => InlayHintTooltip::String(s),
2033 lsp::InlayHintTooltip::MarkupContent(markup_content) => {
2034 InlayHintTooltip::MarkupContent(MarkupContent {
2035 kind: match markup_content.kind {
2036 lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
2037 lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
2038 },
2039 value: markup_content.value,
2040 })
2041 }
2042 }),
2043 resolve_state,
2044 })
2045 }
2046
2047 async fn lsp_inlay_label_to_project(
2048 lsp_label: lsp::InlayHintLabel,
2049 server_id: LanguageServerId,
2050 ) -> anyhow::Result<InlayHintLabel> {
2051 let label = match lsp_label {
2052 lsp::InlayHintLabel::String(s) => InlayHintLabel::String(s),
2053 lsp::InlayHintLabel::LabelParts(lsp_parts) => {
2054 let mut parts = Vec::with_capacity(lsp_parts.len());
2055 for lsp_part in lsp_parts {
2056 parts.push(InlayHintLabelPart {
2057 value: lsp_part.value,
2058 tooltip: lsp_part.tooltip.map(|tooltip| match tooltip {
2059 lsp::InlayHintLabelPartTooltip::String(s) => {
2060 InlayHintLabelPartTooltip::String(s)
2061 }
2062 lsp::InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2063 InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2064 kind: match markup_content.kind {
2065 lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
2066 lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
2067 },
2068 value: markup_content.value,
2069 })
2070 }
2071 }),
2072 location: Some(server_id).zip(lsp_part.location),
2073 });
2074 }
2075 InlayHintLabel::LabelParts(parts)
2076 }
2077 };
2078
2079 Ok(label)
2080 }
2081
2082 pub fn project_to_proto_hint(response_hint: InlayHint) -> proto::InlayHint {
2083 let (state, lsp_resolve_state) = match response_hint.resolve_state {
2084 ResolveState::Resolved => (0, None),
2085 ResolveState::CanResolve(server_id, resolve_data) => (
2086 1,
2087 resolve_data
2088 .map(|json_data| {
2089 serde_json::to_string(&json_data)
2090 .expect("failed to serialize resolve json data")
2091 })
2092 .map(|value| proto::resolve_state::LspResolveState {
2093 server_id: server_id.0 as u64,
2094 value,
2095 }),
2096 ),
2097 ResolveState::Resolving => (2, None),
2098 };
2099 let resolve_state = Some(proto::ResolveState {
2100 state,
2101 lsp_resolve_state,
2102 });
2103 proto::InlayHint {
2104 position: Some(language::proto::serialize_anchor(&response_hint.position)),
2105 padding_left: response_hint.padding_left,
2106 padding_right: response_hint.padding_right,
2107 label: Some(proto::InlayHintLabel {
2108 label: Some(match response_hint.label {
2109 InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s),
2110 InlayHintLabel::LabelParts(label_parts) => {
2111 proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts {
2112 parts: label_parts.into_iter().map(|label_part| {
2113 let location_url = label_part.location.as_ref().map(|(_, location)| location.uri.to_string());
2114 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 });
2115 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 });
2116 proto::InlayHintLabelPart {
2117 value: label_part.value,
2118 tooltip: label_part.tooltip.map(|tooltip| {
2119 let proto_tooltip = match tooltip {
2120 InlayHintLabelPartTooltip::String(s) => proto::inlay_hint_label_part_tooltip::Content::Value(s),
2121 InlayHintLabelPartTooltip::MarkupContent(markup_content) => proto::inlay_hint_label_part_tooltip::Content::MarkupContent(proto::MarkupContent {
2122 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
2123 value: markup_content.value,
2124 }),
2125 };
2126 proto::InlayHintLabelPartTooltip {content: Some(proto_tooltip)}
2127 }),
2128 location_url,
2129 location_range_start,
2130 location_range_end,
2131 language_server_id: label_part.location.as_ref().map(|(server_id, _)| server_id.0 as u64),
2132 }}).collect()
2133 })
2134 }
2135 }),
2136 }),
2137 kind: response_hint.kind.map(|kind| kind.name().to_string()),
2138 tooltip: response_hint.tooltip.map(|response_tooltip| {
2139 let proto_tooltip = match response_tooltip {
2140 InlayHintTooltip::String(s) => proto::inlay_hint_tooltip::Content::Value(s),
2141 InlayHintTooltip::MarkupContent(markup_content) => {
2142 proto::inlay_hint_tooltip::Content::MarkupContent(proto::MarkupContent {
2143 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
2144 value: markup_content.value,
2145 })
2146 }
2147 };
2148 proto::InlayHintTooltip {
2149 content: Some(proto_tooltip),
2150 }
2151 }),
2152 resolve_state,
2153 }
2154 }
2155
2156 pub fn proto_to_project_hint(message_hint: proto::InlayHint) -> anyhow::Result<InlayHint> {
2157 let resolve_state = message_hint.resolve_state.as_ref().unwrap_or_else(|| {
2158 panic!("incorrect proto inlay hint message: no resolve state in hint {message_hint:?}",)
2159 });
2160 let resolve_state_data = resolve_state
2161 .lsp_resolve_state.as_ref()
2162 .map(|lsp_resolve_state| {
2163 serde_json::from_str::<Option<lsp::LSPAny>>(&lsp_resolve_state.value)
2164 .with_context(|| format!("incorrect proto inlay hint message: non-json resolve state {lsp_resolve_state:?}"))
2165 .map(|state| (LanguageServerId(lsp_resolve_state.server_id as usize), state))
2166 })
2167 .transpose()?;
2168 let resolve_state = match resolve_state.state {
2169 0 => ResolveState::Resolved,
2170 1 => {
2171 let (server_id, lsp_resolve_state) = resolve_state_data.with_context(|| {
2172 format!(
2173 "No lsp resolve data for the hint that can be resolved: {message_hint:?}"
2174 )
2175 })?;
2176 ResolveState::CanResolve(server_id, lsp_resolve_state)
2177 }
2178 2 => ResolveState::Resolving,
2179 invalid => {
2180 anyhow::bail!("Unexpected resolve state {invalid} for hint {message_hint:?}")
2181 }
2182 };
2183 Ok(InlayHint {
2184 position: message_hint
2185 .position
2186 .and_then(language::proto::deserialize_anchor)
2187 .context("invalid position")?,
2188 label: match message_hint
2189 .label
2190 .and_then(|label| label.label)
2191 .context("missing label")?
2192 {
2193 proto::inlay_hint_label::Label::Value(s) => InlayHintLabel::String(s),
2194 proto::inlay_hint_label::Label::LabelParts(parts) => {
2195 let mut label_parts = Vec::new();
2196 for part in parts.parts {
2197 label_parts.push(InlayHintLabelPart {
2198 value: part.value,
2199 tooltip: part.tooltip.map(|tooltip| match tooltip.content {
2200 Some(proto::inlay_hint_label_part_tooltip::Content::Value(s)) => {
2201 InlayHintLabelPartTooltip::String(s)
2202 }
2203 Some(
2204 proto::inlay_hint_label_part_tooltip::Content::MarkupContent(
2205 markup_content,
2206 ),
2207 ) => InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2208 kind: if markup_content.is_markdown {
2209 HoverBlockKind::Markdown
2210 } else {
2211 HoverBlockKind::PlainText
2212 },
2213 value: markup_content.value,
2214 }),
2215 None => InlayHintLabelPartTooltip::String(String::new()),
2216 }),
2217 location: {
2218 match part
2219 .location_url
2220 .zip(
2221 part.location_range_start.and_then(|start| {
2222 Some(start..part.location_range_end?)
2223 }),
2224 )
2225 .zip(part.language_server_id)
2226 {
2227 Some(((uri, range), server_id)) => Some((
2228 LanguageServerId(server_id as usize),
2229 lsp::Location {
2230 uri: lsp::Url::parse(&uri)
2231 .context("invalid uri in hint part {part:?}")?,
2232 range: lsp::Range::new(
2233 point_to_lsp(PointUtf16::new(
2234 range.start.row,
2235 range.start.column,
2236 )),
2237 point_to_lsp(PointUtf16::new(
2238 range.end.row,
2239 range.end.column,
2240 )),
2241 ),
2242 },
2243 )),
2244 None => None,
2245 }
2246 },
2247 });
2248 }
2249
2250 InlayHintLabel::LabelParts(label_parts)
2251 }
2252 },
2253 padding_left: message_hint.padding_left,
2254 padding_right: message_hint.padding_right,
2255 kind: message_hint
2256 .kind
2257 .as_deref()
2258 .and_then(InlayHintKind::from_name),
2259 tooltip: message_hint.tooltip.and_then(|tooltip| {
2260 Some(match tooltip.content? {
2261 proto::inlay_hint_tooltip::Content::Value(s) => InlayHintTooltip::String(s),
2262 proto::inlay_hint_tooltip::Content::MarkupContent(markup_content) => {
2263 InlayHintTooltip::MarkupContent(MarkupContent {
2264 kind: if markup_content.is_markdown {
2265 HoverBlockKind::Markdown
2266 } else {
2267 HoverBlockKind::PlainText
2268 },
2269 value: markup_content.value,
2270 })
2271 }
2272 })
2273 }),
2274 resolve_state,
2275 })
2276 }
2277
2278 pub fn project_to_lsp_hint(hint: InlayHint, snapshot: &BufferSnapshot) -> lsp::InlayHint {
2279 lsp::InlayHint {
2280 position: point_to_lsp(hint.position.to_point_utf16(snapshot)),
2281 kind: hint.kind.map(|kind| match kind {
2282 InlayHintKind::Type => lsp::InlayHintKind::TYPE,
2283 InlayHintKind::Parameter => lsp::InlayHintKind::PARAMETER,
2284 }),
2285 text_edits: None,
2286 tooltip: hint.tooltip.and_then(|tooltip| {
2287 Some(match tooltip {
2288 InlayHintTooltip::String(s) => lsp::InlayHintTooltip::String(s),
2289 InlayHintTooltip::MarkupContent(markup_content) => {
2290 lsp::InlayHintTooltip::MarkupContent(lsp::MarkupContent {
2291 kind: match markup_content.kind {
2292 HoverBlockKind::PlainText => lsp::MarkupKind::PlainText,
2293 HoverBlockKind::Markdown => lsp::MarkupKind::Markdown,
2294 HoverBlockKind::Code { .. } => return None,
2295 },
2296 value: markup_content.value,
2297 })
2298 }
2299 })
2300 }),
2301 label: match hint.label {
2302 InlayHintLabel::String(s) => lsp::InlayHintLabel::String(s),
2303 InlayHintLabel::LabelParts(label_parts) => lsp::InlayHintLabel::LabelParts(
2304 label_parts
2305 .into_iter()
2306 .map(|part| lsp::InlayHintLabelPart {
2307 value: part.value,
2308 tooltip: part.tooltip.and_then(|tooltip| {
2309 Some(match tooltip {
2310 InlayHintLabelPartTooltip::String(s) => {
2311 lsp::InlayHintLabelPartTooltip::String(s)
2312 }
2313 InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2314 lsp::InlayHintLabelPartTooltip::MarkupContent(
2315 lsp::MarkupContent {
2316 kind: match markup_content.kind {
2317 HoverBlockKind::PlainText => {
2318 lsp::MarkupKind::PlainText
2319 }
2320 HoverBlockKind::Markdown => {
2321 lsp::MarkupKind::Markdown
2322 }
2323 HoverBlockKind::Code { .. } => return None,
2324 },
2325 value: markup_content.value,
2326 },
2327 )
2328 }
2329 })
2330 }),
2331 location: part.location.map(|(_, location)| location),
2332 command: None,
2333 })
2334 .collect(),
2335 ),
2336 },
2337 padding_left: Some(hint.padding_left),
2338 padding_right: Some(hint.padding_right),
2339 data: match hint.resolve_state {
2340 ResolveState::CanResolve(_, data) => data,
2341 ResolveState::Resolving | ResolveState::Resolved => None,
2342 },
2343 }
2344 }
2345
2346 pub fn can_resolve_inlays(capabilities: &ServerCapabilities) -> bool {
2347 capabilities
2348 .inlay_hint_provider
2349 .as_ref()
2350 .and_then(|options| match options {
2351 OneOf::Left(_is_supported) => None,
2352 OneOf::Right(capabilities) => match capabilities {
2353 lsp::InlayHintServerCapabilities::Options(o) => o.resolve_provider,
2354 lsp::InlayHintServerCapabilities::RegistrationOptions(o) => {
2355 o.inlay_hint_options.resolve_provider
2356 }
2357 },
2358 })
2359 .unwrap_or(false)
2360 }
2361}
2362
2363#[async_trait(?Send)]
2364impl LspCommand for InlayHints {
2365 type Response = Vec<InlayHint>;
2366 type LspRequest = lsp::InlayHintRequest;
2367 type ProtoRequest = proto::InlayHints;
2368
2369 fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
2370 let Some(inlay_hint_provider) = &server_capabilities.inlay_hint_provider else {
2371 return false;
2372 };
2373 match inlay_hint_provider {
2374 lsp::OneOf::Left(enabled) => *enabled,
2375 lsp::OneOf::Right(inlay_hint_capabilities) => match inlay_hint_capabilities {
2376 lsp::InlayHintServerCapabilities::Options(_) => true,
2377 lsp::InlayHintServerCapabilities::RegistrationOptions(_) => false,
2378 },
2379 }
2380 }
2381
2382 fn to_lsp(
2383 &self,
2384 path: &Path,
2385 buffer: &Buffer,
2386 _: &Arc<LanguageServer>,
2387 _: &AppContext,
2388 ) -> lsp::InlayHintParams {
2389 lsp::InlayHintParams {
2390 text_document: lsp::TextDocumentIdentifier {
2391 uri: lsp::Url::from_file_path(path).unwrap(),
2392 },
2393 range: range_to_lsp(self.range.to_point_utf16(buffer)),
2394 work_done_progress_params: Default::default(),
2395 }
2396 }
2397
2398 async fn response_from_lsp(
2399 self,
2400 message: Option<Vec<lsp::InlayHint>>,
2401 project: Model<Project>,
2402 buffer: Model<Buffer>,
2403 server_id: LanguageServerId,
2404 mut cx: AsyncAppContext,
2405 ) -> anyhow::Result<Vec<InlayHint>> {
2406 let (lsp_adapter, lsp_server) =
2407 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
2408 // `typescript-language-server` adds padding to the left for type hints, turning
2409 // `const foo: boolean` into `const foo : boolean` which looks odd.
2410 // `rust-analyzer` does not have the padding for this case, and we have to accommodate both.
2411 //
2412 // We could trim the whole string, but being pessimistic on par with the situation above,
2413 // there might be a hint with multiple whitespaces at the end(s) which we need to display properly.
2414 // Hence let's use a heuristic first to handle the most awkward case and look for more.
2415 let force_no_type_left_padding =
2416 lsp_adapter.name.0.as_ref() == "typescript-language-server";
2417
2418 let hints = message.unwrap_or_default().into_iter().map(|lsp_hint| {
2419 let resolve_state = if InlayHints::can_resolve_inlays(lsp_server.capabilities()) {
2420 ResolveState::CanResolve(lsp_server.server_id(), lsp_hint.data.clone())
2421 } else {
2422 ResolveState::Resolved
2423 };
2424
2425 let buffer = buffer.clone();
2426 cx.spawn(move |mut cx| async move {
2427 InlayHints::lsp_to_project_hint(
2428 lsp_hint,
2429 &buffer,
2430 server_id,
2431 resolve_state,
2432 force_no_type_left_padding,
2433 &mut cx,
2434 )
2435 .await
2436 })
2437 });
2438 future::join_all(hints)
2439 .await
2440 .into_iter()
2441 .collect::<anyhow::Result<_>>()
2442 .context("lsp to project inlay hints conversion")
2443 }
2444
2445 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints {
2446 proto::InlayHints {
2447 project_id,
2448 buffer_id: buffer.remote_id().into(),
2449 start: Some(language::proto::serialize_anchor(&self.range.start)),
2450 end: Some(language::proto::serialize_anchor(&self.range.end)),
2451 version: serialize_version(&buffer.version()),
2452 }
2453 }
2454
2455 async fn from_proto(
2456 message: proto::InlayHints,
2457 _: Model<Project>,
2458 buffer: Model<Buffer>,
2459 mut cx: AsyncAppContext,
2460 ) -> Result<Self> {
2461 let start = message
2462 .start
2463 .and_then(language::proto::deserialize_anchor)
2464 .context("invalid start")?;
2465 let end = message
2466 .end
2467 .and_then(language::proto::deserialize_anchor)
2468 .context("invalid end")?;
2469 buffer
2470 .update(&mut cx, |buffer, _| {
2471 buffer.wait_for_version(deserialize_version(&message.version))
2472 })?
2473 .await?;
2474
2475 Ok(Self { range: start..end })
2476 }
2477
2478 fn response_to_proto(
2479 response: Vec<InlayHint>,
2480 _: &mut Project,
2481 _: PeerId,
2482 buffer_version: &clock::Global,
2483 _: &mut AppContext,
2484 ) -> proto::InlayHintsResponse {
2485 proto::InlayHintsResponse {
2486 hints: response
2487 .into_iter()
2488 .map(|response_hint| InlayHints::project_to_proto_hint(response_hint))
2489 .collect(),
2490 version: serialize_version(buffer_version),
2491 }
2492 }
2493
2494 async fn response_from_proto(
2495 self,
2496 message: proto::InlayHintsResponse,
2497 _: Model<Project>,
2498 buffer: Model<Buffer>,
2499 mut cx: AsyncAppContext,
2500 ) -> anyhow::Result<Vec<InlayHint>> {
2501 buffer
2502 .update(&mut cx, |buffer, _| {
2503 buffer.wait_for_version(deserialize_version(&message.version))
2504 })?
2505 .await?;
2506
2507 let mut hints = Vec::new();
2508 for message_hint in message.hints {
2509 hints.push(InlayHints::proto_to_project_hint(message_hint)?);
2510 }
2511
2512 Ok(hints)
2513 }
2514
2515 fn buffer_id_from_proto(message: &proto::InlayHints) -> Result<BufferId> {
2516 BufferId::new(message.buffer_id)
2517 }
2518}