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