Skip to content

Commit a836247

Browse files
bors[bot]matklad
andauthored
Merge #3069
3069: Simplify Assists interface r=matklad a=matklad Instead of building a physical tree structure, just tag related assists with the same group Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
2 parents aa5f80a + 9769c51 commit a836247

File tree

6 files changed

+152
-156
lines changed

6 files changed

+152
-156
lines changed

crates/ra_assists/src/assist_ctx.rs

Lines changed: 68 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
//! This module defines `AssistCtx` -- the API surface that is exposed to assists.
2-
use either::Either;
32
use hir::{InFile, SourceAnalyzer, SourceBinder};
43
use ra_db::{FileRange, SourceDatabase};
54
use ra_fmt::{leading_indent, reindent};
@@ -11,12 +10,36 @@ use ra_syntax::{
1110
};
1211
use ra_text_edit::TextEditBuilder;
1312

14-
use crate::{AssistAction, AssistId, AssistLabel, ResolvedAssist};
13+
use crate::{AssistAction, AssistId, AssistLabel, GroupLabel, ResolvedAssist};
1514

1615
#[derive(Clone, Debug)]
17-
pub(crate) enum Assist {
18-
Unresolved { label: AssistLabel },
19-
Resolved { assist: ResolvedAssist },
16+
pub(crate) struct Assist(pub(crate) Vec<AssistInfo>);
17+
18+
#[derive(Clone, Debug)]
19+
pub(crate) struct AssistInfo {
20+
pub(crate) label: AssistLabel,
21+
pub(crate) group_label: Option<GroupLabel>,
22+
pub(crate) action: Option<AssistAction>,
23+
}
24+
25+
impl AssistInfo {
26+
fn new(label: AssistLabel) -> AssistInfo {
27+
AssistInfo { label, group_label: None, action: None }
28+
}
29+
30+
fn resolved(self, action: AssistAction) -> AssistInfo {
31+
AssistInfo { action: Some(action), ..self }
32+
}
33+
34+
fn with_group(self, group_label: GroupLabel) -> AssistInfo {
35+
AssistInfo { group_label: Some(group_label), ..self }
36+
}
37+
38+
pub(crate) fn into_resolved(self) -> Option<ResolvedAssist> {
39+
let label = self.label;
40+
let group_label = self.group_label;
41+
self.action.map(|action| ResolvedAssist { label, group_label, action })
42+
}
2043
}
2144

2245
pub(crate) type AssistHandler = fn(AssistCtx) -> Option<Assist>;
@@ -84,44 +107,21 @@ impl<'a> AssistCtx<'a> {
84107
) -> Option<Assist> {
85108
let label = AssistLabel::new(label.into(), id);
86109

87-
let assist = if self.should_compute_edit {
110+
let mut info = AssistInfo::new(label);
111+
if self.should_compute_edit {
88112
let action = {
89113
let mut edit = ActionBuilder::default();
90114
f(&mut edit);
91115
edit.build()
92116
};
93-
Assist::Resolved { assist: ResolvedAssist { label, action_data: Either::Left(action) } }
94-
} else {
95-
Assist::Unresolved { label }
117+
info = info.resolved(action)
96118
};
97119

98-
Some(assist)
120+
Some(Assist(vec![info]))
99121
}
100122

101-
pub(crate) fn add_assist_group(
102-
self,
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)
123+
pub(crate) fn add_assist_group(self, group_name: impl Into<String>) -> AssistGroup<'a> {
124+
AssistGroup { ctx: self, group_name: group_name.into(), assists: Vec::new() }
125125
}
126126

127127
pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> {
@@ -155,20 +155,48 @@ impl<'a> AssistCtx<'a> {
155155
}
156156
}
157157

158+
pub(crate) struct AssistGroup<'a> {
159+
ctx: AssistCtx<'a>,
160+
group_name: String,
161+
assists: Vec<AssistInfo>,
162+
}
163+
164+
impl<'a> AssistGroup<'a> {
165+
pub(crate) fn add_assist(
166+
&mut self,
167+
id: AssistId,
168+
label: impl Into<String>,
169+
f: impl FnOnce(&mut ActionBuilder),
170+
) {
171+
let label = AssistLabel::new(label.into(), id);
172+
173+
let mut info = AssistInfo::new(label).with_group(GroupLabel(self.group_name.clone()));
174+
if self.ctx.should_compute_edit {
175+
let action = {
176+
let mut edit = ActionBuilder::default();
177+
f(&mut edit);
178+
edit.build()
179+
};
180+
info = info.resolved(action)
181+
};
182+
183+
self.assists.push(info)
184+
}
185+
186+
pub(crate) fn finish(self) -> Option<Assist> {
187+
assert!(!self.assists.is_empty());
188+
Some(Assist(self.assists))
189+
}
190+
}
191+
158192
#[derive(Default)]
159193
pub(crate) struct ActionBuilder {
160194
edit: TextEditBuilder,
161195
cursor_position: Option<TextUnit>,
162196
target: Option<TextRange>,
163-
label: Option<String>,
164197
}
165198

