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