Skip to content

Commit 1d874a2

Browse files
bors[bot]matklad
andauthored
Merge #7043
7043: Simplify assists resolution API r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <[email protected]>
2 parents 44893bb + 2f22675 commit 1d874a2

File tree

6 files changed

+69
-120
lines changed

6 files changed

+69
-120
lines changed

crates/assists/src/assist_context.rs

Lines changed: 12 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use text_edit::{TextEdit, TextEditBuilder};
1919

2020
use crate::{
2121
assist_config::{AssistConfig, SnippetCap},
22-
Assist, AssistId, AssistKind, GroupLabel, ResolvedAssist,
22+
Assist, AssistId, AssistKind, GroupLabel,
2323
};
2424

2525
/// `AssistContext` allows to apply an assist or check if it could be applied.
@@ -105,46 +105,23 @@ impl<'a> AssistContext<'a> {
105105
pub(crate) struct Assists {
106106
resolve: bool,
107107
file: FileId,
108-
buf: Vec<(Assist, Option<SourceChange>)>,
108+
buf: Vec<Assist>,
109109
allowed: Option<Vec<AssistKind>>,
110110
}
111111

112112
impl Assists {
113-
pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists {
113+
pub(crate) fn new(ctx: &AssistContext, resolve: bool) -> Assists {
114114
Assists {
115-
resolve: true,
115+
resolve,
116116
file: ctx.frange.file_id,
117117
buf: Vec::new(),
118118
allowed: ctx.config.allowed.clone(),
119119
}
120120
}
121121

122-
pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists {
123-
Assists {
124-
resolve: false,
125-
file: ctx.frange.file_id,
126-
buf: Vec::new(),
127-
allowed: ctx.config.allowed.clone(),
128-
}
129-
}
130-
131-
pub(crate) fn finish_unresolved(self) -> Vec<Assist> {
132-
assert!(!self.resolve);
133-
self.finish()
134-
.into_iter()
135-
.map(|(label, edit)| {
136-
assert!(edit.is_none());
137-
label
138-
})
139-
.collect()
140-
}
141-
142-
pub(crate) fn finish_resolved(self) -> Vec<ResolvedAssist> {
143-
assert!(self.resolve);
144-
self.finish()
145-
.into_iter()
146-
.map(|(label, edit)| ResolvedAssist { assist: label, source_change: edit.unwrap() })
147-
.collect()
122+
pub(crate) fn finish(mut self) -> Vec<Assist> {
123+
self.buf.sort_by_key(|assist| assist.target.len());
124+
self.buf
148125
}
149126

150127
pub(crate) fn add(
@@ -158,7 +135,7 @@ impl Assists {
158135
return None;
159136
}
160137
let label = Label::new(label.into());
161-
let assist = Assist { id, label, group: None, target };
138+
let assist = Assist { id, label, group: None, target, source_change: None };
162139
self.add_impl(assist, f)
163140
}
164141

@@ -174,28 +151,24 @@ impl Assists {
174151
return None;
175152
}
176153
let label = Label::new(label.into());
177-
let assist = Assist { id, label, group: Some(group.clone()), target };
154+
let assist = Assist { id, label, group: Some(group.clone()), target, source_change: None };
178155
self.add_impl(assist, f)
179156
}
180157

181-
fn add_impl(&mut self, assist: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> {
158+
fn add_impl(&mut self, mut assist: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> {
182159
let source_change = if self.resolve {
183160
let mut builder = AssistBuilder::new(self.file);
184161
f(&mut builder);
185162
Some(builder.finish())
186163
} else {
187164
None
188165
};
166+
assist.source_change = source_change.clone();
189167

190-
self.buf.push((assist, source_change));
168+
self.buf.push(assist);
191169
Some(())
192170
}
193171

194-
fn finish(mut self) -> Vec<(Assist, Option<SourceChange>)> {
195-
self.buf.sort_by_key(|(label, _edit)| label.target.len());
196-
self.buf
197-
}
198-
199172
fn is_allowed(&self, id: &AssistId) -> bool {
200173
match &self.allowed {
201174
Some(allowed) => allowed.iter().any(|kind| kind.contains(id.1)),

crates/assists/src/lib.rs

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -73,45 +73,32 @@ pub struct Assist {
7373
/// Target ranges are used to sort assists: the smaller the target range,
7474
/// the more specific assist is, and so it should be sorted first.
7575
pub target: TextRange,
76-
}
77-
78-
#[derive(Debug, Clone)]
79-
pub struct ResolvedAssist {
80-
pub assist: Assist,
81-
pub source_change: SourceChange,
76+
/// Computing source change sometimes is much more costly then computing the
77+
/// other fields. Additionally, the actual change is not required to show
78+
/// the lightbulb UI, it only is needed when the user tries to apply an
79+
/// assist. So, we compute it lazily: the API allow requesting assists with
80+
/// or without source change. We could (and in fact, used to) distinguish
81+
/// between resolved and unresolved assists at the type level, but this is
82+
/// cumbersome, especially if you want to embed an assist into another data
83+
/// structure, such as a diagnostic.
84+
pub source_change: Option<SourceChange>,
8285
}
8386

8487
impl Assist {
8588
/// Return all the assists applicable at the given position.
86-
///
87-
/// Assists are returned in the "unresolved" state, that is only labels are
88-
/// returned, without actual edits.
89-
pub fn unresolved(db: &RootDatabase, config: &AssistConfig, range: FileRange) -> Vec<Assist> {
90-
let sema = Semantics::new(db);
91-
let ctx = AssistContext::new(sema, config, range);
92-
let mut acc = Assists::new_unresolved(&ctx);
93-
handlers::all().iter().for_each(|handler| {
94-
handler(&mut acc, &ctx);
95-
});
96-
acc.finish_unresolved()
97-
}
98-
99-
/// Return all the assists applicable at the given position.
100-
///
101-
/// Assists are returned in the "resolved" state, that is with edit fully
102-
/// computed.
103-
pub fn resolved(
89+
pub fn get(
10490
db: &RootDatabase,
10591
config: &AssistConfig,
92+
resolve: bool,
10693
range: FileRange,
107-
) -> Vec<ResolvedAssist> {
94+
) -> Vec<Assist> {
10895
let sema = Semantics::new(db);
10996
let ctx = AssistContext::new(sema, config, range);
110-
let mut acc = Assists::new_resolved(&ctx);
97+
let mut acc = Assists::new(&ctx, resolve);
11198
handlers::all().iter().for_each(|handler| {
11299
handler(&mut acc, &ctx);
113100
});
114-
acc.finish_resolved()
101+
acc.finish()
115102
}
116103
}
117104

crates/assists/src/tests.rs

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -48,24 +48,25 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) {
4848
let before = db.file_text(file_id).to_string();
4949
let frange = FileRange { file_id, range: selection.into() };
5050

51-
let assist = Assist::resolved(&db, &AssistConfig::default(), frange)
51+
let assist = Assist::get(&db, &AssistConfig::default(), true, frange)
5252
.into_iter()
53-
.find(|assist| assist.assist.id.0 == assist_id)
53+
.find(|assist| assist.id.0 == assist_id)
5454
.unwrap_or_else(|| {
5555
panic!(
5656
"\n\nAssist is not applicable: {}\nAvailable assists: {}",
5757
assist_id,
58-
Assist::resolved(&db, &AssistConfig::default(), frange)
58+
Assist::get(&db, &AssistConfig::default(), false, frange)
5959
.into_iter()
60-
.map(|assist| assist.assist.id.0)
60+
.map(|assist| assist.id.0)
6161
.collect::<Vec<_>>()
6262
.join(", ")
6363
)
6464
});
6565

6666
let actual = {
67+
let source_change = assist.source_change.unwrap();
6768
let mut actual = before;
68-
for source_file_edit in assist.source_change.source_file_edits {
69+
for source_file_edit in source_change.source_file_edits {
6970
if source_file_edit.file_id == file_id {
7071
source_file_edit.edit.apply(&mut actual)
7172
}
@@ -90,18 +91,18 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label:
9091
let sema = Semantics::new(&db);
9192
let config = AssistConfig::default();
9293
let ctx = AssistContext::new(sema, &config, frange);
93-
let mut acc = Assists::new_resolved(&ctx);
94+
let mut acc = Assists::new(&ctx, true);
9495
handler(&mut acc, &ctx);
95-
let mut res = acc.finish_resolved();
96+
let mut res = acc.finish();
9697

9798
let assist = match assist_label {
98-
Some(label) => res.into_iter().find(|resolved| resolved.assist.label == label),
99+
Some(label) => res.into_iter().find(|resolved| resolved.label == label),
99100
None => res.pop(),
100101
};
101102

102103
match (assist, expected) {
103104
(Some(assist), ExpectedResult::After(after)) => {
104-
let mut source_change = assist.source_change;
105+
let mut source_change = assist.source_change.unwrap();
105106
assert!(!source_change.source_file_edits.is_empty());
106107
let skip_header = source_change.source_file_edits.len() == 1
107108
&& source_change.file_system_edits.len() == 0;
@@ -138,7 +139,7 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label:
138139
assert_eq_text!(after, &buf);
139140
}
140141
(Some(assist), ExpectedResult::Target(target)) => {
141-
let range = assist.assist.target;
142+
let range = assist.target;
142143
assert_eq_text!(&text_without_caret[range], target);
143144
}
144145
(Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"),
@@ -155,14 +156,11 @@ fn assist_order_field_struct() {
155156
let (before_cursor_pos, before) = extract_offset(before);
156157
let (db, file_id) = with_single_file(&before);
157158
let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) };
158-
let assists = Assist::resolved(&db, &AssistConfig::default(), frange);
159+
let assists = Assist::get(&db, &AssistConfig::default(), false, frange);
159160
let mut assists = assists.iter();
160161

161-
assert_eq!(
162-
assists.next().expect("expected assist").assist.label,
163-
"Change visibility to pub(crate)"
164-
);
165-
assert_eq!(assists.next().expect("expected assist").assist.label, "Add `#[derive]`");
162+
assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)");
163+
assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`");
166164
}
167165

168166
#[test]
@@ -178,11 +176,11 @@ fn assist_order_if_expr() {
178176
let (range, before) = extract_range(before);
179177
let (db, file_id) = with_single_file(&before);
180178
let frange = FileRange { file_id, range };
181-
let assists = Assist::resolved(&db, &AssistConfig::default(), frange);
179+
let assists = Assist::get(&db, &AssistConfig::default(), false, frange);
182180
let mut assists = assists.iter();
183181

184-
assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable");
185-
assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match");
182+
assert_eq!(assists.next().expect("expected assist").label, "Extract into variable");
183+
assert_eq!(assists.next().expect("expected assist").label, "Replace with match");
186184
}
187185

188186
#[test]
@@ -203,27 +201,27 @@ fn assist_filter_works() {
203201
let mut cfg = AssistConfig::default();
204202
cfg.allowed = Some(vec![AssistKind::Refactor]);
205203

206-
let assists = Assist::resolved(&db, &cfg, frange);
204+
let assists = Assist::get(&db, &cfg, false, frange);
207205
let mut assists = assists.iter();
208206

209-
assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable");
210-
assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match");
207+
assert_eq!(assists.next().expect("expected assist").label, "Extract into variable");
208+
assert_eq!(assists.next().expect("expected assist").label, "Replace with match");
211209
}
212210

213211
{
214212
let mut cfg = AssistConfig::default();
215213
cfg.allowed = Some(vec![AssistKind::RefactorExtract]);
216-
let assists = Assist::resolved(&db, &cfg, frange);
214+
let assists = Assist::get(&db, &cfg, false, frange);
217215
assert_eq!(assists.len(), 1);
218216

219217
let mut assists = assists.iter();
220-
assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable");
218+
assert_eq!(assists.next().expect("expected assist").label, "Extract into variable");
221219
}
222220

223221
{
224222
let mut cfg = AssistConfig::default();
225223
cfg.allowed = Some(vec![AssistKind::QuickFix]);
226-
let assists = Assist::resolved(&db, &cfg, frange);
224+
let assists = Assist::get(&db, &cfg, false, frange);
227225
assert!(assists.is_empty(), "All asserts but quickfixes should be filtered out");
228226
}
229227
}

crates/ide/src/lib.rs

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ pub use crate::{
7979
HighlightedRange,
8080
},
8181
};
82-
pub use assists::{Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist};
82+
pub use assists::{Assist, AssistConfig, AssistId, AssistKind};
8383
pub use completion::{
8484
CompletionConfig, CompletionItem, CompletionItemKind, CompletionResolveCapability,
8585
CompletionScore, ImportEdit, InsertTextFormat,
@@ -491,22 +491,16 @@ impl Analysis {
491491
}
492492

493493
/// Computes assists (aka code actions aka intentions) for the given
494-
/// position. Computes enough info to show the lightbulb list in the editor,
495-
/// but doesn't compute actual edits, to improve performance.
496-
///
497-
/// When the user clicks on the assist, call `resolve_assists` to get the
498-
/// edit.
499-
pub fn assists(&self, config: &AssistConfig, frange: FileRange) -> Cancelable<Vec<Assist>> {
500-
self.with_db(|db| Assist::unresolved(db, config, frange))
501-
}
502-
503-
/// Computes resolved assists with source changes for the given position.
504-
pub fn resolve_assists(
494+
/// position. If `resolve == false`, computes enough info to show the
495+
/// lightbulb list in the editor, but doesn't compute actual edits, to
496+
/// improve performance.
497+
pub fn assists(
505498
&self,
506499
config: &AssistConfig,
500+
resolve: bool,
507501
frange: FileRange,
508-
) -> Cancelable<Vec<ResolvedAssist>> {
509-
self.with_db(|db| assists::Assist::resolved(db, config, frange))
502+
) -> Cancelable<Vec<Assist>> {
503+
self.with_db(|db| Assist::get(db, config, resolve, frange))
510504
}
511505

512506
/// Computes the set of diagnostics for the given file.

crates/rust-analyzer/src/handlers.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -946,12 +946,12 @@ pub(crate) fn handle_code_action(
946946

947947
if snap.config.client_caps.code_action_resolve {
948948
for (index, assist) in
949-
snap.analysis.assists(&assists_config, frange)?.into_iter().enumerate()
949+
snap.analysis.assists(&assists_config, false, frange)?.into_iter().enumerate()
950950
{
951951
res.push(to_proto::unresolved_code_action(&snap, params.clone(), assist, index)?);
952952
}
953953
} else {
954-
for assist in snap.analysis.resolve_assists(&assists_config, frange)?.into_iter() {
954+
for assist in snap.analysis.assists(&assists_config, true, frange)?.into_iter() {
955955
res.push(to_proto::resolved_code_action(&snap, assist)?);
956956
}
957957
}
@@ -1014,11 +1014,11 @@ pub(crate) fn handle_code_action_resolve(
10141014
.only
10151015
.map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect());
10161016

1017-
let assists = snap.analysis.resolve_assists(&snap.config.assist, frange)?;
1017+
let assists = snap.analysis.assists(&snap.config.assist, true, frange)?;
10181018
let (id, index) = split_once(&params.id, ':').unwrap();
10191019
let index = index.parse::<usize>().unwrap();
10201020
let assist = &assists[index];
1021-
assert!(assist.assist.id.0 == id);
1021+
assert!(assist.id.0 == id);
10221022
let edit = to_proto::resolved_code_action(&snap, assist.clone())?.edit;
10231023
code_action.edit = edit;
10241024
Ok(code_action)

0 commit comments

Comments
 (0)