Skip to content

Commit 9769c51

Browse files
committed
Simplify Assists interface
Instead of building a physical tree structure, just "tag" related assists with the same group
1 parent fb99831 commit 9769c51

File tree

5 files changed

+108
-141
lines changed

5 files changed

+108
-141
lines changed

crates/ra_assists/src/assist_ctx.rs

Lines changed: 38 additions & 45 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,18 +107,17 @@ 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

101123
pub(crate) fn add_assist_group(self, group_name: impl Into<String>) -> AssistGroup<'a> {
@@ -136,7 +158,7 @@ impl<'a> AssistCtx<'a> {
136158
pub(crate) struct AssistGroup<'a> {
137159
ctx: AssistCtx<'a>,
138160
group_name: String,
139-
assists: Vec<Assist>,
161+
assists: Vec<AssistInfo>,
140162
}
141163

142164
impl<'a> AssistGroup<'a> {
@@ -148,49 +170,22 @@ impl<'a> AssistGroup<'a> {
148170
) {
149171
let label = AssistLabel::new(label.into(), id);
150172

151-
let assist = if self.ctx.should_compute_edit {
173+
let mut info = AssistInfo::new(label).with_group(GroupLabel(self.group_name.clone()));
174+
if self.ctx.should_compute_edit {
152175
let action = {
153176
let mut edit = ActionBuilder::default();
154177
f(&mut edit);
155178
edit.build()
156179
};
157-
Assist::Resolved { assist: ResolvedAssist { label, action_data: Either::Left(action) } }
158-
} else {
159-
Assist::Unresolved { label }
180+
info = info.resolved(action)
160181
};
161182

162-
self.assists.push(assist)
183+
self.assists.push(info)
163184
}
164185

165186
pub(crate) fn finish(self) -> Option<Assist> {
166187
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)
188+
Some(Assist(self.assists))
194189
}
195190
}
196191

@@ -199,7 +194,6 @@ pub(crate) struct ActionBuilder {
199194
edit: TextEditBuilder,
200195
cursor_position: Option<TextUnit>,
201196
target: Option<TextRange>,
202-
label: Option<String>,
203197
}
204198

205199
impl ActionBuilder {
@@ -261,7 +255,6 @@ impl ActionBuilder {
261255
edit: self.edit.finish(),
262256
cursor_position: self.cursor_position,
263257
target: self.target,
264-
label: self.label,
265258
}
266259
}
267260
}

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/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);

crates/ra_ide/src/assists.rs

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
//! FIXME: write short doc here
22
3-
use either::Either;
43
use ra_assists::{resolved_assists, AssistAction, AssistLabel};
54
use ra_db::{FilePosition, FileRange};
65
use ra_ide_db::RootDatabase;
@@ -13,7 +12,8 @@ pub use ra_assists::AssistId;
1312
pub struct Assist {
1413
pub id: AssistId,
1514
pub label: String,
16-
pub change_data: Either<SourceChange, Vec<SourceChange>>,
15+
pub group_label: Option<String>,
16+
pub source_change: SourceChange,
1717
}
1818

1919
pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> {
@@ -25,17 +25,8 @@ pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> {
2525
Assist {
2626
id: assist_label.id,
2727
label: assist_label.label.clone(),
28-
change_data: match assist.action_data {
29-
Either::Left(action) => {
30-
Either::Left(action_to_edit(action, file_id, assist_label))
31-
}
32-
Either::Right(actions) => Either::Right(
33-
actions
34-
.into_iter()
35-
.map(|action| action_to_edit(action, file_id, assist_label))
36-
.collect(),
37-
),
38-
},
28+
group_label: assist.group_label.map(|it| it.0),
29+
source_change: action_to_edit(assist.action, file_id, assist_label),
3930
}
4031
})
4132
.collect()
@@ -47,9 +38,6 @@ fn action_to_edit(
4738
assist_label: &AssistLabel,
4839
) -> SourceChange {
4940
let file_edit = SourceFileEdit { file_id, edit: action.edit };
50-
SourceChange::source_file_edit(
51-
action.label.unwrap_or_else(|| assist_label.label.clone()),
52-
file_edit,
53-
)
54-
.with_cursor_opt(action.cursor_position.map(|offset| FilePosition { offset, file_id }))
41+
SourceChange::source_file_edit(assist_label.label.clone(), file_edit)
42+
.with_cursor_opt(action.cursor_position.map(|offset| FilePosition { offset, file_id }))
5543
}

0 commit comments

Comments
 (0)