aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists
diff options
context:
space:
mode:
authorEkaterina Babshukova <[email protected]>2019-08-22 19:31:21 +0100
committerEkaterina Babshukova <[email protected]>2019-08-22 22:43:12 +0100
commite84f93cb5b651696637d87b98653d7e8f9149086 (patch)
tree9e4ea5877bce11dbc620bb56e01f86d761b7c5fc /crates/ra_assists
parent08e5d394dfbca28b15ed5dc772d55d48f87c3f54 (diff)
refactor fill_match_arms assist
Diffstat (limited to 'crates/ra_assists')
-rw-r--r--crates/ra_assists/src/add_missing_impl_members.rs14
-rw-r--r--crates/ra_assists/src/ast_editor.rs93
-rw-r--r--crates/ra_assists/src/fill_match_arms.rs195
3 files changed, 170 insertions, 132 deletions
diff --git a/crates/ra_assists/src/add_missing_impl_members.rs b/crates/ra_assists/src/add_missing_impl_members.rs
index 31c7d4e80..cbeb7054f 100644
--- a/crates/ra_assists/src/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/add_missing_impl_members.rs
@@ -1,13 +1,14 @@
1use hir::{db::HirDatabase, HasSource};
2use ra_syntax::{
3 ast::{self, AstNode, NameOwner},
4 SmolStr,
5};
6
1use crate::{ 7use crate::{
2 ast_editor::{AstBuilder, AstEditor}, 8 ast_editor::{AstBuilder, AstEditor},
3 Assist, AssistCtx, AssistId, 9 Assist, AssistCtx, AssistId,
4}; 10};
5 11
6use hir::{db::HirDatabase, HasSource};
7use ra_db::FilePosition;
8use ra_syntax::ast::{self, AstNode, NameOwner};
9use ra_syntax::SmolStr;
10
11#[derive(PartialEq)] 12#[derive(PartialEq)]
12enum AddMissingImplMembersMode { 13enum AddMissingImplMembersMode {
13 DefaultMethodsOnly, 14 DefaultMethodsOnly,
@@ -43,8 +44,7 @@ fn add_missing_impl_members_inner(
43 44
44 let trait_def = { 45 let trait_def = {
45 let file_id = ctx.frange.file_id; 46 let file_id = ctx.frange.file_id;
46 let position = FilePosition { file_id, offset: impl_node.syntax().text_range().start() }; 47 let analyzer = hir::SourceAnalyzer::new(ctx.db, file_id, impl_node.syntax(), None);
47 let analyzer = hir::SourceAnalyzer::new(ctx.db, position.file_id, impl_node.syntax(), None);
48 48
49 resolve_target_trait_def(ctx.db, &analyzer, &impl_node)? 49 resolve_target_trait_def(ctx.db, &analyzer, &impl_node)?
50 }; 50 };
diff --git a/crates/ra_assists/src/ast_editor.rs b/crates/ra_assists/src/ast_editor.rs
index 95b871b30..5b6952426 100644
--- a/crates/ra_assists/src/ast_editor.rs
+++ b/crates/ra_assists/src/ast_editor.rs
@@ -1,6 +1,8 @@
1use std::{iter, ops::RangeInclusive}; 1use std::{iter, ops::RangeInclusive};
2 2
3use arrayvec::ArrayVec; 3use arrayvec::ArrayVec;
4use itertools::Itertools;
5
4use hir::Name; 6use hir::Name;
5use ra_fmt::leading_indent; 7use ra_fmt::leading_indent;
6use ra_syntax::{ 8use ra_syntax::{
@@ -168,8 +170,7 @@ impl AstEditor<ast::NamedFieldList> {
168 170
169impl AstEditor<ast::ItemList> { 171impl AstEditor<ast::ItemList> {
170 pub fn append_items(&mut self, items: impl Iterator<Item = ast::ImplItem>) { 172 pub fn append_items(&mut self, items: impl Iterator<Item = ast::ImplItem>) {
171 let n_existing_items = self.ast().impl_items().count(); 173 if !self.ast().syntax().text().contains_char('\n') {
172 if n_existing_items == 0 {
173 self.do_make_multiline(); 174 self.do_make_multiline();
174 } 175 }
175 items.for_each(|it| self.append_item(it)); 176 items.for_each(|it| self.append_item(it));
@@ -288,6 +289,94 @@ impl AstBuilder<ast::NameRef> {
288 } 289 }
289} 290}
290 291
292impl AstBuilder<ast::Path> {
293 fn from_text(text: &str) -> ast::Path {
294 ast_node_from_file_text(text)
295 }
296
297 pub fn from_pieces(enum_name: ast::Name, var_name: ast::Name) -> ast::Path {
298 Self::from_text(&format!("{}::{}", enum_name.syntax(), var_name.syntax()))
299 }
300}
301
302impl AstBuilder<ast::BindPat> {
303 fn from_text(text: &str) -> ast::BindPat {
304 ast_node_from_file_text(&format!("fn f({}: ())", text))
305 }
306
307 pub fn from_name(name: &ast::Name) -> ast::BindPat {
308 Self::from_text(name.text())
309 }
310}
311
312impl AstBuilder<ast::PlaceholderPat> {
313 fn from_text(text: &str) -> ast::PlaceholderPat {
314 ast_node_from_file_text(&format!("fn f({}: ())", text))
315 }
316
317 pub fn placeholder() -> ast::PlaceholderPat {
318 Self::from_text("_")
319 }
320}
321
322impl AstBuilder<ast::TupleStructPat> {
323 fn from_text(text: &str) -> ast::TupleStructPat {
324 ast_node_from_file_text(&format!("fn f({}: ())", text))
325 }
326
327 pub fn from_pieces(
328 path: &ast::Path,
329 pats: impl Iterator<Item = ast::Pat>,
330 ) -> ast::TupleStructPat {
331 let pats_str = pats.map(|p| p.syntax().to_string()).collect::<Vec<_>>().join(", ");
332 Self::from_text(&format!("{}({})", path.syntax(), pats_str))
333 }
334}
335
336impl AstBuilder<ast::StructPat> {
337 fn from_text(text: &str) -> ast::StructPat {
338 ast_node_from_file_text(&format!("fn f({}: ())", text))
339 }
340
341 pub fn from_pieces(path: &ast::Path, pats: impl Iterator<Item = ast::Pat>) -> ast::StructPat {
342 let pats_str = pats.map(|p| p.syntax().to_string()).collect::<Vec<_>>().join(", ");
343 Self::from_text(&format!("{}{{ {} }}", path.syntax(), pats_str))
344 }
345}
346
347impl AstBuilder<ast::PathPat> {
348 fn from_text(text: &str) -> ast::PathPat {
349 ast_node_from_file_text(&format!("fn f({}: ())", text))
350 }
351
352 pub fn from_path(path: &ast::Path) -> ast::PathPat {
353 let path_str = path.syntax().text().to_string();
354 Self::from_text(path_str.as_str())
355 }
356}
357
358impl AstBuilder<ast::MatchArm> {
359 fn from_text(text: &str) -> ast::MatchArm {
360 ast_node_from_file_text(&format!("fn f() {{ match () {{{}}} }}", text))
361 }
362
363 pub fn from_pieces(pats: impl Iterator<Item = ast::Pat>, expr: &ast::Expr) -> ast::MatchArm {
364 let pats_str = pats.map(|p| p.syntax().to_string()).join(" | ");
365 Self::from_text(&format!("{} => {}", pats_str, expr.syntax()))
366 }
367}
368
369impl AstBuilder<ast::MatchArmList> {
370 fn from_text(text: &str) -> ast::MatchArmList {
371 ast_node_from_file_text(&format!("fn f() {{ match () {{{}}} }}", text))
372 }
373
374 pub fn from_arms(arms: impl Iterator<Item = ast::MatchArm>) -> ast::MatchArmList {
375 let arms_str = arms.map(|arm| format!("\n {}", arm.syntax())).join(",");
376 Self::from_text(&format!("{},\n", arms_str))
377 }
378}
379
291fn ast_node_from_file_text<N: AstNode>(text: &str) -> N { 380fn ast_node_from_file_text<N: AstNode>(text: &str) -> N {
292 let parse = SourceFile::parse(text); 381 let parse = SourceFile::parse(text);
293 let res = parse.tree().syntax().descendants().find_map(N::cast).unwrap().to_owned(); 382 let res = parse.tree().syntax().descendants().find_map(N::cast).unwrap().to_owned();
diff --git a/crates/ra_assists/src/fill_match_arms.rs b/crates/ra_assists/src/fill_match_arms.rs
index 85ff5c052..ce715a449 100644
--- a/crates/ra_assists/src/fill_match_arms.rs
+++ b/crates/ra_assists/src/fill_match_arms.rs
@@ -1,97 +1,91 @@
1use itertools::Itertools; 1use std::iter;
2use std::fmt::Write;
3 2
4use hir::{db::HirDatabase, AdtDef, FieldSource, HasSource}; 3use hir::{db::HirDatabase, AdtDef, HasSource};
5use ra_syntax::ast::{self, AstNode}; 4use ra_syntax::ast::{self, AstNode, NameOwner};
6 5
7use crate::{Assist, AssistCtx, AssistId}; 6use crate::{ast_editor::AstBuilder, Assist, AssistCtx, AssistId};
8
9fn is_trivial_arm(arm: &ast::MatchArm) -> bool {
10 fn single_pattern(arm: &ast::MatchArm) -> Option<ast::Pat> {
11 let (pat,) = arm.pats().collect_tuple()?;
12 Some(pat)
13 }
14 match single_pattern(arm) {
15 Some(ast::Pat::PlaceholderPat(..)) => true,
16 _ => false,
17 }
18}
19 7
20pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 8pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
21 let match_expr = ctx.node_at_offset::<ast::MatchExpr>()?; 9 let match_expr = ctx.node_at_offset::<ast::MatchExpr>()?;
10 let match_arm_list = match_expr.match_arm_list()?;
22 11
23 // We already have some match arms, so we don't provide any assists. 12 // We already have some match arms, so we don't provide any assists.
24 // Unless if there is only one trivial match arm possibly created 13 // Unless if there is only one trivial match arm possibly created
25 // by match postfix complete. Trivial match arm is the catch all arm. 14 // by match postfix complete. Trivial match arm is the catch all arm.
26 if let Some(arm_list) = match_expr.match_arm_list() { 15 let mut existing_arms = match_arm_list.arms();
27 let mut arm_iter = arm_list.arms(); 16 if let Some(arm) = existing_arms.next() {
28 let first = arm_iter.next(); 17 if !is_trivial(&arm) || existing_arms.next().is_some() {
29 18 return None;
30 match &first {
31 // If there arm list is empty or there is only one trivial arm, then proceed.
32 Some(arm) if is_trivial_arm(arm) => {
33 if arm_iter.next() != None {
34 return None;
35 }
36 }
37 None => {}
38
39 _ => {
40 return None;
41 }
42 } 19 }
43 }; 20 };
44 21
45 let expr = match_expr.expr()?; 22 let expr = match_expr.expr()?;
46 let analyzer = hir::SourceAnalyzer::new(ctx.db, ctx.frange.file_id, expr.syntax(), None); 23 let enum_def = {
47 let match_expr_ty = analyzer.type_of(ctx.db, &expr)?; 24 let file_id = ctx.frange.file_id;
48 let enum_def = analyzer.autoderef(ctx.db, match_expr_ty).find_map(|ty| match ty.as_adt() { 25 let analyzer = hir::SourceAnalyzer::new(ctx.db, file_id, expr.syntax(), None);
49 Some((AdtDef::Enum(e), _)) => Some(e), 26 resolve_enum_def(ctx.db, &analyzer, &expr)?
50 _ => None, 27 };
51 })?; 28 let variant_list = enum_def.variant_list()?;
52 let enum_name = enum_def.name(ctx.db)?;
53 let db = ctx.db;
54 29
55 ctx.add_action(AssistId("fill_match_arms"), "fill match arms", |edit| { 30 ctx.add_action(AssistId("fill_match_arms"), "fill match arms", |edit| {
56 let mut buf = format!("match {} {{\n", expr.syntax().text().to_string()); 31 let variants = variant_list.variants();
57 let variants = enum_def.variants(db); 32 let arms = variants.into_iter().filter_map(build_pat).map(|pat| {
58 for variant in variants { 33 AstBuilder::<ast::MatchArm>::from_pieces(
59 let name = match variant.name(db) { 34 iter::once(pat),
60 Some(it) => it, 35 &AstBuilder::<ast::Expr>::unit(),
61 None => continue, 36 )
62 }; 37 });
63 write!(&mut buf, " {}::{}", enum_name, name.to_string()).unwrap(); 38 let new_arm_list = AstBuilder::<ast::MatchArmList>::from_arms(arms);
64
65 let pat = variant
66 .fields(db)
67 .into_iter()
68 .map(|field| {
69 let name = field.name(db).to_string();
70 let src = field.source(db);
71 match src.ast {
72 FieldSource::Named(_) => name,
73 FieldSource::Pos(_) => "_".to_string(),
74 }
75 })
76 .collect::<Vec<_>>();
77 39
78 match pat.first().map(|s| s.as_str()) {
79 Some("_") => write!(&mut buf, "({})", pat.join(", ")).unwrap(),
80 Some(_) => write!(&mut buf, "{{{}}}", pat.join(", ")).unwrap(),
81 None => (),
82 };
83
84 buf.push_str(" => (),\n");
85 }
86 buf.push_str("}");
87 edit.target(match_expr.syntax().text_range()); 40 edit.target(match_expr.syntax().text_range());
88 edit.set_cursor(expr.syntax().text_range().start()); 41 edit.set_cursor(expr.syntax().text_range().start());
89 edit.replace_node_and_indent(match_expr.syntax(), buf); 42 edit.replace_node_and_indent(match_arm_list.syntax(), new_arm_list.syntax().text());
90 }); 43 });
91 44
92 ctx.build() 45 ctx.build()
93} 46}
94 47
48fn is_trivial(arm: &ast::MatchArm) -> bool {
49 arm.pats().any(|pat| match pat {
50 ast::Pat::PlaceholderPat(..) => true,
51 _ => false,
52 })
53}
54
55fn resolve_enum_def(
56 db: &impl HirDatabase,
57 analyzer: &hir::SourceAnalyzer,
58 expr: &ast::Expr,
59) -> Option<ast::EnumDef> {
60 let expr_ty = analyzer.type_of(db, &expr)?;
61
62 analyzer.autoderef(db, expr_ty).find_map(|ty| match ty.as_adt() {
63 Some((AdtDef::Enum(e), _)) => Some(e.source(db).ast),
64 _ => None,
65 })
66}
67
68fn build_pat(var: ast::EnumVariant) -> Option<ast::Pat> {
69 let path = &AstBuilder::<ast::Path>::from_pieces(var.parent_enum().name()?, var.name()?);
70
71 let pat: ast::Pat = match var.kind() {
72 ast::StructKind::Tuple(field_list) => {
73 let pats = iter::repeat(AstBuilder::<ast::PlaceholderPat>::placeholder().into())
74 .take(field_list.fields().count());
75 AstBuilder::<ast::TupleStructPat>::from_pieces(path, pats).into()
76 }
77 ast::StructKind::Named(field_list) => {
78 let pats = field_list
79 .fields()
80 .map(|f| AstBuilder::<ast::BindPat>::from_name(&f.name().unwrap()).into());
81 AstBuilder::<ast::StructPat>::from_pieces(path, pats).into()
82 }
83 ast::StructKind::Unit => AstBuilder::<ast::PathPat>::from_path(path).into(),
84 };
85
86 Some(pat)
87}
88
95#[cfg(test)] 89#[cfg(test)]
96mod tests { 90mod tests {
97 use crate::helpers::{check_assist, check_assist_target}; 91 use crate::helpers::{check_assist, check_assist_target};
@@ -108,7 +102,7 @@ mod tests {
108 Bs, 102 Bs,
109 Cs(String), 103 Cs(String),
110 Ds(String, String), 104 Ds(String, String),
111 Es{x: usize, y: usize} 105 Es{ x: usize, y: usize }
112 } 106 }
113 107
114 fn main() { 108 fn main() {
@@ -122,7 +116,7 @@ mod tests {
122 Bs, 116 Bs,
123 Cs(String), 117 Cs(String),
124 Ds(String, String), 118 Ds(String, String),
125 Es{x: usize, y: usize} 119 Es{ x: usize, y: usize }
126 } 120 }
127 121
128 fn main() { 122 fn main() {
@@ -132,7 +126,7 @@ mod tests {
132 A::Bs => (), 126 A::Bs => (),
133 A::Cs(_) => (), 127 A::Cs(_) => (),
134 A::Ds(_, _) => (), 128 A::Ds(_, _) => (),
135 A::Es{x, y} => (), 129 A::Es{ x, y } => (),
136 } 130 }
137 } 131 }
138 "#, 132 "#,
@@ -170,7 +164,7 @@ mod tests {
170 fill_match_arms, 164 fill_match_arms,
171 r#" 165 r#"
172 enum A { 166 enum A {
173 Es{x: usize, y: usize} 167 Es{ x: usize, y: usize }
174 } 168 }
175 169
176 fn foo(a: &mut A) { 170 fn foo(a: &mut A) {
@@ -180,57 +174,12 @@ mod tests {
180 "#, 174 "#,
181 r#" 175 r#"
182 enum A { 176 enum A {
183 Es{x: usize, y: usize} 177 Es{ x: usize, y: usize }
184 } 178 }
185 179
186 fn foo(a: &mut A) { 180 fn foo(a: &mut A) {
187 match <|>a { 181 match <|>a {
188 A::Es{x, y} => (), 182 A::Es{ x, y } => (),
189 }
190 }
191 "#,
192 );
193
194 check_assist(
195 fill_match_arms,
196 r#"
197 enum E { X, Y}
198
199 fn main() {
200 match &E::X<|>
201 }
202 "#,
203 r#"
204 enum E { X, Y}
205
206 fn main() {
207 match <|>&E::X {
208 E::X => (),
209 E::Y => (),
210 }
211 }
212 "#,
213 );
214 }
215
216 #[test]
217 fn fill_match_arms_no_body() {
218 check_assist(
219 fill_match_arms,
220 r#"
221 enum E { X, Y}
222
223 fn main() {
224 match E::X<|>
225 }
226 "#,
227 r#"
228 enum E { X, Y}
229
230 fn main() {
231 match <|>E::X {
232 E::X => (),
233 E::Y => (),
234 } 183 }
235 } 184 }
236 "#, 185 "#,
@@ -242,7 +191,7 @@ mod tests {
242 check_assist_target( 191 check_assist_target(
243 fill_match_arms, 192 fill_match_arms,
244 r#" 193 r#"
245 enum E { X, Y} 194 enum E { X, Y }
246 195
247 fn main() { 196 fn main() {
248 match E::X<|> {} 197 match E::X<|> {}