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