]> git.lizzy.rs Git - rust.git/blob - crates/assists/src/tests.rs
Merge #5749
[rust.git] / crates / assists / src / tests.rs
1 mod generated;
2
3 use base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt};
4 use hir::Semantics;
5 use ide_db::RootDatabase;
6 use syntax::TextRange;
7 use test_utils::{assert_eq_text, extract_offset, extract_range};
8
9 use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists};
10 use stdx::trim_indent;
11
12 pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
13     RootDatabase::with_single_file(text)
14 }
15
16 pub(crate) fn check_assist(assist: Handler, ra_fixture_before: &str, ra_fixture_after: &str) {
17     let ra_fixture_after = trim_indent(ra_fixture_after);
18     check(assist, ra_fixture_before, ExpectedResult::After(&ra_fixture_after));
19 }
20
21 // FIXME: instead of having a separate function here, maybe use
22 // `extract_ranges` and mark the target as `<target> </target>` in the
23 // fixture?
24 pub(crate) fn check_assist_target(assist: Handler, ra_fixture: &str, target: &str) {
25     check(assist, ra_fixture, ExpectedResult::Target(target));
26 }
27
28 pub(crate) fn check_assist_not_applicable(assist: Handler, ra_fixture: &str) {
29     check(assist, ra_fixture, ExpectedResult::NotApplicable);
30 }
31
32 fn check_doc_test(assist_id: &str, before: &str, after: &str) {
33     let after = trim_indent(after);
34     let (db, file_id, selection) = RootDatabase::with_range_or_offset(&before);
35     let before = db.file_text(file_id).to_string();
36     let frange = FileRange { file_id, range: selection.into() };
37
38     let mut assist = Assist::resolved(&db, &AssistConfig::default(), frange)
39         .into_iter()
40         .find(|assist| assist.assist.id.0 == assist_id)
41         .unwrap_or_else(|| {
42             panic!(
43                 "\n\nAssist is not applicable: {}\nAvailable assists: {}",
44                 assist_id,
45                 Assist::resolved(&db, &AssistConfig::default(), frange)
46                     .into_iter()
47                     .map(|assist| assist.assist.id.0)
48                     .collect::<Vec<_>>()
49                     .join(", ")
50             )
51         });
52
53     let actual = {
54         let change = assist.source_change.source_file_edits.pop().unwrap();
55         let mut actual = before;
56         change.edit.apply(&mut actual);
57         actual
58     };
59     assert_eq_text!(&after, &actual);
60 }
61
62 enum ExpectedResult<'a> {
63     NotApplicable,
64     After(&'a str),
65     Target(&'a str),
66 }
67
68 fn check(handler: Handler, before: &str, expected: ExpectedResult) {
69     let (db, file_with_caret_id, range_or_offset) = RootDatabase::with_range_or_offset(before);
70     let text_without_caret = db.file_text(file_with_caret_id).to_string();
71
72     let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() };
73
74     let sema = Semantics::new(&db);
75     let config = AssistConfig::default();
76     let ctx = AssistContext::new(sema, &config, frange);
77     let mut acc = Assists::new_resolved(&ctx);
78     handler(&mut acc, &ctx);
79     let mut res = acc.finish_resolved();
80     let assist = res.pop();
81     match (assist, expected) {
82         (Some(assist), ExpectedResult::After(after)) => {
83             let mut source_change = assist.source_change;
84             let change = source_change.source_file_edits.pop().unwrap();
85
86             let mut actual = db.file_text(change.file_id).as_ref().to_owned();
87             change.edit.apply(&mut actual);
88             assert_eq_text!(after, &actual);
89         }
90         (Some(assist), ExpectedResult::Target(target)) => {
91             let range = assist.assist.target;
92             assert_eq_text!(&text_without_caret[range], target);
93         }
94         (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"),
95         (None, ExpectedResult::After(_)) | (None, ExpectedResult::Target(_)) => {
96             panic!("code action is not applicable")
97         }
98         (None, ExpectedResult::NotApplicable) => (),
99     };
100 }
101
102 #[test]
103 fn assist_order_field_struct() {
104     let before = "struct Foo { <|>bar: u32 }";
105     let (before_cursor_pos, before) = extract_offset(before);
106     let (db, file_id) = with_single_file(&before);
107     let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) };
108     let assists = Assist::resolved(&db, &AssistConfig::default(), frange);
109     let mut assists = assists.iter();
110
111     assert_eq!(
112         assists.next().expect("expected assist").assist.label,
113         "Change visibility to pub(crate)"
114     );
115     assert_eq!(assists.next().expect("expected assist").assist.label, "Add `#[derive]`");
116 }
117
118 #[test]
119 fn assist_order_if_expr() {
120     let before = "
121     pub fn test_some_range(a: int) -> bool {
122         if let 2..6 = <|>5<|> {
123             true
124         } else {
125             false
126         }
127     }";
128     let (range, before) = extract_range(before);
129     let (db, file_id) = with_single_file(&before);
130     let frange = FileRange { file_id, range };
131     let assists = Assist::resolved(&db, &AssistConfig::default(), frange);
132     let mut assists = assists.iter();
133
134     assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable");
135     assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match");
136 }
137
138 #[test]
139 fn assist_filter_works() {
140     let before = "
141     pub fn test_some_range(a: int) -> bool {
142         if let 2..6 = <|>5<|> {
143             true
144         } else {
145             false
146         }
147     }";
148     let (range, before) = extract_range(before);
149     let (db, file_id) = with_single_file(&before);
150     let frange = FileRange { file_id, range };
151
152     {
153         let mut cfg = AssistConfig::default();
154         cfg.allowed = Some(vec![AssistKind::Refactor]);
155
156         let assists = Assist::resolved(&db, &cfg, frange);
157         let mut assists = assists.iter();
158
159         assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable");
160         assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match");
161     }
162
163     {
164         let mut cfg = AssistConfig::default();
165         cfg.allowed = Some(vec![AssistKind::RefactorExtract]);
166         let assists = Assist::resolved(&db, &cfg, frange);
167         assert_eq!(assists.len(), 1);
168
169         let mut assists = assists.iter();
170         assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable");
171     }
172
173     {
174         let mut cfg = AssistConfig::default();
175         cfg.allowed = Some(vec![AssistKind::QuickFix]);
176         let assists = Assist::resolved(&db, &cfg, frange);
177         assert!(assists.is_empty(), "All asserts but quickfixes should be filtered out");
178     }
179 }