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