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