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 match &capabilities.server_capabilities.type_definition_provider {
732 None => false,
733 Some(lsp::TypeDefinitionProviderCapability::Simple(false)) => false,
734 _ => true,
735 }
736 }
737
738 fn to_lsp(
739 &self,
740 path: &Path,
741 _: &Buffer,
742 _: &Arc<LanguageServer>,
743 _: &AppContext,
744 ) -> lsp::GotoTypeDefinitionParams {
745 lsp::GotoTypeDefinitionParams {
746 text_document_position_params: lsp::TextDocumentPositionParams {
747 text_document: lsp::TextDocumentIdentifier {
748 uri: lsp::Url::from_file_path(path).unwrap(),
749 },
750 position: point_to_lsp(self.position),
751 },
752 work_done_progress_params: Default::default(),
753 partial_result_params: Default::default(),
754 }
755 }
756
757 async fn response_from_lsp(
758 self,
759 message: Option<lsp::GotoTypeDefinitionResponse>,
760 project: Model<LspStore>,
761 buffer: Model<Buffer>,
762 server_id: LanguageServerId,
763 cx: AsyncAppContext,
764 ) -> Result<Vec<LocationLink>> {
765 location_links_from_lsp(message, project, buffer, server_id, cx).await
766 }
767
768 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetTypeDefinition {
769 proto::GetTypeDefinition {
770 project_id,
771 buffer_id: buffer.remote_id().into(),
772 position: Some(language::proto::serialize_anchor(
773 &buffer.anchor_before(self.position),
774 )),
775 version: serialize_version(&buffer.version()),
776 }
777 }
778
779 async fn from_proto(
780 message: proto::GetTypeDefinition,
781 _: Model<LspStore>,
782 buffer: Model<Buffer>,
783 mut cx: AsyncAppContext,
784 ) -> Result<Self> {
785 let position = message
786 .position
787 .and_then(deserialize_anchor)
788 .ok_or_else(|| anyhow!("invalid position"))?;
789 buffer
790 .update(&mut cx, |buffer, _| {
791 buffer.wait_for_version(deserialize_version(&message.version))
792 })?
793 .await?;
794 Ok(Self {
795 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
796 })
797 }
798
799 fn response_to_proto(
800 response: Vec<LocationLink>,
801 lsp_store: &mut LspStore,
802 peer_id: PeerId,
803 _: &clock::Global,
804 cx: &mut AppContext,
805 ) -> proto::GetTypeDefinitionResponse {
806 let links = location_links_to_proto(response, lsp_store, peer_id, cx);
807 proto::GetTypeDefinitionResponse { links }
808 }
809
810 async fn response_from_proto(
811 self,
812 message: proto::GetTypeDefinitionResponse,
813 project: Model<LspStore>,
814 _: Model<Buffer>,
815 cx: AsyncAppContext,
816 ) -> Result<Vec<LocationLink>> {
817 location_links_from_proto(message.links, project, cx).await
818 }
819
820 fn buffer_id_from_proto(message: &proto::GetTypeDefinition) -> Result<BufferId> {
821 BufferId::new(message.buffer_id)
822 }
823}
824
825fn language_server_for_buffer(
826 lsp_store: &Model<LspStore>,
827 buffer: &Model<Buffer>,
828 server_id: LanguageServerId,
829 cx: &mut AsyncAppContext,
830) -> Result<(Arc<CachedLspAdapter>, Arc<LanguageServer>)> {
831 lsp_store
832 .update(cx, |lsp_store, cx| {
833 lsp_store
834 .language_server_for_buffer(buffer.read(cx), server_id, cx)
835 .map(|(adapter, server)| (adapter.clone(), server.clone()))
836 })?
837 .ok_or_else(|| anyhow!("no language server found for buffer"))
838}
839
840async fn location_links_from_proto(
841 proto_links: Vec<proto::LocationLink>,
842 lsp_store: Model<LspStore>,
843 mut cx: AsyncAppContext,
844) -> Result<Vec<LocationLink>> {
845 let mut links = Vec::new();
846
847 for link in proto_links {
848 let origin = match link.origin {
849 Some(origin) => {
850 let buffer_id = BufferId::new(origin.buffer_id)?;
851 let buffer = lsp_store
852 .update(&mut cx, |lsp_store, cx| {
853 lsp_store.wait_for_remote_buffer(buffer_id, cx)
854 })?
855 .await?;
856 let start = origin
857 .start
858 .and_then(deserialize_anchor)
859 .ok_or_else(|| anyhow!("missing origin start"))?;
860 let end = origin
861 .end
862 .and_then(deserialize_anchor)
863 .ok_or_else(|| anyhow!("missing origin end"))?;
864 buffer
865 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
866 .await?;
867 Some(Location {
868 buffer,
869 range: start..end,
870 })
871 }
872 None => None,
873 };
874
875 let target = link.target.ok_or_else(|| anyhow!("missing target"))?;
876 let buffer_id = BufferId::new(target.buffer_id)?;
877 let buffer = lsp_store
878 .update(&mut cx, |lsp_store, cx| {
879 lsp_store.wait_for_remote_buffer(buffer_id, cx)
880 })?
881 .await?;
882 let start = target
883 .start
884 .and_then(deserialize_anchor)
885 .ok_or_else(|| anyhow!("missing target start"))?;
886 let end = target
887 .end
888 .and_then(deserialize_anchor)
889 .ok_or_else(|| anyhow!("missing target end"))?;
890 buffer
891 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
892 .await?;
893 let target = Location {
894 buffer,
895 range: start..end,
896 };
897
898 links.push(LocationLink { origin, target })
899 }
900
901 Ok(links)
902}
903
904async fn location_links_from_lsp(
905 message: Option<lsp::GotoDefinitionResponse>,
906 lsp_store: Model<LspStore>,
907 buffer: Model<Buffer>,
908 server_id: LanguageServerId,
909 mut cx: AsyncAppContext,
910) -> Result<Vec<LocationLink>> {
911 let message = match message {
912 Some(message) => message,
913 None => return Ok(Vec::new()),
914 };
915
916 let mut unresolved_links = Vec::new();
917 match message {
918 lsp::GotoDefinitionResponse::Scalar(loc) => {
919 unresolved_links.push((None, loc.uri, loc.range));
920 }
921
922 lsp::GotoDefinitionResponse::Array(locs) => {
923 unresolved_links.extend(locs.into_iter().map(|l| (None, l.uri, l.range)));
924 }
925
926 lsp::GotoDefinitionResponse::Link(links) => {
927 unresolved_links.extend(links.into_iter().map(|l| {
928 (
929 l.origin_selection_range,
930 l.target_uri,
931 l.target_selection_range,
932 )
933 }));
934 }
935 }
936
937 let (lsp_adapter, language_server) =
938 language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
939 let mut definitions = Vec::new();
940 for (origin_range, target_uri, target_range) in unresolved_links {
941 let target_buffer_handle = lsp_store
942 .update(&mut cx, |this, cx| {
943 this.open_local_buffer_via_lsp(
944 target_uri,
945 language_server.server_id(),
946 lsp_adapter.name.clone(),
947 cx,
948 )
949 })?
950 .await?;
951
952 cx.update(|cx| {
953 let origin_location = origin_range.map(|origin_range| {
954 let origin_buffer = buffer.read(cx);
955 let origin_start =
956 origin_buffer.clip_point_utf16(point_from_lsp(origin_range.start), Bias::Left);
957 let origin_end =
958 origin_buffer.clip_point_utf16(point_from_lsp(origin_range.end), Bias::Left);
959 Location {
960 buffer: buffer.clone(),
961 range: origin_buffer.anchor_after(origin_start)
962 ..origin_buffer.anchor_before(origin_end),
963 }
964 });
965
966 let target_buffer = target_buffer_handle.read(cx);
967 let target_start =
968 target_buffer.clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
969 let target_end =
970 target_buffer.clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
971 let target_location = Location {
972 buffer: target_buffer_handle,
973 range: target_buffer.anchor_after(target_start)
974 ..target_buffer.anchor_before(target_end),
975 };
976
977 definitions.push(LocationLink {
978 origin: origin_location,
979 target: target_location,
980 })
981 })?;
982 }
983 Ok(definitions)
984}
985
986fn location_links_to_proto(
987 links: Vec<LocationLink>,
988 lsp_store: &mut LspStore,
989 peer_id: PeerId,
990 cx: &mut AppContext,
991) -> Vec<proto::LocationLink> {
992 links
993 .into_iter()
994 .map(|definition| {
995 let origin = definition.origin.map(|origin| {
996 lsp_store
997 .buffer_store()
998 .update(cx, |buffer_store, cx| {
999 buffer_store.create_buffer_for_peer(&origin.buffer, peer_id, cx)
1000 })
1001 .detach_and_log_err(cx);
1002
1003 let buffer_id = origin.buffer.read(cx).remote_id().into();
1004 proto::Location {
1005 start: Some(serialize_anchor(&origin.range.start)),
1006 end: Some(serialize_anchor(&origin.range.end)),
1007 buffer_id,
1008 }
1009 });
1010
1011 lsp_store
1012 .buffer_store()
1013 .update(cx, |buffer_store, cx| {
1014 buffer_store.create_buffer_for_peer(&definition.target.buffer, peer_id, cx)
1015 })
1016 .detach_and_log_err(cx);
1017
1018 let buffer_id = definition.target.buffer.read(cx).remote_id().into();
1019 let target = proto::Location {
1020 start: Some(serialize_anchor(&definition.target.range.start)),
1021 end: Some(serialize_anchor(&definition.target.range.end)),
1022 buffer_id,
1023 };
1024
1025 proto::LocationLink {
1026 origin,
1027 target: Some(target),
1028 }
1029 })
1030 .collect()
1031}
1032
1033#[async_trait(?Send)]
1034impl LspCommand for GetReferences {
1035 type Response = Vec<Location>;
1036 type LspRequest = lsp::request::References;
1037 type ProtoRequest = proto::GetReferences;
1038
1039 fn status(&self) -> Option<String> {
1040 return Some("Finding references...".to_owned());
1041 }
1042
1043 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
1044 match &capabilities.server_capabilities.references_provider {
1045 Some(OneOf::Left(has_support)) => *has_support,
1046 Some(OneOf::Right(_)) => true,
1047 None => false,
1048 }
1049 }
1050
1051 fn to_lsp(
1052 &self,
1053 path: &Path,
1054 _: &Buffer,
1055 _: &Arc<LanguageServer>,
1056 _: &AppContext,
1057 ) -> lsp::ReferenceParams {
1058 lsp::ReferenceParams {
1059 text_document_position: lsp::TextDocumentPositionParams {
1060 text_document: lsp::TextDocumentIdentifier {
1061 uri: lsp::Url::from_file_path(path).unwrap(),
1062 },
1063 position: point_to_lsp(self.position),
1064 },
1065 work_done_progress_params: Default::default(),
1066 partial_result_params: Default::default(),
1067 context: lsp::ReferenceContext {
1068 include_declaration: true,
1069 },
1070 }
1071 }
1072
1073 async fn response_from_lsp(
1074 self,
1075 locations: Option<Vec<lsp::Location>>,
1076 lsp_store: Model<LspStore>,
1077 buffer: Model<Buffer>,
1078 server_id: LanguageServerId,
1079 mut cx: AsyncAppContext,
1080 ) -> Result<Vec<Location>> {
1081 let mut references = Vec::new();
1082 let (lsp_adapter, language_server) =
1083 language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
1084
1085 if let Some(locations) = locations {
1086 for lsp_location in locations {
1087 let target_buffer_handle = lsp_store
1088 .update(&mut cx, |lsp_store, cx| {
1089 lsp_store.open_local_buffer_via_lsp(
1090 lsp_location.uri,
1091 language_server.server_id(),
1092 lsp_adapter.name.clone(),
1093 cx,
1094 )
1095 })?
1096 .await?;
1097
1098 target_buffer_handle
1099 .clone()
1100 .update(&mut cx, |target_buffer, _| {
1101 let target_start = target_buffer
1102 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
1103 let target_end = target_buffer
1104 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
1105 references.push(Location {
1106 buffer: target_buffer_handle,
1107 range: target_buffer.anchor_after(target_start)
1108 ..target_buffer.anchor_before(target_end),
1109 });
1110 })?;
1111 }
1112 }
1113
1114 Ok(references)
1115 }
1116
1117 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetReferences {
1118 proto::GetReferences {
1119 project_id,
1120 buffer_id: buffer.remote_id().into(),
1121 position: Some(language::proto::serialize_anchor(
1122 &buffer.anchor_before(self.position),
1123 )),
1124 version: serialize_version(&buffer.version()),
1125 }
1126 }
1127
1128 async fn from_proto(
1129 message: proto::GetReferences,
1130 _: Model<LspStore>,
1131 buffer: Model<Buffer>,
1132 mut cx: AsyncAppContext,
1133 ) -> Result<Self> {
1134 let position = message
1135 .position
1136 .and_then(deserialize_anchor)
1137 .ok_or_else(|| anyhow!("invalid position"))?;
1138 buffer
1139 .update(&mut cx, |buffer, _| {
1140 buffer.wait_for_version(deserialize_version(&message.version))
1141 })?
1142 .await?;
1143 Ok(Self {
1144 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
1145 })
1146 }
1147
1148 fn response_to_proto(
1149 response: Vec<Location>,
1150 lsp_store: &mut LspStore,
1151 peer_id: PeerId,
1152 _: &clock::Global,
1153 cx: &mut AppContext,
1154 ) -> proto::GetReferencesResponse {
1155 let locations = response
1156 .into_iter()
1157 .map(|definition| {
1158 lsp_store
1159 .buffer_store()
1160 .update(cx, |buffer_store, cx| {
1161 buffer_store.create_buffer_for_peer(&definition.buffer, peer_id, cx)
1162 })
1163 .detach_and_log_err(cx);
1164 let buffer_id = definition.buffer.read(cx).remote_id();
1165 proto::Location {
1166 start: Some(serialize_anchor(&definition.range.start)),
1167 end: Some(serialize_anchor(&definition.range.end)),
1168 buffer_id: buffer_id.into(),
1169 }
1170 })
1171 .collect();
1172 proto::GetReferencesResponse { locations }
1173 }
1174
1175 async fn response_from_proto(
1176 self,
1177 message: proto::GetReferencesResponse,
1178 project: Model<LspStore>,
1179 _: Model<Buffer>,
1180 mut cx: AsyncAppContext,
1181 ) -> Result<Vec<Location>> {
1182 let mut locations = Vec::new();
1183 for location in message.locations {
1184 let buffer_id = BufferId::new(location.buffer_id)?;
1185 let target_buffer = project
1186 .update(&mut cx, |this, cx| {
1187 this.wait_for_remote_buffer(buffer_id, cx)
1188 })?
1189 .await?;
1190 let start = location
1191 .start
1192 .and_then(deserialize_anchor)
1193 .ok_or_else(|| anyhow!("missing target start"))?;
1194 let end = location
1195 .end
1196 .and_then(deserialize_anchor)
1197 .ok_or_else(|| anyhow!("missing target end"))?;
1198 target_buffer
1199 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
1200 .await?;
1201 locations.push(Location {
1202 buffer: target_buffer,
1203 range: start..end,
1204 })
1205 }
1206 Ok(locations)
1207 }
1208
1209 fn buffer_id_from_proto(message: &proto::GetReferences) -> Result<BufferId> {
1210 BufferId::new(message.buffer_id)
1211 }
1212}
1213
1214#[async_trait(?Send)]
1215impl LspCommand for GetDocumentHighlights {
1216 type Response = Vec<DocumentHighlight>;
1217 type LspRequest = lsp::request::DocumentHighlightRequest;
1218 type ProtoRequest = proto::GetDocumentHighlights;
1219
1220 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
1221 capabilities
1222 .server_capabilities
1223 .document_highlight_provider
1224 .is_some()
1225 }
1226
1227 fn to_lsp(
1228 &self,
1229 path: &Path,
1230 _: &Buffer,
1231 _: &Arc<LanguageServer>,
1232 _: &AppContext,
1233 ) -> lsp::DocumentHighlightParams {
1234 lsp::DocumentHighlightParams {
1235 text_document_position_params: lsp::TextDocumentPositionParams {
1236 text_document: lsp::TextDocumentIdentifier {
1237 uri: lsp::Url::from_file_path(path).unwrap(),
1238 },
1239 position: point_to_lsp(self.position),
1240 },
1241 work_done_progress_params: Default::default(),
1242 partial_result_params: Default::default(),
1243 }
1244 }
1245
1246 async fn response_from_lsp(
1247 self,
1248 lsp_highlights: Option<Vec<lsp::DocumentHighlight>>,
1249 _: Model<LspStore>,
1250 buffer: Model<Buffer>,
1251 _: LanguageServerId,
1252 mut cx: AsyncAppContext,
1253 ) -> Result<Vec<DocumentHighlight>> {
1254 buffer.update(&mut cx, |buffer, _| {
1255 let mut lsp_highlights = lsp_highlights.unwrap_or_default();
1256 lsp_highlights.sort_unstable_by_key(|h| (h.range.start, Reverse(h.range.end)));
1257 lsp_highlights
1258 .into_iter()
1259 .map(|lsp_highlight| {
1260 let start = buffer
1261 .clip_point_utf16(point_from_lsp(lsp_highlight.range.start), Bias::Left);
1262 let end = buffer
1263 .clip_point_utf16(point_from_lsp(lsp_highlight.range.end), Bias::Left);
1264 DocumentHighlight {
1265 range: buffer.anchor_after(start)..buffer.anchor_before(end),
1266 kind: lsp_highlight
1267 .kind
1268 .unwrap_or(lsp::DocumentHighlightKind::READ),
1269 }
1270 })
1271 .collect()
1272 })
1273 }
1274
1275 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentHighlights {
1276 proto::GetDocumentHighlights {
1277 project_id,
1278 buffer_id: buffer.remote_id().into(),
1279 position: Some(language::proto::serialize_anchor(
1280 &buffer.anchor_before(self.position),
1281 )),
1282 version: serialize_version(&buffer.version()),
1283 }
1284 }
1285
1286 async fn from_proto(
1287 message: proto::GetDocumentHighlights,
1288 _: Model<LspStore>,
1289 buffer: Model<Buffer>,
1290 mut cx: AsyncAppContext,
1291 ) -> Result<Self> {
1292 let position = message
1293 .position
1294 .and_then(deserialize_anchor)
1295 .ok_or_else(|| anyhow!("invalid position"))?;
1296 buffer
1297 .update(&mut cx, |buffer, _| {
1298 buffer.wait_for_version(deserialize_version(&message.version))
1299 })?
1300 .await?;
1301 Ok(Self {
1302 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
1303 })
1304 }
1305
1306 fn response_to_proto(
1307 response: Vec<DocumentHighlight>,
1308 _: &mut LspStore,
1309 _: PeerId,
1310 _: &clock::Global,
1311 _: &mut AppContext,
1312 ) -> proto::GetDocumentHighlightsResponse {
1313 let highlights = response
1314 .into_iter()
1315 .map(|highlight| proto::DocumentHighlight {
1316 start: Some(serialize_anchor(&highlight.range.start)),
1317 end: Some(serialize_anchor(&highlight.range.end)),
1318 kind: match highlight.kind {
1319 DocumentHighlightKind::TEXT => proto::document_highlight::Kind::Text.into(),
1320 DocumentHighlightKind::WRITE => proto::document_highlight::Kind::Write.into(),
1321 DocumentHighlightKind::READ => proto::document_highlight::Kind::Read.into(),
1322 _ => proto::document_highlight::Kind::Text.into(),
1323 },
1324 })
1325 .collect();
1326 proto::GetDocumentHighlightsResponse { highlights }
1327 }
1328
1329 async fn response_from_proto(
1330 self,
1331 message: proto::GetDocumentHighlightsResponse,
1332 _: Model<LspStore>,
1333 buffer: Model<Buffer>,
1334 mut cx: AsyncAppContext,
1335 ) -> Result<Vec<DocumentHighlight>> {
1336 let mut highlights = Vec::new();
1337 for highlight in message.highlights {
1338 let start = highlight
1339 .start
1340 .and_then(deserialize_anchor)
1341 .ok_or_else(|| anyhow!("missing target start"))?;
1342 let end = highlight
1343 .end
1344 .and_then(deserialize_anchor)
1345 .ok_or_else(|| anyhow!("missing target end"))?;
1346 buffer
1347 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
1348 .await?;
1349 let kind = match proto::document_highlight::Kind::from_i32(highlight.kind) {
1350 Some(proto::document_highlight::Kind::Text) => DocumentHighlightKind::TEXT,
1351 Some(proto::document_highlight::Kind::Read) => DocumentHighlightKind::READ,
1352 Some(proto::document_highlight::Kind::Write) => DocumentHighlightKind::WRITE,
1353 None => DocumentHighlightKind::TEXT,
1354 };
1355 highlights.push(DocumentHighlight {
1356 range: start..end,
1357 kind,
1358 });
1359 }
1360 Ok(highlights)
1361 }
1362
1363 fn buffer_id_from_proto(message: &proto::GetDocumentHighlights) -> Result<BufferId> {
1364 BufferId::new(message.buffer_id)
1365 }
1366}
1367
1368#[async_trait(?Send)]
1369impl LspCommand for GetSignatureHelp {
1370 type Response = Option<SignatureHelp>;
1371 type LspRequest = lsp::SignatureHelpRequest;
1372 type ProtoRequest = proto::GetSignatureHelp;
1373
1374 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
1375 capabilities
1376 .server_capabilities
1377 .signature_help_provider
1378 .is_some()
1379 }
1380
1381 fn to_lsp(
1382 &self,
1383 path: &Path,
1384 _: &Buffer,
1385 _: &Arc<LanguageServer>,
1386 _cx: &AppContext,
1387 ) -> lsp::SignatureHelpParams {
1388 let url_result = lsp::Url::from_file_path(path);
1389 if url_result.is_err() {
1390 log::error!("an invalid file path has been specified");
1391 }
1392
1393 lsp::SignatureHelpParams {
1394 text_document_position_params: lsp::TextDocumentPositionParams {
1395 text_document: lsp::TextDocumentIdentifier {
1396 uri: url_result.expect("invalid file path"),
1397 },
1398 position: point_to_lsp(self.position),
1399 },
1400 context: None,
1401 work_done_progress_params: Default::default(),
1402 }
1403 }
1404
1405 async fn response_from_lsp(
1406 self,
1407 message: Option<lsp::SignatureHelp>,
1408 _: Model<LspStore>,
1409 buffer: Model<Buffer>,
1410 _: LanguageServerId,
1411 mut cx: AsyncAppContext,
1412 ) -> Result<Self::Response> {
1413 let language = buffer.update(&mut cx, |buffer, _| buffer.language().cloned())?;
1414 Ok(message.and_then(|message| SignatureHelp::new(message, language)))
1415 }
1416
1417 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
1418 let offset = buffer.point_utf16_to_offset(self.position);
1419 proto::GetSignatureHelp {
1420 project_id,
1421 buffer_id: buffer.remote_id().to_proto(),
1422 position: Some(serialize_anchor(&buffer.anchor_after(offset))),
1423 version: serialize_version(&buffer.version()),
1424 }
1425 }
1426
1427 async fn from_proto(
1428 payload: Self::ProtoRequest,
1429 _: Model<LspStore>,
1430 buffer: Model<Buffer>,
1431 mut cx: AsyncAppContext,
1432 ) -> Result<Self> {
1433 buffer
1434 .update(&mut cx, |buffer, _| {
1435 buffer.wait_for_version(deserialize_version(&payload.version))
1436 })?
1437 .await
1438 .with_context(|| format!("waiting for version for buffer {}", buffer.entity_id()))?;
1439 let buffer_snapshot = buffer.update(&mut cx, |buffer, _| buffer.snapshot())?;
1440 Ok(Self {
1441 position: payload
1442 .position
1443 .and_then(deserialize_anchor)
1444 .context("invalid position")?
1445 .to_point_utf16(&buffer_snapshot),
1446 })
1447 }
1448
1449 fn response_to_proto(
1450 response: Self::Response,
1451 _: &mut LspStore,
1452 _: PeerId,
1453 _: &Global,
1454 _: &mut AppContext,
1455 ) -> proto::GetSignatureHelpResponse {
1456 proto::GetSignatureHelpResponse {
1457 signature_help: response
1458 .map(|signature_help| lsp_to_proto_signature(signature_help.original_data)),
1459 }
1460 }
1461
1462 async fn response_from_proto(
1463 self,
1464 response: proto::GetSignatureHelpResponse,
1465 _: Model<LspStore>,
1466 buffer: Model<Buffer>,
1467 mut cx: AsyncAppContext,
1468 ) -> Result<Self::Response> {
1469 let language = buffer.update(&mut cx, |buffer, _| buffer.language().cloned())?;
1470 Ok(response
1471 .signature_help
1472 .map(|proto_help| proto_to_lsp_signature(proto_help))
1473 .and_then(|lsp_help| SignatureHelp::new(lsp_help, language)))
1474 }
1475
1476 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> Result<BufferId> {
1477 BufferId::new(message.buffer_id)
1478 }
1479}
1480
1481#[async_trait(?Send)]
1482impl LspCommand for GetHover {
1483 type Response = Option<Hover>;
1484 type LspRequest = lsp::request::HoverRequest;
1485 type ProtoRequest = proto::GetHover;
1486
1487 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
1488 match capabilities.server_capabilities.hover_provider {
1489 Some(lsp::HoverProviderCapability::Simple(enabled)) => enabled,
1490 Some(lsp::HoverProviderCapability::Options(_)) => true,
1491 None => false,
1492 }
1493 }
1494
1495 fn to_lsp(
1496 &self,
1497 path: &Path,
1498 _: &Buffer,
1499 _: &Arc<LanguageServer>,
1500 _: &AppContext,
1501 ) -> lsp::HoverParams {
1502 lsp::HoverParams {
1503 text_document_position_params: lsp::TextDocumentPositionParams {
1504 text_document: lsp::TextDocumentIdentifier {
1505 uri: lsp::Url::from_file_path(path).unwrap(),
1506 },
1507 position: point_to_lsp(self.position),
1508 },
1509 work_done_progress_params: Default::default(),
1510 }
1511 }
1512
1513 async fn response_from_lsp(
1514 self,
1515 message: Option<lsp::Hover>,
1516 _: Model<LspStore>,
1517 buffer: Model<Buffer>,
1518 _: LanguageServerId,
1519 mut cx: AsyncAppContext,
1520 ) -> Result<Self::Response> {
1521 let Some(hover) = message else {
1522 return Ok(None);
1523 };
1524
1525 let (language, range) = buffer.update(&mut cx, |buffer, _| {
1526 (
1527 buffer.language().cloned(),
1528 hover.range.map(|range| {
1529 let token_start =
1530 buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left);
1531 let token_end = buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left);
1532 buffer.anchor_after(token_start)..buffer.anchor_before(token_end)
1533 }),
1534 )
1535 })?;
1536
1537 fn hover_blocks_from_marked_string(marked_string: lsp::MarkedString) -> Option<HoverBlock> {
1538 let block = match marked_string {
1539 lsp::MarkedString::String(content) => HoverBlock {
1540 text: content,
1541 kind: HoverBlockKind::Markdown,
1542 },
1543 lsp::MarkedString::LanguageString(lsp::LanguageString { language, value }) => {
1544 HoverBlock {
1545 text: value,
1546 kind: HoverBlockKind::Code { language },
1547 }
1548 }
1549 };
1550 if block.text.is_empty() {
1551 None
1552 } else {
1553 Some(block)
1554 }
1555 }
1556
1557 let contents = match hover.contents {
1558 lsp::HoverContents::Scalar(marked_string) => {
1559 hover_blocks_from_marked_string(marked_string)
1560 .into_iter()
1561 .collect()
1562 }
1563 lsp::HoverContents::Array(marked_strings) => marked_strings
1564 .into_iter()
1565 .filter_map(hover_blocks_from_marked_string)
1566 .collect(),
1567 lsp::HoverContents::Markup(markup_content) => vec![HoverBlock {
1568 text: markup_content.value,
1569 kind: if markup_content.kind == lsp::MarkupKind::Markdown {
1570 HoverBlockKind::Markdown
1571 } else {
1572 HoverBlockKind::PlainText
1573 },
1574 }],
1575 };
1576
1577 Ok(Some(Hover {
1578 contents,
1579 range,
1580 language,
1581 }))
1582 }
1583
1584 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
1585 proto::GetHover {
1586 project_id,
1587 buffer_id: buffer.remote_id().into(),
1588 position: Some(language::proto::serialize_anchor(
1589 &buffer.anchor_before(self.position),
1590 )),
1591 version: serialize_version(&buffer.version),
1592 }
1593 }
1594
1595 async fn from_proto(
1596 message: Self::ProtoRequest,
1597 _: Model<LspStore>,
1598 buffer: Model<Buffer>,
1599 mut cx: AsyncAppContext,
1600 ) -> Result<Self> {
1601 let position = message
1602 .position
1603 .and_then(deserialize_anchor)
1604 .ok_or_else(|| anyhow!("invalid position"))?;
1605 buffer
1606 .update(&mut cx, |buffer, _| {
1607 buffer.wait_for_version(deserialize_version(&message.version))
1608 })?
1609 .await?;
1610 Ok(Self {
1611 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
1612 })
1613 }
1614
1615 fn response_to_proto(
1616 response: Self::Response,
1617 _: &mut LspStore,
1618 _: PeerId,
1619 _: &clock::Global,
1620 _: &mut AppContext,
1621 ) -> proto::GetHoverResponse {
1622 if let Some(response) = response {
1623 let (start, end) = if let Some(range) = response.range {
1624 (
1625 Some(language::proto::serialize_anchor(&range.start)),
1626 Some(language::proto::serialize_anchor(&range.end)),
1627 )
1628 } else {
1629 (None, None)
1630 };
1631
1632 let contents = response
1633 .contents
1634 .into_iter()
1635 .map(|block| proto::HoverBlock {
1636 text: block.text,
1637 is_markdown: block.kind == HoverBlockKind::Markdown,
1638 language: if let HoverBlockKind::Code { language } = block.kind {
1639 Some(language)
1640 } else {
1641 None
1642 },
1643 })
1644 .collect();
1645
1646 proto::GetHoverResponse {
1647 start,
1648 end,
1649 contents,
1650 }
1651 } else {
1652 proto::GetHoverResponse {
1653 start: None,
1654 end: None,
1655 contents: Vec::new(),
1656 }
1657 }
1658 }
1659
1660 async fn response_from_proto(
1661 self,
1662 message: proto::GetHoverResponse,
1663 _: Model<LspStore>,
1664 buffer: Model<Buffer>,
1665 mut cx: AsyncAppContext,
1666 ) -> Result<Self::Response> {
1667 let contents: Vec<_> = message
1668 .contents
1669 .into_iter()
1670 .map(|block| HoverBlock {
1671 text: block.text,
1672 kind: if let Some(language) = block.language {
1673 HoverBlockKind::Code { language }
1674 } else if block.is_markdown {
1675 HoverBlockKind::Markdown
1676 } else {
1677 HoverBlockKind::PlainText
1678 },
1679 })
1680 .collect();
1681 if contents.is_empty() {
1682 return Ok(None);
1683 }
1684
1685 let language = buffer.update(&mut cx, |buffer, _| buffer.language().cloned())?;
1686 let range = if let (Some(start), Some(end)) = (message.start, message.end) {
1687 language::proto::deserialize_anchor(start)
1688 .and_then(|start| language::proto::deserialize_anchor(end).map(|end| start..end))
1689 } else {
1690 None
1691 };
1692 if let Some(range) = range.as_ref() {
1693 buffer
1694 .update(&mut cx, |buffer, _| {
1695 buffer.wait_for_anchors([range.start, range.end])
1696 })?
1697 .await?;
1698 }
1699
1700 Ok(Some(Hover {
1701 contents,
1702 range,
1703 language,
1704 }))
1705 }
1706
1707 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> Result<BufferId> {
1708 BufferId::new(message.buffer_id)
1709 }
1710}
1711
1712#[async_trait(?Send)]
1713impl LspCommand for GetCompletions {
1714 type Response = Vec<CoreCompletion>;
1715 type LspRequest = lsp::request::Completion;
1716 type ProtoRequest = proto::GetCompletions;
1717
1718 fn to_lsp(
1719 &self,
1720 path: &Path,
1721 _: &Buffer,
1722 _: &Arc<LanguageServer>,
1723 _: &AppContext,
1724 ) -> lsp::CompletionParams {
1725 lsp::CompletionParams {
1726 text_document_position: lsp::TextDocumentPositionParams::new(
1727 lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
1728 point_to_lsp(self.position),
1729 ),
1730 context: Some(self.context.clone()),
1731 work_done_progress_params: Default::default(),
1732 partial_result_params: Default::default(),
1733 }
1734 }
1735
1736 async fn response_from_lsp(
1737 self,
1738 completions: Option<lsp::CompletionResponse>,
1739 lsp_store: Model<LspStore>,
1740 buffer: Model<Buffer>,
1741 server_id: LanguageServerId,
1742 mut cx: AsyncAppContext,
1743 ) -> Result<Self::Response> {
1744 let mut response_list = None;
1745 let mut completions = if let Some(completions) = completions {
1746 match completions {
1747 lsp::CompletionResponse::Array(completions) => completions,
1748
1749 lsp::CompletionResponse::List(mut list) => {
1750 let items = std::mem::take(&mut list.items);
1751 response_list = Some(list);
1752 items
1753 }
1754 }
1755 } else {
1756 Default::default()
1757 };
1758
1759 let language_server_adapter = lsp_store
1760 .update(&mut cx, |lsp_store, _| {
1761 lsp_store.language_server_adapter_for_id(server_id)
1762 })?
1763 .ok_or_else(|| anyhow!("no such language server"))?;
1764
1765 let item_defaults = response_list
1766 .as_ref()
1767 .and_then(|list| list.item_defaults.as_ref());
1768
1769 if let Some(item_defaults) = item_defaults {
1770 let default_data = item_defaults.data.as_ref();
1771 let default_commit_characters = item_defaults.commit_characters.as_ref();
1772 let default_insert_text_mode = item_defaults.insert_text_mode.as_ref();
1773
1774 if default_data.is_some()
1775 || default_commit_characters.is_some()
1776 || default_insert_text_mode.is_some()
1777 {
1778 for item in completions.iter_mut() {
1779 if let Some(data) = default_data {
1780 item.data = Some(data.clone())
1781 }
1782 if let Some(characters) = default_commit_characters {
1783 item.commit_characters = Some(characters.clone())
1784 }
1785 if let Some(text_mode) = default_insert_text_mode {
1786 item.insert_text_mode = Some(*text_mode)
1787 }
1788 }
1789 }
1790 }
1791
1792 let mut completion_edits = Vec::new();
1793 buffer.update(&mut cx, |buffer, _cx| {
1794 let snapshot = buffer.snapshot();
1795 let clipped_position = buffer.clip_point_utf16(Unclipped(self.position), Bias::Left);
1796
1797 let mut range_for_token = None;
1798 completions.retain_mut(|lsp_completion| {
1799 let edit = match lsp_completion.text_edit.as_ref() {
1800 // If the language server provides a range to overwrite, then
1801 // check that the range is valid.
1802 Some(completion_text_edit) => {
1803 match parse_completion_text_edit(completion_text_edit, &snapshot) {
1804 Some(edit) => edit,
1805 None => return false,
1806 }
1807 }
1808
1809 // If the language server does not provide a range, then infer
1810 // the range based on the syntax tree.
1811 None => {
1812 if self.position != clipped_position {
1813 log::info!("completion out of expected range");
1814 return false;
1815 }
1816
1817 let default_edit_range = response_list
1818 .as_ref()
1819 .and_then(|list| list.item_defaults.as_ref())
1820 .and_then(|defaults| defaults.edit_range.as_ref())
1821 .and_then(|range| match range {
1822 CompletionListItemDefaultsEditRange::Range(r) => Some(r),
1823 _ => None,
1824 });
1825
1826 let range = if let Some(range) = default_edit_range {
1827 let range = range_from_lsp(*range);
1828 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1829 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1830 if start != range.start.0 || end != range.end.0 {
1831 log::info!("completion out of expected range");
1832 return false;
1833 }
1834
1835 snapshot.anchor_before(start)..snapshot.anchor_after(end)
1836 } else {
1837 range_for_token
1838 .get_or_insert_with(|| {
1839 let offset = self.position.to_offset(&snapshot);
1840 let (range, kind) = snapshot.surrounding_word(offset);
1841 let range = if kind == Some(CharKind::Word) {
1842 range
1843 } else {
1844 offset..offset
1845 };
1846
1847 snapshot.anchor_before(range.start)
1848 ..snapshot.anchor_after(range.end)
1849 })
1850 .clone()
1851 };
1852
1853 let text = lsp_completion
1854 .insert_text
1855 .as_ref()
1856 .unwrap_or(&lsp_completion.label)
1857 .clone();
1858 (range, text)
1859 }
1860 };
1861
1862 completion_edits.push(edit);
1863 true
1864 });
1865 })?;
1866
1867 language_server_adapter
1868 .process_completions(&mut completions)
1869 .await;
1870
1871 Ok(completions
1872 .into_iter()
1873 .zip(completion_edits)
1874 .map(|(lsp_completion, (old_range, mut new_text))| {
1875 LineEnding::normalize(&mut new_text);
1876 CoreCompletion {
1877 old_range,
1878 new_text,
1879 server_id,
1880 lsp_completion,
1881 }
1882 })
1883 .collect())
1884 }
1885
1886 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCompletions {
1887 let anchor = buffer.anchor_after(self.position);
1888 proto::GetCompletions {
1889 project_id,
1890 buffer_id: buffer.remote_id().into(),
1891 position: Some(language::proto::serialize_anchor(&anchor)),
1892 version: serialize_version(&buffer.version()),
1893 }
1894 }
1895
1896 async fn from_proto(
1897 message: proto::GetCompletions,
1898 _: Model<LspStore>,
1899 buffer: Model<Buffer>,
1900 mut cx: AsyncAppContext,
1901 ) -> Result<Self> {
1902 let version = deserialize_version(&message.version);
1903 buffer
1904 .update(&mut cx, |buffer, _| buffer.wait_for_version(version))?
1905 .await?;
1906 let position = message
1907 .position
1908 .and_then(language::proto::deserialize_anchor)
1909 .map(|p| {
1910 buffer.update(&mut cx, |buffer, _| {
1911 buffer.clip_point_utf16(Unclipped(p.to_point_utf16(buffer)), Bias::Left)
1912 })
1913 })
1914 .ok_or_else(|| anyhow!("invalid position"))??;
1915 Ok(Self {
1916 position,
1917 context: CompletionContext {
1918 trigger_kind: CompletionTriggerKind::INVOKED,
1919 trigger_character: None,
1920 },
1921 })
1922 }
1923
1924 fn response_to_proto(
1925 completions: Vec<CoreCompletion>,
1926 _: &mut LspStore,
1927 _: PeerId,
1928 buffer_version: &clock::Global,
1929 _: &mut AppContext,
1930 ) -> proto::GetCompletionsResponse {
1931 proto::GetCompletionsResponse {
1932 completions: completions
1933 .iter()
1934 .map(LspStore::serialize_completion)
1935 .collect(),
1936 version: serialize_version(buffer_version),
1937 }
1938 }
1939
1940 async fn response_from_proto(
1941 self,
1942 message: proto::GetCompletionsResponse,
1943 _project: Model<LspStore>,
1944 buffer: Model<Buffer>,
1945 mut cx: AsyncAppContext,
1946 ) -> Result<Self::Response> {
1947 buffer
1948 .update(&mut cx, |buffer, _| {
1949 buffer.wait_for_version(deserialize_version(&message.version))
1950 })?
1951 .await?;
1952
1953 message
1954 .completions
1955 .into_iter()
1956 .map(LspStore::deserialize_completion)
1957 .collect()
1958 }
1959
1960 fn buffer_id_from_proto(message: &proto::GetCompletions) -> Result<BufferId> {
1961 BufferId::new(message.buffer_id)
1962 }
1963}
1964
1965pub(crate) fn parse_completion_text_edit(
1966 edit: &lsp::CompletionTextEdit,
1967 snapshot: &BufferSnapshot,
1968) -> Option<(Range<Anchor>, String)> {
1969 match edit {
1970 lsp::CompletionTextEdit::Edit(edit) => {
1971 let range = range_from_lsp(edit.range);
1972 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1973 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1974 if start != range.start.0 || end != range.end.0 {
1975 log::info!("completion out of expected range");
1976 None
1977 } else {
1978 Some((
1979 snapshot.anchor_before(start)..snapshot.anchor_after(end),
1980 edit.new_text.clone(),
1981 ))
1982 }
1983 }
1984
1985 lsp::CompletionTextEdit::InsertAndReplace(edit) => {
1986 let range = range_from_lsp(edit.insert);
1987
1988 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1989 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1990 if start != range.start.0 || end != range.end.0 {
1991 log::info!("completion out of expected range");
1992 None
1993 } else {
1994 Some((
1995 snapshot.anchor_before(start)..snapshot.anchor_after(end),
1996 edit.new_text.clone(),
1997 ))
1998 }
1999 }
2000 }
2001}
2002
2003#[async_trait(?Send)]
2004impl LspCommand for GetCodeActions {
2005 type Response = Vec<CodeAction>;
2006 type LspRequest = lsp::request::CodeActionRequest;
2007 type ProtoRequest = proto::GetCodeActions;
2008
2009 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
2010 match &capabilities.server_capabilities.code_action_provider {
2011 None => false,
2012 Some(lsp::CodeActionProviderCapability::Simple(false)) => false,
2013 _ => {
2014 // If we do know that we want specific code actions AND we know that
2015 // the server only supports specific code actions, then we want to filter
2016 // down to the ones that are supported.
2017 if let Some((requested, supported)) = self
2018 .kinds
2019 .as_ref()
2020 .zip(Self::supported_code_action_kinds(capabilities))
2021 {
2022 let server_supported = supported.into_iter().collect::<HashSet<_>>();
2023 requested.iter().any(|kind| server_supported.contains(kind))
2024 } else {
2025 true
2026 }
2027 }
2028 }
2029 }
2030
2031 fn to_lsp(
2032 &self,
2033 path: &Path,
2034 buffer: &Buffer,
2035 language_server: &Arc<LanguageServer>,
2036 _: &AppContext,
2037 ) -> lsp::CodeActionParams {
2038 let relevant_diagnostics = buffer
2039 .snapshot()
2040 .diagnostics_in_range::<_, language::PointUtf16>(self.range.clone(), false)
2041 .map(|entry| entry.to_lsp_diagnostic_stub())
2042 .collect::<Vec<_>>();
2043
2044 let supported =
2045 Self::supported_code_action_kinds(language_server.adapter_server_capabilities());
2046
2047 let only = if let Some(requested) = &self.kinds {
2048 if let Some(supported_kinds) = supported {
2049 let server_supported = supported_kinds.into_iter().collect::<HashSet<_>>();
2050
2051 let filtered = requested
2052 .iter()
2053 .filter(|kind| server_supported.contains(kind))
2054 .cloned()
2055 .collect();
2056 Some(filtered)
2057 } else {
2058 Some(requested.clone())
2059 }
2060 } else {
2061 supported
2062 };
2063
2064 lsp::CodeActionParams {
2065 text_document: lsp::TextDocumentIdentifier::new(
2066 lsp::Url::from_file_path(path).unwrap(),
2067 ),
2068 range: range_to_lsp(self.range.to_point_utf16(buffer)),
2069 work_done_progress_params: Default::default(),
2070 partial_result_params: Default::default(),
2071 context: lsp::CodeActionContext {
2072 diagnostics: relevant_diagnostics,
2073 only,
2074 ..lsp::CodeActionContext::default()
2075 },
2076 }
2077 }
2078
2079 async fn response_from_lsp(
2080 self,
2081 actions: Option<lsp::CodeActionResponse>,
2082 _: Model<LspStore>,
2083 _: Model<Buffer>,
2084 server_id: LanguageServerId,
2085 _: AsyncAppContext,
2086 ) -> Result<Vec<CodeAction>> {
2087 Ok(actions
2088 .unwrap_or_default()
2089 .into_iter()
2090 .filter_map(|entry| {
2091 if let lsp::CodeActionOrCommand::CodeAction(lsp_action) = entry {
2092 Some(CodeAction {
2093 server_id,
2094 range: self.range.clone(),
2095 lsp_action,
2096 })
2097 } else {
2098 None
2099 }
2100 })
2101 .collect())
2102 }
2103
2104 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCodeActions {
2105 proto::GetCodeActions {
2106 project_id,
2107 buffer_id: buffer.remote_id().into(),
2108 start: Some(language::proto::serialize_anchor(&self.range.start)),
2109 end: Some(language::proto::serialize_anchor(&self.range.end)),
2110 version: serialize_version(&buffer.version()),
2111 }
2112 }
2113
2114 async fn from_proto(
2115 message: proto::GetCodeActions,
2116 _: Model<LspStore>,
2117 buffer: Model<Buffer>,
2118 mut cx: AsyncAppContext,
2119 ) -> Result<Self> {
2120 let start = message
2121 .start
2122 .and_then(language::proto::deserialize_anchor)
2123 .ok_or_else(|| anyhow!("invalid start"))?;
2124 let end = message
2125 .end
2126 .and_then(language::proto::deserialize_anchor)
2127 .ok_or_else(|| anyhow!("invalid end"))?;
2128 buffer
2129 .update(&mut cx, |buffer, _| {
2130 buffer.wait_for_version(deserialize_version(&message.version))
2131 })?
2132 .await?;
2133
2134 Ok(Self {
2135 range: start..end,
2136 kinds: None,
2137 })
2138 }
2139
2140 fn response_to_proto(
2141 code_actions: Vec<CodeAction>,
2142 _: &mut LspStore,
2143 _: PeerId,
2144 buffer_version: &clock::Global,
2145 _: &mut AppContext,
2146 ) -> proto::GetCodeActionsResponse {
2147 proto::GetCodeActionsResponse {
2148 actions: code_actions
2149 .iter()
2150 .map(LspStore::serialize_code_action)
2151 .collect(),
2152 version: serialize_version(buffer_version),
2153 }
2154 }
2155
2156 async fn response_from_proto(
2157 self,
2158 message: proto::GetCodeActionsResponse,
2159 _: Model<LspStore>,
2160 buffer: Model<Buffer>,
2161 mut cx: AsyncAppContext,
2162 ) -> Result<Vec<CodeAction>> {
2163 buffer
2164 .update(&mut cx, |buffer, _| {
2165 buffer.wait_for_version(deserialize_version(&message.version))
2166 })?
2167 .await?;
2168 message
2169 .actions
2170 .into_iter()
2171 .map(LspStore::deserialize_code_action)
2172 .collect()
2173 }
2174
2175 fn buffer_id_from_proto(message: &proto::GetCodeActions) -> Result<BufferId> {
2176 BufferId::new(message.buffer_id)
2177 }
2178}
2179
2180impl GetCodeActions {
2181 fn supported_code_action_kinds(
2182 capabilities: AdapterServerCapabilities,
2183 ) -> Option<Vec<CodeActionKind>> {
2184 match capabilities.server_capabilities.code_action_provider {
2185 Some(lsp::CodeActionProviderCapability::Options(CodeActionOptions {
2186 code_action_kinds: Some(supported_action_kinds),
2187 ..
2188 })) => Some(supported_action_kinds.clone()),
2189 _ => capabilities.code_action_kinds,
2190 }
2191 }
2192
2193 pub fn can_resolve_actions(capabilities: &ServerCapabilities) -> bool {
2194 capabilities
2195 .code_action_provider
2196 .as_ref()
2197 .and_then(|options| match options {
2198 lsp::CodeActionProviderCapability::Simple(_is_supported) => None,
2199 lsp::CodeActionProviderCapability::Options(options) => options.resolve_provider,
2200 })
2201 .unwrap_or(false)
2202 }
2203}
2204
2205#[async_trait(?Send)]
2206impl LspCommand for OnTypeFormatting {
2207 type Response = Option<Transaction>;
2208 type LspRequest = lsp::request::OnTypeFormatting;
2209 type ProtoRequest = proto::OnTypeFormatting;
2210
2211 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
2212 let Some(on_type_formatting_options) = &capabilities
2213 .server_capabilities
2214 .document_on_type_formatting_provider
2215 else {
2216 return false;
2217 };
2218 on_type_formatting_options
2219 .first_trigger_character
2220 .contains(&self.trigger)
2221 || on_type_formatting_options
2222 .more_trigger_character
2223 .iter()
2224 .flatten()
2225 .any(|chars| chars.contains(&self.trigger))
2226 }
2227
2228 fn to_lsp(
2229 &self,
2230 path: &Path,
2231 _: &Buffer,
2232 _: &Arc<LanguageServer>,
2233 _: &AppContext,
2234 ) -> lsp::DocumentOnTypeFormattingParams {
2235 lsp::DocumentOnTypeFormattingParams {
2236 text_document_position: lsp::TextDocumentPositionParams::new(
2237 lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
2238 point_to_lsp(self.position),
2239 ),
2240 ch: self.trigger.clone(),
2241 options: self.options.clone(),
2242 }
2243 }
2244
2245 async fn response_from_lsp(
2246 self,
2247 message: Option<Vec<lsp::TextEdit>>,
2248 lsp_store: Model<LspStore>,
2249 buffer: Model<Buffer>,
2250 server_id: LanguageServerId,
2251 mut cx: AsyncAppContext,
2252 ) -> Result<Option<Transaction>> {
2253 if let Some(edits) = message {
2254 let (lsp_adapter, lsp_server) =
2255 language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
2256 LspStore::deserialize_text_edits(
2257 lsp_store,
2258 buffer,
2259 edits,
2260 self.push_to_history,
2261 lsp_adapter,
2262 lsp_server,
2263 &mut cx,
2264 )
2265 .await
2266 } else {
2267 Ok(None)
2268 }
2269 }
2270
2271 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::OnTypeFormatting {
2272 proto::OnTypeFormatting {
2273 project_id,
2274 buffer_id: buffer.remote_id().into(),
2275 position: Some(language::proto::serialize_anchor(
2276 &buffer.anchor_before(self.position),
2277 )),
2278 trigger: self.trigger.clone(),
2279 version: serialize_version(&buffer.version()),
2280 }
2281 }
2282
2283 async fn from_proto(
2284 message: proto::OnTypeFormatting,
2285 _: Model<LspStore>,
2286 buffer: Model<Buffer>,
2287 mut cx: AsyncAppContext,
2288 ) -> Result<Self> {
2289 let position = message
2290 .position
2291 .and_then(deserialize_anchor)
2292 .ok_or_else(|| anyhow!("invalid position"))?;
2293 buffer
2294 .update(&mut cx, |buffer, _| {
2295 buffer.wait_for_version(deserialize_version(&message.version))
2296 })?
2297 .await?;
2298
2299 let options = buffer.update(&mut cx, |buffer, cx| {
2300 lsp_formatting_options(language_settings(buffer.language(), buffer.file(), cx))
2301 })?;
2302
2303 Ok(Self {
2304 position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
2305 trigger: message.trigger.clone(),
2306 options,
2307 push_to_history: false,
2308 })
2309 }
2310
2311 fn response_to_proto(
2312 response: Option<Transaction>,
2313 _: &mut LspStore,
2314 _: PeerId,
2315 _: &clock::Global,
2316 _: &mut AppContext,
2317 ) -> proto::OnTypeFormattingResponse {
2318 proto::OnTypeFormattingResponse {
2319 transaction: response
2320 .map(|transaction| language::proto::serialize_transaction(&transaction)),
2321 }
2322 }
2323
2324 async fn response_from_proto(
2325 self,
2326 message: proto::OnTypeFormattingResponse,
2327 _: Model<LspStore>,
2328 _: Model<Buffer>,
2329 _: AsyncAppContext,
2330 ) -> Result<Option<Transaction>> {
2331 let Some(transaction) = message.transaction else {
2332 return Ok(None);
2333 };
2334 Ok(Some(language::proto::deserialize_transaction(transaction)?))
2335 }
2336
2337 fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> Result<BufferId> {
2338 BufferId::new(message.buffer_id)
2339 }
2340}
2341
2342impl InlayHints {
2343 pub async fn lsp_to_project_hint(
2344 lsp_hint: lsp::InlayHint,
2345 buffer_handle: &Model<Buffer>,
2346 server_id: LanguageServerId,
2347 resolve_state: ResolveState,
2348 force_no_type_left_padding: bool,
2349 cx: &mut AsyncAppContext,
2350 ) -> anyhow::Result<InlayHint> {
2351 let kind = lsp_hint.kind.and_then(|kind| match kind {
2352 lsp::InlayHintKind::TYPE => Some(InlayHintKind::Type),
2353 lsp::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
2354 _ => None,
2355 });
2356
2357 let position = buffer_handle.update(cx, |buffer, _| {
2358 let position = buffer.clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
2359 if kind == Some(InlayHintKind::Parameter) {
2360 buffer.anchor_before(position)
2361 } else {
2362 buffer.anchor_after(position)
2363 }
2364 })?;
2365 let label = Self::lsp_inlay_label_to_project(lsp_hint.label, server_id)
2366 .await
2367 .context("lsp to project inlay hint conversion")?;
2368 let padding_left = if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
2369 false
2370 } else {
2371 lsp_hint.padding_left.unwrap_or(false)
2372 };
2373
2374 Ok(InlayHint {
2375 position,
2376 padding_left,
2377 padding_right: lsp_hint.padding_right.unwrap_or(false),
2378 label,
2379 kind,
2380 tooltip: lsp_hint.tooltip.map(|tooltip| match tooltip {
2381 lsp::InlayHintTooltip::String(s) => InlayHintTooltip::String(s),
2382 lsp::InlayHintTooltip::MarkupContent(markup_content) => {
2383 InlayHintTooltip::MarkupContent(MarkupContent {
2384 kind: match markup_content.kind {
2385 lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
2386 lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
2387 },
2388 value: markup_content.value,
2389 })
2390 }
2391 }),
2392 resolve_state,
2393 })
2394 }
2395
2396 async fn lsp_inlay_label_to_project(
2397 lsp_label: lsp::InlayHintLabel,
2398 server_id: LanguageServerId,
2399 ) -> anyhow::Result<InlayHintLabel> {
2400 let label = match lsp_label {
2401 lsp::InlayHintLabel::String(s) => InlayHintLabel::String(s),
2402 lsp::InlayHintLabel::LabelParts(lsp_parts) => {
2403 let mut parts = Vec::with_capacity(lsp_parts.len());
2404 for lsp_part in lsp_parts {
2405 parts.push(InlayHintLabelPart {
2406 value: lsp_part.value,
2407 tooltip: lsp_part.tooltip.map(|tooltip| match tooltip {
2408 lsp::InlayHintLabelPartTooltip::String(s) => {
2409 InlayHintLabelPartTooltip::String(s)
2410 }
2411 lsp::InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2412 InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2413 kind: match markup_content.kind {
2414 lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
2415 lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
2416 },
2417 value: markup_content.value,
2418 })
2419 }
2420 }),
2421 location: Some(server_id).zip(lsp_part.location),
2422 });
2423 }
2424 InlayHintLabel::LabelParts(parts)
2425 }
2426 };
2427
2428 Ok(label)
2429 }
2430
2431 pub fn project_to_proto_hint(response_hint: InlayHint) -> proto::InlayHint {
2432 let (state, lsp_resolve_state) = match response_hint.resolve_state {
2433 ResolveState::Resolved => (0, None),
2434 ResolveState::CanResolve(server_id, resolve_data) => (
2435 1,
2436 resolve_data
2437 .map(|json_data| {
2438 serde_json::to_string(&json_data)
2439 .expect("failed to serialize resolve json data")
2440 })
2441 .map(|value| proto::resolve_state::LspResolveState {
2442 server_id: server_id.0 as u64,
2443 value,
2444 }),
2445 ),
2446 ResolveState::Resolving => (2, None),
2447 };
2448 let resolve_state = Some(proto::ResolveState {
2449 state,
2450 lsp_resolve_state,
2451 });
2452 proto::InlayHint {
2453 position: Some(language::proto::serialize_anchor(&response_hint.position)),
2454 padding_left: response_hint.padding_left,
2455 padding_right: response_hint.padding_right,
2456 label: Some(proto::InlayHintLabel {
2457 label: Some(match response_hint.label {
2458 InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s),
2459 InlayHintLabel::LabelParts(label_parts) => {
2460 proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts {
2461 parts: label_parts.into_iter().map(|label_part| {
2462 let location_url = label_part.location.as_ref().map(|(_, location)| location.uri.to_string());
2463 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 });
2464 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 });
2465 proto::InlayHintLabelPart {
2466 value: label_part.value,
2467 tooltip: label_part.tooltip.map(|tooltip| {
2468 let proto_tooltip = match tooltip {
2469 InlayHintLabelPartTooltip::String(s) => proto::inlay_hint_label_part_tooltip::Content::Value(s),
2470 InlayHintLabelPartTooltip::MarkupContent(markup_content) => proto::inlay_hint_label_part_tooltip::Content::MarkupContent(proto::MarkupContent {
2471 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
2472 value: markup_content.value,
2473 }),
2474 };
2475 proto::InlayHintLabelPartTooltip {content: Some(proto_tooltip)}
2476 }),
2477 location_url,
2478 location_range_start,
2479 location_range_end,
2480 language_server_id: label_part.location.as_ref().map(|(server_id, _)| server_id.0 as u64),
2481 }}).collect()
2482 })
2483 }
2484 }),
2485 }),
2486 kind: response_hint.kind.map(|kind| kind.name().to_string()),
2487 tooltip: response_hint.tooltip.map(|response_tooltip| {
2488 let proto_tooltip = match response_tooltip {
2489 InlayHintTooltip::String(s) => proto::inlay_hint_tooltip::Content::Value(s),
2490 InlayHintTooltip::MarkupContent(markup_content) => {
2491 proto::inlay_hint_tooltip::Content::MarkupContent(proto::MarkupContent {
2492 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
2493 value: markup_content.value,
2494 })
2495 }
2496 };
2497 proto::InlayHintTooltip {
2498 content: Some(proto_tooltip),
2499 }
2500 }),
2501 resolve_state,
2502 }
2503 }
2504
2505 pub fn proto_to_project_hint(message_hint: proto::InlayHint) -> anyhow::Result<InlayHint> {
2506 let resolve_state = message_hint.resolve_state.as_ref().unwrap_or_else(|| {
2507 panic!("incorrect proto inlay hint message: no resolve state in hint {message_hint:?}",)
2508 });
2509 let resolve_state_data = resolve_state
2510 .lsp_resolve_state.as_ref()
2511 .map(|lsp_resolve_state| {
2512 serde_json::from_str::<Option<lsp::LSPAny>>(&lsp_resolve_state.value)
2513 .with_context(|| format!("incorrect proto inlay hint message: non-json resolve state {lsp_resolve_state:?}"))
2514 .map(|state| (LanguageServerId(lsp_resolve_state.server_id as usize), state))
2515 })
2516 .transpose()?;
2517 let resolve_state = match resolve_state.state {
2518 0 => ResolveState::Resolved,
2519 1 => {
2520 let (server_id, lsp_resolve_state) = resolve_state_data.with_context(|| {
2521 format!(
2522 "No lsp resolve data for the hint that can be resolved: {message_hint:?}"
2523 )
2524 })?;
2525 ResolveState::CanResolve(server_id, lsp_resolve_state)
2526 }
2527 2 => ResolveState::Resolving,
2528 invalid => {
2529 anyhow::bail!("Unexpected resolve state {invalid} for hint {message_hint:?}")
2530 }
2531 };
2532 Ok(InlayHint {
2533 position: message_hint
2534 .position
2535 .and_then(language::proto::deserialize_anchor)
2536 .context("invalid position")?,
2537 label: match message_hint
2538 .label
2539 .and_then(|label| label.label)
2540 .context("missing label")?
2541 {
2542 proto::inlay_hint_label::Label::Value(s) => InlayHintLabel::String(s),
2543 proto::inlay_hint_label::Label::LabelParts(parts) => {
2544 let mut label_parts = Vec::new();
2545 for part in parts.parts {
2546 label_parts.push(InlayHintLabelPart {
2547 value: part.value,
2548 tooltip: part.tooltip.map(|tooltip| match tooltip.content {
2549 Some(proto::inlay_hint_label_part_tooltip::Content::Value(s)) => {
2550 InlayHintLabelPartTooltip::String(s)
2551 }
2552 Some(
2553 proto::inlay_hint_label_part_tooltip::Content::MarkupContent(
2554 markup_content,
2555 ),
2556 ) => InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2557 kind: if markup_content.is_markdown {
2558 HoverBlockKind::Markdown
2559 } else {
2560 HoverBlockKind::PlainText
2561 },
2562 value: markup_content.value,
2563 }),
2564 None => InlayHintLabelPartTooltip::String(String::new()),
2565 }),
2566 location: {
2567 match part
2568 .location_url
2569 .zip(
2570 part.location_range_start.and_then(|start| {
2571 Some(start..part.location_range_end?)
2572 }),
2573 )
2574 .zip(part.language_server_id)
2575 {
2576 Some(((uri, range), server_id)) => Some((
2577 LanguageServerId(server_id as usize),
2578 lsp::Location {
2579 uri: lsp::Url::parse(&uri)
2580 .context("invalid uri in hint part {part:?}")?,
2581 range: lsp::Range::new(
2582 point_to_lsp(PointUtf16::new(
2583 range.start.row,
2584 range.start.column,
2585 )),
2586 point_to_lsp(PointUtf16::new(
2587 range.end.row,
2588 range.end.column,
2589 )),
2590 ),
2591 },
2592 )),
2593 None => None,
2594 }
2595 },
2596 });
2597 }
2598
2599 InlayHintLabel::LabelParts(label_parts)
2600 }
2601 },
2602 padding_left: message_hint.padding_left,
2603 padding_right: message_hint.padding_right,
2604 kind: message_hint
2605 .kind
2606 .as_deref()
2607 .and_then(InlayHintKind::from_name),
2608 tooltip: message_hint.tooltip.and_then(|tooltip| {
2609 Some(match tooltip.content? {
2610 proto::inlay_hint_tooltip::Content::Value(s) => InlayHintTooltip::String(s),
2611 proto::inlay_hint_tooltip::Content::MarkupContent(markup_content) => {
2612 InlayHintTooltip::MarkupContent(MarkupContent {
2613 kind: if markup_content.is_markdown {
2614 HoverBlockKind::Markdown
2615 } else {
2616 HoverBlockKind::PlainText
2617 },
2618 value: markup_content.value,
2619 })
2620 }
2621 })
2622 }),
2623 resolve_state,
2624 })
2625 }
2626
2627 pub fn project_to_lsp_hint(hint: InlayHint, snapshot: &BufferSnapshot) -> lsp::InlayHint {
2628 lsp::InlayHint {
2629 position: point_to_lsp(hint.position.to_point_utf16(snapshot)),
2630 kind: hint.kind.map(|kind| match kind {
2631 InlayHintKind::Type => lsp::InlayHintKind::TYPE,
2632 InlayHintKind::Parameter => lsp::InlayHintKind::PARAMETER,
2633 }),
2634 text_edits: None,
2635 tooltip: hint.tooltip.and_then(|tooltip| {
2636 Some(match tooltip {
2637 InlayHintTooltip::String(s) => lsp::InlayHintTooltip::String(s),
2638 InlayHintTooltip::MarkupContent(markup_content) => {
2639 lsp::InlayHintTooltip::MarkupContent(lsp::MarkupContent {
2640 kind: match markup_content.kind {
2641 HoverBlockKind::PlainText => lsp::MarkupKind::PlainText,
2642 HoverBlockKind::Markdown => lsp::MarkupKind::Markdown,
2643 HoverBlockKind::Code { .. } => return None,
2644 },
2645 value: markup_content.value,
2646 })
2647 }
2648 })
2649 }),
2650 label: match hint.label {
2651 InlayHintLabel::String(s) => lsp::InlayHintLabel::String(s),
2652 InlayHintLabel::LabelParts(label_parts) => lsp::InlayHintLabel::LabelParts(
2653 label_parts
2654 .into_iter()
2655 .map(|part| lsp::InlayHintLabelPart {
2656 value: part.value,
2657 tooltip: part.tooltip.and_then(|tooltip| {
2658 Some(match tooltip {
2659 InlayHintLabelPartTooltip::String(s) => {
2660 lsp::InlayHintLabelPartTooltip::String(s)
2661 }
2662 InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2663 lsp::InlayHintLabelPartTooltip::MarkupContent(
2664 lsp::MarkupContent {
2665 kind: match markup_content.kind {
2666 HoverBlockKind::PlainText => {
2667 lsp::MarkupKind::PlainText
2668 }
2669 HoverBlockKind::Markdown => {
2670 lsp::MarkupKind::Markdown
2671 }
2672 HoverBlockKind::Code { .. } => return None,
2673 },
2674 value: markup_content.value,
2675 },
2676 )
2677 }
2678 })
2679 }),
2680 location: part.location.map(|(_, location)| location),
2681 command: None,
2682 })
2683 .collect(),
2684 ),
2685 },
2686 padding_left: Some(hint.padding_left),
2687 padding_right: Some(hint.padding_right),
2688 data: match hint.resolve_state {
2689 ResolveState::CanResolve(_, data) => data,
2690 ResolveState::Resolving | ResolveState::Resolved => None,
2691 },
2692 }
2693 }
2694
2695 pub fn can_resolve_inlays(capabilities: &ServerCapabilities) -> bool {
2696 capabilities
2697 .inlay_hint_provider
2698 .as_ref()
2699 .and_then(|options| match options {
2700 OneOf::Left(_is_supported) => None,
2701 OneOf::Right(capabilities) => match capabilities {
2702 lsp::InlayHintServerCapabilities::Options(o) => o.resolve_provider,
2703 lsp::InlayHintServerCapabilities::RegistrationOptions(o) => {
2704 o.inlay_hint_options.resolve_provider
2705 }
2706 },
2707 })
2708 .unwrap_or(false)
2709 }
2710}
2711
2712#[async_trait(?Send)]
2713impl LspCommand for InlayHints {
2714 type Response = Vec<InlayHint>;
2715 type LspRequest = lsp::InlayHintRequest;
2716 type ProtoRequest = proto::InlayHints;
2717
2718 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
2719 let Some(inlay_hint_provider) = &capabilities.server_capabilities.inlay_hint_provider
2720 else {
2721 return false;
2722 };
2723 match inlay_hint_provider {
2724 lsp::OneOf::Left(enabled) => *enabled,
2725 lsp::OneOf::Right(inlay_hint_capabilities) => match inlay_hint_capabilities {
2726 lsp::InlayHintServerCapabilities::Options(_) => true,
2727 lsp::InlayHintServerCapabilities::RegistrationOptions(_) => false,
2728 },
2729 }
2730 }
2731
2732 fn to_lsp(
2733 &self,
2734 path: &Path,
2735 buffer: &Buffer,
2736 _: &Arc<LanguageServer>,
2737 _: &AppContext,
2738 ) -> lsp::InlayHintParams {
2739 lsp::InlayHintParams {
2740 text_document: lsp::TextDocumentIdentifier {
2741 uri: lsp::Url::from_file_path(path).unwrap(),
2742 },
2743 range: range_to_lsp(self.range.to_point_utf16(buffer)),
2744 work_done_progress_params: Default::default(),
2745 }
2746 }
2747
2748 async fn response_from_lsp(
2749 self,
2750 message: Option<Vec<lsp::InlayHint>>,
2751 lsp_store: Model<LspStore>,
2752 buffer: Model<Buffer>,
2753 server_id: LanguageServerId,
2754 mut cx: AsyncAppContext,
2755 ) -> anyhow::Result<Vec<InlayHint>> {
2756 let (lsp_adapter, lsp_server) =
2757 language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
2758 // `typescript-language-server` adds padding to the left for type hints, turning
2759 // `const foo: boolean` into `const foo : boolean` which looks odd.
2760 // `rust-analyzer` does not have the padding for this case, and we have to accommodate both.
2761 //
2762 // We could trim the whole string, but being pessimistic on par with the situation above,
2763 // there might be a hint with multiple whitespaces at the end(s) which we need to display properly.
2764 // Hence let's use a heuristic first to handle the most awkward case and look for more.
2765 let force_no_type_left_padding =
2766 lsp_adapter.name.0.as_ref() == "typescript-language-server";
2767
2768 let hints = message.unwrap_or_default().into_iter().map(|lsp_hint| {
2769 let resolve_state = if InlayHints::can_resolve_inlays(&lsp_server.capabilities()) {
2770 ResolveState::CanResolve(lsp_server.server_id(), lsp_hint.data.clone())
2771 } else {
2772 ResolveState::Resolved
2773 };
2774
2775 let buffer = buffer.clone();
2776 cx.spawn(move |mut cx| async move {
2777 InlayHints::lsp_to_project_hint(
2778 lsp_hint,
2779 &buffer,
2780 server_id,
2781 resolve_state,
2782 force_no_type_left_padding,
2783 &mut cx,
2784 )
2785 .await
2786 })
2787 });
2788 future::join_all(hints)
2789 .await
2790 .into_iter()
2791 .collect::<anyhow::Result<_>>()
2792 .context("lsp to project inlay hints conversion")
2793 }
2794
2795 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints {
2796 proto::InlayHints {
2797 project_id,
2798 buffer_id: buffer.remote_id().into(),
2799 start: Some(language::proto::serialize_anchor(&self.range.start)),
2800 end: Some(language::proto::serialize_anchor(&self.range.end)),
2801 version: serialize_version(&buffer.version()),
2802 }
2803 }
2804
2805 async fn from_proto(
2806 message: proto::InlayHints,
2807 _: Model<LspStore>,
2808 buffer: Model<Buffer>,
2809 mut cx: AsyncAppContext,
2810 ) -> Result<Self> {
2811 let start = message
2812 .start
2813 .and_then(language::proto::deserialize_anchor)
2814 .context("invalid start")?;
2815 let end = message
2816 .end
2817 .and_then(language::proto::deserialize_anchor)
2818 .context("invalid end")?;
2819 buffer
2820 .update(&mut cx, |buffer, _| {
2821 buffer.wait_for_version(deserialize_version(&message.version))
2822 })?
2823 .await?;
2824
2825 Ok(Self { range: start..end })
2826 }
2827
2828 fn response_to_proto(
2829 response: Vec<InlayHint>,
2830 _: &mut LspStore,
2831 _: PeerId,
2832 buffer_version: &clock::Global,
2833 _: &mut AppContext,
2834 ) -> proto::InlayHintsResponse {
2835 proto::InlayHintsResponse {
2836 hints: response
2837 .into_iter()
2838 .map(|response_hint| InlayHints::project_to_proto_hint(response_hint))
2839 .collect(),
2840 version: serialize_version(buffer_version),
2841 }
2842 }
2843
2844 async fn response_from_proto(
2845 self,
2846 message: proto::InlayHintsResponse,
2847 _: Model<LspStore>,
2848 buffer: Model<Buffer>,
2849 mut cx: AsyncAppContext,
2850 ) -> anyhow::Result<Vec<InlayHint>> {
2851 buffer
2852 .update(&mut cx, |buffer, _| {
2853 buffer.wait_for_version(deserialize_version(&message.version))
2854 })?
2855 .await?;
2856
2857 let mut hints = Vec::new();
2858 for message_hint in message.hints {
2859 hints.push(InlayHints::proto_to_project_hint(message_hint)?);
2860 }
2861
2862 Ok(hints)
2863 }
2864
2865 fn buffer_id_from_proto(message: &proto::InlayHints) -> Result<BufferId> {
2866 BufferId::new(message.buffer_id)
2867 }
2868}
2869
2870#[async_trait(?Send)]
2871impl LspCommand for LinkedEditingRange {
2872 type Response = Vec<Range<Anchor>>;
2873 type LspRequest = lsp::request::LinkedEditingRange;
2874 type ProtoRequest = proto::LinkedEditingRange;
2875
2876 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
2877 let Some(linked_editing_options) = &capabilities
2878 .server_capabilities
2879 .linked_editing_range_provider
2880 else {
2881 return false;
2882 };
2883 if let LinkedEditingRangeServerCapabilities::Simple(false) = linked_editing_options {
2884 return false;
2885 }
2886 return true;
2887 }
2888
2889 fn to_lsp(
2890 &self,
2891 path: &Path,
2892 buffer: &Buffer,
2893 _server: &Arc<LanguageServer>,
2894 _: &AppContext,
2895 ) -> lsp::LinkedEditingRangeParams {
2896 let position = self.position.to_point_utf16(&buffer.snapshot());
2897 lsp::LinkedEditingRangeParams {
2898 text_document_position_params: lsp::TextDocumentPositionParams::new(
2899 lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
2900 point_to_lsp(position),
2901 ),
2902 work_done_progress_params: Default::default(),
2903 }
2904 }
2905
2906 async fn response_from_lsp(
2907 self,
2908 message: Option<lsp::LinkedEditingRanges>,
2909 _: Model<LspStore>,
2910 buffer: Model<Buffer>,
2911 _server_id: LanguageServerId,
2912 cx: AsyncAppContext,
2913 ) -> Result<Vec<Range<Anchor>>> {
2914 if let Some(lsp::LinkedEditingRanges { mut ranges, .. }) = message {
2915 ranges.sort_by_key(|range| range.start);
2916 let ranges = 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
2928 ranges
2929 } else {
2930 Ok(vec![])
2931 }
2932 }
2933
2934 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::LinkedEditingRange {
2935 proto::LinkedEditingRange {
2936 project_id,
2937 buffer_id: buffer.remote_id().to_proto(),
2938 position: Some(serialize_anchor(&self.position)),
2939 version: serialize_version(&buffer.version()),
2940 }
2941 }
2942
2943 async fn from_proto(
2944 message: proto::LinkedEditingRange,
2945 _: Model<LspStore>,
2946 buffer: Model<Buffer>,
2947 mut cx: AsyncAppContext,
2948 ) -> Result<Self> {
2949 let position = message
2950 .position
2951 .ok_or_else(|| anyhow!("invalid position"))?;
2952 buffer
2953 .update(&mut cx, |buffer, _| {
2954 buffer.wait_for_version(deserialize_version(&message.version))
2955 })?
2956 .await?;
2957 let position = deserialize_anchor(position).ok_or_else(|| anyhow!("invalid position"))?;
2958 buffer
2959 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([position]))?
2960 .await?;
2961 Ok(Self { position })
2962 }
2963
2964 fn response_to_proto(
2965 response: Vec<Range<Anchor>>,
2966 _: &mut LspStore,
2967 _: PeerId,
2968 buffer_version: &clock::Global,
2969 _: &mut AppContext,
2970 ) -> proto::LinkedEditingRangeResponse {
2971 proto::LinkedEditingRangeResponse {
2972 items: response
2973 .into_iter()
2974 .map(|range| proto::AnchorRange {
2975 start: Some(serialize_anchor(&range.start)),
2976 end: Some(serialize_anchor(&range.end)),
2977 })
2978 .collect(),
2979 version: serialize_version(buffer_version),
2980 }
2981 }
2982
2983 async fn response_from_proto(
2984 self,
2985 message: proto::LinkedEditingRangeResponse,
2986 _: Model<LspStore>,
2987 buffer: Model<Buffer>,
2988 mut cx: AsyncAppContext,
2989 ) -> Result<Vec<Range<Anchor>>> {
2990 buffer
2991 .update(&mut cx, |buffer, _| {
2992 buffer.wait_for_version(deserialize_version(&message.version))
2993 })?
2994 .await?;
2995 let items: Vec<Range<Anchor>> = message
2996 .items
2997 .into_iter()
2998 .filter_map(|range| {
2999 let start = deserialize_anchor(range.start?)?;
3000 let end = deserialize_anchor(range.end?)?;
3001 Some(start..end)
3002 })
3003 .collect();
3004 for range in &items {
3005 buffer
3006 .update(&mut cx, |buffer, _| {
3007 buffer.wait_for_anchors([range.start, range.end])
3008 })?
3009 .await?;
3010 }
3011 Ok(items)
3012 }
3013
3014 fn buffer_id_from_proto(message: &proto::LinkedEditingRange) -> Result<BufferId> {
3015 BufferId::new(message.buffer_id)
3016 }
3017}