aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/assist_ctx.rs92
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs33
2 files changed, 77 insertions, 48 deletions
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs
index 81f999090..5924a3fd5 100644
--- a/crates/ra_assists/src/assist_ctx.rs
+++ b/crates/ra_assists/src/assist_ctx.rs
@@ -98,30 +98,8 @@ impl<'a> AssistCtx<'a> {
98 Some(assist) 98 Some(assist)
99 } 99 }
100 100
101 pub(crate) fn add_assist_group( 101 pub(crate) fn add_assist_group(self, group_name: impl Into<String>) -> AssistGroup<'a> {
102 self, 102 AssistGroup { ctx: self, group_name: group_name.into(), assists: Vec::new() }
103 id: AssistId,
104 label: impl Into<String>,
105 f: impl FnOnce() -> Vec<ActionBuilder>,
106 ) -> Option<Assist> {
107 let label = AssistLabel::new(label.into(), id);
108 let assist = if self.should_compute_edit {
109 let actions = f();
110 assert!(!actions.is_empty(), "Assist cannot have no");
111
112 Assist::Resolved {
113 assist: ResolvedAssist {
114 label,
115 action_data: Either::Right(
116 actions.into_iter().map(ActionBuilder::build).collect(),
117 ),
118 },
119 }
120 } else {
121 Assist::Unresolved { label }
122 };
123
124 Some(assist)
125 } 103 }
126 104
127 pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> { 105 pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> {
@@ -155,6 +133,67 @@ impl<'a> AssistCtx<'a> {
155 } 133 }
156} 134}
157 135
136pub(crate) struct AssistGroup<'a> {
137 ctx: AssistCtx<'a>,
138 group_name: String,
139 assists: Vec<Assist>,
140}
141
142impl<'a> AssistGroup<'a> {
143 pub(crate) fn add_assist(
144 &mut self,
145 id: AssistId,
146 label: impl Into<String>,
147 f: impl FnOnce(&mut ActionBuilder),
148 ) {
149 let label = AssistLabel::new(label.into(), id);
150
151 let assist = if self.ctx.should_compute_edit {
152 let action = {
153 let mut edit = ActionBuilder::default();
154 f(&mut edit);
155 edit.build()
156 };
157 Assist::Resolved { assist: ResolvedAssist { label, action_data: Either::Left(action) } }
158 } else {
159 Assist::Unresolved { label }
160 };
161
162 self.assists.push(assist)
163 }
164
165 pub(crate) fn finish(self) -> Option<Assist> {
166 assert!(!self.assists.is_empty());
167 let mut label = match &self.assists[0] {
168 Assist::Unresolved { label } => label.clone(),
169 Assist::Resolved { assist } => assist.label.clone(),
170 };
171 label.label = self.group_name;
172 let assist = if self.ctx.should_compute_edit {
173 Assist::Resolved {
174 assist: ResolvedAssist {
175 label,
176 action_data: Either::Right(
177 self.assists
178 .into_iter()
179 .map(|assist| match assist {
180 Assist::Resolved {
181 assist:
182 ResolvedAssist { label: _, action_data: Either::Left(it) },
183 } => it,
184 _ => unreachable!(),
185 })
186 .collect(),
187 ),
188 },
189 }
190 } else {
191 Assist::Unresolved { label }
192 };
193 Some(assist)
194 }
195}
196
158#[derive(Default)] 197#[derive(Default)]
159pub(crate) struct ActionBuilder { 198pub(crate) struct ActionBuilder {
160 edit: TextEditBuilder, 199 edit: TextEditBuilder,
@@ -164,11 +203,6 @@ pub(crate) struct ActionBuilder {
164} 203}
165 204
166impl ActionBuilder { 205impl ActionBuilder {
167 /// Adds a custom label to the action, if it needs to be different from the assist label
168 pub(crate) fn label(&mut self, label: impl Into<String>) {
169 self.label = Some(label.into())
170 }
171
172 /// Replaces specified `range` of text with a given string. 206 /// Replaces specified `range` of text with a given string.
173 pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) { 207 pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
174 self.edit.replace(range, replace_with.into()) 208 self.edit.replace(range, replace_with.into())
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index 4514b8691..d13332f37 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -1,12 +1,8 @@
1use hir::ModPath;
2use ra_ide_db::imports_locator::ImportsLocator; 1use ra_ide_db::imports_locator::ImportsLocator;
3use ra_syntax::{ 2use ra_syntax::ast::{self, AstNode};
4 ast::{self, AstNode},
5 SyntaxNode,
6};
7 3
8use crate::{ 4use crate::{
9 assist_ctx::{ActionBuilder, Assist, AssistCtx}, 5 assist_ctx::{Assist, AssistCtx},
10 insert_use_statement, AssistId, 6 insert_use_statement, AssistId,
11}; 7};
12use std::collections::BTreeSet; 8use std::collections::BTreeSet;
@@ -67,19 +63,18 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> {
67 return None; 63 return None;
68 } 64 }
69 65
70 ctx.add_assist_group(AssistId("auto_import"), format!("Import {}", name_to_import), || { 66 let mut group = ctx.add_assist_group(format!("Import {}", name_to_import));
71 proposed_imports 67 for import in proposed_imports {
72 .into_iter() 68 group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), |edit| {
73 .map(|import| import_to_action(import, &position, &path_to_import_syntax)) 69 insert_use_statement(
74 .collect() 70 &position,
75 }) 71 path_to_import_syntax,
76} 72 &import,
77 73 edit.text_edit_builder(),
78fn import_to_action(import: ModPath, position: &SyntaxNode, anchor: &SyntaxNode) -> ActionBuilder { 74 );
79 let mut action_builder = ActionBuilder::default(); 75 });
80 action_builder.label(format!("Import `{}`", &import)); 76 }
81 insert_use_statement(position, anchor, &import, action_builder.text_edit_builder()); 77 group.finish()
82 action_builder
83} 78}
84 79
85#[cfg(test)] 80#[cfg(test)]