mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-13 12:36:47 +00:00
Categorize assists
This commit is contained in:
parent
4cb8bf03c5
commit
b98c16a034
@ -19,7 +19,7 @@ use ra_text_edit::TextEditBuilder;
|
||||
|
||||
use crate::{
|
||||
assist_config::{AssistConfig, SnippetCap},
|
||||
Assist, AssistId, GroupLabel, ResolvedAssist,
|
||||
Assist, AssistId, AssistKind, GroupLabel, ResolvedAssist,
|
||||
};
|
||||
|
||||
/// `AssistContext` allows to apply an assist or check if it could be applied.
|
||||
@ -135,22 +135,24 @@ impl Assists {
|
||||
pub(crate) fn add(
|
||||
&mut self,
|
||||
id: AssistId,
|
||||
kind: AssistKind,
|
||||
label: impl Into<String>,
|
||||
target: TextRange,
|
||||
f: impl FnOnce(&mut AssistBuilder),
|
||||
) -> Option<()> {
|
||||
let label = Assist::new(id, label.into(), None, target);
|
||||
let label = Assist::new(id, kind, label.into(), None, target);
|
||||
self.add_impl(label, f)
|
||||
}
|
||||
pub(crate) fn add_group(
|
||||
&mut self,
|
||||
group: &GroupLabel,
|
||||
id: AssistId,
|
||||
kind: AssistKind,
|
||||
label: impl Into<String>,
|
||||
target: TextRange,
|
||||
f: impl FnOnce(&mut AssistBuilder),
|
||||
) -> Option<()> {
|
||||
let label = Assist::new(id, label.into(), Some(group.clone()), target);
|
||||
let label = Assist::new(id, kind, label.into(), Some(group.clone()), target);
|
||||
self.add_impl(label, f)
|
||||
}
|
||||
fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> {
|
||||
|
@ -8,7 +8,7 @@ use stdx::SepBy;
|
||||
|
||||
use crate::{
|
||||
assist_context::{AssistContext, Assists},
|
||||
AssistId,
|
||||
AssistId, AssistKind,
|
||||
};
|
||||
|
||||
// Assist: add_custom_impl
|
||||
@ -52,7 +52,7 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<
|
||||
format!("Add custom impl `{}` for `{}`", trait_token.text().as_str(), annotated_name);
|
||||
|
||||
let target = attr.syntax().text_range();
|
||||
acc.add(AssistId("add_custom_impl"), label, target, |builder| {
|
||||
acc.add(AssistId("add_custom_impl"), AssistKind::Refactor, label, target, |builder| {
|
||||
let new_attr_input = input
|
||||
.syntax()
|
||||
.descendants_with_tokens()
|
||||
|
@ -4,7 +4,7 @@ use ra_syntax::{
|
||||
TextSize,
|
||||
};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// Assist: add_derive
|
||||
//
|
||||
@ -29,7 +29,7 @@ pub(crate) fn add_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
|
||||
let node_start = derive_insertion_offset(&nominal)?;
|
||||
let target = nominal.syntax().text_range();
|
||||
acc.add(AssistId("add_derive"), "Add `#[derive]`", target, |builder| {
|
||||
acc.add(AssistId("add_derive"), AssistKind::Refactor, "Add `#[derive]`", target, |builder| {
|
||||
let derive_attr = nominal
|
||||
.attrs()
|
||||
.filter_map(|x| x.as_simple_call())
|
||||
|
@ -4,7 +4,7 @@ use ra_syntax::{
|
||||
TextRange,
|
||||
};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// Assist: add_explicit_type
|
||||
//
|
||||
@ -60,6 +60,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio
|
||||
let inferred_type = ty.display_source_code(ctx.db(), module.into()).ok()?;
|
||||
acc.add(
|
||||
AssistId("add_explicit_type"),
|
||||
AssistKind::RefactorRewrite,
|
||||
format!("Insert explicit type `{}`", inferred_type),
|
||||
pat_range,
|
||||
|builder| match ascribed_ty {
|
||||
|
@ -2,7 +2,7 @@ use ra_ide_db::RootDatabase;
|
||||
use ra_syntax::ast::{self, AstNode, NameOwner};
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{utils::FamousDefs, AssistContext, AssistId, Assists};
|
||||
use crate::{utils::FamousDefs, AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// Assist: add_from_impl_for_enum
|
||||
//
|
||||
@ -46,6 +46,7 @@ pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) ->
|
||||
let target = variant.syntax().text_range();
|
||||
acc.add(
|
||||
AssistId("add_from_impl_for_enum"),
|
||||
AssistKind::Refactor,
|
||||
"Add From impl for this enum variant",
|
||||
target,
|
||||
|edit| {
|
||||
|
@ -13,7 +13,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use crate::{
|
||||
assist_config::SnippetCap,
|
||||
utils::{render_snippet, Cursor},
|
||||
AssistContext, AssistId, Assists,
|
||||
AssistContext, AssistId, AssistKind, Assists,
|
||||
};
|
||||
|
||||
// Assist: add_function
|
||||
@ -62,15 +62,21 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
||||
let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?;
|
||||
|
||||
let target = call.syntax().text_range();
|
||||
acc.add(AssistId("add_function"), "Add function", target, |builder| {
|
||||
let function_template = function_builder.render();
|
||||
builder.edit_file(function_template.file);
|
||||
let new_fn = function_template.to_string(ctx.config.snippet_cap);
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn),
|
||||
None => builder.insert(function_template.insert_offset, new_fn),
|
||||
}
|
||||
})
|
||||
acc.add(
|
||||
AssistId("add_function"),
|
||||
AssistKind::RefactorExtract,
|
||||
"Add function",
|
||||
target,
|
||||
|builder| {
|
||||
let function_template = function_builder.render();
|
||||
builder.edit_file(function_template.file);
|
||||
let new_fn = function_template.to_string(ctx.config.snippet_cap);
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn),
|
||||
None => builder.insert(function_template.insert_offset, new_fn),
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
struct FunctionTemplate {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use ra_syntax::ast::{self, AstNode, NameOwner, TypeParamsOwner};
|
||||
use stdx::{format_to, SepBy};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// Assist: add_impl
|
||||
//
|
||||
@ -26,38 +26,46 @@ pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
|
||||
let name = nominal.name()?;
|
||||
let target = nominal.syntax().text_range();
|
||||
acc.add(AssistId("add_impl"), format!("Implement {}", name.text().as_str()), target, |edit| {
|
||||
let type_params = nominal.type_param_list();
|
||||
let start_offset = nominal.syntax().text_range().end();
|
||||
let mut buf = String::new();
|
||||
buf.push_str("\n\nimpl");
|
||||
if let Some(type_params) = &type_params {
|
||||
format_to!(buf, "{}", type_params.syntax());
|
||||
}
|
||||
buf.push_str(" ");
|
||||
buf.push_str(name.text().as_str());
|
||||
if let Some(type_params) = type_params {
|
||||
let lifetime_params = type_params
|
||||
.lifetime_params()
|
||||
.filter_map(|it| it.lifetime_token())
|
||||
.map(|it| it.text().clone());
|
||||
let type_params =
|
||||
type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone());
|
||||
acc.add(
|
||||
AssistId("add_impl"),
|
||||
AssistKind::Refactor,
|
||||
format!("Implement {}", name.text().as_str()),
|
||||
target,
|
||||
|edit| {
|
||||
let type_params = nominal.type_param_list();
|
||||
let start_offset = nominal.syntax().text_range().end();
|
||||
let mut buf = String::new();
|
||||
buf.push_str("\n\nimpl");
|
||||
if let Some(type_params) = &type_params {
|
||||
format_to!(buf, "{}", type_params.syntax());
|
||||
}
|
||||
buf.push_str(" ");
|
||||
buf.push_str(name.text().as_str());
|
||||
if let Some(type_params) = type_params {
|
||||
let lifetime_params = type_params
|
||||
.lifetime_params()
|
||||
.filter_map(|it| it.lifetime_token())
|
||||
.map(|it| it.text().clone());
|
||||
let type_params = type_params
|
||||
.type_params()
|
||||
.filter_map(|it| it.name())
|
||||
.map(|it| it.text().clone());
|
||||
|
||||
let generic_params = lifetime_params.chain(type_params).sep_by(", ");
|
||||
format_to!(buf, "<{}>", generic_params)
|
||||
}
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => {
|
||||
buf.push_str(" {\n $0\n}");
|
||||
edit.insert_snippet(cap, start_offset, buf);
|
||||
let generic_params = lifetime_params.chain(type_params).sep_by(", ");
|
||||
format_to!(buf, "<{}>", generic_params)
|
||||
}
|
||||
None => {
|
||||
buf.push_str(" {\n}");
|
||||
edit.insert(start_offset, buf);
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => {
|
||||
buf.push_str(" {\n $0\n}");
|
||||
edit.insert_snippet(cap, start_offset, buf);
|
||||
}
|
||||
None => {
|
||||
buf.push_str(" {\n}");
|
||||
edit.insert(start_offset, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -12,7 +12,7 @@ use crate::{
|
||||
assist_context::{AssistContext, Assists},
|
||||
ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
|
||||
utils::{get_missing_assoc_items, render_snippet, resolve_target_trait, Cursor},
|
||||
AssistId,
|
||||
AssistId, AssistKind,
|
||||
};
|
||||
|
||||
#[derive(PartialEq)]
|
||||
@ -147,7 +147,7 @@ fn add_missing_impl_members_inner(
|
||||
}
|
||||
|
||||
let target = impl_def.syntax().text_range();
|
||||
acc.add(AssistId(assist_id), label, target, |builder| {
|
||||
acc.add(AssistId(assist_id), AssistKind::QuickFix, label, target, |builder| {
|
||||
let n_existing_items = impl_item_list.assoc_items().count();
|
||||
let source_scope = ctx.sema.scope_for_def(trait_);
|
||||
let target_scope = ctx.sema.scope(impl_item_list.syntax());
|
||||
|
@ -7,7 +7,7 @@ use ra_syntax::{
|
||||
};
|
||||
use stdx::{format_to, SepBy};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// Assist: add_new
|
||||
//
|
||||
@ -42,50 +42,56 @@ pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
let impl_def = find_struct_impl(&ctx, &strukt)?;
|
||||
|
||||
let target = strukt.syntax().text_range();
|
||||
acc.add(AssistId("add_new"), "Add default constructor", target, |builder| {
|
||||
let mut buf = String::with_capacity(512);
|
||||
acc.add(
|
||||
AssistId("add_new"),
|
||||
AssistKind::Refactor,
|
||||
"Add default constructor",
|
||||
target,
|
||||
|builder| {
|
||||
let mut buf = String::with_capacity(512);
|
||||
|
||||
if impl_def.is_some() {
|
||||
buf.push('\n');
|
||||
}
|
||||
|
||||
let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
|
||||
|
||||
let params = field_list
|
||||
.fields()
|
||||
.filter_map(|f| {
|
||||
Some(format!("{}: {}", f.name()?.syntax(), f.ascribed_type()?.syntax()))
|
||||
})
|
||||
.sep_by(", ");
|
||||
let fields = field_list.fields().filter_map(|f| f.name()).sep_by(", ");
|
||||
|
||||
format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);
|
||||
|
||||
let start_offset = impl_def
|
||||
.and_then(|impl_def| {
|
||||
if impl_def.is_some() {
|
||||
buf.push('\n');
|
||||
let start = impl_def
|
||||
.syntax()
|
||||
.descendants_with_tokens()
|
||||
.find(|t| t.kind() == T!['{'])?
|
||||
.text_range()
|
||||
.end();
|
||||
|
||||
Some(start)
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
buf = generate_impl_text(&strukt, &buf);
|
||||
strukt.syntax().text_range().end()
|
||||
});
|
||||
|
||||
match ctx.config.snippet_cap {
|
||||
None => builder.insert(start_offset, buf),
|
||||
Some(cap) => {
|
||||
buf = buf.replace("fn new", "fn $0new");
|
||||
builder.insert_snippet(cap, start_offset, buf);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
|
||||
|
||||
let params = field_list
|
||||
.fields()
|
||||
.filter_map(|f| {
|
||||
Some(format!("{}: {}", f.name()?.syntax(), f.ascribed_type()?.syntax()))
|
||||
})
|
||||
.sep_by(", ");
|
||||
let fields = field_list.fields().filter_map(|f| f.name()).sep_by(", ");
|
||||
|
||||
format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);
|
||||
|
||||
let start_offset = impl_def
|
||||
.and_then(|impl_def| {
|
||||
buf.push('\n');
|
||||
let start = impl_def
|
||||
.syntax()
|
||||
.descendants_with_tokens()
|
||||
.find(|t| t.kind() == T!['{'])?
|
||||
.text_range()
|
||||
.end();
|
||||
|
||||
Some(start)
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
buf = generate_impl_text(&strukt, &buf);
|
||||
strukt.syntax().text_range().end()
|
||||
});
|
||||
|
||||
match ctx.config.snippet_cap {
|
||||
None => builder.insert(start_offset, buf),
|
||||
Some(cap) => {
|
||||
buf = buf.replace("fn new", "fn $0new");
|
||||
builder.insert_snippet(cap, start_offset, buf);
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Generates the surrounding `impl Type { <code> }` including type and lifetime
|
||||
|
@ -4,7 +4,7 @@ use test_utils::mark;
|
||||
|
||||
use crate::{
|
||||
assist_context::{AssistContext, Assists},
|
||||
AssistId,
|
||||
AssistId, AssistKind,
|
||||
};
|
||||
|
||||
// Assist: add_turbo_fish
|
||||
@ -45,12 +45,16 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<(
|
||||
mark::hit!(add_turbo_fish_non_generic);
|
||||
return None;
|
||||
}
|
||||
acc.add(AssistId("add_turbo_fish"), "Add `::<>`", ident.text_range(), |builder| {
|
||||
match ctx.config.snippet_cap {
|
||||
acc.add(
|
||||
AssistId("add_turbo_fish"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Add `::<>`",
|
||||
ident.text_range(),
|
||||
|builder| match ctx.config.snippet_cap {
|
||||
Some(cap) => builder.insert_snippet(cap, ident.text_range().end(), "::<${0:_}>"),
|
||||
None => builder.insert(ident.text_range().end(), "::<_>"),
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,6 +1,6 @@
|
||||
use ra_syntax::ast::{self, AstNode};
|
||||
|
||||
use crate::{utils::invert_boolean_expression, AssistContext, AssistId, Assists};
|
||||
use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// Assist: apply_demorgan
|
||||
//
|
||||
@ -39,11 +39,17 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<(
|
||||
let rhs_range = rhs.syntax().text_range();
|
||||
let not_rhs = invert_boolean_expression(rhs);
|
||||
|
||||
acc.add(AssistId("apply_demorgan"), "Apply De Morgan's law", op_range, |edit| {
|
||||
edit.replace(op_range, opposite_op);
|
||||
edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text()));
|
||||
edit.replace(rhs_range, format!("{})", not_rhs.syntax().text()));
|
||||
})
|
||||
acc.add(
|
||||
AssistId("apply_demorgan"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Apply De Morgan's law",
|
||||
op_range,
|
||||
|edit| {
|
||||
edit.replace(op_range, opposite_op);
|
||||
edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text()));
|
||||
edit.replace(rhs_range, format!("{})", not_rhs.syntax().text()));
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Return the opposite text for a given logical operator, if it makes sense
|
||||
|
@ -13,7 +13,9 @@ use ra_syntax::{
|
||||
};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists, GroupLabel};
|
||||
use crate::{
|
||||
utils::insert_use_statement, AssistContext, AssistId, AssistKind, Assists, GroupLabel,
|
||||
};
|
||||
|
||||
// Assist: auto_import
|
||||
//
|
||||
@ -47,6 +49,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
||||
acc.add_group(
|
||||
&group,
|
||||
AssistId("auto_import"),
|
||||
AssistKind::QuickFix,
|
||||
format!("Import `{}`", &import),
|
||||
range,
|
||||
|builder| {
|
||||
|
@ -3,7 +3,7 @@ use ra_syntax::{
|
||||
AstNode, SyntaxNode,
|
||||
};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||
use test_utils::mark;
|
||||
|
||||
// Assist: change_return_type_to_result
|
||||
@ -36,6 +36,7 @@ pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContex
|
||||
|
||||
acc.add(
|
||||
AssistId("change_return_type_to_result"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Change return type to Result",
|
||||
type_ref.syntax().text_range(),
|
||||
|builder| {
|
||||
|
@ -6,7 +6,7 @@ use ra_syntax::{
|
||||
};
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{utils::vis_offset, AssistContext, AssistId, Assists};
|
||||
use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// Assist: change_visibility
|
||||
//
|
||||
@ -62,9 +62,15 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
return None;
|
||||
};
|
||||
|
||||
acc.add(AssistId("change_visibility"), "Change visibility to pub(crate)", target, |edit| {
|
||||
edit.insert(offset, "pub(crate) ");
|
||||
})
|
||||
acc.add(
|
||||
AssistId("change_visibility"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Change visibility to pub(crate)",
|
||||
target,
|
||||
|edit| {
|
||||
edit.insert(offset, "pub(crate) ");
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
|
||||
@ -72,6 +78,7 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
|
||||
let target = vis.syntax().text_range();
|
||||
return acc.add(
|
||||
AssistId("change_visibility"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Change Visibility to pub(crate)",
|
||||
target,
|
||||
|edit| {
|
||||
@ -83,6 +90,7 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
|
||||
let target = vis.syntax().text_range();
|
||||
return acc.add(
|
||||
AssistId("change_visibility"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Change visibility to pub",
|
||||
target,
|
||||
|edit| {
|
||||
|
@ -15,7 +15,7 @@ use ra_syntax::{
|
||||
use crate::{
|
||||
assist_context::{AssistContext, Assists},
|
||||
utils::invert_boolean_expression,
|
||||
AssistId,
|
||||
AssistId, AssistKind,
|
||||
};
|
||||
|
||||
// Assist: convert_to_guarded_return
|
||||
@ -99,86 +99,93 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
|
||||
then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?;
|
||||
|
||||
let target = if_expr.syntax().text_range();
|
||||
acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| {
|
||||
let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
|
||||
let new_block = match if_let_pat {
|
||||
None => {
|
||||
// If.
|
||||
let new_expr = {
|
||||
let then_branch =
|
||||
make::block_expr(once(make::expr_stmt(early_expression).into()), None);
|
||||
let cond = invert_boolean_expression(cond_expr);
|
||||
make::expr_if(make::condition(cond, None), then_branch).indent(if_indent_level)
|
||||
};
|
||||
replace(new_expr.syntax(), &then_block, &parent_block, &if_expr)
|
||||
}
|
||||
Some((path, bound_ident)) => {
|
||||
// If-let.
|
||||
let match_expr = {
|
||||
let happy_arm = {
|
||||
let pat = make::tuple_struct_pat(
|
||||
path,
|
||||
once(make::bind_pat(make::name("it")).into()),
|
||||
);
|
||||
let expr = {
|
||||
let name_ref = make::name_ref("it");
|
||||
let segment = make::path_segment(name_ref);
|
||||
let path = make::path_unqualified(segment);
|
||||
make::expr_path(path)
|
||||
acc.add(
|
||||
AssistId("convert_to_guarded_return"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Convert to guarded return",
|
||||
target,
|
||||
|edit| {
|
||||
let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
|
||||
let new_block = match if_let_pat {
|
||||
None => {
|
||||
// If.
|
||||
let new_expr = {
|
||||
let then_branch =
|
||||
make::block_expr(once(make::expr_stmt(early_expression).into()), None);
|
||||
let cond = invert_boolean_expression(cond_expr);
|
||||
make::expr_if(make::condition(cond, None), then_branch)
|
||||
.indent(if_indent_level)
|
||||
};
|
||||
replace(new_expr.syntax(), &then_block, &parent_block, &if_expr)
|
||||
}
|
||||
Some((path, bound_ident)) => {
|
||||
// If-let.
|
||||
let match_expr = {
|
||||
let happy_arm = {
|
||||
let pat = make::tuple_struct_pat(
|
||||
path,
|
||||
once(make::bind_pat(make::name("it")).into()),
|
||||
);
|
||||
let expr = {
|
||||
let name_ref = make::name_ref("it");
|
||||
let segment = make::path_segment(name_ref);
|
||||
let path = make::path_unqualified(segment);
|
||||
make::expr_path(path)
|
||||
};
|
||||
make::match_arm(once(pat.into()), expr)
|
||||
};
|
||||
make::match_arm(once(pat.into()), expr)
|
||||
|
||||
let sad_arm = make::match_arm(
|
||||
// FIXME: would be cool to use `None` or `Err(_)` if appropriate
|
||||
once(make::placeholder_pat().into()),
|
||||
early_expression,
|
||||
);
|
||||
|
||||
make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm]))
|
||||
};
|
||||
|
||||
let sad_arm = make::match_arm(
|
||||
// FIXME: would be cool to use `None` or `Err(_)` if appropriate
|
||||
once(make::placeholder_pat().into()),
|
||||
early_expression,
|
||||
let let_stmt = make::let_stmt(
|
||||
make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(),
|
||||
Some(match_expr),
|
||||
);
|
||||
let let_stmt = let_stmt.indent(if_indent_level);
|
||||
replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr)
|
||||
}
|
||||
};
|
||||
edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap());
|
||||
|
||||
make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm]))
|
||||
};
|
||||
|
||||
let let_stmt = make::let_stmt(
|
||||
make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(),
|
||||
Some(match_expr),
|
||||
fn replace(
|
||||
new_expr: &SyntaxNode,
|
||||
then_block: &ast::BlockExpr,
|
||||
parent_block: &ast::BlockExpr,
|
||||
if_expr: &ast::IfExpr,
|
||||
) -> SyntaxNode {
|
||||
let then_block_items = then_block.dedent(IndentLevel(1));
|
||||
let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
|
||||
let end_of_then =
|
||||
if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
|
||||
end_of_then.prev_sibling_or_token().unwrap()
|
||||
} else {
|
||||
end_of_then
|
||||
};
|
||||
let mut then_statements = new_expr.children_with_tokens().chain(
|
||||
then_block_items
|
||||
.syntax()
|
||||
.children_with_tokens()
|
||||
.skip(1)
|
||||
.take_while(|i| *i != end_of_then),
|
||||
);
|
||||
let let_stmt = let_stmt.indent(if_indent_level);
|
||||
replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr)
|
||||
replace_children(
|
||||
&parent_block.syntax(),
|
||||
RangeInclusive::new(
|
||||
if_expr.clone().syntax().clone().into(),
|
||||
if_expr.syntax().clone().into(),
|
||||
),
|
||||
&mut then_statements,
|
||||
)
|
||||
}
|
||||
};
|
||||
edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap());
|
||||
|
||||
fn replace(
|
||||
new_expr: &SyntaxNode,
|
||||
then_block: &ast::BlockExpr,
|
||||
parent_block: &ast::BlockExpr,
|
||||
if_expr: &ast::IfExpr,
|
||||
) -> SyntaxNode {
|
||||
let then_block_items = then_block.dedent(IndentLevel(1));
|
||||
let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
|
||||
let end_of_then =
|
||||
if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
|
||||
end_of_then.prev_sibling_or_token().unwrap()
|
||||
} else {
|
||||
end_of_then
|
||||
};
|
||||
let mut then_statements = new_expr.children_with_tokens().chain(
|
||||
then_block_items
|
||||
.syntax()
|
||||
.children_with_tokens()
|
||||
.skip(1)
|
||||
.take_while(|i| *i != end_of_then),
|
||||
);
|
||||
replace_children(
|
||||
&parent_block.syntax(),
|
||||
RangeInclusive::new(
|
||||
if_expr.clone().syntax().clone().into(),
|
||||
if_expr.syntax().clone().into(),
|
||||
),
|
||||
&mut then_statements,
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -10,7 +10,8 @@ use ra_syntax::{
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{
|
||||
assist_context::AssistBuilder, utils::insert_use_statement, AssistContext, AssistId, Assists,
|
||||
assist_context::AssistBuilder, utils::insert_use_statement, AssistContext, AssistId,
|
||||
AssistKind, Assists,
|
||||
};
|
||||
|
||||
// Assist: extract_struct_from_enum_variant
|
||||
@ -49,6 +50,7 @@ pub(crate) fn extract_struct_from_enum_variant(
|
||||
let target = variant.syntax().text_range();
|
||||
acc.add(
|
||||
AssistId("extract_struct_from_enum_variant"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Extract struct from enum variant",
|
||||
target,
|
||||
|builder| {
|
||||
|
@ -9,7 +9,7 @@ use ra_syntax::{
|
||||
use stdx::format_to;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// Assist: extract_variable
|
||||
//
|
||||
@ -43,80 +43,86 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
|
||||
return None;
|
||||
}
|
||||
let target = expr.syntax().text_range();
|
||||
acc.add(AssistId("extract_variable"), "Extract into variable", target, move |edit| {
|
||||
let field_shorthand = match expr.syntax().parent().and_then(ast::RecordField::cast) {
|
||||
Some(field) => field.name_ref(),
|
||||
None => None,
|
||||
};
|
||||
acc.add(
|
||||
AssistId("extract_variable"),
|
||||
AssistKind::RefactorExtract,
|
||||
"Extract into variable",
|
||||
target,
|
||||
move |edit| {
|
||||
let field_shorthand = match expr.syntax().parent().and_then(ast::RecordField::cast) {
|
||||
Some(field) => field.name_ref(),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let mut buf = String::new();
|
||||
let mut buf = String::new();
|
||||
|
||||
let var_name = match &field_shorthand {
|
||||
Some(it) => it.to_string(),
|
||||
None => "var_name".to_string(),
|
||||
};
|
||||
let expr_range = match &field_shorthand {
|
||||
Some(it) => it.syntax().text_range().cover(expr.syntax().text_range()),
|
||||
None => expr.syntax().text_range(),
|
||||
};
|
||||
let var_name = match &field_shorthand {
|
||||
Some(it) => it.to_string(),
|
||||
None => "var_name".to_string(),
|
||||
};
|
||||
let expr_range = match &field_shorthand {
|
||||
Some(it) => it.syntax().text_range().cover(expr.syntax().text_range()),
|
||||
None => expr.syntax().text_range(),
|
||||
};
|
||||
|
||||
if wrap_in_block {
|
||||
format_to!(buf, "{{ let {} = ", var_name);
|
||||
} else {
|
||||
format_to!(buf, "let {} = ", var_name);
|
||||
};
|
||||
format_to!(buf, "{}", expr.syntax());
|
||||
if wrap_in_block {
|
||||
format_to!(buf, "{{ let {} = ", var_name);
|
||||
} else {
|
||||
format_to!(buf, "let {} = ", var_name);
|
||||
};
|
||||
format_to!(buf, "{}", expr.syntax());
|
||||
|
||||
let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone());
|
||||
let is_full_stmt = if let Some(expr_stmt) = &full_stmt {
|
||||
Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone())
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if is_full_stmt {
|
||||
mark::hit!(test_extract_var_expr_stmt);
|
||||
if full_stmt.unwrap().semicolon_token().is_none() {
|
||||
buf.push_str(";");
|
||||
let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone());
|
||||
let is_full_stmt = if let Some(expr_stmt) = &full_stmt {
|
||||
Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone())
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if is_full_stmt {
|
||||
mark::hit!(test_extract_var_expr_stmt);
|
||||
if full_stmt.unwrap().semicolon_token().is_none() {
|
||||
buf.push_str(";");
|
||||
}
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => {
|
||||
let snip = buf
|
||||
.replace(&format!("let {}", var_name), &format!("let $0{}", var_name));
|
||||
edit.replace_snippet(cap, expr_range, snip)
|
||||
}
|
||||
None => edit.replace(expr_range, buf),
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
buf.push_str(";");
|
||||
|
||||
// We want to maintain the indent level,
|
||||
// but we do not want to duplicate possible
|
||||
// extra newlines in the indent block
|
||||
let text = indent.text();
|
||||
if text.starts_with('\n') {
|
||||
buf.push_str("\n");
|
||||
buf.push_str(text.trim_start_matches('\n'));
|
||||
} else {
|
||||
buf.push_str(text);
|
||||
}
|
||||
|
||||
edit.replace(expr_range, var_name.clone());
|
||||
let offset = anchor_stmt.text_range().start();
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => {
|
||||
let snip =
|
||||
buf.replace(&format!("let {}", var_name), &format!("let $0{}", var_name));
|
||||
edit.replace_snippet(cap, expr_range, snip)
|
||||
edit.insert_snippet(cap, offset, snip)
|
||||
}
|
||||
None => edit.replace(expr_range, buf),
|
||||
None => edit.insert(offset, buf),
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
buf.push_str(";");
|
||||
|
||||
// We want to maintain the indent level,
|
||||
// but we do not want to duplicate possible
|
||||
// extra newlines in the indent block
|
||||
let text = indent.text();
|
||||
if text.starts_with('\n') {
|
||||
buf.push_str("\n");
|
||||
buf.push_str(text.trim_start_matches('\n'));
|
||||
} else {
|
||||
buf.push_str(text);
|
||||
}
|
||||
|
||||
edit.replace(expr_range, var_name.clone());
|
||||
let offset = anchor_stmt.text_range().start();
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => {
|
||||
let snip =
|
||||
buf.replace(&format!("let {}", var_name), &format!("let $0{}", var_name));
|
||||
edit.insert_snippet(cap, offset, snip)
|
||||
if wrap_in_block {
|
||||
edit.insert(anchor_stmt.text_range().end(), " }");
|
||||
}
|
||||
None => edit.insert(offset, buf),
|
||||
}
|
||||
|
||||
if wrap_in_block {
|
||||
edit.insert(anchor_stmt.text_range().end(), " }");
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Check whether the node is a valid expression which can be extracted to a variable.
|
||||
|
@ -8,7 +8,7 @@ use test_utils::mark;
|
||||
|
||||
use crate::{
|
||||
utils::{render_snippet, Cursor, FamousDefs},
|
||||
AssistContext, AssistId, Assists,
|
||||
AssistContext, AssistId, AssistKind, Assists,
|
||||
};
|
||||
|
||||
// Assist: fill_match_arms
|
||||
@ -103,24 +103,30 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
|
||||
}
|
||||
|
||||
let target = match_expr.syntax().text_range();
|
||||
acc.add(AssistId("fill_match_arms"), "Fill match arms", target, |builder| {
|
||||
let new_arm_list = match_arm_list.remove_placeholder();
|
||||
let n_old_arms = new_arm_list.arms().count();
|
||||
let new_arm_list = new_arm_list.append_arms(missing_arms);
|
||||
let first_new_arm = new_arm_list.arms().nth(n_old_arms);
|
||||
let old_range = match_arm_list.syntax().text_range();
|
||||
match (first_new_arm, ctx.config.snippet_cap) {
|
||||
(Some(first_new_arm), Some(cap)) => {
|
||||
let snippet = render_snippet(
|
||||
cap,
|
||||
new_arm_list.syntax(),
|
||||
Cursor::Before(first_new_arm.syntax()),
|
||||
);
|
||||
builder.replace_snippet(cap, old_range, snippet);
|
||||
acc.add(
|
||||
AssistId("fill_match_arms"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Fill match arms",
|
||||
target,
|
||||
|builder| {
|
||||
let new_arm_list = match_arm_list.remove_placeholder();
|
||||
let n_old_arms = new_arm_list.arms().count();
|
||||
let new_arm_list = new_arm_list.append_arms(missing_arms);
|
||||
let first_new_arm = new_arm_list.arms().nth(n_old_arms);
|
||||
let old_range = match_arm_list.syntax().text_range();
|
||||
match (first_new_arm, ctx.config.snippet_cap) {
|
||||
(Some(first_new_arm), Some(cap)) => {
|
||||
let snippet = render_snippet(
|
||||
cap,
|
||||
new_arm_list.syntax(),
|
||||
Cursor::Before(first_new_arm.syntax()),
|
||||
);
|
||||
builder.replace_snippet(cap, old_range, snippet);
|
||||
}
|
||||
_ => builder.replace(old_range, new_arm_list.to_string()),
|
||||
}
|
||||
_ => builder.replace(old_range, new_arm_list.to_string()),
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool {
|
||||
|
@ -2,7 +2,7 @@ use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution};
|
||||
use ra_db::FileId;
|
||||
use ra_syntax::{ast, AstNode, TextRange, TextSize};
|
||||
|
||||
use crate::{utils::vis_offset, AssistContext, AssistId, Assists};
|
||||
use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// FIXME: this really should be a fix for diagnostic, rather than an assist.
|
||||
|
||||
@ -58,7 +58,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O
|
||||
Some(name) => format!("Change visibility of {} to {}", name, missing_visibility),
|
||||
};
|
||||
|
||||
acc.add(AssistId("fix_visibility"), assist_label, target, |builder| {
|
||||
acc.add(AssistId("fix_visibility"), AssistKind::QuickFix, assist_label, target, |builder| {
|
||||
builder.edit_file(target_file);
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
|
||||
@ -101,7 +101,7 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) ->
|
||||
let assist_label =
|
||||
format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility);
|
||||
|
||||
acc.add(AssistId("fix_visibility"), assist_label, target, |builder| {
|
||||
acc.add(AssistId("fix_visibility"), AssistKind::QuickFix, assist_label, target, |builder| {
|
||||
builder.edit_file(target_file);
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
|
||||
|
@ -1,6 +1,6 @@
|
||||
use ra_syntax::ast::{AstNode, BinExpr, BinOp};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// Assist: flip_binexpr
|
||||
//
|
||||
@ -33,13 +33,19 @@ pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
||||
return None;
|
||||
}
|
||||
|
||||
acc.add(AssistId("flip_binexpr"), "Flip binary expression", op_range, |edit| {
|
||||
if let FlipAction::FlipAndReplaceOp(new_op) = action {
|
||||
edit.replace(op_range, new_op);
|
||||
}
|
||||
edit.replace(lhs.text_range(), rhs.text());
|
||||
edit.replace(rhs.text_range(), lhs.text());
|
||||
})
|
||||
acc.add(
|
||||
AssistId("flip_binexpr"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Flip binary expression",
|
||||
op_range,
|
||||
|edit| {
|
||||
if let FlipAction::FlipAndReplaceOp(new_op) = action {
|
||||
edit.replace(op_range, new_op);
|
||||
}
|
||||
edit.replace(lhs.text_range(), rhs.text());
|
||||
edit.replace(rhs.text_range(), lhs.text());
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
enum FlipAction {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use ra_syntax::{algo::non_trivia_sibling, Direction, T};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// Assist: flip_comma
|
||||
//
|
||||
@ -28,10 +28,16 @@ pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
return None;
|
||||
}
|
||||
|
||||
acc.add(AssistId("flip_comma"), "Flip comma", comma.text_range(), |edit| {
|
||||
edit.replace(prev.text_range(), next.to_string());
|
||||
edit.replace(next.text_range(), prev.to_string());
|
||||
})
|
||||
acc.add(
|
||||
AssistId("flip_comma"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Flip comma",
|
||||
comma.text_range(),
|
||||
|edit| {
|
||||
edit.replace(prev.text_range(), next.to_string());
|
||||
edit.replace(next.text_range(), prev.to_string());
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -4,7 +4,7 @@ use ra_syntax::{
|
||||
Direction, T,
|
||||
};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// Assist: flip_trait_bound
|
||||
//
|
||||
@ -33,10 +33,16 @@ pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext) -> Option
|
||||
);
|
||||
|
||||
let target = plus.text_range();
|
||||
acc.add(AssistId("flip_trait_bound"), "Flip trait bounds", target, |edit| {
|
||||
edit.replace(before.text_range(), after.to_string());
|
||||
edit.replace(after.text_range(), before.to_string());
|
||||
})
|
||||
acc.add(
|
||||
AssistId("flip_trait_bound"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Flip trait bounds",
|
||||
target,
|
||||
|edit| {
|
||||
edit.replace(before.text_range(), after.to_string());
|
||||
edit.replace(after.text_range(), before.to_string());
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -7,7 +7,7 @@ use test_utils::mark;
|
||||
|
||||
use crate::{
|
||||
assist_context::{AssistContext, Assists},
|
||||
AssistId,
|
||||
AssistId, AssistKind,
|
||||
};
|
||||
|
||||
// Assist: inline_local_variable
|
||||
@ -110,13 +110,20 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
|
||||
let init_in_paren = format!("({})", &init_str);
|
||||
|
||||
let target = bind_pat.syntax().text_range();
|
||||
acc.add(AssistId("inline_local_variable"), "Inline variable", target, move |builder| {
|
||||
builder.delete(delete_range);
|
||||
for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) {
|
||||
let replacement = if should_wrap { init_in_paren.clone() } else { init_str.clone() };
|
||||
builder.replace(desc.file_range.range, replacement)
|
||||
}
|
||||
})
|
||||
acc.add(
|
||||
AssistId("inline_local_variable"),
|
||||
AssistKind::RefactorInline,
|
||||
"Inline variable",
|
||||
target,
|
||||
move |builder| {
|
||||
builder.delete(delete_range);
|
||||
for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) {
|
||||
let replacement =
|
||||
if should_wrap { init_in_paren.clone() } else { init_str.clone() };
|
||||
builder.replace(desc.file_range.range, replacement)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -4,7 +4,7 @@ use ra_syntax::{
|
||||
};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{assist_context::AssistBuilder, AssistContext, AssistId, Assists};
|
||||
use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
static ASSIST_NAME: &str = "introduce_named_lifetime";
|
||||
static ASSIST_LABEL: &str = "Introduce named lifetime";
|
||||
@ -83,7 +83,7 @@ fn generate_fn_def_assist(
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
acc.add(AssistId(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |builder| {
|
||||
acc.add(AssistId(ASSIST_NAME), AssistKind::Refactor, ASSIST_LABEL, lifetime_loc, |builder| {
|
||||
add_lifetime_param(fn_def, builder, end_of_fn_ident, new_lifetime_param);
|
||||
builder.replace(lifetime_loc, format!("'{}", new_lifetime_param));
|
||||
loc_needing_lifetime.map(|loc| builder.insert(loc, format!("'{} ", new_lifetime_param)));
|
||||
@ -98,7 +98,7 @@ fn generate_impl_def_assist(
|
||||
) -> Option<()> {
|
||||
let new_lifetime_param = generate_unique_lifetime_param_name(&impl_def.type_param_list())?;
|
||||
let end_of_impl_kw = impl_def.impl_token()?.text_range().end();
|
||||
acc.add(AssistId(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |builder| {
|
||||
acc.add(AssistId(ASSIST_NAME), AssistKind::Refactor, ASSIST_LABEL, lifetime_loc, |builder| {
|
||||
add_lifetime_param(impl_def, builder, end_of_impl_kw, new_lifetime_param);
|
||||
builder.replace(lifetime_loc, format!("'{}", new_lifetime_param));
|
||||
})
|
||||
|
@ -6,7 +6,7 @@ use ra_syntax::{
|
||||
use crate::{
|
||||
assist_context::{AssistContext, Assists},
|
||||
utils::invert_boolean_expression,
|
||||
AssistId,
|
||||
AssistId, AssistKind,
|
||||
};
|
||||
|
||||
// Assist: invert_if
|
||||
@ -54,7 +54,7 @@ pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
let else_node = else_block.syntax();
|
||||
let else_range = else_node.text_range();
|
||||
let then_range = then_node.text_range();
|
||||
acc.add(AssistId("invert_if"), "Invert if", if_range, |edit| {
|
||||
acc.add(AssistId("invert_if"), AssistKind::RefactorRewrite, "Invert if", if_range, |edit| {
|
||||
edit.replace(cond_range, flip_cond.syntax().text());
|
||||
edit.replace(else_range, then_node.text());
|
||||
edit.replace(then_range, else_node.text());
|
||||
|
@ -8,7 +8,7 @@ use ra_syntax::{
|
||||
|
||||
use crate::{
|
||||
assist_context::{AssistContext, Assists},
|
||||
AssistId,
|
||||
AssistId, AssistKind,
|
||||
};
|
||||
|
||||
// Assist: merge_imports
|
||||
@ -56,9 +56,15 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()
|
||||
};
|
||||
|
||||
let target = tree.syntax().text_range();
|
||||
acc.add(AssistId("merge_imports"), "Merge imports", target, |builder| {
|
||||
builder.rewrite(rewriter);
|
||||
})
|
||||
acc.add(
|
||||
AssistId("merge_imports"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Merge imports",
|
||||
target,
|
||||
|builder| {
|
||||
builder.rewrite(rewriter);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn next_prev() -> impl Iterator<Item = Direction> {
|
||||
|
@ -6,7 +6,7 @@ use ra_syntax::{
|
||||
Direction,
|
||||
};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists, TextRange};
|
||||
use crate::{AssistContext, AssistId, AssistKind, Assists, TextRange};
|
||||
|
||||
// Assist: merge_match_arms
|
||||
//
|
||||
@ -59,25 +59,31 @@ pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option
|
||||
return None;
|
||||
}
|
||||
|
||||
acc.add(AssistId("merge_match_arms"), "Merge match arms", current_text_range, |edit| {
|
||||
let pats = if arms_to_merge.iter().any(contains_placeholder) {
|
||||
"_".into()
|
||||
} else {
|
||||
arms_to_merge
|
||||
.iter()
|
||||
.filter_map(ast::MatchArm::pat)
|
||||
.map(|x| x.syntax().to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join(" | ")
|
||||
};
|
||||
acc.add(
|
||||
AssistId("merge_match_arms"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Merge match arms",
|
||||
current_text_range,
|
||||
|edit| {
|
||||
let pats = if arms_to_merge.iter().any(contains_placeholder) {
|
||||
"_".into()
|
||||
} else {
|
||||
arms_to_merge
|
||||
.iter()
|
||||
.filter_map(ast::MatchArm::pat)
|
||||
.map(|x| x.syntax().to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join(" | ")
|
||||
};
|
||||
|
||||
let arm = format!("{} => {}", pats, current_expr.syntax().text());
|
||||
let arm = format!("{} => {}", pats, current_expr.syntax().text());
|
||||
|
||||
let start = arms_to_merge.first().unwrap().syntax().text_range().start();
|
||||
let end = arms_to_merge.last().unwrap().syntax().text_range().end();
|
||||
let start = arms_to_merge.first().unwrap().syntax().text_range().start();
|
||||
let end = arms_to_merge.last().unwrap().syntax().text_range().end();
|
||||
|
||||
edit.replace(TextRange::new(start, end), arm);
|
||||
})
|
||||
edit.replace(TextRange::new(start, end), arm);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn contains_placeholder(a: &ast::MatchArm) -> bool {
|
||||
|
@ -5,7 +5,7 @@ use ra_syntax::{
|
||||
T,
|
||||
};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// Assist: move_bounds_to_where_clause
|
||||
//
|
||||
@ -50,29 +50,37 @@ pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext
|
||||
};
|
||||
|
||||
let target = type_param_list.syntax().text_range();
|
||||
acc.add(AssistId("move_bounds_to_where_clause"), "Move to where clause", target, |edit| {
|
||||
let new_params = type_param_list
|
||||
.type_params()
|
||||
.filter(|it| it.type_bound_list().is_some())
|
||||
.map(|type_param| {
|
||||
let without_bounds = type_param.remove_bounds();
|
||||
(type_param, without_bounds)
|
||||
});
|
||||
acc.add(
|
||||
AssistId("move_bounds_to_where_clause"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Move to where clause",
|
||||
target,
|
||||
|edit| {
|
||||
let new_params = type_param_list
|
||||
.type_params()
|
||||
.filter(|it| it.type_bound_list().is_some())
|
||||
.map(|type_param| {
|
||||
let without_bounds = type_param.remove_bounds();
|
||||
(type_param, without_bounds)
|
||||
});
|
||||
|
||||
let new_type_param_list = type_param_list.replace_descendants(new_params);
|
||||
edit.replace_ast(type_param_list.clone(), new_type_param_list);
|
||||
let new_type_param_list = type_param_list.replace_descendants(new_params);
|
||||
edit.replace_ast(type_param_list.clone(), new_type_param_list);
|
||||
|
||||
let where_clause = {
|
||||
let predicates = type_param_list.type_params().filter_map(build_predicate);
|
||||
make::where_clause(predicates)
|
||||
};
|
||||
let where_clause = {
|
||||
let predicates = type_param_list.type_params().filter_map(build_predicate);
|
||||
make::where_clause(predicates)
|
||||
};
|
||||
|
||||
let to_insert = match anchor.prev_sibling_or_token() {
|
||||
Some(ref elem) if elem.kind() == WHITESPACE => format!("{} ", where_clause.syntax()),
|
||||
_ => format!(" {}", where_clause.syntax()),
|
||||
};
|
||||
edit.insert(anchor.text_range().start(), to_insert);
|
||||
})
|
||||
let to_insert = match anchor.prev_sibling_or_token() {
|
||||
Some(ref elem) if elem.kind() == WHITESPACE => {
|
||||
format!("{} ", where_clause.syntax())
|
||||
}
|
||||
_ => format!(" {}", where_clause.syntax()),
|
||||
};
|
||||
edit.insert(anchor.text_range().start(), to_insert);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> {
|
||||
|
@ -3,7 +3,7 @@ use ra_syntax::{
|
||||
SyntaxKind::WHITESPACE,
|
||||
};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// Assist: move_guard_to_arm_body
|
||||
//
|
||||
@ -40,17 +40,23 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) ->
|
||||
let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text());
|
||||
|
||||
let target = guard.syntax().text_range();
|
||||
acc.add(AssistId("move_guard_to_arm_body"), "Move guard to arm body", target, |edit| {
|
||||
match space_before_guard {
|
||||
Some(element) if element.kind() == WHITESPACE => {
|
||||
edit.delete(element.text_range());
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
acc.add(
|
||||
AssistId("move_guard_to_arm_body"),
|
||||
AssistKind::RefactorExtract,
|
||||
"Move guard to arm body",
|
||||
target,
|
||||
|edit| {
|
||||
match space_before_guard {
|
||||
Some(element) if element.kind() == WHITESPACE => {
|
||||
edit.delete(element.text_range());
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
|
||||
edit.delete(guard.syntax().text_range());
|
||||
edit.replace_node_and_indent(arm_expr.syntax(), buf);
|
||||
})
|
||||
edit.delete(guard.syntax().text_range());
|
||||
edit.replace_node_and_indent(arm_expr.syntax(), buf);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Assist: move_arm_cond_to_match_guard
|
||||
@ -101,6 +107,7 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex
|
||||
let target = if_expr.syntax().text_range();
|
||||
acc.add(
|
||||
AssistId("move_arm_cond_to_match_guard"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Move condition to match guard",
|
||||
target,
|
||||
|edit| {
|
||||
|
@ -5,7 +5,7 @@ use ra_syntax::{
|
||||
TextSize,
|
||||
};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// Assist: make_raw_string
|
||||
//
|
||||
@ -26,14 +26,23 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext) -> Option<
|
||||
let token = ctx.find_token_at_offset(STRING).and_then(ast::String::cast)?;
|
||||
let value = token.value()?;
|
||||
let target = token.syntax().text_range();
|
||||
acc.add(AssistId("make_raw_string"), "Rewrite as raw string", target, |edit| {
|
||||
let max_hash_streak = count_hashes(&value);
|
||||
let mut hashes = String::with_capacity(max_hash_streak + 1);
|
||||
for _ in 0..hashes.capacity() {
|
||||
hashes.push('#');
|
||||
}
|
||||
edit.replace(token.syntax().text_range(), format!("r{}\"{}\"{}", hashes, value, hashes));
|
||||
})
|
||||
acc.add(
|
||||
AssistId("make_raw_string"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Rewrite as raw string",
|
||||
target,
|
||||
|edit| {
|
||||
let max_hash_streak = count_hashes(&value);
|
||||
let mut hashes = String::with_capacity(max_hash_streak + 1);
|
||||
for _ in 0..hashes.capacity() {
|
||||
hashes.push('#');
|
||||
}
|
||||
edit.replace(
|
||||
token.syntax().text_range(),
|
||||
format!("r{}\"{}\"{}", hashes, value, hashes),
|
||||
);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Assist: make_usual_string
|
||||
@ -55,11 +64,17 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext) -> Optio
|
||||
let token = ctx.find_token_at_offset(RAW_STRING).and_then(ast::RawString::cast)?;
|
||||
let value = token.value()?;
|
||||
let target = token.syntax().text_range();
|
||||
acc.add(AssistId("make_usual_string"), "Rewrite as regular string", target, |edit| {
|
||||
// parse inside string to escape `"`
|
||||
let escaped = value.escape_default().to_string();
|
||||
edit.replace(token.syntax().text_range(), format!("\"{}\"", escaped));
|
||||
})
|
||||
acc.add(
|
||||
AssistId("make_usual_string"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Rewrite as regular string",
|
||||
target,
|
||||
|edit| {
|
||||
// parse inside string to escape `"`
|
||||
let escaped = value.escape_default().to_string();
|
||||
edit.replace(token.syntax().text_range(), format!("\"{}\"", escaped));
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Assist: add_hash
|
||||
@ -80,7 +95,7 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext) -> Optio
|
||||
pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
let token = ctx.find_token_at_offset(RAW_STRING)?;
|
||||
let target = token.text_range();
|
||||
acc.add(AssistId("add_hash"), "Add # to raw string", target, |edit| {
|
||||
acc.add(AssistId("add_hash"), AssistKind::Refactor, "Add # to raw string", target, |edit| {
|
||||
edit.insert(token.text_range().start() + TextSize::of('r'), "#");
|
||||
edit.insert(token.text_range().end(), "#");
|
||||
})
|
||||
@ -109,18 +124,24 @@ pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
||||
return None;
|
||||
}
|
||||
let target = token.text_range();
|
||||
acc.add(AssistId("remove_hash"), "Remove hash from raw string", target, |edit| {
|
||||
let result = &text[2..text.len() - 1];
|
||||
let result = if result.starts_with('\"') {
|
||||
// FIXME: this logic is wrong, not only the last has has to handled specially
|
||||
// no more hash, escape
|
||||
let internal_str = &result[1..result.len() - 1];
|
||||
format!("\"{}\"", internal_str.escape_default().to_string())
|
||||
} else {
|
||||
result.to_owned()
|
||||
};
|
||||
edit.replace(token.text_range(), format!("r{}", result));
|
||||
})
|
||||
acc.add(
|
||||
AssistId("remove_hash"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Remove hash from raw string",
|
||||
target,
|
||||
|edit| {
|
||||
let result = &text[2..text.len() - 1];
|
||||
let result = if result.starts_with('\"') {
|
||||
// FIXME: this logic is wrong, not only the last has has to handled specially
|
||||
// no more hash, escape
|
||||
let internal_str = &result[1..result.len() - 1];
|
||||
format!("\"{}\"", internal_str.escape_default().to_string())
|
||||
} else {
|
||||
result.to_owned()
|
||||
};
|
||||
edit.replace(token.text_range(), format!("r{}", result));
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn count_hashes(s: &str) -> usize {
|
||||
|
@ -3,7 +3,7 @@ use ra_syntax::{
|
||||
TextSize, T,
|
||||
};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// Assist: remove_dbg
|
||||
//
|
||||
@ -38,7 +38,7 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
};
|
||||
|
||||
let target = macro_call.syntax().text_range();
|
||||
acc.add(AssistId("remove_dbg"), "Remove dbg!()", target, |builder| {
|
||||
acc.add(AssistId("remove_dbg"), AssistKind::Refactor, "Remove dbg!()", target, |builder| {
|
||||
builder.replace(macro_range, macro_content);
|
||||
})
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use ra_syntax::{SyntaxKind, TextRange, T};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// Assist: remove_mut
|
||||
//
|
||||
@ -26,7 +26,13 @@ pub(crate) fn remove_mut(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
};
|
||||
|
||||
let target = mut_token.text_range();
|
||||
acc.add(AssistId("remove_mut"), "Remove `mut` keyword", target, |builder| {
|
||||
builder.delete(TextRange::new(delete_from, delete_to));
|
||||
})
|
||||
acc.add(
|
||||
AssistId("remove_mut"),
|
||||
AssistKind::Refactor,
|
||||
"Remove `mut` keyword",
|
||||
target,
|
||||
|builder| {
|
||||
builder.delete(TextRange::new(delete_from, delete_to));
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct};
|
||||
use ra_ide_db::RootDatabase;
|
||||
use ra_syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// Assist: reorder_fields
|
||||
//
|
||||
@ -42,11 +42,17 @@ fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
}
|
||||
|
||||
let target = record.syntax().text_range();
|
||||
acc.add(AssistId("reorder_fields"), "Reorder record fields", target, |edit| {
|
||||
for (old, new) in fields.iter().zip(&sorted_fields) {
|
||||
algo::diff(old, new).into_text_edit(edit.text_edit_builder());
|
||||
}
|
||||
})
|
||||
acc.add(
|
||||
AssistId("reorder_fields"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Reorder record fields",
|
||||
target,
|
||||
|edit| {
|
||||
for (old, new) in fields.iter().zip(&sorted_fields) {
|
||||
algo::diff(old, new).into_text_edit(edit.text_edit_builder());
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn get_fields_kind(node: &SyntaxNode) -> Vec<SyntaxKind> {
|
||||
|
@ -8,7 +8,7 @@ use ra_syntax::{
|
||||
AstNode,
|
||||
};
|
||||
|
||||
use crate::{utils::TryEnum, AssistContext, AssistId, Assists};
|
||||
use crate::{utils::TryEnum, AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// Assist: replace_if_let_with_match
|
||||
//
|
||||
@ -48,29 +48,36 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
|
||||
};
|
||||
|
||||
let target = if_expr.syntax().text_range();
|
||||
acc.add(AssistId("replace_if_let_with_match"), "Replace with match", target, move |edit| {
|
||||
let match_expr = {
|
||||
let then_arm = {
|
||||
let then_block = then_block.reset_indent().indent(IndentLevel(1));
|
||||
let then_expr = unwrap_trivial_block(then_block);
|
||||
make::match_arm(vec![pat.clone()], then_expr)
|
||||
acc.add(
|
||||
AssistId("replace_if_let_with_match"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Replace with match",
|
||||
target,
|
||||
move |edit| {
|
||||
let match_expr = {
|
||||
let then_arm = {
|
||||
let then_block = then_block.reset_indent().indent(IndentLevel(1));
|
||||
let then_expr = unwrap_trivial_block(then_block);
|
||||
make::match_arm(vec![pat.clone()], then_expr)
|
||||
};
|
||||
let else_arm = {
|
||||
let pattern = ctx
|
||||
.sema
|
||||
.type_of_pat(&pat)
|
||||
.and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty))
|
||||
.map(|it| it.sad_pattern())
|
||||
.unwrap_or_else(|| make::placeholder_pat().into());
|
||||
let else_expr = unwrap_trivial_block(else_block);
|
||||
make::match_arm(vec![pattern], else_expr)
|
||||
};
|
||||
let match_expr =
|
||||
make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm]));
|
||||
match_expr.indent(IndentLevel::from_node(if_expr.syntax()))
|
||||
};
|
||||
let else_arm = {
|
||||
let pattern = ctx
|
||||
.sema
|
||||
.type_of_pat(&pat)
|
||||
.and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty))
|
||||
.map(|it| it.sad_pattern())
|
||||
.unwrap_or_else(|| make::placeholder_pat().into());
|
||||
let else_expr = unwrap_trivial_block(else_block);
|
||||
make::match_arm(vec![pattern], else_expr)
|
||||
};
|
||||
let match_expr = make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm]));
|
||||
match_expr.indent(IndentLevel::from_node(if_expr.syntax()))
|
||||
};
|
||||
|
||||
edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr);
|
||||
})
|
||||
edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -9,7 +9,7 @@ use ra_syntax::{
|
||||
AstNode, T,
|
||||
};
|
||||
|
||||
use crate::{utils::TryEnum, AssistContext, AssistId, Assists};
|
||||
use crate::{utils::TryEnum, AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// Assist: replace_let_with_if_let
|
||||
//
|
||||
@ -44,24 +44,32 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) ->
|
||||
let happy_variant = TryEnum::from_ty(&ctx.sema, &ty).map(|it| it.happy_case());
|
||||
|
||||
let target = let_kw.text_range();
|
||||
acc.add(AssistId("replace_let_with_if_let"), "Replace with if-let", target, |edit| {
|
||||
let with_placeholder: ast::Pat = match happy_variant {
|
||||
None => make::placeholder_pat().into(),
|
||||
Some(var_name) => make::tuple_struct_pat(
|
||||
make::path_unqualified(make::path_segment(make::name_ref(var_name))),
|
||||
once(make::placeholder_pat().into()),
|
||||
)
|
||||
.into(),
|
||||
};
|
||||
let block = make::block_expr(None, None).indent(IndentLevel::from_node(let_stmt.syntax()));
|
||||
let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block);
|
||||
let stmt = make::expr_stmt(if_);
|
||||
acc.add(
|
||||
AssistId("replace_let_with_if_let"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Replace with if-let",
|
||||
target,
|
||||
|edit| {
|
||||
let with_placeholder: ast::Pat = match happy_variant {
|
||||
None => make::placeholder_pat().into(),
|
||||
Some(var_name) => make::tuple_struct_pat(
|
||||
make::path_unqualified(make::path_segment(make::name_ref(var_name))),
|
||||
once(make::placeholder_pat().into()),
|
||||
)
|
||||
.into(),
|
||||
};
|
||||
let block =
|
||||
make::block_expr(None, None).indent(IndentLevel::from_node(let_stmt.syntax()));
|
||||
let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block);
|
||||
let stmt = make::expr_stmt(if_);
|
||||
|
||||
let placeholder = stmt.syntax().descendants().find_map(ast::PlaceholderPat::cast).unwrap();
|
||||
let stmt = stmt.replace_descendant(placeholder.into(), original_pat);
|
||||
let placeholder =
|
||||
stmt.syntax().descendants().find_map(ast::PlaceholderPat::cast).unwrap();
|
||||
let stmt = stmt.replace_descendant(placeholder.into(), original_pat);
|
||||
|
||||
edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt));
|
||||
})
|
||||
edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt));
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -3,7 +3,7 @@ use ra_syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SmolStr, SyntaxNo
|
||||
|
||||
use crate::{
|
||||
utils::{find_insert_use_container, insert_use_statement},
|
||||
AssistContext, AssistId, Assists,
|
||||
AssistContext, AssistId, AssistKind, Assists,
|
||||
};
|
||||
|
||||
// Assist: replace_qualified_name_with_use
|
||||
@ -38,6 +38,7 @@ pub(crate) fn replace_qualified_name_with_use(
|
||||
let target = path.syntax().text_range();
|
||||
acc.add(
|
||||
AssistId("replace_qualified_name_with_use"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Replace qualified path with use",
|
||||
target,
|
||||
|builder| {
|
||||
|
@ -11,7 +11,7 @@ use ra_syntax::{
|
||||
|
||||
use crate::{
|
||||
utils::{render_snippet, Cursor, TryEnum},
|
||||
AssistContext, AssistId, Assists,
|
||||
AssistContext, AssistId, AssistKind, Assists,
|
||||
};
|
||||
|
||||
// Assist: replace_unwrap_with_match
|
||||
@ -46,37 +46,44 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext)
|
||||
let ty = ctx.sema.type_of_expr(&caller)?;
|
||||
let happy_variant = TryEnum::from_ty(&ctx.sema, &ty)?.happy_case();
|
||||
let target = method_call.syntax().text_range();
|
||||
acc.add(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", target, |builder| {
|
||||
let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant)));
|
||||
let it = make::bind_pat(make::name("a")).into();
|
||||
let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into();
|
||||
acc.add(
|
||||
AssistId("replace_unwrap_with_match"),
|
||||
AssistKind::RefactorRewrite,
|
||||
"Replace unwrap with match",
|
||||
target,
|
||||
|builder| {
|
||||
let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant)));
|
||||
let it = make::bind_pat(make::name("a")).into();
|
||||
let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into();
|
||||
|
||||
let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a")));
|
||||
let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path));
|
||||
let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a")));
|
||||
let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path));
|
||||
|
||||
let unreachable_call = make::expr_unreachable();
|
||||
let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call);
|
||||
let unreachable_call = make::expr_unreachable();
|
||||
let err_arm =
|
||||
make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call);
|
||||
|
||||
let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]);
|
||||
let match_expr = make::expr_match(caller.clone(), match_arm_list)
|
||||
.indent(IndentLevel::from_node(method_call.syntax()));
|
||||
let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]);
|
||||
let match_expr = make::expr_match(caller.clone(), match_arm_list)
|
||||
.indent(IndentLevel::from_node(method_call.syntax()));
|
||||
|
||||
let range = method_call.syntax().text_range();
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => {
|
||||
let err_arm = match_expr
|
||||
.syntax()
|
||||
.descendants()
|
||||
.filter_map(ast::MatchArm::cast)
|
||||
.last()
|
||||
.unwrap();
|
||||
let snippet =
|
||||
render_snippet(cap, match_expr.syntax(), Cursor::Before(err_arm.syntax()));
|
||||
builder.replace_snippet(cap, range, snippet)
|
||||
let range = method_call.syntax().text_range();
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => {
|
||||
let err_arm = match_expr
|
||||
.syntax()
|
||||
.descendants()
|
||||
.filter_map(ast::MatchArm::cast)
|
||||
.last()
|
||||
.unwrap();
|
||||
let snippet =
|
||||
render_snippet(cap, match_expr.syntax(), Cursor::Before(err_arm.syntax()));
|
||||
builder.replace_snippet(cap, range, snippet)
|
||||
}
|
||||
None => builder.replace(range, match_expr.to_string()),
|
||||
}
|
||||
None => builder.replace(range, match_expr.to_string()),
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -2,7 +2,7 @@ use std::iter::successors;
|
||||
|
||||
use ra_syntax::{ast, AstNode, T};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// Assist: split_import
|
||||
//
|
||||
@ -28,7 +28,7 @@ pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
||||
}
|
||||
|
||||
let target = colon_colon.text_range();
|
||||
acc.add(AssistId("split_import"), "Split import", target, |edit| {
|
||||
acc.add(AssistId("split_import"), AssistKind::RefactorExtract, "Split import", target, |edit| {
|
||||
edit.replace_ast(use_tree, new_tree);
|
||||
})
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use ra_syntax::{
|
||||
AstNode, TextRange, T,
|
||||
};
|
||||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||
|
||||
// Assist: unwrap_block
|
||||
//
|
||||
@ -50,35 +50,47 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
||||
let ancestor_then_branch = ancestor.then_branch()?;
|
||||
|
||||
let target = then_branch.syntax().text_range();
|
||||
return acc.add(assist_id, assist_label, target, |edit| {
|
||||
let range_to_del_else_if = TextRange::new(
|
||||
ancestor_then_branch.syntax().text_range().end(),
|
||||
l_curly_token.text_range().start(),
|
||||
);
|
||||
let range_to_del_rest = TextRange::new(
|
||||
then_branch.syntax().text_range().end(),
|
||||
if_expr.syntax().text_range().end(),
|
||||
);
|
||||
return acc.add(
|
||||
assist_id,
|
||||
AssistKind::Refactor,
|
||||
assist_label,
|
||||
target,
|
||||
|edit| {
|
||||
let range_to_del_else_if = TextRange::new(
|
||||
ancestor_then_branch.syntax().text_range().end(),
|
||||
l_curly_token.text_range().start(),
|
||||
);
|
||||
let range_to_del_rest = TextRange::new(
|
||||
then_branch.syntax().text_range().end(),
|
||||
if_expr.syntax().text_range().end(),
|
||||
);
|
||||
|
||||
edit.delete(range_to_del_rest);
|
||||
edit.delete(range_to_del_else_if);
|
||||
edit.replace(
|
||||
target,
|
||||
update_expr_string(then_branch.to_string(), &[' ', '{']),
|
||||
);
|
||||
});
|
||||
edit.delete(range_to_del_rest);
|
||||
edit.delete(range_to_del_else_if);
|
||||
edit.replace(
|
||||
target,
|
||||
update_expr_string(then_branch.to_string(), &[' ', '{']),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let target = block.syntax().text_range();
|
||||
return acc.add(assist_id, assist_label, target, |edit| {
|
||||
let range_to_del = TextRange::new(
|
||||
then_branch.syntax().text_range().end(),
|
||||
l_curly_token.text_range().start(),
|
||||
);
|
||||
return acc.add(
|
||||
assist_id,
|
||||
AssistKind::RefactorRewrite,
|
||||
assist_label,
|
||||
target,
|
||||
|edit| {
|
||||
let range_to_del = TextRange::new(
|
||||
then_branch.syntax().text_range().end(),
|
||||
l_curly_token.text_range().start(),
|
||||
);
|
||||
|
||||
edit.delete(range_to_del);
|
||||
edit.replace(target, update_expr_string(block.to_string(), &[' ', '{']));
|
||||
});
|
||||
edit.delete(range_to_del);
|
||||
edit.replace(target, update_expr_string(block.to_string(), &[' ', '{']));
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => return None,
|
||||
@ -86,7 +98,7 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
||||
|
||||
let unwrapped = unwrap_trivial_block(block);
|
||||
let target = unwrapped.syntax().text_range();
|
||||
acc.add(assist_id, assist_label, target, |builder| {
|
||||
acc.add(assist_id, AssistKind::RefactorRewrite, assist_label, target, |builder| {
|
||||
builder.replace(
|
||||
parent.syntax().text_range(),
|
||||
update_expr_string(unwrapped.to_string(), &[' ', '{', '\n']),
|
||||
|
@ -37,6 +37,7 @@ pub struct GroupLabel(pub String);
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Assist {
|
||||
pub id: AssistId,
|
||||
pub kind: AssistKind,
|
||||
/// Short description of the assist, as shown in the UI.
|
||||
pub label: String,
|
||||
pub group: Option<GroupLabel>,
|
||||
@ -51,6 +52,18 @@ pub struct ResolvedAssist {
|
||||
pub source_change: SourceChange,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum AssistKind {
|
||||
None,
|
||||
QuickFix,
|
||||
Refactor,
|
||||
RefactorExtract,
|
||||
RefactorInline,
|
||||
RefactorRewrite,
|
||||
Source,
|
||||
OrganizeImports,
|
||||
}
|
||||
|
||||
impl Assist {
|
||||
/// Return all the assists applicable at the given position.
|
||||
///
|
||||
@ -86,13 +99,14 @@ impl Assist {
|
||||
|
||||
pub(crate) fn new(
|
||||
id: AssistId,
|
||||
kind: AssistKind,
|
||||
label: String,
|
||||
group: Option<GroupLabel>,
|
||||
target: TextRange,
|
||||
) -> Assist {
|
||||
// FIXME: make fields private, so that this invariant can't be broken
|
||||
assert!(label.starts_with(|c: char| c.is_uppercase()));
|
||||
Assist { id, label, group, target }
|
||||
Assist { id, kind, label, group, target }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ pub use crate::{
|
||||
};
|
||||
|
||||
pub use hir::{Documentation, Semantics};
|
||||
pub use ra_assists::{Assist, AssistConfig, AssistId, ResolvedAssist};
|
||||
pub use ra_assists::{Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist};
|
||||
pub use ra_db::{
|
||||
Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot,
|
||||
SourceRootId,
|
||||
|
@ -4,9 +4,9 @@ use std::path::{self, Path};
|
||||
use itertools::Itertools;
|
||||
use ra_db::{FileId, FileRange};
|
||||
use ra_ide::{
|
||||
Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind,
|
||||
FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel,
|
||||
InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess,
|
||||
Assist, AssistKind, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold,
|
||||
FoldKind, FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange,
|
||||
Indel, InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess,
|
||||
ResolvedAssist, Runnable, Severity, SourceChange, SourceFileEdit, TextEdit,
|
||||
};
|
||||
use ra_syntax::{SyntaxKind, TextRange, TextSize};
|
||||
@ -627,6 +627,20 @@ pub(crate) fn call_hierarchy_item(
|
||||
Ok(lsp_types::CallHierarchyItem { name, kind, tags: None, detail, uri, range, selection_range })
|
||||
}
|
||||
|
||||
pub(crate) fn code_action_kind(kind: AssistKind) -> String {
|
||||
match kind {
|
||||
AssistKind::None => lsp_types::code_action_kind::EMPTY,
|
||||
AssistKind::QuickFix => lsp_types::code_action_kind::QUICKFIX,
|
||||
AssistKind::Refactor => lsp_types::code_action_kind::REFACTOR,
|
||||
AssistKind::RefactorExtract => lsp_types::code_action_kind::REFACTOR_EXTRACT,
|
||||
AssistKind::RefactorInline => lsp_types::code_action_kind::REFACTOR_INLINE,
|
||||
AssistKind::RefactorRewrite => lsp_types::code_action_kind::REFACTOR_REWRITE,
|
||||
AssistKind::Source => lsp_types::code_action_kind::SOURCE,
|
||||
AssistKind::OrganizeImports => lsp_types::code_action_kind::SOURCE_ORGANIZE_IMPORTS,
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
|
||||
pub(crate) fn unresolved_code_action(
|
||||
snap: &GlobalStateSnapshot,
|
||||
assist: Assist,
|
||||
@ -636,7 +650,7 @@ pub(crate) fn unresolved_code_action(
|
||||
title: assist.label,
|
||||
id: Some(format!("{}:{}", assist.id.0.to_owned(), index.to_string())),
|
||||
group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0),
|
||||
kind: Some(String::new()),
|
||||
kind: Some(code_action_kind(assist.kind)),
|
||||
edit: None,
|
||||
command: None,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user