diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-02-06 14:01:07 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-02-06 14:01:07 +0000 |
commit | f6cf9393a8327b4c0385bccbc5be84a79bd50057 (patch) | |
tree | 4af15c8906b85de01a15c717bc1fac388952cd3d /crates/ra_ide_api/src | |
parent | 736a55c97e69f95e6ff4a0c3dafb2018e8ea05f9 (diff) | |
parent | 0c5fd8f7cbf04eda763e55bc9a38dad5f7ec917d (diff) |
Merge #750
750: Move assists to a separate crate r=matklad a=matklad
I am slowly coming to conclusion that ide_api_light does not make a lot of sense after all :D
This PR moves assists to a separate crate, so that assists can use database (so, inspect types, do name-resolution, etc)
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_ide_api/src')
-rw-r--r-- | crates/ra_ide_api/src/assists.rs | 109 | ||||
-rw-r--r-- | crates/ra_ide_api/src/assists/fill_match_arm.rs | 157 | ||||
-rw-r--r-- | crates/ra_ide_api/src/assists/snapshots/tests__fill_match_arm1.snap | 20 | ||||
-rw-r--r-- | crates/ra_ide_api/src/assists/snapshots/tests__fill_match_arm2.snap | 20 | ||||
-rw-r--r-- | crates/ra_ide_api/src/imp.rs | 11 | ||||
-rw-r--r-- | crates/ra_ide_api/src/lib.rs | 2 |
6 files changed, 24 insertions, 295 deletions
diff --git a/crates/ra_ide_api/src/assists.rs b/crates/ra_ide_api/src/assists.rs index 2da251df5..2a96fdf47 100644 --- a/crates/ra_ide_api/src/assists.rs +++ b/crates/ra_ide_api/src/assists.rs | |||
@@ -1,89 +1,24 @@ | |||
1 | mod fill_match_arm; | 1 | use ra_db::{FileRange, FilePosition}; |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use crate::{SourceFileEdit, SourceChange, db::RootDatabase}; |
4 | TextRange, SourceFile, AstNode, | 4 | |
5 | algo::find_node_at_offset, | 5 | pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<SourceChange> { |
6 | }; | 6 | ra_assists::assists(db, frange) |
7 | use ra_ide_api_light::{ | 7 | .into_iter() |
8 | LocalEdit, | 8 | .map(|(label, action)| { |
9 | assists::{ | 9 | let file_id = frange.file_id; |
10 | Assist, | 10 | let file_edit = SourceFileEdit { |
11 | AssistBuilder | 11 | file_id, |
12 | } | 12 | edit: action.edit, |
13 | }; | 13 | }; |
14 | use crate::{ | 14 | SourceChange { |
15 | db::RootDatabase, | 15 | label: label.label, |
16 | FileId | 16 | source_file_edits: vec![file_edit], |
17 | }; | 17 | file_system_edits: vec![], |
18 | 18 | cursor_position: action | |
19 | /// Return all the assists applicable at the given position. | 19 | .cursor_position |
20 | pub(crate) fn assists( | 20 | .map(|offset| FilePosition { offset, file_id }), |
21 | db: &RootDatabase, | 21 | } |
22 | file_id: FileId, | 22 | }) |
23 | file: &SourceFile, | ||
24 | range: TextRange, | ||
25 | ) -> Vec<LocalEdit> { | ||
26 | let ctx = AssistCtx::new(db, file_id, file, range); | ||
27 | [fill_match_arm::fill_match_arm] | ||
28 | .iter() | ||
29 | .filter_map(|&assist| ctx.clone().apply(assist)) | ||
30 | .collect() | 23 | .collect() |
31 | } | 24 | } |
32 | |||
33 | #[derive(Debug, Clone)] | ||
34 | pub struct AssistCtx<'a> { | ||
35 | file_id: FileId, | ||
36 | source_file: &'a SourceFile, | ||
37 | db: &'a RootDatabase, | ||
38 | range: TextRange, | ||
39 | should_compute_edit: bool, | ||
40 | } | ||
41 | |||
42 | impl<'a> AssistCtx<'a> { | ||
43 | pub(crate) fn new( | ||
44 | db: &'a RootDatabase, | ||
45 | file_id: FileId, | ||
46 | source_file: &'a SourceFile, | ||
47 | range: TextRange, | ||
48 | ) -> AssistCtx<'a> { | ||
49 | AssistCtx { | ||
50 | source_file, | ||
51 | file_id, | ||
52 | db, | ||
53 | range, | ||
54 | should_compute_edit: false, | ||
55 | } | ||
56 | } | ||
57 | |||
58 | pub fn apply(mut self, assist: fn(AssistCtx) -> Option<Assist>) -> Option<LocalEdit> { | ||
59 | self.should_compute_edit = true; | ||
60 | match assist(self) { | ||
61 | None => None, | ||
62 | Some(Assist::Edit(e)) => Some(e), | ||
63 | Some(Assist::Applicable) => unreachable!(), | ||
64 | } | ||
65 | } | ||
66 | |||
67 | #[allow(unused)] | ||
68 | pub fn check(mut self, assist: fn(AssistCtx) -> Option<Assist>) -> bool { | ||
69 | self.should_compute_edit = false; | ||
70 | match assist(self) { | ||
71 | None => false, | ||
72 | Some(Assist::Edit(_)) => unreachable!(), | ||
73 | Some(Assist::Applicable) => true, | ||
74 | } | ||
75 | } | ||
76 | |||
77 | fn build(self, label: impl Into<String>, f: impl FnOnce(&mut AssistBuilder)) -> Option<Assist> { | ||
78 | if !self.should_compute_edit { | ||
79 | return Some(Assist::Applicable); | ||
80 | } | ||
81 | let mut edit = AssistBuilder::default(); | ||
82 | f(&mut edit); | ||
83 | Some(edit.build(label)) | ||
84 | } | ||
85 | |||
86 | pub(crate) fn node_at_offset<N: AstNode>(&self) -> Option<&'a N> { | ||
87 | find_node_at_offset(self.source_file.syntax(), self.range.start()) | ||
88 | } | ||
89 | } | ||
diff --git a/crates/ra_ide_api/src/assists/fill_match_arm.rs b/crates/ra_ide_api/src/assists/fill_match_arm.rs deleted file mode 100644 index 6ae829d85..000000000 --- a/crates/ra_ide_api/src/assists/fill_match_arm.rs +++ /dev/null | |||
@@ -1,157 +0,0 @@ | |||
1 | use std::fmt::Write; | ||
2 | use hir::{ | ||
3 | AdtDef, | ||
4 | source_binder, | ||
5 | Ty, | ||
6 | FieldSource, | ||
7 | }; | ||
8 | use ra_ide_api_light::{ | ||
9 | assists::{ | ||
10 | Assist, | ||
11 | AssistBuilder | ||
12 | } | ||
13 | }; | ||
14 | use ra_syntax::{ | ||
15 | ast::{ | ||
16 | self, | ||
17 | AstNode, | ||
18 | } | ||
19 | }; | ||
20 | |||
21 | use crate::assists::AssistCtx; | ||
22 | |||
23 | pub fn fill_match_arm(ctx: AssistCtx) -> Option<Assist> { | ||
24 | let match_expr = ctx.node_at_offset::<ast::MatchExpr>()?; | ||
25 | |||
26 | // We already have some match arms, so we don't provide any assists. | ||
27 | match match_expr.match_arm_list() { | ||
28 | Some(arm_list) if arm_list.arms().count() > 0 => { | ||
29 | return None; | ||
30 | } | ||
31 | _ => {} | ||
32 | } | ||
33 | |||
34 | let expr = match_expr.expr()?; | ||
35 | let function = source_binder::function_from_child_node(ctx.db, ctx.file_id, expr.syntax())?; | ||
36 | let infer_result = function.infer(ctx.db); | ||
37 | let syntax_mapping = function.body_syntax_mapping(ctx.db); | ||
38 | let node_expr = syntax_mapping.node_expr(expr)?; | ||
39 | let match_expr_ty = infer_result[node_expr].clone(); | ||
40 | match match_expr_ty { | ||
41 | Ty::Adt { def_id, .. } => match def_id { | ||
42 | AdtDef::Enum(e) => { | ||
43 | let mut buf = format!("match {} {{\n", expr.syntax().text().to_string()); | ||
44 | let variants = e.variants(ctx.db); | ||
45 | for variant in variants { | ||
46 | let name = variant.name(ctx.db)?; | ||
47 | write!( | ||
48 | &mut buf, | ||
49 | " {}::{}", | ||
50 | e.name(ctx.db)?.to_string(), | ||
51 | name.to_string() | ||
52 | ) | ||
53 | .expect("write fmt"); | ||
54 | |||
55 | let pat = variant | ||
56 | .fields(ctx.db) | ||
57 | .into_iter() | ||
58 | .map(|field| { | ||
59 | let name = field.name(ctx.db).to_string(); | ||
60 | let (_, source) = field.source(ctx.db); | ||
61 | match source { | ||
62 | FieldSource::Named(_) => name, | ||
63 | FieldSource::Pos(_) => "_".to_string(), | ||
64 | } | ||
65 | }) | ||
66 | .collect::<Vec<_>>(); | ||
67 | |||
68 | match pat.first().map(|s| s.as_str()) { | ||
69 | Some("_") => write!(&mut buf, "({})", pat.join(", ")).expect("write fmt"), | ||
70 | Some(_) => write!(&mut buf, "{{{}}}", pat.join(", ")).expect("write fmt"), | ||
71 | None => (), | ||
72 | }; | ||
73 | |||
74 | buf.push_str(" => (),\n"); | ||
75 | } | ||
76 | buf.push_str("}"); | ||
77 | ctx.build("fill match arms", |edit: &mut AssistBuilder| { | ||
78 | edit.replace_node_and_indent(match_expr.syntax(), buf); | ||
79 | }) | ||
80 | } | ||
81 | _ => None, | ||
82 | }, | ||
83 | _ => None, | ||
84 | } | ||
85 | } | ||
86 | |||
87 | #[cfg(test)] | ||
88 | mod tests { | ||
89 | use insta::assert_debug_snapshot_matches; | ||
90 | |||
91 | use ra_syntax::{TextRange, TextUnit}; | ||
92 | |||
93 | use crate::{ | ||
94 | FileRange, | ||
95 | mock_analysis::{analysis_and_position, single_file_with_position} | ||
96 | }; | ||
97 | use ra_db::SourceDatabase; | ||
98 | |||
99 | fn test_assit(name: &str, code: &str) { | ||
100 | let (analysis, position) = if code.contains("//-") { | ||
101 | analysis_and_position(code) | ||
102 | } else { | ||
103 | single_file_with_position(code) | ||
104 | }; | ||
105 | let frange = FileRange { | ||
106 | file_id: position.file_id, | ||
107 | range: TextRange::offset_len(position.offset, TextUnit::from(1)), | ||
108 | }; | ||
109 | let source_file = analysis | ||
110 | .with_db(|db| db.parse(frange.file_id)) | ||
111 | .expect("source file"); | ||
112 | let ret = analysis | ||
113 | .with_db(|db| crate::assists::assists(db, frange.file_id, &source_file, frange.range)) | ||
114 | .expect("assists"); | ||
115 | |||
116 | assert_debug_snapshot_matches!(name, ret); | ||
117 | } | ||
118 | |||
119 | #[test] | ||
120 | fn test_fill_match_arm() { | ||
121 | test_assit( | ||
122 | "fill_match_arm1", | ||
123 | r#" | ||
124 | enum A { | ||
125 | As, | ||
126 | Bs, | ||
127 | Cs(String), | ||
128 | Ds(String, String), | ||
129 | Es{x: usize, y: usize} | ||
130 | } | ||
131 | |||
132 | fn main() { | ||
133 | let a = A::As; | ||
134 | match a<|> | ||
135 | } | ||
136 | "#, | ||
137 | ); | ||
138 | |||
139 | test_assit( | ||
140 | "fill_match_arm2", | ||
141 | r#" | ||
142 | enum A { | ||
143 | As, | ||
144 | Bs, | ||
145 | Cs(String), | ||
146 | Ds(String, String), | ||
147 | Es{x: usize, y: usize} | ||
148 | } | ||
149 | |||
150 | fn main() { | ||
151 | let a = A::As; | ||
152 | match a<|> {} | ||
153 | } | ||
154 | "#, | ||
155 | ); | ||
156 | } | ||
157 | } | ||
diff --git a/crates/ra_ide_api/src/assists/snapshots/tests__fill_match_arm1.snap b/crates/ra_ide_api/src/assists/snapshots/tests__fill_match_arm1.snap deleted file mode 100644 index 980726d92..000000000 --- a/crates/ra_ide_api/src/assists/snapshots/tests__fill_match_arm1.snap +++ /dev/null | |||
@@ -1,20 +0,0 @@ | |||
1 | --- | ||
2 | created: "2019-02-03T15:38:46.094184+00:00" | ||
3 | creator: [email protected] | ||
4 | expression: ret | ||
5 | source: crates/ra_ide_api/src/assits/fill_match_arm.rs | ||
6 | --- | ||
7 | [ | ||
8 | LocalEdit { | ||
9 | label: "fill match arms", | ||
10 | edit: TextEdit { | ||
11 | atoms: [ | ||
12 | AtomTextEdit { | ||
13 | delete: [211; 218), | ||
14 | insert: "match a {\n A::As => (),\n A::Bs => (),\n A::Cs(_) => (),\n A::Ds(_, _) => (),\n A::Es{x, y} => (),\n }" | ||
15 | } | ||
16 | ] | ||
17 | }, | ||
18 | cursor_position: None | ||
19 | } | ||
20 | ] | ||
diff --git a/crates/ra_ide_api/src/assists/snapshots/tests__fill_match_arm2.snap b/crates/ra_ide_api/src/assists/snapshots/tests__fill_match_arm2.snap deleted file mode 100644 index cee0efe74..000000000 --- a/crates/ra_ide_api/src/assists/snapshots/tests__fill_match_arm2.snap +++ /dev/null | |||
@@ -1,20 +0,0 @@ | |||
1 | --- | ||
2 | created: "2019-02-03T15:41:34.640074+00:00" | ||
3 | creator: [email protected] | ||
4 | expression: ret | ||
5 | source: crates/ra_ide_api/src/assits/fill_match_arm.rs | ||
6 | --- | ||
7 | [ | ||
8 | LocalEdit { | ||
9 | label: "fill match arms", | ||
10 | edit: TextEdit { | ||
11 | atoms: [ | ||
12 | AtomTextEdit { | ||
13 | delete: [211; 221), | ||
14 | insert: "match a {\n A::As => (),\n A::Bs => (),\n A::Cs(_) => (),\n A::Ds(_, _) => (),\n A::Es{x, y} => (),\n }" | ||
15 | } | ||
16 | ] | ||
17 | }, | ||
18 | cursor_position: None | ||
19 | } | ||
20 | ] | ||
diff --git a/crates/ra_ide_api/src/imp.rs b/crates/ra_ide_api/src/imp.rs index fd8637ad2..b139efabf 100644 --- a/crates/ra_ide_api/src/imp.rs +++ b/crates/ra_ide_api/src/imp.rs | |||
@@ -19,7 +19,7 @@ use ra_syntax::{ | |||
19 | 19 | ||
20 | use crate::{ | 20 | use crate::{ |
21 | AnalysisChange, | 21 | AnalysisChange, |
22 | CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit, | 22 | CrateId, db, Diagnostic, FileId, FilePosition, FileSystemEdit, |
23 | Query, RootChange, SourceChange, SourceFileEdit, | 23 | Query, RootChange, SourceChange, SourceFileEdit, |
24 | symbol_index::{FileSymbol, SymbolsDatabase}, | 24 | symbol_index::{FileSymbol, SymbolsDatabase}, |
25 | status::syntax_tree_stats | 25 | status::syntax_tree_stats |
@@ -236,15 +236,6 @@ impl db::RootDatabase { | |||
236 | res | 236 | res |
237 | } | 237 | } |
238 | 238 | ||
239 | pub(crate) fn assists(&self, frange: FileRange) -> Vec<SourceChange> { | ||
240 | let file = self.parse(frange.file_id); | ||
241 | ra_ide_api_light::assists::assists(&file, frange.range) | ||
242 | .into_iter() | ||
243 | .chain(crate::assists::assists(self, frange.file_id, &file, frange.range).into_iter()) | ||
244 | .map(|local_edit| SourceChange::from_local_edit(frange.file_id, local_edit)) | ||
245 | .collect() | ||
246 | } | ||
247 | |||
248 | pub(crate) fn index_resolve(&self, name_ref: &ast::NameRef) -> Vec<FileSymbol> { | 239 | pub(crate) fn index_resolve(&self, name_ref: &ast::NameRef) -> Vec<FileSymbol> { |
249 | let name = name_ref.text(); | 240 | let name = name_ref.text(); |
250 | let mut query = Query::new(name.to_string()); | 241 | let mut query = Query::new(name.to_string()); |
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 3a187d7a5..8beaba5de 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs | |||
@@ -477,7 +477,7 @@ impl Analysis { | |||
477 | /// Computes assists (aks code actons aka intentions) for the given | 477 | /// Computes assists (aks code actons aka intentions) for the given |
478 | /// position. | 478 | /// position. |
479 | pub fn assists(&self, frange: FileRange) -> Cancelable<Vec<SourceChange>> { | 479 | pub fn assists(&self, frange: FileRange) -> Cancelable<Vec<SourceChange>> { |
480 | self.with_db(|db| db.assists(frange)) | 480 | self.with_db(|db| assists::assists(db, frange)) |
481 | } | 481 | } |
482 | 482 | ||
483 | /// Computes the set of diagnostics for the given file. | 483 | /// Computes the set of diagnostics for the given file. |