From 73dc8b6f06b49f4728a50e83781c361e9a8b3100 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 2 Jan 2020 01:39:01 +0200 Subject: [PATCH 1/4] Another attempt to add multiple edits --- crates/ra_assists/src/assist_ctx.rs | 45 ++++++++++++++++--- .../src/assists/inline_local_variable.rs | 4 +- crates/ra_assists/src/doc_tests.rs | 6 +-- crates/ra_assists/src/lib.rs | 7 ++- crates/ra_ide/src/assists.rs | 35 +++++++++++---- .../ra_lsp_server/src/main_loop/handlers.rs | 12 +++-- editors/code/src/commands/index.ts | 4 +- editors/code/src/source_change.ts | 12 ++++- 8 files changed, 97 insertions(+), 28 deletions(-) diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index 1a65b5dc070a..879216a3669a 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs @@ -14,7 +14,7 @@ use crate::{AssistAction, AssistId, AssistLabel}; #[derive(Clone, Debug)] pub(crate) enum Assist { Unresolved { label: AssistLabel }, - Resolved { label: AssistLabel, action: AssistAction }, + Resolved { label: AssistLabel, action: AssistAction, alternative_actions: Vec }, } /// `AssistCtx` allows to apply an assist or check if it could be applied. @@ -81,18 +81,43 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> { self, id: AssistId, label: impl Into, - f: impl FnOnce(&mut AssistBuilder), + f: impl FnOnce(&mut ActionBuilder), ) -> Option { let label = AssistLabel { label: label.into(), id }; assert!(label.label.chars().nth(0).unwrap().is_uppercase()); let assist = if self.should_compute_edit { let action = { - let mut edit = AssistBuilder::default(); + let mut edit = ActionBuilder::default(); f(&mut edit); edit.build() }; - Assist::Resolved { label, action } + Assist::Resolved { label, action, alternative_actions: Vec::default() } + } else { + Assist::Unresolved { label } + }; + + Some(assist) + } + + #[allow(dead_code)] // will be used for auto import assist with multiple actions + pub(crate) fn add_assist_group( + self, + id: AssistId, + label: impl Into, + f: impl FnOnce() -> (ActionBuilder, Vec), + ) -> Option { + let label = AssistLabel { label: label.into(), id }; + let assist = if self.should_compute_edit { + let (action, alternative_actions) = f(); + Assist::Resolved { + label, + action: action.build(), + alternative_actions: alternative_actions + .into_iter() + .map(ActionBuilder::build) + .collect(), + } } else { Assist::Unresolved { label } }; @@ -128,13 +153,20 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> { } #[derive(Default)] -pub(crate) struct AssistBuilder { +pub(crate) struct ActionBuilder { edit: TextEditBuilder, cursor_position: Option, target: Option, + label: Option, } -impl AssistBuilder { +impl ActionBuilder { + #[allow(dead_code)] + /// Adds a custom label to the action, if it needs to be different from the assist label + pub fn label(&mut self, label: impl Into) { + self.label = Some(label.into()) + } + /// Replaces specified `range` of text with a given string. pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into) { self.edit.replace(range, replace_with.into()) @@ -193,6 +225,7 @@ impl AssistBuilder { edit: self.edit.finish(), cursor_position: self.cursor_position, target: self.target, + label: self.label, } } } diff --git a/crates/ra_assists/src/assists/inline_local_variable.rs b/crates/ra_assists/src/assists/inline_local_variable.rs index 164aee90cdaa..45e0f983fecb 100644 --- a/crates/ra_assists/src/assists/inline_local_variable.rs +++ b/crates/ra_assists/src/assists/inline_local_variable.rs @@ -4,7 +4,7 @@ use ra_syntax::{ TextRange, }; -use crate::assist_ctx::AssistBuilder; +use crate::assist_ctx::ActionBuilder; use crate::{Assist, AssistCtx, AssistId}; // Assist: inline_local_variable @@ -94,7 +94,7 @@ pub(crate) fn inline_local_varialbe(ctx: AssistCtx) -> Option< ctx.add_assist( AssistId("inline_local_variable"), "Inline variable", - move |edit: &mut AssistBuilder| { + move |edit: &mut ActionBuilder| { edit.delete(delete_range); for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { if should_wrap { diff --git a/crates/ra_assists/src/doc_tests.rs b/crates/ra_assists/src/doc_tests.rs index a8f8446cb637..21bee228dadc 100644 --- a/crates/ra_assists/src/doc_tests.rs +++ b/crates/ra_assists/src/doc_tests.rs @@ -15,16 +15,16 @@ fn check(assist_id: &str, before: &str, after: &str) { let (db, file_id) = TestDB::with_single_file(&before); let frange = FileRange { file_id, range: selection.into() }; - let (_assist_id, action) = crate::assists(&db, frange) + let (_assist_id, action, _) = crate::assists(&db, frange) .into_iter() - .find(|(id, _)| id.id.0 == assist_id) + .find(|(id, _, _)| id.id.0 == assist_id) .unwrap_or_else(|| { panic!( "\n\nAssist is not applicable: {}\nAvailable assists: {}", assist_id, crate::assists(&db, frange) .into_iter() - .map(|(id, _)| id.id.0) + .map(|(id, _, _)| id.id.0) .collect::>() .join(", ") ) diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 150b34ac7124..95a530821ec6 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -35,6 +35,7 @@ pub struct AssistLabel { #[derive(Debug, Clone)] pub struct AssistAction { + pub label: Option, pub edit: TextEdit, pub cursor_position: Option, pub target: Option, @@ -64,7 +65,7 @@ where /// /// Assists are returned in the "resolved" state, that is with edit fully /// computed. -pub fn assists(db: &H, range: FileRange) -> Vec<(AssistLabel, AssistAction)> +pub fn assists(db: &H, range: FileRange) -> Vec<(AssistLabel, AssistAction, Vec)> where H: HirDatabase + 'static, { @@ -75,7 +76,9 @@ where .iter() .filter_map(|f| f(ctx.clone())) .map(|a| match a { - Assist::Resolved { label, action } => (label, action), + Assist::Resolved { label, action, alternative_actions } => { + (label, action, alternative_actions) + } Assist::Unresolved { .. } => unreachable!(), }) .collect::>(); diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs index e00589733b7a..db6e4e8b704e 100644 --- a/crates/ra_ide/src/assists.rs +++ b/crates/ra_ide/src/assists.rs @@ -2,27 +2,46 @@ use ra_db::{FilePosition, FileRange}; -use crate::{db::RootDatabase, SourceChange, SourceFileEdit}; +use crate::{db::RootDatabase, FileId, SourceChange, SourceFileEdit}; pub use ra_assists::AssistId; +use ra_assists::{AssistAction, AssistLabel}; #[derive(Debug)] pub struct Assist { pub id: AssistId, pub change: SourceChange, + pub label: String, + pub alternative_changes: Vec, } pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec { ra_assists::assists(db, frange) .into_iter() - .map(|(label, action)| { + .map(|(assist_label, action, alternative_actions)| { let file_id = frange.file_id; - let file_edit = SourceFileEdit { file_id, edit: action.edit }; - let id = label.id; - let change = SourceChange::source_file_edit(label.label, file_edit).with_cursor_opt( - action.cursor_position.map(|offset| FilePosition { offset, file_id }), - ); - Assist { id, change } + Assist { + id: assist_label.id, + label: assist_label.label.clone(), + change: action_to_edit(action, file_id, &assist_label), + alternative_changes: alternative_actions + .into_iter() + .map(|action| action_to_edit(action, file_id, &assist_label)) + .collect(), + } }) .collect() } + +fn action_to_edit( + action: AssistAction, + file_id: FileId, + assist_label: &AssistLabel, +) -> SourceChange { + let file_edit = SourceFileEdit { file_id, edit: action.edit }; + SourceChange::source_file_edit( + action.label.unwrap_or_else(|| assist_label.label.clone()), + file_edit, + ) + .with_cursor_opt(action.cursor_position.map(|offset| FilePosition { offset, file_id })) +} diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index f2db575eabc3..ec3c0a557ae7 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs @@ -644,7 +644,6 @@ pub fn handle_code_action( let line_index = world.analysis().file_line_index(file_id)?; let range = params.range.conv_with(&line_index); - let assists = world.analysis().assists(FileRange { file_id, range })?.into_iter(); let diagnostics = world.analysis().diagnostics(file_id)?; let mut res = CodeActionResponse::default(); @@ -697,14 +696,19 @@ pub fn handle_code_action( res.push(action.into()); } - for assist in assists { - let title = assist.change.label.clone(); + for assist in world.analysis().assists(FileRange { file_id, range })?.into_iter() { + let title = assist.label.clone(); let edit = assist.change.try_conv_with(&world)?; + let alternative_edits = assist + .alternative_changes + .into_iter() + .map(|change| change.try_conv_with(&world)) + .collect::>>()?; let command = Command { title, command: "rust-analyzer.applySourceChange".to_string(), - arguments: Some(vec![to_value(edit).unwrap()]), + arguments: Some(vec![to_value(edit).unwrap(), to_value(alternative_edits).unwrap()]), }; let action = CodeAction { title: command.title.clone(), diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts index 9a1697dcbe6b..0ff708b1fed7 100644 --- a/editors/code/src/commands/index.ts +++ b/editors/code/src/commands/index.ts @@ -34,8 +34,8 @@ function showReferences(ctx: Ctx): Cmd { } function applySourceChange(ctx: Ctx): Cmd { - return async (change: sourceChange.SourceChange) => { - sourceChange.applySourceChange(ctx, change); + return async (change: sourceChange.SourceChange, alternativeChanges: sourceChange.SourceChange[] | undefined) => { + sourceChange.applySourceChange(ctx, change, alternativeChanges); }; } diff --git a/editors/code/src/source_change.ts b/editors/code/src/source_change.ts index a336269baa8b..b19d325d5da3 100644 --- a/editors/code/src/source_change.ts +++ b/editors/code/src/source_change.ts @@ -9,7 +9,7 @@ export interface SourceChange { cursorPosition?: lc.TextDocumentPositionParams; } -export async function applySourceChange(ctx: Ctx, change: SourceChange) { +async function applySelectedSourceChange(ctx: Ctx, change: SourceChange) { const client = ctx.client; if (!client) return; @@ -55,3 +55,13 @@ export async function applySourceChange(ctx: Ctx, change: SourceChange) { ); } } + +export async function applySourceChange(ctx: Ctx, change: SourceChange, alternativeChanges: SourceChange[] | undefined) { + if (alternativeChanges !== undefined && alternativeChanges.length > 0) { + const selectedChange = await vscode.window.showQuickPick([change, ...alternativeChanges]); + if (!selectedChange) return; + await applySelectedSourceChange(ctx, selectedChange); + } else { + await applySelectedSourceChange(ctx, change); + } +} From 78a21253b494e573885ac8336bff6e93b401046f Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sun, 12 Jan 2020 00:40:36 +0200 Subject: [PATCH 2/4] Apply the api design suggestions --- Cargo.lock | 1 + crates/ra_assists/src/assist_ctx.rs | 25 +++++++----- crates/ra_assists/src/doc_tests.rs | 8 ++-- crates/ra_assists/src/lib.rs | 40 +++++++++++++------ crates/ra_ide/src/assists.rs | 23 +++++++---- crates/ra_lsp_server/Cargo.toml | 1 + .../ra_lsp_server/src/main_loop/handlers.rs | 28 ++++++++----- editors/code/src/commands/index.ts | 17 +++++++- editors/code/src/main.ts | 1 + editors/code/src/source_change.ts | 12 +----- 10 files changed, 97 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a0444e97ab8f..2e7698b59ca3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1067,6 +1067,7 @@ version = "0.1.0" dependencies = [ "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "jod-thread 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "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 879216a3669a..e13f969c787b 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs @@ -1,5 +1,6 @@ //! This module defines `AssistCtx` -- the API surface that is exposed to assists. use hir::{db::HirDatabase, InFile, SourceAnalyzer}; +use itertools::Either; use ra_db::FileRange; use ra_fmt::{leading_indent, reindent}; use ra_syntax::{ @@ -9,12 +10,12 @@ use ra_syntax::{ }; use ra_text_edit::TextEditBuilder; -use crate::{AssistAction, AssistId, AssistLabel}; +use crate::{AssistAction, AssistId, AssistLabel, ResolvedAssist}; #[derive(Clone, Debug)] pub(crate) enum Assist { Unresolved { label: AssistLabel }, - Resolved { label: AssistLabel, action: AssistAction, alternative_actions: Vec }, + Resolved { assist: ResolvedAssist }, } /// `AssistCtx` allows to apply an assist or check if it could be applied. @@ -92,7 +93,7 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> { f(&mut edit); edit.build() }; - Assist::Resolved { label, action, alternative_actions: Vec::default() } + Assist::Resolved { assist: ResolvedAssist { label, action_data: Either::Left(action) } } } else { Assist::Unresolved { label } }; @@ -105,18 +106,20 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> { self, id: AssistId, label: impl Into, - f: impl FnOnce() -> (ActionBuilder, Vec), + f: impl FnOnce() -> Vec, ) -> Option { let label = AssistLabel { label: label.into(), id }; let assist = if self.should_compute_edit { - let (action, alternative_actions) = f(); + let actions = f(); + assert!(!actions.is_empty(), "Assist cannot have no"); + Assist::Resolved { - label, - action: action.build(), - alternative_actions: alternative_actions - .into_iter() - .map(ActionBuilder::build) - .collect(), + assist: ResolvedAssist { + label, + action_data: Either::Right( + actions.into_iter().map(ActionBuilder::build).collect(), + ), + }, } } else { Assist::Unresolved { label } diff --git a/crates/ra_assists/src/doc_tests.rs b/crates/ra_assists/src/doc_tests.rs index 21bee228dadc..5dc1ee23374f 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) { let (db, file_id) = TestDB::with_single_file(&before); let frange = FileRange { file_id, range: selection.into() }; - let (_assist_id, action, _) = crate::assists(&db, frange) + let assist = crate::assists(&db, frange) .into_iter() - .find(|(id, _, _)| id.id.0 == assist_id) + .find(|assist| assist.label.id.0 == assist_id) .unwrap_or_else(|| { panic!( "\n\nAssist is not applicable: {}\nAvailable assists: {}", assist_id, crate::assists(&db, frange) .into_iter() - .map(|(id, _, _)| id.id.0) + .map(|assist| assist.label.id.0) .collect::>() .join(", ") ) }); - let actual = action.edit.apply(&before); + let actual = assist.get_first_action().edit.apply(&before); assert_eq_text!(after, &actual); } diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 95a530821ec6..a2983ae87c7a 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -14,6 +14,7 @@ mod test_db; pub mod ast_transform; use hir::db::HirDatabase; +use itertools::Either; use ra_db::FileRange; use ra_syntax::{TextRange, TextUnit}; use ra_text_edit::TextEdit; @@ -41,6 +42,21 @@ pub struct AssistAction { pub target: Option, } +#[derive(Debug, Clone)] +pub struct ResolvedAssist { + pub label: AssistLabel, + pub action_data: Either>, +} + +impl ResolvedAssist { + pub fn get_first_action(&self) -> AssistAction { + match &self.action_data { + Either::Left(action) => action.clone(), + Either::Right(actions) => actions[0].clone(), + } + } +} + /// Return all the assists applicable at the given position. /// /// Assists are returned in the "unresolved" state, that is only labels are @@ -65,7 +81,7 @@ where /// /// Assists are returned in the "resolved" state, that is with edit fully /// computed. -pub fn assists(db: &H, range: FileRange) -> Vec<(AssistLabel, AssistAction, Vec)> +pub fn assists(db: &H, range: FileRange) -> Vec where H: HirDatabase + 'static, { @@ -76,13 +92,11 @@ where .iter() .filter_map(|f| f(ctx.clone())) .map(|a| match a { - Assist::Resolved { label, action, alternative_actions } => { - (label, action, alternative_actions) - } + Assist::Resolved { assist } => assist, Assist::Unresolved { .. } => unreachable!(), }) .collect::>(); - a.sort_by(|a, b| match (a.1.target, b.1.target) { + a.sort_by(|a, b| match (a.get_first_action().target, b.get_first_action().target) { (Some(a), Some(b)) => a.len().cmp(&b.len()), (Some(_), None) => Ordering::Less, (None, Some(_)) => Ordering::Greater, @@ -177,7 +191,7 @@ mod helpers { AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); let action = match assist { Assist::Unresolved { .. } => unreachable!(), - Assist::Resolved { action, .. } => action, + Assist::Resolved { assist } => assist.get_first_action(), }; let actual = action.edit.apply(&before); @@ -204,7 +218,7 @@ mod helpers { AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); let action = match assist { Assist::Unresolved { .. } => unreachable!(), - Assist::Resolved { action, .. } => action, + Assist::Resolved { assist } => assist.get_first_action(), }; let mut actual = action.edit.apply(&before); @@ -227,7 +241,7 @@ mod helpers { AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); let action = match assist { Assist::Unresolved { .. } => unreachable!(), - Assist::Resolved { action, .. } => action, + Assist::Resolved { assist } => assist.get_first_action(), }; let range = action.target.expect("expected target on action"); @@ -246,7 +260,7 @@ mod helpers { AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); let action = match assist { Assist::Unresolved { .. } => unreachable!(), - Assist::Resolved { action, .. } => action, + Assist::Resolved { assist } => assist.get_first_action(), }; let range = action.target.expect("expected target on action"); @@ -296,10 +310,10 @@ mod tests { let mut assists = assists.iter(); assert_eq!( - assists.next().expect("expected assist").0.label, + assists.next().expect("expected assist").label.label, "Change visibility to pub(crate)" ); - assert_eq!(assists.next().expect("expected assist").0.label, "Add `#[derive]`"); + assert_eq!(assists.next().expect("expected assist").label.label, "Add `#[derive]`"); } #[test] @@ -318,7 +332,7 @@ mod tests { let assists = super::assists(&db, frange); let mut assists = assists.iter(); - assert_eq!(assists.next().expect("expected assist").0.label, "Extract into variable"); - assert_eq!(assists.next().expect("expected assist").0.label, "Replace with match"); + assert_eq!(assists.next().expect("expected assist").label.label, "Extract into variable"); + assert_eq!(assists.next().expect("expected assist").label.label, "Replace with match"); } } diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs index db6e4e8b704e..e30eee5f49ff 100644 --- a/crates/ra_ide/src/assists.rs +++ b/crates/ra_ide/src/assists.rs @@ -4,30 +4,37 @@ use ra_db::{FilePosition, FileRange}; use crate::{db::RootDatabase, FileId, SourceChange, SourceFileEdit}; +use itertools::Either; pub use ra_assists::AssistId; use ra_assists::{AssistAction, AssistLabel}; #[derive(Debug)] pub struct Assist { pub id: AssistId, - pub change: SourceChange, pub label: String, - pub alternative_changes: Vec, + pub change_data: Either>, } pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec { ra_assists::assists(db, frange) .into_iter() - .map(|(assist_label, action, alternative_actions)| { + .map(|assist| { let file_id = frange.file_id; + let assist_label = &assist.label; Assist { id: assist_label.id, label: assist_label.label.clone(), - change: action_to_edit(action, file_id, &assist_label), - alternative_changes: alternative_actions - .into_iter() - .map(|action| action_to_edit(action, file_id, &assist_label)) - .collect(), + change_data: match assist.action_data { + Either::Left(action) => { + Either::Left(action_to_edit(action, file_id, assist_label)) + } + Either::Right(actions) => Either::Right( + actions + .into_iter() + .map(|action| action_to_edit(action, file_id, assist_label)) + .collect(), + ), + }, } }) .collect() diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml index c08e67b8e658..73bce8b08683 100644 --- a/crates/ra_lsp_server/Cargo.toml +++ b/crates/ra_lsp_server/Cargo.toml @@ -28,6 +28,7 @@ ra_prof = { path = "../ra_prof" } ra_vfs_glob = { path = "../ra_vfs_glob" } env_logger = { version = "0.7.1", default-features = false, features = ["humantime"] } ra_cargo_watch = { path = "../ra_cargo_watch" } +itertools = "0.8" [dev-dependencies] tempfile = "3" diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index ec3c0a557ae7..d76639474276 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 @@ use std::{fmt::Write as _, io::Write as _}; +use itertools::Either; use lsp_server::ErrorCode; use lsp_types::{ CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, @@ -698,18 +699,25 @@ pub fn handle_code_action( for assist in world.analysis().assists(FileRange { file_id, range })?.into_iter() { let title = assist.label.clone(); - let edit = assist.change.try_conv_with(&world)?; - let alternative_edits = assist - .alternative_changes - .into_iter() - .map(|change| change.try_conv_with(&world)) - .collect::>>()?; - let command = Command { - title, - command: "rust-analyzer.applySourceChange".to_string(), - arguments: Some(vec![to_value(edit).unwrap(), to_value(alternative_edits).unwrap()]), + let command = match assist.change_data { + Either::Left(change) => Command { + title, + command: "rust-analyzer.applySourceChange".to_string(), + arguments: Some(vec![to_value(change.try_conv_with(&world)?)?]), + }, + Either::Right(changes) => Command { + title, + command: "rust-analyzer.selectAndApplySourceChange".to_string(), + arguments: Some(vec![to_value( + changes + .into_iter() + .map(|change| change.try_conv_with(&world)) + .collect::>>()?, + )?]), + }, }; + let action = CodeAction { title: command.title.clone(), kind: match assist.id { diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts index 0ff708b1fed7..dc075aa8289c 100644 --- a/editors/code/src/commands/index.ts +++ b/editors/code/src/commands/index.ts @@ -34,8 +34,20 @@ function showReferences(ctx: Ctx): Cmd { } function applySourceChange(ctx: Ctx): Cmd { - return async (change: sourceChange.SourceChange, alternativeChanges: sourceChange.SourceChange[] | undefined) => { - sourceChange.applySourceChange(ctx, change, alternativeChanges); + return async (change: sourceChange.SourceChange) => { + sourceChange.applySourceChange(ctx, change); + }; +} + +function selectAndApplySourceChange(ctx: Ctx): Cmd { + return async (changes: sourceChange.SourceChange[]) => { + if (changes.length === 1) { + await sourceChange.applySourceChange(ctx, changes[0]); + } else if (changes.length > 0) { + const selectedChange = await vscode.window.showQuickPick(changes); + if (!selectedChange) return; + await sourceChange.applySourceChange(ctx, selectedChange); + } }; } @@ -59,5 +71,6 @@ export { runSingle, showReferences, applySourceChange, + selectAndApplySourceChange, reload }; diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 430ad31b4c37..0494ccf63f21 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -26,6 +26,7 @@ export async function activate(context: vscode.ExtensionContext) { ctx.registerCommand('runSingle', commands.runSingle); ctx.registerCommand('showReferences', commands.showReferences); ctx.registerCommand('applySourceChange', commands.applySourceChange); + ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange); if (ctx.config.enableEnhancedTyping) { ctx.overrideCommand('type', commands.onEnter); diff --git a/editors/code/src/source_change.ts b/editors/code/src/source_change.ts index b19d325d5da3..a336269baa8b 100644 --- a/editors/code/src/source_change.ts +++ b/editors/code/src/source_change.ts @@ -9,7 +9,7 @@ export interface SourceChange { cursorPosition?: lc.TextDocumentPositionParams; } -async function applySelectedSourceChange(ctx: Ctx, change: SourceChange) { +export async function applySourceChange(ctx: Ctx, change: SourceChange) { const client = ctx.client; if (!client) return; @@ -55,13 +55,3 @@ async function applySelectedSourceChange(ctx: Ctx, change: SourceChange) { ); } } - -export async function applySourceChange(ctx: Ctx, change: SourceChange, alternativeChanges: SourceChange[] | undefined) { - if (alternativeChanges !== undefined && alternativeChanges.length > 0) { - const selectedChange = await vscode.window.showQuickPick([change, ...alternativeChanges]); - if (!selectedChange) return; - await applySelectedSourceChange(ctx, selectedChange); - } else { - await applySelectedSourceChange(ctx, change); - } -} From d51cf7794d110b064fd0e8d53726b4608ec50d1a Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Wed, 15 Jan 2020 20:20:20 +0200 Subject: [PATCH 3/4] itertools::Either -> either::Either --- Cargo.lock | 4 ++-- crates/ra_assists/Cargo.toml | 2 +- crates/ra_assists/src/assist_ctx.rs | 2 +- crates/ra_assists/src/lib.rs | 2 +- crates/ra_ide/src/assists.rs | 2 +- crates/ra_lsp_server/Cargo.toml | 2 +- crates/ra_lsp_server/src/main_loop/handlers.rs | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e7698b59ca3..45c4de2b6fc6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -869,8 +869,8 @@ version = "0.1.0" name = "ra_assists" version = "0.1.0" dependencies = [ + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "format-buf 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "join_to_string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "ra_db 0.1.0", "ra_fmt 0.1.0", @@ -1066,8 +1066,8 @@ name = "ra_lsp_server" version = "0.1.0" dependencies = [ "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "jod-thread 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-server 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/crates/ra_assists/Cargo.toml b/crates/ra_assists/Cargo.toml index 50be8d9bc282..0d2109e4ed61 100644 --- a/crates/ra_assists/Cargo.toml +++ b/crates/ra_assists/Cargo.toml @@ -11,7 +11,7 @@ doctest = false format-buf = "1.0.0" join_to_string = "0.1.3" rustc-hash = "1.0" -itertools = "0.8.0" +either = "1.5" ra_syntax = { path = "../ra_syntax" } ra_text_edit = { path = "../ra_text_edit" } diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index e13f969c787b..621b9feb2f87 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs @@ -1,6 +1,6 @@ //! This module defines `AssistCtx` -- the API surface that is exposed to assists. +use either::Either; use hir::{db::HirDatabase, InFile, SourceAnalyzer}; -use itertools::Either; use ra_db::FileRange; use ra_fmt::{leading_indent, reindent}; use ra_syntax::{ diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index a2983ae87c7a..d45b589667a9 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -13,8 +13,8 @@ mod doc_tests; mod test_db; pub mod ast_transform; +use either::Either; use hir::db::HirDatabase; -use itertools::Either; use ra_db::FileRange; use ra_syntax::{TextRange, TextUnit}; use ra_text_edit::TextEdit; diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs index e30eee5f49ff..a936900da3ee 100644 --- a/crates/ra_ide/src/assists.rs +++ b/crates/ra_ide/src/assists.rs @@ -4,7 +4,7 @@ use ra_db::{FilePosition, FileRange}; use crate::{db::RootDatabase, FileId, SourceChange, SourceFileEdit}; -use itertools::Either; +use either::Either; pub use ra_assists::AssistId; use ra_assists::{AssistAction, AssistLabel}; diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml index 73bce8b08683..579158780b3c 100644 --- a/crates/ra_lsp_server/Cargo.toml +++ b/crates/ra_lsp_server/Cargo.toml @@ -28,7 +28,7 @@ ra_prof = { path = "../ra_prof" } ra_vfs_glob = { path = "../ra_vfs_glob" } env_logger = { version = "0.7.1", default-features = false, features = ["humantime"] } ra_cargo_watch = { path = "../ra_cargo_watch" } -itertools = "0.8" +either = "1.5" [dev-dependencies] tempfile = "3" diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index d76639474276..9e996488081e 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs @@ -3,7 +3,7 @@ use std::{fmt::Write as _, io::Write as _}; -use itertools::Either; +use either::Either; use lsp_server::ErrorCode; use lsp_types::{ CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, From 79b77403b65877e4d20bbbac6dd853a3beead445 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Wed, 15 Jan 2020 20:21:05 +0200 Subject: [PATCH 4/4] Reduce visibility --- crates/ra_assists/src/assist_ctx.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index 621b9feb2f87..9d533fa0c162 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs @@ -166,7 +166,7 @@ pub(crate) struct ActionBuilder { impl ActionBuilder { #[allow(dead_code)] /// Adds a custom label to the action, if it needs to be different from the assist label - pub fn label(&mut self, label: impl Into) { + pub(crate) fn label(&mut self, label: impl Into) { self.label = Some(label.into()) }