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