aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists
diff options
context:
space:
mode:
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.rs116
-rw-r--r--crates/ra_assists/src/change_visibility.rs2
-rw-r--r--crates/ra_assists/src/fill_match_arms.rs195
4 files changed, 184 insertions, 143 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..6815638dc 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::{
@@ -17,7 +19,10 @@ pub struct AstEditor<N: AstNode> {
17} 19}
18 20
19impl<N: AstNode> AstEditor<N> { 21impl<N: AstNode> AstEditor<N> {
20 pub fn new(node: N) -> AstEditor<N> { 22 pub fn new(node: N) -> AstEditor<N>
23 where
24 N: Clone,
25 {
21 AstEditor { original_ast: node.clone(), ast: node } 26 AstEditor { original_ast: node.clone(), ast: node }
22 } 27 }
23 28
@@ -88,15 +93,15 @@ impl<N: AstNode> AstEditor<N> {
88 } 93 }
89} 94}
90 95
91impl AstEditor<ast::NamedFieldList> { 96impl AstEditor<ast::RecordFieldList> {
92 pub fn append_field(&mut self, field: &ast::NamedField) { 97 pub fn append_field(&mut self, field: &ast::RecordField) {
93 self.insert_field(InsertPosition::Last, field) 98 self.insert_field(InsertPosition::Last, field)
94 } 99 }
95 100
96 pub fn insert_field( 101 pub fn insert_field(
97 &mut self, 102 &mut self,
98 position: InsertPosition<&'_ ast::NamedField>, 103 position: InsertPosition<&'_ ast::RecordField>,
99 field: &ast::NamedField, 104 field: &ast::RecordField,
100 ) { 105 ) {
101 let is_multiline = self.ast().syntax().text().contains_char('\n'); 106 let is_multiline = self.ast().syntax().text().contains_char('\n');
102 let ws; 107 let ws;
@@ -168,8 +173,7 @@ impl AstEditor<ast::NamedFieldList> {
168 173
169impl AstEditor<ast::ItemList> { 174impl AstEditor<ast::ItemList> {
170 pub fn append_items(&mut self, items: impl Iterator<Item = ast::ImplItem>) { 175 pub fn append_items(&mut self, items: impl Iterator<Item = ast::ImplItem>) {
171 let n_existing_items = self.ast().impl_items().count(); 176 if !self.ast().syntax().text().contains_char('\n') {
172 if n_existing_items == 0 {
173 self.do_make_multiline(); 177 self.do_make_multiline();
174 } 178 }
175 items.for_each(|it| self.append_item(it)); 179 items.for_each(|it| self.append_item(it));
@@ -241,16 +245,16 @@ pub struct AstBuilder<N: AstNode> {
241 _phantom: std::marker::PhantomData<N>, 245 _phantom: std::marker::PhantomData<N>,
242} 246}
243 247
244impl AstBuilder<ast::NamedField> { 248impl AstBuilder<ast::RecordField> {
245 pub fn from_name(name: &Name) -> ast::NamedField { 249 pub fn from_name(name: &Name) -> ast::RecordField {
246 ast_node_from_file_text(&format!("fn f() {{ S {{ {}: (), }} }}", name)) 250 ast_node_from_file_text(&format!("fn f() {{ S {{ {}: (), }} }}", name))
247 } 251 }
248 252
249 fn from_text(text: &str) -> ast::NamedField { 253 fn from_text(text: &str) -> ast::RecordField {
250 ast_node_from_file_text(&format!("fn f() {{ S {{ {}, }} }}", text)) 254 ast_node_from_file_text(&format!("fn f() {{ S {{ {}, }} }}", text))
251 } 255 }
252 256
253 pub fn from_pieces(name: &ast::NameRef, expr: Option<&ast::Expr>) -> ast::NamedField { 257 pub fn from_pieces(name: &ast::NameRef, expr: Option<&ast::Expr>) -> ast::RecordField {
254 match expr { 258 match expr {
255 Some(expr) => Self::from_text(&format!("{}: {}", name.syntax(), expr.syntax())), 259 Some(expr) => Self::from_text(&format!("{}: {}", name.syntax(), expr.syntax())),
256 None => Self::from_text(&name.syntax().to_string()), 260 None => Self::from_text(&name.syntax().to_string()),
@@ -288,9 +292,97 @@ impl AstBuilder<ast::NameRef> {
288 } 292 }
289} 293}
290 294
295impl AstBuilder<ast::Path> {
296 fn from_text(text: &str) -> ast::Path {
297 ast_node_from_file_text(text)
298 }
299
300 pub fn from_pieces(enum_name: ast::Name, var_name: ast::Name) -> ast::Path {
301 Self::from_text(&format!("{}::{}", enum_name.syntax(), var_name.syntax()))
302 }
303}
304
305impl AstBuilder<ast::BindPat> {
306 fn from_text(text: &str) -> ast::BindPat {
307 ast_node_from_file_text(&format!("fn f({}: ())", text))
308 }
309
310 pub fn from_name(name: &ast::Name) -> ast::BindPat {
311 Self::from_text(name.text())
312 }
313}
314
315impl AstBuilder<ast::PlaceholderPat> {
316 fn from_text(text: &str) -> ast::PlaceholderPat {
317 ast_node_from_file_text(&format!("fn f({}: ())", text))
318 }
319
320 pub fn placeholder() -> ast::PlaceholderPat {
321 Self::from_text("_")
322 }
323}
324
325impl AstBuilder<ast::TupleStructPat> {
326 fn from_text(text: &str) -> ast::TupleStructPat {
327 ast_node_from_file_text(&format!("fn f({}: ())", text))
328 }
329
330 pub fn from_pieces(
331 path: &ast::Path,
332 pats: impl Iterator<Item = ast::Pat>,
333 ) -> ast::TupleStructPat {
334 let pats_str = pats.map(|p| p.syntax().to_string()).collect::<Vec<_>>().join(", ");
335 Self::from_text(&format!("{}({})", path.syntax(), pats_str))
336 }
337}
338
339impl AstBuilder<ast::RecordPat> {
340 fn from_text(text: &str) -> ast::RecordPat {
341 ast_node_from_file_text(&format!("fn f({}: ())", text))
342 }
343
344 pub fn from_pieces(path: &ast::Path, pats: impl Iterator<Item = ast::Pat>) -> ast::RecordPat {
345 let pats_str = pats.map(|p| p.syntax().to_string()).collect::<Vec<_>>().join(", ");
346 Self::from_text(&format!("{}{{ {} }}", path.syntax(), pats_str))
347 }
348}
349
350impl AstBuilder<ast::PathPat> {
351 fn from_text(text: &str) -> ast::PathPat {
352 ast_node_from_file_text(&format!("fn f({}: ())", text))
353 }
354
355 pub fn from_path(path: &ast::Path) -> ast::PathPat {
356 let path_str = path.syntax().text().to_string();
357 Self::from_text(path_str.as_str())
358 }
359}
360
361impl AstBuilder<ast::MatchArm> {
362 fn from_text(text: &str) -> ast::MatchArm {
363 ast_node_from_file_text(&format!("fn f() {{ match () {{{}}} }}", text))
364 }
365
366 pub fn from_pieces(pats: impl Iterator<Item = ast::Pat>, expr: &ast::Expr) -> ast::MatchArm {
367 let pats_str = pats.map(|p| p.syntax().to_string()).join(" | ");
368 Self::from_text(&format!("{} => {}", pats_str, expr.syntax()))
369 }
370}
371
372impl AstBuilder<ast::MatchArmList> {
373 fn from_text(text: &str) -> ast::MatchArmList {
374 ast_node_from_file_text(&format!("fn f() {{ match () {{{}}} }}", text))
375 }
376
377 pub fn from_arms(arms: impl Iterator<Item = ast::MatchArm>) -> ast::MatchArmList {
378 let arms_str = arms.map(|arm| format!("\n {}", arm.syntax())).join(",");
379 Self::from_text(&format!("{},\n", arms_str))
380 }
381}
382
291fn ast_node_from_file_text<N: AstNode>(text: &str) -> N { 383fn ast_node_from_file_text<N: AstNode>(text: &str) -> N {
292 let parse = SourceFile::parse(text); 384 let parse = SourceFile::parse(text);
293 let res = parse.tree().syntax().descendants().find_map(N::cast).unwrap().to_owned(); 385 let res = parse.tree().syntax().descendants().find_map(N::cast).unwrap();
294 res 386 res
295} 387}
296 388
diff --git a/crates/ra_assists/src/change_visibility.rs b/crates/ra_assists/src/change_visibility.rs
index d28cdd07b..60c74debc 100644
--- a/crates/ra_assists/src/change_visibility.rs
+++ b/crates/ra_assists/src/change_visibility.rs
@@ -38,7 +38,7 @@ fn add_vis(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
38 (vis_offset(&parent), keyword.text_range()) 38 (vis_offset(&parent), keyword.text_range())
39 } else { 39 } else {
40 let ident = ctx.token_at_offset().find(|leaf| leaf.kind() == IDENT)?; 40 let ident = ctx.token_at_offset().find(|leaf| leaf.kind() == IDENT)?;
41 let field = ident.parent().ancestors().find_map(ast::NamedFieldDef::cast)?; 41 let field = ident.parent().ancestors().find_map(ast::RecordFieldDef::cast)?;
42 if field.name()?.syntax().text_range() != ident.text_range() && field.visibility().is_some() 42 if field.name()?.syntax().text_range() != ident.text_range() && field.visibility().is_some()
43 { 43 {
44 return None; 44 return None;
diff --git a/crates/ra_assists/src/fill_match_arms.rs b/crates/ra_assists/src/fill_match_arms.rs
index 85ff5c052..cc3879562 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::RecordPat>::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<|> {}