1mod signature_help;
2
3use crate::{
4 lsp_store::{LocalLspStore, LspStore},
5 CodeAction, CoreCompletion, DocumentHighlight, Hover, HoverBlock, HoverBlockKind, InlayHint,
6 InlayHintLabel, InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Location,
7 LocationLink, MarkupContent, 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 LocalLspStore::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 mut cx: AsyncAppContext,
421 ) -> Result<ProjectTransaction> {
422 let message = message
423 .transaction
424 .ok_or_else(|| anyhow!("missing transaction"))?;
425 lsp_store
426 .update(&mut cx, |lsp_store, cx| {
427 lsp_store.buffer_store().update(cx, |buffer_store, cx| {
428 buffer_store.deserialize_project_transaction(message, self.push_to_history, cx)
429 })
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_local_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_edit_range = item_defaults.edit_range.as_ref();
1780 let default_insert_text_format = item_defaults.insert_text_format.as_ref();
1781 let default_insert_text_mode = item_defaults.insert_text_mode.as_ref();
1782
1783 if default_data.is_some()
1784 || default_commit_characters.is_some()
1785 || default_edit_range.is_some()
1786 || default_insert_text_format.is_some()
1787 || default_insert_text_mode.is_some()
1788 {
1789 for item in completions.iter_mut() {
1790 if item.data.is_none() && default_data.is_some() {
1791 item.data = default_data.cloned()
1792 }
1793 if item.commit_characters.is_none() && default_commit_characters.is_some() {
1794 item.commit_characters = default_commit_characters.cloned()
1795 }
1796 if item.text_edit.is_none() {
1797 if let Some(default_edit_range) = default_edit_range {
1798 match default_edit_range {
1799 CompletionListItemDefaultsEditRange::Range(range) => {
1800 item.text_edit =
1801 Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
1802 range: *range,
1803 new_text: item.label.clone(),
1804 }))
1805 }
1806 CompletionListItemDefaultsEditRange::InsertAndReplace {
1807 insert,
1808 replace,
1809 } => {
1810 item.text_edit =
1811 Some(lsp::CompletionTextEdit::InsertAndReplace(
1812 lsp::InsertReplaceEdit {
1813 new_text: item.label.clone(),
1814 insert: *insert,
1815 replace: *replace,
1816 },
1817 ))
1818 }
1819 }
1820 }
1821 }
1822 if item.insert_text_format.is_none() && default_insert_text_format.is_some() {
1823 item.insert_text_format = default_insert_text_format.cloned()
1824 }
1825 if item.insert_text_mode.is_none() && default_insert_text_mode.is_some() {
1826 item.insert_text_mode = default_insert_text_mode.cloned()
1827 }
1828 }
1829 }
1830 }
1831
1832 let mut completion_edits = Vec::new();
1833 buffer.update(&mut cx, |buffer, _cx| {
1834 let snapshot = buffer.snapshot();
1835 let clipped_position = buffer.clip_point_utf16(Unclipped(self.position), Bias::Left);
1836
1837 let mut range_for_token = None;
1838 completions.retain_mut(|lsp_completion| {
1839 let edit = match lsp_completion.text_edit.as_ref() {
1840 // If the language server provides a range to overwrite, then
1841 // check that the range is valid.
1842 Some(completion_text_edit) => {
1843 match parse_completion_text_edit(completion_text_edit, &snapshot) {
1844 Some(edit) => edit,
1845 None => return false,
1846 }
1847 }
1848
1849 // If the language server does not provide a range, then infer
1850 // the range based on the syntax tree.
1851 None => {
1852 if self.position != clipped_position {
1853 log::info!("completion out of expected range");
1854 return false;
1855 }
1856
1857 let default_edit_range = response_list
1858 .as_ref()
1859 .and_then(|list| list.item_defaults.as_ref())
1860 .and_then(|defaults| defaults.edit_range.as_ref())
1861 .and_then(|range| match range {
1862 CompletionListItemDefaultsEditRange::Range(r) => Some(r),
1863 _ => None,
1864 });
1865
1866 let range = if let Some(range) = default_edit_range {
1867 let range = range_from_lsp(*range);
1868 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1869 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1870 if start != range.start.0 || end != range.end.0 {
1871 log::info!("completion out of expected range");
1872 return false;
1873 }
1874
1875 snapshot.anchor_before(start)..snapshot.anchor_after(end)
1876 } else {
1877 range_for_token
1878 .get_or_insert_with(|| {
1879 let offset = self.position.to_offset(&snapshot);
1880 let (range, kind) = snapshot.surrounding_word(offset);
1881 let range = if kind == Some(CharKind::Word) {
1882 range
1883 } else {
1884 offset..offset
1885 };
1886
1887 snapshot.anchor_before(range.start)
1888 ..snapshot.anchor_after(range.end)
1889 })
1890 .clone()
1891 };
1892
1893 let text = lsp_completion
1894 .insert_text
1895 .as_ref()
1896 .unwrap_or(&lsp_completion.label)
1897 .clone();
1898 (range, text)
1899 }
1900 };
1901
1902 completion_edits.push(edit);
1903 true
1904 });
1905 })?;
1906
1907 language_server_adapter
1908 .process_completions(&mut completions)
1909 .await;
1910
1911 Ok(completions
1912 .into_iter()
1913 .zip(completion_edits)
1914 .map(|(lsp_completion, (old_range, mut new_text))| {
1915 LineEnding::normalize(&mut new_text);
1916 CoreCompletion {
1917 old_range,
1918 new_text,
1919 server_id,
1920 lsp_completion,
1921 }
1922 })
1923 .collect())
1924 }
1925
1926 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCompletions {
1927 let anchor = buffer.anchor_after(self.position);
1928 proto::GetCompletions {
1929 project_id,
1930 buffer_id: buffer.remote_id().into(),
1931 position: Some(language::proto::serialize_anchor(&anchor)),
1932 version: serialize_version(&buffer.version()),
1933 }
1934 }
1935
1936 async fn from_proto(
1937 message: proto::GetCompletions,
1938 _: Model<LspStore>,
1939 buffer: Model<Buffer>,
1940 mut cx: AsyncAppContext,
1941 ) -> Result<Self> {
1942 let version = deserialize_version(&message.version);
1943 buffer
1944 .update(&mut cx, |buffer, _| buffer.wait_for_version(version))?
1945 .await?;
1946 let position = message
1947 .position
1948 .and_then(language::proto::deserialize_anchor)
1949 .map(|p| {
1950 buffer.update(&mut cx, |buffer, _| {
1951 buffer.clip_point_utf16(Unclipped(p.to_point_utf16(buffer)), Bias::Left)
1952 })
1953 })
1954 .ok_or_else(|| anyhow!("invalid position"))??;
1955 Ok(Self {
1956 position,
1957 context: CompletionContext {
1958 trigger_kind: CompletionTriggerKind::INVOKED,
1959 trigger_character: None,
1960 },
1961 })
1962 }
1963
1964 fn response_to_proto(
1965 completions: Vec<CoreCompletion>,
1966 _: &mut LspStore,
1967 _: PeerId,
1968 buffer_version: &clock::Global,
1969 _: &mut AppContext,
1970 ) -> proto::GetCompletionsResponse {
1971 proto::GetCompletionsResponse {
1972 completions: completions
1973 .iter()
1974 .map(LspStore::serialize_completion)
1975 .collect(),
1976 version: serialize_version(buffer_version),
1977 }
1978 }
1979
1980 async fn response_from_proto(
1981 self,
1982 message: proto::GetCompletionsResponse,
1983 _project: Model<LspStore>,
1984 buffer: Model<Buffer>,
1985 mut cx: AsyncAppContext,
1986 ) -> Result<Self::Response> {
1987 buffer
1988 .update(&mut cx, |buffer, _| {
1989 buffer.wait_for_version(deserialize_version(&message.version))
1990 })?
1991 .await?;
1992
1993 message
1994 .completions
1995 .into_iter()
1996 .map(LspStore::deserialize_completion)
1997 .collect()
1998 }
1999
2000 fn buffer_id_from_proto(message: &proto::GetCompletions) -> Result<BufferId> {
2001 BufferId::new(message.buffer_id)
2002 }
2003}
2004
2005pub(crate) fn parse_completion_text_edit(
2006 edit: &lsp::CompletionTextEdit,
2007 snapshot: &BufferSnapshot,
2008) -> Option<(Range<Anchor>, String)> {
2009 match edit {
2010 lsp::CompletionTextEdit::Edit(edit) => {
2011 let range = range_from_lsp(edit.range);
2012 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
2013 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
2014 if start != range.start.0 || end != range.end.0 {
2015 log::info!("completion out of expected range");
2016 None
2017 } else {
2018 Some((
2019 snapshot.anchor_before(start)..snapshot.anchor_after(end),
2020 edit.new_text.clone(),
2021 ))
2022 }
2023 }
2024
2025 lsp::CompletionTextEdit::InsertAndReplace(edit) => {
2026 let range = range_from_lsp(edit.insert);
2027
2028 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
2029 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
2030 if start != range.start.0 || end != range.end.0 {
2031 log::info!("completion out of expected range");
2032 None
2033 } else {
2034 Some((
2035 snapshot.anchor_before(start)..snapshot.anchor_after(end),
2036 edit.new_text.clone(),
2037 ))
2038 }
2039 }
2040 }
2041}
2042
2043#[async_trait(?Send)]
2044impl LspCommand for GetCodeActions {
2045 type Response = Vec<CodeAction>;
2046 type LspRequest = lsp::request::CodeActionRequest;
2047 type ProtoRequest = proto::GetCodeActions;
2048
2049 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
2050 match &capabilities.server_capabilities.code_action_provider {
2051 None => false,
2052 Some(lsp::CodeActionProviderCapability::Simple(false)) => false,
2053 _ => {
2054 // If we do know that we want specific code actions AND we know that
2055 // the server only supports specific code actions, then we want to filter
2056 // down to the ones that are supported.
2057 if let Some((requested, supported)) = self
2058 .kinds
2059 .as_ref()
2060 .zip(Self::supported_code_action_kinds(capabilities))
2061 {
2062 let server_supported = supported.into_iter().collect::<HashSet<_>>();
2063 requested.iter().any(|kind| server_supported.contains(kind))
2064 } else {
2065 true
2066 }
2067 }
2068 }
2069 }
2070
2071 fn to_lsp(
2072 &self,
2073 path: &Path,
2074 buffer: &Buffer,
2075 language_server: &Arc<LanguageServer>,
2076 _: &AppContext,
2077 ) -> lsp::CodeActionParams {
2078 let relevant_diagnostics = buffer
2079 .snapshot()
2080 .diagnostics_in_range::<_, language::PointUtf16>(self.range.clone(), false)
2081 .map(|entry| entry.to_lsp_diagnostic_stub())
2082 .collect::<Vec<_>>();
2083
2084 let supported =
2085 Self::supported_code_action_kinds(language_server.adapter_server_capabilities());
2086
2087 let only = if let Some(requested) = &self.kinds {
2088 if let Some(supported_kinds) = supported {
2089 let server_supported = supported_kinds.into_iter().collect::<HashSet<_>>();
2090
2091 let filtered = requested
2092 .iter()
2093 .filter(|kind| server_supported.contains(kind))
2094 .cloned()
2095 .collect();
2096 Some(filtered)
2097 } else {
2098 Some(requested.clone())
2099 }
2100 } else {
2101 supported
2102 };
2103
2104 lsp::CodeActionParams {
2105 text_document: lsp::TextDocumentIdentifier::new(
2106 lsp::Url::from_file_path(path).unwrap(),
2107 ),
2108 range: range_to_lsp(self.range.to_point_utf16(buffer)),
2109 work_done_progress_params: Default::default(),
2110 partial_result_params: Default::default(),
2111 context: lsp::CodeActionContext {
2112 diagnostics: relevant_diagnostics,
2113 only,
2114 ..lsp::CodeActionContext::default()
2115 },
2116 }
2117 }
2118
2119 async fn response_from_lsp(
2120 self,
2121 actions: Option<lsp::CodeActionResponse>,
2122 _: Model<LspStore>,
2123 _: Model<Buffer>,
2124 server_id: LanguageServerId,
2125 _: AsyncAppContext,
2126 ) -> Result<Vec<CodeAction>> {
2127 let requested_kinds_set = if let Some(kinds) = self.kinds {
2128 Some(kinds.into_iter().collect::<HashSet<_>>())
2129 } else {
2130 None
2131 };
2132
2133 Ok(actions
2134 .unwrap_or_default()
2135 .into_iter()
2136 .filter_map(|entry| {
2137 let lsp::CodeActionOrCommand::CodeAction(lsp_action) = entry else {
2138 return None;
2139 };
2140
2141 if let Some((requested_kinds, kind)) =
2142 requested_kinds_set.as_ref().zip(lsp_action.kind.as_ref())
2143 {
2144 if !requested_kinds.contains(kind) {
2145 return None;
2146 }
2147 }
2148
2149 Some(CodeAction {
2150 server_id,
2151 range: self.range.clone(),
2152 lsp_action,
2153 })
2154 })
2155 .collect())
2156 }
2157
2158 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCodeActions {
2159 proto::GetCodeActions {
2160 project_id,
2161 buffer_id: buffer.remote_id().into(),
2162 start: Some(language::proto::serialize_anchor(&self.range.start)),
2163 end: Some(language::proto::serialize_anchor(&self.range.end)),
2164 version: serialize_version(&buffer.version()),
2165 }
2166 }
2167
2168 async fn from_proto(
2169 message: proto::GetCodeActions,
2170 _: Model<LspStore>,
2171 buffer: Model<Buffer>,
2172 mut cx: AsyncAppContext,
2173 ) -> Result<Self> {
2174 let start = message
2175 .start
2176 .and_then(language::proto::deserialize_anchor)
2177 .ok_or_else(|| anyhow!("invalid start"))?;
2178 let end = message
2179 .end
2180 .and_then(language::proto::deserialize_anchor)
2181 .ok_or_else(|| anyhow!("invalid end"))?;
2182 buffer
2183 .update(&mut cx, |buffer, _| {
2184 buffer.wait_for_version(deserialize_version(&message.version))
2185 })?
2186 .await?;
2187
2188 Ok(Self {
2189 range: start..end,
2190 kinds: None,
2191 })
2192 }
2193
2194 fn response_to_proto(
2195 code_actions: Vec<CodeAction>,
2196 _: &mut LspStore,
2197 _: PeerId,
2198 buffer_version: &clock::Global,
2199 _: &mut AppContext,
2200 ) -> proto::GetCodeActionsResponse {
2201 proto::GetCodeActionsResponse {
2202 actions: code_actions
2203 .iter()
2204 .map(LspStore::serialize_code_action)
2205 .collect(),
2206 version: serialize_version(buffer_version),
2207 }
2208 }
2209
2210 async fn response_from_proto(
2211 self,
2212 message: proto::GetCodeActionsResponse,
2213 _: Model<LspStore>,
2214 buffer: Model<Buffer>,
2215 mut cx: AsyncAppContext,
2216 ) -> Result<Vec<CodeAction>> {
2217 buffer
2218 .update(&mut cx, |buffer, _| {
2219 buffer.wait_for_version(deserialize_version(&message.version))
2220 })?
2221 .await?;
2222 message
2223 .actions
2224 .into_iter()
2225 .map(LspStore::deserialize_code_action)
2226 .collect()
2227 }
2228
2229 fn buffer_id_from_proto(message: &proto::GetCodeActions) -> Result<BufferId> {
2230 BufferId::new(message.buffer_id)
2231 }
2232}
2233
2234impl GetCodeActions {
2235 fn supported_code_action_kinds(
2236 capabilities: AdapterServerCapabilities,
2237 ) -> Option<Vec<CodeActionKind>> {
2238 match capabilities.server_capabilities.code_action_provider {
2239 Some(lsp::CodeActionProviderCapability::Options(CodeActionOptions {
2240 code_action_kinds: Some(supported_action_kinds),
2241 ..
2242 })) => Some(supported_action_kinds.clone()),
2243 _ => capabilities.code_action_kinds,
2244 }
2245 }
2246
2247 pub fn can_resolve_actions(capabilities: &ServerCapabilities) -> bool {
2248 capabilities
2249 .code_action_provider
2250 .as_ref()
2251 .and_then(|options| match options {
2252 lsp::CodeActionProviderCapability::Simple(_is_supported) => None,
2253 lsp::CodeActionProviderCapability::Options(options) => options.resolve_provider,
2254 })
2255 .unwrap_or(false)
2256 }
2257}
2258
2259#[async_trait(?Send)]
2260impl LspCommand for OnTypeFormatting {
2261 type Response = Option<Transaction>;
2262 type LspRequest = lsp::request::OnTypeFormatting;
2263 type ProtoRequest = proto::OnTypeFormatting;
2264
2265 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
2266 let Some(on_type_formatting_options) = &capabilities
2267 .server_capabilities
2268 .document_on_type_formatting_provider
2269 else {
2270 return false;
2271 };
2272 on_type_formatting_options
2273 .first_trigger_character
2274 .contains(&self.trigger)
2275 || on_type_formatting_options
2276 .more_trigger_character
2277 .iter()
2278 .flatten()
2279 .any(|chars| chars.contains(&self.trigger))
2280 }
2281
2282 fn to_lsp(
2283 &self,
2284 path: &Path,
2285 _: &Buffer,
2286 _: &Arc<LanguageServer>,
2287 _: &AppContext,
2288 ) -> lsp::DocumentOnTypeFormattingParams {
2289 lsp::DocumentOnTypeFormattingParams {
2290 text_document_position: lsp::TextDocumentPositionParams::new(
2291 lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
2292 point_to_lsp(self.position),
2293 ),
2294 ch: self.trigger.clone(),
2295 options: self.options.clone(),
2296 }
2297 }
2298
2299 async fn response_from_lsp(
2300 self,
2301 message: Option<Vec<lsp::TextEdit>>,
2302 lsp_store: Model<LspStore>,
2303 buffer: Model<Buffer>,
2304 server_id: LanguageServerId,
2305 mut cx: AsyncAppContext,
2306 ) -> Result<Option<Transaction>> {
2307 if let Some(edits) = message {
2308 let (lsp_adapter, lsp_server) =
2309 language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
2310 LocalLspStore::deserialize_text_edits(
2311 lsp_store,
2312 buffer,
2313 edits,
2314 self.push_to_history,
2315 lsp_adapter,
2316 lsp_server,
2317 &mut cx,
2318 )
2319 .await
2320 } else {
2321 Ok(None)
2322 }
2323 }
2324
2325 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::OnTypeFormatting {
2326 proto::OnTypeFormatting {
2327 project_id,
2328 buffer_id: buffer.remote_id().into(),
2329 position: Some(language::proto::serialize_anchor(
2330 &buffer.anchor_before(self.position),
2331 )),
2332 trigger: self.trigger.clone(),
2333 version: serialize_version(&buffer.version()),
2334 }
2335 }
2336
2337 async fn from_proto(
2338 message: proto::OnTypeFormatting,
2339 _: Model<LspStore>,
2340 buffer: Model<Buffer>,
2341 mut cx: AsyncAppContext,
2342 ) -> Result<Self> {
2343 let position = message
2344 .position
2345 .and_then(deserialize_anchor)
2346 .ok_or_else(|| anyhow!("invalid position"))?;
2347 buffer
2348 .update(&mut cx, |buffer, _| {
2349 buffer.wait_for_version(deserialize_version(&message.version))
2350 })?
2351 .await?;
2352
2353 let options = buffer.update(&mut cx, |buffer, cx| {
2354 lsp_formatting_options(
2355 language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx).as_ref(),
2356 )
2357 })?;
2358
2359 Ok(Self {
2360 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
2361 trigger: message.trigger.clone(),
2362 options,
2363 push_to_history: false,
2364 })
2365 }
2366
2367 fn response_to_proto(
2368 response: Option<Transaction>,
2369 _: &mut LspStore,
2370 _: PeerId,
2371 _: &clock::Global,
2372 _: &mut AppContext,
2373 ) -> proto::OnTypeFormattingResponse {
2374 proto::OnTypeFormattingResponse {
2375 transaction: response
2376 .map(|transaction| language::proto::serialize_transaction(&transaction)),
2377 }
2378 }
2379
2380 async fn response_from_proto(
2381 self,
2382 message: proto::OnTypeFormattingResponse,
2383 _: Model<LspStore>,
2384 _: Model<Buffer>,
2385 _: AsyncAppContext,
2386 ) -> Result<Option<Transaction>> {
2387 let Some(transaction) = message.transaction else {
2388 return Ok(None);
2389 };
2390 Ok(Some(language::proto::deserialize_transaction(transaction)?))
2391 }
2392
2393 fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> Result<BufferId> {
2394 BufferId::new(message.buffer_id)
2395 }
2396}
2397
2398impl InlayHints {
2399 pub async fn lsp_to_project_hint(
2400 lsp_hint: lsp::InlayHint,
2401 buffer_handle: &Model<Buffer>,
2402 server_id: LanguageServerId,
2403 resolve_state: ResolveState,
2404 force_no_type_left_padding: bool,
2405 cx: &mut AsyncAppContext,
2406 ) -> anyhow::Result<InlayHint> {
2407 let kind = lsp_hint.kind.and_then(|kind| match kind {
2408 lsp::InlayHintKind::TYPE => Some(InlayHintKind::Type),
2409 lsp::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
2410 _ => None,
2411 });
2412
2413 let position = buffer_handle.update(cx, |buffer, _| {
2414 let position = buffer.clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
2415 if kind == Some(InlayHintKind::Parameter) {
2416 buffer.anchor_before(position)
2417 } else {
2418 buffer.anchor_after(position)
2419 }
2420 })?;
2421 let label = Self::lsp_inlay_label_to_project(lsp_hint.label, server_id)
2422 .await
2423 .context("lsp to project inlay hint conversion")?;
2424 let padding_left = if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
2425 false
2426 } else {
2427 lsp_hint.padding_left.unwrap_or(false)
2428 };
2429
2430 Ok(InlayHint {
2431 position,
2432 padding_left,
2433 padding_right: lsp_hint.padding_right.unwrap_or(false),
2434 label,
2435 kind,
2436 tooltip: lsp_hint.tooltip.map(|tooltip| match tooltip {
2437 lsp::InlayHintTooltip::String(s) => InlayHintTooltip::String(s),
2438 lsp::InlayHintTooltip::MarkupContent(markup_content) => {
2439 InlayHintTooltip::MarkupContent(MarkupContent {
2440 kind: match markup_content.kind {
2441 lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
2442 lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
2443 },
2444 value: markup_content.value,
2445 })
2446 }
2447 }),
2448 resolve_state,
2449 })
2450 }
2451
2452 async fn lsp_inlay_label_to_project(
2453 lsp_label: lsp::InlayHintLabel,
2454 server_id: LanguageServerId,
2455 ) -> anyhow::Result<InlayHintLabel> {
2456 let label = match lsp_label {
2457 lsp::InlayHintLabel::String(s) => InlayHintLabel::String(s),
2458 lsp::InlayHintLabel::LabelParts(lsp_parts) => {
2459 let mut parts = Vec::with_capacity(lsp_parts.len());
2460 for lsp_part in lsp_parts {
2461 parts.push(InlayHintLabelPart {
2462 value: lsp_part.value,
2463 tooltip: lsp_part.tooltip.map(|tooltip| match tooltip {
2464 lsp::InlayHintLabelPartTooltip::String(s) => {
2465 InlayHintLabelPartTooltip::String(s)
2466 }
2467 lsp::InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2468 InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2469 kind: match markup_content.kind {
2470 lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
2471 lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
2472 },
2473 value: markup_content.value,
2474 })
2475 }
2476 }),
2477 location: Some(server_id).zip(lsp_part.location),
2478 });
2479 }
2480 InlayHintLabel::LabelParts(parts)
2481 }
2482 };
2483
2484 Ok(label)
2485 }
2486
2487 pub fn project_to_proto_hint(response_hint: InlayHint) -> proto::InlayHint {
2488 let (state, lsp_resolve_state) = match response_hint.resolve_state {
2489 ResolveState::Resolved => (0, None),
2490 ResolveState::CanResolve(server_id, resolve_data) => (
2491 1,
2492 Some(proto::resolve_state::LspResolveState {
2493 server_id: server_id.0 as u64,
2494 value: resolve_data.map(|json_data| {
2495 serde_json::to_string(&json_data)
2496 .expect("failed to serialize resolve json data")
2497 }),
2498 }),
2499 ),
2500 ResolveState::Resolving => (2, None),
2501 };
2502 let resolve_state = Some(proto::ResolveState {
2503 state,
2504 lsp_resolve_state,
2505 });
2506 proto::InlayHint {
2507 position: Some(language::proto::serialize_anchor(&response_hint.position)),
2508 padding_left: response_hint.padding_left,
2509 padding_right: response_hint.padding_right,
2510 label: Some(proto::InlayHintLabel {
2511 label: Some(match response_hint.label {
2512 InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s),
2513 InlayHintLabel::LabelParts(label_parts) => {
2514 proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts {
2515 parts: label_parts.into_iter().map(|label_part| {
2516 let location_url = label_part.location.as_ref().map(|(_, location)| location.uri.to_string());
2517 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 });
2518 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 });
2519 proto::InlayHintLabelPart {
2520 value: label_part.value,
2521 tooltip: label_part.tooltip.map(|tooltip| {
2522 let proto_tooltip = match tooltip {
2523 InlayHintLabelPartTooltip::String(s) => proto::inlay_hint_label_part_tooltip::Content::Value(s),
2524 InlayHintLabelPartTooltip::MarkupContent(markup_content) => proto::inlay_hint_label_part_tooltip::Content::MarkupContent(proto::MarkupContent {
2525 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
2526 value: markup_content.value,
2527 }),
2528 };
2529 proto::InlayHintLabelPartTooltip {content: Some(proto_tooltip)}
2530 }),
2531 location_url,
2532 location_range_start,
2533 location_range_end,
2534 language_server_id: label_part.location.as_ref().map(|(server_id, _)| server_id.0 as u64),
2535 }}).collect()
2536 })
2537 }
2538 }),
2539 }),
2540 kind: response_hint.kind.map(|kind| kind.name().to_string()),
2541 tooltip: response_hint.tooltip.map(|response_tooltip| {
2542 let proto_tooltip = match response_tooltip {
2543 InlayHintTooltip::String(s) => proto::inlay_hint_tooltip::Content::Value(s),
2544 InlayHintTooltip::MarkupContent(markup_content) => {
2545 proto::inlay_hint_tooltip::Content::MarkupContent(proto::MarkupContent {
2546 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
2547 value: markup_content.value,
2548 })
2549 }
2550 };
2551 proto::InlayHintTooltip {
2552 content: Some(proto_tooltip),
2553 }
2554 }),
2555 resolve_state,
2556 }
2557 }
2558
2559 pub fn proto_to_project_hint(message_hint: proto::InlayHint) -> anyhow::Result<InlayHint> {
2560 let resolve_state = message_hint.resolve_state.as_ref().unwrap_or_else(|| {
2561 panic!("incorrect proto inlay hint message: no resolve state in hint {message_hint:?}",)
2562 });
2563 let resolve_state_data = resolve_state
2564 .lsp_resolve_state.as_ref()
2565 .map(|lsp_resolve_state| {
2566 let value = lsp_resolve_state.value.as_deref().map(|value| {
2567 serde_json::from_str::<Option<lsp::LSPAny>>(value)
2568 .with_context(|| format!("incorrect proto inlay hint message: non-json resolve state {lsp_resolve_state:?}"))
2569 }).transpose()?.flatten();
2570 anyhow::Ok((LanguageServerId(lsp_resolve_state.server_id as usize), value))
2571 })
2572 .transpose()?;
2573 let resolve_state = match resolve_state.state {
2574 0 => ResolveState::Resolved,
2575 1 => {
2576 let (server_id, lsp_resolve_state) = resolve_state_data.with_context(|| {
2577 format!(
2578 "No lsp resolve data for the hint that can be resolved: {message_hint:?}"
2579 )
2580 })?;
2581 ResolveState::CanResolve(server_id, lsp_resolve_state)
2582 }
2583 2 => ResolveState::Resolving,
2584 invalid => {
2585 anyhow::bail!("Unexpected resolve state {invalid} for hint {message_hint:?}")
2586 }
2587 };
2588 Ok(InlayHint {
2589 position: message_hint
2590 .position
2591 .and_then(language::proto::deserialize_anchor)
2592 .context("invalid position")?,
2593 label: match message_hint
2594 .label
2595 .and_then(|label| label.label)
2596 .context("missing label")?
2597 {
2598 proto::inlay_hint_label::Label::Value(s) => InlayHintLabel::String(s),
2599 proto::inlay_hint_label::Label::LabelParts(parts) => {
2600 let mut label_parts = Vec::new();
2601 for part in parts.parts {
2602 label_parts.push(InlayHintLabelPart {
2603 value: part.value,
2604 tooltip: part.tooltip.map(|tooltip| match tooltip.content {
2605 Some(proto::inlay_hint_label_part_tooltip::Content::Value(s)) => {
2606 InlayHintLabelPartTooltip::String(s)
2607 }
2608 Some(
2609 proto::inlay_hint_label_part_tooltip::Content::MarkupContent(
2610 markup_content,
2611 ),
2612 ) => InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2613 kind: if markup_content.is_markdown {
2614 HoverBlockKind::Markdown
2615 } else {
2616 HoverBlockKind::PlainText
2617 },
2618 value: markup_content.value,
2619 }),
2620 None => InlayHintLabelPartTooltip::String(String::new()),
2621 }),
2622 location: {
2623 match part
2624 .location_url
2625 .zip(
2626 part.location_range_start.and_then(|start| {
2627 Some(start..part.location_range_end?)
2628 }),
2629 )
2630 .zip(part.language_server_id)
2631 {
2632 Some(((uri, range), server_id)) => Some((
2633 LanguageServerId(server_id as usize),
2634 lsp::Location {
2635 uri: lsp::Url::parse(&uri)
2636 .context("invalid uri in hint part {part:?}")?,
2637 range: lsp::Range::new(
2638 point_to_lsp(PointUtf16::new(
2639 range.start.row,
2640 range.start.column,
2641 )),
2642 point_to_lsp(PointUtf16::new(
2643 range.end.row,
2644 range.end.column,
2645 )),
2646 ),
2647 },
2648 )),
2649 None => None,
2650 }
2651 },
2652 });
2653 }
2654
2655 InlayHintLabel::LabelParts(label_parts)
2656 }
2657 },
2658 padding_left: message_hint.padding_left,
2659 padding_right: message_hint.padding_right,
2660 kind: message_hint
2661 .kind
2662 .as_deref()
2663 .and_then(InlayHintKind::from_name),
2664 tooltip: message_hint.tooltip.and_then(|tooltip| {
2665 Some(match tooltip.content? {
2666 proto::inlay_hint_tooltip::Content::Value(s) => InlayHintTooltip::String(s),
2667 proto::inlay_hint_tooltip::Content::MarkupContent(markup_content) => {
2668 InlayHintTooltip::MarkupContent(MarkupContent {
2669 kind: if markup_content.is_markdown {
2670 HoverBlockKind::Markdown
2671 } else {
2672 HoverBlockKind::PlainText
2673 },
2674 value: markup_content.value,
2675 })
2676 }
2677 })
2678 }),
2679 resolve_state,
2680 })
2681 }
2682
2683 pub fn project_to_lsp_hint(hint: InlayHint, snapshot: &BufferSnapshot) -> lsp::InlayHint {
2684 lsp::InlayHint {
2685 position: point_to_lsp(hint.position.to_point_utf16(snapshot)),
2686 kind: hint.kind.map(|kind| match kind {
2687 InlayHintKind::Type => lsp::InlayHintKind::TYPE,
2688 InlayHintKind::Parameter => lsp::InlayHintKind::PARAMETER,
2689 }),
2690 text_edits: None,
2691 tooltip: hint.tooltip.and_then(|tooltip| {
2692 Some(match tooltip {
2693 InlayHintTooltip::String(s) => lsp::InlayHintTooltip::String(s),
2694 InlayHintTooltip::MarkupContent(markup_content) => {
2695 lsp::InlayHintTooltip::MarkupContent(lsp::MarkupContent {
2696 kind: match markup_content.kind {
2697 HoverBlockKind::PlainText => lsp::MarkupKind::PlainText,
2698 HoverBlockKind::Markdown => lsp::MarkupKind::Markdown,
2699 HoverBlockKind::Code { .. } => return None,
2700 },
2701 value: markup_content.value,
2702 })
2703 }
2704 })
2705 }),
2706 label: match hint.label {
2707 InlayHintLabel::String(s) => lsp::InlayHintLabel::String(s),
2708 InlayHintLabel::LabelParts(label_parts) => lsp::InlayHintLabel::LabelParts(
2709 label_parts
2710 .into_iter()
2711 .map(|part| lsp::InlayHintLabelPart {
2712 value: part.value,
2713 tooltip: part.tooltip.and_then(|tooltip| {
2714 Some(match tooltip {
2715 InlayHintLabelPartTooltip::String(s) => {
2716 lsp::InlayHintLabelPartTooltip::String(s)
2717 }
2718 InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2719 lsp::InlayHintLabelPartTooltip::MarkupContent(
2720 lsp::MarkupContent {
2721 kind: match markup_content.kind {
2722 HoverBlockKind::PlainText => {
2723 lsp::MarkupKind::PlainText
2724 }
2725 HoverBlockKind::Markdown => {
2726 lsp::MarkupKind::Markdown
2727 }
2728 HoverBlockKind::Code { .. } => return None,
2729 },
2730 value: markup_content.value,
2731 },
2732 )
2733 }
2734 })
2735 }),
2736 location: part.location.map(|(_, location)| location),
2737 command: None,
2738 })
2739 .collect(),
2740 ),
2741 },
2742 padding_left: Some(hint.padding_left),
2743 padding_right: Some(hint.padding_right),
2744 data: match hint.resolve_state {
2745 ResolveState::CanResolve(_, data) => data,
2746 ResolveState::Resolving | ResolveState::Resolved => None,
2747 },
2748 }
2749 }
2750
2751 pub fn can_resolve_inlays(capabilities: &ServerCapabilities) -> bool {
2752 capabilities
2753 .inlay_hint_provider
2754 .as_ref()
2755 .and_then(|options| match options {
2756 OneOf::Left(_is_supported) => None,
2757 OneOf::Right(capabilities) => match capabilities {
2758 lsp::InlayHintServerCapabilities::Options(o) => o.resolve_provider,
2759 lsp::InlayHintServerCapabilities::RegistrationOptions(o) => {
2760 o.inlay_hint_options.resolve_provider
2761 }
2762 },
2763 })
2764 .unwrap_or(false)
2765 }
2766}
2767
2768#[async_trait(?Send)]
2769impl LspCommand for InlayHints {
2770 type Response = Vec<InlayHint>;
2771 type LspRequest = lsp::InlayHintRequest;
2772 type ProtoRequest = proto::InlayHints;
2773
2774 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
2775 let Some(inlay_hint_provider) = &capabilities.server_capabilities.inlay_hint_provider
2776 else {
2777 return false;
2778 };
2779 match inlay_hint_provider {
2780 lsp::OneOf::Left(enabled) => *enabled,
2781 lsp::OneOf::Right(inlay_hint_capabilities) => match inlay_hint_capabilities {
2782 lsp::InlayHintServerCapabilities::Options(_) => true,
2783 lsp::InlayHintServerCapabilities::RegistrationOptions(_) => false,
2784 },
2785 }
2786 }
2787
2788 fn to_lsp(
2789 &self,
2790 path: &Path,
2791 buffer: &Buffer,
2792 _: &Arc<LanguageServer>,
2793 _: &AppContext,
2794 ) -> lsp::InlayHintParams {
2795 lsp::InlayHintParams {
2796 text_document: lsp::TextDocumentIdentifier {
2797 uri: lsp::Url::from_file_path(path).unwrap(),
2798 },
2799 range: range_to_lsp(self.range.to_point_utf16(buffer)),
2800 work_done_progress_params: Default::default(),
2801 }
2802 }
2803
2804 async fn response_from_lsp(
2805 self,
2806 message: Option<Vec<lsp::InlayHint>>,
2807 lsp_store: Model<LspStore>,
2808 buffer: Model<Buffer>,
2809 server_id: LanguageServerId,
2810 mut cx: AsyncAppContext,
2811 ) -> anyhow::Result<Vec<InlayHint>> {
2812 let (lsp_adapter, lsp_server) =
2813 language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
2814 // `typescript-language-server` adds padding to the left for type hints, turning
2815 // `const foo: boolean` into `const foo : boolean` which looks odd.
2816 // `rust-analyzer` does not have the padding for this case, and we have to accommodate both.
2817 //
2818 // We could trim the whole string, but being pessimistic on par with the situation above,
2819 // there might be a hint with multiple whitespaces at the end(s) which we need to display properly.
2820 // Hence let's use a heuristic first to handle the most awkward case and look for more.
2821 let force_no_type_left_padding =
2822 lsp_adapter.name.0.as_ref() == "typescript-language-server";
2823
2824 let hints = message.unwrap_or_default().into_iter().map(|lsp_hint| {
2825 let resolve_state = if InlayHints::can_resolve_inlays(&lsp_server.capabilities()) {
2826 ResolveState::CanResolve(lsp_server.server_id(), lsp_hint.data.clone())
2827 } else {
2828 ResolveState::Resolved
2829 };
2830
2831 let buffer = buffer.clone();
2832 cx.spawn(move |mut cx| async move {
2833 InlayHints::lsp_to_project_hint(
2834 lsp_hint,
2835 &buffer,
2836 server_id,
2837 resolve_state,
2838 force_no_type_left_padding,
2839 &mut cx,
2840 )
2841 .await
2842 })
2843 });
2844 future::join_all(hints)
2845 .await
2846 .into_iter()
2847 .collect::<anyhow::Result<_>>()
2848 .context("lsp to project inlay hints conversion")
2849 }
2850
2851 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints {
2852 proto::InlayHints {
2853 project_id,
2854 buffer_id: buffer.remote_id().into(),
2855 start: Some(language::proto::serialize_anchor(&self.range.start)),
2856 end: Some(language::proto::serialize_anchor(&self.range.end)),
2857 version: serialize_version(&buffer.version()),
2858 }
2859 }
2860
2861 async fn from_proto(
2862 message: proto::InlayHints,
2863 _: Model<LspStore>,
2864 buffer: Model<Buffer>,
2865 mut cx: AsyncAppContext,
2866 ) -> Result<Self> {
2867 let start = message
2868 .start
2869 .and_then(language::proto::deserialize_anchor)
2870 .context("invalid start")?;
2871 let end = message
2872 .end
2873 .and_then(language::proto::deserialize_anchor)
2874 .context("invalid end")?;
2875 buffer
2876 .update(&mut cx, |buffer, _| {
2877 buffer.wait_for_version(deserialize_version(&message.version))
2878 })?
2879 .await?;
2880
2881 Ok(Self { range: start..end })
2882 }
2883
2884 fn response_to_proto(
2885 response: Vec<InlayHint>,
2886 _: &mut LspStore,
2887 _: PeerId,
2888 buffer_version: &clock::Global,
2889 _: &mut AppContext,
2890 ) -> proto::InlayHintsResponse {
2891 proto::InlayHintsResponse {
2892 hints: response
2893 .into_iter()
2894 .map(InlayHints::project_to_proto_hint)
2895 .collect(),
2896 version: serialize_version(buffer_version),
2897 }
2898 }
2899
2900 async fn response_from_proto(
2901 self,
2902 message: proto::InlayHintsResponse,
2903 _: Model<LspStore>,
2904 buffer: Model<Buffer>,
2905 mut cx: AsyncAppContext,
2906 ) -> anyhow::Result<Vec<InlayHint>> {
2907 buffer
2908 .update(&mut cx, |buffer, _| {
2909 buffer.wait_for_version(deserialize_version(&message.version))
2910 })?
2911 .await?;
2912
2913 let mut hints = Vec::new();
2914 for message_hint in message.hints {
2915 hints.push(InlayHints::proto_to_project_hint(message_hint)?);
2916 }
2917
2918 Ok(hints)
2919 }
2920
2921 fn buffer_id_from_proto(message: &proto::InlayHints) -> Result<BufferId> {
2922 BufferId::new(message.buffer_id)
2923 }
2924}
2925
2926#[async_trait(?Send)]
2927impl LspCommand for LinkedEditingRange {
2928 type Response = Vec<Range<Anchor>>;
2929 type LspRequest = lsp::request::LinkedEditingRange;
2930 type ProtoRequest = proto::LinkedEditingRange;
2931
2932 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
2933 let Some(linked_editing_options) = &capabilities
2934 .server_capabilities
2935 .linked_editing_range_provider
2936 else {
2937 return false;
2938 };
2939 if let LinkedEditingRangeServerCapabilities::Simple(false) = linked_editing_options {
2940 return false;
2941 }
2942 true
2943 }
2944
2945 fn to_lsp(
2946 &self,
2947 path: &Path,
2948 buffer: &Buffer,
2949 _server: &Arc<LanguageServer>,
2950 _: &AppContext,
2951 ) -> lsp::LinkedEditingRangeParams {
2952 let position = self.position.to_point_utf16(&buffer.snapshot());
2953 lsp::LinkedEditingRangeParams {
2954 text_document_position_params: lsp::TextDocumentPositionParams::new(
2955 lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
2956 point_to_lsp(position),
2957 ),
2958 work_done_progress_params: Default::default(),
2959 }
2960 }
2961
2962 async fn response_from_lsp(
2963 self,
2964 message: Option<lsp::LinkedEditingRanges>,
2965 _: Model<LspStore>,
2966 buffer: Model<Buffer>,
2967 _server_id: LanguageServerId,
2968 cx: AsyncAppContext,
2969 ) -> Result<Vec<Range<Anchor>>> {
2970 if let Some(lsp::LinkedEditingRanges { mut ranges, .. }) = message {
2971 ranges.sort_by_key(|range| range.start);
2972
2973 buffer.read_with(&cx, |buffer, _| {
2974 ranges
2975 .into_iter()
2976 .map(|range| {
2977 let start =
2978 buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left);
2979 let end = buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left);
2980 buffer.anchor_before(start)..buffer.anchor_after(end)
2981 })
2982 .collect()
2983 })
2984 } else {
2985 Ok(vec![])
2986 }
2987 }
2988
2989 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::LinkedEditingRange {
2990 proto::LinkedEditingRange {
2991 project_id,
2992 buffer_id: buffer.remote_id().to_proto(),
2993 position: Some(serialize_anchor(&self.position)),
2994 version: serialize_version(&buffer.version()),
2995 }
2996 }
2997
2998 async fn from_proto(
2999 message: proto::LinkedEditingRange,
3000 _: Model<LspStore>,
3001 buffer: Model<Buffer>,
3002 mut cx: AsyncAppContext,
3003 ) -> Result<Self> {
3004 let position = message
3005 .position
3006 .ok_or_else(|| anyhow!("invalid position"))?;
3007 buffer
3008 .update(&mut cx, |buffer, _| {
3009 buffer.wait_for_version(deserialize_version(&message.version))
3010 })?
3011 .await?;
3012 let position = deserialize_anchor(position).ok_or_else(|| anyhow!("invalid position"))?;
3013 buffer
3014 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([position]))?
3015 .await?;
3016 Ok(Self { position })
3017 }
3018
3019 fn response_to_proto(
3020 response: Vec<Range<Anchor>>,
3021 _: &mut LspStore,
3022 _: PeerId,
3023 buffer_version: &clock::Global,
3024 _: &mut AppContext,
3025 ) -> proto::LinkedEditingRangeResponse {
3026 proto::LinkedEditingRangeResponse {
3027 items: response
3028 .into_iter()
3029 .map(|range| proto::AnchorRange {
3030 start: Some(serialize_anchor(&range.start)),
3031 end: Some(serialize_anchor(&range.end)),
3032 })
3033 .collect(),
3034 version: serialize_version(buffer_version),
3035 }
3036 }
3037
3038 async fn response_from_proto(
3039 self,
3040 message: proto::LinkedEditingRangeResponse,
3041 _: Model<LspStore>,
3042 buffer: Model<Buffer>,
3043 mut cx: AsyncAppContext,
3044 ) -> Result<Vec<Range<Anchor>>> {
3045 buffer
3046 .update(&mut cx, |buffer, _| {
3047 buffer.wait_for_version(deserialize_version(&message.version))
3048 })?
3049 .await?;
3050 let items: Vec<Range<Anchor>> = message
3051 .items
3052 .into_iter()
3053 .filter_map(|range| {
3054 let start = deserialize_anchor(range.start?)?;
3055 let end = deserialize_anchor(range.end?)?;
3056 Some(start..end)
3057 })
3058 .collect();
3059 for range in &items {
3060 buffer
3061 .update(&mut cx, |buffer, _| {
3062 buffer.wait_for_anchors([range.start, range.end])
3063 })?
3064 .await?;
3065 }
3066 Ok(items)
3067 }
3068
3069 fn buffer_id_from_proto(message: &proto::LinkedEditingRange) -> Result<BufferId> {
3070 BufferId::new(message.buffer_id)
3071 }
3072}