Skip to content

Commit 38a5cb2

Browse files
committed
Add AssistConfig
1 parent d51c1f6 commit 38a5cb2

File tree

13 files changed

+129
-39
lines changed

13 files changed

+129
-39
lines changed
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//! Settings for tweaking assists.
2+
//!
3+
//! The fun thing here is `SnippetCap` -- this type can only be created in this
4+
//! module, and we use to statically check that we only produce snippet
5+
//! assists if we are allowed to.
6+
7+
#[derive(Clone, Debug, PartialEq, Eq)]
8+
pub struct AssistConfig {
9+
pub snippet_cap: Option<SnippetCap>,
10+
}
11+
12+
impl AssistConfig {
13+
pub fn allow_snippets(&mut self, yes: bool) {
14+
self.snippet_cap = if yes { Some(SnippetCap { _private: () }) } else { None }
15+
}
16+
}
17+
18+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19+
pub struct SnippetCap {
20+
_private: (),
21+
}
22+
23+
impl Default for AssistConfig {
24+
fn default() -> Self {
25+
AssistConfig { snippet_cap: Some(SnippetCap { _private: () }) }
26+
}
27+
}

crates/ra_assists/src/assist_context.rs

+45-6
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ use ra_syntax::{
1515
};
1616
use ra_text_edit::TextEditBuilder;
1717

18-
use crate::{Assist, AssistId, GroupLabel, ResolvedAssist};
18+
use crate::{
19+
assist_config::{AssistConfig, SnippetCap},
20+
Assist, AssistId, GroupLabel, ResolvedAssist,
21+
};
1922

2023
/// `AssistContext` allows to apply an assist or check if it could be applied.
2124
///
@@ -48,17 +51,22 @@ use crate::{Assist, AssistId, GroupLabel, ResolvedAssist};
4851
/// moment, because the LSP API is pretty awkward in this place, and it's much
4952
/// easier to just compute the edit eagerly :-)
5053
pub(crate) struct AssistContext<'a> {
54+
pub(crate) config: &'a AssistConfig,
5155
pub(crate) sema: Semantics<'a, RootDatabase>,
5256
pub(crate) db: &'a RootDatabase,
5357
pub(crate) frange: FileRange,
5458
source_file: SourceFile,
5559
}
5660

5761
impl<'a> AssistContext<'a> {
58-
pub fn new(sema: Semantics<'a, RootDatabase>, frange: FileRange) -> AssistContext<'a> {
62+
pub(crate) fn new(
63+
sema: Semantics<'a, RootDatabase>,
64+
config: &'a AssistConfig,
65+
frange: FileRange,
66+
) -> AssistContext<'a> {
5967
let source_file = sema.parse(frange.file_id);
6068
let db = sema.db;
61-
AssistContext { sema, db, frange, source_file }
69+
AssistContext { config, sema, db, frange, source_file }
6270
}
6371

6472
// NB, this ignores active selection.
@@ -165,11 +173,17 @@ pub(crate) struct AssistBuilder {
165173
edit: TextEditBuilder,
166174
cursor_position: Option<TextSize>,
167175
file: FileId,
176+
is_snippet: bool,
168177
}
169178

170179
impl AssistBuilder {
171180
pub(crate) fn new(file: FileId) -> AssistBuilder {
172-
AssistBuilder { edit: TextEditBuilder::default(), cursor_position: None, file }
181+
AssistBuilder {
182+
edit: TextEditBuilder::default(),
183+
cursor_position: None,
184+
file,
185+
is_snippet: false,
186+
}
173187
}
174188

175189
/// Remove specified `range` of text.
@@ -180,10 +194,30 @@ impl AssistBuilder {
180194
pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) {
181195
self.edit.insert(offset, text.into())
182196
}
197+
/// Append specified `text` at the given `offset`
198+
pub(crate) fn insert_snippet(
199+
&mut self,
200+
_cap: SnippetCap,
201+
offset: TextSize,
202+
text: impl Into<String>,
203+
) {
204+
self.is_snippet = true;
205+
self.edit.insert(offset, text.into())
206+
}
183207
/// Replaces specified `range` of text with a given string.
184208
pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
185209
self.edit.replace(range, replace_with.into())
186210
}
211+
/// Append specified `text` at the given `offset`
212+
pub(crate) fn replace_snippet(
213+
&mut self,
214+
_cap: SnippetCap,
215+
range: TextRange,
216+
replace_with: impl Into<String>,
217+
) {
218+
self.is_snippet = true;
219+
self.edit.replace(range, replace_with.into())
220+
}
187221
pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) {
188222
algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
189223
}
@@ -227,7 +261,12 @@ impl AssistBuilder {
227261
if edit.is_empty() && self.cursor_position.is_none() {
228262
panic!("Only call `add_assist` if the assist can be applied")
229263
}
230-
SingleFileChange { label: change_label, edit, cursor_position: self.cursor_position }
231-
.into_source_change(self.file)
264+
let mut res =
265+
SingleFileChange { label: change_label, edit, cursor_position: self.cursor_position }
266+
.into_source_change(self.file);
267+
if self.is_snippet {
268+
res.is_snippet = true;
269+
}
270+
res
232271
}
233272
}

