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 _: 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 lsp_completion,
1429 }
1430 })
1431 })
1432 });
1433
1434 Ok(futures::future::join_all(completions).await)
1435 }
1436
1437 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCompletions {
1438 let anchor = buffer.anchor_after(self.position);
1439 proto::GetCompletions {
1440 project_id,
1441 buffer_id: buffer.remote_id(),
1442 position: Some(language::proto::serialize_anchor(&anchor)),
1443 version: serialize_version(&buffer.version()),
1444 }
1445 }
1446
1447 async fn from_proto(
1448 message: proto::GetCompletions,
1449 _: ModelHandle<Project>,
1450 buffer: ModelHandle<Buffer>,
1451 mut cx: AsyncAppContext,
1452 ) -> Result<Self> {
1453 let version = deserialize_version(&message.version);
1454 buffer
1455 .update(&mut cx, |buffer, _| buffer.wait_for_version(version))
1456 .await?;
1457 let position = message
1458 .position
1459 .and_then(language::proto::deserialize_anchor)
1460 .map(|p| {
1461 buffer.read_with(&cx, |buffer, _| {
1462 buffer.clip_point_utf16(Unclipped(p.to_point_utf16(buffer)), Bias::Left)
1463 })
1464 })
1465 .ok_or_else(|| anyhow!("invalid position"))?;
1466 Ok(Self { position })
1467 }
1468
1469 fn response_to_proto(
1470 completions: Vec<Completion>,
1471 _: &mut Project,
1472 _: PeerId,
1473 buffer_version: &clock::Global,
1474 _: &mut AppContext,
1475 ) -> proto::GetCompletionsResponse {
1476 proto::GetCompletionsResponse {
1477 completions: completions
1478 .iter()
1479 .map(language::proto::serialize_completion)
1480 .collect(),
1481 version: serialize_version(&buffer_version),
1482 }
1483 }
1484
1485 async fn response_from_proto(
1486 self,
1487 message: proto::GetCompletionsResponse,
1488 _: ModelHandle<Project>,
1489 buffer: ModelHandle<Buffer>,
1490 mut cx: AsyncAppContext,
1491 ) -> Result<Vec<Completion>> {
1492 buffer
1493 .update(&mut cx, |buffer, _| {
1494 buffer.wait_for_version(deserialize_version(&message.version))
1495 })
1496 .await?;
1497
1498 let language = buffer.read_with(&cx, |buffer, _| buffer.language().cloned());
1499 let completions = message.completions.into_iter().map(|completion| {
1500 language::proto::deserialize_completion(completion, language.clone())
1501 });
1502 futures::future::try_join_all(completions).await
1503 }
1504
1505 fn buffer_id_from_proto(message: &proto::GetCompletions) -> u64 {
1506 message.buffer_id
1507 }
1508}
1509
1510#[async_trait(?Send)]
1511impl LspCommand for GetCodeActions {
1512 type Response = Vec<CodeAction>;
1513 type LspRequest = lsp::request::CodeActionRequest;
1514 type ProtoRequest = proto::GetCodeActions;
1515
1516 fn check_capabilities(&self, capabilities: &ServerCapabilities) -> bool {
1517 match &capabilities.code_action_provider {
1518 None => false,
1519 Some(lsp::CodeActionProviderCapability::Simple(false)) => false,
1520 _ => true,
1521 }
1522 }
1523
1524 fn to_lsp(
1525 &self,
1526 path: &Path,
1527 buffer: &Buffer,
1528 language_server: &Arc<LanguageServer>,
1529 _: &AppContext,
1530 ) -> lsp::CodeActionParams {
1531 let relevant_diagnostics = buffer
1532 .snapshot()
1533 .diagnostics_in_range::<_, usize>(self.range.clone(), false)
1534 .map(|entry| entry.to_lsp_diagnostic_stub())
1535 .collect();
1536 lsp::CodeActionParams {
1537 text_document: lsp::TextDocumentIdentifier::new(
1538 lsp::Url::from_file_path(path).unwrap(),
1539 ),
1540 range: range_to_lsp(self.range.to_point_utf16(buffer)),
1541 work_done_progress_params: Default::default(),
1542 partial_result_params: Default::default(),
1543 context: lsp::CodeActionContext {
1544 diagnostics: relevant_diagnostics,
1545 only: language_server.code_action_kinds(),
1546 ..lsp::CodeActionContext::default()
1547 },
1548 }
1549 }
1550
1551 async fn response_from_lsp(
1552 self,
1553 actions: Option<lsp::CodeActionResponse>,
1554 _: ModelHandle<Project>,
1555 _: ModelHandle<Buffer>,
1556 server_id: LanguageServerId,
1557 _: AsyncAppContext,
1558 ) -> Result<Vec<CodeAction>> {
1559 Ok(actions
1560 .unwrap_or_default()
1561 .into_iter()
1562 .filter_map(|entry| {
1563 if let lsp::CodeActionOrCommand::CodeAction(lsp_action) = entry {
1564 Some(CodeAction {
1565 server_id,
1566 range: self.range.clone(),
1567 lsp_action,
1568 })
1569 } else {
1570 None
1571 }
1572 })
1573 .collect())
1574 }
1575
1576 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCodeActions {
1577 proto::GetCodeActions {
1578 project_id,
1579 buffer_id: buffer.remote_id(),
1580 start: Some(language::proto::serialize_anchor(&self.range.start)),
1581 end: Some(language::proto::serialize_anchor(&self.range.end)),
1582 version: serialize_version(&buffer.version()),
1583 }
1584 }
1585
1586 async fn from_proto(
1587 message: proto::GetCodeActions,
1588 _: ModelHandle<Project>,
1589 buffer: ModelHandle<Buffer>,
1590 mut cx: AsyncAppContext,
1591 ) -> Result<Self> {
1592 let start = message
1593 .start
1594 .and_then(language::proto::deserialize_anchor)
1595 .ok_or_else(|| anyhow!("invalid start"))?;
1596 let end = message
1597 .end
1598 .and_then(language::proto::deserialize_anchor)
1599 .ok_or_else(|| anyhow!("invalid end"))?;
1600 buffer
1601 .update(&mut cx, |buffer, _| {
1602 buffer.wait_for_version(deserialize_version(&message.version))
1603 })
1604 .await?;
1605
1606 Ok(Self { range: start..end })
1607 }
1608
1609 fn response_to_proto(
1610 code_actions: Vec<CodeAction>,
1611 _: &mut Project,
1612 _: PeerId,
1613 buffer_version: &clock::Global,
1614 _: &mut AppContext,
1615 ) -> proto::GetCodeActionsResponse {
1616 proto::GetCodeActionsResponse {
1617 actions: code_actions
1618 .iter()
1619 .map(language::proto::serialize_code_action)
1620 .collect(),
1621 version: serialize_version(&buffer_version),
1622 }
1623 }
1624
1625 async fn response_from_proto(
1626 self,
1627 message: proto::GetCodeActionsResponse,
1628 _: ModelHandle<Project>,
1629 buffer: ModelHandle<Buffer>,
1630 mut cx: AsyncAppContext,
1631 ) -> Result<Vec<CodeAction>> {
1632 buffer
1633 .update(&mut cx, |buffer, _| {
1634 buffer.wait_for_version(deserialize_version(&message.version))
1635 })
1636 .await?;
1637 message
1638 .actions
1639 .into_iter()
1640 .map(language::proto::deserialize_code_action)
1641 .collect()
1642 }
1643
1644 fn buffer_id_from_proto(message: &proto::GetCodeActions) -> u64 {
1645 message.buffer_id
1646 }
1647}
1648
1649#[async_trait(?Send)]
1650impl LspCommand for OnTypeFormatting {
1651 type Response = Option<Transaction>;
1652 type LspRequest = lsp::request::OnTypeFormatting;
1653 type ProtoRequest = proto::OnTypeFormatting;
1654
1655 fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
1656 let Some(on_type_formatting_options) = &server_capabilities.document_on_type_formatting_provider else { return false };
1657 on_type_formatting_options
1658 .first_trigger_character
1659 .contains(&self.trigger)
1660 || on_type_formatting_options
1661 .more_trigger_character
1662 .iter()
1663 .flatten()
1664 .any(|chars| chars.contains(&self.trigger))
1665 }
1666
1667 fn to_lsp(
1668 &self,
1669 path: &Path,
1670 _: &Buffer,
1671 _: &Arc<LanguageServer>,
1672 _: &AppContext,
1673 ) -> lsp::DocumentOnTypeFormattingParams {
1674 lsp::DocumentOnTypeFormattingParams {
1675 text_document_position: lsp::TextDocumentPositionParams::new(
1676 lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path).unwrap()),
1677 point_to_lsp(self.position),
1678 ),
1679 ch: self.trigger.clone(),
1680 options: lsp_formatting_options(self.options.tab_size),
1681 }
1682 }
1683
1684 async fn response_from_lsp(
1685 self,
1686 message: Option<Vec<lsp::TextEdit>>,
1687 project: ModelHandle<Project>,
1688 buffer: ModelHandle<Buffer>,
1689 server_id: LanguageServerId,
1690 mut cx: AsyncAppContext,
1691 ) -> Result<Option<Transaction>> {
1692 if let Some(edits) = message {
1693 let (lsp_adapter, lsp_server) =
1694 language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
1695 Project::deserialize_edits(
1696 project,
1697 buffer,
1698 edits,
1699 self.push_to_history,
1700 lsp_adapter,
1701 lsp_server,
1702 &mut cx,
1703 )
1704 .await
1705 } else {
1706 Ok(None)
1707 }
1708 }
1709
1710 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::OnTypeFormatting {
1711 proto::OnTypeFormatting {
1712 project_id,
1713 buffer_id: buffer.remote_id(),
1714 position: Some(language::proto::serialize_anchor(
1715 &buffer.anchor_before(self.position),
1716 )),
1717 trigger: self.trigger.clone(),
1718 version: serialize_version(&buffer.version()),
1719 }
1720 }
1721
1722 async fn from_proto(
1723 message: proto::OnTypeFormatting,
1724 _: ModelHandle<Project>,
1725 buffer: ModelHandle<Buffer>,
1726 mut cx: AsyncAppContext,
1727 ) -> Result<Self> {
1728 let position = message
1729 .position
1730 .and_then(deserialize_anchor)
1731 .ok_or_else(|| anyhow!("invalid position"))?;
1732 buffer
1733 .update(&mut cx, |buffer, _| {
1734 buffer.wait_for_version(deserialize_version(&message.version))
1735 })
1736 .await?;
1737
1738 let tab_size = buffer.read_with(&cx, |buffer, cx| {
1739 language_settings(buffer.language(), buffer.file(), cx).tab_size
1740 });
1741
1742 Ok(Self {
1743 position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)),
1744 trigger: message.trigger.clone(),
1745 options: lsp_formatting_options(tab_size.get()).into(),
1746 push_to_history: false,
1747 })
1748 }
1749
1750 fn response_to_proto(
1751 response: Option<Transaction>,
1752 _: &mut Project,
1753 _: PeerId,
1754 _: &clock::Global,
1755 _: &mut AppContext,
1756 ) -> proto::OnTypeFormattingResponse {
1757 proto::OnTypeFormattingResponse {
1758 transaction: response
1759 .map(|transaction| language::proto::serialize_transaction(&transaction)),
1760 }
1761 }
1762
1763 async fn response_from_proto(
1764 self,
1765 message: proto::OnTypeFormattingResponse,
1766 _: ModelHandle<Project>,
1767 _: ModelHandle<Buffer>,
1768 _: AsyncAppContext,
1769 ) -> Result<Option<Transaction>> {
1770 let Some(transaction) = message.transaction else { return Ok(None) };
1771 Ok(Some(language::proto::deserialize_transaction(transaction)?))
1772 }
1773
1774 fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> u64 {
1775 message.buffer_id
1776 }
1777}
1778
1779#[async_trait(?Send)]
1780impl LspCommand for InlayHints {
1781 type Response = Vec<InlayHint>;
1782 type LspRequest = lsp::InlayHintRequest;
1783 type ProtoRequest = proto::InlayHints;
1784
1785 fn check_capabilities(&self, server_capabilities: &lsp::ServerCapabilities) -> bool {
1786 let Some(inlay_hint_provider) = &server_capabilities.inlay_hint_provider else { return false };
1787 match inlay_hint_provider {
1788 lsp::OneOf::Left(enabled) => *enabled,
1789 lsp::OneOf::Right(inlay_hint_capabilities) => match inlay_hint_capabilities {
1790 lsp::InlayHintServerCapabilities::Options(_) => true,
1791 lsp::InlayHintServerCapabilities::RegistrationOptions(_) => false,
1792 },
1793 }
1794 }
1795
1796 fn to_lsp(
1797 &self,
1798 path: &Path,
1799 buffer: &Buffer,
1800 _: &Arc<LanguageServer>,
1801 _: &AppContext,
1802 ) -> lsp::InlayHintParams {
1803 lsp::InlayHintParams {
1804 text_document: lsp::TextDocumentIdentifier {
1805 uri: lsp::Url::from_file_path(path).unwrap(),
1806 },
1807 range: range_to_lsp(self.range.to_point_utf16(buffer)),
1808 work_done_progress_params: Default::default(),
1809 }
1810 }
1811
1812 async fn response_from_lsp(
1813 self,
1814 message: Option<Vec<lsp::InlayHint>>,
1815 project: ModelHandle<Project>,
1816 buffer: ModelHandle<Buffer>,
1817 server_id: LanguageServerId,
1818 mut cx: AsyncAppContext,
1819 ) -> Result<Vec<InlayHint>> {
1820 let (lsp_adapter, _) = language_server_for_buffer(&project, &buffer, server_id, &mut cx)?;
1821 // `typescript-language-server` adds padding to the left for type hints, turning
1822 // `const foo: boolean` into `const foo : boolean` which looks odd.
1823 // `rust-analyzer` does not have the padding for this case, and we have to accomodate both.
1824 //
1825 // We could trim the whole string, but being pessimistic on par with the situation above,
1826 // there might be a hint with multiple whitespaces at the end(s) which we need to display properly.
1827 // Hence let's use a heuristic first to handle the most awkward case and look for more.
1828 let force_no_type_left_padding =
1829 lsp_adapter.name.0.as_ref() == "typescript-language-server";
1830 cx.read(|cx| {
1831 let origin_buffer = buffer.read(cx);
1832 Ok(message
1833 .unwrap_or_default()
1834 .into_iter()
1835 .map(|lsp_hint| {
1836 let kind = lsp_hint.kind.and_then(|kind| match kind {
1837 lsp::InlayHintKind::TYPE => Some(InlayHintKind::Type),
1838 lsp::InlayHintKind::PARAMETER => Some(InlayHintKind::Parameter),
1839 _ => None,
1840 });
1841 let position = origin_buffer
1842 .clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left);
1843 let padding_left =
1844 if force_no_type_left_padding && kind == Some(InlayHintKind::Type) {
1845 false
1846 } else {
1847 lsp_hint.padding_left.unwrap_or(false)
1848 };
1849 InlayHint {
1850 buffer_id: origin_buffer.remote_id(),
1851 position: if kind == Some(InlayHintKind::Parameter) {
1852 origin_buffer.anchor_before(position)
1853 } else {
1854 origin_buffer.anchor_after(position)
1855 },
1856 padding_left,
1857 padding_right: lsp_hint.padding_right.unwrap_or(false),
1858 label: match lsp_hint.label {
1859 lsp::InlayHintLabel::String(s) => InlayHintLabel::String(s),
1860 lsp::InlayHintLabel::LabelParts(lsp_parts) => {
1861 InlayHintLabel::LabelParts(
1862 lsp_parts
1863 .into_iter()
1864 .map(|label_part| InlayHintLabelPart {
1865 value: label_part.value,
1866 tooltip: label_part.tooltip.map(
1867 |tooltip| {
1868 match tooltip {
1869 lsp::InlayHintLabelPartTooltip::String(s) => {
1870 InlayHintLabelPartTooltip::String(s)
1871 }
1872 lsp::InlayHintLabelPartTooltip::MarkupContent(
1873 markup_content,
1874 ) => InlayHintLabelPartTooltip::MarkupContent(
1875 MarkupContent {
1876 kind: format!("{:?}", markup_content.kind),
1877 value: markup_content.value,
1878 },
1879 ),
1880 }
1881 },
1882 ),
1883 location: label_part.location.map(|lsp_location| {
1884 let target_start = origin_buffer.clip_point_utf16(
1885 point_from_lsp(lsp_location.range.start),
1886 Bias::Left,
1887 );
1888 let target_end = origin_buffer.clip_point_utf16(
1889 point_from_lsp(lsp_location.range.end),
1890 Bias::Left,
1891 );
1892 Location {
1893 buffer: buffer.clone(),
1894 range: origin_buffer.anchor_after(target_start)
1895 ..origin_buffer.anchor_before(target_end),
1896 }
1897 }),
1898 })
1899 .collect(),
1900 )
1901 }
1902 },
1903 kind,
1904 tooltip: lsp_hint.tooltip.map(|tooltip| match tooltip {
1905 lsp::InlayHintTooltip::String(s) => InlayHintTooltip::String(s),
1906 lsp::InlayHintTooltip::MarkupContent(markup_content) => {
1907 InlayHintTooltip::MarkupContent(MarkupContent {
1908 kind: format!("{:?}", markup_content.kind),
1909 value: markup_content.value,
1910 })
1911 }
1912 }),
1913 }
1914 })
1915 .collect())
1916 })
1917 }
1918
1919 fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints {
1920 proto::InlayHints {
1921 project_id,
1922 buffer_id: buffer.remote_id(),
1923 start: Some(language::proto::serialize_anchor(&self.range.start)),
1924 end: Some(language::proto::serialize_anchor(&self.range.end)),
1925 version: serialize_version(&buffer.version()),
1926 }
1927 }
1928
1929 async fn from_proto(
1930 message: proto::InlayHints,
1931 _: ModelHandle<Project>,
1932 buffer: ModelHandle<Buffer>,
1933 mut cx: AsyncAppContext,
1934 ) -> Result<Self> {
1935 let start = message
1936 .start
1937 .and_then(language::proto::deserialize_anchor)
1938 .context("invalid start")?;
1939 let end = message
1940 .end
1941 .and_then(language::proto::deserialize_anchor)
1942 .context("invalid end")?;
1943 buffer
1944 .update(&mut cx, |buffer, _| {
1945 buffer.wait_for_version(deserialize_version(&message.version))
1946 })
1947 .await?;
1948
1949 Ok(Self { range: start..end })
1950 }
1951
1952 fn response_to_proto(
1953 response: Vec<InlayHint>,
1954 _: &mut Project,
1955 _: PeerId,
1956 buffer_version: &clock::Global,
1957 cx: &mut AppContext,
1958 ) -> proto::InlayHintsResponse {
1959 proto::InlayHintsResponse {
1960 hints: response
1961 .into_iter()
1962 .map(|response_hint| proto::InlayHint {
1963 position: Some(language::proto::serialize_anchor(&response_hint.position)),
1964 padding_left: response_hint.padding_left,
1965 padding_right: response_hint.padding_right,
1966 label: Some(proto::InlayHintLabel {
1967 label: Some(match response_hint.label {
1968 InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s),
1969 InlayHintLabel::LabelParts(label_parts) => {
1970 proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts {
1971 parts: label_parts.into_iter().map(|label_part| proto::InlayHintLabelPart {
1972 value: label_part.value,
1973 tooltip: label_part.tooltip.map(|tooltip| {
1974 let proto_tooltip = match tooltip {
1975 InlayHintLabelPartTooltip::String(s) => proto::inlay_hint_label_part_tooltip::Content::Value(s),
1976 InlayHintLabelPartTooltip::MarkupContent(markup_content) => proto::inlay_hint_label_part_tooltip::Content::MarkupContent(proto::MarkupContent {
1977 kind: markup_content.kind,
1978 value: markup_content.value,
1979 }),
1980 };
1981 proto::InlayHintLabelPartTooltip {content: Some(proto_tooltip)}
1982 }),
1983 location: label_part.location.map(|location| proto::Location {
1984 start: Some(serialize_anchor(&location.range.start)),
1985 end: Some(serialize_anchor(&location.range.end)),
1986 buffer_id: location.buffer.read(cx).remote_id(),
1987 }),
1988 }).collect()
1989 })
1990 }
1991 }),
1992 }),
1993 kind: response_hint.kind.map(|kind| kind.name().to_string()),
1994 tooltip: response_hint.tooltip.map(|response_tooltip| {
1995 let proto_tooltip = match response_tooltip {
1996 InlayHintTooltip::String(s) => {
1997 proto::inlay_hint_tooltip::Content::Value(s)
1998 }
1999 InlayHintTooltip::MarkupContent(markup_content) => {
2000 proto::inlay_hint_tooltip::Content::MarkupContent(
2001 proto::MarkupContent {
2002 kind: markup_content.kind,
2003 value: markup_content.value,
2004 },
2005 )
2006 }
2007 };
2008 proto::InlayHintTooltip {
2009 content: Some(proto_tooltip),
2010 }
2011 }),
2012 })
2013 .collect(),
2014 version: serialize_version(buffer_version),
2015 }
2016 }
2017
2018 async fn response_from_proto(
2019 self,
2020 message: proto::InlayHintsResponse,
2021 project: ModelHandle<Project>,
2022 buffer: ModelHandle<Buffer>,
2023 mut cx: AsyncAppContext,
2024 ) -> Result<Vec<InlayHint>> {
2025 buffer
2026 .update(&mut cx, |buffer, _| {
2027 buffer.wait_for_version(deserialize_version(&message.version))
2028 })
2029 .await?;
2030
2031 let mut hints = Vec::new();
2032 for message_hint in message.hints {
2033 let buffer_id = message_hint
2034 .position
2035 .as_ref()
2036 .and_then(|location| location.buffer_id)
2037 .context("missing buffer id")?;
2038 let hint = InlayHint {
2039 buffer_id,
2040 position: message_hint
2041 .position
2042 .and_then(language::proto::deserialize_anchor)
2043 .context("invalid position")?,
2044 label: match message_hint
2045 .label
2046 .and_then(|label| label.label)
2047 .context("missing label")?
2048 {
2049 proto::inlay_hint_label::Label::Value(s) => InlayHintLabel::String(s),
2050 proto::inlay_hint_label::Label::LabelParts(parts) => {
2051 let mut label_parts = Vec::new();
2052 for part in parts.parts {
2053 label_parts.push(InlayHintLabelPart {
2054 value: part.value,
2055 tooltip: part.tooltip.map(|tooltip| match tooltip.content {
2056 Some(proto::inlay_hint_label_part_tooltip::Content::Value(s)) => InlayHintLabelPartTooltip::String(s),
2057 Some(proto::inlay_hint_label_part_tooltip::Content::MarkupContent(markup_content)) => InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
2058 kind: markup_content.kind,
2059 value: markup_content.value,
2060 }),
2061 None => InlayHintLabelPartTooltip::String(String::new()),
2062 }),
2063 location: match part.location {
2064 Some(location) => {
2065 let target_buffer = project
2066 .update(&mut cx, |this, cx| {
2067 this.wait_for_remote_buffer(location.buffer_id, cx)
2068 })
2069 .await?;
2070 Some(Location {
2071 range: location
2072 .start
2073 .and_then(language::proto::deserialize_anchor)
2074 .context("invalid start")?
2075 ..location
2076 .end
2077 .and_then(language::proto::deserialize_anchor)
2078 .context("invalid end")?,
2079 buffer: target_buffer,
2080 })},
2081 None => None,
2082 },
2083 });
2084 }
2085
2086 InlayHintLabel::LabelParts(label_parts)
2087 }
2088 },
2089 padding_left: message_hint.padding_left,
2090 padding_right: message_hint.padding_right,
2091 kind: message_hint
2092 .kind
2093 .as_deref()
2094 .and_then(InlayHintKind::from_name),
2095 tooltip: message_hint.tooltip.and_then(|tooltip| {
2096 Some(match tooltip.content? {
2097 proto::inlay_hint_tooltip::Content::Value(s) => InlayHintTooltip::String(s),
2098 proto::inlay_hint_tooltip::Content::MarkupContent(markup_content) => {
2099 InlayHintTooltip::MarkupContent(MarkupContent {
2100 kind: markup_content.kind,
2101 value: markup_content.value,
2102 })
2103 }
2104 })
2105 }),
2106 };
2107
2108 hints.push(hint);
2109 }
2110
2111 Ok(hints)
2112 }
2113
2114 fn buffer_id_from_proto(message: &proto::InlayHints) -> u64 {
2115 message.buffer_id
2116 }
2117}