aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKirill Bulatov <[email protected]>2020-01-11 22:40:36 +0000
committerKirill Bulatov <[email protected]>2020-01-15 18:17:17 +0000
commit78a21253b494e573885ac8336bff6e93b401046f (patch)
tree3ec31f8481f9a930b0b3afb6cc0ce4c97cf648f2
parent73dc8b6f06b49f4728a50e83781c361e9a8b3100 (diff)
Apply the api design suggestions
-rw-r--r--Cargo.lock1
-rw-r--r--crates/ra_assists/src/assist_ctx.rs25
-rw-r--r--crates/ra_assists/src/doc_tests.rs8
-rw-r--r--crates/ra_assists/src/lib.rs40
-rw-r--r--crates/ra_ide/src/assists.rs23
-rw-r--r--crates/ra_lsp_server/Cargo.toml1
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs28
-rw-r--r--editors/code/src/commands/index.ts17
-rw-r--r--editors/code/src/main.ts1
-rw-r--r--editors/code/src/source_change.ts12
10 files changed, 97 insertions, 59 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a0444e97a..2e7698b59 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1067,6 +1067,7 @@ version = "0.1.0"
1067dependencies = [ 1067dependencies = [
1068 "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 1068 "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
1069 "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 1069 "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
1070 "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
1070 "jod-thread 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1071 "jod-thread 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
1071 "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 1072 "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
1072 "lsp-server 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 1073 "lsp-server 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs
index 879216a36..e13f969c7 100644
--- a/crates/ra_assists/src/assist_ctx.rs
+++ b/crates/ra_assists/src/assist_ctx.rs
@@ -1,5 +1,6 @@
1//! This module defines `AssistCtx` -- the API surface that is exposed to assists. 1//! This module defines `AssistCtx` -- the API surface that is exposed to assists.
2use hir::{db::HirDatabase, InFile, SourceAnalyzer}; 2use hir::{db::HirDatabase, InFile, SourceAnalyzer};
3use itertools::Either;
3use ra_db::FileRange; 4use ra_db::FileRange;
4use ra_fmt::{leading_indent, reindent}; 5use ra_fmt::{leading_indent, reindent};
5use ra_syntax::{ 6use ra_syntax::{
@@ -9,12 +10,12 @@ use ra_syntax::{
9}; 10};
10use ra_text_edit::TextEditBuilder; 11use ra_text_edit::TextEditBuilder;
11 12
12use crate::{AssistAction, AssistId, AssistLabel}; 13use crate::{AssistAction, AssistId, AssistLabel, ResolvedAssist};
13 14
14#[derive(Clone, Debug)] 15#[derive(Clone, Debug)]
15pub(crate) enum Assist { 16pub(crate) enum Assist {
16 Unresolved { label: AssistLabel }, 17 Unresolved { label: AssistLabel },
17 Resolved { label: AssistLabel, action: AssistAction, alternative_actions: Vec<AssistAction> }, 18 Resolved { assist: ResolvedAssist },
18} 19}
19 20
20/// `AssistCtx` allows to apply an assist or check if it could be applied. 21/// `AssistCtx` allows to apply an assist or check if it could be applied.
@@ -92,7 +93,7 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> {
92 f(&mut edit); 93 f(&mut edit);
93 edit.build() 94 edit.build()
94 }; 95 };
95 Assist::Resolved { label, action, alternative_actions: Vec::default() } 96 Assist::Resolved { assist: ResolvedAssist { label, action_data: Either::Left(action) } }
96 } else { 97 } else {
97 Assist::Unresolved { label } 98 Assist::Unresolved { label }
98 }; 99 };
@@ -105,18 +106,20 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> {
105 self, 106 self,
106 id: AssistId, 107 id: AssistId,
107 label: impl Into<String>, 108 label: impl Into<String>,
108 f: impl FnOnce() -> (ActionBuilder, Vec<ActionBuilder>), 109 f: impl FnOnce() -> Vec<ActionBuilder>,
109 ) -> Option<Assist> { 110 ) -> Option<Assist> {
110 let label = AssistLabel { label: label.into(), id }; 111 let label = AssistLabel { label: label.into(), id };
111 let assist = if self.should_compute_edit { 112 let assist = if self.should_compute_edit {
112 let (action, alternative_actions) = f(); 113 let actions = f();
114 assert!(!actions.is_empty(), "Assist cannot have no");
115
113 Assist::Resolved { 116 Assist::Resolved {
114 label, 117 assist: ResolvedAssist {
115 action: action.build(), 118 label,
116 alternative_actions: alternative_actions 119 action_data: Either::Right(
117 .into_iter() 120 actions.into_iter().map(ActionBuilder::build).collect(),
118 .map(ActionBuilder::build) 121 ),
119 .collect(), 122 },
120 } 123 }
121 } else { 124 } else {
122 Assist::Unresolved { label } 125 Assist::Unresolved { label }
diff --git a/crates/ra_assists/src/doc_tests.rs b/crates/ra_assists/src/doc_tests.rs
index 21bee228d..5dc1ee233 100644
--- a/crates/ra_assists/src/doc_tests.rs
+++ b/crates/ra_assists/src/doc_tests.rs
@@ -15,21 +15,21 @@ fn check(assist_id: &str, before: &str, after: &str) {
15 let (db, file_id) = TestDB::with_single_file(&before); 15 let (db, file_id) = TestDB::with_single_file(&before);
16 let frange = FileRange { file_id, range: selection.into() }; 16 let frange = FileRange { file_id, range: selection.into() };
17 17
18 let (_assist_id, action, _) = crate::assists(&db, frange) 18 let assist = crate::assists(&db, frange)
19 .into_iter() 19 .into_iter()
20 .find(|(id, _, _)| id.id.0 == assist_id) 20 .find(|assist| assist.label.id.0 == assist_id)
21 .unwrap_or_else(|| { 21 .unwrap_or_else(|| {
22 panic!( 22 panic!(
23 "\n\nAssist is not applicable: {}\nAvailable assists: {}", 23 "\n\nAssist is not applicable: {}\nAvailable assists: {}",
24 assist_id, 24 assist_id,
25 crate::assists(&db, frange) 25 crate::assists(&db, frange)
26 .into_iter() 26 .into_iter()
27 .map(|(id, _, _)| id.id.0) 27 .map(|assist| assist.label.id.0)
28 .collect::<Vec<_>>() 28 .collect::<Vec<_>>()
29 .join(", ") 29 .join(", ")
30 ) 30 )
31 }); 31 });
32 32
33 let actual = action.edit.apply(&before); 33 let actual = assist.get_first_action().edit.apply(&before);
34 assert_eq_text!(after, &actual); 34 assert_eq_text!(after, &actual);
35} 35}
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 95a530821..a2983ae87 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -14,6 +14,7 @@ mod test_db;
14pub mod ast_transform; 14pub mod ast_transform;
15 15
16use hir::db::HirDatabase; 16use hir::db::HirDatabase;
17use itertools::Either;
17use ra_db::FileRange; 18use ra_db::FileRange;
18use ra_syntax::{TextRange, TextUnit}; 19use ra_syntax::{TextRange, TextUnit};
19use ra_text_edit::TextEdit; 20use ra_text_edit::TextEdit;
@@ -41,6 +42,21 @@ pub struct AssistAction {
41 pub target: Option<TextRange>, 42 pub target: Option<TextRange>,
42} 43}
43 44
45#[derive(Debug, Clone)]
46pub struct ResolvedAssist {
47 pub label: AssistLabel,
48 pub action_data: Either<AssistAction, Vec<AssistAction>>,
49}
50
51impl ResolvedAssist {
52 pub fn get_first_action(&self) -> AssistAction {
53 match &self.action_data {
54 Either::Left(action) => action.clone(),
55 Either::Right(actions) => actions[0].clone(),
56 }
57 }
58}
59
44/// Return all the assists applicable at the given position. 60/// Return all the assists applicable at the given position.
45/// 61///
46/// Assists are returned in the "unresolved" state, that is only labels are 62/// Assists are returned in the "unresolved" state, that is only labels are
@@ -65,7 +81,7 @@ where
65/// 81///
66/// Assists are returned in the "resolved" state, that is with edit fully 82/// Assists are returned in the "resolved" state, that is with edit fully
67/// computed. 83/// computed.
68pub fn assists<H>(db: &H, range: FileRange) -> Vec<(AssistLabel, AssistAction, Vec<AssistAction>)> 84pub fn assists<H>(db: &H, range: FileRange) -> Vec<ResolvedAssist>
69where 85where
70 H: HirDatabase + 'static, 86 H: HirDatabase + 'static,
71{ 87{
@@ -76,13 +92,11 @@ where
76 .iter() 92 .iter()
77 .filter_map(|f| f(ctx.clone())) 93 .filter_map(|f| f(ctx.clone()))
78 .map(|a| match a { 94 .map(|a| match a {
79 Assist::Resolved { label, action, alternative_actions } => { 95 Assist::Resolved { assist } => assist,
80 (label, action, alternative_actions)
81 }
82 Assist::Unresolved { .. } => unreachable!(), 96 Assist::Unresolved { .. } => unreachable!(),
83 }) 97 })
84 .collect::<Vec<_>>(); 98 .collect::<Vec<_>>();
85 a.sort_by(|a, b| match (a.1.target, b.1.target) { 99 a.sort_by(|a, b| match (a.get_first_action().target, b.get_first_action().target) {
86 (Some(a), Some(b)) => a.len().cmp(&b.len()), 100 (Some(a), Some(b)) => a.len().cmp(&b.len()),
87 (Some(_), None) => Ordering::Less, 101 (Some(_), None) => Ordering::Less,
88 (None, Some(_)) => Ordering::Greater, 102 (None, Some(_)) => Ordering::Greater,
@@ -177,7 +191,7 @@ mod helpers {
177 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); 191 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
178 let action = match assist { 192 let action = match assist {
179 Assist::Unresolved { .. } => unreachable!(), 193 Assist::Unresolved { .. } => unreachable!(),
180 Assist::Resolved { action, .. } => action, 194 Assist::Resolved { assist } => assist.get_first_action(),
181 }; 195 };
182 196
183 let actual = action.edit.apply(&before); 197 let actual = action.edit.apply(&before);
@@ -204,7 +218,7 @@ mod helpers {
204 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); 218 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
205 let action = match assist { 219 let action = match assist {
206 Assist::Unresolved { .. } => unreachable!(), 220 Assist::Unresolved { .. } => unreachable!(),
207 Assist::Resolved { action, .. } => action, 221 Assist::Resolved { assist } => assist.get_first_action(),
208 }; 222 };
209 223
210 let mut actual = action.edit.apply(&before); 224 let mut actual = action.edit.apply(&before);
@@ -227,7 +241,7 @@ mod helpers {
227 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); 241 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
228 let action = match assist { 242 let action = match assist {
229 Assist::Unresolved { .. } => unreachable!(), 243 Assist::Unresolved { .. } => unreachable!(),
230 Assist::Resolved { action, .. } => action, 244 Assist::Resolved { assist } => assist.get_first_action(),
231 }; 245 };
232 246
233 let range = action.target.expect("expected target on action"); 247 let range = action.target.expect("expected target on action");
@@ -246,7 +260,7 @@ mod helpers {
246 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); 260 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
247 let action = match assist { 261 let action = match assist {
248 Assist::Unresolved { .. } => unreachable!(), 262 Assist::Unresolved { .. } => unreachable!(),
249 Assist::Resolved { action, .. } => action, 263 Assist::Resolved { assist } => assist.get_first_action(),
250 }; 264 };
251 265
252 let range = action.target.expect("expected target on action"); 266 let range = action.target.expect("expected target on action");
@@ -296,10 +310,10 @@ mod tests {
296 let mut assists = assists.iter(); 310 let mut assists = assists.iter();
297 311
298 assert_eq!( 312 assert_eq!(
299 assists.next().expect("expected assist").0.label, 313 assists.next().expect("expected assist").label.label,
300 "Change visibility to pub(crate)" 314 "Change visibility to pub(crate)"
301 ); 315 );
302 assert_eq!(assists.next().expect("expected assist").0.label, "Add `#[derive]`"); 316 assert_eq!(assists.next().expect("expected assist").label.label, "Add `#[derive]`");
303 } 317 }
304 318
305 #[test] 319 #[test]
@@ -318,7 +332,7 @@ mod tests {
318 let assists = super::assists(&db, frange); 332 let assists = super::assists(&db, frange);
319 let mut assists = assists.iter(); 333 let mut assists = assists.iter();
320 334
321 assert_eq!(assists.next().expect("expected assist").0.label, "Extract into variable"); 335 assert_eq!(assists.next().expect("expected assist").label.label, "Extract into variable");
322 assert_eq!(assists.next().expect("expected assist").0.label, "Replace with match"); 336 assert_eq!(assists.next().expect("expected assist").label.label, "Replace with match");
323 } 337 }
324} 338}
diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs
index db6e4e8b7..e30eee5f4 100644
--- a/crates/ra_ide/src/assists.rs
+++ b/crates/ra_ide/src/assists.rs
@@ -4,30 +4,37 @@ use ra_db::{FilePosition, FileRange};
4 4
5use crate::{db::RootDatabase, FileId, SourceChange, SourceFileEdit}; 5use crate::{db::RootDatabase, FileId, SourceChange, SourceFileEdit};
6 6
7use itertools::Either;
7pub use ra_assists::AssistId; 8pub use ra_assists::AssistId;
8use ra_assists::{AssistAction, AssistLabel}; 9use ra_assists::{AssistAction, AssistLabel};
9 10
10#[derive(Debug)] 11#[derive(Debug)]
11pub struct Assist { 12pub struct Assist {
12 pub id: AssistId, 13 pub id: AssistId,
13 pub change: SourceChange,
14 pub label: String, 14 pub label: String,
15 pub alternative_changes: Vec<SourceChange>, 15 pub change_data: Either<SourceChange, Vec<SourceChange>>,
16} 16}
17 17
18pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> { 18pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> {
19 ra_assists::assists(db, frange) 19 ra_assists::assists(db, frange)
20 .into_iter() 20 .into_iter()
21 .map(|(assist_label, action, alternative_actions)| { 21 .map(|assist| {
22 let file_id = frange.file_id; 22 let file_id = frange.file_id;
23 let assist_label = &assist.label;
23 Assist { 24 Assist {
24 id: assist_label.id, 25 id: assist_label.id,
25 label: assist_label.label.clone(), 26 label: assist_label.label.clone(),
26 change: action_to_edit(action, file_id, &assist_label), 27 change_data: match assist.action_data {
27 alternative_changes: alternative_actions 28 Either::Left(action) => {
28 .into_iter() 29 Either::Left(action_to_edit(action, file_id, assist_label))
29 .map(|action| action_to_edit(action, file_id, &assist_label)) 30 }
30 .collect(), 31 Either::Right(actions) => Either::Right(
32 actions
33 .into_iter()
34 .map(|action| action_to_edit(action, file_id, assist_label))
35 .collect(),
36 ),
37 },
31 } 38 }
32 }) 39 })
33 .collect() 40 .collect()
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml
index c08e67b8e..73bce8b08 100644
--- a/crates/ra_lsp_server/Cargo.toml
+++ b/crates/ra_lsp_server/Cargo.toml
@@ -28,6 +28,7 @@ ra_prof = { path = "../ra_prof" }
28ra_vfs_glob = { path = "../ra_vfs_glob" } 28ra_vfs_glob = { path = "../ra_vfs_glob" }
29env_logger = { version = "0.7.1", default-features = false, features = ["humantime"] } 29env_logger = { version = "0.7.1", default-features = false, features = ["humantime"] }
30ra_cargo_watch = { path = "../ra_cargo_watch" } 30ra_cargo_watch = { path = "../ra_cargo_watch" }
31itertools = "0.8"
31 32
32[dev-dependencies] 33[dev-dependencies]
33tempfile = "3" 34tempfile = "3"
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index ec3c0a557..d76639474 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -3,6 +3,7 @@
3 3
4use std::{fmt::Write as _, io::Write as _}; 4use std::{fmt::Write as _, io::Write as _};
5 5
6use itertools::Either;
6use lsp_server::ErrorCode; 7use lsp_server::ErrorCode;
7use lsp_types::{ 8use lsp_types::{
8 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, 9 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
@@ -698,18 +699,25 @@ pub fn handle_code_action(
698 699
699 for assist in world.analysis().assists(FileRange { file_id, range })?.into_iter() { 700 for assist in world.analysis().assists(FileRange { file_id, range })?.into_iter() {
700 let title = assist.label.clone(); 701 let title = assist.label.clone();
701 let edit = assist.change.try_conv_with(&world)?;
702 let alternative_edits = assist
703 .alternative_changes
704 .into_iter()
705 .map(|change| change.try_conv_with(&world))
706 .collect::<Result<Vec<_>>>()?;
707 702
708 let command = Command { 703 let command = match assist.change_data {
709 title, 704 Either::Left(change) => Command {
710 command: "rust-analyzer.applySourceChange".to_string(), 705 title,
711 arguments: Some(vec![to_value(edit).unwrap(), to_value(alternative_edits).unwrap()]), 706 command: "rust-analyzer.applySourceChange".to_string(),
707 arguments: Some(vec![to_value(change.try_conv_with(&world)?)?]),
708 },
709 Either::Right(changes) => Command {
710 title,
711 command: "rust-analyzer.selectAndApplySourceChange".to_string(),
712 arguments: Some(vec![to_value(
713 changes
714 .into_iter()
715 .map(|change| change.try_conv_with(&world))
716 .collect::<Result<Vec<_>>>()?,
717 )?]),
718 },
712 }; 719 };
720
713 let action = CodeAction { 721 let action = CodeAction {
714 title: command.title.clone(), 722 title: command.title.clone(),
715 kind: match assist.id { 723 kind: match assist.id {
diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts
index 0ff708b1f..dc075aa82 100644
--- a/editors/code/src/commands/index.ts
+++ b/editors/code/src/commands/index.ts
@@ -34,8 +34,20 @@ function showReferences(ctx: Ctx): Cmd {
34} 34}
35 35
36function applySourceChange(ctx: Ctx): Cmd { 36function applySourceChange(ctx: Ctx): Cmd {
37 return async (change: sourceChange.SourceChange, alternativeChanges: sourceChange.SourceChange[] | undefined) => { 37 return async (change: sourceChange.SourceChange) => {
38 sourceChange.applySourceChange(ctx, change, alternativeChanges); 38 sourceChange.applySourceChange(ctx, change);
39 };
40}
41
42function selectAndApplySourceChange(ctx: Ctx): Cmd {
43 return async (changes: sourceChange.SourceChange[]) => {
44 if (changes.length === 1) {
45 await sourceChange.applySourceChange(ctx, changes[0]);
46 } else if (changes.length > 0) {
47 const selectedChange = await vscode.window.showQuickPick(changes);
48 if (!selectedChange) return;
49 await sourceChange.applySourceChange(ctx, selectedChange);
50 }
39 }; 51 };
40} 52}
41 53
@@ -59,5 +71,6 @@ export {
59 runSingle, 71 runSingle,
60 showReferences, 72 showReferences,
61 applySourceChange, 73 applySourceChange,
74 selectAndApplySourceChange,
62 reload 75 reload
63}; 76};
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 430ad31b4..0494ccf63 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -26,6 +26,7 @@ export async function activate(context: vscode.ExtensionContext) {
26 ctx.registerCommand('runSingle', commands.runSingle); 26 ctx.registerCommand('runSingle', commands.runSingle);
27 ctx.registerCommand('showReferences', commands.showReferences); 27 ctx.registerCommand('showReferences', commands.showReferences);
28 ctx.registerCommand('applySourceChange', commands.applySourceChange); 28 ctx.registerCommand('applySourceChange', commands.applySourceChange);
29 ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange);
29 30
30 if (ctx.config.enableEnhancedTyping) { 31 if (ctx.config.enableEnhancedTyping) {
31 ctx.overrideCommand('type', commands.onEnter); 32 ctx.overrideCommand('type', commands.onEnter);
diff --git a/editors/code/src/source_change.ts b/editors/code/src/source_change.ts
index b19d325d5..a336269ba 100644
--- a/editors/code/src/source_change.ts
+++ b/editors/code/src/source_change.ts
@@ -9,7 +9,7 @@ export interface SourceChange {
9 cursorPosition?: lc.TextDocumentPositionParams; 9 cursorPosition?: lc.TextDocumentPositionParams;
10} 10}
11 11
12async function applySelectedSourceChange(ctx: Ctx, change: SourceChange) { 12export async function applySourceChange(ctx: Ctx, change: SourceChange) {
13 const client = ctx.client; 13 const client = ctx.client;
14 if (!client) return; 14 if (!client) return;
15 15
@@ -55,13 +55,3 @@ async function applySelectedSourceChange(ctx: Ctx, change: SourceChange) {
55 ); 55 );
56 } 56 }
57} 57}
58
59export async function applySourceChange(ctx: Ctx, change: SourceChange, alternativeChanges: SourceChange[] | undefined) {
60 if (alternativeChanges !== undefined && alternativeChanges.length > 0) {
61 const selectedChange = await vscode.window.showQuickPick([change, ...alternativeChanges]);
62 if (!selectedChange) return;
63 await applySelectedSourceChange(ctx, selectedChange);
64 } else {
65 await applySelectedSourceChange(ctx, change);
66 }
67}