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