aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-02-06 14:01:07 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-02-06 14:01:07 +0000
commitf6cf9393a8327b4c0385bccbc5be84a79bd50057 (patch)
tree4af15c8906b85de01a15c717bc1fac388952cd3d /crates/ra_ide_api
parent736a55c97e69f95e6ff4a0c3dafb2018e8ea05f9 (diff)
parent0c5fd8f7cbf04eda763e55bc9a38dad5f7ec917d (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')
-rw-r--r--crates/ra_ide_api/Cargo.toml1
-rw-r--r--crates/ra_ide_api/src/assists.rs109
-rw-r--r--crates/ra_ide_api/src/assists/fill_match_arm.rs157
-rw-r--r--crates/ra_ide_api/src/assists/snapshots/tests__fill_match_arm1.snap20
-rw-r--r--crates/ra_ide_api/src/assists/snapshots/tests__fill_match_arm2.snap20
-rw-r--r--crates/ra_ide_api/src/imp.rs11
-rw-r--r--crates/ra_ide_api/src/lib.rs2
7 files changed, 25 insertions, 295 deletions
diff --git a/crates/ra_ide_api/Cargo.toml b/crates/ra_ide_api/Cargo.toml
index 54de9b2e3..95cccf8cf 100644
--- a/crates/ra_ide_api/Cargo.toml
+++ b/crates/ra_ide_api/Cargo.toml
@@ -24,6 +24,7 @@ ra_text_edit = { path = "../ra_text_edit" }
24ra_db = { path = "../ra_db" } 24ra_db = { path = "../ra_db" }
25hir = { path = "../ra_hir", package = "ra_hir" } 25hir = { path = "../ra_hir", package = "ra_hir" }
26test_utils = { path = "../test_utils" } 26test_utils = { path = "../test_utils" }
27ra_assists = { path = "../ra_assists" }
27 28
28[dev-dependencies] 29[dev-dependencies]
29insta = "0.6.1" 30insta = "0.6.1"
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 @@
1mod fill_match_arm; 1use ra_db::{FileRange, FilePosition};
2 2
3use ra_syntax::{ 3use crate::{SourceFileEdit, SourceChange, db::RootDatabase};
4 TextRange, SourceFile, AstNode, 4
5 algo::find_node_at_offset, 5pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<SourceChange> {
6}; 6 ra_assists::assists(db, frange)
7use 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 };
14use 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
20pub(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)]
34pub 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
42impl<'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 @@
1use std::fmt::Write;
2use hir::{
3 AdtDef,
4 source_binder,
5 Ty,
6 FieldSource,
7};
8use ra_ide_api_light::{
9 assists::{
10 Assist,
11 AssistBuilder
12 }
13};
14use ra_syntax::{
15 ast::{
16 self,
17 AstNode,
18 }
19};
20
21use crate::assists::AssistCtx;
22
23pub 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)]
88mod 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---
2created: "2019-02-03T15:38:46.094184+00:00"
3creator: [email protected]
4expression: ret
5source: 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---
2created: "2019-02-03T15:41:34.640074+00:00"
3creator: [email protected]
4expression: ret
5source: 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
20use crate::{ 20use 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.