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