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