166199
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-
172200
/// Replaces specified `range` of text with a given string.
173201
pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
174202
self.edit.replace(range, replace_with.into())
@@ -227,7 +255,6 @@ impl ActionBuilder {
227255
edit: self.edit.finish(),
228256
cursor_position: self.cursor_position,
229257
target: self.target,
230-
label: self.label,
231258
}
232259
}
233260
}

crates/ra_assists/src/doc_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,6 @@ fn check(assist_id: &str, before: &str, after: &str) {
3030
)
3131
});
3232

33-
let actual = assist.get_first_action().edit.apply(&before);
33+
let actual = assist.action.edit.apply(&before);
3434
assert_eq_text!(after, &actual);
3535
}

crates/ra_assists/src/handlers/auto_import.rs

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
1-
use hir::ModPath;
21
use ra_ide_db::imports_locator::ImportsLocator;
3-
use ra_syntax::{
4-
ast::{self, AstNode},
5-
SyntaxNode,
6-
};
2+
use ra_syntax::ast::{self, AstNode};
73

84
use crate::{
9-
assist_ctx::{ActionBuilder, Assist, AssistCtx},
5+
assist_ctx::{Assist, AssistCtx},
106
insert_use_statement, AssistId,
117
};
128
use std::collections::BTreeSet;
@@ -67,19 +63,18 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> {
6763
return None;
6864
}
6965

70-
ctx.add_assist_group(AssistId("auto_import"), format!("Import {}", name_to_import), || {
71-
proposed_imports
72-
.into_iter()
73-
.map(|import| import_to_action(import, &position, &path_to_import_syntax))
74-
.collect()
75-
})
76-
}
77-
78-
fn import_to_action(import: ModPath, position: &SyntaxNode, anchor: &SyntaxNode) -> ActionBuilder {
79-
let mut action_builder = ActionBuilder::default();
80-
action_builder.label(format!("Import `{}`", &import));
81-
insert_use_statement(position, anchor, &import, action_builder.text_edit_builder());
82-
action_builder
66+
let mut group = ctx.add_assist_group(format!("Import {}", name_to_import));
67+
for import in proposed_imports {
68+
group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), |edit| {
69+
insert_use_statement(
70+
&position,
71+
path_to_import_syntax,
72+
&import,
73+
edit.text_edit_builder(),
74+
);
75+
});
76+
}
77+
group.finish()
8378
}
8479

8580
#[cfg(test)]

crates/ra_assists/src/lib.rs

Lines changed: 15 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ mod doc_tests;
1212
mod utils;
1313
pub mod ast_transform;
1414

15-
use std::cmp::Ordering;
16-
17-
use either::Either;
1815
use ra_db::FileRange;
1916
use ra_ide_db::RootDatabase;
2017
use ra_syntax::{TextRange, TextUnit};
@@ -35,6 +32,9 @@ pub struct AssistLabel {
3532
pub id: AssistId,
3633
}
3734

