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