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