35+
#[derive(Clone, Debug)]
36+
pub struct GroupLabel(pub String);
37+
3838
impl AssistLabel {
3939
pub(crate) fn new(label: String, id: AssistId) -> AssistLabel {
4040
// FIXME: make fields private, so that this invariant can't be broken
@@ -45,7 +45,6 @@ impl AssistLabel {
4545

4646
#[derive(Debug, Clone)]
4747
pub struct AssistAction {
48-
pub label: Option<String>,
4948
pub edit: TextEdit,
5049
pub cursor_position: Option<TextUnit>,
5150
// FIXME: This belongs to `AssistLabel`
@@ -55,16 +54,8 @@ pub struct AssistAction {
5554
#[derive(Debug, Clone)]
5655
pub struct ResolvedAssist {
5756
pub label: AssistLabel,
58-
pub action_data: Either<AssistAction, Vec<AssistAction>>,
59-
}
60-
61-
impl ResolvedAssist {
62-
pub fn get_first_action(&self) -> AssistAction {
63-
match &self.action_data {
64-
Either::Left(action) => action.clone(),
65-
Either::Right(actions) => actions[0].clone(),
66-
}
67-
}
57+
pub group_label: Option<GroupLabel>,
58+
pub action: AssistAction,
6859
}
6960

7061
/// Return all the assists applicable at the given position.
@@ -76,10 +67,8 @@ pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabe
7667
handlers::all()
7768
.iter()
7869
.filter_map(|f| f(ctx.clone()))
79-
.map(|a| match a {
80-
Assist::Unresolved { label } => label,
81-
Assist::Resolved { .. } => unreachable!(),
82-
})
70+
.flat_map(|it| it.0)
71+
.map(|a| a.label)
8372
.collect()
8473
}
8574

@@ -92,24 +81,13 @@ pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssi
9281
let mut a = handlers::all()
9382
.iter()
9483
.filter_map(|f| f(ctx.clone()))
95-
.map(|a| match a {
96-
Assist::Resolved { assist } => assist,
97-
Assist::Unresolved { .. } => unreachable!(),
98-
})
84+
.flat_map(|it| it.0)
85+
.map(|it| it.into_resolved().unwrap())
9986
.collect::<Vec<_>>();
100-
sort_assists(&mut a);
87+
a.sort_by_key(|it| it.action.target.map_or(TextUnit::from(!0u32), |it| it.len()));
10188
a
10289
}
10390

104-
fn sort_assists(assists: &mut [ResolvedAssist]) {
105-
assists.sort_by(|a, b| match (a.get_first_action().target, b.get_first_action().target) {
106-
(Some(a), Some(b)) => a.len().cmp(&b.len()),
107-
(Some(_), None) => Ordering::Less,
108-
(None, Some(_)) => Ordering::Greater,
109-
(None, None) => Ordering::Equal,
110-
});
111-
}
112-
11391
mod handlers {
11492
use crate::AssistHandler;
11593

@@ -184,7 +162,7 @@ mod helpers {
184162
use ra_syntax::TextRange;
185163
use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range};
186164

187-
use crate::{Assist, AssistCtx, AssistHandler};
165+
use crate::{AssistCtx, AssistHandler};
188166

189167
pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
190168
let (mut db, file_id) = RootDatabase::with_single_file(text);
@@ -202,10 +180,7 @@ mod helpers {
202180
FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
203181
let assist =
204182
assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable");
205-
let action = match assist {
206-
Assist::Unresolved { .. } => unreachable!(),
207-
Assist::Resolved { assist } => assist.get_first_action(),
208-
};
183+
let action = assist.0[0].action.clone().unwrap();
209184

210185
let actual = action.edit.apply(&before);
211186
let actual_cursor_pos = match action.cursor_position {
@@ -225,10 +200,7 @@ mod helpers {
225200
let frange = FileRange { file_id, range };
226201
let assist =
227202
assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable");
228-
let action = match assist {
229-
Assist::Unresolved { .. } => unreachable!(),
230-
Assist::Resolved { assist } => assist.get_first_action(),
231-
};
203+
let action = assist.0[0].action.clone().unwrap();
232204

233205
let mut actual = action.edit.apply(&before);
234206
if let Some(pos) = action.cursor_position {
@@ -244,10 +216,7 @@ mod helpers {
244216
FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
245217
let assist =
246218
assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable");
247-
let action = match assist {
248-
Assist::Unresolved { .. } => unreachable!(),
249-
Assist::Resolved { assist } => assist.get_first_action(),
250-
};
219+
let action = assist.0[0].action.clone().unwrap();
251220

252221
let range = action.target.expect("expected target on action");
253222
assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
@@ -259,10 +228,7 @@ mod helpers {
259228
let frange = FileRange { file_id, range };
260229
let assist =
261230
assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable");
262-
let action = match assist {
263-
Assist::Unresolved { .. } => unreachable!(),
264-
Assist::Resolved { assist } => assist.get_first_action(),
265-
};
231+
let action = assist.0[0].action.clone().unwrap();
266232

267233
let range = action.target.expect("expected target on action");
268234
assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);

0 commit comments

Comments
 (0)