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