aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ide/src/annotations.rs937
-rw-r--r--crates/ide/src/lib.rs14
-rw-r--r--crates/rust-analyzer/src/from_proto.rs40
-rw-r--r--crates/rust-analyzer/src/handlers.rs228
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs8
-rw-r--r--crates/rust-analyzer/src/to_proto.rs145
6 files changed, 1171 insertions, 201 deletions
diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs
new file mode 100644
index 000000000..414a60bed
--- /dev/null
+++ b/crates/ide/src/annotations.rs
@@ -0,0 +1,937 @@
1use hir::Semantics;
2use ide_db::{
3 base_db::{FileId, FilePosition, FileRange, SourceDatabase},
4 RootDatabase, SymbolKind,
5};
6use syntax::TextRange;
7
8use crate::{
9 file_structure::file_structure,
10 fn_references::find_all_methods,
11 goto_implementation::goto_implementation,
12 references::find_all_refs,
13 runnables::{runnables, Runnable},
14 NavigationTarget, RunnableKind,
15};
16
17// Feature: Annotations
18//
19// Provides user with annotations above items for looking up references or impl blocks
20// and running/debugging binaries.
21#[derive(Debug)]
22pub struct Annotation {
23 pub range: TextRange,
24 pub kind: AnnotationKind,
25}
26
27#[derive(Debug)]
28pub enum AnnotationKind {
29 Runnable { debug: bool, runnable: Runnable },
30 HasImpls { position: FilePosition, data: Option<Vec<NavigationTarget>> },
31 HasReferences { position: FilePosition, data: Option<Vec<FileRange>> },
32}
33
34pub struct AnnotationConfig {
35 pub binary_target: bool,
36 pub annotate_runnables: bool,
37 pub annotate_impls: bool,
38 pub annotate_references: bool,
39 pub annotate_method_references: bool,
40 pub run: bool,
41 pub debug: bool,
42}
43
44pub(crate) fn annotations(
45 db: &RootDatabase,
46 file_id: FileId,
47 config: AnnotationConfig,
48) -> Vec<Annotation> {
49 let mut annotations = Vec::default();
50
51 if config.annotate_runnables {
52 for runnable in runnables(db, file_id) {
53 if should_skip_runnable(&runnable.kind, config.binary_target) {
54 continue;
55 }
56
57 let action = runnable.action();
58 let range = runnable.nav.full_range;
59
60 if action.debugee && config.debug {
61 annotations.push(Annotation {
62 range,
63
64 // FIXME: This one allocates without reason if run is enabled, but debug is disabled
65 kind: AnnotationKind::Runnable { debug: true, runnable: runnable.clone() },
66 });
67 }
68
69 if config.run {
70 annotations.push(Annotation {
71 range,
72 kind: AnnotationKind::Runnable { debug: false, runnable },
73 });
74 }
75 }
76 }
77
78 file_structure(&db.parse(file_id).tree())
79 .into_iter()
80 .filter(|node| {
81 matches!(
82 node.kind,
83 SymbolKind::Trait
84 | SymbolKind::Struct
85 | SymbolKind::Enum
86 | SymbolKind::Union
87 | SymbolKind::Const
88 )
89 })
90 .for_each(|node| {
91 if config.annotate_impls && node.kind != SymbolKind::Const {
92 annotations.push(Annotation {
93 range: node.node_range,
94 kind: AnnotationKind::HasImpls {
95 position: FilePosition { file_id, offset: node.navigation_range.start() },
96 data: None,
97 },
98 });
99 }
100
101 if config.annotate_references {
102 annotations.push(Annotation {
103 range: node.node_range,
104 kind: AnnotationKind::HasReferences {
105 position: FilePosition { file_id, offset: node.navigation_range.start() },
106 data: None,
107 },
108 });
109 }
110 });
111
112 if config.annotate_method_references {
113 annotations.extend(find_all_methods(db, file_id).into_iter().map(|method| Annotation {
114 range: method.range,
115 kind: AnnotationKind::HasReferences {
116 position: FilePosition { file_id, offset: method.range.start() },
117 data: None,
118 },
119 }));
120 }
121
122 annotations
123}
124
125pub(crate) fn resolve_annotation(db: &RootDatabase, mut annotation: Annotation) -> Annotation {
126 match annotation.kind {
127 AnnotationKind::HasImpls { position, ref mut data } => {
128 *data = goto_implementation(db, position).map(|range| range.info);
129 }
130 AnnotationKind::HasReferences { position, ref mut data } => {
131 *data = find_all_refs(&Semantics::new(db), position, None).map(|result| {
132 result
133 .references
134 .into_iter()
135 .map(|(file_id, access)| {
136 access.into_iter().map(move |(range, _)| FileRange { file_id, range })
137 })
138 .flatten()
139 .collect()
140 });
141 }
142 _ => {}
143 };
144
145 annotation
146}
147
148fn should_skip_runnable(kind: &RunnableKind, binary_target: bool) -> bool {
149 match kind {
150 RunnableKind::Bin => !binary_target,
151 _ => false,
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use expect_test::{expect, Expect};
158
159 use crate::{fixture, Annotation, AnnotationConfig};
160
161 fn check(ra_fixture: &str, expect: Expect) {
162 let (analysis, file_id) = fixture::file(ra_fixture);
163
164 let annotations: Vec<Annotation> = analysis
165 .annotations(
166 file_id,
167 AnnotationConfig {
168 binary_target: true,
169 annotate_runnables: true,
170 annotate_impls: true,
171 annotate_references: true,
172 annotate_method_references: true,
173 run: true,
174 debug: true,
175 },
176 )
177 .unwrap()
178 .into_iter()
179 .map(|annotation| analysis.resolve_annotation(annotation).unwrap())
180 .collect();
181
182 expect.assert_debug_eq(&annotations);
183 }
184
185 #[test]
186 fn const_annotations() {
187 check(
188 r#"
189const DEMO: i32 = 123;
190
191const UNUSED: i32 = 123;
192
193fn main() {
194 let hello = DEMO;
195}
196 "#,
197 expect![[r#"
198 [
199 Annotation {
200 range: 50..85,
201 kind: Runnable {
202 debug: true,
203 runnable: Runnable {
204 nav: NavigationTarget {
205 file_id: FileId(
206 0,
207 ),
208 full_range: 50..85,
209 focus_range: 53..57,
210 name: "main",
211 kind: Function,
212 },
213 kind: Bin,
214 cfg: None,
215 },
216 },
217 },
218 Annotation {
219 range: 50..85,
220 kind: Runnable {
221 debug: false,
222 runnable: Runnable {
223 nav: NavigationTarget {
224 file_id: FileId(
225 0,
226 ),
227 full_range: 50..85,
228 focus_range: 53..57,
229 name: "main",
230 kind: Function,
231 },
232 kind: Bin,
233 cfg: None,
234 },
235 },
236 },
237 Annotation {
238 range: 0..22,
239 kind: HasReferences {
240 position: FilePosition {
241 file_id: FileId(
242 0,
243 ),
244 offset: 6,
245 },
246 data: Some(
247 [
248 FileRange {
249 file_id: FileId(
250 0,
251 ),
252 range: 78..82,
253 },
254 ],
255 ),
256 },
257 },
258 Annotation {
259 range: 24..48,
260 kind: HasReferences {
261 position: FilePosition {
262 file_id: FileId(
263 0,
264 ),
265 offset: 30,
266 },
267 data: Some(
268 [],
269 ),
270 },
271 },
272 Annotation {
273 range: 53..57,
274 kind: HasReferences {
275 position: FilePosition {
276 file_id: FileId(
277 0,
278 ),
279 offset: 53,
280 },
281 data: Some(
282 [],
283 ),
284 },
285 },
286 ]
287 "#]],
288 );
289 }
290
291 #[test]
292 fn struct_references_annotations() {
293 check(
294 r#"
295struct Test;
296
297fn main() {
298 let test = Test;
299}
300 "#,
301 expect![[r#"
302 [
303 Annotation {
304 range: 14..48,
305 kind: Runnable {
306 debug: true,
307 runnable: Runnable {
308 nav: NavigationTarget {
309 file_id: FileId(
310 0,
311 ),
312 full_range: 14..48,
313 focus_range: 17..21,
314 name: "main",
315 kind: Function,
316 },
317 kind: Bin,
318 cfg: None,
319 },
320 },
321 },
322 Annotation {
323 range: 14..48,
324 kind: Runnable {
325 debug: false,
326 runnable: Runnable {
327 nav: NavigationTarget {
328 file_id: FileId(
329 0,
330 ),
331 full_range: 14..48,
332 focus_range: 17..21,
333 name: "main",
334 kind: Function,
335 },
336 kind: Bin,
337 cfg: None,
338 },
339 },
340 },
341 Annotation {
342 range: 0..12,
343 kind: HasImpls {
344 position: FilePosition {
345 file_id: FileId(
346 0,
347 ),
348 offset: 7,
349 },
350 data: Some(
351 [],
352 ),
353 },
354 },
355 Annotation {
356 range: 0..12,
357 kind: HasReferences {
358 position: FilePosition {
359 file_id: FileId(
360 0,
361 ),
362 offset: 7,
363 },
364 data: Some(
365 [
366 FileRange {
367 file_id: FileId(
368 0,
369 ),
370 range: 41..45,
371 },
372 ],
373 ),
374 },
375 },
376 Annotation {
377 range: 17..21,
378 kind: HasReferences {
379 position: FilePosition {
380 file_id: FileId(
381 0,
382 ),
383 offset: 17,
384 },
385 data: Some(
386 [],
387 ),
388 },
389 },
390 ]
391 "#]],
392 );
393 }
394
395 #[test]
396 fn struct_and_trait_impls_annotations() {
397 check(
398 r#"
399struct Test;
400
401trait MyCoolTrait {}
402
403impl MyCoolTrait for Test {}
404
405fn main() {
406 let test = Test;
407}
408 "#,
409 expect![[r#"
410 [
411 Annotation {
412 range: 66..100,
413 kind: Runnable {
414 debug: true,
415 runnable: Runnable {
416 nav: NavigationTarget {
417 file_id: FileId(
418 0,
419 ),
420 full_range: 66..100,
421 focus_range: 69..73,
422 name: "main",
423 kind: Function,
424 },
425 kind: Bin,
426 cfg: None,
427 },
428 },
429 },
430 Annotation {
431 range: 66..100,
432 kind: Runnable {
433 debug: false,
434 runnable: Runnable {
435 nav: NavigationTarget {
436 file_id: FileId(
437 0,
438 ),
439 full_range: 66..100,
440 focus_range: 69..73,
441 name: "main",
442 kind: Function,
443 },
444 kind: Bin,
445 cfg: None,
446 },
447 },
448 },
449 Annotation {
450 range: 0..12,
451 kind: HasImpls {
452 position: FilePosition {
453 file_id: FileId(
454 0,
455 ),
456 offset: 7,
457 },
458 data: Some(
459 [
460 NavigationTarget {
461 file_id: FileId(
462 0,
463 ),
464 full_range: 36..64,
465 focus_range: 57..61,
466 name: "impl",
467 kind: Impl,
468 },
469 ],
470 ),
471 },
472 },
473 Annotation {
474 range: 0..12,
475 kind: HasReferences {
476 position: FilePosition {
477 file_id: FileId(
478 0,
479 ),
480 offset: 7,
481 },
482 data: Some(
483 [
484 FileRange {
485 file_id: FileId(
486 0,
487 ),
488 range: 57..61,
489 },
490 FileRange {
491 file_id: FileId(
492 0,
493 ),
494 range: 93..97,
495 },
496 ],
497 ),
498 },
499 },
500 Annotation {
501 range: 14..34,
502 kind: HasImpls {
503 position: FilePosition {
504 file_id: FileId(
505 0,
506 ),
507 offset: 20,
508 },
509 data: Some(
510 [
511 NavigationTarget {
512 file_id: FileId(
513 0,
514 ),
515 full_range: 36..64,
516 focus_range: 57..61,
517 name: "impl",
518 kind: Impl,
519 },
520 ],
521 ),
522 },
523 },
524 Annotation {
525 range: 14..34,
526 kind: HasReferences {
527 position: FilePosition {
528 file_id: FileId(
529 0,
530 ),
531 offset: 20,
532 },
533 data: Some(
534 [
535 FileRange {
536 file_id: FileId(
537 0,
538 ),
539 range: 41..52,
540 },
541 ],
542 ),
543 },
544 },
545 Annotation {
546 range: 69..73,
547 kind: HasReferences {
548 position: FilePosition {
549 file_id: FileId(
550 0,
551 ),
552 offset: 69,
553 },
554 data: Some(
555 [],
556 ),
557 },
558 },
559 ]
560 "#]],
561 );
562 }
563
564 #[test]
565 fn runnable_annotation() {
566 check(
567 r#"
568fn main() {}
569 "#,
570 expect![[r#"
571 [
572 Annotation {
573 range: 0..12,
574 kind: Runnable {
575 debug: true,
576 runnable: Runnable {
577 nav: NavigationTarget {
578 file_id: FileId(
579 0,
580 ),
581 full_range: 0..12,
582 focus_range: 3..7,
583 name: "main",
584 kind: Function,
585 },
586 kind: Bin,
587 cfg: None,
588 },
589 },
590 },
591 Annotation {
592 range: 0..12,
593 kind: Runnable {
594 debug: false,
595 runnable: Runnable {
596 nav: NavigationTarget {
597 file_id: FileId(
598 0,
599 ),
600 full_range: 0..12,
601 focus_range: 3..7,
602 name: "main",
603 kind: Function,
604 },
605 kind: Bin,
606 cfg: None,
607 },
608 },
609 },
610 Annotation {
611 range: 3..7,
612 kind: HasReferences {
613 position: FilePosition {
614 file_id: FileId(
615 0,
616 ),
617 offset: 3,
618 },
619 data: Some(
620 [],
621 ),
622 },
623 },
624 ]
625 "#]],
626 );
627 }
628
629 #[test]
630 fn method_annotations() {
631 check(
632 r#"
633struct Test;
634
635impl Test {
636 fn self_by_ref(&self) {}
637}
638
639fn main() {
640 Test.self_by_ref();
641}
642 "#,
643 expect![[r#"
644 [
645 Annotation {
646 range: 58..95,
647 kind: Runnable {
648 debug: true,
649 runnable: Runnable {
650 nav: NavigationTarget {
651 file_id: FileId(
652 0,
653 ),
654 full_range: 58..95,
655 focus_range: 61..65,
656 name: "main",
657 kind: Function,
658 },
659 kind: Bin,
660 cfg: None,
661 },
662 },
663 },
664 Annotation {
665 range: 58..95,
666 kind: Runnable {
667 debug: false,
668 runnable: Runnable {
669 nav: NavigationTarget {
670 file_id: FileId(
671 0,
672 ),
673 full_range: 58..95,
674 focus_range: 61..65,
675 name: "main",
676 kind: Function,
677 },
678 kind: Bin,
679 cfg: None,
680 },
681 },
682 },
683 Annotation {
684 range: 0..12,
685 kind: HasImpls {
686 position: FilePosition {
687 file_id: FileId(
688 0,
689 ),
690 offset: 7,
691 },
692 data: Some(
693 [
694 NavigationTarget {
695 file_id: FileId(
696 0,
697 ),
698 full_range: 14..56,
699 focus_range: 19..23,
700 name: "impl",
701 kind: Impl,
702 },
703 ],
704 ),
705 },
706 },
707 Annotation {
708 range: 0..12,
709 kind: HasReferences {
710 position: FilePosition {
711 file_id: FileId(
712 0,
713 ),
714 offset: 7,
715 },
716 data: Some(
717 [
718 FileRange {
719 file_id: FileId(
720 0,
721 ),
722 range: 19..23,
723 },
724 FileRange {
725 file_id: FileId(
726 0,
727 ),
728 range: 74..78,
729 },
730 ],
731 ),
732 },
733 },
734 Annotation {
735 range: 33..44,
736 kind: HasReferences {
737 position: FilePosition {
738 file_id: FileId(
739 0,
740 ),
741 offset: 33,
742 },
743 data: Some(
744 [
745 FileRange {
746 file_id: FileId(
747 0,
748 ),
749 range: 79..90,
750 },
751 ],
752 ),
753 },
754 },
755 Annotation {
756 range: 61..65,
757 kind: HasReferences {
758 position: FilePosition {
759 file_id: FileId(
760 0,
761 ),
762 offset: 61,
763 },
764 data: Some(
765 [],
766 ),
767 },
768 },
769 ]
770 "#]],
771 );
772 }
773
774 #[test]
775 fn test_annotations() {
776 check(
777 r#"
778fn main() {}
779
780mod tests {
781 #[test]
782 fn my_cool_test() {}
783}
784 "#,
785 expect![[r#"
786 [
787 Annotation {
788 range: 0..12,
789 kind: Runnable {
790 debug: true,
791 runnable: Runnable {
792 nav: NavigationTarget {
793 file_id: FileId(
794 0,
795 ),
796 full_range: 0..12,
797 focus_range: 3..7,
798 name: "main",
799 kind: Function,
800 },
801 kind: Bin,
802 cfg: None,
803 },
804 },
805 },
806 Annotation {
807 range: 0..12,
808 kind: Runnable {
809 debug: false,
810 runnable: Runnable {
811 nav: NavigationTarget {
812 file_id: FileId(
813 0,
814 ),
815 full_range: 0..12,
816 focus_range: 3..7,
817 name: "main",
818 kind: Function,
819 },
820 kind: Bin,
821 cfg: None,
822 },
823 },
824 },
825 Annotation {
826 range: 14..64,
827 kind: Runnable {
828 debug: true,
829 runnable: Runnable {
830 nav: NavigationTarget {
831 file_id: FileId(
832 0,
833 ),
834 full_range: 14..64,
835 focus_range: 18..23,
836 name: "tests",
837 kind: Module,
838 },
839 kind: TestMod {
840 path: "tests",
841 },
842 cfg: None,
843 },
844 },
845 },
846 Annotation {
847 range: 14..64,
848 kind: Runnable {
849 debug: false,
850 runnable: Runnable {
851 nav: NavigationTarget {
852 file_id: FileId(
853 0,
854 ),
855 full_range: 14..64,
856 focus_range: 18..23,
857 name: "tests",
858 kind: Module,
859 },
860 kind: TestMod {
861 path: "tests",
862 },
863 cfg: None,
864 },
865 },
866 },
867 Annotation {
868 range: 30..62,
869 kind: Runnable {
870 debug: true,
871 runnable: Runnable {
872 nav: NavigationTarget {
873 file_id: FileId(
874 0,
875 ),
876 full_range: 30..62,
877 focus_range: 45..57,
878 name: "my_cool_test",
879 kind: Function,
880 },
881 kind: Test {
882 test_id: Path(
883 "tests::my_cool_test",
884 ),
885 attr: TestAttr {
886 ignore: false,
887 },
888 },
889 cfg: None,
890 },
891 },
892 },
893 Annotation {
894 range: 30..62,
895 kind: Runnable {
896 debug: false,
897 runnable: Runnable {
898 nav: NavigationTarget {
899 file_id: FileId(
900 0,
901 ),
902 full_range: 30..62,
903 focus_range: 45..57,
904 name: "my_cool_test",
905 kind: Function,
906 },
907 kind: Test {
908 test_id: Path(
909 "tests::my_cool_test",
910 ),
911 attr: TestAttr {
912 ignore: false,
913 },
914 },
915 cfg: None,
916 },
917 },
918 },
919 Annotation {
920 range: 3..7,
921 kind: HasReferences {
922 position: FilePosition {
923 file_id: FileId(
924 0,
925 ),
926 offset: 3,
927 },
928 data: Some(
929 [],
930 ),
931 },
932 },
933 ]
934 "#]],
935 );
936 }
937}
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 592b12925..89e7bef7d 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -22,6 +22,7 @@ mod markup;
22mod prime_caches; 22mod prime_caches;
23mod display; 23mod display;
24 24
25mod annotations;
25mod call_hierarchy; 26mod call_hierarchy;
26mod diagnostics; 27mod diagnostics;
27mod expand_macro; 28mod expand_macro;
@@ -63,6 +64,7 @@ use syntax::SourceFile;
63use crate::display::ToNav; 64use crate::display::ToNav;
64 65
65pub use crate::{ 66pub use crate::{
67 annotations::{Annotation, AnnotationConfig, AnnotationKind},
66 call_hierarchy::CallItem, 68 call_hierarchy::CallItem,
67 diagnostics::{Diagnostic, DiagnosticsConfig, Fix, Severity}, 69 diagnostics::{Diagnostic, DiagnosticsConfig, Fix, Severity},
68 display::navigation_target::NavigationTarget, 70 display::navigation_target::NavigationTarget,
@@ -555,6 +557,18 @@ impl Analysis {
555 }) 557 })
556 } 558 }
557 559
560 pub fn annotations(
561 &self,
562 file_id: FileId,
563 config: AnnotationConfig,
564 ) -> Cancelable<Vec<Annotation>> {
565 self.with_db(|db| annotations::annotations(db, file_id, config))
566 }
567
568 pub fn resolve_annotation(&self, annotation: Annotation) -> Cancelable<Annotation> {
569 self.with_db(|db| annotations::resolve_annotation(db, annotation))
570 }
571
558 /// Performs an operation on that may be Canceled. 572 /// Performs an operation on that may be Canceled.
559 fn with_db<F, T>(&self, f: F) -> Cancelable<T> 573 fn with_db<F, T>(&self, f: F) -> Cancelable<T>
560 where 574 where
diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/from_proto.rs
index aa6b808d6..6676eebf4 100644
--- a/crates/rust-analyzer/src/from_proto.rs
+++ b/crates/rust-analyzer/src/from_proto.rs
@@ -1,12 +1,12 @@
1//! Conversion lsp_types types to rust-analyzer specific ones. 1//! Conversion lsp_types types to rust-analyzer specific ones.
2use std::convert::TryFrom; 2use std::convert::TryFrom;
3 3
4use ide::{AssistKind, LineCol, LineIndex}; 4use ide::{Annotation, AnnotationKind, AssistKind, LineCol, LineIndex};
5use ide_db::base_db::{FileId, FilePosition, FileRange}; 5use ide_db::base_db::{FileId, FilePosition, FileRange};
6use syntax::{TextRange, TextSize}; 6use syntax::{TextRange, TextSize};
7use vfs::AbsPathBuf; 7use vfs::AbsPathBuf;
8 8
9use crate::{global_state::GlobalStateSnapshot, Result}; 9use crate::{from_json, global_state::GlobalStateSnapshot, lsp_ext, Result};
10 10
11pub(crate) fn abs_path(url: &lsp_types::Url) -> Result<AbsPathBuf> { 11pub(crate) fn abs_path(url: &lsp_types::Url) -> Result<AbsPathBuf> {
12 let path = url.to_file_path().map_err(|()| "url is not a file")?; 12 let path = url.to_file_path().map_err(|()| "url is not a file")?;
@@ -66,3 +66,39 @@ pub(crate) fn assist_kind(kind: lsp_types::CodeActionKind) -> Option<AssistKind>
66 66
67 Some(assist_kind) 67 Some(assist_kind)
68} 68}
69
70pub(crate) fn annotation(
71 world: &GlobalStateSnapshot,
72 code_lens: lsp_types::CodeLens,
73) -> Result<Annotation> {
74 let data = code_lens.data.unwrap();
75 let resolve = from_json::<lsp_ext::CodeLensResolveData>("CodeLensResolveData", data)?;
76
77 match resolve {
78 lsp_ext::CodeLensResolveData::Impls(params) => {
79 let file_id =
80 world.url_to_file_id(&params.text_document_position_params.text_document.uri)?;
81 let line_index = world.analysis.file_line_index(file_id)?;
82
83 Ok(Annotation {
84 range: text_range(&line_index, code_lens.range),
85 kind: AnnotationKind::HasImpls {
86 position: file_position(world, params.text_document_position_params)?,
87 data: None,
88 },
89 })
90 }
91 lsp_ext::CodeLensResolveData::References(params) => {
92 let file_id = world.url_to_file_id(&params.text_document.uri)?;
93 let line_index = world.analysis.file_line_index(file_id)?;
94
95 Ok(Annotation {
96 range: text_range(&line_index, code_lens.range),
97 kind: AnnotationKind::HasReferences {
98 position: file_position(world, params)?,
99 data: None,
100 },
101 })
102 }
103 }
104}
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 8898c12e3..b051c8f6c 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -9,8 +9,9 @@ use std::{
9}; 9};
10 10
11use ide::{ 11use ide::{
12 FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, LineIndex, NavigationTarget, 12 AnnotationConfig, FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, LineIndex,
13 Query, RangeInfo, Runnable, RunnableKind, SearchScope, SourceChange, TextEdit, 13 NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope, SourceChange,
14 TextEdit,
14}; 15};
15use ide_db::SymbolKind; 16use ide_db::SymbolKind;
16use itertools::Itertools; 17use itertools::Itertools;
@@ -35,7 +36,7 @@ use crate::{
35 cargo_target_spec::CargoTargetSpec, 36 cargo_target_spec::CargoTargetSpec,
36 config::RustfmtConfig, 37 config::RustfmtConfig,
37 diff::diff, 38 diff::diff,
38 from_json, from_proto, 39 from_proto,
39 global_state::{GlobalState, GlobalStateSnapshot}, 40 global_state::{GlobalState, GlobalStateSnapshot},
40 line_endings::LineEndings, 41 line_endings::LineEndings,
41 lsp_ext::{self, InlayHint, InlayHintsParams}, 42 lsp_ext::{self, InlayHint, InlayHintsParams},
@@ -1078,177 +1079,51 @@ pub(crate) fn handle_code_lens(
1078 params: lsp_types::CodeLensParams, 1079 params: lsp_types::CodeLensParams,
1079) -> Result<Option<Vec<CodeLens>>> { 1080) -> Result<Option<Vec<CodeLens>>> {
1080 let _p = profile::span("handle_code_lens"); 1081 let _p = profile::span("handle_code_lens");
1081 let mut lenses: Vec<CodeLens> = Default::default();
1082 1082
1083 let lens_config = snap.config.lens(); 1083 let lens_config = snap.config.lens();
1084 if lens_config.none() { 1084 if lens_config.none() {
1085 // early return before any db query! 1085 // early return before any db query!
1086 return Ok(Some(lenses)); 1086 return Ok(Some(Vec::default()));
1087 } 1087 }
1088 1088
1089 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?; 1089 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1090 let line_index = snap.analysis.file_line_index(file_id)?; 1090 let cargo_target_spec = CargoTargetSpec::for_file(&snap, file_id)?;
1091 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?;
1092
1093 if lens_config.runnable() {
1094 // Gather runnables
1095 for runnable in snap.analysis.runnables(file_id)? {
1096 if should_skip_target(&runnable, cargo_spec.as_ref()) {
1097 continue;
1098 }
1099
1100 let action = runnable.action();
1101 let range = to_proto::range(&line_index, runnable.nav.full_range);
1102 let r = to_proto::runnable(&snap, file_id, runnable)?;
1103 if lens_config.run {
1104 let lens = CodeLens {
1105 range,
1106 command: Some(run_single_command(&r, action.run_title)),
1107 data: None,
1108 };
1109 lenses.push(lens);
1110 }
1111 1091
1112 if action.debugee && lens_config.debug { 1092 let lenses = snap
1113 let debug_lens = 1093 .analysis
1114 CodeLens { range, command: Some(debug_single_command(&r)), data: None }; 1094 .annotations(
1115 lenses.push(debug_lens); 1095 file_id,
1116 } 1096 AnnotationConfig {
1117 } 1097 binary_target: cargo_target_spec
1118 } 1098 .map(|spec| {
1119 1099 matches!(
1120 if lens_config.implementations || lens_config.refs { 1100 spec.target_kind,
1121 snap.analysis 1101 TargetKind::Bin | TargetKind::Example | TargetKind::Test
1122 .file_structure(file_id)? 1102 )
1123 .into_iter()
1124 .filter(|it| {
1125 matches!(
1126 it.kind,
1127 SymbolKind::Trait | SymbolKind::Struct | SymbolKind::Enum | SymbolKind::Union
1128 )
1129 })
1130 .for_each(|it| {
1131 let range = to_proto::range(&line_index, it.node_range);
1132 let position = to_proto::position(&line_index, it.navigation_range.start());
1133 let doc_pos = lsp_types::TextDocumentPositionParams::new(
1134 params.text_document.clone(),
1135 position,
1136 );
1137 let goto_params = lsp_types::request::GotoImplementationParams {
1138 text_document_position_params: doc_pos.clone(),
1139 work_done_progress_params: Default::default(),
1140 partial_result_params: Default::default(),
1141 };
1142
1143 if lens_config.implementations {
1144 lenses.push(CodeLens {
1145 range,
1146 command: None,
1147 data: Some(to_value(CodeLensResolveData::Impls(goto_params)).unwrap()),
1148 })
1149 }
1150
1151 if lens_config.refs {
1152 lenses.push(CodeLens {
1153 range,
1154 command: None,
1155 data: Some(to_value(CodeLensResolveData::References(doc_pos)).unwrap()),
1156 }) 1103 })
1157 } 1104 .unwrap_or(false),
1158 }); 1105 annotate_runnables: lens_config.runnable(),
1159 } 1106 annotate_impls: lens_config.implementations,
1160 1107 annotate_references: lens_config.refs,
1161 if lens_config.method_refs { 1108 annotate_method_references: lens_config.method_refs,
1162 lenses.extend(snap.analysis.find_all_methods(file_id)?.into_iter().map(|it| { 1109 run: lens_config.run,
1163 let range = to_proto::range(&line_index, it.range); 1110 debug: lens_config.debug,
1164 let position = to_proto::position(&line_index, it.range.start()); 1111 },
1165 let lens_params = 1112 )?
1166 lsp_types::TextDocumentPositionParams::new(params.text_document.clone(), position); 1113 .into_iter()
1167 1114 .map(|annotation| to_proto::code_lens(&snap, annotation).unwrap())
1168 CodeLens { 1115 .collect();
1169 range,
1170 command: None,
1171 data: Some(to_value(CodeLensResolveData::References(lens_params)).unwrap()),
1172 }
1173 }));
1174 }
1175 1116
1176 Ok(Some(lenses)) 1117 Ok(Some(lenses))
1177} 1118}
1178 1119
1179#[derive(Debug, Serialize, Deserialize)]
1180#[serde(rename_all = "camelCase")]
1181enum CodeLensResolveData {
1182 Impls(lsp_types::request::GotoImplementationParams),
1183 References(lsp_types::TextDocumentPositionParams),
1184}
1185
1186pub(crate) fn handle_code_lens_resolve( 1120pub(crate) fn handle_code_lens_resolve(
1187 snap: GlobalStateSnapshot, 1121 snap: GlobalStateSnapshot,
1188 code_lens: CodeLens, 1122 code_lens: CodeLens,
1189) -> Result<CodeLens> { 1123) -> Result<CodeLens> {
1190 let _p = profile::span("handle_code_lens_resolve"); 1124 let annotation = from_proto::annotation(&snap, code_lens)?;
1191 let data = code_lens.data.unwrap();
1192 let resolve = from_json::<Option<CodeLensResolveData>>("CodeLensResolveData", data)?;
1193 match resolve {
1194 Some(CodeLensResolveData::Impls(lens_params)) => {
1195 let locations: Vec<Location> =
1196 match handle_goto_implementation(snap, lens_params.clone())? {
1197 Some(lsp_types::GotoDefinitionResponse::Scalar(loc)) => vec![loc],
1198 Some(lsp_types::GotoDefinitionResponse::Array(locs)) => locs,
1199 Some(lsp_types::GotoDefinitionResponse::Link(links)) => links
1200 .into_iter()
1201 .map(|link| Location::new(link.target_uri, link.target_selection_range))
1202 .collect(),
1203 _ => vec![],
1204 };
1205
1206 let title = implementation_title(locations.len());
1207 let cmd = show_references_command(
1208 title,
1209 &lens_params.text_document_position_params.text_document.uri,
1210 code_lens.range.start,
1211 locations,
1212 );
1213 Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None })
1214 }
1215 Some(CodeLensResolveData::References(doc_position)) => {
1216 let position = from_proto::file_position(&snap, doc_position.clone())?;
1217 let locations = snap
1218 .analysis
1219 .find_all_refs(position, None)
1220 .unwrap_or(None)
1221 .map(|r| {
1222 r.references
1223 .into_iter()
1224 .flat_map(|(file_id, ranges)| {
1225 ranges.into_iter().map(move |(range, _)| FileRange { file_id, range })
1226 })
1227 .filter_map(|frange| to_proto::location(&snap, frange).ok())
1228 .collect_vec()
1229 })
1230 .unwrap_or_default();
1231
1232 let title = reference_title(locations.len());
1233 let cmd = if locations.is_empty() {
1234 Command { title, command: "".into(), arguments: None }
1235 } else {
1236 show_references_command(
1237 title,
1238 &doc_position.text_document.uri,
1239 code_lens.range.start,
1240 locations,
1241 )
1242 };
1243 1125
1244 Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None }) 1126 Ok(to_proto::code_lens(&snap, snap.analysis.resolve_annotation(annotation)?)?)
1245 }
1246 None => Ok(CodeLens {
1247 range: code_lens.range,
1248 command: Some(Command { title: "Error".into(), ..Default::default() }),
1249 data: None,
1250 }),
1251 }
1252} 1127}
1253 1128
1254pub(crate) fn handle_document_highlight( 1129pub(crate) fn handle_document_highlight(
@@ -1547,43 +1422,6 @@ pub(crate) fn handle_open_cargo_toml(
1547 Ok(Some(res)) 1422 Ok(Some(res))
1548} 1423}
1549 1424
1550fn implementation_title(count: usize) -> String {
1551 if count == 1 {
1552 "1 implementation".into()
1553 } else {
1554 format!("{} implementations", count)
1555 }
1556}
1557
1558fn reference_title(count: usize) -> String {
1559 if count == 1 {
1560 "1 reference".into()
1561 } else {
1562 format!("{} references", count)
1563 }
1564}
1565
1566fn show_references_command(
1567 title: String,
1568 uri: &lsp_types::Url,
1569 position: lsp_types::Position,
1570 locations: Vec<lsp_types::Location>,
1571) -> Command {
1572 // We cannot use the 'editor.action.showReferences' command directly
1573 // because that command requires vscode types which we convert in the handler
1574 // on the client side.
1575
1576 Command {
1577 title,
1578 command: "rust-analyzer.showReferences".into(),
1579 arguments: Some(vec![
1580 to_value(uri).unwrap(),
1581 to_value(position).unwrap(),
1582 to_value(locations).unwrap(),
1583 ]),
1584 }
1585}
1586
1587fn run_single_command(runnable: &lsp_ext::Runnable, title: &str) -> Command { 1425fn run_single_command(runnable: &lsp_ext::Runnable, title: &str) -> Command {
1588 Command { 1426 Command {
1589 title: title.to_string(), 1427 title: title.to_string(),
@@ -1635,8 +1473,8 @@ fn show_impl_command_link(
1635 .into_iter() 1473 .into_iter()
1636 .filter_map(|nav| to_proto::location_from_nav(snap, nav).ok()) 1474 .filter_map(|nav| to_proto::location_from_nav(snap, nav).ok())
1637 .collect(); 1475 .collect();
1638 let title = implementation_title(locations.len()); 1476 let title = to_proto::implementation_title(locations.len());
1639 let command = show_references_command(title, &uri, position, locations); 1477 let command = to_proto::show_references_command(title, &uri, position, locations);
1640 1478
1641 return Some(lsp_ext::CommandLinkGroup { 1479 return Some(lsp_ext::CommandLinkGroup {
1642 commands: vec![to_command_link(command, "Go to implementations".into())], 1480 commands: vec![to_command_link(command, "Go to implementations".into())],
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index ce5a0e822..a1ad855c3 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -377,3 +377,11 @@ impl Request for OpenCargoToml {
377pub struct OpenCargoTomlParams { 377pub struct OpenCargoTomlParams {
378 pub text_document: TextDocumentIdentifier, 378 pub text_document: TextDocumentIdentifier,
379} 379}
380
381/// Information about CodeLens, that is to be resolved.
382#[derive(Debug, Serialize, Deserialize)]
383#[serde(rename_all = "camelCase")]
384pub(crate) enum CodeLensResolveData {
385 Impls(lsp_types::request::GotoImplementationParams),
386 References(lsp_types::TextDocumentPositionParams),
387}
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index be10ac1ae..29fac96fb 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -5,13 +5,15 @@ use std::{
5}; 5};
6 6
7use ide::{ 7use ide::{
8 Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId, 8 Annotation, AnnotationKind, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind,
9 FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct, HlRange, HlTag, Indel, 9 Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct,
10 InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, NavigationTarget, ReferenceAccess, 10 HlRange, HlTag, Indel, InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup,
11 RenameError, Runnable, Severity, SourceChange, TextEdit, TextRange, TextSize, 11 NavigationTarget, ReferenceAccess, RenameError, Runnable, Severity, SourceChange, TextEdit,
12 TextRange, TextSize,
12}; 13};
13use ide_db::SymbolKind; 14use ide_db::SymbolKind;
14use itertools::Itertools; 15use itertools::Itertools;
16use serde_json::to_value;
15 17
16use crate::{ 18use crate::{
17 cargo_target_spec::CargoTargetSpec, global_state::GlobalStateSnapshot, 19 cargo_target_spec::CargoTargetSpec, global_state::GlobalStateSnapshot,
@@ -863,6 +865,141 @@ pub(crate) fn runnable(
863 }) 865 })
864} 866}
865 867
868pub(crate) fn code_lens(
869 snap: &GlobalStateSnapshot,
870 annotation: Annotation,
871) -> Result<lsp_types::CodeLens> {
872 match annotation.kind {
873 AnnotationKind::Runnable { debug, runnable: run } => {
874 let line_index = snap.analysis.file_line_index(run.nav.file_id)?;
875 let annotation_range = range(&line_index, annotation.range);
876
877 let action = run.action();
878 let r = runnable(&snap, run.nav.file_id, run)?;
879
880 let command = if debug {
881 lsp_types::Command {
882 title: action.run_title.to_string(),
883 command: "rust-analyzer.runSingle".into(),
884 arguments: Some(vec![to_value(r).unwrap()]),
885 }
886 } else {
887 lsp_types::Command {
888 title: "Debug".into(),
889 command: "rust-analyzer.debugSingle".into(),
890 arguments: Some(vec![to_value(r).unwrap()]),
891 }
892 };
893
894 Ok(lsp_types::CodeLens { range: annotation_range, command: Some(command), data: None })
895 }
896 AnnotationKind::HasImpls { position: file_position, data } => {
897 let line_index = snap.analysis.file_line_index(file_position.file_id)?;
898 let annotation_range = range(&line_index, annotation.range);
899 let url = url(snap, file_position.file_id);
900
901 let position = position(&line_index, file_position.offset);
902
903 let id = lsp_types::TextDocumentIdentifier { uri: url.clone() };
904
905 let doc_pos = lsp_types::TextDocumentPositionParams::new(id.clone(), position);
906
907 let goto_params = lsp_types::request::GotoImplementationParams {
908 text_document_position_params: doc_pos.clone(),
909 work_done_progress_params: Default::default(),
910 partial_result_params: Default::default(),
911 };
912
913 let command = data.map(|ranges| {
914 let locations: Vec<lsp_types::Location> = ranges
915 .into_iter()
916 .filter_map(|target| {
917 location(
918 snap,
919 FileRange { file_id: target.file_id, range: target.full_range },
920 )
921 .ok()
922 })
923 .collect();
924
925 show_references_command(
926 implementation_title(locations.len()),
927 &url,
928 position,
929 locations,
930 )
931 });
932
933 Ok(lsp_types::CodeLens {
934 range: annotation_range,
935 command,
936 data: Some(to_value(lsp_ext::CodeLensResolveData::Impls(goto_params)).unwrap()),
937 })
938 }
939 AnnotationKind::HasReferences { position: file_position, data } => {
940 let line_index = snap.analysis.file_line_index(file_position.file_id)?;
941 let annotation_range = range(&line_index, annotation.range);
942 let url = url(snap, file_position.file_id);
943
944 let position = position(&line_index, file_position.offset);
945
946 let id = lsp_types::TextDocumentIdentifier { uri: url.clone() };
947
948 let doc_pos = lsp_types::TextDocumentPositionParams::new(id, position);
949
950 let command = data.map(|ranges| {
951 let locations: Vec<lsp_types::Location> =
952 ranges.into_iter().filter_map(|range| location(snap, range).ok()).collect();
953
954 show_references_command(reference_title(locations.len()), &url, position, locations)
955 });
956
957 Ok(lsp_types::CodeLens {
958 range: annotation_range,
959 command,
960 data: Some(to_value(lsp_ext::CodeLensResolveData::References(doc_pos)).unwrap()),
961 })
962 }
963 }
964}
965
966pub(crate) fn show_references_command(
967 title: String,
968 uri: &lsp_types::Url,
969 position: lsp_types::Position,
970 locations: Vec<lsp_types::Location>,
971) -> lsp_types::Command {
972 // We cannot use the 'editor.action.showReferences' command directly
973 // because that command requires vscode types which we convert in the handler
974 // on the client side.
975
976 lsp_types::Command {
977 title,
978 command: "rust-analyzer.showReferences".into(),
979 arguments: Some(vec![
980 to_value(uri).unwrap(),
981 to_value(position).unwrap(),
982 to_value(locations).unwrap(),
983 ]),
984 }
985}
986
987pub(crate) fn implementation_title(count: usize) -> String {
988 if count == 1 {
989 "1 implementation".into()
990 } else {
991 format!("{} implementations", count)
992 }
993}
994
995pub(crate) fn reference_title(count: usize) -> String {
996 if count == 1 {
997 "1 reference".into()
998 } else {
999 format!("{} references", count)
1000 }
1001}
1002
866pub(crate) fn markup_content(markup: Markup) -> lsp_types::MarkupContent { 1003pub(crate) fn markup_content(markup: Markup) -> lsp_types::MarkupContent {
867 let value = crate::markdown::format_docs(markup.as_str()); 1004 let value = crate::markdown::format_docs(markup.as_str());
868 lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value } 1005 lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value }