diff options
Diffstat (limited to 'crates/ra_assists/src')
23 files changed, 178 insertions, 355 deletions
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index c45262efa..189cad7d0 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs | |||
@@ -1,8 +1,10 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
1 | use hir::db::HirDatabase; | 3 | use hir::db::HirDatabase; |
2 | use ra_db::FileRange; | 4 | use ra_db::FileRange; |
3 | use ra_fmt::{leading_indent, reindent}; | 5 | use ra_fmt::{leading_indent, reindent}; |
4 | use ra_syntax::{ | 6 | use ra_syntax::{ |
5 | algo::{find_covering_element, find_node_at_offset}, | 7 | algo::{self, find_covering_element, find_node_at_offset}, |
6 | AstNode, SourceFile, SyntaxElement, SyntaxNode, SyntaxToken, TextRange, TextUnit, | 8 | AstNode, SourceFile, SyntaxElement, SyntaxNode, SyntaxToken, TextRange, TextUnit, |
7 | TokenAtOffset, | 9 | TokenAtOffset, |
8 | }; | 10 | }; |
@@ -177,6 +179,10 @@ impl AssistBuilder { | |||
177 | &mut self.edit | 179 | &mut self.edit |
178 | } | 180 | } |
179 | 181 | ||
182 | pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) { | ||
183 | algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) | ||
184 | } | ||
185 | |||
180 | fn build(self) -> AssistAction { | 186 | fn build(self) -> AssistAction { |
181 | AssistAction { | 187 | AssistAction { |
182 | edit: self.edit.finish(), | 188 | edit: self.edit.finish(), |
diff --git a/crates/ra_assists/src/assists/add_derive.rs b/crates/ra_assists/src/assists/add_derive.rs index 9c88644df..77ecc33c9 100644 --- a/crates/ra_assists/src/assists/add_derive.rs +++ b/crates/ra_assists/src/assists/add_derive.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
1 | use hir::db::HirDatabase; | 3 | use hir::db::HirDatabase; |
2 | use ra_syntax::{ | 4 | use ra_syntax::{ |
3 | ast::{self, AstNode, AttrsOwner}, | 5 | ast::{self, AstNode, AttrsOwner}, |
@@ -13,7 +15,7 @@ pub(crate) fn add_derive(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> | |||
13 | ctx.add_action(AssistId("add_derive"), "add `#[derive]`", |edit| { | 15 | ctx.add_action(AssistId("add_derive"), "add `#[derive]`", |edit| { |
14 | let derive_attr = nominal | 16 | let derive_attr = nominal |
15 | .attrs() | 17 | .attrs() |
16 | .filter_map(|x| x.as_call()) | 18 | .filter_map(|x| x.as_simple_call()) |
17 | .filter(|(name, _arg)| name == "derive") | 19 | .filter(|(name, _arg)| name == "derive") |
18 | .map(|(_name, arg)| arg) | 20 | .map(|(_name, arg)| arg) |
19 | .next(); | 21 | .next(); |
diff --git a/crates/ra_assists/src/assists/add_explicit_type.rs b/crates/ra_assists/src/assists/add_explicit_type.rs index 78f0f7f28..8c83dc987 100644 --- a/crates/ra_assists/src/assists/add_explicit_type.rs +++ b/crates/ra_assists/src/assists/add_explicit_type.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
1 | use hir::{db::HirDatabase, HirDisplay, Ty}; | 3 | use hir::{db::HirDatabase, HirDisplay, Ty}; |
2 | use ra_syntax::{ | 4 | use ra_syntax::{ |
3 | ast::{self, AstNode, LetStmt, NameOwner}, | 5 | ast::{self, AstNode, LetStmt, NameOwner}, |
diff --git a/crates/ra_assists/src/assists/add_impl.rs b/crates/ra_assists/src/assists/add_impl.rs index 4b61f4031..94801fbc9 100644 --- a/crates/ra_assists/src/assists/add_impl.rs +++ b/crates/ra_assists/src/assists/add_impl.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
1 | use format_buf::format; | 3 | use format_buf::format; |
2 | use hir::db::HirDatabase; | 4 | use hir::db::HirDatabase; |
3 | use join_to_string::join; | 5 | use join_to_string::join; |
diff --git a/crates/ra_assists/src/assists/add_missing_impl_members.rs b/crates/ra_assists/src/assists/add_missing_impl_members.rs index 23da1e65f..565b96fb5 100644 --- a/crates/ra_assists/src/assists/add_missing_impl_members.rs +++ b/crates/ra_assists/src/assists/add_missing_impl_members.rs | |||
@@ -1,10 +1,12 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
1 | use hir::{db::HirDatabase, HasSource}; | 3 | use hir::{db::HirDatabase, HasSource}; |
2 | use ra_syntax::{ | 4 | use ra_syntax::{ |
3 | ast::{self, make, AstNode, NameOwner}, | 5 | ast::{self, edit, make, AstNode, NameOwner}, |
4 | SmolStr, | 6 | SmolStr, |
5 | }; | 7 | }; |
6 | 8 | ||
7 | use crate::{ast_editor::AstEditor, Assist, AssistCtx, AssistId}; | 9 | use crate::{Assist, AssistCtx, AssistId}; |
8 | 10 | ||
9 | #[derive(PartialEq)] | 11 | #[derive(PartialEq)] |
10 | enum AddMissingImplMembersMode { | 12 | enum AddMissingImplMembersMode { |
@@ -75,37 +77,32 @@ fn add_missing_impl_members_inner( | |||
75 | 77 | ||
76 | ctx.add_action(AssistId(assist_id), label, |edit| { | 78 | ctx.add_action(AssistId(assist_id), label, |edit| { |
77 | let n_existing_items = impl_item_list.impl_items().count(); | 79 | let n_existing_items = impl_item_list.impl_items().count(); |
78 | let items = missing_items.into_iter().map(|it| match it { | 80 | let items = missing_items |
79 | ast::ImplItem::FnDef(def) => strip_docstring(add_body(def).into()), | 81 | .into_iter() |
80 | _ => strip_docstring(it), | 82 | .map(|it| match it { |
81 | }); | 83 | ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)), |
82 | let mut ast_editor = AstEditor::new(impl_item_list); | 84 | _ => it, |
83 | 85 | }) | |
84 | ast_editor.append_items(items); | 86 | .map(|it| edit::strip_attrs_and_docs(&it)); |
85 | 87 | let new_impl_item_list = impl_item_list.append_items(items); | |
86 | let first_new_item = ast_editor.ast().impl_items().nth(n_existing_items).unwrap(); | 88 | let cursor_position = { |
87 | let cursor_position = first_new_item.syntax().text_range().start(); | 89 | let first_new_item = new_impl_item_list.impl_items().nth(n_existing_items).unwrap(); |
88 | ast_editor.into_text_edit(edit.text_edit_builder()); | 90 | first_new_item.syntax().text_range().start() |
89 | 91 | }; | |
92 | |||
93 | edit.replace_ast(impl_item_list, new_impl_item_list); | ||
90 | edit.set_cursor(cursor_position); | 94 | edit.set_cursor(cursor_position); |
91 | }); | 95 | }); |
92 | 96 | ||
93 | ctx.build() | 97 | ctx.build() |
94 | } | 98 | } |
95 | 99 | ||
96 | fn strip_docstring(item: ast::ImplItem) -> ast::ImplItem { | ||
97 | let mut ast_editor = AstEditor::new(item); | ||
98 | ast_editor.strip_attrs_and_docs(); | ||
99 | ast_editor.ast().to_owned() | ||
100 | } | ||
101 | |||
102 | fn add_body(fn_def: ast::FnDef) -> ast::FnDef { | 100 | fn add_body(fn_def: ast::FnDef) -> ast::FnDef { |
103 | let mut ast_editor = AstEditor::new(fn_def.clone()); | ||
104 | if fn_def.body().is_none() { | 101 | if fn_def.body().is_none() { |
105 | let body = make::block_from_expr(make::expr_unimplemented()); | 102 | fn_def.with_body(make::block_from_expr(make::expr_unimplemented())) |
106 | ast_editor.set_body(&body); | 103 | } else { |
104 | fn_def | ||
107 | } | 105 | } |
108 | ast_editor.ast().to_owned() | ||
109 | } | 106 | } |
110 | 107 | ||
111 | /// Given an `ast::ImplBlock`, resolves the target trait (the one being | 108 | /// Given an `ast::ImplBlock`, resolves the target trait (the one being |
@@ -332,5 +329,4 @@ impl Foo for S { | |||
332 | }", | 329 | }", |
333 | ) | 330 | ) |
334 | } | 331 | } |
335 | |||
336 | } | 332 | } |
diff --git a/crates/ra_assists/src/assists/apply_demorgan.rs b/crates/ra_assists/src/assists/apply_demorgan.rs new file mode 100644 index 000000000..5f2b0dd18 --- /dev/null +++ b/crates/ra_assists/src/assists/apply_demorgan.rs | |||
@@ -0,0 +1,102 @@ | |||
1 | //! This contains the functions associated with the demorgan assist. | ||
2 | //! This assist transforms boolean expressions of the form `!a || !b` into | ||
3 | //! `!(a && b)`. | ||
4 | use hir::db::HirDatabase; | ||
5 | use ra_syntax::ast::{self, AstNode}; | ||
6 | use ra_syntax::SyntaxNode; | ||
7 | |||
8 | use crate::{Assist, AssistCtx, AssistId}; | ||
9 | |||
10 | /// Assist for applying demorgan's law | ||
11 | /// | ||
12 | /// This transforms expressions of the form `!l || !r` into `!(l && r)`. | ||
13 | /// This also works with `&&`. This assist can only be applied with the cursor | ||
14 | /// on either `||` or `&&`, with both operands being a negation of some kind. | ||
15 | /// This means something of the form `!x` or `x != y`. | ||
16 | pub(crate) fn apply_demorgan(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | ||
17 | let expr = ctx.node_at_offset::<ast::BinExpr>()?; | ||
18 | let op = expr.op_kind()?; | ||
19 | let op_range = expr.op_token()?.text_range(); | ||
20 | let opposite_op = opposite_logic_op(op)?; | ||
21 | let cursor_in_range = ctx.frange.range.is_subrange(&op_range); | ||
22 | if !cursor_in_range { | ||
23 | return None; | ||
24 | } | ||
25 | let lhs = expr.lhs()?.syntax().clone(); | ||
26 | let lhs_range = lhs.text_range(); | ||
27 | let rhs = expr.rhs()?.syntax().clone(); | ||
28 | let rhs_range = rhs.text_range(); | ||
29 | let not_lhs = undo_negation(lhs)?; | ||
30 | let not_rhs = undo_negation(rhs)?; | ||
31 | |||
32 | ctx.add_action(AssistId("apply_demorgan"), "apply demorgan's law", |edit| { | ||
33 | edit.target(op_range); | ||
34 | edit.replace(op_range, opposite_op); | ||
35 | edit.replace(lhs_range, format!("!({}", not_lhs)); | ||
36 | edit.replace(rhs_range, format!("{})", not_rhs)); | ||
37 | }); | ||
38 | ctx.build() | ||
39 | } | ||
40 | |||
41 | // Return the opposite text for a given logical operator, if it makes sense | ||
42 | fn opposite_logic_op(kind: ast::BinOp) -> Option<&'static str> { | ||
43 | match kind { | ||
44 | ast::BinOp::BooleanOr => Some("&&"), | ||
45 | ast::BinOp::BooleanAnd => Some("||"), | ||
46 | _ => None, | ||
47 | } | ||
48 | } | ||
49 | |||
50 | // This function tries to undo unary negation, or inequality | ||
51 | fn undo_negation(node: SyntaxNode) -> Option<String> { | ||
52 | match ast::Expr::cast(node)? { | ||
53 | ast::Expr::BinExpr(bin) => match bin.op_kind()? { | ||
54 | ast::BinOp::NegatedEqualityTest => { | ||
55 | let lhs = bin.lhs()?.syntax().text(); | ||
56 | let rhs = bin.rhs()?.syntax().text(); | ||
57 | Some(format!("{} == {}", lhs, rhs)) | ||
58 | } | ||
59 | _ => None, | ||
60 | }, | ||
61 | ast::Expr::PrefixExpr(pe) => match pe.op_kind()? { | ||
62 | ast::PrefixOp::Not => { | ||
63 | let child = pe.expr()?.syntax().text(); | ||
64 | Some(String::from(child)) | ||
65 | } | ||
66 | _ => None, | ||
67 | }, | ||
68 | _ => None, | ||
69 | } | ||
70 | } | ||
71 | |||
72 | #[cfg(test)] | ||
73 | mod tests { | ||
74 | use super::*; | ||
75 | |||
76 | use crate::helpers::{check_assist, check_assist_not_applicable}; | ||
77 | |||
78 | #[test] | ||
79 | fn demorgan_turns_and_into_or() { | ||
80 | check_assist(apply_demorgan, "fn f() { !x &&<|> !x }", "fn f() { !(x ||<|> x) }") | ||
81 | } | ||
82 | |||
83 | #[test] | ||
84 | fn demorgan_turns_or_into_and() { | ||
85 | check_assist(apply_demorgan, "fn f() { !x ||<|> !x }", "fn f() { !(x &&<|> x) }") | ||
86 | } | ||
87 | |||
88 | #[test] | ||
89 | fn demorgan_removes_inequality() { | ||
90 | check_assist(apply_demorgan, "fn f() { x != x ||<|> !x }", "fn f() { !(x == x &&<|> x) }") | ||
91 | } | ||
92 | |||
93 | #[test] | ||
94 | fn demorgan_doesnt_apply_with_cursor_not_on_op() { | ||
95 | check_assist_not_applicable(apply_demorgan, "fn f() { <|> !x || !x }") | ||
96 | } | ||
97 | |||
98 | #[test] | ||
99 | fn demorgan_doesnt_apply_when_operands_arent_negated_already() { | ||
100 | check_assist_not_applicable(apply_demorgan, "fn f() { x ||<|> x }") | ||
101 | } | ||
102 | } | ||
diff --git a/crates/ra_assists/src/assists/auto_import.rs b/crates/ra_assists/src/assists/auto_import.rs index 5aae98546..02c58e7c6 100644 --- a/crates/ra_assists/src/assists/auto_import.rs +++ b/crates/ra_assists/src/assists/auto_import.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
1 | use hir::{self, db::HirDatabase}; | 3 | use hir::{self, db::HirDatabase}; |
2 | use ra_text_edit::TextEditBuilder; | 4 | use ra_text_edit::TextEditBuilder; |
3 | 5 | ||
@@ -448,7 +450,6 @@ fn make_assist_add_in_tree_list( | |||
448 | fmt_segments_raw(target, &mut buf); | 450 | fmt_segments_raw(target, &mut buf); |
449 | edit.insert(offset, buf); | 451 | edit.insert(offset, buf); |
450 | } else { | 452 | } else { |
451 | |||
452 | } | 453 | } |
453 | } | 454 | } |
454 | 455 | ||
@@ -512,7 +513,7 @@ pub fn collect_hir_path_segments(path: &hir::Path) -> Option<Vec<SmolStr>> { | |||
512 | hir::PathKind::Plain => {} | 513 | hir::PathKind::Plain => {} |
513 | hir::PathKind::Self_ => ps.push("self".into()), | 514 | hir::PathKind::Self_ => ps.push("self".into()), |
514 | hir::PathKind::Super => ps.push("super".into()), | 515 | hir::PathKind::Super => ps.push("super".into()), |
515 | hir::PathKind::Type(_) => return None, | 516 | hir::PathKind::Type(_) | hir::PathKind::DollarCrate(_) => return None, |
516 | } | 517 | } |
517 | for s in path.segments.iter() { | 518 | for s in path.segments.iter() { |
518 | ps.push(s.name.to_string().into()); | 519 | ps.push(s.name.to_string().into()); |
diff --git a/crates/ra_assists/src/assists/change_visibility.rs b/crates/ra_assists/src/assists/change_visibility.rs index 60c74debc..df92c6b67 100644 --- a/crates/ra_assists/src/assists/change_visibility.rs +++ b/crates/ra_assists/src/assists/change_visibility.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
1 | use hir::db::HirDatabase; | 3 | use hir::db::HirDatabase; |
2 | use ra_syntax::{ | 4 | use ra_syntax::{ |
3 | ast::{self, NameOwner, VisibilityOwner}, | 5 | ast::{self, NameOwner, VisibilityOwner}, |
diff --git a/crates/ra_assists/src/assists/fill_match_arms.rs b/crates/ra_assists/src/assists/fill_match_arms.rs index db82db89a..7335cce09 100644 --- a/crates/ra_assists/src/assists/fill_match_arms.rs +++ b/crates/ra_assists/src/assists/fill_match_arms.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
1 | use std::iter; | 3 | use std::iter; |
2 | 4 | ||
3 | use hir::{db::HirDatabase, Adt, HasSource}; | 5 | use hir::{db::HirDatabase, Adt, HasSource}; |
diff --git a/crates/ra_assists/src/assists/flip_binexpr.rs b/crates/ra_assists/src/assists/flip_binexpr.rs index b55b36a8e..c51035282 100644 --- a/crates/ra_assists/src/assists/flip_binexpr.rs +++ b/crates/ra_assists/src/assists/flip_binexpr.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
1 | use hir::db::HirDatabase; | 3 | use hir::db::HirDatabase; |
2 | use ra_syntax::ast::{AstNode, BinExpr, BinOp}; | 4 | use ra_syntax::ast::{AstNode, BinExpr, BinOp}; |
3 | 5 | ||
diff --git a/crates/ra_assists/src/assists/flip_comma.rs b/crates/ra_assists/src/assists/flip_comma.rs index 5ee7561bc..e31cc5e7d 100644 --- a/crates/ra_assists/src/assists/flip_comma.rs +++ b/crates/ra_assists/src/assists/flip_comma.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
1 | use hir::db::HirDatabase; | 3 | use hir::db::HirDatabase; |
2 | use ra_syntax::{algo::non_trivia_sibling, Direction, T}; | 4 | use ra_syntax::{algo::non_trivia_sibling, Direction, T}; |
3 | 5 | ||
diff --git a/crates/ra_assists/src/assists/inline_local_variable.rs b/crates/ra_assists/src/assists/inline_local_variable.rs index eedb29199..9bd64decc 100644 --- a/crates/ra_assists/src/assists/inline_local_variable.rs +++ b/crates/ra_assists/src/assists/inline_local_variable.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
1 | use hir::db::HirDatabase; | 3 | use hir::db::HirDatabase; |
2 | use ra_syntax::{ | 4 | use ra_syntax::{ |
3 | ast::{self, AstNode, AstToken}, | 5 | ast::{self, AstNode, AstToken}, |
diff --git a/crates/ra_assists/src/assists/introduce_variable.rs b/crates/ra_assists/src/assists/introduce_variable.rs index 470ffe120..43378c4b0 100644 --- a/crates/ra_assists/src/assists/introduce_variable.rs +++ b/crates/ra_assists/src/assists/introduce_variable.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
1 | use format_buf::format; | 3 | use format_buf::format; |
2 | use hir::db::HirDatabase; | 4 | use hir::db::HirDatabase; |
3 | use ra_syntax::{ | 5 | use ra_syntax::{ |
diff --git a/crates/ra_assists/src/assists/merge_match_arms.rs b/crates/ra_assists/src/assists/merge_match_arms.rs index 3b6a99895..17baa98f9 100644 --- a/crates/ra_assists/src/assists/merge_match_arms.rs +++ b/crates/ra_assists/src/assists/merge_match_arms.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
1 | use crate::{Assist, AssistCtx, AssistId, TextRange, TextUnit}; | 3 | use crate::{Assist, AssistCtx, AssistId, TextRange, TextUnit}; |
2 | use hir::db::HirDatabase; | 4 | use hir::db::HirDatabase; |
3 | use ra_syntax::ast::{AstNode, MatchArm}; | 5 | use ra_syntax::ast::{AstNode, MatchArm}; |
diff --git a/crates/ra_assists/src/assists/move_bounds.rs b/crates/ra_assists/src/assists/move_bounds.rs index fd4bdc55c..f791d22b0 100644 --- a/crates/ra_assists/src/assists/move_bounds.rs +++ b/crates/ra_assists/src/assists/move_bounds.rs | |||
@@ -1,11 +1,13 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
1 | use hir::db::HirDatabase; | 3 | use hir::db::HirDatabase; |
2 | use ra_syntax::{ | 4 | use ra_syntax::{ |
3 | ast::{self, make, AstNode, NameOwner, TypeBoundsOwner}, | 5 | ast::{self, edit, make, AstNode, NameOwner, TypeBoundsOwner}, |
4 | SyntaxElement, | 6 | SyntaxElement, |
5 | SyntaxKind::*, | 7 | SyntaxKind::*, |
6 | }; | 8 | }; |
7 | 9 | ||
8 | use crate::{ast_editor::AstEditor, Assist, AssistCtx, AssistId}; | 10 | use crate::{Assist, AssistCtx, AssistId}; |
9 | 11 | ||
10 | pub(crate) fn move_bounds_to_where_clause(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 12 | pub(crate) fn move_bounds_to_where_clause(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { |
11 | let type_param_list = ctx.node_at_offset::<ast::TypeParamList>()?; | 13 | let type_param_list = ctx.node_at_offset::<ast::TypeParamList>()?; |
@@ -39,14 +41,12 @@ pub(crate) fn move_bounds_to_where_clause(mut ctx: AssistCtx<impl HirDatabase>) | |||
39 | .type_params() | 41 | .type_params() |
40 | .filter(|it| it.type_bound_list().is_some()) | 42 | .filter(|it| it.type_bound_list().is_some()) |
41 | .map(|type_param| { | 43 | .map(|type_param| { |
42 | let without_bounds = | 44 | let without_bounds = type_param.remove_bounds(); |
43 | AstEditor::new(type_param.clone()).remove_bounds().ast().clone(); | ||
44 | (type_param, without_bounds) | 45 | (type_param, without_bounds) |
45 | }); | 46 | }); |
46 | 47 | ||
47 | let mut ast_editor = AstEditor::new(type_param_list.clone()); | 48 | let new_type_param_list = edit::replace_descendants(&type_param_list, new_params); |
48 | ast_editor.replace_descendants(new_params); | 49 | edit.replace_ast(type_param_list.clone(), new_type_param_list); |
49 | ast_editor.into_text_edit(edit.text_edit_builder()); | ||
50 | 50 | ||
51 | let where_clause = { | 51 | let where_clause = { |
52 | let predicates = type_param_list.type_params().filter_map(build_predicate); | 52 | let predicates = type_param_list.type_params().filter_map(build_predicate); |
diff --git a/crates/ra_assists/src/assists/move_guard.rs b/crates/ra_assists/src/assists/move_guard.rs index 699221e33..51aea6334 100644 --- a/crates/ra_assists/src/assists/move_guard.rs +++ b/crates/ra_assists/src/assists/move_guard.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
1 | use hir::db::HirDatabase; | 3 | use hir::db::HirDatabase; |
2 | use ra_syntax::{ | 4 | use ra_syntax::{ |
3 | ast, | 5 | ast, |
diff --git a/crates/ra_assists/src/assists/raw_string.rs b/crates/ra_assists/src/assists/raw_string.rs index 200aaa59a..2d2e31e51 100644 --- a/crates/ra_assists/src/assists/raw_string.rs +++ b/crates/ra_assists/src/assists/raw_string.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
1 | use hir::db::HirDatabase; | 3 | use hir::db::HirDatabase; |
2 | use ra_syntax::{ast::AstNode, ast::Literal, TextRange, TextUnit}; | 4 | use ra_syntax::{ast::AstNode, ast::Literal, TextRange, TextUnit}; |
3 | use rustc_lexer; | 5 | use rustc_lexer; |
diff --git a/crates/ra_assists/src/assists/remove_dbg.rs b/crates/ra_assists/src/assists/remove_dbg.rs index 870133fda..1a7e2b305 100644 --- a/crates/ra_assists/src/assists/remove_dbg.rs +++ b/crates/ra_assists/src/assists/remove_dbg.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
1 | use crate::{Assist, AssistCtx, AssistId}; | 3 | use crate::{Assist, AssistCtx, AssistId}; |
2 | use hir::db::HirDatabase; | 4 | use hir::db::HirDatabase; |
3 | use ra_syntax::{ | 5 | use ra_syntax::{ |
diff --git a/crates/ra_assists/src/assists/replace_if_let_with_match.rs b/crates/ra_assists/src/assists/replace_if_let_with_match.rs index 401835c57..749ff338a 100644 --- a/crates/ra_assists/src/assists/replace_if_let_with_match.rs +++ b/crates/ra_assists/src/assists/replace_if_let_with_match.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
1 | use format_buf::format; | 3 | use format_buf::format; |
2 | use hir::db::HirDatabase; | 4 | use hir::db::HirDatabase; |
3 | use ra_fmt::extract_trivial_expression; | 5 | use ra_fmt::extract_trivial_expression; |
diff --git a/crates/ra_assists/src/assists/split_import.rs b/crates/ra_assists/src/assists/split_import.rs index 2c1edddb9..fe3e64af5 100644 --- a/crates/ra_assists/src/assists/split_import.rs +++ b/crates/ra_assists/src/assists/split_import.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
1 | use std::iter::successors; | 3 | use std::iter::successors; |
2 | 4 | ||
3 | use hir::db::HirDatabase; | 5 | use hir::db::HirDatabase; |
@@ -49,13 +51,13 @@ mod tests { | |||
49 | fn split_import_works_with_trees() { | 51 | fn split_import_works_with_trees() { |
50 | check_assist( | 52 | check_assist( |
51 | split_import, | 53 | split_import, |
52 | "use algo:<|>:visitor::{Visitor, visit}", | 54 | "use crate:<|>:db::{RootDatabase, FileSymbol}", |
53 | "use algo::{<|>visitor::{Visitor, visit}}", | 55 | "use crate::{<|>db::{RootDatabase, FileSymbol}}", |
54 | ) | 56 | ) |
55 | } | 57 | } |
56 | 58 | ||
57 | #[test] | 59 | #[test] |
58 | fn split_import_target() { | 60 | fn split_import_target() { |
59 | check_assist_target(split_import, "use algo::<|>visitor::{Visitor, visit}", "::"); | 61 | check_assist_target(split_import, "use crate::<|>db::{RootDatabase, FileSymbol}", "::"); |
60 | } | 62 | } |
61 | } | 63 | } |
diff --git a/crates/ra_assists/src/ast_editor.rs b/crates/ra_assists/src/ast_editor.rs deleted file mode 100644 index 2a685f26e..000000000 --- a/crates/ra_assists/src/ast_editor.rs +++ /dev/null | |||
@@ -1,315 +0,0 @@ | |||
1 | use std::{iter, ops::RangeInclusive}; | ||
2 | |||
3 | use arrayvec::ArrayVec; | ||
4 | use rustc_hash::FxHashMap; | ||
5 | |||
6 | use ra_fmt::leading_indent; | ||
7 | use ra_syntax::{ | ||
8 | algo, | ||
9 | ast::{self, TypeBoundsOwner}, | ||
10 | AstNode, Direction, InsertPosition, SyntaxElement, | ||
11 | SyntaxKind::*, | ||
12 | T, | ||
13 | }; | ||
14 | use ra_text_edit::TextEditBuilder; | ||
15 | |||
16 | pub struct AstEditor<N: AstNode> { | ||
17 | original_ast: N, | ||
18 | ast: N, | ||
19 | } | ||
20 | |||
21 | impl<N: AstNode> AstEditor<N> { | ||
22 | pub fn new(node: N) -> AstEditor<N> | ||
23 | where | ||
24 | N: Clone, | ||
25 | { | ||
26 | AstEditor { original_ast: node.clone(), ast: node } | ||
27 | } | ||
28 | |||
29 | pub fn into_text_edit(self, builder: &mut TextEditBuilder) { | ||
30 | for (from, to) in algo::diff(&self.original_ast.syntax(), self.ast().syntax()) { | ||
31 | builder.replace(from.text_range(), to.to_string()) | ||
32 | } | ||
33 | } | ||
34 | |||
35 | pub fn ast(&self) -> &N { | ||
36 | &self.ast | ||
37 | } | ||
38 | |||
39 | pub fn replace_descendants<T: AstNode>( | ||
40 | &mut self, | ||
41 | replacement_map: impl Iterator<Item = (T, T)>, | ||
42 | ) -> &mut Self { | ||
43 | let map = replacement_map | ||
44 | .map(|(from, to)| (from.syntax().clone().into(), to.syntax().clone().into())) | ||
45 | .collect::<FxHashMap<_, _>>(); | ||
46 | let new_syntax = algo::replace_descendants(self.ast.syntax(), &map); | ||
47 | self.ast = N::cast(new_syntax).unwrap(); | ||
48 | self | ||
49 | } | ||
50 | |||
51 | #[must_use] | ||
52 | fn insert_children( | ||
53 | &self, | ||
54 | position: InsertPosition<SyntaxElement>, | ||
55 | mut to_insert: impl Iterator<Item = SyntaxElement>, | ||
56 | ) -> N { | ||
57 | let new_syntax = algo::insert_children(self.ast().syntax(), position, &mut to_insert); | ||
58 | N::cast(new_syntax).unwrap() | ||
59 | } | ||
60 | |||
61 | #[must_use] | ||
62 | fn replace_children( | ||
63 | &self, | ||
64 | to_delete: RangeInclusive<SyntaxElement>, | ||
65 | mut to_insert: impl Iterator<Item = SyntaxElement>, | ||
66 | ) -> N { | ||
67 | let new_syntax = algo::replace_children(self.ast().syntax(), to_delete, &mut to_insert); | ||
68 | N::cast(new_syntax).unwrap() | ||
69 | } | ||
70 | |||
71 | fn do_make_multiline(&mut self) { | ||
72 | let l_curly = | ||
73 | match self.ast().syntax().children_with_tokens().find(|it| it.kind() == T!['{']) { | ||
74 | Some(it) => it, | ||
75 | None => return, | ||
76 | }; | ||
77 | let sibling = match l_curly.next_sibling_or_token() { | ||
78 | Some(it) => it, | ||
79 | None => return, | ||
80 | }; | ||
81 | let existing_ws = match sibling.as_token() { | ||
82 | None => None, | ||
83 | Some(tok) if tok.kind() != WHITESPACE => None, | ||
84 | Some(ws) => { | ||
85 | if ws.text().contains('\n') { | ||
86 | return; | ||
87 | } | ||
88 | Some(ws.clone()) | ||
89 | } | ||
90 | }; | ||
91 | |||
92 | let indent = leading_indent(self.ast().syntax()).unwrap_or("".into()); | ||
93 | let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); | ||
94 | let to_insert = iter::once(ws.ws().into()); | ||
95 | self.ast = match existing_ws { | ||
96 | None => self.insert_children(InsertPosition::After(l_curly), to_insert), | ||
97 | Some(ws) => { | ||
98 | self.replace_children(RangeInclusive::new(ws.clone().into(), ws.into()), to_insert) | ||
99 | } | ||
100 | }; | ||
101 | } | ||
102 | } | ||
103 | |||
104 | impl AstEditor<ast::RecordFieldList> { | ||
105 | pub fn append_field(&mut self, field: &ast::RecordField) { | ||
106 | self.insert_field(InsertPosition::Last, field) | ||
107 | } | ||
108 | |||
109 | pub fn insert_field( | ||
110 | &mut self, | ||
111 | position: InsertPosition<&'_ ast::RecordField>, | ||
112 | field: &ast::RecordField, | ||
113 | ) { | ||
114 | let is_multiline = self.ast().syntax().text().contains_char('\n'); | ||
115 | let ws; | ||
116 | let space = if is_multiline { | ||
117 | ws = tokens::WsBuilder::new(&format!( | ||
118 | "\n{} ", | ||
119 | leading_indent(self.ast().syntax()).unwrap_or("".into()) | ||
120 | )); | ||
121 | ws.ws() | ||
122 | } else { | ||
123 | tokens::single_space() | ||
124 | }; | ||
125 | |||
126 | let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new(); | ||
127 | to_insert.push(space.into()); | ||
128 | to_insert.push(field.syntax().clone().into()); | ||
129 | to_insert.push(tokens::comma().into()); | ||
130 | |||
131 | macro_rules! after_l_curly { | ||
132 | () => {{ | ||
133 | let anchor = match self.l_curly() { | ||
134 | Some(it) => it, | ||
135 | None => return, | ||
136 | }; | ||
137 | InsertPosition::After(anchor) | ||
138 | }}; | ||
139 | } | ||
140 | |||
141 | macro_rules! after_field { | ||
142 | ($anchor:expr) => { | ||
143 | if let Some(comma) = $anchor | ||
144 | .syntax() | ||
145 | .siblings_with_tokens(Direction::Next) | ||
146 | .find(|it| it.kind() == T![,]) | ||
147 | { | ||
148 | InsertPosition::After(comma) | ||
149 | } else { | ||
150 | to_insert.insert(0, tokens::comma().into()); | ||
151 | InsertPosition::After($anchor.syntax().clone().into()) | ||
152 | } | ||
153 | }; | ||
154 | }; | ||
155 | |||
156 | let position = match position { | ||
157 | InsertPosition::First => after_l_curly!(), | ||
158 | InsertPosition::Last => { | ||
159 | if !is_multiline { | ||
160 | // don't insert comma before curly | ||
161 | to_insert.pop(); | ||
162 | } | ||
163 | match self.ast().fields().last() { | ||
164 | Some(it) => after_field!(it), | ||
165 | None => after_l_curly!(), | ||
166 | } | ||
167 | } | ||
168 | InsertPosition::Before(anchor) => { | ||
169 | InsertPosition::Before(anchor.syntax().clone().into()) | ||
170 | } | ||
171 | InsertPosition::After(anchor) => after_field!(anchor), | ||
172 | }; | ||
173 | |||
174 | self.ast = self.insert_children(position, to_insert.iter().cloned()); | ||
175 | } | ||
176 | |||
177 | fn l_curly(&self) -> Option<SyntaxElement> { | ||
178 | self.ast().syntax().children_with_tokens().find(|it| it.kind() == T!['{']) | ||
179 | } | ||
180 | } | ||
181 | |||
182 | impl AstEditor<ast::ItemList> { | ||
183 | pub fn append_items(&mut self, items: impl Iterator<Item = ast::ImplItem>) { | ||
184 | if !self.ast().syntax().text().contains_char('\n') { | ||
185 | self.do_make_multiline(); | ||
186 | } | ||
187 | items.for_each(|it| self.append_item(it)); | ||
188 | } | ||
189 | |||
190 | pub fn append_item(&mut self, item: ast::ImplItem) { | ||
191 | let (indent, position) = match self.ast().impl_items().last() { | ||
192 | Some(it) => ( | ||
193 | leading_indent(it.syntax()).unwrap_or_default().to_string(), | ||
194 | InsertPosition::After(it.syntax().clone().into()), | ||
195 | ), | ||
196 | None => match self.l_curly() { | ||
197 | Some(it) => ( | ||
198 | " ".to_string() + &leading_indent(self.ast().syntax()).unwrap_or_default(), | ||
199 | InsertPosition::After(it), | ||
200 | ), | ||
201 | None => return, | ||
202 | }, | ||
203 | }; | ||
204 | let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); | ||
205 | let to_insert: ArrayVec<[SyntaxElement; 2]> = | ||
206 | [ws.ws().into(), item.syntax().clone().into()].into(); | ||
207 | self.ast = self.insert_children(position, to_insert.into_iter()); | ||
208 | } | ||
209 | |||
210 | fn l_curly(&self) -> Option<SyntaxElement> { | ||
211 | self.ast().syntax().children_with_tokens().find(|it| it.kind() == T!['{']) | ||
212 | } | ||
213 | } | ||
214 | |||
215 | impl AstEditor<ast::ImplItem> { | ||
216 | pub fn strip_attrs_and_docs(&mut self) { | ||
217 | while let Some(start) = self | ||
218 | .ast() | ||
219 | .syntax() | ||
220 | .children_with_tokens() | ||
221 | .find(|it| it.kind() == ATTR || it.kind() == COMMENT) | ||
222 | { | ||
223 | let end = match &start.next_sibling_or_token() { | ||
224 | Some(el) if el.kind() == WHITESPACE => el.clone(), | ||
225 | Some(_) | None => start.clone(), | ||
226 | }; | ||
227 | self.ast = self.replace_children(RangeInclusive::new(start, end), iter::empty()); | ||
228 | } | ||
229 | } | ||
230 | } | ||
231 | |||
232 | impl AstEditor<ast::FnDef> { | ||
233 | pub fn set_body(&mut self, body: &ast::Block) { | ||
234 | let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new(); | ||
235 | let old_body_or_semi: SyntaxElement = if let Some(old_body) = self.ast().body() { | ||
236 | old_body.syntax().clone().into() | ||
237 | } else if let Some(semi) = self.ast().semicolon_token() { | ||
238 | to_insert.push(tokens::single_space().into()); | ||
239 | semi.into() | ||
240 | } else { | ||
241 | to_insert.push(tokens::single_space().into()); | ||
242 | to_insert.push(body.syntax().clone().into()); | ||
243 | self.ast = self.insert_children(InsertPosition::Last, to_insert.into_iter()); | ||
244 | return; | ||
245 | }; | ||
246 | to_insert.push(body.syntax().clone().into()); | ||
247 | let replace_range = RangeInclusive::new(old_body_or_semi.clone(), old_body_or_semi); | ||
248 | self.ast = self.replace_children(replace_range, to_insert.into_iter()) | ||
249 | } | ||
250 | } | ||
251 | |||
252 | impl AstEditor<ast::TypeParam> { | ||
253 | pub fn remove_bounds(&mut self) -> &mut Self { | ||
254 | let colon = match self.ast.colon_token() { | ||
255 | Some(it) => it, | ||
256 | None => return self, | ||
257 | }; | ||
258 | let end = match self.ast.type_bound_list() { | ||
259 | Some(it) => it.syntax().clone().into(), | ||
260 | None => colon.clone().into(), | ||
261 | }; | ||
262 | self.ast = self.replace_children(RangeInclusive::new(colon.into(), end), iter::empty()); | ||
263 | self | ||
264 | } | ||
265 | } | ||
266 | |||
267 | mod tokens { | ||
268 | use once_cell::sync::Lazy; | ||
269 | use ra_syntax::{AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken, T}; | ||
270 | |||
271 | static SOURCE_FILE: Lazy<Parse<SourceFile>> = Lazy::new(|| SourceFile::parse(",\n; ;")); | ||
272 | |||
273 | pub(crate) fn comma() -> SyntaxToken { | ||
274 | SOURCE_FILE | ||
275 | .tree() | ||
276 | .syntax() | ||
277 | .descendants_with_tokens() | ||
278 | .filter_map(|it| it.into_token()) | ||
279 | .find(|it| it.kind() == T![,]) | ||
280 | .unwrap() | ||
281 | } | ||
282 | |||
283 | pub(crate) fn single_space() -> SyntaxToken { | ||
284 | SOURCE_FILE | ||
285 | .tree() | ||
286 | .syntax() | ||
287 | .descendants_with_tokens() | ||
288 | .filter_map(|it| it.into_token()) | ||
289 | .find(|it| it.kind() == WHITESPACE && it.text().as_str() == " ") | ||
290 | .unwrap() | ||
291 | } | ||
292 | |||
293 | #[allow(unused)] | ||
294 | pub(crate) fn single_newline() -> SyntaxToken { | ||
295 | SOURCE_FILE | ||
296 | .tree() | ||
297 | .syntax() | ||
298 | .descendants_with_tokens() | ||
299 | .filter_map(|it| it.into_token()) | ||
300 | .find(|it| it.kind() == WHITESPACE && it.text().as_str() == "\n") | ||
301 | .unwrap() | ||
302 | } | ||
303 | |||
304 | pub(crate) struct WsBuilder(SourceFile); | ||
305 | |||
306 | impl WsBuilder { | ||
307 | pub(crate) fn new(text: &str) -> WsBuilder { | ||
308 | WsBuilder(SourceFile::parse(text).ok().unwrap()) | ||
309 | } | ||
310 | pub(crate) fn ws(&self) -> SyntaxToken { | ||
311 | self.0.syntax().first_child_or_token().unwrap().into_token().unwrap() | ||
312 | } | ||
313 | } | ||
314 | |||
315 | } | ||
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 3ca3320f7..d2376c475 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -7,7 +7,6 @@ | |||
7 | 7 | ||
8 | mod assist_ctx; | 8 | mod assist_ctx; |
9 | mod marks; | 9 | mod marks; |
10 | pub mod ast_editor; | ||
11 | 10 | ||
12 | use hir::db::HirDatabase; | 11 | use hir::db::HirDatabase; |
13 | use itertools::Itertools; | 12 | use itertools::Itertools; |
@@ -93,6 +92,7 @@ mod assists { | |||
93 | mod add_derive; | 92 | mod add_derive; |
94 | mod add_explicit_type; | 93 | mod add_explicit_type; |
95 | mod add_impl; | 94 | mod add_impl; |
95 | mod apply_demorgan; | ||
96 | mod flip_comma; | 96 | mod flip_comma; |
97 | mod flip_binexpr; | 97 | mod flip_binexpr; |
98 | mod change_visibility; | 98 | mod change_visibility; |
@@ -114,6 +114,7 @@ mod assists { | |||
114 | add_derive::add_derive, | 114 | add_derive::add_derive, |
115 | add_explicit_type::add_explicit_type, | 115 | add_explicit_type::add_explicit_type, |
116 | add_impl::add_impl, | 116 | add_impl::add_impl, |
117 | apply_demorgan::apply_demorgan, | ||
117 | change_visibility::change_visibility, | 118 | change_visibility::change_visibility, |
118 | fill_match_arms::fill_match_arms, | 119 | fill_match_arms::fill_match_arms, |
119 | merge_match_arms::merge_match_arms, | 120 | merge_match_arms::merge_match_arms, |
diff --git a/crates/ra_assists/src/marks.rs b/crates/ra_assists/src/marks.rs index a29f9f658..c20e4db9e 100644 --- a/crates/ra_assists/src/marks.rs +++ b/crates/ra_assists/src/marks.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | //! See test_utils/src/marks.rs | ||
2 | |||
1 | test_utils::marks!( | 3 | test_utils::marks!( |
2 | introduce_var_in_comment_is_not_applicable | 4 | introduce_var_in_comment_is_not_applicable |
3 | test_introduce_var_expr_stmt | 5 | test_introduce_var_expr_stmt |