Skip to content

Commit

Permalink
Apply the api design suggestions
Browse files Browse the repository at this point in the history
  • Loading branch information
SomeoneToIgnore committed Jan 12, 2020
1 parent e2b5449 commit 7c6a71b
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 59 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 14 additions & 11 deletions crates/ra_assists/src/assist_ctx.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand All @@ -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<AssistAction> },
Resolved { assist: ResolvedAssist },
}

/// `AssistCtx` allows to apply an assist or check if it could be applied.
Expand Down Expand Up @@ -90,7 +91,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 }
};
Expand All @@ -103,18 +104,20 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> {
self,
id: AssistId,
label: impl Into<String>,
f: impl FnOnce() -> (ActionBuilder, Vec<ActionBuilder>),
f: impl FnOnce() -> Vec<ActionBuilder>,
) -> Option<Assist> {
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 }
Expand Down
8 changes: 4 additions & 4 deletions crates/ra_assists/src/doc_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Vec<_>>()
.join(", ")
)
});

let actual = action.edit.apply(&before);
let actual = assist.get_first_action().edit.apply(&before);
assert_eq_text!(after, &actual);
}
40 changes: 27 additions & 13 deletions crates/ra_assists/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -41,6 +42,21 @@ pub struct AssistAction {
pub target: Option<TextRange>,
}

#[derive(Debug, Clone)]
pub struct ResolvedAssist {
pub label: AssistLabel,
pub action_data: Either<AssistAction, Vec<AssistAction>>,
}

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
Expand All @@ -65,7 +81,7 @@ where
///
/// Assists are returned in the "resolved" state, that is with edit fully
/// computed.
pub fn assists<H>(db: &H, range: FileRange) -> Vec<(AssistLabel, AssistAction, Vec<AssistAction>)>
pub fn assists<H>(db: &H, range: FileRange) -> Vec<ResolvedAssist>
where
H: HirDatabase + 'static,
{
Expand All @@ -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::<Vec<_>>();
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,
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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");
Expand All @@ -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");
Expand Down Expand Up @@ -295,8 +309,8 @@ mod tests {
let assists = super::assists(&db, frange);
let mut assists = assists.iter();

assert_eq!(assists.next().expect("expected assist").0.label, "make pub(crate)");
assert_eq!(assists.next().expect("expected assist").0.label, "add `#[derive]`");
assert_eq!(assists.next().expect("expected assist").label.label, "make pub(crate)");
assert_eq!(assists.next().expect("expected assist").label.label, "add `#[derive]`");
}

#[test]
Expand All @@ -315,7 +329,7 @@ mod tests {
let assists = super::assists(&db, frange);
let mut assists = assists.iter();

assert_eq!(assists.next().expect("expected assist").0.label, "introduce variable");
assert_eq!(assists.next().expect("expected assist").0.label, "replace with match");
assert_eq!(assists.next().expect("expected assist").label.label, "introduce variable");
assert_eq!(assists.next().expect("expected assist").label.label, "replace with match");
}
}
23 changes: 15 additions & 8 deletions crates/ra_ide/src/assists.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<SourceChange>,
pub change_data: Either<SourceChange, Vec<SourceChange>>,
}

pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> {
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()
Expand Down
1 change: 1 addition & 0 deletions crates/ra_lsp_server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
28 changes: 18 additions & 10 deletions crates/ra_lsp_server/src/main_loop/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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::<Result<Vec<_>>>()?;

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::<Result<Vec<_>>>()?,
)?]),
},
};

let action = CodeAction {
title: command.title.clone(),
kind: match assist.id {
Expand Down
17 changes: 15 additions & 2 deletions editors/code/src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
};
}

Expand All @@ -59,5 +71,6 @@ export {
runSingle,
showReferences,
applySourceChange,
selectAndApplySourceChange,
reload
};
1 change: 1 addition & 0 deletions editors/code/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
12 changes: 1 addition & 11 deletions editors/code/src/source_change.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
}
}

0 comments on commit 7c6a71b

Please sign in to comment.