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