Assign IDs to assists

This commit is contained in:
Aleksey Kladov 2019-02-24 13:53:35 +03:00
parent f6f160391d
commit ef442b8682
16 changed files with 102 additions and 43 deletions

View file

@ -5,12 +5,12 @@ use ra_syntax::{
TextUnit,
};
use crate::{AssistCtx, Assist};
use crate::{AssistCtx, Assist, AssistId};
pub(crate) fn add_derive(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
let nominal = ctx.node_at_offset::<ast::NominalDef>()?;
let node_start = derive_insertion_offset(nominal)?;
ctx.add_action("add `#[derive]`", |edit| {
ctx.add_action(AssistId("add_derive"), "add `#[derive]`", |edit| {
let derive_attr = nominal
.attrs()
.filter_map(|x| x.as_call())

View file

@ -5,12 +5,12 @@ use ra_syntax::{
TextUnit,
};
use crate::{AssistCtx, Assist};
use crate::{AssistCtx, Assist, AssistId};
pub(crate) fn add_impl(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
let nominal = ctx.node_at_offset::<ast::NominalDef>()?;
let name = nominal.name()?;
ctx.add_action("add impl", |edit| {
ctx.add_action(AssistId("add_impl"), "add impl", |edit| {
edit.target(nominal.syntax().range());
let type_params = nominal.type_param_list();
let start_offset = nominal.syntax().range().end();

View file

@ -7,7 +7,7 @@ use ra_syntax::{
};
use ra_fmt::{leading_indent, reindent};
use crate::{AssistLabel, AssistAction};
use crate::{AssistLabel, AssistAction, AssistId};
#[derive(Clone, Debug)]
pub(crate) enum Assist {
@ -81,10 +81,11 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> {
pub(crate) fn add_action(
&mut self,
id: AssistId,
label: impl Into<String>,
f: impl FnOnce(&mut AssistBuilder),
) -> &mut Self {
let label = AssistLabel { label: label.into() };
let label = AssistLabel { label: label.into(), id };
match &mut self.assist {
Assist::Unresolved(labels) => labels.push(label),
Assist::Resolved(labels_actions) => {

View file

@ -4,7 +4,10 @@ use ra_syntax::{
ast::{ self, NameOwner }, AstNode, SyntaxNode, Direction, TextRange,
SyntaxKind::{ PATH, PATH_SEGMENT, COLONCOLON, COMMA }
};
use crate::assist_ctx::{AssistCtx, Assist, AssistBuilder};
use crate::{
AssistId,
assist_ctx::{AssistCtx, Assist, AssistBuilder},
};
fn collect_path_segments(path: &ast::Path) -> Option<Vec<&ast::PathSegment>> {
let mut v = Vec::new();
@ -526,6 +529,7 @@ pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
if let Some(module) = path.syntax().ancestors().find_map(ast::Module::cast) {
if let (Some(item_list), Some(name)) = (module.item_list(), module.name()) {
ctx.add_action(
AssistId("auto_import"),
format!("import {} in mod {}", fmt_segments(&segments), name.text()),
|edit| {
apply_auto_import(item_list.syntax(), path, &segments, edit);
@ -534,9 +538,13 @@ pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
}
} else {
let current_file = node.ancestors().find_map(ast::SourceFile::cast)?;
ctx.add_action(format!("import {} in the current file", fmt_segments(&segments)), |edit| {
apply_auto_import(current_file.syntax(), path, &segments, edit);
});
ctx.add_action(
AssistId("auto_import"),
format!("import {} in the current file", fmt_segments(&segments)),
|edit| {
apply_auto_import(current_file.syntax(), path, &segments, edit);
},
);
}
ctx.build()

View file

@ -5,7 +5,7 @@ use ra_syntax::{
SyntaxKind::{VISIBILITY, FN_KW, MOD_KW, STRUCT_KW, ENUM_KW, TRAIT_KW, FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF, IDENT, WHITESPACE, COMMENT, ATTR},
};
use crate::{AssistCtx, Assist};
use crate::{AssistCtx, Assist, AssistId};
pub(crate) fn change_visibility(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
if let Some(vis) = ctx.node_at_offset::<ast::Visibility>() {
@ -41,7 +41,7 @@ fn add_vis(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
(vis_offset(field.syntax()), ident.range())
};
ctx.add_action("make pub(crate)", |edit| {
ctx.add_action(AssistId("change_visibility"), "make pub(crate)", |edit| {
edit.target(target);
edit.insert(offset, "pub(crate) ");
edit.set_cursor(offset);
@ -63,7 +63,7 @@ fn vis_offset(node: &SyntaxNode) -> TextUnit {
fn change_vis(mut ctx: AssistCtx<impl HirDatabase>, vis: &ast::Visibility) -> Option<Assist> {
if vis.syntax().text() == "pub" {
ctx.add_action("change to pub(crate)", |edit| {
ctx.add_action(AssistId("change_visibility"), "change to pub(crate)", |edit| {
edit.target(vis.syntax().range());
edit.replace(vis.syntax().range(), "pub(crate)");
edit.set_cursor(vis.syntax().range().start())
@ -72,7 +72,7 @@ fn change_vis(mut ctx: AssistCtx<impl HirDatabase>, vis: &ast::Visibility) -> Op
return ctx.build();
}
if vis.syntax().text() == "pub(crate)" {
ctx.add_action("change to pub", |edit| {
ctx.add_action(AssistId("change_visibility"), "change to pub", |edit| {
edit.target(vis.syntax().range());
edit.replace(vis.syntax().range(), "pub");
edit.set_cursor(vis.syntax().range().start());

View file

@ -6,7 +6,7 @@ use hir::{
};
use ra_syntax::ast::{self, AstNode};
use crate::{AssistCtx, Assist};
use crate::{AssistCtx, Assist, AssistId};
pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
let match_expr = ctx.node_at_offset::<ast::MatchExpr>()?;
@ -37,7 +37,7 @@ pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As
let enum_name = enum_def.name(ctx.db)?;
let db = ctx.db;
ctx.add_action("fill match arms", |edit| {
ctx.add_action(AssistId("fill_match_arms"), "fill match arms", |edit| {
let mut buf = format!("match {} {{\n", expr.syntax().text().to_string());
let variants = enum_def.variants(db);
for variant in variants {

View file

@ -5,13 +5,13 @@ use ra_syntax::{
algo::non_trivia_sibling,
};
use crate::{AssistCtx, Assist};
use crate::{AssistCtx, Assist, AssistId};
pub(crate) fn flip_comma(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
let comma = ctx.leaf_at_offset().find(|leaf| leaf.kind() == COMMA)?;
let prev = non_trivia_sibling(comma, Direction::Prev)?;
let next = non_trivia_sibling(comma, Direction::Next)?;
ctx.add_action("flip comma", |edit| {
ctx.add_action(AssistId("flip_comma"), "flip comma", |edit| {
edit.target(comma.range());
edit.replace(prev.range(), next.text());
edit.replace(next.range(), prev.text());

View file

@ -6,7 +6,7 @@ use ra_syntax::{
}, SyntaxNode, TextUnit,
};
use crate::{AssistCtx, Assist};
use crate::{AssistCtx, Assist, AssistId};
pub(crate) fn introduce_variable(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
let node = ctx.covering_node();
@ -19,7 +19,7 @@ pub(crate) fn introduce_variable(mut ctx: AssistCtx<impl HirDatabase>) -> Option
if indent.kind() != WHITESPACE {
return None;
}
ctx.add_action("introduce variable", move |edit| {
ctx.add_action(AssistId("introduce_variable"), "introduce variable", move |edit| {
let mut buf = String::new();
let cursor_offset = if wrap_in_block {

View file

@ -16,10 +16,16 @@ use hir::db::HirDatabase;
pub(crate) use crate::assist_ctx::{AssistCtx, Assist};
/// Unique identifier of the assist, should not be shown to the user
/// directly.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AssistId(pub &'static str);
#[derive(Debug, Clone)]
pub struct AssistLabel {
/// Short description of the assist, as shown in the UI.
pub label: String,
pub id: AssistId,
}
#[derive(Debug, Clone)]

View file

@ -6,7 +6,7 @@ use ra_syntax::{
L_PAREN, R_PAREN, L_CURLY, R_CURLY, L_BRACK, R_BRACK, EXCL
},
};
use crate::{AssistCtx, Assist};
use crate::{AssistCtx, Assist, AssistId};
pub(crate) fn remove_dbg(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
let macro_call = ctx.node_at_offset::<ast::MacroCall>()?;
@ -46,7 +46,7 @@ pub(crate) fn remove_dbg(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist>
macro_args.text().slice(start..end).to_string()
};
ctx.add_action("remove dbg!()", |edit| {
ctx.add_action(AssistId("remove_dbg"), "remove dbg!()", |edit| {
edit.target(macro_call.syntax().range());
edit.replace(macro_range, macro_content);
edit.set_cursor(cursor_pos);

View file

@ -2,7 +2,7 @@ use ra_syntax::{AstNode, ast};
use ra_fmt::extract_trivial_expression;
use hir::db::HirDatabase;
use crate::{AssistCtx, Assist};
use crate::{AssistCtx, Assist, AssistId};
pub(crate) fn replace_if_let_with_match(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
let if_expr: &ast::IfExpr = ctx.node_at_offset()?;
@ -15,7 +15,7 @@ pub(crate) fn replace_if_let_with_match(mut ctx: AssistCtx<impl HirDatabase>) ->
ast::ElseBranchFlavor::IfExpr(_) => return None,
};
ctx.add_action("replace with match", |edit| {
ctx.add_action(AssistId("replace_if_let_with_match"), "replace with match", |edit| {
let match_expr = build_match_expr(expr, pat, then_block, else_block);
edit.target(if_expr.syntax().range());
edit.replace_node_and_indent(if_expr.syntax(), match_expr);

View file

@ -5,7 +5,7 @@ use ra_syntax::{
algo::generate,
};
use crate::{AssistCtx, Assist};
use crate::{AssistCtx, Assist, AssistId};
pub(crate) fn split_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
let colon_colon = ctx.leaf_at_offset().find(|leaf| leaf.kind() == COLONCOLON)?;
@ -23,7 +23,7 @@ pub(crate) fn split_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assis
None => top_path.syntax().range().end(),
};
ctx.add_action("split import", |edit| {
ctx.add_action(AssistId("split_import"), "split import", |edit| {
edit.target(colon_colon.range());
edit.insert(l_curly, "{");
edit.insert(r_curly, "}");

View file

@ -2,20 +2,30 @@ use ra_db::{FileRange, FilePosition};
use crate::{SourceFileEdit, SourceChange, db::RootDatabase};
pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<SourceChange> {
pub use ra_assists::AssistId;
#[derive(Debug)]
pub struct Assist {
pub id: AssistId,
pub change: SourceChange,
}
pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> {
ra_assists::assists(db, frange)
.into_iter()
.map(|(label, action)| {
let file_id = frange.file_id;
let file_edit = SourceFileEdit { file_id, edit: action.edit };
SourceChange {
let id = label.id;
let change = SourceChange {
label: label.label,
source_file_edits: vec![file_edit],
file_system_edits: vec![],
cursor_position: action
.cursor_position
.map(|offset| FilePosition { offset, file_id }),
}
};
Assist { id, change }
})
.collect()
}

View file

@ -57,6 +57,7 @@ pub use crate::{
runnables::{Runnable, RunnableKind},
navigation_target::NavigationTarget,
references::ReferenceSearchResult,
assists::{Assist, AssistId},
};
pub use ra_ide_api_light::{
Fold, FoldKind, HighlightedRange, Severity, StructureNode, LocalEdit,
@ -368,7 +369,7 @@ impl Analysis {
/// Computes assists (aks code actons aka intentions) for the given
/// position.
pub fn assists(&self, frange: FileRange) -> Cancelable<Vec<SourceChange>> {
pub fn assists(&self, frange: FileRange) -> Cancelable<Vec<Assist>> {
self.with_db(|db| assists::assists(db, frange))
}

View file

@ -1,6 +1,6 @@
use gen_lsp_server::ErrorCode;
use lsp_types::{
CodeActionResponse, CodeLens, Command, Diagnostic, DiagnosticSeverity,
CodeActionResponse, CodeLens, Command, Diagnostic, DiagnosticSeverity, CodeAction,
DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange,
FoldingRangeKind, FoldingRangeParams, Hover, HoverContents, Location, MarkupContent,
MarkupKind, ParameterInformation, ParameterLabel, Position, PrepareRenameResponse, Range,
@ -9,6 +9,7 @@ use lsp_types::{
};
use ra_ide_api::{
FileId, FilePosition, FileRange, FoldKind, Query, RangeInfo, RunnableKind, Severity, Cancelable,
AssistId,
};
use ra_syntax::{AstNode, SyntaxKind, TextUnit};
use rustc_hash::FxHashMap;
@ -576,28 +577,57 @@ pub fn handle_code_action(
let range = params.range.conv_with(&line_index);
let assists = world.analysis().assists(FileRange { file_id, range })?.into_iter();
let fixes = world
.analysis()
.diagnostics(file_id)?
let diagnostics = world.analysis().diagnostics(file_id)?;
let mut res: Vec<CodeAction> = Vec::new();
let fixes_from_diagnostics = diagnostics
.into_iter()
.filter_map(|d| Some((d.range, d.fix?)))
.filter(|(diag_range, _fix)| diag_range.intersection(&range).is_some())
.map(|(_range, fix)| fix);
let mut res = Vec::new();
for source_edit in assists.chain(fixes) {
for source_edit in fixes_from_diagnostics {
let title = source_edit.label.clone();
let edit = source_edit.try_conv_with(&world)?;
let cmd = Command {
let command = Command {
title,
command: "rust-analyzer.applySourceChange".to_string(),
arguments: Some(vec![to_value(edit).unwrap()]),
};
res.push(cmd);
let action = CodeAction {
title: command.title.clone(),
kind: None,
diagnostics: None,
edit: None,
command: Some(command),
};
res.push(action);
}
Ok(Some(CodeActionResponse::Commands(res)))
for assist in assists {
let title = assist.change.label.clone();
let edit = assist.change.try_conv_with(&world)?;
let command = Command {
title,
command: "rust-analyzer.applySourceChange".to_string(),
arguments: Some(vec![to_value(edit).unwrap()]),
};
let action = CodeAction {
title: command.title.clone(),
kind: match assist.id {
AssistId("introduce_variable") => Some("refactor.extract.variable".to_string()),
_ => None,
},
diagnostics: None,
edit: None,
command: Some(command),
};
res.push(action);
}
Ok(Some(CodeActionResponse::Actions(res)))
}
pub fn handle_code_lens(

View file

@ -225,10 +225,12 @@ fn main() {}
context: empty_context(),
},
json!([
{
{
"command": {
"arguments": [
{
"cursorPosition": null,
"label": "create module",
"workspaceEdit": {
"documentChanges": [
{
@ -236,13 +238,14 @@ fn main() {}
"uri": "file:///[..]/src/bar.rs"
}
]
},
"label": "create module"
}
}
],
"command": "rust-analyzer.applySourceChange",
"title": "create module"
}
},
"title": "create module"
}
]),
);