1use crate::{
2 DocumentHighlight, Hover, HoverBlock, HoverBlockKind, InlayHint, InlayHintLabel,
3 InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Location, LocationLink,
4 MarkupContent, Project, ProjectTransaction,
5};
6use anyhow::{anyhow, Context, Result};
7use async_trait::async_trait;
8use client::proto::{self, PeerId};
9use fs::LineEnding;
10use gpui::{AppContext, AsyncAppContext, ModelHandle};
11use language::{
12 language_settings::{language_settings, InlayHintKind},
13 point_from_lsp, point_to_lsp,
14 proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version},
15 range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, CachedLspAdapter, CharKind, CodeAction,
16 Completion, OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Transaction, Unclipped,
17};
18use lsp::{DocumentHighlightKind, LanguageServer, LanguageServerId, ServerCapabilities};
19use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc};
20
21pub fn lsp_formatting_options(tab_size: u32) -> lsp::FormattingOptions {
22 lsp::FormattingOptions {
23 tab_size,
24 insert_spaces: true,
25 insert_final_newline: Some(true),
26 ..lsp::FormattingOptions::default()
27 }
28}
29
30#[async_trait(?Send)]
31pub(crate) trait LspCommand: 'static + Sized {
32 type Response: 'static + Default + Send;
33 type LspRequest: 'static + Send + lsp::request::Request;
34 type ProtoRequest: 'static + Send + proto::RequestMessage;
35
36 fn check_capabilities(&self, _: &lsp::ServerCapabilities) -> bool {
37 true
38 }
39
40 fn to_lsp(
41 &self,
42 path: &Path,
43 buffer: &Buffer,
44 language_server: &Arc<LanguageServer>,
45 cx: &AppContext,
46 ) -> <Self::LspRequest as lsp::request::Request>::Params;
47
48 async fn response_from_lsp(
49 self,
50 message: <Self::LspRequest as lsp::request::Request>::Result,
51 project: ModelHandle<Project>,
52 buffer: ModelHandle<Buffer>,
53 server_id: LanguageServerId,
54 cx: AsyncAppContext,
55 ) -> Result<Self::Response>;
56
57 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest;
58
59 async fn from_proto(
60 message: Self::ProtoRequest,
61 project: ModelHandle<Project>,
62 buffer: ModelHandle<Buffer>,
63 cx: AsyncAppContext,
64 ) -> Result<Self>;
65
66 fn response_to_proto(
67 response: Self::Response,
68 project: &mut Project,
69 peer_id: PeerId,
70 buffer_version: &clock::Global,
71 cx: &mut AppContext,
72 ) -> <Self::ProtoRequest as proto::RequestMessage>::Response;
73
74 async fn response_from_proto(
75 self,
76 message: <Self::ProtoRequest as proto::RequestMessage>::Response,
77 project: ModelHandle<Project>,
78 buffer: ModelHandle<Buffer>,
79 cx: AsyncAppContext,
80 ) -> Result<Self::Response>;
81
82 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64;
83}
84
85pub(crate) struct PrepareRename {
86 pub position: PointUtf16,
87}
88
89pub(crate) struct PerformRename {
90 pub position: PointUtf16,
91 pub new_name: String,
92 pub push_to_history: bool,
93}
94
95pub(crate) struct GetDefinition {
96 pub position: PointUtf16,
97}
98
99pub(crate) struct GetTypeDefinition {
100 pub position: PointUtf16,
101}
102
103pub(crate) struct GetReferences {
104 pub position: PointUtf16,
105}
106
107pub(crate) struct GetDocumentHighlights {
108 pub position: PointUtf16,
109}
110
111pub(crate) struct GetHover {
112 pub position: PointUtf16,
113}
114
115pub(crate) struct GetCompletions {
116 pub position: PointUtf16,
117}
118
119pub(crate) struct GetCodeActions {
120 pub range: Range<Anchor>,
121}
122
123pub(crate) struct OnTypeFormatting {
124 pub position: PointUtf16,
125 pub trigger: String,
126 pub options: FormattingOptions,
127 pub push_to_history: bool,
128}
129
130pub(crate) struct InlayHints {
131 pub range: Range<Anchor>,
132}
133
134pub(crate) struct FormattingOptions {
135 tab_size: u32,
136}
137
138impl From<lsp::FormattingOptions> for FormattingOptions {
139 fn from(value: lsp::FormattingOptions) -> Self {
140 Self {
141 tab_size: value.tab_size,
142 }
143 }
144}
145
146#[async_trait(?Send)]
147impl LspCommand for PrepareRename {
148 type Response = Option<Range<Anchor>>;
149 type LspRequest = lsp::request::PrepareRenameRequest;
150 type ProtoRequest = proto::PrepareRename;
151
152 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
153 if let Some(lsp::OneOf::Right(rename)) = &capabilities.rename_provider {
154 rename.prepare_provider == Some(true)
155 } else {
156 false
157 }
158 }
159
160 fn to_lsp(
161 &self,
162 path: &Path,
163 _: &Buffer,
164 _: &Arc<LanguageServer>,
165 _: &AppContext,
166 ) -> lsp::TextDocumentPositionParams {
167 lsp::TextDocumentPositionParams {
168 text_document: lsp::TextDocumentIdentifier {
169 uri: lsp::Url::from_file_path(path).unwrap(),
170 },
171 position: point_to_lsp(self.position),
172 }
173 }
174
175 async fn response_from_lsp(
176 self,
177 message: Option<lsp::PrepareRenameResponse>,
178 _: ModelHandle<Project>,
179 buffer: ModelHandle<Buffer>,
180 _: LanguageServerId,
181 cx: AsyncAppContext,
182 ) -> Result<Option<Range<Anchor>>> {
183 buffer.read_with(&cx, |buffer, _| {
184 if let Some(
185 lsp::PrepareRenameResponse::Range(range)
186 | lsp::PrepareRenameResponse::RangeWithPlaceholder { range, .. },
187 ) = message
188 {
189 let Range { start, end } = range_from_lsp(range);
190 if buffer.clip_point_utf16(start, Bias::Left) == start.0
191 && buffer.clip_point_utf16(end, Bias::Left) == end.0
192 {
193 return Ok(Some(buffer.anchor_after(start)..buffer.anchor_before(end)));
194 }
195 }
196 Ok(None)
197 })
198 }
199
200 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PrepareRename {
201 proto::PrepareRename {
202 project_id,
203 buffer_id: buffer.remote_id(),
204 position: Some(language::proto::serialize_anchor(
205 &buffer.anchor_before(self.position),
206 )),
207 version: serialize_version(&buffer.version()),
208 }
209 }
210
211 async fn from_proto(
212 message: proto::PrepareRename,
213 _: ModelHandle<Project>,
214 buffer: ModelHandle<Buffer>,
215 mut cx: AsyncAppContext,
216 ) -> Result<Self> {
217 let position = message
218 .position
219 .and_then(deserialize_anchor)
220 .ok_or_else(|| anyhow!("invalid position"))?;
221 buffer
222 .update(&mut cx, |buffer, _| {
223 buffer.wait_for_version(deserialize_version(&message.version))
224 })
225 .await?;
226
227 Ok(Self {
228 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
229 })
230 }
231
232 fn response_to_proto(
233 range: Option<Range<Anchor>>,
234 _: &mut Project,
235 _: PeerId,
236 buffer_version: &clock::Global,
237 _: &mut AppContext,
238 ) -> proto::PrepareRenameResponse {
239 proto::PrepareRenameResponse {
240 can_rename: range.is_some(),
241 start: range
242 .as_ref()
243 .map(|range| language::proto::serialize_anchor(&range.start)),
244 end: range
245 .as_ref()
246 .map(|range| language::proto::serialize_anchor(&range.end)),
247 version: serialize_version(buffer_version),
248 }
249 }
250
251 async fn response_from_proto(
252 self,
253 message: proto::PrepareRenameResponse,
254 _: ModelHandle<Project>,
255 buffer: ModelHandle<Buffer>,
256 mut cx: AsyncAppContext,
257 ) -> Result<Option<Range<Anchor>>> {
258 if message.can_rename {
259 buffer
260 .update(&mut cx, |buffer, _| {
261 buffer.wait_for_version(deserialize_version(&message.version))
262 })
263 .await?;
264 let start = message.start.and_then(deserialize_anchor);
265 let end = message.end.and_then(deserialize_anchor);
266 Ok(start.zip(end).map(|(start, end)| start..end))
267 } else {
268 Ok(None)
269 }
270 }
271
272 fn buffer_id_from_proto(message: &proto::PrepareRename) -> u64 {
273 message.buffer_id
274 }
275}
276
277#[async_trait(?Send)]
278impl LspCommand for PerformRename {
279 type Response = ProjectTransaction;
280 type LspRequest = lsp::request::Rename;
281 type ProtoRequest = proto::PerformRename;
282
283 fn to_lsp(
284 &self,
285 path: &Path,
286 _: &Buffer,
287 _: &Arc<LanguageServer>,
288 _: &AppContext,
289 ) -> lsp::RenameParams {
290 lsp::RenameParams {
291 text_document_position: lsp::TextDocumentPositionParams {
292 text_document: lsp::TextDocumentIdentifier {
293 uri: lsp::Url::from_file_path(path).unwrap(),
294 },
295 position: point_to_lsp(self.position),
296 },
297 new_name: self.new_name.clone(),
298 work_done_progress_params: Default::default(),
299 }
300 }
301
302 async fn response_from_lsp(
303 self,
304 message: Option<lsp::WorkspaceEdit>,
305 project: ModelHandle<Project>,
306 buffer: ModelHandle<Buffer>,
307 server_id: LanguageServerId,
308 mut cx: AsyncAppContext,
309 ) -> Result<ProjectTransaction> {
310 if let Some(edit) = message {
311 let (lsp_adapter, lsp_server) =
312 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
313 Project::deserialize_workspace_edit(
314 project,
315 edit,
316 self.push_to_history,
317 lsp_adapter,
318 lsp_server,
319 &mut cx,
320 )
321 .await
322 } else {
323 Ok(ProjectTransaction::default())
324 }
325 }
326
327 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PerformRename {
328 proto::PerformRename {
329 project_id,
330 buffer_id: buffer.remote_id(),
331 position: Some(language::proto::serialize_anchor(
332 &buffer.anchor_before(self.position),
333 )),
334 new_name: self.new_name.clone(),
335 version: serialize_version(&buffer.version()),
336 }
337 }
338
339 async fn from_proto(
340 message: proto::PerformRename,
341 _: ModelHandle<Project>,
342 buffer: ModelHandle<Buffer>,
343 mut cx: AsyncAppContext,
344 ) -> Result<Self> {
345 let position = message
346 .position
347 .and_then(deserialize_anchor)
348 .ok_or_else(|| anyhow!("invalid position"))?;
349 buffer
350 .update(&mut cx, |buffer, _| {
351 buffer.wait_for_version(deserialize_version(&message.version))
352 })
353 .await?;
354 Ok(Self {
355 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
356 new_name: message.new_name,
357 push_to_history: false,
358 })
359 }
360
361 fn response_to_proto(
362 response: ProjectTransaction,
363 project: &mut Project,
364 peer_id: PeerId,
365 _: &clock::Global,
366 cx: &mut AppContext,
367 ) -> proto::PerformRenameResponse {
368 let transaction = project.serialize_project_transaction_for_peer(response, peer_id, cx);
369 proto::PerformRenameResponse {
370 transaction: Some(transaction),
371 }
372 }
373
374 async fn response_from_proto(
375 self,
376 message: proto::PerformRenameResponse,
377 project: ModelHandle<Project>,
378 _: ModelHandle<Buffer>,
379 mut cx: AsyncAppContext,
380 ) -> Result<ProjectTransaction> {
381 let message = message
382 .transaction
383 .ok_or_else(|| anyhow!("missing transaction"))?;
384 project
385 .update(&mut cx, |project, cx| {
386 project.deserialize_project_transaction(message, self.push_to_history, cx)
387 })
388 .await
389 }
390
391 fn buffer_id_from_proto(message: &proto::PerformRename) -> u64 {
392 message.buffer_id
393 }
394}
395
396#[async_trait(?Send)]
397impl LspCommand for GetDefinition {
398 type Response = Vec<LocationLink>;
399 type LspRequest = lsp::request::GotoDefinition;
400 type ProtoRequest = proto::GetDefinition;
401
402 fn to_lsp(
403 &self,
404 path: &Path,
405 _: &Buffer,
406 _: &Arc<LanguageServer>,
407 _: &AppContext,
408 ) -> lsp::GotoDefinitionParams {
409 lsp::GotoDefinitionParams {
410 text_document_position_params: lsp::TextDocumentPositionParams {
411 text_document: lsp::TextDocumentIdentifier {
412 uri: lsp::Url::from_file_path(path).unwrap(),
413 },
414 position: point_to_lsp(self.position),
415 },
416 work_done_progress_params: Default::default(),
417 partial_result_params: Default::default(),
418 }
419 }
420
421 async fn response_from_lsp(
422 self,
423 message: Option<lsp::GotoDefinitionResponse>,
424 project: ModelHandle<Project>,
425 buffer: ModelHandle<Buffer>,
426 server_id: LanguageServerId,
427 cx: AsyncAppContext,
428 ) -> Result<Vec<LocationLink>> {
429 location_links_from_lsp(message, project, buffer, server_id, cx).await
430 }
431
432 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDefinition {
433 proto::GetDefinition {
434 project_id,
435 buffer_id: buffer.remote_id(),
436 position: Some(language::proto::serialize_anchor(
437 &buffer.anchor_before(self.position),
438 )),
439 version: serialize_version(&buffer.version()),
440 }
441 }
442
443 async fn from_proto(
444 message: proto::GetDefinition,
445 _: ModelHandle<Project>,
446 buffer: ModelHandle<Buffer>,
447 mut cx: AsyncAppContext,
448 ) -> Result<Self> {
449 let position = message
450 .position
451 .and_then(deserialize_anchor)
452 .ok_or_else(|| anyhow!("invalid position"))?;
453 buffer
454 .update(&mut cx, |buffer, _| {
455 buffer.wait_for_version(deserialize_version(&message.version))
456 })
457 .await?;
458 Ok(Self {
459 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
460 })
461 }
462
463 fn response_to_proto(
464 response: Vec<LocationLink>,
465 project: &mut Project,
466 peer_id: PeerId,
467 _: &clock::Global,
468 cx: &mut AppContext,
469 ) -> proto::GetDefinitionResponse {
470 let links = location_links_to_proto(response, project, peer_id, cx);
471 proto::GetDefinitionResponse { links }
472 }
473
474 async fn response_from_proto(
475 self,
476 message: proto::GetDefinitionResponse,
477 project: ModelHandle<Project>,
478 _: ModelHandle<Buffer>,
479 cx: AsyncAppContext,
480 ) -> Result<Vec<LocationLink>> {
481 location_links_from_proto(message.links, project, cx).await
482 }
483
484 fn buffer_id_from_proto(message: &proto::GetDefinition) -> u64 {
485 message.buffer_id
486 }
487}
488
489#[async_trait(?Send)]
490impl LspCommand for GetTypeDefinition {
491 type Response = Vec<LocationLink>;
492 type LspRequest = lsp::request::GotoTypeDefinition;
493 type ProtoRequest = proto::GetTypeDefinition;
494
495 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
496 match &capabilities.type_definition_provider {
497 None => false,
498 Some(lsp::TypeDefinitionProviderCapability::Simple(false)) => false,
499 _ => true,
500 }
501 }
502
503 fn to_lsp(
504 &self,
505 path: &Path,
506 _: &Buffer,
507 _: &Arc<LanguageServer>,
508 _: &AppContext,
509 ) -> lsp::GotoTypeDefinitionParams {
510 lsp::GotoTypeDefinitionParams {
511 text_document_position_params: lsp::TextDocumentPositionParams {
512 text_document: lsp::TextDocumentIdentifier {
513 uri: lsp::Url::from_file_path(path).unwrap(),
514 },
515 position: point_to_lsp(self.position),
516 },
517 work_done_progress_params: Default::default(),
518 partial_result_params: Default::default(),
519 }
520 }
521
522 async fn response_from_lsp(
523 self,
524 message: Option<lsp::GotoTypeDefinitionResponse>,
525 project: ModelHandle<Project>,
526 buffer: ModelHandle<Buffer>,
527 server_id: LanguageServerId,
528 cx: AsyncAppContext,
529 ) -> Result<Vec<LocationLink>> {
530 location_links_from_lsp(message, project, buffer, server_id, cx).await
531 }
532
533 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetTypeDefinition {
534 proto::GetTypeDefinition {
535 project_id,
536 buffer_id: buffer.remote_id(),
537 position: Some(language::proto::serialize_anchor(
538 &buffer.anchor_before(self.position),
539 )),
540 version: serialize_version(&buffer.version()),
541 }
542 }
543
544 async fn from_proto(
545 message: proto::GetTypeDefinition,
546 _: ModelHandle<Project>,
547 buffer: ModelHandle<Buffer>,
548 mut cx: AsyncAppContext,
549 ) -> Result<Self> {
550 let position = message
551 .position
552 .and_then(deserialize_anchor)
553 .ok_or_else(|| anyhow!("invalid position"))?;
554 buffer
555 .update(&mut cx, |buffer, _| {
556 buffer.wait_for_version(deserialize_version(&message.version))
557 })
558 .await?;
559 Ok(Self {
560 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
561 })
562 }
563
564 fn response_to_proto(
565 response: Vec<LocationLink>,
566 project: &mut Project,
567 peer_id: PeerId,
568 _: &clock::Global,
569 cx: &mut AppContext,
570 ) -> proto::GetTypeDefinitionResponse {
571 let links = location_links_to_proto(response, project, peer_id, cx);
572 proto::GetTypeDefinitionResponse { links }
573 }
574
575 async fn response_from_proto(
576 self,
577 message: proto::GetTypeDefinitionResponse,
578 project: ModelHandle<Project>,
579 _: ModelHandle<Buffer>,
580 cx: AsyncAppContext,
581 ) -> Result<Vec<LocationLink>> {
582 location_links_from_proto(message.links, project, cx).await
583 }
584
585 fn buffer_id_from_proto(message: &proto::GetTypeDefinition) -> u64 {
586 message.buffer_id
587 }
588}
589
590fn language_server_for_buffer(
591 project: &ModelHandle<Project>,
592 buffer: &ModelHandle<Buffer>,
593 server_id: LanguageServerId,
594 cx: &mut AsyncAppContext,
595) -> Result<(Arc<CachedLspAdapter>, Arc<LanguageServer>)> {
596 project
597 .read_with(cx, |project, cx| {
598 project
599 .language_server_for_buffer(buffer.read(cx), server_id, cx)
600 .map(|(adapter, server)| (adapter.clone(), server.clone()))
601 })
602 .ok_or_else(|| anyhow!("no language server found for buffer"))
603}
604
605async fn location_links_from_proto(
606 proto_links: Vec<proto::LocationLink>,
607 project: ModelHandle<Project>,
608 mut cx: AsyncAppContext,
609) -> Result<Vec<LocationLink>> {
610 let mut links = Vec::new();
611
612 for link in proto_links {
613 let origin = match link.origin {
614 Some(origin) => {
615 let buffer = project
616 .update(&mut cx, |this, cx| {
617 this.wait_for_remote_buffer(origin.buffer_id, cx)
618 })
619 .await?;
620 let start = origin
621 .start
622 .and_then(deserialize_anchor)
623 .ok_or_else(|| anyhow!("missing origin start"))?;
624 let end = origin
625 .end
626 .and_then(deserialize_anchor)
627 .ok_or_else(|| anyhow!("missing origin end"))?;
628 buffer
629 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))
630 .await?;
631 Some(Location {
632 buffer,
633 range: start..end,
634 })
635 }
636 None => None,
637 };
638
639 let target = link.target.ok_or_else(|| anyhow!("missing target"))?;
640 let buffer = project
641 .update(&mut cx, |this, cx| {
642 this.wait_for_remote_buffer(target.buffer_id, cx)
643 })
644 .await?;
645 let start = target
646 .start
647 .and_then(deserialize_anchor)
648 .ok_or_else(|| anyhow!("missing target start"))?;
649 let end = target
650 .end
651 .and_then(deserialize_anchor)
652 .ok_or_else(|| anyhow!("missing target end"))?;
653 buffer
654 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))
655 .await?;
656 let target = Location {
657 buffer,
658 range: start..end,
659 };
660
661 links.push(LocationLink { origin, target })
662 }
663
664 Ok(links)
665}
666
667async fn location_links_from_lsp(
668 message: Option<lsp::GotoDefinitionResponse>,
669 project: ModelHandle<Project>,
670 buffer: ModelHandle<Buffer>,
671 server_id: LanguageServerId,
672 mut cx: AsyncAppContext,
673) -> Result<Vec<LocationLink>> {
674 let message = match message {
675 Some(message) => message,
676 None => return Ok(Vec::new()),
677 };
678
679 let mut unresolved_links = Vec::new();
680 match message {
681 lsp::GotoDefinitionResponse::Scalar(loc) => {
682 unresolved_links.push((None, loc.uri, loc.range));
683 }
684
685 lsp::GotoDefinitionResponse::Array(locs) => {
686 unresolved_links.extend(locs.into_iter().map(|l| (None, l.uri, l.range)));
687 }
688
689 lsp::GotoDefinitionResponse::Link(links) => {
690 unresolved_links.extend(links.into_iter().map(|l| {
691 (
692 l.origin_selection_range,
693 l.target_uri,
694 l.target_selection_range,
695 )
696 }));
697 }
698 }
699
700 let (lsp_adapter, language_server) =
701 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
702 let mut definitions = Vec::new();
703 for (origin_range, target_uri, target_range) in unresolved_links {
704 let target_buffer_handle = project
705 .update(&mut cx, |this, cx| {
706 this.open_local_buffer_via_lsp(
707 target_uri,
708 language_server.server_id(),
709 lsp_adapter.name.clone(),
710 cx,
711 )
712 })
713 .await?;
714
715 cx.read(|cx| {
716 let origin_location = origin_range.map(|origin_range| {
717 let origin_buffer = buffer.read(cx);
718 let origin_start =
719 origin_buffer.clip_point_utf16(point_from_lsp(origin_range.start), Bias::Left);
720 let origin_end =
721 origin_buffer.clip_point_utf16(point_from_lsp(origin_range.end), Bias::Left);
722 Location {
723 buffer: buffer.clone(),
724 range: origin_buffer.anchor_after(origin_start)
725 ..origin_buffer.anchor_before(origin_end),
726 }
727 });
728
729 let target_buffer = target_buffer_handle.read(cx);
730 let target_start =
731 target_buffer.clip_point_utf16(point_from_lsp(target_range.start), Bias::Left);
732 let target_end =
733 target_buffer.clip_point_utf16(point_from_lsp(target_range.end), Bias::Left);
734 let target_location = Location {
735 buffer: target_buffer_handle,
736 range: target_buffer.anchor_after(target_start)
737 ..target_buffer.anchor_before(target_end),
738 };
739
740 definitions.push(LocationLink {
741 origin: origin_location,
742 target: target_location,
743 })
744 });
745 }
746 Ok(definitions)
747}
748
749fn location_links_to_proto(
750 links: Vec<LocationLink>,
751 project: &mut Project,
752 peer_id: PeerId,
753 cx: &mut AppContext,
754) -> Vec<proto::LocationLink> {
755 links
756 .into_iter()
757 .map(|definition| {
758 let origin = definition.origin.map(|origin| {
759 let buffer_id = project.create_buffer_for_peer(&origin.buffer, peer_id, cx);
760 proto::Location {
761 start: Some(serialize_anchor(&origin.range.start)),
762 end: Some(serialize_anchor(&origin.range.end)),
763 buffer_id,
764 }
765 });
766
767 let buffer_id = project.create_buffer_for_peer(&definition.target.buffer, peer_id, cx);
768 let target = proto::Location {
769 start: Some(serialize_anchor(&definition.target.range.start)),
770 end: Some(serialize_anchor(&definition.target.range.end)),
771 buffer_id,
772 };
773
774 proto::LocationLink {
775 origin,
776 target: Some(target),
777 }
778 })
779 .collect()
780}
781
782#[async_trait(?Send)]
783impl LspCommand for GetReferences {
784 type Response = Vec<Location>;
785 type LspRequest = lsp::request::References;
786 type ProtoRequest = proto::GetReferences;
787
788 fn to_lsp(
789 &self,
790 path: &Path,
791 _: &Buffer,
792 _: &Arc<LanguageServer>,
793 _: &AppContext,
794 ) -> lsp::ReferenceParams {
795 lsp::ReferenceParams {
796 text_document_position: lsp::TextDocumentPositionParams {
797 text_document: lsp::TextDocumentIdentifier {
798 uri: lsp::Url::from_file_path(path).unwrap(),
799 },
800 position: point_to_lsp(self.position),
801 },
802 work_done_progress_params: Default::default(),
803 partial_result_params: Default::default(),
804 context: lsp::ReferenceContext {
805 include_declaration: true,
806 },
807 }
808 }
809
810 async fn response_from_lsp(
811 self,
812 locations: Option<Vec<lsp::Location>>,
813 project: ModelHandle<Project>,
814 buffer: ModelHandle<Buffer>,
815 server_id: LanguageServerId,
816 mut cx: AsyncAppContext,
817 ) -> Result<Vec<Location>> {
818 let mut references = Vec::new();
819 let (lsp_adapter, language_server) =
820 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
821
822 if let Some(locations) = locations {
823 for lsp_location in locations {
824 let target_buffer_handle = project
825 .update(&mut cx, |this, cx| {
826 this.open_local_buffer_via_lsp(
827 lsp_location.uri,
828 language_server.server_id(),
829 lsp_adapter.name.clone(),
830 cx,
831 )
832 })
833 .await?;
834
835 cx.read(|cx| {
836 let target_buffer = target_buffer_handle.read(cx);
837 let target_start = target_buffer
838 .clip_point_utf16(point_from_lsp(lsp_location.range.start), Bias::Left);
839 let target_end = target_buffer
840 .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left);
841 references.push(Location {
842 buffer: target_buffer_handle,
843 range: target_buffer.anchor_after(target_start)
844 ..target_buffer.anchor_before(target_end),
845 });
846 });
847 }
848 }
849
850 Ok(references)
851 }
852
853 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetReferences {
854 proto::GetReferences {
855 project_id,
856 buffer_id: buffer.remote_id(),
857 position: Some(language::proto::serialize_anchor(
858 &buffer.anchor_before(self.position),
859 )),
860 version: serialize_version(&buffer.version()),
861 }
862 }
863
864 async fn from_proto(
865 message: proto::GetReferences,
866 _: ModelHandle<Project>,
867 buffer: ModelHandle<Buffer>,
868 mut cx: AsyncAppContext,
869 ) -> Result<Self> {
870 let position = message
871 .position
872 .and_then(deserialize_anchor)
873 .ok_or_else(|| anyhow!("invalid position"))?;
874 buffer
875 .update(&mut cx, |buffer, _| {
876 buffer.wait_for_version(deserialize_version(&message.version))
877 })
878 .await?;
879 Ok(Self {
880 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
881 })
882 }
883
884 fn response_to_proto(
885 response: Vec<Location>,
886 project: &mut Project,
887 peer_id: PeerId,
888 _: &clock::Global,
889 cx: &mut AppContext,
890 ) -> proto::GetReferencesResponse {
891 let locations = response
892 .into_iter()
893 .map(|definition| {
894 let buffer_id = project.create_buffer_for_peer(&definition.buffer, peer_id, cx);
895 proto::Location {
896 start: Some(serialize_anchor(&definition.range.start)),
897 end: Some(serialize_anchor(&definition.range.end)),
898 buffer_id,
899 }
900 })
901 .collect();
902 proto::GetReferencesResponse { locations }
903 }
904
905 async fn response_from_proto(
906 self,
907 message: proto::GetReferencesResponse,
908 project: ModelHandle<Project>,
909 _: ModelHandle<Buffer>,
910 mut cx: AsyncAppContext,
911 ) -> Result<Vec<Location>> {
912 let mut locations = Vec::new();
913 for location in message.locations {
914 let target_buffer = project
915 .update(&mut cx, |this, cx| {
916 this.wait_for_remote_buffer(location.buffer_id, cx)
917 })
918 .await?;
919 let start = location
920 .start
921 .and_then(deserialize_anchor)
922 .ok_or_else(|| anyhow!("missing target start"))?;
923 let end = location
924 .end
925 .and_then(deserialize_anchor)
926 .ok_or_else(|| anyhow!("missing target end"))?;
927 target_buffer
928 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))
929 .await?;
930 locations.push(Location {
931 buffer: target_buffer,
932 range: start..end,
933 })
934 }
935 Ok(locations)
936 }
937
938 fn buffer_id_from_proto(message: &proto::GetReferences) -> u64 {
939 message.buffer_id
940 }
941}
942
943#[async_trait(?Send)]
944impl LspCommand for GetDocumentHighlights {
945 type Response = Vec<DocumentHighlight>;
946 type LspRequest = lsp::request::DocumentHighlightRequest;
947 type ProtoRequest = proto::GetDocumentHighlights;
948
949 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
950 capabilities.document_highlight_provider.is_some()
951 }
952
953 fn to_lsp(
954 &self,
955 path: &Path,
956 _: &Buffer,
957 _: &Arc<LanguageServer>,
958 _: &AppContext,
959 ) -> lsp::DocumentHighlightParams {
960 lsp::DocumentHighlightParams {
961 text_document_position_params: lsp::TextDocumentPositionParams {
962 text_document: lsp::TextDocumentIdentifier {
963 uri: lsp::Url::from_file_path(path).unwrap(),
964 },
965 position: point_to_lsp(self.position),
966 },
967 work_done_progress_params: Default::default(),
968 partial_result_params: Default::default(),
969 }
970 }
971
972 async fn response_from_lsp(
973 self,
974 lsp_highlights: Option<Vec<lsp::DocumentHighlight>>,
975 _: ModelHandle<Project>,
976 buffer: ModelHandle<Buffer>,
977 _: LanguageServerId,
978 cx: AsyncAppContext,
979 ) -> Result<Vec<DocumentHighlight>> {
980 buffer.read_with(&cx, |buffer, _| {
981 let mut lsp_highlights = lsp_highlights.unwrap_or_default();
982 lsp_highlights.sort_unstable_by_key(|h| (h.range.start, Reverse(h.range.end)));
983 Ok(lsp_highlights
984 .into_iter()
985 .map(|lsp_highlight| {
986 let start = buffer
987 .clip_point_utf16(point_from_lsp(lsp_highlight.range.start), Bias::Left);
988 let end = buffer
989 .clip_point_utf16(point_from_lsp(lsp_highlight.range.end), Bias::Left);
990 DocumentHighlight {
991 range: buffer.anchor_after(start)..buffer.anchor_before(end),
992 kind: lsp_highlight
993 .kind
994 .unwrap_or(lsp::DocumentHighlightKind::READ),
995 }
996 })
997 .collect())
998 })
999 }
1000
1001 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentHighlights {
1002 proto::GetDocumentHighlights {
1003 project_id,
1004 buffer_id: buffer.remote_id(),
1005 position: Some(language::proto::serialize_anchor(
1006 &buffer.anchor_before(self.position),
1007 )),
1008 version: serialize_version(&buffer.version()),
1009 }
1010 }
1011
1012 async fn from_proto(
1013 message: proto::GetDocumentHighlights,
1014 _: ModelHandle<Project>,
1015 buffer: ModelHandle<Buffer>,
1016 mut cx: AsyncAppContext,
1017 ) -> Result<Self> {
1018 let position = message
1019 .position
1020 .and_then(deserialize_anchor)
1021 .ok_or_else(|| anyhow!("invalid position"))?;
1022 buffer
1023 .update(&mut cx, |buffer, _| {
1024 buffer.wait_for_version(deserialize_version(&message.version))
1025 })
1026 .await?;
1027 Ok(Self {
1028 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
1029 })
1030 }
1031
1032 fn response_to_proto(
1033 response: Vec<DocumentHighlight>,
1034 _: &mut Project,
1035 _: PeerId,
1036 _: &clock::Global,
1037 _: &mut AppContext,
1038 ) -> proto::GetDocumentHighlightsResponse {
1039 let highlights = response
1040 .into_iter()
1041 .map(|highlight| proto::DocumentHighlight {
1042 start: Some(serialize_anchor(&highlight.range.start)),
1043 end: Some(serialize_anchor(&highlight.range.end)),
1044 kind: match highlight.kind {
1045 DocumentHighlightKind::TEXT => proto::document_highlight::Kind::Text.into(),
1046 DocumentHighlightKind::WRITE => proto::document_highlight::Kind::Write.into(),
1047 DocumentHighlightKind::READ => proto::document_highlight::Kind::Read.into(),
1048 _ => proto::document_highlight::Kind::Text.into(),
1049 },
1050 })
1051 .collect();
1052 proto::GetDocumentHighlightsResponse { highlights }
1053 }
1054
1055 async fn response_from_proto(
1056 self,
1057 message: proto::GetDocumentHighlightsResponse,
1058 _: ModelHandle<Project>,
1059 buffer: ModelHandle<Buffer>,
1060 mut cx: AsyncAppContext,
1061 ) -> Result<Vec<DocumentHighlight>> {
1062 let mut highlights = Vec::new();
1063 for highlight in message.highlights {
1064 let start = highlight
1065 .start
1066 .and_then(deserialize_anchor)
1067 .ok_or_else(|| anyhow!("missing target start"))?;
1068 let end = highlight
1069 .end
1070 .and_then(deserialize_anchor)
1071 .ok_or_else(|| anyhow!("missing target end"))?;
1072 buffer
1073 .update(&mut cx, |buffer, _| buffer.wait_for_anchors([start, end]))
1074 .await?;
1075 let kind = match proto::document_highlight::Kind::from_i32(highlight.kind) {
1076 Some(proto::document_highlight::Kind::Text) => DocumentHighlightKind::TEXT,
1077 Some(proto::document_highlight::Kind::Read) => DocumentHighlightKind::READ,
1078 Some(proto::document_highlight::Kind::Write) => DocumentHighlightKind::WRITE,
1079 None => DocumentHighlightKind::TEXT,
1080 };
1081 highlights.push(DocumentHighlight {
1082 range: start..end,
1083 kind,
1084 });
1085 }
1086 Ok(highlights)
1087 }
1088
1089 fn buffer_id_from_proto(message: &proto::GetDocumentHighlights) -> u64 {
1090 message.buffer_id
1091 }
1092}
1093
1094#[async_trait(?Send)]
1095impl LspCommand for GetHover {
1096 type Response = Option<Hover>;
1097 type LspRequest = lsp::request::HoverRequest;
1098 type ProtoRequest = proto::GetHover;
1099
1100 fn to_lsp(
1101 &self,
1102 path: &Path,
1103 _: &Buffer,
1104 _: &Arc<LanguageServer>,
1105 _: &AppContext,
1106 ) -> lsp::HoverParams {
1107 lsp::HoverParams {
1108 text_document_position_params: lsp::TextDocumentPositionParams {
1109 text_document: lsp::TextDocumentIdentifier {
1110 uri: lsp::Url::from_file_path(path).unwrap(),
1111 },
1112 position: point_to_lsp(self.position),
1113 },
1114 work_done_progress_params: Default::default(),
1115 }
1116 }
1117
1118 async fn response_from_lsp(
1119 self,
1120 message: Option<lsp::Hover>,
1121 _: ModelHandle<Project>,
1122 buffer: ModelHandle<Buffer>,
1123 _: LanguageServerId,
1124 cx: AsyncAppContext,
1125 ) -> Result<Self::Response> {
1126 Ok(message.and_then(|hover| {
1127 let (language, range) = cx.read(|cx| {
1128 let buffer = buffer.read(cx);
1129 (
1130 buffer.language().cloned(),
1131 hover.range.map(|range| {
1132 let token_start =
1133 buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left);
1134 let token_end =
1135 buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left);
1136 buffer.anchor_after(token_start)..buffer.anchor_before(token_end)
1137 }),
1138 )
1139 });
1140
1141 fn hover_blocks_from_marked_string(
1142 marked_string: lsp::MarkedString,
1143 ) -> Option<HoverBlock> {
1144 let block = match marked_string {
1145 lsp::MarkedString::String(content) => HoverBlock {
1146 text: content,
1147 kind: HoverBlockKind::Markdown,
1148 },
1149 lsp::MarkedString::LanguageString(lsp::LanguageString { language, value }) => {
1150 HoverBlock {
1151 text: value,
1152 kind: HoverBlockKind::Code { language },
1153 }
1154 }
1155 };
1156 if block.text.is_empty() {
1157 None
1158 } else {
1159 Some(block)
1160 }
1161 }
1162
1163 let contents = cx.read(|_| match hover.contents {
1164 lsp::HoverContents::Scalar(marked_string) => {
1165 hover_blocks_from_marked_string(marked_string)
1166 .into_iter()
1167 .collect()
1168 }
1169 lsp::HoverContents::Array(marked_strings) => marked_strings
1170 .into_iter()
1171 .filter_map(hover_blocks_from_marked_string)
1172 .collect(),
1173 lsp::HoverContents::Markup(markup_content) => vec![HoverBlock {
1174 text: markup_content.value,
1175 kind: if markup_content.kind == lsp::MarkupKind::Markdown {
1176 HoverBlockKind::Markdown
1177 } else {
1178 HoverBlockKind::PlainText
1179 },
1180 }],
1181 });
1182
1183 Some(Hover {
1184 contents,
1185 range,
1186 language,
1187 })
1188 }))
1189 }
1190
1191 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest {
1192 proto::GetHover {
1193 project_id,
1194 buffer_id: buffer.remote_id(),
1195 position: Some(language::proto::serialize_anchor(
1196 &buffer.anchor_before(self.position),
1197 )),
1198 version: serialize_version(&buffer.version),
1199 }
1200 }
1201
1202 async fn from_proto(
1203 message: Self::ProtoRequest,
1204 _: ModelHandle<Project>,
1205 buffer: ModelHandle<Buffer>,
1206 mut cx: AsyncAppContext,
1207 ) -> Result<Self> {
1208 let position = message
1209 .position
1210 .and_then(deserialize_anchor)
1211 .ok_or_else(|| anyhow!("invalid position"))?;
1212 buffer
1213 .update(&mut cx, |buffer, _| {
1214 buffer.wait_for_version(deserialize_version(&message.version))
1215 })
1216 .await?;
1217 Ok(Self {
1218 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
1219 })
1220 }
1221
1222 fn response_to_proto(
1223 response: Self::Response,
1224 _: &mut Project,
1225 _: PeerId,
1226 _: &clock::Global,
1227 _: &mut AppContext,
1228 ) -> proto::GetHoverResponse {
1229 if let Some(response) = response {
1230 let (start, end) = if let Some(range) = response.range {
1231 (
1232 Some(language::proto::serialize_anchor(&range.start)),
1233 Some(language::proto::serialize_anchor(&range.end)),
1234 )
1235 } else {
1236 (None, None)
1237 };
1238
1239 let contents = response
1240 .contents
1241 .into_iter()
1242 .map(|block| proto::HoverBlock {
1243 text: block.text,
1244 is_markdown: block.kind == HoverBlockKind::Markdown,
1245 language: if let HoverBlockKind::Code { language } = block.kind {
1246 Some(language)
1247 } else {
1248 None
1249 },
1250 })
1251 .collect();
1252
1253 proto::GetHoverResponse {
1254 start,
1255 end,
1256 contents,
1257 }
1258 } else {
1259 proto::GetHoverResponse {
1260 start: None,
1261 end: None,
1262 contents: Vec::new(),
1263 }
1264 }
1265 }
1266
1267 async fn response_from_proto(
1268 self,
1269 message: proto::GetHoverResponse,
1270 _: ModelHandle<Project>,
1271 buffer: ModelHandle<Buffer>,
1272 cx: AsyncAppContext,
1273 ) -> Result<Self::Response> {
1274 let contents: Vec<_> = message
1275 .contents
1276 .into_iter()
1277 .map(|block| HoverBlock {
1278 text: block.text,
1279 kind: if let Some(language) = block.language {
1280 HoverBlockKind::Code { language }
1281 } else if block.is_markdown {
1282 HoverBlockKind::Markdown
1283 } else {
1284 HoverBlockKind::PlainText
1285 },
1286 })
1287 .collect();
1288 if contents.is_empty() {
1289 return Ok(None);
1290 }
1291
1292 let language = buffer.read_with(&cx, |buffer, _| buffer.language().cloned());
1293 let range = if let (Some(start), Some(end)) = (message.start, message.end) {
1294 language::proto::deserialize_anchor(start)
1295 .and_then(|start| language::proto::deserialize_anchor(end).map(|end| start..end))
1296 } else {
1297 None
1298 };
1299
1300 Ok(Some(Hover {
1301 contents,
1302 range,
1303 language,
1304 }))
1305 }
1306
1307 fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64 {
1308 message.buffer_id
1309 }
1310}
1311
1312#[async_trait(?Send)]
1313impl LspCommand for GetCompletions {
1314 type Response = Vec<Completion>;
1315 type LspRequest = lsp::request::Completion;
1316 type ProtoRequest = proto::GetCompletions;
1317
1318 fn to_lsp(
1319 &self,
1320 path: &Path,
1321 _: &Buffer,
1322 _: &Arc<LanguageServer>,
1323 _: &AppContext,
1324 ) -> lsp::CompletionParams {
1325 lsp::CompletionParams {
1326 text_document_position: lsp::TextDocumentPositionParams::new(
1327 lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
1328 point_to_lsp(self.position),
1329 ),
1330 context: Default::default(),
1331 work_done_progress_params: Default::default(),
1332 partial_result_params: Default::default(),
1333 }
1334 }
1335
1336 async fn response_from_lsp(
1337 self,
1338 completions: Option<lsp::CompletionResponse>,
1339 _: ModelHandle<Project>,
1340 buffer: ModelHandle<Buffer>,
1341 server_id: LanguageServerId,
1342 cx: AsyncAppContext,
1343 ) -> Result<Vec<Completion>> {
1344 let completions = if let Some(completions) = completions {
1345 match completions {
1346 lsp::CompletionResponse::Array(completions) => completions,
1347 lsp::CompletionResponse::List(list) => list.items,
1348 }
1349 } else {
1350 Default::default()
1351 };
1352
1353 let completions = buffer.read_with(&cx, |buffer, _| {
1354 let language = buffer.language().cloned();
1355 let snapshot = buffer.snapshot();
1356 let clipped_position = buffer.clip_point_utf16(Unclipped(self.position), Bias::Left);
1357 let mut range_for_token = None;
1358 completions
1359 .into_iter()
1360 .filter_map(move |mut lsp_completion| {
1361 let (old_range, mut new_text) = match lsp_completion.text_edit.as_ref() {
1362 // If the language server provides a range to overwrite, then
1363 // check that the range is valid.
1364 Some(lsp::CompletionTextEdit::Edit(edit)) => {
1365 let range = range_from_lsp(edit.range);
1366 let start = snapshot.clip_point_utf16(range.start, Bias::Left);
1367 let end = snapshot.clip_point_utf16(range.end, Bias::Left);
1368 if start != range.start.0 || end != range.end.0 {
1369 log::info!("completion out of expected range");
1370 return None;
1371 }
1372 (
1373 snapshot.anchor_before(start)..snapshot.anchor_after(end),
1374 edit.new_text.clone(),
1375 )
1376 }
1377 // If the language server does not provide a range, then infer
1378 // the range based on the syntax tree.
1379 None => {
1380 if self.position != clipped_position {
1381 log::info!("completion out of expected range");
1382 return None;
1383 }
1384 let Range { start, end } = range_for_token
1385 .get_or_insert_with(|| {
1386 let offset = self.position.to_offset(&snapshot);
1387 let (range, kind) = snapshot.surrounding_word(offset);
1388 if kind == Some(CharKind::Word) {
1389 range
1390 } else {
1391 offset..offset
1392 }
1393 })
1394 .clone();
1395 let text = lsp_completion
1396 .insert_text
1397 .as_ref()
1398 .unwrap_or(&lsp_completion.label)
1399 .clone();
1400 (
1401 snapshot.anchor_before(start)..snapshot.anchor_after(end),
1402 text,
1403 )
1404 }
1405 Some(lsp::CompletionTextEdit::InsertAndReplace(_)) => {
1406 log::info!("unsupported insert/replace completion");
1407 return None;
1408 }
1409 };
1410
1411 let language = language.clone();
1412 LineEnding::normalize(&mut new_text);
1413 Some(async move {
1414 let mut label = None;
1415 if let Some(language) = language {
1416 language.process_completion(&mut lsp_completion).await;
1417 label = language.label_for_completion(&lsp_completion).await;
1418 }
1419 Completion {
1420 old_range,
1421 new_text,
1422 label: label.unwrap_or_else(|| {
1423 language::CodeLabel::plain(
1424 lsp_completion.label.clone(),
1425 lsp_completion.filter_text.as_deref(),
1426 )
1427 }),
1428 server_id,
1429 lsp_completion,
1430 }
1431 })
1432 })
1433 });
1434
1435 Ok(futures::future::join_all(completions).await)
1436 }
1437
1438 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCompletions {
1439 let anchor = buffer.anchor_after(self.position);
1440 proto::GetCompletions {
1441 project_id,
1442 buffer_id: buffer.remote_id(),
1443 position: Some(language::proto::serialize_anchor(&anchor)),
1444 version: serialize_version(&buffer.version()),
1445 }
1446 }
1447
1448 async fn from_proto(
1449 message: proto::GetCompletions,
1450 _: ModelHandle<Project>,
1451 buffer: ModelHandle<Buffer>,
1452 mut cx: AsyncAppContext,
1453 ) -> Result<Self> {
1454 let version = deserialize_version(&message.version);
1455 buffer
1456 .update(&mut cx, |buffer, _| buffer.wait_for_version(version))
1457 .await?;
1458 let position = message
1459 .position
1460 .and_then(language::proto::deserialize_anchor)
1461 .map(|p| {
1462 buffer.read_with(&cx, |buffer, _| {
1463 buffer.clip_point_utf16(Unclipped(p.to_point_utf16(buffer)), Bias::Left)
1464 })
1465 })
1466 .ok_or_else(|| anyhow!("invalid position"))?;
1467 Ok(Self { position })
1468 }
1469
1470 fn response_to_proto(
1471 completions: Vec<Completion>,
1472 _: &mut Project,
1473 _: PeerId,
1474 buffer_version: &clock::Global,
1475 _: &mut AppContext,
1476 ) -> proto::GetCompletionsResponse {
1477 proto::GetCompletionsResponse {
1478 completions: completions
1479 .iter()
1480 .map(language::proto::serialize_completion)
1481 .collect(),
1482 version: serialize_version(&buffer_version),
1483 }
1484 }
1485
1486 async fn response_from_proto(
1487 self,
1488 message: proto::GetCompletionsResponse,
1489 _: ModelHandle<Project>,
1490 buffer: ModelHandle<Buffer>,
1491 mut cx: AsyncAppContext,
1492 ) -> Result<Vec<Completion>> {
1493 buffer
1494 .update(&mut cx, |buffer, _| {
1495 buffer.wait_for_version(deserialize_version(&message.version))
1496 })
1497 .await?;
1498
1499 let language = buffer.read_with(&cx, |buffer, _| buffer.language().cloned());
1500 let completions = message.completions.into_iter().map(|completion| {
1501 language::proto::deserialize_completion(completion, language.clone())
1502 });
1503 futures::future::try_join_all(completions).await
1504 }
1505
1506 fn buffer_id_from_proto(message: &proto::GetCompletions) -> u64 {
1507 message.buffer_id
1508 }
1509}
1510
1511#[async_trait(?Send)]
1512impl LspCommand for GetCodeActions {
1513 type Response = Vec<CodeAction>;
1514 type LspRequest = lsp::request::CodeActionRequest;
1515 type ProtoRequest = proto::GetCodeActions;
1516
1517 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
1518 match &capabilities.code_action_provider {
1519 None => false,
1520 Some(lsp::CodeActionProviderCapability::Simple(false)) => false,
1521 _ => true,
1522 }
1523 }
1524
1525 fn to_lsp(
1526 &self,
1527 path: &Path,
1528 buffer: &Buffer,
1529 language_server: &Arc<LanguageServer>,
1530 _: &AppContext,
1531 ) -> lsp::CodeActionParams {
1532 let relevant_diagnostics = buffer
1533 .snapshot()
1534 .diagnostics_in_range::<_, usize>(self.range.clone(), false)
1535 .map(|entry| entry.to_lsp_diagnostic_stub())
1536 .collect();
1537 lsp::CodeActionParams {
1538 text_document: lsp::TextDocumentIdentifier::new(
1539 lsp::Url::from_file_path(path).unwrap(),
1540 ),
1541 range: range_to_lsp(self.range.to_point_utf16(buffer)),
1542 work_done_progress_params: Default::default(),
1543 partial_result_params: Default::default(),
1544 context: lsp::CodeActionContext {
1545 diagnostics: relevant_diagnostics,
1546 only: language_server.code_action_kinds(),
1547 ..lsp::CodeActionContext::default()
1548 },
1549 }
1550 }
1551
1552 async fn response_from_lsp(
1553 self,
1554 actions: Option<lsp::CodeActionResponse>,
1555 _: ModelHandle<Project>,
1556 _: ModelHandle<Buffer>,
1557 server_id: LanguageServerId,
1558 _: AsyncAppContext,
1559 ) -> Result<Vec<CodeAction>> {
1560 Ok(actions
1561 .unwrap_or_default()
1562 .into_iter()
1563 .filter_map(|entry| {
1564 if let lsp::CodeActionOrCommand::CodeAction(lsp_action) = entry {
1565 Some(CodeAction {
1566 server_id,
1567 range: self.range.clone(),
1568 lsp_action,
1569 })
1570 } else {
1571 None
1572 }
1573 })
1574 .collect())
1575 }
1576
1577 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCodeActions {
1578 proto::GetCodeActions {
1579 project_id,
1580 buffer_id: buffer.remote_id(),
1581 start: Some(language::proto::serialize_anchor(&self.range.start)),
1582 end: Some(language::proto::serialize_anchor(&self.range.end)),
1583 version: serialize_version(&buffer.version()),
1584 }
1585 }
1586
1587 async fn from_proto(
1588 message: proto::GetCodeActions,
1589 _: ModelHandle<Project>,
1590 buffer: ModelHandle<Buffer>,
1591 mut cx: AsyncAppContext,
1592 ) -> Result<Self> {
1593 let start = message
1594 .start
1595 .and_then(language::proto::deserialize_anchor)
1596 .ok_or_else(|| anyhow!("invalid start"))?;
1597 let end = message
1598 .end
1599 .and_then(language::proto::deserialize_anchor)
1600 .ok_or_else(|| anyhow!("invalid end"))?;
1601 buffer
1602 .update(&mut cx, |buffer, _| {
1603 buffer.wait_for_version(deserialize_version(&message.version))
1604 })
1605 .await?;
1606
1607 Ok(Self { range: start..end })
1608 }
1609
1610 fn response_to_proto(
1611 code_actions: Vec<CodeAction>,
1612 _: &mut Project,
1613 _: PeerId,
1614 buffer_version: &clock::Global,
1615 _: &mut AppContext,
1616 ) -> proto::GetCodeActionsResponse {
1617 proto::GetCodeActionsResponse {
1618 actions: code_actions
1619 .iter()
1620 .map(language::proto::serialize_code_action)
1621 .collect(),
1622 version: serialize_version(&buffer_version),
1623 }
1624 }
1625
1626 async fn response_from_proto(
1627 self,
1628 message: proto::GetCodeActionsResponse,
1629 _: ModelHandle<Project>,
1630 buffer: ModelHandle<Buffer>,
1631 mut cx: AsyncAppContext,
1632 ) -> Result<Vec<CodeAction>> {
1633 buffer
1634 .update(&mut cx, |buffer, _| {
1635 buffer.wait_for_version(deserialize_version(&message.version))
1636 })
1637 .await?;
1638 message
1639 .actions
1640 .into_iter()
1641 .map(language::proto::deserialize_code_action)
1642 .collect()
1643 }
1644
1645 fn buffer_id_from_proto(message: &proto::GetCodeActions) -> u64 {
1646 message.buffer_id
1647 }
1648}
1649
1650#[async_trait(?Send)]
1651impl LspCommand for OnTypeFormatting {
1652 type Response = Option<Transaction>;
1653 type LspRequest = lsp::request::OnTypeFormatting;
1654 type ProtoRequest = proto::OnTypeFormatting;
1655
1656 fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
1657 let Some(on_type_formatting_options) = &server_capabilities.document_on_type_formatting_provider else { return false };
1658 on_type_formatting_options
1659 .first_trigger_character
1660 .contains(&self.trigger)
1661 || on_type_formatting_options
1662 .more_trigger_character
1663 .iter()
1664 .flatten()
1665 .any(|chars| chars.contains(&self.trigger))
1666 }
1667
1668 fn to_lsp(
1669 &self,
1670 path: &Path,
1671 _: &Buffer,
1672 _: &Arc<LanguageServer>,
1673 _: &AppContext,
1674 ) -> lsp::DocumentOnTypeFormattingParams {
1675 lsp::DocumentOnTypeFormattingParams {
1676 text_document_position: lsp::TextDocumentPositionParams::new(
1677 lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
1678 point_to_lsp(self.position),
1679 ),
1680 ch: self.trigger.clone(),
1681 options: lsp_formatting_options(self.options.tab_size),
1682 }
1683 }
1684
1685 async fn response_from_lsp(
1686 self,
1687 message: Option<Vec<lsp::TextEdit>>,
1688 project: ModelHandle<Project>,
1689 buffer: ModelHandle<Buffer>,
1690 server_id: LanguageServerId,
1691 mut cx: AsyncAppContext,
1692 ) -> Result<Option<Transaction>> {
1693 if let Some(edits) = message {
1694 let (lsp_adapter, lsp_server) =
1695 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
1696 Project::deserialize_edits(
1697 project,
1698 buffer,
1699 edits,
1700 self.push_to_history,
1701 lsp_adapter,
1702 lsp_server,
1703 &mut cx,
1704 )
1705 .await
1706 } else {
1707 Ok(None)
1708 }
1709 }
1710
1711 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::OnTypeFormatting {
1712 proto::OnTypeFormatting {
1713 project_id,
1714 buffer_id: buffer.remote_id(),
1715 position: Some(language::proto::serialize_anchor(
1716 &buffer.anchor_before(self.position),
1717 )),
1718 trigger: self.trigger.clone(),
1719 version: serialize_version(&buffer.version()),
1720 }
1721 }
1722
1723 async fn from_proto(
1724 message: proto::OnTypeFormatting,
1725 _: ModelHandle<Project>,
1726 buffer: ModelHandle<Buffer>,
1727 mut cx: AsyncAppContext,
1728 ) -> Result<Self> {
1729 let position = message
1730 .position
1731 .and_then(deserialize_anchor)
1732 .ok_or_else(|| anyhow!("invalid position"))?;
1733 buffer
1734 .update(&mut cx, |buffer, _| {
1735 buffer.wait_for_version(deserialize_version(&message.version))
1736 })
1737 .await?;
1738
1739 let tab_size = buffer.read_with(&cx, |buffer, cx| {
1740 language_settings(buffer.language(), buffer.file(), cx).tab_size
1741 });
1742
1743 Ok(Self {
1744 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
1745 trigger: message.trigger.clone(),
1746 options: lsp_formatting_options(tab_size.get()).into(),
1747 push_to_history: false,
1748 })
1749 }
1750
1751 fn response_to_proto(
1752 response: Option<Transaction>,
1753 _: &mut Project,
1754 _: PeerId,
1755 _: &clock::Global,
1756 _: &mut AppContext,
1757 ) -> proto::OnTypeFormattingResponse {
1758 proto::OnTypeFormattingResponse {
1759 transaction: response
1760 .map(|transaction| language::proto::serialize_transaction(&transaction)),
1761 }
1762 }
1763
1764 async fn response_from_proto(
1765 self,
1766 message: proto::OnTypeFormattingResponse,
1767 _: ModelHandle<Project>,
1768 _: ModelHandle<Buffer>,
1769 _: AsyncAppContext,
1770 ) -> Result<Option<Transaction>> {
1771 let Some(transaction) = message.transaction else { return Ok(None) };
1772 Ok(Some(language::proto::deserialize_transaction(transaction)?))
1773 }
1774
1775 fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> u64 {
1776 message.buffer_id
1777 }
1778}
1779
1780#[async_trait(?Send)]
1781impl LspCommand for InlayHints {
1782 type Response = Vec<InlayHint>;
1783 type LspRequest = lsp::InlayHintRequest;
1784 type ProtoRequest = proto::InlayHints;
1785
1786 fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
1787 let Some(inlay_hint_provider) = &server_capabilities.inlay_hint_provider else { return false };
1788 match inlay_hint_provider {
1789 lsp::OneOf::Left(enabled) => *enabled,
1790 lsp::OneOf::Right(inlay_hint_capabilities) => match inlay_hint_capabilities {
1791 lsp::InlayHintServerCapabilities::Options(_) => true,
1792 lsp::InlayHintServerCapabilities::RegistrationOptions(_) => false,
1793 },
1794 }
1795 }
1796
1797 fn to_lsp(
1798 &self,
1799 path: &Path,
1800 buffer: &Buffer,
1801 _: &Arc<LanguageServer>,
1802 _: &AppContext,
1803 ) -> lsp::InlayHintParams {
1804 lsp::InlayHintParams {
1805 text_document: lsp::TextDocumentIdentifier {
1806 uri: lsp::Url::from_file_path(path).unwrap(),
1807 },
1808 range: range_to_lsp(self.range.to_point_utf16(buffer)),
1809 work_done_progress_params: Default::default(),
1810 }
1811 }
1812
1813 async fn response_from_lsp(
1814 self,
1815 message: Option<Vec<lsp::InlayHint>>,
1816 project: ModelHandle<Project>,
1817 buffer: ModelHandle<Buffer>,
1818 server_id: LanguageServerId,
1819 mut cx: AsyncAppContext,
1820 ) -> Result<Vec<InlayHint>> {
1821 let (lsp_adapter, _) = language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
1822 // `typescript-language-server` adds padding to the left for type hints, turning
1823 // `const foo: boolean` into `const foo : boolean` which looks odd.
1824 // `rust-analyzer` does not have the padding for this case, and we have to accomodate both.
1825 //
1826 // We could trim the whole string, but being pessimistic on par with the situation above,
1827 // there might be a hint with multiple whitespaces at the end(s) which we need to display properly.
1828 // Hence let's use a heuristic first to handle the most awkward case and look for more.
1829 let force_no_type_left_padding =
1830 lsp_adapter.name.0.as_ref() == "typescript-language-server";
1831 cx.read(|cx| {
1832 let origin_buffer = buffer.read(cx);
1833 Ok(message
1834 .unwrap_or_default()
1835 .into_iter()
1836 .map(|lsp_hint| {
1837 let kind = lsp_hint.kind.and_then(|kind| match kind {
1838 lsp::InlayHintKind::TYPE => Some(InlayHintKind::Type),
1839 lsp::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
1840 _ => None,
1841 });
1842 let position = origin_buffer
1843 .clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
1844 let padding_left =
1845 if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
1846 false
1847 } else {
1848 lsp_hint.padding_left.unwrap_or(false)
1849 };
1850 InlayHint {
1851 buffer_id: origin_buffer.remote_id(),
1852 position: if kind == Some(InlayHintKind::Parameter) {
1853 origin_buffer.anchor_before(position)
1854 } else {
1855 origin_buffer.anchor_after(position)
1856 },
1857 padding_left,
1858 padding_right: lsp_hint.padding_right.unwrap_or(false),
1859 label: match lsp_hint.label {
1860 lsp::InlayHintLabel::String(s) => InlayHintLabel::String(s),
1861 lsp::InlayHintLabel::LabelParts(lsp_parts) => {
1862 InlayHintLabel::LabelParts(
1863 lsp_parts
1864 .into_iter()
1865 .map(|label_part| InlayHintLabelPart {
1866 value: label_part.value,
1867 tooltip: label_part.tooltip.map(
1868 |tooltip| {
1869 match tooltip {
1870 lsp::InlayHintLabelPartTooltip::String(s) => {
1871 InlayHintLabelPartTooltip::String(s)
1872 }
1873 lsp::InlayHintLabelPartTooltip::MarkupContent(
1874 markup_content,
1875 ) => InlayHintLabelPartTooltip::MarkupContent(
1876 MarkupContent {
1877 kind: format!("{:?}", markup_content.kind),
1878 value: markup_content.value,
1879 },
1880 ),
1881 }
1882 },
1883 ),
1884 location: label_part.location.map(|lsp_location| {
1885 let target_start = origin_buffer.clip_point_utf16(
1886 point_from_lsp(lsp_location.range.start),
1887 Bias::Left,
1888 );
1889 let target_end = origin_buffer.clip_point_utf16(
1890 point_from_lsp(lsp_location.range.end),
1891 Bias::Left,
1892 );
1893 Location {
1894 buffer: buffer.clone(),
1895 range: origin_buffer.anchor_after(target_start)
1896 ..origin_buffer.anchor_before(target_end),
1897 }
1898 }),
1899 })
1900 .collect(),
1901 )
1902 }
1903 },
1904 kind,
1905 tooltip: lsp_hint.tooltip.map(|tooltip| match tooltip {
1906 lsp::InlayHintTooltip::String(s) => InlayHintTooltip::String(s),
1907 lsp::InlayHintTooltip::MarkupContent(markup_content) => {
1908 InlayHintTooltip::MarkupContent(MarkupContent {
1909 kind: format!("{:?}", markup_content.kind),
1910 value: markup_content.value,
1911 })
1912 }
1913 }),
1914 }
1915 })
1916 .collect())
1917 })
1918 }
1919
1920 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints {
1921 proto::InlayHints {
1922 project_id,
1923 buffer_id: buffer.remote_id(),
1924 start: Some(language::proto::serialize_anchor(&self.range.start)),
1925 end: Some(language::proto::serialize_anchor(&self.range.end)),
1926 version: serialize_version(&buffer.version()),
1927 }
1928 }
1929
1930 async fn from_proto(
1931 message: proto::InlayHints,
1932 _: ModelHandle<Project>,
1933 buffer: ModelHandle<Buffer>,
1934 mut cx: AsyncAppContext,
1935 ) -> Result<Self> {
1936 let start = message
1937 .start
1938 .and_then(language::proto::deserialize_anchor)
1939 .context("invalid start")?;
1940 let end = message
1941 .end
1942 .and_then(language::proto::deserialize_anchor)
1943 .context("invalid end")?;
1944 buffer
1945 .update(&mut cx, |buffer, _| {
1946 buffer.wait_for_version(deserialize_version(&message.version))
1947 })
1948 .await?;
1949
1950 Ok(Self { range: start..end })
1951 }
1952
1953 fn response_to_proto(
1954 response: Vec<InlayHint>,
1955 _: &mut Project,
1956 _: PeerId,
1957 buffer_version: &clock::Global,
1958 _: &mut AppContext,
1959 ) -> proto::InlayHintsResponse {
1960 proto::InlayHintsResponse {
1961 hints: response
1962 .into_iter()
1963 .map(|response_hint| proto::InlayHint {
1964 position: Some(language::proto::serialize_anchor(&response_hint.position)),
1965 padding_left: response_hint.padding_left,
1966 padding_right: response_hint.padding_right,
1967 kind: response_hint.kind.map(|kind| kind.name().to_string()),
1968 // Do not pass extra data such as tooltips to clients: host can put tooltip data from the cache during resolution.
1969 tooltip: None,
1970 // Similarly, do not pass label parts to clients: host can return a detailed list during resolution.
1971 label: Some(proto::InlayHintLabel {
1972 label: Some(proto::inlay_hint_label::Label::Value(
1973 match response_hint.label {
1974 InlayHintLabel::String(s) => s,
1975 InlayHintLabel::LabelParts(_) => response_hint.text(),
1976 },
1977 )),
1978 }),
1979 })
1980 .collect(),
1981 version: serialize_version(buffer_version),
1982 }
1983 }
1984
1985 async fn response_from_proto(
1986 self,
1987 message: proto::InlayHintsResponse,
1988 project: ModelHandle<Project>,
1989 buffer: ModelHandle<Buffer>,
1990 mut cx: AsyncAppContext,
1991 ) -> Result<Vec<InlayHint>> {
1992 buffer
1993 .update(&mut cx, |buffer, _| {
1994 buffer.wait_for_version(deserialize_version(&message.version))
1995 })
1996 .await?;
1997
1998 let mut hints = Vec::new();
1999 for message_hint in message.hints {
2000 let buffer_id = message_hint
2001 .position
2002 .as_ref()
2003 .and_then(|location| location.buffer_id)
2004 .context("missing buffer id")?;
2005 let hint = InlayHint {
2006 buffer_id,
2007 position: message_hint
2008 .position
2009 .and_then(language::proto::deserialize_anchor)
2010 .context("invalid position")?,
2011 label: match message_hint
2012 .label
2013 .and_then(|label| label.label)
2014 .context("missing label")?
2015 {
2016 proto::inlay_hint_label::Label::Value(s) => InlayHintLabel::String(s),
2017 proto::inlay_hint_label::Label::LabelParts(parts) => {
2018 let mut label_parts = Vec::new();
2019 for part in parts.parts {
2020 label_parts.push(InlayHintLabelPart {
2021 value: part.value,
2022 tooltip: part.tooltip.map(|tooltip| match tooltip.content {
2023 Some(proto::inlay_hint_label_part_tooltip::Content::Value(s)) => InlayHintLabelPartTooltip::String(s),
2024 Some(proto::inlay_hint_label_part_tooltip::Content::MarkupContent(markup_content)) => InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2025 kind: markup_content.kind,
2026 value: markup_content.value,
2027 }),
2028 None => InlayHintLabelPartTooltip::String(String::new()),
2029 }),
2030 location: match part.location {
2031 Some(location) => {
2032 let target_buffer = project
2033 .update(&mut cx, |this, cx| {
2034 this.wait_for_remote_buffer(location.buffer_id, cx)
2035 })
2036 .await?;
2037 Some(Location {
2038 range: location
2039 .start
2040 .and_then(language::proto::deserialize_anchor)
2041 .context("invalid start")?
2042 ..location
2043 .end
2044 .and_then(language::proto::deserialize_anchor)
2045 .context("invalid end")?,
2046 buffer: target_buffer,
2047 })},
2048 None => None,
2049 },
2050 });
2051 }
2052
2053 InlayHintLabel::LabelParts(label_parts)
2054 }
2055 },
2056 padding_left: message_hint.padding_left,
2057 padding_right: message_hint.padding_right,
2058 kind: message_hint
2059 .kind
2060 .as_deref()
2061 .and_then(InlayHintKind::from_name),
2062 tooltip: message_hint.tooltip.and_then(|tooltip| {
2063 Some(match tooltip.content? {
2064 proto::inlay_hint_tooltip::Content::Value(s) => InlayHintTooltip::String(s),
2065 proto::inlay_hint_tooltip::Content::MarkupContent(markup_content) => {
2066 InlayHintTooltip::MarkupContent(MarkupContent {
2067 kind: markup_content.kind,
2068 value: markup_content.value,
2069 })
2070 }
2071 })
2072 }),
2073 };
2074
2075 hints.push(hint);
2076 }
2077
2078 Ok(hints)
2079 }
2080
2081 fn buffer_id_from_proto(message: &proto::InlayHints) -> u64 {
2082 message.buffer_id
2083 }
2084}