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