3 base_db::{FileId, FilePosition, FileRange, SourceDatabase},
4 RootDatabase, SymbolKind,
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,
17 // Feature: Annotations
19 // Provides user with annotations above items for looking up references or impl blocks
20 // and running/debugging binaries.
22 pub struct Annotation {
24 pub kind: AnnotationKind,
28 pub enum AnnotationKind {
29 Runnable { debug: bool, runnable: Runnable },
30 HasImpls { position: FilePosition, data: Option<Vec<NavigationTarget>> },
31 HasReferences { position: FilePosition, data: Option<Vec<FileRange>> },
34 pub 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,
44 pub(crate) fn annotations(
47 config: AnnotationConfig,
48 ) -> Vec<Annotation> {
49 let mut annotations = Vec::default();
51 if config.annotate_runnables {
52 for runnable in runnables(db, file_id) {
53 if should_skip_runnable(&runnable.kind, config.binary_target) {
57 let action = runnable.action();
58 let range = runnable.nav.full_range;
61 annotations.push(Annotation {
64 // FIXME: This one allocates without reason if run is enabled, but debug is disabled
65 kind: AnnotationKind::Runnable { debug: false, runnable: runnable.clone() },
69 if action.debugee && config.debug {
70 annotations.push(Annotation {
72 kind: AnnotationKind::Runnable { debug: true, runnable },
78 file_structure(&db.parse(file_id).tree())
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() },
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() },
112 if config.annotate_method_references {
113 annotations.extend(find_all_methods(db, file_id).into_iter().map(|method| Annotation {
115 kind: AnnotationKind::HasReferences {
116 position: FilePosition { file_id, offset: method.range.start() },
125 pub(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);
130 AnnotationKind::HasReferences { position, ref mut data } => {
131 *data = find_all_refs(&Semantics::new(db), position, None).map(|result| {
135 .map(|(file_id, access)| {
136 access.into_iter().map(move |(range, _)| FileRange { file_id, range })
148 fn should_skip_runnable(kind: &RunnableKind, binary_target: bool) -> bool {
150 RunnableKind::Bin => !binary_target,
157 use expect_test::{expect, Expect};
159 use crate::{fixture, Annotation, AnnotationConfig};
161 fn check(ra_fixture: &str, expect: Expect) {
162 let (analysis, file_id) = fixture::file(ra_fixture);
164 let annotations: Vec<Annotation> = analysis
169 annotate_runnables: true,
170 annotate_impls: true,
171 annotate_references: true,
172 annotate_method_references: true,
179 .map(|annotation| analysis.resolve_annotation(annotation).unwrap())
182 expect.assert_debug_eq(&annotations);
186 fn const_annotations() {
189 const DEMO: i32 = 123;
191 const UNUSED: i32 = 123;
204 nav: NavigationTarget {
223 nav: NavigationTarget {
239 kind: HasReferences {
240 position: FilePosition {
260 kind: HasReferences {
261 position: FilePosition {
274 kind: HasReferences {
275 position: FilePosition {
292 fn struct_references_annotations() {
308 nav: NavigationTarget {
327 nav: NavigationTarget {
344 position: FilePosition {
357 kind: HasReferences {
358 position: FilePosition {
378 kind: HasReferences {
379 position: FilePosition {
396 fn struct_and_trait_impls_annotations() {
403 impl MyCoolTrait for Test {}
416 nav: NavigationTarget {
435 nav: NavigationTarget {
452 position: FilePosition {
475 kind: HasReferences {
476 position: FilePosition {
503 position: FilePosition {
526 kind: HasReferences {
527 position: FilePosition {
547 kind: HasReferences {
548 position: FilePosition {
565 fn runnable_annotation() {
577 nav: NavigationTarget {
596 nav: NavigationTarget {
612 kind: HasReferences {
613 position: FilePosition {
630 fn method_annotations() {
636 fn self_by_ref(&self) {}
650 nav: NavigationTarget {
669 nav: NavigationTarget {
686 position: FilePosition {
709 kind: HasReferences {
710 position: FilePosition {
736 kind: HasReferences {
737 position: FilePosition {
757 kind: HasReferences {
758 position: FilePosition {
775 fn test_annotations() {
792 nav: NavigationTarget {
811 nav: NavigationTarget {
830 nav: NavigationTarget {
851 nav: NavigationTarget {
872 nav: NavigationTarget {
878 name: "my_cool_test",
883 "tests::my_cool_test",
898 nav: NavigationTarget {
904 name: "my_cool_test",
909 "tests::my_cool_test",
921 kind: HasReferences {
922 position: FilePosition {