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