crates/ra_assists/src/lib.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ macro_rules! eprintln {
1010
($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
1111
}
1212

13+
mod assist_config;
1314
mod assist_context;
1415
mod marks;
1516
#[cfg(test)]
@@ -24,6 +25,8 @@ use ra_syntax::TextRange;
2425

2526
pub(crate) use crate::assist_context::{AssistContext, Assists};
2627

28+
pub use assist_config::AssistConfig;
29+
2730
/// Unique identifier of the assist, should not be shown to the user
2831
/// directly.
2932
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -54,9 +57,9 @@ impl Assist {
5457
///
5558
/// Assists are returned in the "unresolved" state, that is only labels are
5659
/// returned, without actual edits.
57-
pub fn unresolved(db: &RootDatabase, range: FileRange) -> Vec<Assist> {
60+
pub fn unresolved(db: &RootDatabase, config: &AssistConfig, range: FileRange) -> Vec<Assist> {
5861
let sema = Semantics::new(db);
59-
let ctx = AssistContext::new(sema, range);
62+
let ctx = AssistContext::new(sema, config, range);
6063
let mut acc = Assists::new_unresolved(&ctx);
6164
handlers::all().iter().for_each(|handler| {
6265
handler(&mut acc, &ctx);
@@ -68,9 +71,13 @@ impl Assist {
6871
///
6972
/// Assists are returned in the "resolved" state, that is with edit fully
7073
/// computed.
71-
pub fn resolved(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> {
74+
pub fn resolved(
75+
db: &RootDatabase,
76+
config: &AssistConfig,
77+
range: FileRange,
78+
) -> Vec<ResolvedAssist> {
7279
let sema = Semantics::new(db);
73-
let ctx = AssistContext::new(sema, range);
80+
let ctx = AssistContext::new(sema,config, range);
7481
let mut acc = Assists::new_resolved(&ctx);
7582
handlers::all().iter().for_each(|handler| {
7683
handler(&mut acc, &ctx);

crates/ra_assists/src/tests.rs

+20-18
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use test_utils::{
1111
RangeOrOffset,
1212
};
1313

14-
use crate::{handlers::Handler, Assist, AssistContext, Assists};
14+
use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, Assists};
1515

1616
pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
1717
let (mut db, file_id) = RootDatabase::with_single_file(text);
@@ -41,14 +41,14 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) {
4141
let (db, file_id) = crate::tests::with_single_file(&before);
4242
let frange = FileRange { file_id, range: selection.into() };
4343

44-
let mut assist = Assist::resolved(&db, frange)
44+
let mut assist = Assist::resolved(&db, &AssistConfig::default(), frange)
4545
.into_iter()
4646
.find(|assist| assist.assist.id.0 == assist_id)
4747
.unwrap_or_else(|| {
4848
panic!(
4949
"\n\nAssist is not applicable: {}\nAvailable assists: {}",
5050
assist_id,
51-
Assist::resolved(&db, frange)
51+
Assist::resolved(&db, &AssistConfig::default(), frange)
5252
.into_iter()
5353
.map(|assist| assist.assist.id.0)
5454
.collect::<Vec<_>>()
@@ -90,7 +90,8 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult) {
9090
let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() };
9191

9292
let sema = Semantics::new(&db);
93-
let ctx = AssistContext::new(sema, frange);
93+
let config = AssistConfig::default();
94+
let ctx = AssistContext::new(sema, &config, frange);
9495
let mut acc = Assists::new_resolved(&ctx);
9596
handler(&mut acc, &ctx);
9697
let mut res = acc.finish_resolved();
@@ -103,19 +104,20 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult) {
103104
let mut actual = db.file_text(change.file_id).as_ref().to_owned();
104105
change.edit.apply(&mut actual);
105106

106-
match source_change.cursor_position {
107-
None => {
108-
if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset {
109-
let off = change
110-
.edit
111-
.apply_to_offset(before_cursor_pos)
112-
.expect("cursor position is affected by the edit");
113-
actual = add_cursor(&actual, off)
107+
if !source_change.is_snippet {
108+
match source_change.cursor_position {
109+
None => {
110+
if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset {
111+
let off = change
112+
.edit
113+
.apply_to_offset(before_cursor_pos)
114+
.expect("cursor position is affected by the edit");
115+
actual = add_cursor(&actual, off)
116+
}
114117
}
115-
}
116-
Some(off) => actual = add_cursor(&actual, off.offset),
117-
};
118-
118+
Some(off) => actual = add_cursor(&actual, off.offset),
119+
};
120+
}
119121
assert_eq_text!(after, &actual);
120122
}
121123
(Some(assist), ExpectedResult::Target(target)) => {
@@ -136,7 +138,7 @@ fn assist_order_field_struct() {
136138
let (before_cursor_pos, before) = extract_offset(before);
137139
let (db, file_id) = with_single_file(&before);
138140
let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) };
139-
let assists = Assist::resolved(&db, frange);
141+
let assists = Assist::resolved(&db, &AssistConfig::default(), frange);
140142
let mut assists = assists.iter();
141143

142144
assert_eq!(
@@ -159,7 +161,7 @@ fn assist_order_if_expr() {
159161
let (range, before) = extract_range(before);
160162
let (db, file_id) = with_single_file(&before);
161163
let frange = FileRange { file_id, range };
162-
let assists = Assist::resolved(&db, frange);
164+
let assists = Assist::resolved(&db, &AssistConfig::default(), frange);
163165
let mut assists = assists.iter();
164166

165167
assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable");

crates/ra_ide/src/completion.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ pub use crate::completion::{
5959
/// with ordering of completions (currently this is done by the client).
6060
pub(crate) fn completions(
6161
db: &RootDatabase,
62-
position: FilePosition,
6362
config: &CompletionConfig,
63+
position: FilePosition,
6464
) -> Option<Completions> {
6565
let ctx = CompletionContext::new(db, position, config)?;
6666

crates/ra_ide/src/completion/test_utils.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub(crate) fn do_completion_with_options(
2020
} else {
2121
single_file_with_position(code)
2222
};
23-
let completions = analysis.completions(position, options).unwrap().unwrap();
23+
let completions = analysis.completions(options, position).unwrap().unwrap();
2424
let completion_items: Vec<CompletionItem> = completions.into();
2525
let mut kind_completions: Vec<CompletionItem> =
2626
completion_items.into_iter().filter(|c| c.completion_kind == kind).collect();

crates/ra_ide/src/diagnostics.rs

+2
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,7 @@ mod tests {
629629
},
630630
],
631631
cursor_position: None,
632+
is_snippet: false,
632633
},
633634
),
634635
severity: Error,
@@ -685,6 +686,7 @@ mod tests {
685686
],
686687
file_system_edits: [],
687688
cursor_position: None,
689+
is_snippet: false,
688690
},
689691
),
690692
severity: Error,

crates/ra_ide/src/lib.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ pub use crate::{
8282
};
8383

8484
pub use hir::Documentation;
85-
pub use ra_assists::AssistId;
85+
pub use ra_assists::{AssistId, AssistConfig};
8686
pub use ra_db::{
8787
Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId,
8888
};
@@ -458,17 +458,17 @@ impl Analysis {
458458
/// Computes completions at the given position.
459459
pub fn completions(
460460
&self,
461-
position: FilePosition,
462461
config: &CompletionConfig,
462+
position: FilePosition,
463463
) -> Cancelable<Option<Vec<CompletionItem>>> {
464-
self.with_db(|db| completion::completions(db, position, config).map(Into::into))
464+
self.with_db(|db| completion::completions(db, config, position).map(Into::into))
465465
}
466466

467467
/// Computes assists (aka code actions aka intentions) for the given
468468
/// position.
469-
pub fn assists(&self, frange: FileRange) -> Cancelable<Vec<Assist>> {
469+
pub fn assists(&self, config: &AssistConfig, frange: FileRange) -> Cancelable<Vec<Assist>> {
470470
self.with_db(|db| {
471-
ra_assists::Assist::resolved(db, frange)
471+
ra_assists::Assist::resolved(db, config, frange)
472472
.into_iter()
473473
.map(|assist| Assist {
474474
id: assist.assist.id,

crates/ra_ide/src/references/rename.rs

+3
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,7 @@ mod tests {
558558
},
559559
],
560560
cursor_position: None,
561+
is_snippet: false,
561562
},
562563
},
563564
)
@@ -610,6 +611,7 @@ mod tests {
610611
},
611612
],
612613
cursor_position: None,
614+
is_snippet: false,
613615
},
614616
},
615617
)
@@ -706,6 +708,7 @@ mod tests {
706708
},
707709
],
708710
cursor_position: None,
711+
is_snippet: false,
709712
},
710713
},
711714
)

crates/ra_ide_db/src/source_change.rs

+5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub struct SourceChange {
1313
pub source_file_edits: Vec<SourceFileEdit>,
1414
pub file_system_edits: Vec<FileSystemEdit>,
1515
pub cursor_position: Option<FilePosition>,
16+
pub is_snippet: bool,
1617
}
1718

1819
impl SourceChange {
@@ -28,6 +29,7 @@ impl SourceChange {
2829
source_file_edits,
2930
file_system_edits,
3031
cursor_position: None,
32+
is_snippet: false,
3133
}
3234
}
3335

@@ -41,6 +43,7 @@ impl SourceChange {
4143
source_file_edits: edits,
4244
file_system_edits: vec![],
4345
cursor_position: None,
46+
is_snippet: false,
4447
}
4548
}
4649

@@ -52,6 +55,7 @@ impl SourceChange {
5255
source_file_edits: vec![],
5356
file_system_edits: edits,
5457
cursor_position: None,
58+
is_snippet: false,
5559
}
5660
}
5761

@@ -115,6 +119,7 @@ impl SingleFileChange {
115119
source_file_edits: vec![SourceFileEdit { file_id, edit: self.edit }],
116120
file_system_edits: Vec::new(),
117121
cursor_position: self.cursor_position.map(|offset| FilePosition { file_id, offset }),
122+
is_snippet: false,
118123
}
119124
}
120125
}

0 commit comments

Comments
 (0)