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