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