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