aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists')
-rw-r--r--crates/ra_assists/src/assist_context.rs19
-rw-r--r--crates/ra_assists/src/handlers/early_return.rs2
-rw-r--r--crates/ra_assists/src/handlers/merge_imports.rs2
-rw-r--r--crates/ra_assists/src/handlers/merge_match_arms.rs2
-rw-r--r--crates/ra_assists/src/handlers/split_import.rs2
-rw-r--r--crates/ra_assists/src/lib.rs80
-rw-r--r--crates/ra_assists/src/tests.rs24
7 files changed, 65 insertions, 66 deletions
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs
index 203ad1273..3085c4330 100644
--- a/crates/ra_assists/src/assist_context.rs
+++ b/crates/ra_assists/src/assist_context.rs
@@ -15,7 +15,7 @@ use ra_syntax::{
15}; 15};
16use ra_text_edit::TextEditBuilder; 16use ra_text_edit::TextEditBuilder;
17 17
18use crate::{AssistId, AssistLabel, GroupLabel, ResolvedAssist}; 18use crate::{Assist, AssistId, GroupLabel, ResolvedAssist};
19 19
20/// `AssistContext` allows to apply an assist or check if it could be applied. 20/// `AssistContext` allows to apply an assist or check if it could be applied.
21/// 21///
@@ -76,8 +76,7 @@ impl<'a> AssistContext<'a> {
76 find_node_at_offset(self.source_file.syntax(), self.offset()) 76 find_node_at_offset(self.source_file.syntax(), self.offset())
77 } 77 }
78 pub(crate) fn find_node_at_offset_with_descend<N: AstNode>(&self) -> Option<N> { 78 pub(crate) fn find_node_at_offset_with_descend<N: AstNode>(&self) -> Option<N> {
79 self.sema 79 self.sema.find_node_at_offset_with_descend(self.source_file.syntax(), self.offset())
80 .find_node_at_offset_with_descend(self.source_file.syntax(), self.frange.range.start())
81 } 80 }
82 pub(crate) fn covering_element(&self) -> SyntaxElement { 81 pub(crate) fn covering_element(&self) -> SyntaxElement {
83 find_covering_element(self.source_file.syntax(), self.frange.range) 82 find_covering_element(self.source_file.syntax(), self.frange.range)
@@ -91,7 +90,7 @@ impl<'a> AssistContext<'a> {
91pub(crate) struct Assists { 90pub(crate) struct Assists {
92 resolve: bool, 91 resolve: bool,
93 file: FileId, 92 file: FileId,
94 buf: Vec<(AssistLabel, Option<SourceChange>)>, 93 buf: Vec<(Assist, Option<SourceChange>)>,
95} 94}
96 95
97impl Assists { 96impl Assists {
@@ -102,7 +101,7 @@ impl Assists {
102 Assists { resolve: false, file: ctx.frange.file_id, buf: Vec::new() } 101 Assists { resolve: false, file: ctx.frange.file_id, buf: Vec::new() }
103 } 102 }
104 103
105 pub(crate) fn finish_unresolved(self) -> Vec<AssistLabel> { 104 pub(crate) fn finish_unresolved(self) -> Vec<Assist> {
106 assert!(!self.resolve); 105 assert!(!self.resolve);
107 self.finish() 106 self.finish()
108 .into_iter() 107 .into_iter()
@@ -117,7 +116,7 @@ impl Assists {
117 assert!(self.resolve); 116 assert!(self.resolve);
118 self.finish() 117 self.finish()
119 .into_iter() 118 .into_iter()
120 .map(|(label, edit)| ResolvedAssist { label, source_change: edit.unwrap() }) 119 .map(|(label, edit)| ResolvedAssist { assist: label, source_change: edit.unwrap() })
121 .collect() 120 .collect()
122 } 121 }
123 122
@@ -128,7 +127,7 @@ impl Assists {
128 target: TextRange, 127 target: TextRange,
129 f: impl FnOnce(&mut AssistBuilder), 128 f: impl FnOnce(&mut AssistBuilder),
130 ) -> Option<()> { 129 ) -> Option<()> {
131 let label = AssistLabel::new(id, label.into(), None, target); 130 let label = Assist::new(id, label.into(), None, target);
132 self.add_impl(label, f) 131 self.add_impl(label, f)
133 } 132 }
134 pub(crate) fn add_group( 133 pub(crate) fn add_group(
@@ -139,10 +138,10 @@ impl Assists {
139 target: TextRange, 138 target: TextRange,
140 f: impl FnOnce(&mut AssistBuilder), 139 f: impl FnOnce(&mut AssistBuilder),
141 ) -> Option<()> { 140 ) -> Option<()> {
142 let label = AssistLabel::new(id, label.into(), Some(group.clone()), target); 141 let label = Assist::new(id, label.into(), Some(group.clone()), target);
143 self.add_impl(label, f) 142 self.add_impl(label, f)
144 } 143 }
145 fn add_impl(&mut self, label: AssistLabel, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { 144 fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> {
146 let change_label = label.label.clone(); 145 let change_label = label.label.clone();
147 let source_change = if self.resolve { 146 let source_change = if self.resolve {
148 let mut builder = AssistBuilder::new(self.file); 147 let mut builder = AssistBuilder::new(self.file);
@@ -156,7 +155,7 @@ impl Assists {
156 Some(()) 155 Some(())
157 } 156 }
158 157
159 fn finish(mut self) -> Vec<(AssistLabel, Option<SourceChange>)> { 158 fn finish(mut self) -> Vec<(Assist, Option<SourceChange>)> {
160 self.buf.sort_by_key(|(label, _edit)| label.target.len()); 159 self.buf.sort_by_key(|(label, _edit)| label.target.len());
161 self.buf 160 self.buf
162 } 161 }
diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs
index ccf91797c..810784ad5 100644
--- a/crates/ra_assists/src/handlers/early_return.rs
+++ b/crates/ra_assists/src/handlers/early_return.rs
@@ -93,7 +93,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
93 } 93 }
94 94
95 then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?; 95 then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?;
96 let cursor_position = ctx.frange.range.start(); 96 let cursor_position = ctx.offset();
97 97
98 let target = if_expr.syntax().text_range(); 98 let target = if_expr.syntax().text_range();
99 acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| { 99 acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| {
diff --git a/crates/ra_assists/src/handlers/merge_imports.rs b/crates/ra_assists/src/handlers/merge_imports.rs
index 8e1d93312..ac3e53c27 100644
--- a/crates/ra_assists/src/handlers/merge_imports.rs
+++ b/crates/ra_assists/src/handlers/merge_imports.rs
@@ -26,7 +26,7 @@ use crate::{
26pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 26pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
27 let tree: ast::UseTree = ctx.find_node_at_offset()?; 27 let tree: ast::UseTree = ctx.find_node_at_offset()?;
28 let mut rewriter = SyntaxRewriter::default(); 28 let mut rewriter = SyntaxRewriter::default();
29 let mut offset = ctx.frange.range.start(); 29 let mut offset = ctx.offset();
30 30
31 if let Some(use_item) = tree.syntax().parent().and_then(ast::UseItem::cast) { 31 if let Some(use_item) = tree.syntax().parent().and_then(ast::UseItem::cast) {
32 let (merged, to_delete) = next_prev() 32 let (merged, to_delete) = next_prev()
diff --git a/crates/ra_assists/src/handlers/merge_match_arms.rs b/crates/ra_assists/src/handlers/merge_match_arms.rs
index cfe4df47b..d4e38aa6a 100644
--- a/crates/ra_assists/src/handlers/merge_match_arms.rs
+++ b/crates/ra_assists/src/handlers/merge_match_arms.rs
@@ -45,7 +45,7 @@ pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option
45 InExpr(TextSize), 45 InExpr(TextSize),
46 InPat(TextSize), 46 InPat(TextSize),
47 } 47 }
48 let cursor_pos = ctx.frange.range.start(); 48 let cursor_pos = ctx.offset();
49 let cursor_pos = if current_expr.syntax().text_range().contains(cursor_pos) { 49 let cursor_pos = if current_expr.syntax().text_range().contains(cursor_pos) {
50 CursorPos::InExpr(current_text_range.end() - cursor_pos) 50 CursorPos::InExpr(current_text_range.end() - cursor_pos)
51 } else { 51 } else {
diff --git a/crates/ra_assists/src/handlers/split_import.rs b/crates/ra_assists/src/handlers/split_import.rs
index 159033731..b2757e50c 100644
--- a/crates/ra_assists/src/handlers/split_import.rs
+++ b/crates/ra_assists/src/handlers/split_import.rs
@@ -26,7 +26,7 @@ pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
26 if new_tree == use_tree { 26 if new_tree == use_tree {
27 return None; 27 return None;
28 } 28 }
29 let cursor = ctx.frange.range.start(); 29 let cursor = ctx.offset();
30 30
31 let target = colon_colon.text_range(); 31 let target = colon_colon.text_range();
32 acc.add(AssistId("split_import"), "Split import", target, |edit| { 32 acc.add(AssistId("split_import"), "Split import", target, |edit| {
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 011613762..b6dc7cb1b 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -29,8 +29,11 @@ pub(crate) use crate::assist_context::{AssistContext, Assists};
29#[derive(Debug, Clone, Copy, PartialEq, Eq)] 29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub struct AssistId(pub &'static str); 30pub struct AssistId(pub &'static str);
31 31
32#[derive(Clone, Debug)]
33pub struct GroupLabel(pub String);
34
32#[derive(Debug, Clone)] 35#[derive(Debug, Clone)]
33pub struct AssistLabel { 36pub struct Assist {
34 pub id: AssistId, 37 pub id: AssistId,
35 /// Short description of the assist, as shown in the UI. 38 /// Short description of the assist, as shown in the UI.
36 pub label: String, 39 pub label: String,
@@ -40,56 +43,53 @@ pub struct AssistLabel {
40 pub target: TextRange, 43 pub target: TextRange,
41} 44}
42 45
43#[derive(Clone, Debug)] 46#[derive(Debug, Clone)]
44pub struct GroupLabel(pub String); 47pub struct ResolvedAssist {
48 pub assist: Assist,
49 pub source_change: SourceChange,
50}
51
52impl Assist {
53 /// Return all the assists applicable at the given position.
54 ///
55 /// Assists are returned in the "unresolved" state, that is only labels are
56 /// returned, without actual edits.
57 pub fn unresolved(db: &RootDatabase, range: FileRange) -> Vec<Assist> {
58 let sema = Semantics::new(db);
59 let ctx = AssistContext::new(sema, range);
60 let mut acc = Assists::new_unresolved(&ctx);
61 handlers::all().iter().for_each(|handler| {
62 handler(&mut acc, &ctx);
63 });
64 acc.finish_unresolved()
65 }
66
67 /// Return all the assists applicable at the given position.
68 ///
69 /// Assists are returned in the "resolved" state, that is with edit fully
70 /// computed.
71 pub fn resolved(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> {
72 let sema = Semantics::new(db);
73 let ctx = AssistContext::new(sema, range);
74 let mut acc = Assists::new_resolved(&ctx);
75 handlers::all().iter().for_each(|handler| {
76 handler(&mut acc, &ctx);
77 });
78 acc.finish_resolved()
79 }
45 80
46impl AssistLabel {
47 pub(crate) fn new( 81 pub(crate) fn new(
48 id: AssistId, 82 id: AssistId,
49 label: String, 83 label: String,
50 group: Option<GroupLabel>, 84 group: Option<GroupLabel>,
51 target: TextRange, 85 target: TextRange,
52 ) -> AssistLabel { 86 ) -> Assist {
53 // FIXME: make fields private, so that this invariant can't be broken 87 // FIXME: make fields private, so that this invariant can't be broken
54 assert!(label.starts_with(|c: char| c.is_uppercase())); 88 assert!(label.starts_with(|c: char| c.is_uppercase()));
55 AssistLabel { id, label, group, target } 89 Assist { id, label, group, target }
56 } 90 }
57} 91}
58 92
59#[derive(Debug, Clone)]
60pub struct ResolvedAssist {
61 pub label: AssistLabel,
62 pub source_change: SourceChange,
63}
64
65/// Return all the assists applicable at the given position.
66///
67/// Assists are returned in the "unresolved" state, that is only labels are
68/// returned, without actual edits.
69pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> {
70 let sema = Semantics::new(db);
71 let ctx = AssistContext::new(sema, range);
72 let mut acc = Assists::new_unresolved(&ctx);
73 handlers::all().iter().for_each(|handler| {
74 handler(&mut acc, &ctx);
75 });
76 acc.finish_unresolved()
77}
78
79/// Return all the assists applicable at the given position.
80///
81/// Assists are returned in the "resolved" state, that is with edit fully
82/// computed.
83pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> {
84 let sema = Semantics::new(db);
85 let ctx = AssistContext::new(sema, range);
86 let mut acc = Assists::new_resolved(&ctx);
87 handlers::all().iter().for_each(|handler| {
88 handler(&mut acc, &ctx);
89 });
90 acc.finish_resolved()
91}
92
93mod handlers { 93mod handlers {
94 use crate::{AssistContext, Assists}; 94 use crate::{AssistContext, Assists};
95 95
diff --git a/crates/ra_assists/src/tests.rs b/crates/ra_assists/src/tests.rs
index 45b2d9733..a3eacb8f1 100644
--- a/crates/ra_assists/src/tests.rs
+++ b/crates/ra_assists/src/tests.rs
@@ -11,7 +11,7 @@ use test_utils::{
11 RangeOrOffset, 11 RangeOrOffset,
12}; 12};
13 13
14use crate::{handlers::Handler, resolved_assists, AssistContext, Assists}; 14use crate::{handlers::Handler, Assist, AssistContext, Assists};
15 15
16pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { 16pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
17 let (mut db, file_id) = RootDatabase::with_single_file(text); 17 let (mut db, file_id) = RootDatabase::with_single_file(text);
@@ -41,16 +41,16 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) {
41 let (db, file_id) = crate::tests::with_single_file(&before); 41 let (db, file_id) = crate::tests::with_single_file(&before);
42 let frange = FileRange { file_id, range: selection.into() }; 42 let frange = FileRange { file_id, range: selection.into() };
43 43
44 let mut assist = resolved_assists(&db, frange) 44 let mut assist = Assist::resolved(&db, frange)
45 .into_iter() 45 .into_iter()
46 .find(|assist| assist.label.id.0 == assist_id) 46 .find(|assist| assist.assist.id.0 == assist_id)
47 .unwrap_or_else(|| { 47 .unwrap_or_else(|| {
48 panic!( 48 panic!(
49 "\n\nAssist is not applicable: {}\nAvailable assists: {}", 49 "\n\nAssist is not applicable: {}\nAvailable assists: {}",
50 assist_id, 50 assist_id,
51 resolved_assists(&db, frange) 51 Assist::resolved(&db, frange)
52 .into_iter() 52 .into_iter()
53 .map(|assist| assist.label.id.0) 53 .map(|assist| assist.assist.id.0)
54 .collect::<Vec<_>>() 54 .collect::<Vec<_>>()
55 .join(", ") 55 .join(", ")
56 ) 56 )
@@ -119,7 +119,7 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult) {
119 assert_eq_text!(after, &actual); 119 assert_eq_text!(after, &actual);
120 } 120 }
121 (Some(assist), ExpectedResult::Target(target)) => { 121 (Some(assist), ExpectedResult::Target(target)) => {
122 let range = assist.label.target; 122 let range = assist.assist.target;
123 assert_eq_text!(&text_without_caret[range], target); 123 assert_eq_text!(&text_without_caret[range], target);
124 } 124 }
125 (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"), 125 (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"),
@@ -136,14 +136,14 @@ fn assist_order_field_struct() {
136 let (before_cursor_pos, before) = extract_offset(before); 136 let (before_cursor_pos, before) = extract_offset(before);
137 let (db, file_id) = with_single_file(&before); 137 let (db, file_id) = with_single_file(&before);
138 let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; 138 let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) };
139 let assists = resolved_assists(&db, frange); 139 let assists = Assist::resolved(&db, frange);
140 let mut assists = assists.iter(); 140 let mut assists = assists.iter();
141 141
142 assert_eq!( 142 assert_eq!(
143 assists.next().expect("expected assist").label.label, 143 assists.next().expect("expected assist").assist.label,
144 "Change visibility to pub(crate)" 144 "Change visibility to pub(crate)"
145 ); 145 );
146 assert_eq!(assists.next().expect("expected assist").label.label, "Add `#[derive]`"); 146 assert_eq!(assists.next().expect("expected assist").assist.label, "Add `#[derive]`");
147} 147}
148 148
149#[test] 149#[test]
@@ -159,9 +159,9 @@ fn assist_order_if_expr() {
159 let (range, before) = extract_range(before); 159 let (range, before) = extract_range(before);
160 let (db, file_id) = with_single_file(&before); 160 let (db, file_id) = with_single_file(&before);
161 let frange = FileRange { file_id, range }; 161 let frange = FileRange { file_id, range };
162 let assists = resolved_assists(&db, frange); 162 let assists = Assist::resolved(&db, frange);
163 let mut assists = assists.iter(); 163 let mut assists = assists.iter();
164 164
165 assert_eq!(assists.next().expect("expected assist").label.label, "Extract into variable"); 165 assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable");
166 assert_eq!(assists.next().expect("expected assist").label.label, "Replace with match"); 166 assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match");
167} 167}