diff options
-rw-r--r-- | crates/ra_assists/src/assist_ctx.rs | 92 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/auto_import.rs | 33 |
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 | ||
136 | pub(crate) struct AssistGroup<'a> { | ||
137 | ctx: AssistCtx<'a>, | ||
138 | group_name: String, | ||
139 | assists: Vec<Assist>, | ||
140 | } | ||
141 | |||
142 | impl<'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)] |
159 | pub(crate) struct ActionBuilder { | 198 | pub(crate) struct ActionBuilder { |
160 | edit: TextEditBuilder, | 199 | edit: TextEditBuilder, |
@@ -164,11 +203,6 @@ pub(crate) struct ActionBuilder { | |||
164 | } | 203 | } |
165 | 204 | ||
166 | impl ActionBuilder { | 205 | impl 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 @@ | |||
1 | use hir::ModPath; | ||
2 | use ra_ide_db::imports_locator::ImportsLocator; | 1 | use ra_ide_db::imports_locator::ImportsLocator; |
3 | use ra_syntax::{ | 2 | use ra_syntax::ast::{self, AstNode}; |
4 | ast::{self, AstNode}, | ||
5 | SyntaxNode, | ||
6 | }; | ||
7 | 3 | ||
8 | use crate::{ | 4 | use crate::{ |
9 | assist_ctx::{ActionBuilder, Assist, AssistCtx}, | 5 | assist_ctx::{Assist, AssistCtx}, |
10 | insert_use_statement, AssistId, | 6 | insert_use_statement, AssistId, |
11 | }; | 7 | }; |
12 | use std::collections::BTreeSet; | 8 | use 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(), | |
78 | fn 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)] |