aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-02-07 16:28:33 +0000
committerGitHub <[email protected]>2020-02-07 16:28:33 +0000
commit5397f05bfe7f3b18229a65040c6685e762b2f9a3 (patch)
treea3c4aab400ffe1c84bd33e094a047798e7136d2d
parent1996762b1f2b9cb196cc879f0ce26d28a3c450c8 (diff)
parentd00add1f1fec59494c3c1a99c27937ae3891458d (diff)
Merge #3049
3049: Introduce assists utils r=matklad a=matklad Co-authored-by: Aleksey Kladov <[email protected]>
-rw-r--r--crates/ra_assists/src/assist_ctx.rs25
-rw-r--r--crates/ra_assists/src/handlers/add_custom_impl.rs (renamed from crates/ra_assists/src/assists/add_custom_impl.rs)0
-rw-r--r--crates/ra_assists/src/handlers/add_derive.rs (renamed from crates/ra_assists/src/assists/add_derive.rs)0
-rw-r--r--crates/ra_assists/src/handlers/add_explicit_type.rs (renamed from crates/ra_assists/src/assists/add_explicit_type.rs)0
-rw-r--r--crates/ra_assists/src/handlers/add_impl.rs (renamed from crates/ra_assists/src/assists/add_impl.rs)0
-rw-r--r--crates/ra_assists/src/handlers/add_import.rs (renamed from crates/ra_assists/src/assists/add_import.rs)0
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs (renamed from crates/ra_assists/src/assists/add_missing_impl_members.rs)0
-rw-r--r--crates/ra_assists/src/handlers/add_new.rs (renamed from crates/ra_assists/src/assists/add_new.rs)0
-rw-r--r--crates/ra_assists/src/handlers/apply_demorgan.rs (renamed from crates/ra_assists/src/assists/apply_demorgan.rs)3
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs (renamed from crates/ra_assists/src/assists/auto_import.rs)9
-rw-r--r--crates/ra_assists/src/handlers/change_visibility.rs (renamed from crates/ra_assists/src/assists/change_visibility.rs)0
-rw-r--r--crates/ra_assists/src/handlers/early_return.rs (renamed from crates/ra_assists/src/assists/early_return.rs)2
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs (renamed from crates/ra_assists/src/assists/fill_match_arms.rs)0
-rw-r--r--crates/ra_assists/src/handlers/flip_binexpr.rs (renamed from crates/ra_assists/src/assists/flip_binexpr.rs)0
-rw-r--r--crates/ra_assists/src/handlers/flip_comma.rs (renamed from crates/ra_assists/src/assists/flip_comma.rs)0
-rw-r--r--crates/ra_assists/src/handlers/flip_trait_bound.rs (renamed from crates/ra_assists/src/assists/flip_trait_bound.rs)0
-rw-r--r--crates/ra_assists/src/handlers/inline_local_variable.rs (renamed from crates/ra_assists/src/assists/inline_local_variable.rs)0
-rw-r--r--crates/ra_assists/src/handlers/introduce_variable.rs (renamed from crates/ra_assists/src/assists/introduce_variable.rs)0
-rw-r--r--crates/ra_assists/src/handlers/invert_if.rs (renamed from crates/ra_assists/src/assists/invert_if.rs)25
-rw-r--r--crates/ra_assists/src/handlers/merge_match_arms.rs (renamed from crates/ra_assists/src/assists/merge_match_arms.rs)0
-rw-r--r--crates/ra_assists/src/handlers/move_bounds.rs (renamed from crates/ra_assists/src/assists/move_bounds.rs)0
-rw-r--r--crates/ra_assists/src/handlers/move_guard.rs (renamed from crates/ra_assists/src/assists/move_guard.rs)0
-rw-r--r--crates/ra_assists/src/handlers/raw_string.rs (renamed from crates/ra_assists/src/assists/raw_string.rs)0
-rw-r--r--crates/ra_assists/src/handlers/remove_dbg.rs (renamed from crates/ra_assists/src/assists/remove_dbg.rs)0
-rw-r--r--crates/ra_assists/src/handlers/replace_if_let_with_match.rs (renamed from crates/ra_assists/src/assists/replace_if_let_with_match.rs)0
-rw-r--r--crates/ra_assists/src/handlers/split_import.rs (renamed from crates/ra_assists/src/assists/split_import.rs)0
-rw-r--r--crates/ra_assists/src/lib.rs111
-rw-r--r--crates/ra_assists/src/utils.rs27
-rw-r--r--xtask/src/codegen.rs2
-rw-r--r--xtask/tests/tidy-tests/docs.rs2
30 files changed, 97 insertions, 109 deletions
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs
index f32072dbd..81f999090 100644
--- a/crates/ra_assists/src/assist_ctx.rs
+++ b/crates/ra_assists/src/assist_ctx.rs
@@ -19,6 +19,8 @@ pub(crate) enum Assist {
19 Resolved { assist: ResolvedAssist }, 19 Resolved { assist: ResolvedAssist },
20} 20}
21 21
22pub(crate) type AssistHandler = fn(AssistCtx) -> Option<Assist>;
23
22/// `AssistCtx` allows to apply an assist or check if it could be applied. 24/// `AssistCtx` allows to apply an assist or check if it could be applied.
23/// 25///
24/// Assists use a somewhat over-engineered approach, given the current needs. The 26/// Assists use a somewhat over-engineered approach, given the current needs. The
@@ -57,7 +59,7 @@ pub(crate) struct AssistCtx<'a> {
57 should_compute_edit: bool, 59 should_compute_edit: bool,
58} 60}
59 61
60impl<'a> Clone for AssistCtx<'a> { 62impl Clone for AssistCtx<'_> {
61 fn clone(&self) -> Self { 63 fn clone(&self) -> Self {
62 AssistCtx { 64 AssistCtx {
63 db: self.db, 65 db: self.db,
@@ -69,31 +71,18 @@ impl<'a> Clone for AssistCtx<'a> {
69} 71}
70 72
71impl<'a> AssistCtx<'a> { 73impl<'a> AssistCtx<'a> {
72 pub(crate) fn with_ctx<F, T>( 74 pub fn new(db: &RootDatabase, frange: FileRange, should_compute_edit: bool) -> AssistCtx {
73 db: &RootDatabase,
74 frange: FileRange,
75 should_compute_edit: bool,
76 f: F,
77 ) -> T
78 where
79 F: FnOnce(AssistCtx) -> T,
80 {
81 let parse = db.parse(frange.file_id); 75 let parse = db.parse(frange.file_id);
82 76 AssistCtx { db, frange, source_file: parse.tree(), should_compute_edit }
83 let ctx = AssistCtx { db, frange, source_file: parse.tree(), should_compute_edit };
84 f(ctx)
85 } 77 }
86}
87 78
88impl<'a> AssistCtx<'a> {
89 pub(crate) fn add_assist( 79 pub(crate) fn add_assist(
90 self, 80 self,
91 id: AssistId, 81 id: AssistId,
92 label: impl Into<String>, 82 label: impl Into<String>,
93 f: impl FnOnce(&mut ActionBuilder), 83 f: impl FnOnce(&mut ActionBuilder),
94 ) -> Option<Assist> { 84 ) -> Option<Assist> {
95 let label = AssistLabel { label: label.into(), id }; 85 let label = AssistLabel::new(label.into(), id);
96 assert!(label.label.chars().nth(0).unwrap().is_uppercase());
97 86
98 let assist = if self.should_compute_edit { 87 let assist = if self.should_compute_edit {
99 let action = { 88 let action = {
@@ -115,7 +104,7 @@ impl<'a> AssistCtx<'a> {
115 label: impl Into<String>, 104 label: impl Into<String>,
116 f: impl FnOnce() -> Vec<ActionBuilder>, 105 f: impl FnOnce() -> Vec<ActionBuilder>,
117 ) -> Option<Assist> { 106 ) -> Option<Assist> {
118 let label = AssistLabel { label: label.into(), id }; 107 let label = AssistLabel::new(label.into(), id);
119 let assist = if self.should_compute_edit { 108 let assist = if self.should_compute_edit {
120 let actions = f(); 109 let actions = f();
121 assert!(!actions.is_empty(), "Assist cannot have no"); 110 assert!(!actions.is_empty(), "Assist cannot have no");
diff --git a/crates/ra_assists/src/assists/add_custom_impl.rs b/crates/ra_assists/src/handlers/add_custom_impl.rs
index 7fdd816bf..7fdd816bf 100644
--- a/crates/ra_assists/src/assists/add_custom_impl.rs
+++ b/crates/ra_assists/src/handlers/add_custom_impl.rs
diff --git a/crates/ra_assists/src/assists/add_derive.rs b/crates/ra_assists/src/handlers/add_derive.rs
index b0d1a0a80..b0d1a0a80 100644
--- a/crates/ra_assists/src/assists/add_derive.rs
+++ b/crates/ra_assists/src/handlers/add_derive.rs
diff --git a/crates/ra_assists/src/assists/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs
index 2cb9d2f48..2cb9d2f48 100644
--- a/crates/ra_assists/src/assists/add_explicit_type.rs
+++ b/crates/ra_assists/src/handlers/add_explicit_type.rs
diff --git a/crates/ra_assists/src/assists/add_impl.rs b/crates/ra_assists/src/handlers/add_impl.rs
index 241b085fd..241b085fd 100644
--- a/crates/ra_assists/src/assists/add_impl.rs
+++ b/crates/ra_assists/src/handlers/add_impl.rs
diff --git a/crates/ra_assists/src/assists/add_import.rs b/crates/ra_assists/src/handlers/add_import.rs
index f03dddac8..f03dddac8 100644
--- a/crates/ra_assists/src/assists/add_import.rs
+++ b/crates/ra_assists/src/handlers/add_import.rs
diff --git a/crates/ra_assists/src/assists/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
index 448697d31..448697d31 100644
--- a/crates/ra_assists/src/assists/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
diff --git a/crates/ra_assists/src/assists/add_new.rs b/crates/ra_assists/src/handlers/add_new.rs
index a08639311..a08639311 100644
--- a/crates/ra_assists/src/assists/add_new.rs
+++ b/crates/ra_assists/src/handlers/add_new.rs
diff --git a/crates/ra_assists/src/assists/apply_demorgan.rs b/crates/ra_assists/src/handlers/apply_demorgan.rs
index ba08a8223..239807e24 100644
--- a/crates/ra_assists/src/assists/apply_demorgan.rs
+++ b/crates/ra_assists/src/handlers/apply_demorgan.rs
@@ -1,7 +1,6 @@
1use super::invert_if::invert_boolean_expression;
2use ra_syntax::ast::{self, AstNode}; 1use ra_syntax::ast::{self, AstNode};
3 2
4use crate::{Assist, AssistCtx, AssistId}; 3use crate::{utils::invert_boolean_expression, Assist, AssistCtx, AssistId};
5 4
6// Assist: apply_demorgan 5// Assist: apply_demorgan
7// 6//
diff --git a/crates/ra_assists/src/assists/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index 10c4b7d7c..84b5474f9 100644
--- a/crates/ra_assists/src/assists/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -1,4 +1,5 @@
1use hir::ModPath; 1use hir::ModPath;
2use ra_ide_db::imports_locator::ImportsLocator;
2use ra_syntax::{ 3use ra_syntax::{
3 ast::{self, AstNode}, 4 ast::{self, AstNode},
4 SyntaxNode, 5 SyntaxNode,
@@ -8,7 +9,7 @@ use crate::{
8 assist_ctx::{ActionBuilder, Assist, AssistCtx}, 9 assist_ctx::{ActionBuilder, Assist, AssistCtx},
9 auto_import_text_edit, AssistId, 10 auto_import_text_edit, AssistId,
10}; 11};
11use ra_ide_db::imports_locator::ImportsLocator; 12use std::collections::BTreeSet;
12 13
13// Assist: auto_import 14// Assist: auto_import
14// 15//
@@ -60,7 +61,8 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> {
60 .filter_map(|module_def| module_with_name_to_import.find_use_path(ctx.db, module_def)) 61 .filter_map(|module_def| module_with_name_to_import.find_use_path(ctx.db, module_def))
61 .filter(|use_path| !use_path.segments.is_empty()) 62 .filter(|use_path| !use_path.segments.is_empty())
62 .take(20) 63 .take(20)
63 .collect::<std::collections::BTreeSet<_>>(); 64 .collect::<BTreeSet<_>>();
65
64 if proposed_imports.is_empty() { 66 if proposed_imports.is_empty() {
65 return None; 67 return None;
66 } 68 }
@@ -82,9 +84,10 @@ fn import_to_action(import: ModPath, position: &SyntaxNode, anchor: &SyntaxNode)
82 84
83#[cfg(test)] 85#[cfg(test)]
84mod tests { 86mod tests {
85 use super::*;
86 use crate::helpers::{check_assist, check_assist_not_applicable}; 87 use crate::helpers::{check_assist, check_assist_not_applicable};
87 88
89 use super::*;
90
88 #[test] 91 #[test]
89 fn applicable_when_found_an_import() { 92 fn applicable_when_found_an_import() {
90 check_assist( 93 check_assist(
diff --git a/crates/ra_assists/src/assists/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs
index f325b6f92..f325b6f92 100644
--- a/crates/ra_assists/src/assists/change_visibility.rs
+++ b/crates/ra_assists/src/handlers/change_visibility.rs
diff --git a/crates/ra_assists/src/assists/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs
index 8f30dc586..22f88884f 100644
--- a/crates/ra_assists/src/assists/early_return.rs
+++ b/crates/ra_assists/src/handlers/early_return.rs
@@ -10,7 +10,7 @@ use ra_syntax::{
10 10
11use crate::{ 11use crate::{
12 assist_ctx::{Assist, AssistCtx}, 12 assist_ctx::{Assist, AssistCtx},
13 assists::invert_if::invert_boolean_expression, 13 utils::invert_boolean_expression,
14 AssistId, 14 AssistId,
15}; 15};
16 16
diff --git a/crates/ra_assists/src/assists/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index 0908fc246..0908fc246 100644
--- a/crates/ra_assists/src/assists/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
diff --git a/crates/ra_assists/src/assists/flip_binexpr.rs b/crates/ra_assists/src/handlers/flip_binexpr.rs
index bfcc09e90..bfcc09e90 100644
--- a/crates/ra_assists/src/assists/flip_binexpr.rs
+++ b/crates/ra_assists/src/handlers/flip_binexpr.rs
diff --git a/crates/ra_assists/src/assists/flip_comma.rs b/crates/ra_assists/src/handlers/flip_comma.rs
index 1dacf29f8..1dacf29f8 100644
--- a/crates/ra_assists/src/assists/flip_comma.rs
+++ b/crates/ra_assists/src/handlers/flip_comma.rs
diff --git a/crates/ra_assists/src/assists/flip_trait_bound.rs b/crates/ra_assists/src/handlers/flip_trait_bound.rs
index f56769624..f56769624 100644
--- a/crates/ra_assists/src/assists/flip_trait_bound.rs
+++ b/crates/ra_assists/src/handlers/flip_trait_bound.rs
diff --git a/crates/ra_assists/src/assists/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs
index 91b588243..91b588243 100644
--- a/crates/ra_assists/src/assists/inline_local_variable.rs
+++ b/crates/ra_assists/src/handlers/inline_local_variable.rs
diff --git a/crates/ra_assists/src/assists/introduce_variable.rs b/crates/ra_assists/src/handlers/introduce_variable.rs
index 7312ce687..7312ce687 100644
--- a/crates/ra_assists/src/assists/introduce_variable.rs
+++ b/crates/ra_assists/src/handlers/introduce_variable.rs
diff --git a/crates/ra_assists/src/assists/invert_if.rs b/crates/ra_assists/src/handlers/invert_if.rs
index 983392f21..a594e7e0c 100644
--- a/crates/ra_assists/src/assists/invert_if.rs
+++ b/crates/ra_assists/src/handlers/invert_if.rs
@@ -1,7 +1,7 @@
1use ra_syntax::ast::{self, make, AstNode}; 1use ra_syntax::ast::{self, AstNode};
2use ra_syntax::T; 2use ra_syntax::T;
3 3
4use crate::{Assist, AssistCtx, AssistId}; 4use crate::{utils::invert_boolean_expression, Assist, AssistCtx, AssistId};
5 5
6// Assist: invert_if 6// Assist: invert_if
7// 7//
@@ -51,27 +51,6 @@ pub(crate) fn invert_if(ctx: AssistCtx) -> Option<Assist> {
51 None 51 None
52} 52}
53 53
54pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr {
55 if let Some(expr) = invert_special_case(&expr) {
56 return expr;
57 }
58 make::expr_prefix(T![!], expr)
59}
60
61pub(crate) fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
62 match expr {
63 ast::Expr::BinExpr(bin) => match bin.op_kind()? {
64 ast::BinOp::NegatedEqualityTest => bin.replace_op(T![==]).map(|it| it.into()),
65 ast::BinOp::EqualityTest => bin.replace_op(T![!=]).map(|it| it.into()),
66 _ => None,
67 },
68 ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::PrefixOp::Not => pe.expr(),
69 // FIXME:
70 // ast::Expr::Literal(true | false )
71 _ => None,
72 }
73}
74
75#[cfg(test)] 54#[cfg(test)]
76mod tests { 55mod tests {
77 use super::*; 56 use super::*;
diff --git a/crates/ra_assists/src/assists/merge_match_arms.rs b/crates/ra_assists/src/handlers/merge_match_arms.rs
index 670614dd8..670614dd8 100644
--- a/crates/ra_assists/src/assists/merge_match_arms.rs
+++ b/crates/ra_assists/src/handlers/merge_match_arms.rs
diff --git a/crates/ra_assists/src/assists/move_bounds.rs b/crates/ra_assists/src/handlers/move_bounds.rs
index 90793b5fc..90793b5fc 100644
--- a/crates/ra_assists/src/assists/move_bounds.rs
+++ b/crates/ra_assists/src/handlers/move_bounds.rs
diff --git a/crates/ra_assists/src/assists/move_guard.rs b/crates/ra_assists/src/handlers/move_guard.rs
index 2b91ce7c4..2b91ce7c4 100644
--- a/crates/ra_assists/src/assists/move_guard.rs
+++ b/crates/ra_assists/src/handlers/move_guard.rs
diff --git a/crates/ra_assists/src/assists/raw_string.rs b/crates/ra_assists/src/handlers/raw_string.rs
index 2c0a1e126..2c0a1e126 100644
--- a/crates/ra_assists/src/assists/raw_string.rs
+++ b/crates/ra_assists/src/handlers/raw_string.rs
diff --git a/crates/ra_assists/src/assists/remove_dbg.rs b/crates/ra_assists/src/handlers/remove_dbg.rs
index 5085649b4..5085649b4 100644
--- a/crates/ra_assists/src/assists/remove_dbg.rs
+++ b/crates/ra_assists/src/handlers/remove_dbg.rs
diff --git a/crates/ra_assists/src/assists/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
index e6cd50bc1..e6cd50bc1 100644
--- a/crates/ra_assists/src/assists/replace_if_let_with_match.rs
+++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
diff --git a/crates/ra_assists/src/assists/split_import.rs b/crates/ra_assists/src/handlers/split_import.rs
index 2c3f07a79..2c3f07a79 100644
--- a/crates/ra_assists/src/assists/split_import.rs
+++ b/crates/ra_assists/src/handlers/split_import.rs
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 3f3df3f96..eca6dec4b 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -9,16 +9,19 @@ mod assist_ctx;
9mod marks; 9mod marks;
10#[cfg(test)] 10#[cfg(test)]
11mod doc_tests; 11mod doc_tests;
12mod utils;
12pub mod ast_transform; 13pub mod ast_transform;
13 14
15use std::cmp::Ordering;
16
14use either::Either; 17use either::Either;
15use ra_db::FileRange; 18use ra_db::FileRange;
16use ra_ide_db::RootDatabase; 19use ra_ide_db::RootDatabase;
17use ra_syntax::{TextRange, TextUnit}; 20use ra_syntax::{TextRange, TextUnit};
18use ra_text_edit::TextEdit; 21use ra_text_edit::TextEdit;
19 22
20pub(crate) use crate::assist_ctx::{Assist, AssistCtx}; 23pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler};
21pub use crate::assists::add_import::auto_import_text_edit; 24pub use crate::handlers::add_import::auto_import_text_edit;
22 25
23/// Unique identifier of the assist, should not be shown to the user 26/// Unique identifier of the assist, should not be shown to the user
24/// directly. 27/// directly.
@@ -32,11 +35,20 @@ pub struct AssistLabel {
32 pub id: AssistId, 35 pub id: AssistId,
33} 36}
34 37
38impl AssistLabel {
39 pub(crate) fn new(label: String, id: AssistId) -> AssistLabel {
40 // FIXME: make fields private, so that this invariant can't be broken
41 assert!(label.chars().nth(0).unwrap().is_uppercase());
42 AssistLabel { label: label.into(), id }
43 }
44}
45
35#[derive(Debug, Clone)] 46#[derive(Debug, Clone)]
36pub struct AssistAction { 47pub struct AssistAction {
37 pub label: Option<String>, 48 pub label: Option<String>,
38 pub edit: TextEdit, 49 pub edit: TextEdit,
39 pub cursor_position: Option<TextUnit>, 50 pub cursor_position: Option<TextUnit>,
51 // FIXME: This belongs to `AssistLabel`
40 pub target: Option<TextRange>, 52 pub target: Option<TextRange>,
41} 53}
42 54
@@ -60,16 +72,15 @@ impl ResolvedAssist {
60/// Assists are returned in the "unresolved" state, that is only labels are 72/// Assists are returned in the "unresolved" state, that is only labels are
61/// returned, without actual edits. 73/// returned, without actual edits.
62pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> { 74pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> {
63 AssistCtx::with_ctx(db, range, false, |ctx| { 75 let ctx = AssistCtx::new(db, range, false);
64 assists::all() 76 handlers::all()
65 .iter() 77 .iter()
66 .filter_map(|f| f(ctx.clone())) 78 .filter_map(|f| f(ctx.clone()))
67 .map(|a| match a { 79 .map(|a| match a {
68 Assist::Unresolved { label } => label, 80 Assist::Unresolved { label } => label,
69 Assist::Resolved { .. } => unreachable!(), 81 Assist::Resolved { .. } => unreachable!(),
70 }) 82 })
71 .collect() 83 .collect()
72 })
73} 84}
74 85
75/// Return all the assists applicable at the given position. 86/// Return all the assists applicable at the given position.
@@ -77,22 +88,20 @@ pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabe
77/// Assists are returned in the "resolved" state, that is with edit fully 88/// Assists are returned in the "resolved" state, that is with edit fully
78/// computed. 89/// computed.
79pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> { 90pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> {
80 AssistCtx::with_ctx(db, range, true, |ctx| { 91 let ctx = AssistCtx::new(db, range, true);
81 let mut a = assists::all() 92 let mut a = handlers::all()
82 .iter() 93 .iter()
83 .filter_map(|f| f(ctx.clone())) 94 .filter_map(|f| f(ctx.clone()))
84 .map(|a| match a { 95 .map(|a| match a {
85 Assist::Resolved { assist } => assist, 96 Assist::Resolved { assist } => assist,
86 Assist::Unresolved { .. } => unreachable!(), 97 Assist::Unresolved { .. } => unreachable!(),
87 }) 98 })
88 .collect(); 99 .collect::<Vec<_>>();
89 sort_assists(&mut a); 100 sort_assists(&mut a);
90 a 101 a
91 })
92} 102}
93 103
94fn sort_assists(assists: &mut Vec<ResolvedAssist>) { 104fn sort_assists(assists: &mut [ResolvedAssist]) {
95 use std::cmp::Ordering;
96 assists.sort_by(|a, b| match (a.get_first_action().target, b.get_first_action().target) { 105 assists.sort_by(|a, b| match (a.get_first_action().target, b.get_first_action().target) {
97 (Some(a), Some(b)) => a.len().cmp(&b.len()), 106 (Some(a), Some(b)) => a.len().cmp(&b.len()),
98 (Some(_), None) => Ordering::Less, 107 (Some(_), None) => Ordering::Less,
@@ -101,8 +110,8 @@ fn sort_assists(assists: &mut Vec<ResolvedAssist>) {
101 }); 110 });
102} 111}
103 112
104mod assists { 113mod handlers {
105 use crate::{Assist, AssistCtx}; 114 use crate::AssistHandler;
106 115
107 mod add_derive; 116 mod add_derive;
108 mod add_explicit_type; 117 mod add_explicit_type;
@@ -130,7 +139,7 @@ mod assists {
130 mod move_bounds; 139 mod move_bounds;
131 mod early_return; 140 mod early_return;
132 141
133 pub(crate) fn all() -> &'static [fn(AssistCtx) -> Option<Assist>] { 142 pub(crate) fn all() -> &'static [AssistHandler] {
134 &[ 143 &[
135 add_derive::add_derive, 144 add_derive::add_derive,
136 add_explicit_type::add_explicit_type, 145 add_explicit_type::add_explicit_type,
@@ -175,7 +184,7 @@ mod helpers {
175 use ra_syntax::TextRange; 184 use ra_syntax::TextRange;
176 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range}; 185 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range};
177 186
178 use crate::{Assist, AssistCtx}; 187 use crate::{Assist, AssistCtx, AssistHandler};
179 188
180 pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { 189 pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
181 let (mut db, file_id) = RootDatabase::with_single_file(text); 190 let (mut db, file_id) = RootDatabase::with_single_file(text);
@@ -186,13 +195,13 @@ mod helpers {
186 (db, file_id) 195 (db, file_id)
187 } 196 }
188 197
189 pub(crate) fn check_assist(assist: fn(AssistCtx) -> Option<Assist>, before: &str, after: &str) { 198 pub(crate) fn check_assist(assist: AssistHandler, before: &str, after: &str) {
190 let (before_cursor_pos, before) = extract_offset(before); 199 let (before_cursor_pos, before) = extract_offset(before);
191 let (db, file_id) = with_single_file(&before); 200 let (db, file_id) = with_single_file(&before);
192 let frange = 201 let frange =
193 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; 202 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
194 let assist = 203 let assist =
195 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); 204 assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable");
196 let action = match assist { 205 let action = match assist {
197 Assist::Unresolved { .. } => unreachable!(), 206 Assist::Unresolved { .. } => unreachable!(),
198 Assist::Resolved { assist } => assist.get_first_action(), 207 Assist::Resolved { assist } => assist.get_first_action(),
@@ -210,16 +219,12 @@ mod helpers {
210 assert_eq_text!(after, &actual); 219 assert_eq_text!(after, &actual);
211 } 220 }
212 221
213 pub(crate) fn check_assist_range( 222 pub(crate) fn check_assist_range(assist: AssistHandler, before: &str, after: &str) {
214 assist: fn(AssistCtx) -> Option<Assist>,
215 before: &str,
216 after: &str,
217 ) {
218 let (range, before) = extract_range(before); 223 let (range, before) = extract_range(before);
219 let (db, file_id) = with_single_file(&before); 224 let (db, file_id) = with_single_file(&before);
220 let frange = FileRange { file_id, range }; 225 let frange = FileRange { file_id, range };
221 let assist = 226 let assist =
222 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); 227 assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable");
223 let action = match assist { 228 let action = match assist {
224 Assist::Unresolved { .. } => unreachable!(), 229 Assist::Unresolved { .. } => unreachable!(),
225 Assist::Resolved { assist } => assist.get_first_action(), 230 Assist::Resolved { assist } => assist.get_first_action(),
@@ -232,17 +237,13 @@ mod helpers {
232 assert_eq_text!(after, &actual); 237 assert_eq_text!(after, &actual);
233 } 238 }
234 239
235 pub(crate) fn check_assist_target( 240 pub(crate) fn check_assist_target(assist: AssistHandler, before: &str, target: &str) {
236 assist: fn(AssistCtx) -> Option<Assist>,
237 before: &str,
238 target: &str,
239 ) {
240 let (before_cursor_pos, before) = extract_offset(before); 241 let (before_cursor_pos, before) = extract_offset(before);
241 let (db, file_id) = with_single_file(&before); 242 let (db, file_id) = with_single_file(&before);
242 let frange = 243 let frange =
243 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; 244 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
244 let assist = 245 let assist =
245 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); 246 assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable");
246 let action = match assist { 247 let action = match assist {
247 Assist::Unresolved { .. } => unreachable!(), 248 Assist::Unresolved { .. } => unreachable!(),
248 Assist::Resolved { assist } => assist.get_first_action(), 249 Assist::Resolved { assist } => assist.get_first_action(),
@@ -252,16 +253,12 @@ mod helpers {
252 assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target); 253 assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
253 } 254 }
254 255
255 pub(crate) fn check_assist_range_target( 256 pub(crate) fn check_assist_range_target(assist: AssistHandler, before: &str, target: &str) {
256 assist: fn(AssistCtx) -> Option<Assist>,
257 before: &str,
258 target: &str,
259 ) {
260 let (range, before) = extract_range(before); 257 let (range, before) = extract_range(before);
261 let (db, file_id) = with_single_file(&before); 258 let (db, file_id) = with_single_file(&before);
262 let frange = FileRange { file_id, range }; 259 let frange = FileRange { file_id, range };
263 let assist = 260 let assist =
264 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); 261 assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable");
265 let action = match assist { 262 let action = match assist {
266 Assist::Unresolved { .. } => unreachable!(), 263 Assist::Unresolved { .. } => unreachable!(),
267 Assist::Resolved { assist } => assist.get_first_action(), 264 Assist::Resolved { assist } => assist.get_first_action(),
@@ -271,26 +268,20 @@ mod helpers {
271 assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target); 268 assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
272 } 269 }
273 270
274 pub(crate) fn check_assist_not_applicable( 271 pub(crate) fn check_assist_not_applicable(assist: AssistHandler, before: &str) {
275 assist: fn(AssistCtx) -> Option<Assist>,
276 before: &str,
277 ) {
278 let (before_cursor_pos, before) = extract_offset(before); 272 let (before_cursor_pos, before) = extract_offset(before);
279 let (db, file_id) = with_single_file(&before); 273 let (db, file_id) = with_single_file(&before);
280 let frange = 274 let frange =
281 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; 275 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
282 let assist = AssistCtx::with_ctx(&db, frange, true, assist); 276 let assist = assist(AssistCtx::new(&db, frange, true));
283 assert!(assist.is_none()); 277 assert!(assist.is_none());
284 } 278 }
285 279
286 pub(crate) fn check_assist_range_not_applicable( 280 pub(crate) fn check_assist_range_not_applicable(assist: AssistHandler, before: &str) {
287 assist: fn(AssistCtx) -> Option<Assist>,
288 before: &str,
289 ) {
290 let (range, before) = extract_range(before); 281 let (range, before) = extract_range(before);
291 let (db, file_id) = with_single_file(&before); 282 let (db, file_id) = with_single_file(&before);
292 let frange = FileRange { file_id, range }; 283 let frange = FileRange { file_id, range };
293 let assist = AssistCtx::with_ctx(&db, frange, true, assist); 284 let assist = assist(AssistCtx::new(&db, frange, true));
294 assert!(assist.is_none()); 285 assert!(assist.is_none());
295 } 286 }
296} 287}
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs
new file mode 100644
index 000000000..0d5722295
--- /dev/null
+++ b/crates/ra_assists/src/utils.rs
@@ -0,0 +1,27 @@
1//! Assorted functions shared by several assists.
2
3use ra_syntax::{
4 ast::{self, make},
5 T,
6};
7
8pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr {
9 if let Some(expr) = invert_special_case(&expr) {
10 return expr;
11 }
12 make::expr_prefix(T![!], expr)
13}
14
15fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
16 match expr {
17 ast::Expr::BinExpr(bin) => match bin.op_kind()? {
18 ast::BinOp::NegatedEqualityTest => bin.replace_op(T![==]).map(|it| it.into()),
19 ast::BinOp::EqualityTest => bin.replace_op(T![!=]).map(|it| it.into()),
20 _ => None,
21 },
22 ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::PrefixOp::Not => pe.expr(),
23 // FIXME:
24 // ast::Expr::Literal(true | false )
25 _ => None,
26 }
27}
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs
index efa638e06..a53d57335 100644
--- a/xtask/src/codegen.rs
+++ b/xtask/src/codegen.rs
@@ -25,7 +25,7 @@ const ERR_INLINE_TESTS_DIR: &str = "crates/ra_syntax/test_data/parser/inline/err
25pub const SYNTAX_KINDS: &str = "crates/ra_parser/src/syntax_kind/generated.rs"; 25pub const SYNTAX_KINDS: &str = "crates/ra_parser/src/syntax_kind/generated.rs";
26pub const AST: &str = "crates/ra_syntax/src/ast/generated.rs"; 26pub const AST: &str = "crates/ra_syntax/src/ast/generated.rs";
27 27
28const ASSISTS_DIR: &str = "crates/ra_assists/src/assists"; 28const ASSISTS_DIR: &str = "crates/ra_assists/src/handlers";
29const ASSISTS_TESTS: &str = "crates/ra_assists/src/doc_tests/generated.rs"; 29const ASSISTS_TESTS: &str = "crates/ra_assists/src/doc_tests/generated.rs";
30const ASSISTS_DOCS: &str = "docs/user/assists.md"; 30const ASSISTS_DOCS: &str = "docs/user/assists.md";
31 31
diff --git a/xtask/tests/tidy-tests/docs.rs b/xtask/tests/tidy-tests/docs.rs
index 8a005d6c4..6a69e7d6a 100644
--- a/xtask/tests/tidy-tests/docs.rs
+++ b/xtask/tests/tidy-tests/docs.rs
@@ -6,7 +6,7 @@ use xtask::project_root;
6fn is_exclude_dir(p: &Path) -> bool { 6fn is_exclude_dir(p: &Path) -> bool {
7 // Test hopefully don't really need comments, and for assists we already 7 // Test hopefully don't really need comments, and for assists we already
8 // have special comments which are source of doc tests and user docs. 8 // have special comments which are source of doc tests and user docs.
9 let exclude_dirs = ["tests", "test_data", "assists"]; 9 let exclude_dirs = ["tests", "test_data", "handlers"];
10 let mut cur_path = p; 10 let mut cur_path = p;
11 while let Some(path) = cur_path.parent() { 11 while let Some(path) = cur_path.parent() {
12 if exclude_dirs.iter().any(|dir| path.ends_with(dir)) { 12 if exclude_dirs.iter().any(|dir| path.ends_with(dir)) {