1mod signature_help;
2
3use crate::{
4 CodeAction, CompletionSource, CoreCompletion, CoreCompletionResponse, DocumentColor,
5 DocumentHighlight, DocumentSymbol, Hover, HoverBlock, HoverBlockKind, InlayHint,
6 InlayHintLabel, InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Location,
7 LocationLink, LspAction, LspPullDiagnostics, MarkupContent, PrepareRenameResponse,
8 ProjectTransaction, PulledDiagnostics, ResolveState,
9 lsp_store::{LocalLspStore, LspStore},
10};
11use anyhow::{Context as _, Result};
12use async_trait::async_trait;
13use client::proto::{self, PeerId};
14use clock::Global;
15use collections::{HashMap, HashSet};
16use futures::future;
17use gpui::{App, AsyncApp, Entity, Task};
18use language::{
19 Anchor, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CharKind, CharScopeContext,
20 OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Transaction, Unclipped,
21 language_settings::{InlayHintKind, LanguageSettings, language_settings},
22 point_from_lsp, point_to_lsp,
23 proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version},
24 range_from_lsp, range_to_lsp,
25};
26use lsp::{
27 AdapterServerCapabilities, CodeActionKind, CodeActionOptions, CodeDescription,
28 CompletionContext, CompletionListItemDefaultsEditRange, CompletionTriggerKind,
29 DiagnosticServerCapabilities, DocumentHighlightKind, LanguageServer, LanguageServerId,
30 LinkedEditingRangeServerCapabilities, OneOf, RenameOptions, ServerCapabilities,
31};
32use serde_json::Value;
33use signature_help::{lsp_to_proto_signature, proto_to_lsp_signature};
34use std::{
35 cmp::Reverse, collections::hash_map, mem, ops::Range, path::Path, str::FromStr, sync::Arc,
36};
37use text::{BufferId, LineEnding};
38use util::{ResultExt as _, debug_panic};
39
40pub use signature_help::SignatureHelp;
41
42pub fn lsp_formatting_options(settings: &LanguageSettings) -> lsp::FormattingOptions {
43 lsp::FormattingOptions {
44 tab_size: settings.tab_size.into(),
45 insert_spaces: !settings.hard_tabs,
46 trim_trailing_whitespace: Some(settings.remove_trailing_whitespace_on_save),
47 trim_final_newlines: Some(settings.ensure_final_newline_on_save),
48 insert_final_newline: Some(settings.ensure_final_newline_on_save),
49 ..lsp::FormattingOptions::default()
50 }
51}
52
53pub fn file_path_to_lsp_url(path: &Path) -> Result<lsp::Uri> {
54 match lsp::Uri::from_file_path(path) {
55 Ok(url) => Ok(url),
56 Err(()) => anyhow::bail!("Invalid file path provided to LSP request: {path:?}"),
57 }
58}
59
60pub(crate) fn make_text_document_identifier(path: &Path) -> Result<lsp::TextDocumentIdentifier> {
61 Ok(lsp::TextDocumentIdentifier {
62 uri: file_path_to_lsp_url(path)?,
63 })
64}
65
66pub(crate) fn make_lsp_text_document_position(
67 path: &Path,
68 position: PointUtf16,
69) -> Result<lsp::TextDocumentPositionParams> {
70 Ok(lsp::TextDocumentPositionParams {
71 text_document: make_text_document_identifier(path)?,
72 position: point_to_lsp(position),
73 })
74}
75
76#[async_trait(?Send)]
77pub trait LspCommand: 'static + Sized + Send + std::fmt::Debug {
78 type Response: 'static + Default + Send + std::fmt::Debug;
79 type LspRequest: 'static + Send + lsp::request::Request;
80 type ProtoRequest: 'static + Send + proto::RequestMessage;
81
82 fn display_name(&self) -> &str;
83
84 fn status(&self) -> Option<String> {
85 None
86 }
87
88 fn to_lsp_params_or_response(
89 &self,
90 path: &Path,
91 buffer: &Buffer,
92 language_server: &Arc<LanguageServer>,
93 cx: &App,
94 ) -> Result<
95 LspParamsOrResponse<<Self::LspRequest as lsp::request::Request>::Params, Self::Response>,
96 > {
97 if self.check_capabilities(language_server.adapter_server_capabilities()) {
98 Ok(LspParamsOrResponse::Params(self.to_lsp(
99 path,
100 buffer,
101 language_server,
102 cx,
103 )?))
104 } else {
105 Ok(LspParamsOrResponse::Response(Default::default()))
106 }
107 }
108
109 /// When false, `to_lsp_params_or_response` default implementation will return the default response.
110 fn check_capabilities(&self, _: AdapterServerCapabilities) -> bool;
111
112 fn to_lsp(
113 &self,
114 path: &Path,
115 buffer: &Buffer,
116 language_server: &Arc<LanguageServer>,
117 cx: &App,
118 ) -> Result<<Self::LspRequest as lsp::request::Request>::Params>;
119
120 async fn response_from_lsp(
121 self,
122 message: <Self::LspRequest as lsp::request::Request>::Result,
123 lsp_store: Entity<LspStore>,
124 buffer: Entity<Buffer>,
125 server_id: LanguageServerId,
126 cx: AsyncApp,
127 ) -> Result<Self::Response>;
128
129 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest;
130
131 async fn from_proto(
132 message: Self::ProtoRequest,
133 lsp_store: Entity<LspStore>,
134 buffer: Entity<Buffer>,
135 cx: AsyncApp,
136 ) -> Result<Self>;
137
138 fn response_to_proto(
139 response: Self::Response,
140 lsp_store: &mut LspStore,
141 peer_id: PeerId,
142 buffer_version: &clock::Global,
143 cx: &mut App,
144 ) -> <Self::ProtoRequest as proto::RequestMessage>::Response;
145
146 async fn response_from_proto(
147 self,
148 message: <Self::ProtoRequest as proto::RequestMessage>::Response,
149 lsp_store: Entity<LspStore>,
150 buffer: Entity<Buffer>,
151 cx: AsyncApp,
152 ) -> Result<Self::Response>;
153
154 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> Result<BufferId>;
155}
156
157pub enum LspParamsOrResponse<P, R> {
158 Params(P),
159 Response(R),
160}
161
162#[derive(Debug)]
163pub(crate) struct PrepareRename {
164 pub position: PointUtf16,
165}
166
167#[derive(Debug)]
168pub(crate) struct PerformRename {
169 pub position: PointUtf16,
170 pub new_name: String,
171 pub push_to_history: bool,
172}
173
174#[derive(Debug, Clone, Copy)]
175pub struct GetDefinitions {
176 pub position: PointUtf16,
177}
178
179#[derive(Debug, Clone, Copy)]
180pub(crate) struct GetDeclarations {
181 pub position: PointUtf16,
182}
183
184#[derive(Debug, Clone, Copy)]
185pub(crate) struct GetTypeDefinitions {
186 pub position: PointUtf16,
187}
188
189#[derive(Debug, Clone, Copy)]
190pub(crate) struct GetImplementations {
191 pub position: PointUtf16,
192}
193
194#[derive(Debug, Clone, Copy)]
195pub(crate) struct GetReferences {
196 pub position: PointUtf16,
197}
198
199#[derive(Debug)]
200pub(crate) struct GetDocumentHighlights {
201 pub position: PointUtf16,
202}
203
204#[derive(Debug, Copy, Clone)]
205pub(crate) struct GetDocumentSymbols;
206
207#[derive(Clone, Debug)]
208pub(crate) struct GetSignatureHelp {
209 pub position: PointUtf16,
210}
211
212#[derive(Clone, Debug)]
213pub(crate) struct GetHover {
214 pub position: PointUtf16,
215}
216
217#[derive(Debug)]
218pub(crate) struct GetCompletions {
219 pub position: PointUtf16,
220 pub context: CompletionContext,
221 pub server_id: Option<lsp::LanguageServerId>,
222}
223
224#[derive(Clone, Debug)]
225pub(crate) struct GetCodeActions {
226 pub range: Range<Anchor>,
227 pub kinds: Option<Vec<lsp::CodeActionKind>>,
228}
229
230#[derive(Debug)]
231pub(crate) struct OnTypeFormatting {
232 pub position: PointUtf16,
233 pub trigger: String,
234 pub options: lsp::FormattingOptions,
235 pub push_to_history: bool,
236}
237
238#[derive(Clone, Debug)]
239pub(crate) struct InlayHints {
240 pub range: Range<Anchor>,
241}
242
243#[derive(Debug, Copy, Clone)]
244pub(crate) struct GetCodeLens;
245
246#[derive(Debug, Copy, Clone)]
247pub(crate) struct GetDocumentColor;
248
249impl GetCodeLens {
250 pub(crate) fn can_resolve_lens(capabilities: &ServerCapabilities) -> bool {
251 capabilities
252 .code_lens_provider
253 .as_ref()
254 .and_then(|code_lens_options| code_lens_options.resolve_provider)
255 .unwrap_or(false)
256 }
257}
258
259#[derive(Debug)]
260pub(crate) struct LinkedEditingRange {
261 pub position: Anchor,
262}
263
264#[derive(Clone, Debug)]
265pub(crate) struct GetDocumentDiagnostics {
266 /// We cannot blindly rely on server's capabilities.diagnostic_provider, as they're a singular field, whereas
267 /// a server can register multiple diagnostic providers post-mortem.
268 pub dynamic_caps: DiagnosticServerCapabilities,
269 pub previous_result_id: Option<String>,
270}
271
272#[async_trait(?Send)]
273impl LspCommand for PrepareRename {
274 type Response = PrepareRenameResponse;
275 type LspRequest = lsp::request::PrepareRenameRequest;
276 type ProtoRequest = proto::PrepareRename;
277
278 fn display_name(&self) -> &str {
279 "Prepare rename"
280 }
281
282 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
283 capabilities
284 .server_capabilities
285 .rename_provider
286 .is_some_and(|capability| match capability {
287 OneOf::Left(enabled) => enabled,
288 OneOf::Right(options) => options.prepare_provider.unwrap_or(false),
289 })
290 }
291
292 fn to_lsp_params_or_response(
293 &self,
294 path: &Path,
295 buffer: &Buffer,
296 language_server: &Arc<LanguageServer>,
297 cx: &App,
298 ) -> Result<LspParamsOrResponse<lsp::TextDocumentPositionParams, PrepareRenameResponse>> {
299 let rename_provider = language_server
300 .adapter_server_capabilities()
301 .server_capabilities
302 .rename_provider;
303 match rename_provider {
304 Some(lsp::OneOf::Right(RenameOptions {
305 prepare_provider: Some(true),
306 ..
307 })) => Ok(LspParamsOrResponse::Params(self.to_lsp(
308 path,
309 buffer,
310 language_server,
311 cx,
312 )?)),
313 Some(lsp::OneOf::Right(_)) => Ok(LspParamsOrResponse::Response(
314 PrepareRenameResponse::OnlyUnpreparedRenameSupported,
315 )),
316 Some(lsp::OneOf::Left(true)) => Ok(LspParamsOrResponse::Response(
317 PrepareRenameResponse::OnlyUnpreparedRenameSupported,
318 )),
319 _ => anyhow::bail!("Rename not supported"),
320 }
321 }
322
323 fn to_lsp(
324 &self,
325 path: &Path,
326 _: &Buffer,
327 _: &Arc<LanguageServer>,
328 _: &App,
329 ) -> Result<lsp::TextDocumentPositionParams> {
330 make_lsp_text_document_position(path, self.position)
331 }
332
333 async fn response_from_lsp(
334 self,
335 message: Option<lsp::PrepareRenameResponse>,
336 _: Entity<LspStore>,
337 buffer: Entity<Buffer>,
338 _: LanguageServerId,
339 cx: AsyncApp,
340 ) -> Result<PrepareRenameResponse> {
341 buffer.read_with(&cx, |buffer, _| match message {
342 Some(lsp::PrepareRenameResponse::Range(range))
343 | Some(lsp::PrepareRenameResponse::RangeWithPlaceholder { range, .. }) => {
344 let Range { start, end } = range_from_lsp(range);
345 if buffer.clip_point_utf16(start, Bias::Left) == start.0
346 && buffer.clip_point_utf16(end, Bias::Left) == end.0
347 {
348 Ok(PrepareRenameResponse::Success(
349 buffer.anchor_after(start)..buffer.anchor_before(end),
350 ))
351 } else {
352 Ok(PrepareRenameResponse::InvalidPosition)
353 }
354 }
355 Some(lsp::PrepareRenameResponse::DefaultBehavior { .. }) => {
356 let snapshot = buffer.snapshot();
357 let (range, _) = snapshot.surrounding_word(self.position, None);
358 let range = snapshot.anchor_after(range.start)..snapshot.anchor_before(range.end);
359 Ok(PrepareRenameResponse::Success(range))
360 }
361 None => Ok(PrepareRenameResponse::InvalidPosition),
362 })?
363 }
364
365 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PrepareRename {
366 proto::PrepareRename {
367 project_id,
368 buffer_id: buffer.remote_id().into(),
369 position: Some(language::proto::serialize_anchor(
370 &buffer.anchor_before(self.position),
371 )),
372 version: serialize_version(&buffer.version()),
373 }
374 }
375
376 async fn from_proto(
377 message: proto::PrepareRename,
378 _: Entity<LspStore>,
379 buffer: Entity<Buffer>,
380 mut cx: AsyncApp,
381 ) -> Result<Self> {
382 let position = message
383 .position
384 .and_then(deserialize_anchor)
385 .context("invalid position")?;
386 buffer
387 .update(&mut cx, |buffer, _| {
388 buffer.wait_for_version(deserialize_version(&message.version))
389 })?
390 .await?;
391
392 Ok(Self {
393 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer))?,
394 })
395 }
396
397 fn response_to_proto(
398 response: PrepareRenameResponse,
399 _: &mut LspStore,
400 _: PeerId,
401 buffer_version: &clock::Global,
402 _: &mut App,
403 ) -> proto::PrepareRenameResponse {
404 match response {
405 PrepareRenameResponse::Success(range) => proto::PrepareRenameResponse {
406 can_rename: true,
407 only_unprepared_rename_supported: false,
408 start: Some(language::proto::serialize_anchor(&range.start)),
409 end: Some(language::proto::serialize_anchor(&range.end)),
410 version: serialize_version(buffer_version),
411 },
412 PrepareRenameResponse::OnlyUnpreparedRenameSupported => proto::PrepareRenameResponse {
413 can_rename: false,
414 only_unprepared_rename_supported: true,
415 start: None,
416 end: None,
417 version: vec![],
418 },
419 PrepareRenameResponse::InvalidPosition => proto::PrepareRenameResponse {
420 can_rename: false,
421 only_unprepared_rename_supported: false,
422 start: None,
423 end: None,
424 version: vec![],
425 },
426 }
427 }
428
429 async fn response_from_proto(
430 self,
431 message: proto::PrepareRenameResponse,
432 _: Entity<LspStore>,
433 buffer: Entity<Buffer>,
434 mut cx: AsyncApp,
435 ) -> Result<PrepareRenameResponse> {
436 if message.can_rename {
437 buffer
438 .update(&mut cx, |buffer, _| {
439 buffer.wait_for_version(deserialize_version(&message.version))
440 })?
441 .await?;
442 if let (Some(start), Some(end)) = (
443 message.start.and_then(deserialize_anchor),
444 message.end.and_then(deserialize_anchor),
445 ) {
446 Ok(PrepareRenameResponse::Success(start..end))
447 } else {
448 anyhow::bail!(
449 "Missing start or end position in remote project PrepareRenameResponse"
450 );
451 }
452 } else if message.only_unprepared_rename_supported {
453 Ok(PrepareRenameResponse::OnlyUnpreparedRenameSupported)
454 } else {
455 Ok(PrepareRenameResponse::InvalidPosition)
456 }
457 }
458
459 fn buffer_id_from_proto(message: &proto::PrepareRename) -> Result<BufferId> {
460 BufferId::new(message.buffer_id)
461 }
462}
463
464#[async_trait(?Send)]
465impl LspCommand for PerformRename {
466 type Response = ProjectTransaction;
467 type LspRequest = lsp::request::Rename;
468 type ProtoRequest = proto::PerformRename;
469
470 fn display_name(&self) -> &str {
471 "Rename"
472 }
473
474 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
475 capabilities
476 .server_capabilities
477 .rename_provider
478 .is_some_and(|capability| match capability {
479 OneOf::Left(enabled) => enabled,
480 OneOf::Right(_options) => true,
481 })
482 }
483
484 fn to_lsp(
485 &self,
486 path: &Path,
487 _: &Buffer,
488 _: &Arc<LanguageServer>,
489 _: &App,
490 ) -> Result<lsp::RenameParams> {
491 Ok(lsp::RenameParams {
492 text_document_position: make_lsp_text_document_position(path, self.position)?,
493 new_name: self.new_name.clone(),
494 work_done_progress_params: Default::default(),
495 })
496 }
497
498 async fn response_from_lsp(
499 self,
500 message: Option<lsp::WorkspaceEdit>,
501 lsp_store: Entity<LspStore>,
502 buffer: Entity<Buffer>,
503 server_id: LanguageServerId,
504 mut cx: AsyncApp,
505 ) -> Result<ProjectTransaction> {
506 if let Some(edit) = message {
507 let (_, lsp_server) =
508 language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
509 LocalLspStore::deserialize_workspace_edit(
510 lsp_store,
511 edit,
512 self.push_to_history,
513 lsp_server,
514 &mut cx,
515 )
516 .await
517 } else {
518 Ok(ProjectTransaction::default())
519 }
520 }
521
522 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PerformRename {
523 proto::PerformRename {
524 project_id,
525 buffer_id: buffer.remote_id().into(),
526 position: Some(language::proto::serialize_anchor(
527 &buffer.anchor_before(self.position),
528 )),
529 new_name: self.new_name.clone(),
530 version: serialize_version(&buffer.version()),
531 }
532 }
533
534 async fn from_proto(
535 message: proto::PerformRename,
536 _: Entity<LspStore>,
537 buffer: Entity<Buffer>,
538 mut cx: AsyncApp,
539 ) -> Result<Self> {
540 let position = message
541 .position
542 .and_then(deserialize_anchor)
543 .context("invalid position")?;
544 buffer
545 .update(&mut cx, |buffer, _| {
546 buffer.wait_for_version(deserialize_version(&message.version))
547 })?
548 .await?;
549 Ok(Self {
550 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer))?,
551 new_name: message.new_name,
552 push_to_history: false,
553 })
554 }
555
556 fn response_to_proto(
557 response: ProjectTransaction,
558 lsp_store: &mut LspStore,
559 peer_id: PeerId,
560 _: &clock::Global,
561 cx: &mut App,
562 ) -> proto::PerformRenameResponse {
563 let transaction = lsp_store.buffer_store().update(cx, |buffer_store, cx| {
564 buffer_store.serialize_project_transaction_for_peer(response, peer_id, cx)
565 });
566 proto::PerformRenameResponse {
567 transaction: Some(transaction),
568 }
569 }
570
571 async fn response_from_proto(
572 self,
573 message: proto::PerformRenameResponse,
574 lsp_store: Entity<LspStore>,
575 _: Entity<Buffer>,
576 mut cx: AsyncApp,
577 ) -> Result<ProjectTransaction> {
578 let message = message.transaction.context("missing transaction")?;
579 lsp_store
580 .update(&mut cx, |lsp_store, cx| {
581 lsp_store.buffer_store().update(cx, |buffer_store, cx| {
582 buffer_store.deserialize_project_transaction(message, self.push_to_history, cx)
583 })
584 })?
585 .await
586 }
587
588 fn buffer_id_from_proto(message: &proto::PerformRename) -> Result<BufferId> {
589 BufferId::new(message.buffer_id)
590 }
591}
592
593#[async_trait(?Send)]
594impl LspCommand for GetDefinitions {
595 type Response = Vec<LocationLink>;
596 type LspRequest = lsp::request::GotoDefinition;
597 type ProtoRequest = proto::GetDefinition;
598
599 fn display_name(&self) -> &str {
600 "Get definition"
601 }
602
603 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
604 capabilities
605 .server_capabilities
606 .definition_provider
607 .is_some_and(|capability| match capability {
608 OneOf::Left(supported) => supported,
609 OneOf::Right(_options) => true,
610 })
611 }
612
613 fn to_lsp(
614 &self,
615 path: &Path,
616 _: &Buffer,
617 _: &Arc<LanguageServer>,
618 _: &App,
619 ) -> Result<lsp::GotoDefinitionParams> {
620 Ok(lsp::GotoDefinitionParams {
621 text_document_position_params: make_lsp_text_document_position(path, self.position)?,
622 work_done_progress_params: Default::default(),
623 partial_result_params: Default::default(),
624 })
625 }
626
627 async fn response_from_lsp(
628 self,
629 message: Option<lsp::GotoDefinitionResponse>,
630 lsp_store: Entity<LspStore>,
631 buffer: Entity<Buffer>,
632 server_id: LanguageServerId,
633 cx: AsyncApp,
634 ) -> Result<Vec<LocationLink>> {
635 location_links_from_lsp(message, lsp_store, buffer, server_id, cx).await
636 }
637
638 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDefinition {
639 proto::GetDefinition {
640 project_id,
641 buffer_id: buffer.remote_id().into(),
642 position: Some(language::proto::serialize_anchor(
643 &buffer.anchor_before(self.position),
644 )),
645 version: serialize_version(&buffer.version()),
646 }
647 }
648
649 async fn from_proto(
650 message: proto::GetDefinition,
651 _: Entity<LspStore>,
652 buffer: Entity<Buffer>,
653 mut cx: AsyncApp,
654 ) -> Result<Self> {
655 let position = message
656 .position
657 .and_then(deserialize_anchor)
658 .context("invalid position")?;
659 buffer
660 .update(&mut cx, |buffer, _| {
661 buffer.wait_for_version(deserialize_version(&message.version))
662 })?
663 .await?;
664 Ok(Self {
665 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer))?,
666 })
667 }
668
669 fn response_to_proto(
670 response: Vec<LocationLink>,
671 lsp_store: &mut LspStore,
672 peer_id: PeerId,
673 _: &clock::Global,
674 cx: &mut App,
675 ) -> proto::GetDefinitionResponse {
676 let links = location_links_to_proto(response, lsp_store, peer_id, cx);
677 proto::GetDefinitionResponse { links }
678 }
679
680 async fn response_from_proto(
681 self,
682 message: proto::GetDefinitionResponse,
683 lsp_store: Entity<LspStore>,
684 _: Entity<Buffer>,
685 cx: AsyncApp,
686 ) -> Result<Vec<LocationLink>> {
687 location_links_from_proto(message.links, lsp_store, cx).await
688 }
689
690 fn buffer_id_from_proto(message: &proto::GetDefinition) -> Result<BufferId> {
691 BufferId::new(message.buffer_id)
692 }
693}
694
695#[async_trait(?Send)]
696impl LspCommand for GetDeclarations {
697 type Response = Vec<LocationLink>;
698 type LspRequest = lsp::request::GotoDeclaration;
699 type ProtoRequest = proto::GetDeclaration;
700
701 fn display_name(&self) -> &str {
702 "Get declaration"
703 }
704
705 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
706 capabilities
707 .server_capabilities
708 .declaration_provider
709 .is_some_and(|capability| match capability {
710 lsp::DeclarationCapability::Simple(supported) => supported,
711 lsp::DeclarationCapability::RegistrationOptions(..) => true,
712 lsp::DeclarationCapability::Options(..) => true,
713 })
714 }
715
716 fn to_lsp(
717 &self,
718 path: &Path,
719 _: &Buffer,
720 _: &Arc<LanguageServer>,
721 _: &App,
722 ) -> Result<lsp::GotoDeclarationParams> {
723 Ok(lsp::GotoDeclarationParams {
724 text_document_position_params: make_lsp_text_document_position(path, self.position)?,
725 work_done_progress_params: Default::default(),
726 partial_result_params: Default::default(),
727 })
728 }
729
730 async fn response_from_lsp(
731 self,
732 message: Option<lsp::GotoDeclarationResponse>,
733 lsp_store: Entity<LspStore>,
734 buffer: Entity<Buffer>,
735 server_id: LanguageServerId,
736 cx: AsyncApp,
737 ) -> Result<Vec<LocationLink>> {
738 location_links_from_lsp(message, lsp_store, buffer, server_id, cx).await
739 }
740
741 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDeclaration {
742 proto::GetDeclaration {
743 project_id,
744 buffer_id: buffer.remote_id().into(),
745 position: Some(language::proto::serialize_anchor(
746 &buffer.anchor_before(self.position),
747 )),
748 version: serialize_version(&buffer.version()),
749 }
750 }
751
752 async fn from_proto(
753 message: proto::GetDeclaration,
754 _: Entity<LspStore>,
755 buffer: Entity<Buffer>,
756 mut cx: AsyncApp,
757 ) -> Result<Self> {
758 let position = message
759 .position
760 .and_then(deserialize_anchor)
761 .context("invalid position")?;
762 buffer
763 .update(&mut cx, |buffer, _| {
764 buffer.wait_for_version(deserialize_version(&message.version))
765 })?
766 .await?;
767 Ok(Self {
768 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer))?,
769 })
770 }
771
772 fn response_to_proto(
773 response: Vec<LocationLink>,
774 lsp_store: &mut LspStore,
775 peer_id: PeerId,
776 _: &clock::Global,
777 cx: &mut App,
778 ) -> proto::GetDeclarationResponse {
779 let links = location_links_to_proto(response, lsp_store, peer_id, cx);
780 proto::GetDeclarationResponse { links }
781 }
782
783 async fn response_from_proto(
784 self,
785 message: proto::GetDeclarationResponse,
786 lsp_store: Entity<LspStore>,
787 _: Entity<Buffer>,
788 cx: AsyncApp,
789 ) -> Result<Vec<LocationLink>> {
790 location_links_from_proto(message.links, lsp_store, cx).await
791 }
792
793 fn buffer_id_from_proto(message: &proto::GetDeclaration) -> Result<BufferId> {
794 BufferId::new(message.buffer_id)
795 }
796}
797
798#[async_trait(?Send)]
799impl LspCommand for GetImplementations {
800 type Response = Vec<LocationLink>;
801 type LspRequest = lsp::request::GotoImplementation;
802 type ProtoRequest = proto::GetImplementation;
803
804 fn display_name(&self) -> &str {
805 "Get implementation"
806 }
807
808 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
809 capabilities
810 .server_capabilities
811 .implementation_provider
812 .is_some_and(|capability| match capability {
813 lsp::ImplementationProviderCapability::Simple(enabled) => enabled,
814 lsp::ImplementationProviderCapability::Options(_options) => true,
815 })
816 }
817
818 fn to_lsp(
819 &self,
820 path: &Path,
821 _: &Buffer,
822 _: &Arc<LanguageServer>,
823 _: &App,
824 ) -> Result<lsp::GotoImplementationParams> {
825 Ok(lsp::GotoImplementationParams {
826 text_document_position_params: make_lsp_text_document_position(path, self.position)?,
827 work_done_progress_params: Default::default(),
828 partial_result_params: Default::default(),
829 })
830 }
831
832 async fn response_from_lsp(
833 self,
834 message: Option<lsp::GotoImplementationResponse>,
835 lsp_store: Entity<LspStore>,
836 buffer: Entity<Buffer>,
837 server_id: LanguageServerId,
838 cx: AsyncApp,
839 ) -> Result<Vec<LocationLink>> {
840 location_links_from_lsp(message, lsp_store, buffer, server_id, cx).await
841 }
842
843 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetImplementation {
844 proto::GetImplementation {
845 project_id,
846 buffer_id: buffer.remote_id().into(),
847 position: Some(language::proto::serialize_anchor(
848 &buffer.anchor_before(self.position),
849 )),
850 version: serialize_version(&buffer.version()),
851 }
852 }
853
854 async fn from_proto(
855 message: proto::GetImplementation,
856 _: Entity<LspStore>,
857 buffer: Entity<Buffer>,
858 mut cx: AsyncApp,
859 ) -> Result<Self> {
860 let position = message
861 .position
862 .and_then(deserialize_anchor)
863 .context("invalid position")?;
864 buffer
865 .update(&mut cx, |buffer, _| {
866 buffer.wait_for_version(deserialize_version(&message.version))
867 })?
868 .await?;
869 Ok(Self {
870 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer))?,
871 })
872 }
873
874 fn response_to_proto(
875 response: Vec<LocationLink>,
876 lsp_store: &mut LspStore,
877 peer_id: PeerId,
878 _: &clock::Global,
879 cx: &mut App,
880 ) -> proto::GetImplementationResponse {
881 let links = location_links_to_proto(response, lsp_store, peer_id, cx);
882 proto::GetImplementationResponse { links }
883 }
884
885 async fn response_from_proto(
886 self,
887 message: proto::GetImplementationResponse,
888 project: Entity<LspStore>,
889 _: Entity<Buffer>,
890 cx: AsyncApp,
891 ) -> Result<Vec<LocationLink>> {
892 location_links_from_proto(message.links, project, cx).await
893 }
894
895 fn buffer_id_from_proto(message: &proto::GetImplementation) -> Result<BufferId> {
896 BufferId::new(message.buffer_id)
897 }
898}
899
900#[async_trait(?Send)]
901impl LspCommand for GetTypeDefinitions {
902 type Response = Vec<LocationLink>;
903 type LspRequest = lsp::request::GotoTypeDefinition;
904 type ProtoRequest = proto::GetTypeDefinition;
905
906 fn display_name(&self) -> &str {
907 "Get type definition"
908 }
909
910 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
911 !matches!(
912 &capabilities.server_capabilities.type_definition_provider,
913 None | Some(lsp::TypeDefinitionProviderCapability::Simple(false))
914 )
915 }
916
917 fn to_lsp(
918 &self,
919 path: &Path,
920 _: &Buffer,
921 _: &Arc<LanguageServer>,
922 _: &App,
923 ) -> Result<lsp::GotoTypeDefinitionParams> {
924 Ok(lsp::GotoTypeDefinitionParams {
925 text_document_position_params: make_lsp_text_document_position(path, self.position)?,
926 work_done_progress_params: Default::default(),
927 partial_result_params: Default::default(),
928 })
929 }
930
931 async fn response_from_lsp(
932 self,
933 message: Option<lsp::GotoTypeDefinitionResponse>,
934 project: Entity<LspStore>,
935 buffer: Entity<Buffer>,
936 server_id: LanguageServerId,
937 cx: AsyncApp,
938 ) -> Result<Vec<LocationLink>> {
939 location_links_from_lsp(message, project, buffer, server_id, cx).await
940 }
941
942 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetTypeDefinition {
943 proto::GetTypeDefinition {
944 project_id,
945 buffer_id: buffer.remote_id().into(),
946 position: Some(language::proto::serialize_anchor(
947 &buffer.anchor_before(self.position),
948 )),
949 version: serialize_version(&buffer.version()),
950 }
951 }
952
953 async fn from_proto(
954 message: proto::GetTypeDefinition,
955 _: Entity<LspStore>,
956 buffer: Entity<Buffer>,
957 mut cx: AsyncApp,
958 ) -> Result<Self> {
959 let position = message
960 .position
961 .and_then(deserialize_anchor)
962 .context("invalid position")?;
963 buffer
964 .update(&mut cx, |buffer, _| {
965 buffer.wait_for_version(deserialize_version(&message.version))
966 })?
967 .await?;
968 Ok(Self {
969 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer))?,
970 })
971 }
972
973 fn response_to_proto(
974 response: Vec<LocationLink>,
975 lsp_store: &mut LspStore,
976 peer_id: PeerId,
977 _: &clock::Global,
978 cx: &mut App,
979 ) -> proto::GetTypeDefinitionResponse {
980 let links = location_links_to_proto(response, lsp_store, peer_id, cx);
981 proto::GetTypeDefinitionResponse { links }
982 }
983
984 async fn response_from_proto(
985 self,
986 message: proto::GetTypeDefinitionResponse,
987 project: Entity<LspStore>,
988 _: Entity<Buffer>,
989 cx: AsyncApp,
990 ) -> Result<Vec<LocationLink>> {
991 location_links_from_proto(message.links, project, cx).await
992 }
993
994 fn buffer_id_from_proto(message: &proto::GetTypeDefinition) -> Result<BufferId> {
995 BufferId::new(message.buffer_id)
996 }
997}
998
999fn language_server_for_buffer(
1000 lsp_store: &Entity<LspStore>,
1001 buffer: &Entity<Buffer>,
1002 server_id: LanguageServerId,
1003 cx: &mut AsyncApp,
1004) -> Result<(Arc<CachedLspAdapter>, Arc<LanguageServer>)> {
1005 lsp_store
1006 .update(cx, |lsp_store, cx| {
1007 buffer.update(cx, |buffer, cx| {
1008 lsp_store
1009 .language_server_for_local_buffer(buffer, server_id, cx)
1010 .map(|(adapter, server)| (adapter.clone(), server.clone()))
1011 })
1012 })?
1013 .context("no language server found for buffer")
1014}
1015
1016pub async fn location_links_from_proto(
1017 proto_links: Vec<proto::LocationLink>,
1018 lsp_store: Entity<LspStore>,
1019 mut cx: AsyncApp,
1020) -> Result<Vec<LocationLink>> {
1021 let mut links = Vec::new();
1022
1023 for link in proto_links {
1024 links.push(location_link_from_proto(link, lsp_store.clone(), &mut cx).await?)
1025 }
1026
1027 Ok(links)
1028}
1029
1030pub fn location_link_from_proto(
1031 link: proto::LocationLink,
1032 lsp_store: Entity<LspStore>,
1033 cx: &mut AsyncApp,
1034) -> Task<Result<LocationLink>> {
1035 cx.spawn(async move |cx| {
1036 let origin = match link.origin {
1037 Some(origin) => {
1038 let buffer_id = BufferId::new(origin.buffer_id)?;
1039 let buffer = lsp_store
1040 .update(cx, |lsp_store, cx| {
1041 lsp_store.wait_for_remote_buffer(buffer_id, cx)
1042 })?
1043 .await?;
1044 let start = origin
1045 .start
1046 .and_then(deserialize_anchor)
1047 .context("missing origin start")?;
1048 let end = origin
1049 .end
1050 .and_then(deserialize_anchor)
1051 .context("missing origin end")?;
1052 buffer
1053 .update(cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
1054 .await?;
1055 Some(Location {
1056 buffer,
1057 range: start..end,
1058 })
1059 }
1060 None => None,
1061 };
1062
1063 let target = link.target.context("missing target")?;
1064 let buffer_id = BufferId::new(target.buffer_id)?;
1065 let buffer = lsp_store
1066 .update(cx, |lsp_store, cx| {
1067 lsp_store.wait_for_remote_buffer(buffer_id, cx)
1068 })?
1069 .await?;
1070 let start = target
1071 .start
1072 .and_then(deserialize_anchor)
1073 .context("missing target start")?;
1074 let end = target
1075 .end
1076 .and_then(deserialize_anchor)
1077 .context("missing target end")?;
1078 buffer
1079 .update(cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
1080 .await?;
1081 let target = Location {
1082 buffer,
1083 range: start..end,
1084 };
1085 Ok(LocationLink { origin, target })
1086 })
1087}
1088
1089pub async fn location_links_from_lsp(
1090 message: Option<lsp::GotoDefinitionResponse>,
1091 lsp_store: Entity<LspStore>,
1092 buffer: Entity<Buffer>,
1093 server_id: LanguageServerId,
1094 mut cx: AsyncApp,
1095) -> Result<Vec<LocationLink>> {
1096 let message = match message {
1097 Some(message) => message,
1098 None => return Ok(Vec::new()),
1099 };
1100
1101 let mut unresolved_links = Vec::new();
1102 match message {
1103 lsp::GotoDefinitionResponse::Scalar(loc) => {
1104 unresolved_links.push((None, loc.uri, loc.range));
1105 }
1106
1107 lsp::GotoDefinitionResponse::Array(locs) => {
1108 unresolved_links.extend(locs.into_iter().map(|l| (None, l.uri, l.range)));
1109 }
1110
1111 lsp::GotoDefinitionResponse::Link(links) => {
1112 unresolved_links.extend(links.into_iter().map(|l| {
1113 (
1114 l.origin_selection_range,
1115 l.target_uri,
1116 l.target_selection_range,
1117 )
1118 }));
1119 }
1120 }
1121
1122 let (_, language_server) = language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
1123 let mut definitions = Vec::new();
1124 for (origin_range, target_uri, target_range) in unresolved_links {
1125 let target_buffer_handle = lsp_store
1126 .update(&mut cx, |this, cx| {
1127 this.open_local_buffer_via_lsp(target_uri, language_server.server_id(), cx)
1128 })?
1129 .await?;
1130
1131 cx.update(|cx| {
1132 let origin_location = origin_range.map(|origin_range| {
1133 let origin_buffer = buffer.read(cx);
1134 let origin_start =
1135 origin_buffer.clip_point_utf16(point_from_lsp(origin_range.start), Bias::Left);
1136 let origin_end =
1137 origin_buffer.clip_point_utf16(point_from_lsp(origin_range.end), Bias::Left);
1138 Location {
1139 buffer: buffer.clone(),
1140 range: origin_buffer.anchor_after(origin_start)
1141 ..origin_buffer.anchor_before(origin_end),
1142 }
1143 });
1144
1145 let target_buffer = target_buffer_handle.read(cx);
1146 let target_start =
1147 target_buffer.clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
1148 let target_end =
1149 target_buffer.clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
1150 let target_location = Location {
1151 buffer: target_buffer_handle,
1152 range: target_buffer.anchor_after(target_start)
1153 ..target_buffer.anchor_before(target_end),
1154 };
1155
1156 definitions.push(LocationLink {
1157 origin: origin_location,
1158 target: target_location,
1159 })
1160 })?;
1161 }
1162 Ok(definitions)
1163}
1164
1165pub async fn location_link_from_lsp(
1166 link: lsp::LocationLink,
1167 lsp_store: &Entity<LspStore>,
1168 buffer: &Entity<Buffer>,
1169 server_id: LanguageServerId,
1170 cx: &mut AsyncApp,
1171) -> Result<LocationLink> {
1172 let (_, language_server) = language_server_for_buffer(lsp_store, buffer, server_id, cx)?;
1173
1174 let (origin_range, target_uri, target_range) = (
1175 link.origin_selection_range,
1176 link.target_uri,
1177 link.target_selection_range,
1178 );
1179
1180 let target_buffer_handle = lsp_store
1181 .update(cx, |lsp_store, cx| {
1182 lsp_store.open_local_buffer_via_lsp(target_uri, language_server.server_id(), cx)
1183 })?
1184 .await?;
1185
1186 cx.update(|cx| {
1187 let origin_location = origin_range.map(|origin_range| {
1188 let origin_buffer = buffer.read(cx);
1189 let origin_start =
1190 origin_buffer.clip_point_utf16(point_from_lsp(origin_range.start), Bias::Left);
1191 let origin_end =
1192 origin_buffer.clip_point_utf16(point_from_lsp(origin_range.end), Bias::Left);
1193 Location {
1194 buffer: buffer.clone(),
1195 range: origin_buffer.anchor_after(origin_start)
1196 ..origin_buffer.anchor_before(origin_end),
1197 }
1198 });
1199
1200 let target_buffer = target_buffer_handle.read(cx);
1201 let target_start =
1202 target_buffer.clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
1203 let target_end =
1204 target_buffer.clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
1205 let target_location = Location {
1206 buffer: target_buffer_handle,
1207 range: target_buffer.anchor_after(target_start)
1208 ..target_buffer.anchor_before(target_end),
1209 };
1210
1211 LocationLink {
1212 origin: origin_location,
1213 target: target_location,
1214 }
1215 })
1216}
1217
1218pub fn location_links_to_proto(
1219 links: Vec<LocationLink>,
1220 lsp_store: &mut LspStore,
1221 peer_id: PeerId,
1222 cx: &mut App,
1223) -> Vec<proto::LocationLink> {
1224 links
1225 .into_iter()
1226 .map(|definition| location_link_to_proto(definition, lsp_store, peer_id, cx))
1227 .collect()
1228}
1229
1230pub fn location_link_to_proto(
1231 location: LocationLink,
1232 lsp_store: &mut LspStore,
1233 peer_id: PeerId,
1234 cx: &mut App,
1235) -> proto::LocationLink {
1236 let origin = location.origin.map(|origin| {
1237 lsp_store
1238 .buffer_store()
1239 .update(cx, |buffer_store, cx| {
1240 buffer_store.create_buffer_for_peer(&origin.buffer, peer_id, cx)
1241 })
1242 .detach_and_log_err(cx);
1243
1244 let buffer_id = origin.buffer.read(cx).remote_id().into();
1245 proto::Location {
1246 start: Some(serialize_anchor(&origin.range.start)),
1247 end: Some(serialize_anchor(&origin.range.end)),
1248 buffer_id,
1249 }
1250 });
1251
1252 lsp_store
1253 .buffer_store()
1254 .update(cx, |buffer_store, cx| {
1255 buffer_store.create_buffer_for_peer(&location.target.buffer, peer_id, cx)
1256 })
1257 .detach_and_log_err(cx);
1258
1259 let buffer_id = location.target.buffer.read(cx).remote_id().into();
1260 let target = proto::Location {
1261 start: Some(serialize_anchor(&location.target.range.start)),
1262 end: Some(serialize_anchor(&location.target.range.end)),
1263 buffer_id,
1264 };
1265
1266 proto::LocationLink {
1267 origin,
1268 target: Some(target),
1269 }
1270}
1271
1272#[async_trait(?Send)]
1273impl LspCommand for GetReferences {
1274 type Response = Vec<Location>;
1275 type LspRequest = lsp::request::References;
1276 type ProtoRequest = proto::GetReferences;
1277
1278 fn display_name(&self) -> &str {
1279 "Find all references"
1280 }
1281
1282 fn status(&self) -> Option<String> {
1283 Some("Finding references...".to_owned())
1284 }
1285
1286 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
1287 match &capabilities.server_capabilities.references_provider {
1288 Some(OneOf::Left(has_support)) => *has_support,
1289 Some(OneOf::Right(_)) => true,
1290 None => false,
1291 }
1292 }
1293
1294 fn to_lsp(
1295 &self,
1296 path: &Path,
1297 _: &Buffer,
1298 _: &Arc<LanguageServer>,
1299 _: &App,
1300 ) -> Result<lsp::ReferenceParams> {
1301 Ok(lsp::ReferenceParams {
1302 text_document_position: make_lsp_text_document_position(path, self.position)?,
1303 work_done_progress_params: Default::default(),
1304 partial_result_params: Default::default(),
1305 context: lsp::ReferenceContext {
1306 include_declaration: true,
1307 },
1308 })
1309 }
1310
1311 async fn response_from_lsp(
1312 self,
1313 locations: Option<Vec<lsp::Location>>,
1314 lsp_store: Entity<LspStore>,
1315 buffer: Entity<Buffer>,
1316 server_id: LanguageServerId,
1317 mut cx: AsyncApp,
1318 ) -> Result<Vec<Location>> {
1319 let mut references = Vec::new();
1320 let (_, language_server) =
1321 language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
1322
1323 if let Some(locations) = locations {
1324 for lsp_location in locations {
1325 let target_buffer_handle = lsp_store
1326 .update(&mut cx, |lsp_store, cx| {
1327 lsp_store.open_local_buffer_via_lsp(
1328 lsp_location.uri,
1329 language_server.server_id(),
1330 cx,
1331 )
1332 })?
1333 .await?;
1334
1335 target_buffer_handle
1336 .clone()
1337 .read_with(&cx, |target_buffer, _| {
1338 let target_start = target_buffer
1339 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
1340 let target_end = target_buffer
1341 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
1342 references.push(Location {
1343 buffer: target_buffer_handle,
1344 range: target_buffer.anchor_after(target_start)
1345 ..target_buffer.anchor_before(target_end),
1346 });
1347 })?;
1348 }
1349 }
1350
1351 Ok(references)
1352 }
1353
1354 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetReferences {
1355 proto::GetReferences {
1356 project_id,
1357 buffer_id: buffer.remote_id().into(),
1358 position: Some(language::proto::serialize_anchor(
1359 &buffer.anchor_before(self.position),
1360 )),
1361 version: serialize_version(&buffer.version()),
1362 }
1363 }
1364
1365 async fn from_proto(
1366 message: proto::GetReferences,
1367 _: Entity<LspStore>,
1368 buffer: Entity<Buffer>,
1369 mut cx: AsyncApp,
1370 ) -> Result<Self> {
1371 let position = message
1372 .position
1373 .and_then(deserialize_anchor)
1374 .context("invalid position")?;
1375 buffer
1376 .update(&mut cx, |buffer, _| {
1377 buffer.wait_for_version(deserialize_version(&message.version))
1378 })?
1379 .await?;
1380 Ok(Self {
1381 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer))?,
1382 })
1383 }
1384
1385 fn response_to_proto(
1386 response: Vec<Location>,
1387 lsp_store: &mut LspStore,
1388 peer_id: PeerId,
1389 _: &clock::Global,
1390 cx: &mut App,
1391 ) -> proto::GetReferencesResponse {
1392 let locations = response
1393 .into_iter()
1394 .map(|definition| {
1395 lsp_store
1396 .buffer_store()
1397 .update(cx, |buffer_store, cx| {
1398 buffer_store.create_buffer_for_peer(&definition.buffer, peer_id, cx)
1399 })
1400 .detach_and_log_err(cx);
1401 let buffer_id = definition.buffer.read(cx).remote_id();
1402 proto::Location {
1403 start: Some(serialize_anchor(&definition.range.start)),
1404 end: Some(serialize_anchor(&definition.range.end)),
1405 buffer_id: buffer_id.into(),
1406 }
1407 })
1408 .collect();
1409 proto::GetReferencesResponse { locations }
1410 }
1411
1412 async fn response_from_proto(
1413 self,
1414 message: proto::GetReferencesResponse,
1415 project: Entity<LspStore>,
1416 _: Entity<Buffer>,
1417 mut cx: AsyncApp,
1418 ) -> Result<Vec<Location>> {
1419 let mut locations = Vec::new();
1420 for location in message.locations {
1421 let buffer_id = BufferId::new(location.buffer_id)?;
1422 let target_buffer = project
1423 .update(&mut cx, |this, cx| {
1424 this.wait_for_remote_buffer(buffer_id, cx)
1425 })?
1426 .await?;
1427 let start = location
1428 .start
1429 .and_then(deserialize_anchor)
1430 .context("missing target start")?;
1431 let end = location
1432 .end
1433 .and_then(deserialize_anchor)
1434 .context("missing target end")?;
1435 target_buffer
1436 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
1437 .await?;
1438 locations.push(Location {
1439 buffer: target_buffer,
1440 range: start..end,
1441 })
1442 }
1443 Ok(locations)
1444 }
1445
1446 fn buffer_id_from_proto(message: &proto::GetReferences) -> Result<BufferId> {
1447 BufferId::new(message.buffer_id)
1448 }
1449}
1450
1451#[async_trait(?Send)]
1452impl LspCommand for GetDocumentHighlights {
1453 type Response = Vec<DocumentHighlight>;
1454 type LspRequest = lsp::request::DocumentHighlightRequest;
1455 type ProtoRequest = proto::GetDocumentHighlights;
1456
1457 fn display_name(&self) -> &str {
1458 "Get document highlights"
1459 }
1460
1461 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
1462 capabilities
1463 .server_capabilities
1464 .document_highlight_provider
1465 .is_some_and(|capability| match capability {
1466 OneOf::Left(supported) => supported,
1467 OneOf::Right(_options) => true,
1468 })
1469 }
1470
1471 fn to_lsp(
1472 &self,
1473 path: &Path,
1474 _: &Buffer,
1475 _: &Arc<LanguageServer>,
1476 _: &App,
1477 ) -> Result<lsp::DocumentHighlightParams> {
1478 Ok(lsp::DocumentHighlightParams {
1479 text_document_position_params: make_lsp_text_document_position(path, self.position)?,
1480 work_done_progress_params: Default::default(),
1481 partial_result_params: Default::default(),
1482 })
1483 }
1484
1485 async fn response_from_lsp(
1486 self,
1487 lsp_highlights: Option<Vec<lsp::DocumentHighlight>>,
1488 _: Entity<LspStore>,
1489 buffer: Entity<Buffer>,
1490 _: LanguageServerId,
1491 cx: AsyncApp,
1492 ) -> Result<Vec<DocumentHighlight>> {
1493 buffer.read_with(&cx, |buffer, _| {
1494 let mut lsp_highlights = lsp_highlights.unwrap_or_default();
1495 lsp_highlights.sort_unstable_by_key(|h| (h.range.start, Reverse(h.range.end)));
1496 lsp_highlights
1497 .into_iter()
1498 .map(|lsp_highlight| {
1499 let start = buffer
1500 .clip_point_utf16(point_from_lsp(lsp_highlight.range.start), Bias::Left);
1501 let end = buffer
1502 .clip_point_utf16(point_from_lsp(lsp_highlight.range.end), Bias::Left);
1503 DocumentHighlight {
1504 range: buffer.anchor_after(start)..buffer.anchor_before(end),
1505 kind: lsp_highlight
1506 .kind
1507 .unwrap_or(lsp::DocumentHighlightKind::READ),
1508 }
1509 })
1510 .collect()
1511 })
1512 }
1513
1514 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentHighlights {
1515 proto::GetDocumentHighlights {
1516 project_id,
1517 buffer_id: buffer.remote_id().into(),
1518 position: Some(language::proto::serialize_anchor(
1519 &buffer.anchor_before(self.position),
1520 )),
1521 version: serialize_version(&buffer.version()),
1522 }
1523 }
1524
1525 async fn from_proto(
1526 message: proto::GetDocumentHighlights,
1527 _: Entity<LspStore>,
1528 buffer: Entity<Buffer>,
1529 mut cx: AsyncApp,
1530 ) -> Result<Self> {
1531 let position = message
1532 .position
1533 .and_then(deserialize_anchor)
1534 .context("invalid position")?;
1535 buffer
1536 .update(&mut cx, |buffer, _| {
1537 buffer.wait_for_version(deserialize_version(&message.version))
1538 })?
1539 .await?;
1540 Ok(Self {
1541 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer))?,
1542 })
1543 }
1544
1545 fn response_to_proto(
1546 response: Vec<DocumentHighlight>,
1547 _: &mut LspStore,
1548 _: PeerId,
1549 _: &clock::Global,
1550 _: &mut App,
1551 ) -> proto::GetDocumentHighlightsResponse {
1552 let highlights = response
1553 .into_iter()
1554 .map(|highlight| proto::DocumentHighlight {
1555 start: Some(serialize_anchor(&highlight.range.start)),
1556 end: Some(serialize_anchor(&highlight.range.end)),
1557 kind: match highlight.kind {
1558 DocumentHighlightKind::TEXT => proto::document_highlight::Kind::Text.into(),
1559 DocumentHighlightKind::WRITE => proto::document_highlight::Kind::Write.into(),
1560 DocumentHighlightKind::READ => proto::document_highlight::Kind::Read.into(),
1561 _ => proto::document_highlight::Kind::Text.into(),
1562 },
1563 })
1564 .collect();
1565 proto::GetDocumentHighlightsResponse { highlights }
1566 }
1567
1568 async fn response_from_proto(
1569 self,
1570 message: proto::GetDocumentHighlightsResponse,
1571 _: Entity<LspStore>,
1572 buffer: Entity<Buffer>,
1573 mut cx: AsyncApp,
1574 ) -> Result<Vec<DocumentHighlight>> {
1575 let mut highlights = Vec::new();
1576 for highlight in message.highlights {
1577 let start = highlight
1578 .start
1579 .and_then(deserialize_anchor)
1580 .context("missing target start")?;
1581 let end = highlight
1582 .end
1583 .and_then(deserialize_anchor)
1584 .context("missing target end")?;
1585 buffer
1586 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))?
1587 .await?;
1588 let kind = match proto::document_highlight::Kind::from_i32(highlight.kind) {
1589 Some(proto::document_highlight::Kind::Text) => DocumentHighlightKind::TEXT,
1590 Some(proto::document_highlight::Kind::Read) => DocumentHighlightKind::READ,
1591 Some(proto::document_highlight::Kind::Write) => DocumentHighlightKind::WRITE,
1592 None => DocumentHighlightKind::TEXT,
1593 };
1594 highlights.push(DocumentHighlight {
1595 range: start..end,
1596 kind,
1597 });
1598 }
1599 Ok(highlights)
1600 }
1601
1602 fn buffer_id_from_proto(message: &proto::GetDocumentHighlights) -> Result<BufferId> {
1603 BufferId::new(message.buffer_id)
1604 }
1605}
1606
1607#[async_trait(?Send)]
1608impl LspCommand for GetDocumentSymbols {
1609 type Response = Vec<DocumentSymbol>;
1610 type LspRequest = lsp::request::DocumentSymbolRequest;
1611 type ProtoRequest = proto::GetDocumentSymbols;
1612
1613 fn display_name(&self) -> &str {
1614 "Get document symbols"
1615 }
1616
1617 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
1618 capabilities
1619 .server_capabilities
1620 .document_symbol_provider
1621 .is_some_and(|capability| match capability {
1622 OneOf::Left(supported) => supported,
1623 OneOf::Right(_options) => true,
1624 })
1625 }
1626
1627 fn to_lsp(
1628 &self,
1629 path: &Path,
1630 _: &Buffer,
1631 _: &Arc<LanguageServer>,
1632 _: &App,
1633 ) -> Result<lsp::DocumentSymbolParams> {
1634 Ok(lsp::DocumentSymbolParams {
1635 text_document: make_text_document_identifier(path)?,
1636 work_done_progress_params: Default::default(),
1637 partial_result_params: Default::default(),
1638 })
1639 }
1640
1641 async fn response_from_lsp(
1642 self,
1643 lsp_symbols: Option<lsp::DocumentSymbolResponse>,
1644 _: Entity<LspStore>,
1645 _: Entity<Buffer>,
1646 _: LanguageServerId,
1647 _: AsyncApp,
1648 ) -> Result<Vec<DocumentSymbol>> {
1649 let Some(lsp_symbols) = lsp_symbols else {
1650 return Ok(Vec::new());
1651 };
1652
1653 let symbols: Vec<_> = match lsp_symbols {
1654 lsp::DocumentSymbolResponse::Flat(symbol_information) => symbol_information
1655 .into_iter()
1656 .map(|lsp_symbol| DocumentSymbol {
1657 name: lsp_symbol.name,
1658 kind: lsp_symbol.kind,
1659 range: range_from_lsp(lsp_symbol.location.range),
1660 selection_range: range_from_lsp(lsp_symbol.location.range),
1661 children: Vec::new(),
1662 })
1663 .collect(),
1664 lsp::DocumentSymbolResponse::Nested(nested_responses) => {
1665 fn convert_symbol(lsp_symbol: lsp::DocumentSymbol) -> DocumentSymbol {
1666 DocumentSymbol {
1667 name: lsp_symbol.name,
1668 kind: lsp_symbol.kind,
1669 range: range_from_lsp(lsp_symbol.range),
1670 selection_range: range_from_lsp(lsp_symbol.selection_range),
1671 children: lsp_symbol
1672 .children
1673 .map(|children| {
1674 children.into_iter().map(convert_symbol).collect::<Vec<_>>()
1675 })
1676 .unwrap_or_default(),
1677 }
1678 }
1679 nested_responses.into_iter().map(convert_symbol).collect()
1680 }
1681 };
1682 Ok(symbols)
1683 }
1684
1685 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentSymbols {
1686 proto::GetDocumentSymbols {
1687 project_id,
1688 buffer_id: buffer.remote_id().into(),
1689 version: serialize_version(&buffer.version()),
1690 }
1691 }
1692
1693 async fn from_proto(
1694 message: proto::GetDocumentSymbols,
1695 _: Entity<LspStore>,
1696 buffer: Entity<Buffer>,
1697 mut cx: AsyncApp,
1698 ) -> Result<Self> {
1699 buffer
1700 .update(&mut cx, |buffer, _| {
1701 buffer.wait_for_version(deserialize_version(&message.version))
1702 })?
1703 .await?;
1704 Ok(Self)
1705 }
1706
1707 fn response_to_proto(
1708 response: Vec<DocumentSymbol>,
1709 _: &mut LspStore,
1710 _: PeerId,
1711 _: &clock::Global,
1712 _: &mut App,
1713 ) -> proto::GetDocumentSymbolsResponse {
1714 let symbols = response
1715 .into_iter()
1716 .map(|symbol| {
1717 fn convert_symbol_to_proto(symbol: DocumentSymbol) -> proto::DocumentSymbol {
1718 proto::DocumentSymbol {
1719 name: symbol.name.clone(),
1720 kind: unsafe { mem::transmute::<lsp::SymbolKind, i32>(symbol.kind) },
1721 start: Some(proto::PointUtf16 {
1722 row: symbol.range.start.0.row,
1723 column: symbol.range.start.0.column,
1724 }),
1725 end: Some(proto::PointUtf16 {
1726 row: symbol.range.end.0.row,
1727 column: symbol.range.end.0.column,
1728 }),
1729 selection_start: Some(proto::PointUtf16 {
1730 row: symbol.selection_range.start.0.row,
1731 column: symbol.selection_range.start.0.column,
1732 }),
1733 selection_end: Some(proto::PointUtf16 {
1734 row: symbol.selection_range.end.0.row,
1735 column: symbol.selection_range.end.0.column,
1736 }),
1737 children: symbol
1738 .children
1739 .into_iter()
1740 .map(convert_symbol_to_proto)
1741 .collect(),
1742 }
1743 }
1744 convert_symbol_to_proto(symbol)
1745 })
1746 .collect::<Vec<_>>();
1747
1748 proto::GetDocumentSymbolsResponse { symbols }
1749 }
1750
1751 async fn response_from_proto(
1752 self,
1753 message: proto::GetDocumentSymbolsResponse,
1754 _: Entity<LspStore>,
1755 _: Entity<Buffer>,
1756 _: AsyncApp,
1757 ) -> Result<Vec<DocumentSymbol>> {
1758 let mut symbols = Vec::with_capacity(message.symbols.len());
1759 for serialized_symbol in message.symbols {
1760 fn deserialize_symbol_with_children(
1761 serialized_symbol: proto::DocumentSymbol,
1762 ) -> Result<DocumentSymbol> {
1763 let kind =
1764 unsafe { mem::transmute::<i32, lsp::SymbolKind>(serialized_symbol.kind) };
1765
1766 let start = serialized_symbol.start.context("invalid start")?;
1767 let end = serialized_symbol.end.context("invalid end")?;
1768
1769 let selection_start = serialized_symbol
1770 .selection_start
1771 .context("invalid selection start")?;
1772 let selection_end = serialized_symbol
1773 .selection_end
1774 .context("invalid selection end")?;
1775
1776 Ok(DocumentSymbol {
1777 name: serialized_symbol.name,
1778 kind,
1779 range: Unclipped(PointUtf16::new(start.row, start.column))
1780 ..Unclipped(PointUtf16::new(end.row, end.column)),
1781 selection_range: Unclipped(PointUtf16::new(
1782 selection_start.row,
1783 selection_start.column,
1784 ))
1785 ..Unclipped(PointUtf16::new(selection_end.row, selection_end.column)),
1786 children: serialized_symbol
1787 .children
1788 .into_iter()
1789 .filter_map(|symbol| deserialize_symbol_with_children(symbol).ok())
1790 .collect::<Vec<_>>(),
1791 })
1792 }
1793
1794 symbols.push(deserialize_symbol_with_children(serialized_symbol)?);
1795 }
1796
1797 Ok(symbols)
1798 }
1799
1800 fn buffer_id_from_proto(message: &proto::GetDocumentSymbols) -> Result<BufferId> {
1801 BufferId::new(message.buffer_id)
1802 }
1803}
1804
1805#[async_trait(?Send)]
1806impl LspCommand for GetSignatureHelp {
1807 type Response = Option<SignatureHelp>;
1808 type LspRequest = lsp::SignatureHelpRequest;
1809 type ProtoRequest = proto::GetSignatureHelp;
1810
1811 fn display_name(&self) -> &str {
1812 "Get signature help"
1813 }
1814
1815 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
1816 capabilities
1817 .server_capabilities
1818 .signature_help_provider
1819 .is_some()
1820 }
1821
1822 fn to_lsp(
1823 &self,
1824 path: &Path,
1825 _: &Buffer,
1826 _: &Arc<LanguageServer>,
1827 _cx: &App,
1828 ) -> Result<lsp::SignatureHelpParams> {
1829 Ok(lsp::SignatureHelpParams {
1830 text_document_position_params: make_lsp_text_document_position(path, self.position)?,
1831 context: None,
1832 work_done_progress_params: Default::default(),
1833 })
1834 }
1835
1836 async fn response_from_lsp(
1837 self,
1838 message: Option<lsp::SignatureHelp>,
1839 lsp_store: Entity<LspStore>,
1840 _: Entity<Buffer>,
1841 id: LanguageServerId,
1842 cx: AsyncApp,
1843 ) -> Result<Self::Response> {
1844 let Some(message) = message else {
1845 return Ok(None);
1846 };
1847 cx.update(|cx| {
1848 SignatureHelp::new(
1849 message,
1850 Some(lsp_store.read(cx).languages.clone()),
1851 Some(id),
1852 cx,
1853 )
1854 })
1855 }
1856
1857 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
1858 let offset = buffer.point_utf16_to_offset(self.position);
1859 proto::GetSignatureHelp {
1860 project_id,
1861 buffer_id: buffer.remote_id().to_proto(),
1862 position: Some(serialize_anchor(&buffer.anchor_after(offset))),
1863 version: serialize_version(&buffer.version()),
1864 }
1865 }
1866
1867 async fn from_proto(
1868 payload: Self::ProtoRequest,
1869 _: Entity<LspStore>,
1870 buffer: Entity<Buffer>,
1871 mut cx: AsyncApp,
1872 ) -> Result<Self> {
1873 buffer
1874 .update(&mut cx, |buffer, _| {
1875 buffer.wait_for_version(deserialize_version(&payload.version))
1876 })?
1877 .await
1878 .with_context(|| format!("waiting for version for buffer {}", buffer.entity_id()))?;
1879 let buffer_snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot())?;
1880 Ok(Self {
1881 position: payload
1882 .position
1883 .and_then(deserialize_anchor)
1884 .context("invalid position")?
1885 .to_point_utf16(&buffer_snapshot),
1886 })
1887 }
1888
1889 fn response_to_proto(
1890 response: Self::Response,
1891 _: &mut LspStore,
1892 _: PeerId,
1893 _: &Global,
1894 _: &mut App,
1895 ) -> proto::GetSignatureHelpResponse {
1896 proto::GetSignatureHelpResponse {
1897 signature_help: response
1898 .map(|signature_help| lsp_to_proto_signature(signature_help.original_data)),
1899 }
1900 }
1901
1902 async fn response_from_proto(
1903 self,
1904 response: proto::GetSignatureHelpResponse,
1905 lsp_store: Entity<LspStore>,
1906 _: Entity<Buffer>,
1907 cx: AsyncApp,
1908 ) -> Result<Self::Response> {
1909 cx.update(|cx| {
1910 response
1911 .signature_help
1912 .map(proto_to_lsp_signature)
1913 .and_then(|signature| {
1914 SignatureHelp::new(
1915 signature,
1916 Some(lsp_store.read(cx).languages.clone()),
1917 None,
1918 cx,
1919 )
1920 })
1921 })
1922 }
1923
1924 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> Result<BufferId> {
1925 BufferId::new(message.buffer_id)
1926 }
1927}
1928
1929#[async_trait(?Send)]
1930impl LspCommand for GetHover {
1931 type Response = Option<Hover>;
1932 type LspRequest = lsp::request::HoverRequest;
1933 type ProtoRequest = proto::GetHover;
1934
1935 fn display_name(&self) -> &str {
1936 "Get hover"
1937 }
1938
1939 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
1940 match capabilities.server_capabilities.hover_provider {
1941 Some(lsp::HoverProviderCapability::Simple(enabled)) => enabled,
1942 Some(lsp::HoverProviderCapability::Options(_)) => true,
1943 None => false,
1944 }
1945 }
1946
1947 fn to_lsp(
1948 &self,
1949 path: &Path,
1950 _: &Buffer,
1951 _: &Arc<LanguageServer>,
1952 _: &App,
1953 ) -> Result<lsp::HoverParams> {
1954 Ok(lsp::HoverParams {
1955 text_document_position_params: make_lsp_text_document_position(path, self.position)?,
1956 work_done_progress_params: Default::default(),
1957 })
1958 }
1959
1960 async fn response_from_lsp(
1961 self,
1962 message: Option<lsp::Hover>,
1963 _: Entity<LspStore>,
1964 buffer: Entity<Buffer>,
1965 _: LanguageServerId,
1966 cx: AsyncApp,
1967 ) -> Result<Self::Response> {
1968 let Some(hover) = message else {
1969 return Ok(None);
1970 };
1971
1972 let (language, range) = buffer.read_with(&cx, |buffer, _| {
1973 (
1974 buffer.language().cloned(),
1975 hover.range.map(|range| {
1976 let token_start =
1977 buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left);
1978 let token_end = buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left);
1979 buffer.anchor_after(token_start)..buffer.anchor_before(token_end)
1980 }),
1981 )
1982 })?;
1983
1984 fn hover_blocks_from_marked_string(marked_string: lsp::MarkedString) -> Option<HoverBlock> {
1985 let block = match marked_string {
1986 lsp::MarkedString::String(content) => HoverBlock {
1987 text: content,
1988 kind: HoverBlockKind::Markdown,
1989 },
1990 lsp::MarkedString::LanguageString(lsp::LanguageString { language, value }) => {
1991 HoverBlock {
1992 text: value,
1993 kind: HoverBlockKind::Code { language },
1994 }
1995 }
1996 };
1997 if block.text.is_empty() {
1998 None
1999 } else {
2000 Some(block)
2001 }
2002 }
2003
2004 let contents = match hover.contents {
2005 lsp::HoverContents::Scalar(marked_string) => {
2006 hover_blocks_from_marked_string(marked_string)
2007 .into_iter()
2008 .collect()
2009 }
2010 lsp::HoverContents::Array(marked_strings) => marked_strings
2011 .into_iter()
2012 .filter_map(hover_blocks_from_marked_string)
2013 .collect(),
2014 lsp::HoverContents::Markup(markup_content) => vec![HoverBlock {
2015 text: markup_content.value,
2016 kind: if markup_content.kind == lsp::MarkupKind::Markdown {
2017 HoverBlockKind::Markdown
2018 } else {
2019 HoverBlockKind::PlainText
2020 },
2021 }],
2022 };
2023
2024 Ok(Some(Hover {
2025 contents,
2026 range,
2027 language,
2028 }))
2029 }
2030
2031 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
2032 proto::GetHover {
2033 project_id,
2034 buffer_id: buffer.remote_id().into(),
2035 position: Some(language::proto::serialize_anchor(
2036 &buffer.anchor_before(self.position),
2037 )),
2038 version: serialize_version(&buffer.version),
2039 }
2040 }
2041
2042 async fn from_proto(
2043 message: Self::ProtoRequest,
2044 _: Entity<LspStore>,
2045 buffer: Entity<Buffer>,
2046 mut cx: AsyncApp,
2047 ) -> Result<Self> {
2048 let position = message
2049 .position
2050 .and_then(deserialize_anchor)
2051 .context("invalid position")?;
2052 buffer
2053 .update(&mut cx, |buffer, _| {
2054 buffer.wait_for_version(deserialize_version(&message.version))
2055 })?
2056 .await?;
2057 Ok(Self {
2058 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer))?,
2059 })
2060 }
2061
2062 fn response_to_proto(
2063 response: Self::Response,
2064 _: &mut LspStore,
2065 _: PeerId,
2066 _: &clock::Global,
2067 _: &mut App,
2068 ) -> proto::GetHoverResponse {
2069 if let Some(response) = response {
2070 let (start, end) = if let Some(range) = response.range {
2071 (
2072 Some(language::proto::serialize_anchor(&range.start)),
2073 Some(language::proto::serialize_anchor(&range.end)),
2074 )
2075 } else {
2076 (None, None)
2077 };
2078
2079 let contents = response
2080 .contents
2081 .into_iter()
2082 .map(|block| proto::HoverBlock {
2083 text: block.text,
2084 is_markdown: block.kind == HoverBlockKind::Markdown,
2085 language: if let HoverBlockKind::Code { language } = block.kind {
2086 Some(language)
2087 } else {
2088 None
2089 },
2090 })
2091 .collect();
2092
2093 proto::GetHoverResponse {
2094 start,
2095 end,
2096 contents,
2097 }
2098 } else {
2099 proto::GetHoverResponse {
2100 start: None,
2101 end: None,
2102 contents: Vec::new(),
2103 }
2104 }
2105 }
2106
2107 async fn response_from_proto(
2108 self,
2109 message: proto::GetHoverResponse,
2110 _: Entity<LspStore>,
2111 buffer: Entity<Buffer>,
2112 mut cx: AsyncApp,
2113 ) -> Result<Self::Response> {
2114 let contents: Vec<_> = message
2115 .contents
2116 .into_iter()
2117 .map(|block| HoverBlock {
2118 text: block.text,
2119 kind: if let Some(language) = block.language {
2120 HoverBlockKind::Code { language }
2121 } else if block.is_markdown {
2122 HoverBlockKind::Markdown
2123 } else {
2124 HoverBlockKind::PlainText
2125 },
2126 })
2127 .collect();
2128 if contents.is_empty() {
2129 return Ok(None);
2130 }
2131
2132 let language = buffer.read_with(&cx, |buffer, _| buffer.language().cloned())?;
2133 let range = if let (Some(start), Some(end)) = (message.start, message.end) {
2134 language::proto::deserialize_anchor(start)
2135 .and_then(|start| language::proto::deserialize_anchor(end).map(|end| start..end))
2136 } else {
2137 None
2138 };
2139 if let Some(range) = range.as_ref() {
2140 buffer
2141 .update(&mut cx, |buffer, _| {
2142 buffer.wait_for_anchors([range.start, range.end])
2143 })?
2144 .await?;
2145 }
2146
2147 Ok(Some(Hover {
2148 contents,
2149 range,
2150 language,
2151 }))
2152 }
2153
2154 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> Result<BufferId> {
2155 BufferId::new(message.buffer_id)
2156 }
2157}
2158
2159impl GetCompletions {
2160 pub fn can_resolve_completions(capabilities: &lsp::ServerCapabilities) -> bool {
2161 capabilities
2162 .completion_provider
2163 .as_ref()
2164 .and_then(|options| options.resolve_provider)
2165 .unwrap_or(false)
2166 }
2167}
2168
2169#[async_trait(?Send)]
2170impl LspCommand for GetCompletions {
2171 type Response = CoreCompletionResponse;
2172 type LspRequest = lsp::request::Completion;
2173 type ProtoRequest = proto::GetCompletions;
2174
2175 fn display_name(&self) -> &str {
2176 "Get completion"
2177 }
2178
2179 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
2180 capabilities
2181 .server_capabilities
2182 .completion_provider
2183 .is_some()
2184 }
2185
2186 fn to_lsp(
2187 &self,
2188 path: &Path,
2189 _: &Buffer,
2190 _: &Arc<LanguageServer>,
2191 _: &App,
2192 ) -> Result<lsp::CompletionParams> {
2193 Ok(lsp::CompletionParams {
2194 text_document_position: make_lsp_text_document_position(path, self.position)?,
2195 context: Some(self.context.clone()),
2196 work_done_progress_params: Default::default(),
2197 partial_result_params: Default::default(),
2198 })
2199 }
2200
2201 async fn response_from_lsp(
2202 self,
2203 completions: Option<lsp::CompletionResponse>,
2204 lsp_store: Entity<LspStore>,
2205 buffer: Entity<Buffer>,
2206 server_id: LanguageServerId,
2207 mut cx: AsyncApp,
2208 ) -> Result<Self::Response> {
2209 let mut response_list = None;
2210 let (mut completions, mut is_incomplete) = if let Some(completions) = completions {
2211 match completions {
2212 lsp::CompletionResponse::Array(completions) => (completions, false),
2213 lsp::CompletionResponse::List(mut list) => {
2214 let is_incomplete = list.is_incomplete;
2215 let items = std::mem::take(&mut list.items);
2216 response_list = Some(list);
2217 (items, is_incomplete)
2218 }
2219 }
2220 } else {
2221 (Vec::new(), false)
2222 };
2223
2224 let unfiltered_completions_count = completions.len();
2225
2226 let language_server_adapter = lsp_store
2227 .read_with(&cx, |lsp_store, _| {
2228 lsp_store.language_server_adapter_for_id(server_id)
2229 })?
2230 .with_context(|| format!("no language server with id {server_id}"))?;
2231
2232 let lsp_defaults = response_list
2233 .as_ref()
2234 .and_then(|list| list.item_defaults.clone())
2235 .map(Arc::new);
2236
2237 let mut completion_edits = Vec::new();
2238 buffer.update(&mut cx, |buffer, _cx| {
2239 let snapshot = buffer.snapshot();
2240 let clipped_position = buffer.clip_point_utf16(Unclipped(self.position), Bias::Left);
2241
2242 let mut range_for_token = None;
2243 completions.retain(|lsp_completion| {
2244 let lsp_edit = lsp_completion.text_edit.clone().or_else(|| {
2245 let default_text_edit = lsp_defaults.as_deref()?.edit_range.as_ref()?;
2246 let new_text = lsp_completion
2247 .text_edit_text
2248 .as_ref()
2249 .unwrap_or(&lsp_completion.label)
2250 .clone();
2251 match default_text_edit {
2252 CompletionListItemDefaultsEditRange::Range(range) => {
2253 Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
2254 range: *range,
2255 new_text,
2256 }))
2257 }
2258 CompletionListItemDefaultsEditRange::InsertAndReplace {
2259 insert,
2260 replace,
2261 } => Some(lsp::CompletionTextEdit::InsertAndReplace(
2262 lsp::InsertReplaceEdit {
2263 new_text,
2264 insert: *insert,
2265 replace: *replace,
2266 },
2267 )),
2268 }
2269 });
2270
2271 let edit = match lsp_edit {
2272 // If the language server provides a range to overwrite, then
2273 // check that the range is valid.
2274 Some(completion_text_edit) => {
2275 match parse_completion_text_edit(&completion_text_edit, &snapshot) {
2276 Some(edit) => edit,
2277 None => return false,
2278 }
2279 }
2280 // If the language server does not provide a range, then infer
2281 // the range based on the syntax tree.
2282 None => {
2283 if self.position != clipped_position {
2284 log::info!("completion out of expected range ");
2285 return false;
2286 }
2287
2288 let default_edit_range = lsp_defaults.as_ref().and_then(|lsp_defaults| {
2289 lsp_defaults
2290 .edit_range
2291 .as_ref()
2292 .and_then(|range| match range {
2293 CompletionListItemDefaultsEditRange::Range(r) => Some(r),
2294 _ => None,
2295 })
2296 });
2297
2298 let range = if let Some(range) = default_edit_range {
2299 let range = range_from_lsp(*range);
2300 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
2301 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
2302 if start != range.start.0 || end != range.end.0 {
2303 log::info!("completion out of expected range");
2304 return false;
2305 }
2306
2307 snapshot.anchor_before(start)..snapshot.anchor_after(end)
2308 } else {
2309 range_for_token
2310 .get_or_insert_with(|| {
2311 let offset = self.position.to_offset(&snapshot);
2312 let (range, kind) = snapshot.surrounding_word(
2313 offset,
2314 Some(CharScopeContext::Completion),
2315 );
2316 let range = if kind == Some(CharKind::Word) {
2317 range
2318 } else {
2319 offset..offset
2320 };
2321
2322 snapshot.anchor_before(range.start)
2323 ..snapshot.anchor_after(range.end)
2324 })
2325 .clone()
2326 };
2327
2328 // We already know text_edit is None here
2329 let text = lsp_completion
2330 .insert_text
2331 .as_ref()
2332 .unwrap_or(&lsp_completion.label)
2333 .clone();
2334
2335 ParsedCompletionEdit {
2336 replace_range: range,
2337 insert_range: None,
2338 new_text: text,
2339 }
2340 }
2341 };
2342
2343 completion_edits.push(edit);
2344 true
2345 });
2346 })?;
2347
2348 // If completions were filtered out due to errors that may be transient, mark the result
2349 // incomplete so that it is re-queried.
2350 if unfiltered_completions_count != completions.len() {
2351 is_incomplete = true;
2352 }
2353
2354 language_server_adapter
2355 .process_completions(&mut completions)
2356 .await;
2357
2358 let completions = completions
2359 .into_iter()
2360 .zip(completion_edits)
2361 .map(|(mut lsp_completion, mut edit)| {
2362 LineEnding::normalize(&mut edit.new_text);
2363 if lsp_completion.data.is_none()
2364 && let Some(default_data) = lsp_defaults
2365 .as_ref()
2366 .and_then(|item_defaults| item_defaults.data.clone())
2367 {
2368 // Servers (e.g. JDTLS) prefer unchanged completions, when resolving the items later,
2369 // so we do not insert the defaults here, but `data` is needed for resolving, so this is an exception.
2370 lsp_completion.data = Some(default_data);
2371 }
2372 CoreCompletion {
2373 replace_range: edit.replace_range,
2374 new_text: edit.new_text,
2375 source: CompletionSource::Lsp {
2376 insert_range: edit.insert_range,
2377 server_id,
2378 lsp_completion: Box::new(lsp_completion),
2379 lsp_defaults: lsp_defaults.clone(),
2380 resolved: false,
2381 },
2382 }
2383 })
2384 .collect();
2385
2386 Ok(CoreCompletionResponse {
2387 completions,
2388 is_incomplete,
2389 })
2390 }
2391
2392 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCompletions {
2393 let anchor = buffer.anchor_after(self.position);
2394 proto::GetCompletions {
2395 project_id,
2396 buffer_id: buffer.remote_id().into(),
2397 position: Some(language::proto::serialize_anchor(&anchor)),
2398 version: serialize_version(&buffer.version()),
2399 server_id: self.server_id.map(|id| id.to_proto()),
2400 }
2401 }
2402
2403 async fn from_proto(
2404 message: proto::GetCompletions,
2405 _: Entity<LspStore>,
2406 buffer: Entity<Buffer>,
2407 mut cx: AsyncApp,
2408 ) -> Result<Self> {
2409 let version = deserialize_version(&message.version);
2410 buffer
2411 .update(&mut cx, |buffer, _| buffer.wait_for_version(version))?
2412 .await?;
2413 let position = message
2414 .position
2415 .and_then(language::proto::deserialize_anchor)
2416 .map(|p| {
2417 buffer.read_with(&cx, |buffer, _| {
2418 buffer.clip_point_utf16(Unclipped(p.to_point_utf16(buffer)), Bias::Left)
2419 })
2420 })
2421 .context("invalid position")??;
2422 Ok(Self {
2423 position,
2424 context: CompletionContext {
2425 trigger_kind: CompletionTriggerKind::INVOKED,
2426 trigger_character: None,
2427 },
2428 server_id: message
2429 .server_id
2430 .map(|id| lsp::LanguageServerId::from_proto(id)),
2431 })
2432 }
2433
2434 fn response_to_proto(
2435 response: CoreCompletionResponse,
2436 _: &mut LspStore,
2437 _: PeerId,
2438 buffer_version: &clock::Global,
2439 _: &mut App,
2440 ) -> proto::GetCompletionsResponse {
2441 proto::GetCompletionsResponse {
2442 completions: response
2443 .completions
2444 .iter()
2445 .map(LspStore::serialize_completion)
2446 .collect(),
2447 version: serialize_version(buffer_version),
2448 can_reuse: !response.is_incomplete,
2449 }
2450 }
2451
2452 async fn response_from_proto(
2453 self,
2454 message: proto::GetCompletionsResponse,
2455 _project: Entity<LspStore>,
2456 buffer: Entity<Buffer>,
2457 mut cx: AsyncApp,
2458 ) -> Result<Self::Response> {
2459 buffer
2460 .update(&mut cx, |buffer, _| {
2461 buffer.wait_for_version(deserialize_version(&message.version))
2462 })?
2463 .await?;
2464
2465 let completions = message
2466 .completions
2467 .into_iter()
2468 .map(LspStore::deserialize_completion)
2469 .collect::<Result<Vec<_>>>()?;
2470
2471 Ok(CoreCompletionResponse {
2472 completions,
2473 is_incomplete: !message.can_reuse,
2474 })
2475 }
2476
2477 fn buffer_id_from_proto(message: &proto::GetCompletions) -> Result<BufferId> {
2478 BufferId::new(message.buffer_id)
2479 }
2480}
2481
2482pub struct ParsedCompletionEdit {
2483 pub replace_range: Range<Anchor>,
2484 pub insert_range: Option<Range<Anchor>>,
2485 pub new_text: String,
2486}
2487
2488pub(crate) fn parse_completion_text_edit(
2489 edit: &lsp::CompletionTextEdit,
2490 snapshot: &BufferSnapshot,
2491) -> Option<ParsedCompletionEdit> {
2492 let (replace_range, insert_range, new_text) = match edit {
2493 lsp::CompletionTextEdit::Edit(edit) => (edit.range, None, &edit.new_text),
2494 lsp::CompletionTextEdit::InsertAndReplace(edit) => {
2495 (edit.replace, Some(edit.insert), &edit.new_text)
2496 }
2497 };
2498
2499 let replace_range = {
2500 let range = range_from_lsp(replace_range);
2501 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
2502 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
2503 if start != range.start.0 || end != range.end.0 {
2504 log::info!(
2505 "completion out of expected range, start: {start:?}, end: {end:?}, range: {range:?}"
2506 );
2507 return None;
2508 }
2509 snapshot.anchor_before(start)..snapshot.anchor_after(end)
2510 };
2511
2512 let insert_range = match insert_range {
2513 None => None,
2514 Some(insert_range) => {
2515 let range = range_from_lsp(insert_range);
2516 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
2517 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
2518 if start != range.start.0 || end != range.end.0 {
2519 log::info!("completion (insert) out of expected range");
2520 return None;
2521 }
2522 Some(snapshot.anchor_before(start)..snapshot.anchor_after(end))
2523 }
2524 };
2525
2526 Some(ParsedCompletionEdit {
2527 insert_range,
2528 replace_range,
2529 new_text: new_text.clone(),
2530 })
2531}
2532
2533#[async_trait(?Send)]
2534impl LspCommand for GetCodeActions {
2535 type Response = Vec<CodeAction>;
2536 type LspRequest = lsp::request::CodeActionRequest;
2537 type ProtoRequest = proto::GetCodeActions;
2538
2539 fn display_name(&self) -> &str {
2540 "Get code actions"
2541 }
2542
2543 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
2544 match &capabilities.server_capabilities.code_action_provider {
2545 None => false,
2546 Some(lsp::CodeActionProviderCapability::Simple(false)) => false,
2547 _ => {
2548 // If we do know that we want specific code actions AND we know that
2549 // the server only supports specific code actions, then we want to filter
2550 // down to the ones that are supported.
2551 if let Some((requested, supported)) = self
2552 .kinds
2553 .as_ref()
2554 .zip(Self::supported_code_action_kinds(capabilities))
2555 {
2556 let server_supported = supported.into_iter().collect::<HashSet<_>>();
2557 requested.iter().any(|kind| server_supported.contains(kind))
2558 } else {
2559 true
2560 }
2561 }
2562 }
2563 }
2564
2565 fn to_lsp(
2566 &self,
2567 path: &Path,
2568 buffer: &Buffer,
2569 language_server: &Arc<LanguageServer>,
2570 _: &App,
2571 ) -> Result<lsp::CodeActionParams> {
2572 let mut relevant_diagnostics = Vec::new();
2573 for entry in buffer
2574 .snapshot()
2575 .diagnostics_in_range::<_, language::PointUtf16>(self.range.clone(), false)
2576 {
2577 relevant_diagnostics.push(entry.to_lsp_diagnostic_stub()?);
2578 }
2579
2580 let supported =
2581 Self::supported_code_action_kinds(language_server.adapter_server_capabilities());
2582
2583 let only = if let Some(requested) = &self.kinds {
2584 if let Some(supported_kinds) = supported {
2585 let server_supported = supported_kinds.into_iter().collect::<HashSet<_>>();
2586
2587 let filtered = requested
2588 .iter()
2589 .filter(|kind| server_supported.contains(kind))
2590 .cloned()
2591 .collect();
2592 Some(filtered)
2593 } else {
2594 Some(requested.clone())
2595 }
2596 } else {
2597 supported
2598 };
2599
2600 Ok(lsp::CodeActionParams {
2601 text_document: make_text_document_identifier(path)?,
2602 range: range_to_lsp(self.range.to_point_utf16(buffer))?,
2603 work_done_progress_params: Default::default(),
2604 partial_result_params: Default::default(),
2605 context: lsp::CodeActionContext {
2606 diagnostics: relevant_diagnostics,
2607 only,
2608 ..lsp::CodeActionContext::default()
2609 },
2610 })
2611 }
2612
2613 async fn response_from_lsp(
2614 self,
2615 actions: Option<lsp::CodeActionResponse>,
2616 lsp_store: Entity<LspStore>,
2617 _: Entity<Buffer>,
2618 server_id: LanguageServerId,
2619 cx: AsyncApp,
2620 ) -> Result<Vec<CodeAction>> {
2621 let requested_kinds_set = self
2622 .kinds
2623 .map(|kinds| kinds.into_iter().collect::<HashSet<_>>());
2624
2625 let language_server = cx.update(|cx| {
2626 lsp_store
2627 .read(cx)
2628 .language_server_for_id(server_id)
2629 .with_context(|| {
2630 format!("Missing the language server that just returned a response {server_id}")
2631 })
2632 })??;
2633
2634 let server_capabilities = language_server.capabilities();
2635 let available_commands = server_capabilities
2636 .execute_command_provider
2637 .as_ref()
2638 .map(|options| options.commands.as_slice())
2639 .unwrap_or_default();
2640 Ok(actions
2641 .unwrap_or_default()
2642 .into_iter()
2643 .filter_map(|entry| {
2644 let (lsp_action, resolved) = match entry {
2645 lsp::CodeActionOrCommand::CodeAction(lsp_action) => {
2646 if let Some(command) = lsp_action.command.as_ref()
2647 && !available_commands.contains(&command.command)
2648 {
2649 return None;
2650 }
2651 (LspAction::Action(Box::new(lsp_action)), false)
2652 }
2653 lsp::CodeActionOrCommand::Command(command) => {
2654 if available_commands.contains(&command.command) {
2655 (LspAction::Command(command), true)
2656 } else {
2657 return None;
2658 }
2659 }
2660 };
2661
2662 if let Some((requested_kinds, kind)) =
2663 requested_kinds_set.as_ref().zip(lsp_action.action_kind())
2664 && !requested_kinds.contains(&kind)
2665 {
2666 return None;
2667 }
2668
2669 Some(CodeAction {
2670 server_id,
2671 range: self.range.clone(),
2672 lsp_action,
2673 resolved,
2674 })
2675 })
2676 .collect())
2677 }
2678
2679 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCodeActions {
2680 proto::GetCodeActions {
2681 project_id,
2682 buffer_id: buffer.remote_id().into(),
2683 start: Some(language::proto::serialize_anchor(&self.range.start)),
2684 end: Some(language::proto::serialize_anchor(&self.range.end)),
2685 version: serialize_version(&buffer.version()),
2686 }
2687 }
2688
2689 async fn from_proto(
2690 message: proto::GetCodeActions,
2691 _: Entity<LspStore>,
2692 buffer: Entity<Buffer>,
2693 mut cx: AsyncApp,
2694 ) -> Result<Self> {
2695 let start = message
2696 .start
2697 .and_then(language::proto::deserialize_anchor)
2698 .context("invalid start")?;
2699 let end = message
2700 .end
2701 .and_then(language::proto::deserialize_anchor)
2702 .context("invalid end")?;
2703 buffer
2704 .update(&mut cx, |buffer, _| {
2705 buffer.wait_for_version(deserialize_version(&message.version))
2706 })?
2707 .await?;
2708
2709 Ok(Self {
2710 range: start..end,
2711 kinds: None,
2712 })
2713 }
2714
2715 fn response_to_proto(
2716 code_actions: Vec<CodeAction>,
2717 _: &mut LspStore,
2718 _: PeerId,
2719 buffer_version: &clock::Global,
2720 _: &mut App,
2721 ) -> proto::GetCodeActionsResponse {
2722 proto::GetCodeActionsResponse {
2723 actions: code_actions
2724 .iter()
2725 .map(LspStore::serialize_code_action)
2726 .collect(),
2727 version: serialize_version(buffer_version),
2728 }
2729 }
2730
2731 async fn response_from_proto(
2732 self,
2733 message: proto::GetCodeActionsResponse,
2734 _: Entity<LspStore>,
2735 buffer: Entity<Buffer>,
2736 mut cx: AsyncApp,
2737 ) -> Result<Vec<CodeAction>> {
2738 buffer
2739 .update(&mut cx, |buffer, _| {
2740 buffer.wait_for_version(deserialize_version(&message.version))
2741 })?
2742 .await?;
2743 message
2744 .actions
2745 .into_iter()
2746 .map(LspStore::deserialize_code_action)
2747 .collect()
2748 }
2749
2750 fn buffer_id_from_proto(message: &proto::GetCodeActions) -> Result<BufferId> {
2751 BufferId::new(message.buffer_id)
2752 }
2753}
2754
2755impl GetCodeActions {
2756 fn supported_code_action_kinds(
2757 capabilities: AdapterServerCapabilities,
2758 ) -> Option<Vec<CodeActionKind>> {
2759 match capabilities.server_capabilities.code_action_provider {
2760 Some(lsp::CodeActionProviderCapability::Options(CodeActionOptions {
2761 code_action_kinds: Some(supported_action_kinds),
2762 ..
2763 })) => Some(supported_action_kinds),
2764 _ => capabilities.code_action_kinds,
2765 }
2766 }
2767
2768 pub fn can_resolve_actions(capabilities: &ServerCapabilities) -> bool {
2769 capabilities
2770 .code_action_provider
2771 .as_ref()
2772 .and_then(|options| match options {
2773 lsp::CodeActionProviderCapability::Simple(_is_supported) => None,
2774 lsp::CodeActionProviderCapability::Options(options) => options.resolve_provider,
2775 })
2776 .unwrap_or(false)
2777 }
2778}
2779
2780impl OnTypeFormatting {
2781 pub fn supports_on_type_formatting(trigger: &str, capabilities: &ServerCapabilities) -> bool {
2782 let Some(on_type_formatting_options) = &capabilities.document_on_type_formatting_provider
2783 else {
2784 return false;
2785 };
2786 on_type_formatting_options
2787 .first_trigger_character
2788 .contains(trigger)
2789 || on_type_formatting_options
2790 .more_trigger_character
2791 .iter()
2792 .flatten()
2793 .any(|chars| chars.contains(trigger))
2794 }
2795}
2796
2797#[async_trait(?Send)]
2798impl LspCommand for OnTypeFormatting {
2799 type Response = Option<Transaction>;
2800 type LspRequest = lsp::request::OnTypeFormatting;
2801 type ProtoRequest = proto::OnTypeFormatting;
2802
2803 fn display_name(&self) -> &str {
2804 "Formatting on typing"
2805 }
2806
2807 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
2808 Self::supports_on_type_formatting(&self.trigger, &capabilities.server_capabilities)
2809 }
2810
2811 fn to_lsp(
2812 &self,
2813 path: &Path,
2814 _: &Buffer,
2815 _: &Arc<LanguageServer>,
2816 _: &App,
2817 ) -> Result<lsp::DocumentOnTypeFormattingParams> {
2818 Ok(lsp::DocumentOnTypeFormattingParams {
2819 text_document_position: make_lsp_text_document_position(path, self.position)?,
2820 ch: self.trigger.clone(),
2821 options: self.options.clone(),
2822 })
2823 }
2824
2825 async fn response_from_lsp(
2826 self,
2827 message: Option<Vec<lsp::TextEdit>>,
2828 lsp_store: Entity<LspStore>,
2829 buffer: Entity<Buffer>,
2830 server_id: LanguageServerId,
2831 mut cx: AsyncApp,
2832 ) -> Result<Option<Transaction>> {
2833 if let Some(edits) = message {
2834 let (lsp_adapter, lsp_server) =
2835 language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
2836 LocalLspStore::deserialize_text_edits(
2837 lsp_store,
2838 buffer,
2839 edits,
2840 self.push_to_history,
2841 lsp_adapter,
2842 lsp_server,
2843 &mut cx,
2844 )
2845 .await
2846 } else {
2847 Ok(None)
2848 }
2849 }
2850
2851 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::OnTypeFormatting {
2852 proto::OnTypeFormatting {
2853 project_id,
2854 buffer_id: buffer.remote_id().into(),
2855 position: Some(language::proto::serialize_anchor(
2856 &buffer.anchor_before(self.position),
2857 )),
2858 trigger: self.trigger.clone(),
2859 version: serialize_version(&buffer.version()),
2860 }
2861 }
2862
2863 async fn from_proto(
2864 message: proto::OnTypeFormatting,
2865 _: Entity<LspStore>,
2866 buffer: Entity<Buffer>,
2867 mut cx: AsyncApp,
2868 ) -> Result<Self> {
2869 let position = message
2870 .position
2871 .and_then(deserialize_anchor)
2872 .context("invalid position")?;
2873 buffer
2874 .update(&mut cx, |buffer, _| {
2875 buffer.wait_for_version(deserialize_version(&message.version))
2876 })?
2877 .await?;
2878
2879 let options = buffer.update(&mut cx, |buffer, cx| {
2880 lsp_formatting_options(
2881 language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx).as_ref(),
2882 )
2883 })?;
2884
2885 Ok(Self {
2886 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer))?,
2887 trigger: message.trigger.clone(),
2888 options,
2889 push_to_history: false,
2890 })
2891 }
2892
2893 fn response_to_proto(
2894 response: Option<Transaction>,
2895 _: &mut LspStore,
2896 _: PeerId,
2897 _: &clock::Global,
2898 _: &mut App,
2899 ) -> proto::OnTypeFormattingResponse {
2900 proto::OnTypeFormattingResponse {
2901 transaction: response
2902 .map(|transaction| language::proto::serialize_transaction(&transaction)),
2903 }
2904 }
2905
2906 async fn response_from_proto(
2907 self,
2908 message: proto::OnTypeFormattingResponse,
2909 _: Entity<LspStore>,
2910 _: Entity<Buffer>,
2911 _: AsyncApp,
2912 ) -> Result<Option<Transaction>> {
2913 let Some(transaction) = message.transaction else {
2914 return Ok(None);
2915 };
2916 Ok(Some(language::proto::deserialize_transaction(transaction)?))
2917 }
2918
2919 fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> Result<BufferId> {
2920 BufferId::new(message.buffer_id)
2921 }
2922}
2923
2924impl InlayHints {
2925 pub async fn lsp_to_project_hint(
2926 lsp_hint: lsp::InlayHint,
2927 buffer_handle: &Entity<Buffer>,
2928 server_id: LanguageServerId,
2929 resolve_state: ResolveState,
2930 force_no_type_left_padding: bool,
2931 cx: &mut AsyncApp,
2932 ) -> anyhow::Result<InlayHint> {
2933 let kind = lsp_hint.kind.and_then(|kind| match kind {
2934 lsp::InlayHintKind::TYPE => Some(InlayHintKind::Type),
2935 lsp::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
2936 _ => None,
2937 });
2938
2939 let position = buffer_handle.read_with(cx, |buffer, _| {
2940 let position = buffer.clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
2941 if kind == Some(InlayHintKind::Parameter) {
2942 buffer.anchor_before(position)
2943 } else {
2944 buffer.anchor_after(position)
2945 }
2946 })?;
2947 let label = Self::lsp_inlay_label_to_project(lsp_hint.label, server_id)
2948 .await
2949 .context("lsp to project inlay hint conversion")?;
2950 let padding_left = if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
2951 false
2952 } else {
2953 lsp_hint.padding_left.unwrap_or(false)
2954 };
2955
2956 Ok(InlayHint {
2957 position,
2958 padding_left,
2959 padding_right: lsp_hint.padding_right.unwrap_or(false),
2960 label,
2961 kind,
2962 tooltip: lsp_hint.tooltip.map(|tooltip| match tooltip {
2963 lsp::InlayHintTooltip::String(s) => InlayHintTooltip::String(s),
2964 lsp::InlayHintTooltip::MarkupContent(markup_content) => {
2965 InlayHintTooltip::MarkupContent(MarkupContent {
2966 kind: match markup_content.kind {
2967 lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
2968 lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
2969 },
2970 value: markup_content.value,
2971 })
2972 }
2973 }),
2974 resolve_state,
2975 })
2976 }
2977
2978 async fn lsp_inlay_label_to_project(
2979 lsp_label: lsp::InlayHintLabel,
2980 server_id: LanguageServerId,
2981 ) -> anyhow::Result<InlayHintLabel> {
2982 let label = match lsp_label {
2983 lsp::InlayHintLabel::String(s) => InlayHintLabel::String(s),
2984 lsp::InlayHintLabel::LabelParts(lsp_parts) => {
2985 let mut parts = Vec::with_capacity(lsp_parts.len());
2986 for lsp_part in lsp_parts {
2987 parts.push(InlayHintLabelPart {
2988 value: lsp_part.value,
2989 tooltip: lsp_part.tooltip.map(|tooltip| match tooltip {
2990 lsp::InlayHintLabelPartTooltip::String(s) => {
2991 InlayHintLabelPartTooltip::String(s)
2992 }
2993 lsp::InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
2994 InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2995 kind: match markup_content.kind {
2996 lsp::MarkupKind::PlainText => HoverBlockKind::PlainText,
2997 lsp::MarkupKind::Markdown => HoverBlockKind::Markdown,
2998 },
2999 value: markup_content.value,
3000 })
3001 }
3002 }),
3003 location: Some(server_id).zip(lsp_part.location),
3004 });
3005 }
3006 InlayHintLabel::LabelParts(parts)
3007 }
3008 };
3009
3010 Ok(label)
3011 }
3012
3013 pub fn project_to_proto_hint(response_hint: InlayHint) -> proto::InlayHint {
3014 let (state, lsp_resolve_state) = match response_hint.resolve_state {
3015 ResolveState::Resolved => (0, None),
3016 ResolveState::CanResolve(server_id, resolve_data) => (
3017 1,
3018 Some(proto::resolve_state::LspResolveState {
3019 server_id: server_id.0 as u64,
3020 value: resolve_data.map(|json_data| {
3021 serde_json::to_string(&json_data)
3022 .expect("failed to serialize resolve json data")
3023 }),
3024 }),
3025 ),
3026 ResolveState::Resolving => (2, None),
3027 };
3028 let resolve_state = Some(proto::ResolveState {
3029 state,
3030 lsp_resolve_state,
3031 });
3032 proto::InlayHint {
3033 position: Some(language::proto::serialize_anchor(&response_hint.position)),
3034 padding_left: response_hint.padding_left,
3035 padding_right: response_hint.padding_right,
3036 label: Some(proto::InlayHintLabel {
3037 label: Some(match response_hint.label {
3038 InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s),
3039 InlayHintLabel::LabelParts(label_parts) => {
3040 proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts {
3041 parts: label_parts.into_iter().map(|label_part| {
3042 let location_url = label_part.location.as_ref().map(|(_, location)| location.uri.to_string());
3043 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 });
3044 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 });
3045 proto::InlayHintLabelPart {
3046 value: label_part.value,
3047 tooltip: label_part.tooltip.map(|tooltip| {
3048 let proto_tooltip = match tooltip {
3049 InlayHintLabelPartTooltip::String(s) => proto::inlay_hint_label_part_tooltip::Content::Value(s),
3050 InlayHintLabelPartTooltip::MarkupContent(markup_content) => proto::inlay_hint_label_part_tooltip::Content::MarkupContent(proto::MarkupContent {
3051 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
3052 value: markup_content.value,
3053 }),
3054 };
3055 proto::InlayHintLabelPartTooltip {content: Some(proto_tooltip)}
3056 }),
3057 location_url,
3058 location_range_start,
3059 location_range_end,
3060 language_server_id: label_part.location.as_ref().map(|(server_id, _)| server_id.0 as u64),
3061 }}).collect()
3062 })
3063 }
3064 }),
3065 }),
3066 kind: response_hint.kind.map(|kind| kind.name().to_string()),
3067 tooltip: response_hint.tooltip.map(|response_tooltip| {
3068 let proto_tooltip = match response_tooltip {
3069 InlayHintTooltip::String(s) => proto::inlay_hint_tooltip::Content::Value(s),
3070 InlayHintTooltip::MarkupContent(markup_content) => {
3071 proto::inlay_hint_tooltip::Content::MarkupContent(proto::MarkupContent {
3072 is_markdown: markup_content.kind == HoverBlockKind::Markdown,
3073 value: markup_content.value,
3074 })
3075 }
3076 };
3077 proto::InlayHintTooltip {
3078 content: Some(proto_tooltip),
3079 }
3080 }),
3081 resolve_state,
3082 }
3083 }
3084
3085 pub fn proto_to_project_hint(message_hint: proto::InlayHint) -> anyhow::Result<InlayHint> {
3086 let resolve_state = message_hint.resolve_state.as_ref().unwrap_or_else(|| {
3087 panic!("incorrect proto inlay hint message: no resolve state in hint {message_hint:?}",)
3088 });
3089 let resolve_state_data = resolve_state
3090 .lsp_resolve_state.as_ref()
3091 .map(|lsp_resolve_state| {
3092 let value = lsp_resolve_state.value.as_deref().map(|value| {
3093 serde_json::from_str::<Option<lsp::LSPAny>>(value)
3094 .with_context(|| format!("incorrect proto inlay hint message: non-json resolve state {lsp_resolve_state:?}"))
3095 }).transpose()?.flatten();
3096 anyhow::Ok((LanguageServerId(lsp_resolve_state.server_id as usize), value))
3097 })
3098 .transpose()?;
3099 let resolve_state = match resolve_state.state {
3100 0 => ResolveState::Resolved,
3101 1 => {
3102 let (server_id, lsp_resolve_state) = resolve_state_data.with_context(|| {
3103 format!(
3104 "No lsp resolve data for the hint that can be resolved: {message_hint:?}"
3105 )
3106 })?;
3107 ResolveState::CanResolve(server_id, lsp_resolve_state)
3108 }
3109 2 => ResolveState::Resolving,
3110 invalid => {
3111 anyhow::bail!("Unexpected resolve state {invalid} for hint {message_hint:?}")
3112 }
3113 };
3114 Ok(InlayHint {
3115 position: message_hint
3116 .position
3117 .and_then(language::proto::deserialize_anchor)
3118 .context("invalid position")?,
3119 label: match message_hint
3120 .label
3121 .and_then(|label| label.label)
3122 .context("missing label")?
3123 {
3124 proto::inlay_hint_label::Label::Value(s) => InlayHintLabel::String(s),
3125 proto::inlay_hint_label::Label::LabelParts(parts) => {
3126 let mut label_parts = Vec::new();
3127 for part in parts.parts {
3128 label_parts.push(InlayHintLabelPart {
3129 value: part.value,
3130 tooltip: part.tooltip.map(|tooltip| match tooltip.content {
3131 Some(proto::inlay_hint_label_part_tooltip::Content::Value(s)) => {
3132 InlayHintLabelPartTooltip::String(s)
3133 }
3134 Some(
3135 proto::inlay_hint_label_part_tooltip::Content::MarkupContent(
3136 markup_content,
3137 ),
3138 ) => InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
3139 kind: if markup_content.is_markdown {
3140 HoverBlockKind::Markdown
3141 } else {
3142 HoverBlockKind::PlainText
3143 },
3144 value: markup_content.value,
3145 }),
3146 None => InlayHintLabelPartTooltip::String(String::new()),
3147 }),
3148 location: {
3149 match part
3150 .location_url
3151 .zip(
3152 part.location_range_start.and_then(|start| {
3153 Some(start..part.location_range_end?)
3154 }),
3155 )
3156 .zip(part.language_server_id)
3157 {
3158 Some(((uri, range), server_id)) => Some((
3159 LanguageServerId(server_id as usize),
3160 lsp::Location {
3161 uri: lsp::Uri::from_str(&uri)
3162 .context("invalid uri in hint part {part:?}")?,
3163 range: lsp::Range::new(
3164 point_to_lsp(PointUtf16::new(
3165 range.start.row,
3166 range.start.column,
3167 )),
3168 point_to_lsp(PointUtf16::new(
3169 range.end.row,
3170 range.end.column,
3171 )),
3172 ),
3173 },
3174 )),
3175 None => None,
3176 }
3177 },
3178 });
3179 }
3180
3181 InlayHintLabel::LabelParts(label_parts)
3182 }
3183 },
3184 padding_left: message_hint.padding_left,
3185 padding_right: message_hint.padding_right,
3186 kind: message_hint
3187 .kind
3188 .as_deref()
3189 .and_then(InlayHintKind::from_name),
3190 tooltip: message_hint.tooltip.and_then(|tooltip| {
3191 Some(match tooltip.content? {
3192 proto::inlay_hint_tooltip::Content::Value(s) => InlayHintTooltip::String(s),
3193 proto::inlay_hint_tooltip::Content::MarkupContent(markup_content) => {
3194 InlayHintTooltip::MarkupContent(MarkupContent {
3195 kind: if markup_content.is_markdown {
3196 HoverBlockKind::Markdown
3197 } else {
3198 HoverBlockKind::PlainText
3199 },
3200 value: markup_content.value,
3201 })
3202 }
3203 })
3204 }),
3205 resolve_state,
3206 })
3207 }
3208
3209 pub fn project_to_lsp_hint(hint: InlayHint, snapshot: &BufferSnapshot) -> lsp::InlayHint {
3210 lsp::InlayHint {
3211 position: point_to_lsp(hint.position.to_point_utf16(snapshot)),
3212 kind: hint.kind.map(|kind| match kind {
3213 InlayHintKind::Type => lsp::InlayHintKind::TYPE,
3214 InlayHintKind::Parameter => lsp::InlayHintKind::PARAMETER,
3215 }),
3216 text_edits: None,
3217 tooltip: hint.tooltip.and_then(|tooltip| {
3218 Some(match tooltip {
3219 InlayHintTooltip::String(s) => lsp::InlayHintTooltip::String(s),
3220 InlayHintTooltip::MarkupContent(markup_content) => {
3221 lsp::InlayHintTooltip::MarkupContent(lsp::MarkupContent {
3222 kind: match markup_content.kind {
3223 HoverBlockKind::PlainText => lsp::MarkupKind::PlainText,
3224 HoverBlockKind::Markdown => lsp::MarkupKind::Markdown,
3225 HoverBlockKind::Code { .. } => return None,
3226 },
3227 value: markup_content.value,
3228 })
3229 }
3230 })
3231 }),
3232 label: match hint.label {
3233 InlayHintLabel::String(s) => lsp::InlayHintLabel::String(s),
3234 InlayHintLabel::LabelParts(label_parts) => lsp::InlayHintLabel::LabelParts(
3235 label_parts
3236 .into_iter()
3237 .map(|part| lsp::InlayHintLabelPart {
3238 value: part.value,
3239 tooltip: part.tooltip.and_then(|tooltip| {
3240 Some(match tooltip {
3241 InlayHintLabelPartTooltip::String(s) => {
3242 lsp::InlayHintLabelPartTooltip::String(s)
3243 }
3244 InlayHintLabelPartTooltip::MarkupContent(markup_content) => {
3245 lsp::InlayHintLabelPartTooltip::MarkupContent(
3246 lsp::MarkupContent {
3247 kind: match markup_content.kind {
3248 HoverBlockKind::PlainText => {
3249 lsp::MarkupKind::PlainText
3250 }
3251 HoverBlockKind::Markdown => {
3252 lsp::MarkupKind::Markdown
3253 }
3254 HoverBlockKind::Code { .. } => return None,
3255 },
3256 value: markup_content.value,
3257 },
3258 )
3259 }
3260 })
3261 }),
3262 location: part.location.map(|(_, location)| location),
3263 command: None,
3264 })
3265 .collect(),
3266 ),
3267 },
3268 padding_left: Some(hint.padding_left),
3269 padding_right: Some(hint.padding_right),
3270 data: match hint.resolve_state {
3271 ResolveState::CanResolve(_, data) => data,
3272 ResolveState::Resolving | ResolveState::Resolved => None,
3273 },
3274 }
3275 }
3276
3277 pub fn can_resolve_inlays(capabilities: &ServerCapabilities) -> bool {
3278 capabilities
3279 .inlay_hint_provider
3280 .as_ref()
3281 .and_then(|options| match options {
3282 OneOf::Left(_is_supported) => None,
3283 OneOf::Right(capabilities) => match capabilities {
3284 lsp::InlayHintServerCapabilities::Options(o) => o.resolve_provider,
3285 lsp::InlayHintServerCapabilities::RegistrationOptions(o) => {
3286 o.inlay_hint_options.resolve_provider
3287 }
3288 },
3289 })
3290 .unwrap_or(false)
3291 }
3292
3293 pub fn check_capabilities(capabilities: &ServerCapabilities) -> bool {
3294 capabilities
3295 .inlay_hint_provider
3296 .as_ref()
3297 .is_some_and(|inlay_hint_provider| match inlay_hint_provider {
3298 lsp::OneOf::Left(enabled) => *enabled,
3299 lsp::OneOf::Right(_) => true,
3300 })
3301 }
3302}
3303
3304#[async_trait(?Send)]
3305impl LspCommand for InlayHints {
3306 type Response = Vec<InlayHint>;
3307 type LspRequest = lsp::InlayHintRequest;
3308 type ProtoRequest = proto::InlayHints;
3309
3310 fn display_name(&self) -> &str {
3311 "Inlay hints"
3312 }
3313
3314 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
3315 Self::check_capabilities(&capabilities.server_capabilities)
3316 }
3317
3318 fn to_lsp(
3319 &self,
3320 path: &Path,
3321 buffer: &Buffer,
3322 _: &Arc<LanguageServer>,
3323 _: &App,
3324 ) -> Result<lsp::InlayHintParams> {
3325 Ok(lsp::InlayHintParams {
3326 text_document: lsp::TextDocumentIdentifier {
3327 uri: file_path_to_lsp_url(path)?,
3328 },
3329 range: range_to_lsp(self.range.to_point_utf16(buffer))?,
3330 work_done_progress_params: Default::default(),
3331 })
3332 }
3333
3334 async fn response_from_lsp(
3335 self,
3336 message: Option<Vec<lsp::InlayHint>>,
3337 lsp_store: Entity<LspStore>,
3338 buffer: Entity<Buffer>,
3339 server_id: LanguageServerId,
3340 mut cx: AsyncApp,
3341 ) -> anyhow::Result<Vec<InlayHint>> {
3342 let (lsp_adapter, lsp_server) =
3343 language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?;
3344 // `typescript-language-server` adds padding to the left for type hints, turning
3345 // `const foo: boolean` into `const foo : boolean` which looks odd.
3346 // `rust-analyzer` does not have the padding for this case, and we have to accommodate both.
3347 //
3348 // We could trim the whole string, but being pessimistic on par with the situation above,
3349 // there might be a hint with multiple whitespaces at the end(s) which we need to display properly.
3350 // Hence let's use a heuristic first to handle the most awkward case and look for more.
3351 let force_no_type_left_padding =
3352 lsp_adapter.name.0.as_ref() == "typescript-language-server";
3353
3354 let hints = message.unwrap_or_default().into_iter().map(|lsp_hint| {
3355 let resolve_state = if InlayHints::can_resolve_inlays(&lsp_server.capabilities()) {
3356 ResolveState::CanResolve(lsp_server.server_id(), lsp_hint.data.clone())
3357 } else {
3358 ResolveState::Resolved
3359 };
3360
3361 let buffer = buffer.clone();
3362 cx.spawn(async move |cx| {
3363 InlayHints::lsp_to_project_hint(
3364 lsp_hint,
3365 &buffer,
3366 server_id,
3367 resolve_state,
3368 force_no_type_left_padding,
3369 cx,
3370 )
3371 .await
3372 })
3373 });
3374 future::join_all(hints)
3375 .await
3376 .into_iter()
3377 .collect::<anyhow::Result<_>>()
3378 .context("lsp to project inlay hints conversion")
3379 }
3380
3381 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints {
3382 proto::InlayHints {
3383 project_id,
3384 buffer_id: buffer.remote_id().into(),
3385 start: Some(language::proto::serialize_anchor(&self.range.start)),
3386 end: Some(language::proto::serialize_anchor(&self.range.end)),
3387 version: serialize_version(&buffer.version()),
3388 }
3389 }
3390
3391 async fn from_proto(
3392 message: proto::InlayHints,
3393 _: Entity<LspStore>,
3394 buffer: Entity<Buffer>,
3395 mut cx: AsyncApp,
3396 ) -> Result<Self> {
3397 let start = message
3398 .start
3399 .and_then(language::proto::deserialize_anchor)
3400 .context("invalid start")?;
3401 let end = message
3402 .end
3403 .and_then(language::proto::deserialize_anchor)
3404 .context("invalid end")?;
3405 buffer
3406 .update(&mut cx, |buffer, _| {
3407 buffer.wait_for_version(deserialize_version(&message.version))
3408 })?
3409 .await?;
3410
3411 Ok(Self { range: start..end })
3412 }
3413
3414 fn response_to_proto(
3415 response: Vec<InlayHint>,
3416 _: &mut LspStore,
3417 _: PeerId,
3418 buffer_version: &clock::Global,
3419 _: &mut App,
3420 ) -> proto::InlayHintsResponse {
3421 proto::InlayHintsResponse {
3422 hints: response
3423 .into_iter()
3424 .map(InlayHints::project_to_proto_hint)
3425 .collect(),
3426 version: serialize_version(buffer_version),
3427 }
3428 }
3429
3430 async fn response_from_proto(
3431 self,
3432 message: proto::InlayHintsResponse,
3433 _: Entity<LspStore>,
3434 buffer: Entity<Buffer>,
3435 mut cx: AsyncApp,
3436 ) -> anyhow::Result<Vec<InlayHint>> {
3437 buffer
3438 .update(&mut cx, |buffer, _| {
3439 buffer.wait_for_version(deserialize_version(&message.version))
3440 })?
3441 .await?;
3442
3443 let mut hints = Vec::new();
3444 for message_hint in message.hints {
3445 hints.push(InlayHints::proto_to_project_hint(message_hint)?);
3446 }
3447
3448 Ok(hints)
3449 }
3450
3451 fn buffer_id_from_proto(message: &proto::InlayHints) -> Result<BufferId> {
3452 BufferId::new(message.buffer_id)
3453 }
3454}
3455
3456#[async_trait(?Send)]
3457impl LspCommand for GetCodeLens {
3458 type Response = Vec<CodeAction>;
3459 type LspRequest = lsp::CodeLensRequest;
3460 type ProtoRequest = proto::GetCodeLens;
3461
3462 fn display_name(&self) -> &str {
3463 "Code Lens"
3464 }
3465
3466 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
3467 capabilities
3468 .server_capabilities
3469 .code_lens_provider
3470 .is_some()
3471 }
3472
3473 fn to_lsp(
3474 &self,
3475 path: &Path,
3476 _: &Buffer,
3477 _: &Arc<LanguageServer>,
3478 _: &App,
3479 ) -> Result<lsp::CodeLensParams> {
3480 Ok(lsp::CodeLensParams {
3481 text_document: lsp::TextDocumentIdentifier {
3482 uri: file_path_to_lsp_url(path)?,
3483 },
3484 work_done_progress_params: lsp::WorkDoneProgressParams::default(),
3485 partial_result_params: lsp::PartialResultParams::default(),
3486 })
3487 }
3488
3489 async fn response_from_lsp(
3490 self,
3491 message: Option<Vec<lsp::CodeLens>>,
3492 lsp_store: Entity<LspStore>,
3493 buffer: Entity<Buffer>,
3494 server_id: LanguageServerId,
3495 cx: AsyncApp,
3496 ) -> anyhow::Result<Vec<CodeAction>> {
3497 let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot())?;
3498 let language_server = cx.update(|cx| {
3499 lsp_store
3500 .read(cx)
3501 .language_server_for_id(server_id)
3502 .with_context(|| {
3503 format!("Missing the language server that just returned a response {server_id}")
3504 })
3505 })??;
3506 let server_capabilities = language_server.capabilities();
3507 let available_commands = server_capabilities
3508 .execute_command_provider
3509 .as_ref()
3510 .map(|options| options.commands.as_slice())
3511 .unwrap_or_default();
3512 Ok(message
3513 .unwrap_or_default()
3514 .into_iter()
3515 .filter(|code_lens| {
3516 code_lens
3517 .command
3518 .as_ref()
3519 .is_none_or(|command| available_commands.contains(&command.command))
3520 })
3521 .map(|code_lens| {
3522 let code_lens_range = range_from_lsp(code_lens.range);
3523 let start = snapshot.clip_point_utf16(code_lens_range.start, Bias::Left);
3524 let end = snapshot.clip_point_utf16(code_lens_range.end, Bias::Right);
3525 let range = snapshot.anchor_before(start)..snapshot.anchor_after(end);
3526 CodeAction {
3527 server_id,
3528 range,
3529 lsp_action: LspAction::CodeLens(code_lens),
3530 resolved: false,
3531 }
3532 })
3533 .collect())
3534 }
3535
3536 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCodeLens {
3537 proto::GetCodeLens {
3538 project_id,
3539 buffer_id: buffer.remote_id().into(),
3540 version: serialize_version(&buffer.version()),
3541 }
3542 }
3543
3544 async fn from_proto(
3545 message: proto::GetCodeLens,
3546 _: Entity<LspStore>,
3547 buffer: Entity<Buffer>,
3548 mut cx: AsyncApp,
3549 ) -> Result<Self> {
3550 buffer
3551 .update(&mut cx, |buffer, _| {
3552 buffer.wait_for_version(deserialize_version(&message.version))
3553 })?
3554 .await?;
3555 Ok(Self)
3556 }
3557
3558 fn response_to_proto(
3559 response: Vec<CodeAction>,
3560 _: &mut LspStore,
3561 _: PeerId,
3562 buffer_version: &clock::Global,
3563 _: &mut App,
3564 ) -> proto::GetCodeLensResponse {
3565 proto::GetCodeLensResponse {
3566 lens_actions: response
3567 .iter()
3568 .map(LspStore::serialize_code_action)
3569 .collect(),
3570 version: serialize_version(buffer_version),
3571 }
3572 }
3573
3574 async fn response_from_proto(
3575 self,
3576 message: proto::GetCodeLensResponse,
3577 _: Entity<LspStore>,
3578 buffer: Entity<Buffer>,
3579 mut cx: AsyncApp,
3580 ) -> anyhow::Result<Vec<CodeAction>> {
3581 buffer
3582 .update(&mut cx, |buffer, _| {
3583 buffer.wait_for_version(deserialize_version(&message.version))
3584 })?
3585 .await?;
3586 message
3587 .lens_actions
3588 .into_iter()
3589 .map(LspStore::deserialize_code_action)
3590 .collect::<Result<Vec<_>>>()
3591 .context("deserializing proto code lens response")
3592 }
3593
3594 fn buffer_id_from_proto(message: &proto::GetCodeLens) -> Result<BufferId> {
3595 BufferId::new(message.buffer_id)
3596 }
3597}
3598
3599impl LinkedEditingRange {
3600 pub fn check_server_capabilities(capabilities: ServerCapabilities) -> bool {
3601 let Some(linked_editing_options) = capabilities.linked_editing_range_provider else {
3602 return false;
3603 };
3604 if let LinkedEditingRangeServerCapabilities::Simple(false) = linked_editing_options {
3605 return false;
3606 }
3607 true
3608 }
3609}
3610
3611#[async_trait(?Send)]
3612impl LspCommand for LinkedEditingRange {
3613 type Response = Vec<Range<Anchor>>;
3614 type LspRequest = lsp::request::LinkedEditingRange;
3615 type ProtoRequest = proto::LinkedEditingRange;
3616
3617 fn display_name(&self) -> &str {
3618 "Linked editing range"
3619 }
3620
3621 fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool {
3622 Self::check_server_capabilities(capabilities.server_capabilities)
3623 }
3624
3625 fn to_lsp(
3626 &self,
3627 path: &Path,
3628 buffer: &Buffer,
3629 _server: &Arc<LanguageServer>,
3630 _: &App,
3631 ) -> Result<lsp::LinkedEditingRangeParams> {
3632 let position = self.position.to_point_utf16(&buffer.snapshot());
3633 Ok(lsp::LinkedEditingRangeParams {
3634 text_document_position_params: make_lsp_text_document_position(path, position)?,
3635 work_done_progress_params: Default::default(),
3636 })
3637 }
3638
3639 async fn response_from_lsp(
3640 self,
3641 message: Option<lsp::LinkedEditingRanges>,
3642 _: Entity<LspStore>,
3643 buffer: Entity<Buffer>,
3644 _server_id: LanguageServerId,
3645 cx: AsyncApp,
3646 ) -> Result<Vec<Range<Anchor>>> {
3647 if let Some(lsp::LinkedEditingRanges { mut ranges, .. }) = message {
3648 ranges.sort_by_key(|range| range.start);
3649
3650 buffer.read_with(&cx, |buffer, _| {
3651 ranges
3652 .into_iter()
3653 .map(|range| {
3654 let start =
3655 buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left);
3656 let end = buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left);
3657 buffer.anchor_before(start)..buffer.anchor_after(end)
3658 })
3659 .collect()
3660 })
3661 } else {
3662 Ok(vec![])
3663 }
3664 }
3665
3666 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::LinkedEditingRange {
3667 proto::LinkedEditingRange {
3668 project_id,
3669 buffer_id: buffer.remote_id().to_proto(),
3670 position: Some(serialize_anchor(&self.position)),
3671 version: serialize_version(&buffer.version()),
3672 }
3673 }
3674
3675 async fn from_proto(
3676 message: proto::LinkedEditingRange,
3677 _: Entity<LspStore>,
3678 buffer: Entity<Buffer>,
3679 mut cx: AsyncApp,
3680 ) -> Result<Self> {
3681 let position = message.position.context("invalid position")?;
3682 buffer
3683 .update(&mut cx, |buffer, _| {
3684 buffer.wait_for_version(deserialize_version(&message.version))
3685 })?
3686 .await?;
3687 let position = deserialize_anchor(position).context("invalid position")?;
3688 buffer
3689 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([position]))?
3690 .await?;
3691 Ok(Self { position })
3692 }
3693
3694 fn response_to_proto(
3695 response: Vec<Range<Anchor>>,
3696 _: &mut LspStore,
3697 _: PeerId,
3698 buffer_version: &clock::Global,
3699 _: &mut App,
3700 ) -> proto::LinkedEditingRangeResponse {
3701 proto::LinkedEditingRangeResponse {
3702 items: response
3703 .into_iter()
3704 .map(|range| proto::AnchorRange {
3705 start: Some(serialize_anchor(&range.start)),
3706 end: Some(serialize_anchor(&range.end)),
3707 })
3708 .collect(),
3709 version: serialize_version(buffer_version),
3710 }
3711 }
3712
3713 async fn response_from_proto(
3714 self,
3715 message: proto::LinkedEditingRangeResponse,
3716 _: Entity<LspStore>,
3717 buffer: Entity<Buffer>,
3718 mut cx: AsyncApp,
3719 ) -> Result<Vec<Range<Anchor>>> {
3720 buffer
3721 .update(&mut cx, |buffer, _| {
3722 buffer.wait_for_version(deserialize_version(&message.version))
3723 })?
3724 .await?;
3725 let items: Vec<Range<Anchor>> = message
3726 .items
3727 .into_iter()
3728 .filter_map(|range| {
3729 let start = deserialize_anchor(range.start?)?;
3730 let end = deserialize_anchor(range.end?)?;
3731 Some(start..end)
3732 })
3733 .collect();
3734 for range in &items {
3735 buffer
3736 .update(&mut cx, |buffer, _| {
3737 buffer.wait_for_anchors([range.start, range.end])
3738 })?
3739 .await?;
3740 }
3741 Ok(items)
3742 }
3743
3744 fn buffer_id_from_proto(message: &proto::LinkedEditingRange) -> Result<BufferId> {
3745 BufferId::new(message.buffer_id)
3746 }
3747}
3748
3749impl GetDocumentDiagnostics {
3750 pub fn diagnostics_from_proto(
3751 response: proto::GetDocumentDiagnosticsResponse,
3752 ) -> Vec<LspPullDiagnostics> {
3753 response
3754 .pulled_diagnostics
3755 .into_iter()
3756 .filter_map(|diagnostics| {
3757 Some(LspPullDiagnostics::Response {
3758 server_id: LanguageServerId::from_proto(diagnostics.server_id),
3759 uri: lsp::Uri::from_str(diagnostics.uri.as_str()).log_err()?,
3760 diagnostics: if diagnostics.changed {
3761 PulledDiagnostics::Unchanged {
3762 result_id: diagnostics.result_id?,
3763 }
3764 } else {
3765 PulledDiagnostics::Changed {
3766 result_id: diagnostics.result_id,
3767 diagnostics: diagnostics
3768 .diagnostics
3769 .into_iter()
3770 .filter_map(|diagnostic| {
3771 GetDocumentDiagnostics::deserialize_lsp_diagnostic(diagnostic)
3772 .context("deserializing diagnostics")
3773 .log_err()
3774 })
3775 .collect(),
3776 }
3777 },
3778 })
3779 })
3780 .collect()
3781 }
3782
3783 fn deserialize_lsp_diagnostic(diagnostic: proto::LspDiagnostic) -> Result<lsp::Diagnostic> {
3784 let start = diagnostic.start.context("invalid start range")?;
3785 let end = diagnostic.end.context("invalid end range")?;
3786
3787 let range = Range::<PointUtf16> {
3788 start: PointUtf16 {
3789 row: start.row,
3790 column: start.column,
3791 },
3792 end: PointUtf16 {
3793 row: end.row,
3794 column: end.column,
3795 },
3796 };
3797
3798 let data = diagnostic.data.and_then(|data| Value::from_str(&data).ok());
3799 let code = diagnostic.code.map(lsp::NumberOrString::String);
3800
3801 let related_information = diagnostic
3802 .related_information
3803 .into_iter()
3804 .map(|info| {
3805 let start = info.location_range_start.unwrap();
3806 let end = info.location_range_end.unwrap();
3807
3808 lsp::DiagnosticRelatedInformation {
3809 location: lsp::Location {
3810 range: lsp::Range {
3811 start: point_to_lsp(PointUtf16::new(start.row, start.column)),
3812 end: point_to_lsp(PointUtf16::new(end.row, end.column)),
3813 },
3814 uri: lsp::Uri::from_str(&info.location_url.unwrap()).unwrap(),
3815 },
3816 message: info.message,
3817 }
3818 })
3819 .collect::<Vec<_>>();
3820
3821 let tags = diagnostic
3822 .tags
3823 .into_iter()
3824 .filter_map(|tag| match proto::LspDiagnosticTag::from_i32(tag) {
3825 Some(proto::LspDiagnosticTag::Unnecessary) => Some(lsp::DiagnosticTag::UNNECESSARY),
3826 Some(proto::LspDiagnosticTag::Deprecated) => Some(lsp::DiagnosticTag::DEPRECATED),
3827 _ => None,
3828 })
3829 .collect::<Vec<_>>();
3830
3831 Ok(lsp::Diagnostic {
3832 range: language::range_to_lsp(range)?,
3833 severity: match proto::lsp_diagnostic::Severity::from_i32(diagnostic.severity).unwrap()
3834 {
3835 proto::lsp_diagnostic::Severity::Error => Some(lsp::DiagnosticSeverity::ERROR),
3836 proto::lsp_diagnostic::Severity::Warning => Some(lsp::DiagnosticSeverity::WARNING),
3837 proto::lsp_diagnostic::Severity::Information => {
3838 Some(lsp::DiagnosticSeverity::INFORMATION)
3839 }
3840 proto::lsp_diagnostic::Severity::Hint => Some(lsp::DiagnosticSeverity::HINT),
3841 _ => None,
3842 },
3843 code,
3844 code_description: diagnostic
3845 .code_description
3846 .map(|code_description| CodeDescription {
3847 href: Some(lsp::Uri::from_str(&code_description).unwrap()),
3848 }),
3849 related_information: Some(related_information),
3850 tags: Some(tags),
3851 source: diagnostic.source.clone(),
3852 message: diagnostic.message,
3853 data,
3854 })
3855 }
3856
3857 fn serialize_lsp_diagnostic(diagnostic: lsp::Diagnostic) -> Result<proto::LspDiagnostic> {
3858 let range = language::range_from_lsp(diagnostic.range);
3859 let related_information = diagnostic
3860 .related_information
3861 .unwrap_or_default()
3862 .into_iter()
3863 .map(|related_information| {
3864 let location_range_start =
3865 point_from_lsp(related_information.location.range.start).0;
3866 let location_range_end = point_from_lsp(related_information.location.range.end).0;
3867
3868 Ok(proto::LspDiagnosticRelatedInformation {
3869 location_url: Some(related_information.location.uri.to_string()),
3870 location_range_start: Some(proto::PointUtf16 {
3871 row: location_range_start.row,
3872 column: location_range_start.column,
3873 }),
3874 location_range_end: Some(proto::PointUtf16 {
3875 row: location_range_end.row,
3876 column: location_range_end.column,
3877 }),
3878 message: related_information.message,
3879 })
3880 })
3881 .collect::<Result<Vec<_>>>()?;
3882
3883 let tags = diagnostic
3884 .tags
3885 .unwrap_or_default()
3886 .into_iter()
3887 .map(|tag| match tag {
3888 lsp::DiagnosticTag::UNNECESSARY => proto::LspDiagnosticTag::Unnecessary,
3889 lsp::DiagnosticTag::DEPRECATED => proto::LspDiagnosticTag::Deprecated,
3890 _ => proto::LspDiagnosticTag::None,
3891 } as i32)
3892 .collect();
3893
3894 Ok(proto::LspDiagnostic {
3895 start: Some(proto::PointUtf16 {
3896 row: range.start.0.row,
3897 column: range.start.0.column,
3898 }),
3899 end: Some(proto::PointUtf16 {
3900 row: range.end.0.row,
3901 column: range.end.0.column,
3902 }),
3903 severity: match diagnostic.severity {
3904 Some(lsp::DiagnosticSeverity::ERROR) => proto::lsp_diagnostic::Severity::Error,
3905 Some(lsp::DiagnosticSeverity::WARNING) => proto::lsp_diagnostic::Severity::Warning,
3906 Some(lsp::DiagnosticSeverity::INFORMATION) => {
3907 proto::lsp_diagnostic::Severity::Information
3908 }
3909 Some(lsp::DiagnosticSeverity::HINT) => proto::lsp_diagnostic::Severity::Hint,
3910 _ => proto::lsp_diagnostic::Severity::None,
3911 } as i32,
3912 code: diagnostic.code.as_ref().map(|code| match code {
3913 lsp::NumberOrString::Number(code) => code.to_string(),
3914 lsp::NumberOrString::String(code) => code.clone(),
3915 }),
3916 source: diagnostic.source.clone(),
3917 related_information,
3918 tags,
3919 code_description: diagnostic
3920 .code_description
3921 .and_then(|desc| desc.href.map(|url| url.to_string())),
3922 message: diagnostic.message,
3923 data: diagnostic.data.as_ref().map(|data| data.to_string()),
3924 })
3925 }
3926
3927 pub fn deserialize_workspace_diagnostics_report(
3928 report: lsp::WorkspaceDiagnosticReportResult,
3929 server_id: LanguageServerId,
3930 ) -> Vec<WorkspaceLspPullDiagnostics> {
3931 let mut pulled_diagnostics = HashMap::default();
3932 match report {
3933 lsp::WorkspaceDiagnosticReportResult::Report(workspace_diagnostic_report) => {
3934 for report in workspace_diagnostic_report.items {
3935 match report {
3936 lsp::WorkspaceDocumentDiagnosticReport::Full(report) => {
3937 process_full_workspace_diagnostics_report(
3938 &mut pulled_diagnostics,
3939 server_id,
3940 report,
3941 )
3942 }
3943 lsp::WorkspaceDocumentDiagnosticReport::Unchanged(report) => {
3944 process_unchanged_workspace_diagnostics_report(
3945 &mut pulled_diagnostics,
3946 server_id,
3947 report,
3948 )
3949 }
3950 }
3951 }
3952 }
3953 lsp::WorkspaceDiagnosticReportResult::Partial(
3954 workspace_diagnostic_report_partial_result,
3955 ) => {
3956 for report in workspace_diagnostic_report_partial_result.items {
3957 match report {
3958 lsp::WorkspaceDocumentDiagnosticReport::Full(report) => {
3959 process_full_workspace_diagnostics_report(
3960 &mut pulled_diagnostics,
3961 server_id,
3962 report,
3963 )
3964 }
3965 lsp::WorkspaceDocumentDiagnosticReport::Unchanged(report) => {
3966 process_unchanged_workspace_diagnostics_report(
3967 &mut pulled_diagnostics,
3968 server_id,
3969 report,
3970 )
3971 }
3972 }
3973 }
3974 }
3975 }
3976 pulled_diagnostics.into_values().collect()
3977 }
3978}
3979
3980#[derive(Debug)]
3981pub struct WorkspaceLspPullDiagnostics {
3982 pub version: Option<i32>,
3983 pub diagnostics: LspPullDiagnostics,
3984}
3985
3986fn process_full_workspace_diagnostics_report(
3987 diagnostics: &mut HashMap<lsp::Uri, WorkspaceLspPullDiagnostics>,
3988 server_id: LanguageServerId,
3989 report: lsp::WorkspaceFullDocumentDiagnosticReport,
3990) {
3991 let mut new_diagnostics = HashMap::default();
3992 process_full_diagnostics_report(
3993 &mut new_diagnostics,
3994 server_id,
3995 report.uri,
3996 report.full_document_diagnostic_report,
3997 );
3998 diagnostics.extend(new_diagnostics.into_iter().map(|(uri, diagnostics)| {
3999 (
4000 uri,
4001 WorkspaceLspPullDiagnostics {
4002 version: report.version.map(|v| v as i32),
4003 diagnostics,
4004 },
4005 )
4006 }));
4007}
4008
4009fn process_unchanged_workspace_diagnostics_report(
4010 diagnostics: &mut HashMap<lsp::Uri, WorkspaceLspPullDiagnostics>,
4011 server_id: LanguageServerId,
4012 report: lsp::WorkspaceUnchangedDocumentDiagnosticReport,
4013) {
4014 let mut new_diagnostics = HashMap::default();
4015 process_unchanged_diagnostics_report(
4016 &mut new_diagnostics,
4017 server_id,
4018 report.uri,
4019 report.unchanged_document_diagnostic_report,
4020 );
4021 diagnostics.extend(new_diagnostics.into_iter().map(|(uri, diagnostics)| {
4022 (
4023 uri,
4024 WorkspaceLspPullDiagnostics {
4025 version: report.version.map(|v| v as i32),
4026 diagnostics,
4027 },
4028 )
4029 }));
4030}
4031
4032#[async_trait(?Send)]
4033impl LspCommand for GetDocumentDiagnostics {
4034 type Response = Vec<LspPullDiagnostics>;
4035 type LspRequest = lsp::request::DocumentDiagnosticRequest;
4036 type ProtoRequest = proto::GetDocumentDiagnostics;
4037
4038 fn display_name(&self) -> &str {
4039 "Get diagnostics"
4040 }
4041
4042 fn check_capabilities(&self, _: AdapterServerCapabilities) -> bool {
4043 true
4044 }
4045
4046 fn to_lsp(
4047 &self,
4048 path: &Path,
4049 _: &Buffer,
4050 _: &Arc<LanguageServer>,
4051 _: &App,
4052 ) -> Result<lsp::DocumentDiagnosticParams> {
4053 let identifier = match &self.dynamic_caps {
4054 lsp::DiagnosticServerCapabilities::Options(options) => options.identifier.clone(),
4055 lsp::DiagnosticServerCapabilities::RegistrationOptions(options) => {
4056 options.diagnostic_options.identifier.clone()
4057 }
4058 };
4059
4060 Ok(lsp::DocumentDiagnosticParams {
4061 text_document: lsp::TextDocumentIdentifier {
4062 uri: file_path_to_lsp_url(path)?,
4063 },
4064 identifier,
4065 previous_result_id: self.previous_result_id.clone(),
4066 partial_result_params: Default::default(),
4067 work_done_progress_params: Default::default(),
4068 })
4069 }
4070
4071 async fn response_from_lsp(
4072 self,
4073 message: lsp::DocumentDiagnosticReportResult,
4074 _: Entity<LspStore>,
4075 buffer: Entity<Buffer>,
4076 server_id: LanguageServerId,
4077 cx: AsyncApp,
4078 ) -> Result<Self::Response> {
4079 let url = buffer.read_with(&cx, |buffer, cx| {
4080 buffer
4081 .file()
4082 .and_then(|file| file.as_local())
4083 .map(|file| {
4084 let abs_path = file.abs_path(cx);
4085 file_path_to_lsp_url(&abs_path)
4086 })
4087 .transpose()?
4088 .with_context(|| format!("missing url on buffer {}", buffer.remote_id()))
4089 })??;
4090
4091 let mut pulled_diagnostics = HashMap::default();
4092 match message {
4093 lsp::DocumentDiagnosticReportResult::Report(report) => match report {
4094 lsp::DocumentDiagnosticReport::Full(report) => {
4095 if let Some(related_documents) = report.related_documents {
4096 process_related_documents(
4097 &mut pulled_diagnostics,
4098 server_id,
4099 related_documents,
4100 );
4101 }
4102 process_full_diagnostics_report(
4103 &mut pulled_diagnostics,
4104 server_id,
4105 url,
4106 report.full_document_diagnostic_report,
4107 );
4108 }
4109 lsp::DocumentDiagnosticReport::Unchanged(report) => {
4110 if let Some(related_documents) = report.related_documents {
4111 process_related_documents(
4112 &mut pulled_diagnostics,
4113 server_id,
4114 related_documents,
4115 );
4116 }
4117 process_unchanged_diagnostics_report(
4118 &mut pulled_diagnostics,
4119 server_id,
4120 url,
4121 report.unchanged_document_diagnostic_report,
4122 );
4123 }
4124 },
4125 lsp::DocumentDiagnosticReportResult::Partial(report) => {
4126 if let Some(related_documents) = report.related_documents {
4127 process_related_documents(
4128 &mut pulled_diagnostics,
4129 server_id,
4130 related_documents,
4131 );
4132 }
4133 }
4134 }
4135
4136 Ok(pulled_diagnostics.into_values().collect())
4137 }
4138
4139 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentDiagnostics {
4140 proto::GetDocumentDiagnostics {
4141 project_id,
4142 buffer_id: buffer.remote_id().into(),
4143 version: serialize_version(&buffer.version()),
4144 }
4145 }
4146
4147 async fn from_proto(
4148 _: proto::GetDocumentDiagnostics,
4149 _: Entity<LspStore>,
4150 _: Entity<Buffer>,
4151 _: AsyncApp,
4152 ) -> Result<Self> {
4153 anyhow::bail!(
4154 "proto::GetDocumentDiagnostics is not expected to be converted from proto directly, as it needs `previous_result_id` fetched first"
4155 )
4156 }
4157
4158 fn response_to_proto(
4159 response: Self::Response,
4160 _: &mut LspStore,
4161 _: PeerId,
4162 _: &clock::Global,
4163 _: &mut App,
4164 ) -> proto::GetDocumentDiagnosticsResponse {
4165 let pulled_diagnostics = response
4166 .into_iter()
4167 .filter_map(|diagnostics| match diagnostics {
4168 LspPullDiagnostics::Default => None,
4169 LspPullDiagnostics::Response {
4170 server_id,
4171 uri,
4172 diagnostics,
4173 } => {
4174 let mut changed = false;
4175 let (diagnostics, result_id) = match diagnostics {
4176 PulledDiagnostics::Unchanged { result_id } => (Vec::new(), Some(result_id)),
4177 PulledDiagnostics::Changed {
4178 result_id,
4179 diagnostics,
4180 } => {
4181 changed = true;
4182 (diagnostics, result_id)
4183 }
4184 };
4185 Some(proto::PulledDiagnostics {
4186 changed,
4187 result_id,
4188 uri: uri.to_string(),
4189 server_id: server_id.to_proto(),
4190 diagnostics: diagnostics
4191 .into_iter()
4192 .filter_map(|diagnostic| {
4193 GetDocumentDiagnostics::serialize_lsp_diagnostic(diagnostic)
4194 .context("serializing diagnostics")
4195 .log_err()
4196 })
4197 .collect(),
4198 })
4199 }
4200 })
4201 .collect();
4202
4203 proto::GetDocumentDiagnosticsResponse { pulled_diagnostics }
4204 }
4205
4206 async fn response_from_proto(
4207 self,
4208 response: proto::GetDocumentDiagnosticsResponse,
4209 _: Entity<LspStore>,
4210 _: Entity<Buffer>,
4211 _: AsyncApp,
4212 ) -> Result<Self::Response> {
4213 Ok(Self::diagnostics_from_proto(response))
4214 }
4215
4216 fn buffer_id_from_proto(message: &proto::GetDocumentDiagnostics) -> Result<BufferId> {
4217 BufferId::new(message.buffer_id)
4218 }
4219}
4220
4221#[async_trait(?Send)]
4222impl LspCommand for GetDocumentColor {
4223 type Response = Vec<DocumentColor>;
4224 type LspRequest = lsp::request::DocumentColor;
4225 type ProtoRequest = proto::GetDocumentColor;
4226
4227 fn display_name(&self) -> &str {
4228 "Document color"
4229 }
4230
4231 fn check_capabilities(&self, server_capabilities: AdapterServerCapabilities) -> bool {
4232 server_capabilities
4233 .server_capabilities
4234 .color_provider
4235 .as_ref()
4236 .is_some_and(|capability| match capability {
4237 lsp::ColorProviderCapability::Simple(supported) => *supported,
4238 lsp::ColorProviderCapability::ColorProvider(..) => true,
4239 lsp::ColorProviderCapability::Options(..) => true,
4240 })
4241 }
4242
4243 fn to_lsp(
4244 &self,
4245 path: &Path,
4246 _: &Buffer,
4247 _: &Arc<LanguageServer>,
4248 _: &App,
4249 ) -> Result<lsp::DocumentColorParams> {
4250 Ok(lsp::DocumentColorParams {
4251 text_document: make_text_document_identifier(path)?,
4252 work_done_progress_params: Default::default(),
4253 partial_result_params: Default::default(),
4254 })
4255 }
4256
4257 async fn response_from_lsp(
4258 self,
4259 message: Vec<lsp::ColorInformation>,
4260 _: Entity<LspStore>,
4261 _: Entity<Buffer>,
4262 _: LanguageServerId,
4263 _: AsyncApp,
4264 ) -> Result<Self::Response> {
4265 Ok(message
4266 .into_iter()
4267 .map(|color| DocumentColor {
4268 lsp_range: color.range,
4269 color: color.color,
4270 resolved: false,
4271 color_presentations: Vec::new(),
4272 })
4273 .collect())
4274 }
4275
4276 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
4277 proto::GetDocumentColor {
4278 project_id,
4279 buffer_id: buffer.remote_id().to_proto(),
4280 version: serialize_version(&buffer.version()),
4281 }
4282 }
4283
4284 async fn from_proto(
4285 _: Self::ProtoRequest,
4286 _: Entity<LspStore>,
4287 _: Entity<Buffer>,
4288 _: AsyncApp,
4289 ) -> Result<Self> {
4290 Ok(Self {})
4291 }
4292
4293 fn response_to_proto(
4294 response: Self::Response,
4295 _: &mut LspStore,
4296 _: PeerId,
4297 buffer_version: &clock::Global,
4298 _: &mut App,
4299 ) -> proto::GetDocumentColorResponse {
4300 proto::GetDocumentColorResponse {
4301 colors: response
4302 .into_iter()
4303 .map(|color| {
4304 let start = point_from_lsp(color.lsp_range.start).0;
4305 let end = point_from_lsp(color.lsp_range.end).0;
4306 proto::ColorInformation {
4307 red: color.color.red,
4308 green: color.color.green,
4309 blue: color.color.blue,
4310 alpha: color.color.alpha,
4311 lsp_range_start: Some(proto::PointUtf16 {
4312 row: start.row,
4313 column: start.column,
4314 }),
4315 lsp_range_end: Some(proto::PointUtf16 {
4316 row: end.row,
4317 column: end.column,
4318 }),
4319 }
4320 })
4321 .collect(),
4322 version: serialize_version(buffer_version),
4323 }
4324 }
4325
4326 async fn response_from_proto(
4327 self,
4328 message: proto::GetDocumentColorResponse,
4329 _: Entity<LspStore>,
4330 _: Entity<Buffer>,
4331 _: AsyncApp,
4332 ) -> Result<Self::Response> {
4333 Ok(message
4334 .colors
4335 .into_iter()
4336 .filter_map(|color| {
4337 let start = color.lsp_range_start?;
4338 let start = PointUtf16::new(start.row, start.column);
4339 let end = color.lsp_range_end?;
4340 let end = PointUtf16::new(end.row, end.column);
4341 Some(DocumentColor {
4342 resolved: false,
4343 color_presentations: Vec::new(),
4344 lsp_range: lsp::Range {
4345 start: point_to_lsp(start),
4346 end: point_to_lsp(end),
4347 },
4348 color: lsp::Color {
4349 red: color.red,
4350 green: color.green,
4351 blue: color.blue,
4352 alpha: color.alpha,
4353 },
4354 })
4355 })
4356 .collect())
4357 }
4358
4359 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> Result<BufferId> {
4360 BufferId::new(message.buffer_id)
4361 }
4362}
4363
4364fn process_related_documents(
4365 diagnostics: &mut HashMap<lsp::Uri, LspPullDiagnostics>,
4366 server_id: LanguageServerId,
4367 documents: impl IntoIterator<Item = (lsp::Uri, lsp::DocumentDiagnosticReportKind)>,
4368) {
4369 for (url, report_kind) in documents {
4370 match report_kind {
4371 lsp::DocumentDiagnosticReportKind::Full(report) => {
4372 process_full_diagnostics_report(diagnostics, server_id, url, report)
4373 }
4374 lsp::DocumentDiagnosticReportKind::Unchanged(report) => {
4375 process_unchanged_diagnostics_report(diagnostics, server_id, url, report)
4376 }
4377 }
4378 }
4379}
4380
4381fn process_unchanged_diagnostics_report(
4382 diagnostics: &mut HashMap<lsp::Uri, LspPullDiagnostics>,
4383 server_id: LanguageServerId,
4384 uri: lsp::Uri,
4385 report: lsp::UnchangedDocumentDiagnosticReport,
4386) {
4387 let result_id = report.result_id;
4388 match diagnostics.entry(uri.clone()) {
4389 hash_map::Entry::Occupied(mut o) => match o.get_mut() {
4390 LspPullDiagnostics::Default => {
4391 o.insert(LspPullDiagnostics::Response {
4392 server_id,
4393 uri,
4394 diagnostics: PulledDiagnostics::Unchanged { result_id },
4395 });
4396 }
4397 LspPullDiagnostics::Response {
4398 server_id: existing_server_id,
4399 uri: existing_uri,
4400 diagnostics: existing_diagnostics,
4401 } => {
4402 if server_id != *existing_server_id || &uri != existing_uri {
4403 debug_panic!(
4404 "Unexpected state: file {uri} has two different sets of diagnostics reported"
4405 );
4406 }
4407 match existing_diagnostics {
4408 PulledDiagnostics::Unchanged { .. } => {
4409 *existing_diagnostics = PulledDiagnostics::Unchanged { result_id };
4410 }
4411 PulledDiagnostics::Changed { .. } => {}
4412 }
4413 }
4414 },
4415 hash_map::Entry::Vacant(v) => {
4416 v.insert(LspPullDiagnostics::Response {
4417 server_id,
4418 uri,
4419 diagnostics: PulledDiagnostics::Unchanged { result_id },
4420 });
4421 }
4422 }
4423}
4424
4425fn process_full_diagnostics_report(
4426 diagnostics: &mut HashMap<lsp::Uri, LspPullDiagnostics>,
4427 server_id: LanguageServerId,
4428 uri: lsp::Uri,
4429 report: lsp::FullDocumentDiagnosticReport,
4430) {
4431 let result_id = report.result_id;
4432 match diagnostics.entry(uri.clone()) {
4433 hash_map::Entry::Occupied(mut o) => match o.get_mut() {
4434 LspPullDiagnostics::Default => {
4435 o.insert(LspPullDiagnostics::Response {
4436 server_id,
4437 uri,
4438 diagnostics: PulledDiagnostics::Changed {
4439 result_id,
4440 diagnostics: report.items,
4441 },
4442 });
4443 }
4444 LspPullDiagnostics::Response {
4445 server_id: existing_server_id,
4446 uri: existing_uri,
4447 diagnostics: existing_diagnostics,
4448 } => {
4449 if server_id != *existing_server_id || &uri != existing_uri {
4450 debug_panic!(
4451 "Unexpected state: file {uri} has two different sets of diagnostics reported"
4452 );
4453 }
4454 match existing_diagnostics {
4455 PulledDiagnostics::Unchanged { .. } => {
4456 *existing_diagnostics = PulledDiagnostics::Changed {
4457 result_id,
4458 diagnostics: report.items,
4459 };
4460 }
4461 PulledDiagnostics::Changed {
4462 result_id: existing_result_id,
4463 diagnostics: existing_diagnostics,
4464 } => {
4465 if result_id.is_some() {
4466 *existing_result_id = result_id;
4467 }
4468 existing_diagnostics.extend(report.items);
4469 }
4470 }
4471 }
4472 },
4473 hash_map::Entry::Vacant(v) => {
4474 v.insert(LspPullDiagnostics::Response {
4475 server_id,
4476 uri,
4477 diagnostics: PulledDiagnostics::Changed {
4478 result_id,
4479 diagnostics: report.items,
4480 },
4481 });
4482 }
4483 }
4484}
4485
4486#[cfg(test)]
4487mod tests {
4488 use super::*;
4489 use lsp::{DiagnosticSeverity, DiagnosticTag};
4490 use serde_json::json;
4491
4492 #[test]
4493 fn test_serialize_lsp_diagnostic() {
4494 let lsp_diagnostic = lsp::Diagnostic {
4495 range: lsp::Range {
4496 start: lsp::Position::new(0, 1),
4497 end: lsp::Position::new(2, 3),
4498 },
4499 severity: Some(DiagnosticSeverity::ERROR),
4500 code: Some(lsp::NumberOrString::String("E001".to_string())),
4501 source: Some("test-source".to_string()),
4502 message: "Test error message".to_string(),
4503 related_information: None,
4504 tags: Some(vec![DiagnosticTag::DEPRECATED]),
4505 code_description: None,
4506 data: Some(json!({"detail": "test detail"})),
4507 };
4508
4509 let proto_diagnostic = GetDocumentDiagnostics::serialize_lsp_diagnostic(lsp_diagnostic)
4510 .expect("Failed to serialize diagnostic");
4511
4512 let start = proto_diagnostic.start.unwrap();
4513 let end = proto_diagnostic.end.unwrap();
4514 assert_eq!(start.row, 0);
4515 assert_eq!(start.column, 1);
4516 assert_eq!(end.row, 2);
4517 assert_eq!(end.column, 3);
4518 assert_eq!(
4519 proto_diagnostic.severity,
4520 proto::lsp_diagnostic::Severity::Error as i32
4521 );
4522 assert_eq!(proto_diagnostic.code, Some("E001".to_string()));
4523 assert_eq!(proto_diagnostic.source, Some("test-source".to_string()));
4524 assert_eq!(proto_diagnostic.message, "Test error message");
4525 }
4526
4527 #[test]
4528 fn test_deserialize_lsp_diagnostic() {
4529 let proto_diagnostic = proto::LspDiagnostic {
4530 start: Some(proto::PointUtf16 { row: 0, column: 1 }),
4531 end: Some(proto::PointUtf16 { row: 2, column: 3 }),
4532 severity: proto::lsp_diagnostic::Severity::Warning as i32,
4533 code: Some("ERR".to_string()),
4534 source: Some("Prism".to_string()),
4535 message: "assigned but unused variable - a".to_string(),
4536 related_information: vec![],
4537 tags: vec![],
4538 code_description: None,
4539 data: None,
4540 };
4541
4542 let lsp_diagnostic = GetDocumentDiagnostics::deserialize_lsp_diagnostic(proto_diagnostic)
4543 .expect("Failed to deserialize diagnostic");
4544
4545 assert_eq!(lsp_diagnostic.range.start.line, 0);
4546 assert_eq!(lsp_diagnostic.range.start.character, 1);
4547 assert_eq!(lsp_diagnostic.range.end.line, 2);
4548 assert_eq!(lsp_diagnostic.range.end.character, 3);
4549 assert_eq!(lsp_diagnostic.severity, Some(DiagnosticSeverity::WARNING));
4550 assert_eq!(
4551 lsp_diagnostic.code,
4552 Some(lsp::NumberOrString::String("ERR".to_string()))
4553 );
4554 assert_eq!(lsp_diagnostic.source, Some("Prism".to_string()));
4555 assert_eq!(lsp_diagnostic.message, "assigned but unused variable - a");
4556 }
4557
4558 #[test]
4559 fn test_related_information() {
4560 let related_info = lsp::DiagnosticRelatedInformation {
4561 location: lsp::Location {
4562 uri: lsp::Uri::from_str("file:///test.rs").unwrap(),
4563 range: lsp::Range {
4564 start: lsp::Position::new(1, 1),
4565 end: lsp::Position::new(1, 5),
4566 },
4567 },
4568 message: "Related info message".to_string(),
4569 };
4570
4571 let lsp_diagnostic = lsp::Diagnostic {
4572 range: lsp::Range {
4573 start: lsp::Position::new(0, 0),
4574 end: lsp::Position::new(0, 1),
4575 },
4576 severity: Some(DiagnosticSeverity::INFORMATION),
4577 code: None,
4578 source: Some("Prism".to_string()),
4579 message: "assigned but unused variable - a".to_string(),
4580 related_information: Some(vec![related_info]),
4581 tags: None,
4582 code_description: None,
4583 data: None,
4584 };
4585
4586 let proto_diagnostic = GetDocumentDiagnostics::serialize_lsp_diagnostic(lsp_diagnostic)
4587 .expect("Failed to serialize diagnostic");
4588
4589 assert_eq!(proto_diagnostic.related_information.len(), 1);
4590 let related = &proto_diagnostic.related_information[0];
4591 assert_eq!(related.location_url, Some("file:///test.rs".to_string()));
4592 assert_eq!(related.message, "Related info message");
4593 }
4594
4595 #[test]
4596 fn test_invalid_ranges() {
4597 let proto_diagnostic = proto::LspDiagnostic {
4598 start: None,
4599 end: Some(proto::PointUtf16 { row: 2, column: 3 }),
4600 severity: proto::lsp_diagnostic::Severity::Error as i32,
4601 code: None,
4602 source: None,
4603 message: "Test message".to_string(),
4604 related_information: vec![],
4605 tags: vec![],
4606 code_description: None,
4607 data: None,
4608 };
4609
4610 let result = GetDocumentDiagnostics::deserialize_lsp_diagnostic(proto_diagnostic);
4611 assert!(result.is_err());
4612 }
4613}