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