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