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