Refactor assists API to be more convenient for adding new assists
It now duplicates completion API in its shape.
This commit is contained in:
parent
f4cd75ac06
commit
4867968d22
38 changed files with 525 additions and 597 deletions
|
@ -1,4 +1,6 @@
|
||||||
//! This module defines `AssistCtx` -- the API surface that is exposed to assists.
|
//! See `AssistContext`
|
||||||
|
|
||||||
|
use algo::find_covering_element;
|
||||||
use hir::Semantics;
|
use hir::Semantics;
|
||||||
use ra_db::{FileId, FileRange};
|
use ra_db::{FileId, FileRange};
|
||||||
use ra_fmt::{leading_indent, reindent};
|
use ra_fmt::{leading_indent, reindent};
|
||||||
|
@ -7,7 +9,7 @@ use ra_ide_db::{
|
||||||
RootDatabase,
|
RootDatabase,
|
||||||
};
|
};
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
algo::{self, find_covering_element, find_node_at_offset, SyntaxRewriter},
|
algo::{self, find_node_at_offset, SyntaxRewriter},
|
||||||
AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize,
|
AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize,
|
||||||
TokenAtOffset,
|
TokenAtOffset,
|
||||||
};
|
};
|
||||||
|
@ -15,46 +17,17 @@ use ra_text_edit::TextEditBuilder;
|
||||||
|
|
||||||
use crate::{AssistId, AssistLabel, GroupLabel, ResolvedAssist};
|
use crate::{AssistId, AssistLabel, GroupLabel, ResolvedAssist};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
/// `AssistContext` allows to apply an assist or check if it could be applied.
|
||||||
pub(crate) struct Assist(pub(crate) Vec<AssistInfo>);
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub(crate) struct AssistInfo {
|
|
||||||
pub(crate) label: AssistLabel,
|
|
||||||
pub(crate) group_label: Option<GroupLabel>,
|
|
||||||
pub(crate) source_change: Option<SourceChange>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AssistInfo {
|
|
||||||
fn new(label: AssistLabel) -> AssistInfo {
|
|
||||||
AssistInfo { label, group_label: None, source_change: None }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolved(self, source_change: SourceChange) -> AssistInfo {
|
|
||||||
AssistInfo { source_change: Some(source_change), ..self }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_group(self, group_label: GroupLabel) -> AssistInfo {
|
|
||||||
AssistInfo { group_label: Some(group_label), ..self }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn into_resolved(self) -> Option<ResolvedAssist> {
|
|
||||||
let label = self.label;
|
|
||||||
self.source_change.map(|source_change| ResolvedAssist { label, source_change })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `AssistCtx` allows to apply an assist or check if it could be applied.
|
|
||||||
///
|
///
|
||||||
/// Assists use a somewhat over-engineered approach, given the current needs. The
|
/// Assists use a somewhat over-engineered approach, given the current needs.
|
||||||
/// assists workflow consists of two phases. In the first phase, a user asks for
|
/// The assists workflow consists of two phases. In the first phase, a user asks
|
||||||
/// the list of available assists. In the second phase, the user picks a
|
/// for the list of available assists. In the second phase, the user picks a
|
||||||
/// particular assist and it gets applied.
|
/// particular assist and it gets applied.
|
||||||
///
|
///
|
||||||
/// There are two peculiarities here:
|
/// There are two peculiarities here:
|
||||||
///
|
///
|
||||||
/// * first, we ideally avoid computing more things then necessary to answer
|
/// * first, we ideally avoid computing more things then necessary to answer "is
|
||||||
/// "is assist applicable" in the first phase.
|
/// assist applicable" in the first phase.
|
||||||
/// * second, when we are applying assist, we don't have a guarantee that there
|
/// * second, when we are applying assist, we don't have a guarantee that there
|
||||||
/// weren't any changes between the point when user asked for assists and when
|
/// weren't any changes between the point when user asked for assists and when
|
||||||
/// they applied a particular assist. So, when applying assist, we need to do
|
/// they applied a particular assist. So, when applying assist, we need to do
|
||||||
|
@ -63,152 +36,158 @@ impl AssistInfo {
|
||||||
/// To avoid repeating the same code twice for both "check" and "apply"
|
/// To avoid repeating the same code twice for both "check" and "apply"
|
||||||
/// functions, we use an approach reminiscent of that of Django's function based
|
/// functions, we use an approach reminiscent of that of Django's function based
|
||||||
/// views dealing with forms. Each assist receives a runtime parameter,
|
/// views dealing with forms. Each assist receives a runtime parameter,
|
||||||
/// `should_compute_edit`. It first check if an edit is applicable (potentially
|
/// `resolve`. It first check if an edit is applicable (potentially computing
|
||||||
/// computing info required to compute the actual edit). If it is applicable,
|
/// info required to compute the actual edit). If it is applicable, and
|
||||||
/// and `should_compute_edit` is `true`, it then computes the actual edit.
|
/// `resolve` is `true`, it then computes the actual edit.
|
||||||
///
|
///
|
||||||
/// So, to implement the original assists workflow, we can first apply each edit
|
/// So, to implement the original assists workflow, we can first apply each edit
|
||||||
/// with `should_compute_edit = false`, and then applying the selected edit
|
/// with `resolve = false`, and then applying the selected edit again, with
|
||||||
/// again, with `should_compute_edit = true` this time.
|
/// `resolve = true` this time.
|
||||||
///
|
///
|
||||||
/// Note, however, that we don't actually use such two-phase logic at the
|
/// Note, however, that we don't actually use such two-phase logic at the
|
||||||
/// moment, because the LSP API is pretty awkward in this place, and it's much
|
/// moment, because the LSP API is pretty awkward in this place, and it's much
|
||||||
/// easier to just compute the edit eagerly :-)
|
/// easier to just compute the edit eagerly :-)
|
||||||
#[derive(Clone)]
|
pub(crate) struct AssistContext<'a> {
|
||||||
pub(crate) struct AssistCtx<'a> {
|
pub(crate) sema: Semantics<'a, RootDatabase>,
|
||||||
pub(crate) sema: &'a Semantics<'a, RootDatabase>,
|
pub(super) db: &'a RootDatabase,
|
||||||
pub(crate) db: &'a RootDatabase,
|
|
||||||
pub(crate) frange: FileRange,
|
pub(crate) frange: FileRange,
|
||||||
source_file: SourceFile,
|
source_file: SourceFile,
|
||||||
should_compute_edit: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> AssistCtx<'a> {
|
impl<'a> AssistContext<'a> {
|
||||||
pub fn new(
|
pub fn new(sema: Semantics<'a, RootDatabase>, frange: FileRange) -> AssistContext<'a> {
|
||||||
sema: &'a Semantics<'a, RootDatabase>,
|
|
||||||
frange: FileRange,
|
|
||||||
should_compute_edit: bool,
|
|
||||||
) -> AssistCtx<'a> {
|
|
||||||
let source_file = sema.parse(frange.file_id);
|
let source_file = sema.parse(frange.file_id);
|
||||||
AssistCtx { sema, db: sema.db, frange, source_file, should_compute_edit }
|
let db = sema.db;
|
||||||
|
AssistContext { sema, db, frange, source_file }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_assist(
|
// NB, this ignores active selection.
|
||||||
self,
|
pub(crate) fn offset(&self) -> TextSize {
|
||||||
id: AssistId,
|
self.frange.range.start()
|
||||||
label: impl Into<String>,
|
|
||||||
target: TextRange,
|
|
||||||
f: impl FnOnce(&mut ActionBuilder),
|
|
||||||
) -> Option<Assist> {
|
|
||||||
let label = AssistLabel::new(id, label.into(), None, target);
|
|
||||||
let change_label = label.label.clone();
|
|
||||||
let mut info = AssistInfo::new(label);
|
|
||||||
if self.should_compute_edit {
|
|
||||||
let source_change = {
|
|
||||||
let mut edit = ActionBuilder::new(&self);
|
|
||||||
f(&mut edit);
|
|
||||||
edit.build(change_label)
|
|
||||||
};
|
|
||||||
info = info.resolved(source_change)
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(Assist(vec![info]))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn add_assist_group(self, group_name: impl Into<String>) -> AssistGroup<'a> {
|
|
||||||
let group = GroupLabel(group_name.into());
|
|
||||||
AssistGroup { ctx: self, group, assists: Vec::new() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> {
|
pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> {
|
||||||
self.source_file.syntax().token_at_offset(self.frange.range.start())
|
self.source_file.syntax().token_at_offset(self.offset())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn find_token_at_offset(&self, kind: SyntaxKind) -> Option<SyntaxToken> {
|
pub(crate) fn find_token_at_offset(&self, kind: SyntaxKind) -> Option<SyntaxToken> {
|
||||||
self.token_at_offset().find(|it| it.kind() == kind)
|
self.token_at_offset().find(|it| it.kind() == kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn find_node_at_offset<N: AstNode>(&self) -> Option<N> {
|
pub(crate) fn find_node_at_offset<N: AstNode>(&self) -> Option<N> {
|
||||||
find_node_at_offset(self.source_file.syntax(), self.frange.range.start())
|
find_node_at_offset(self.source_file.syntax(), self.offset())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn find_node_at_offset_with_descend<N: AstNode>(&self) -> Option<N> {
|
pub(crate) fn find_node_at_offset_with_descend<N: AstNode>(&self) -> Option<N> {
|
||||||
self.sema
|
self.sema
|
||||||
.find_node_at_offset_with_descend(self.source_file.syntax(), self.frange.range.start())
|
.find_node_at_offset_with_descend(self.source_file.syntax(), self.frange.range.start())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn covering_element(&self) -> SyntaxElement {
|
pub(crate) fn covering_element(&self) -> SyntaxElement {
|
||||||
find_covering_element(self.source_file.syntax(), self.frange.range)
|
find_covering_element(self.source_file.syntax(), self.frange.range)
|
||||||
}
|
}
|
||||||
|
// FIXME: remove
|
||||||
pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement {
|
pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement {
|
||||||
find_covering_element(self.source_file.syntax(), range)
|
find_covering_element(self.source_file.syntax(), range)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct AssistGroup<'a> {
|
pub(crate) struct Assists {
|
||||||
ctx: AssistCtx<'a>,
|
resolve: bool,
|
||||||
group: GroupLabel,
|
file: FileId,
|
||||||
assists: Vec<AssistInfo>,
|
buf: Vec<(AssistLabel, Option<SourceChange>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> AssistGroup<'a> {
|
impl Assists {
|
||||||
pub(crate) fn add_assist(
|
pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists {
|
||||||
|
Assists { resolve: true, file: ctx.frange.file_id, buf: Vec::new() }
|
||||||
|
}
|
||||||
|
pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists {
|
||||||
|
Assists { resolve: false, file: ctx.frange.file_id, buf: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn finish_unresolved(self) -> Vec<AssistLabel> {
|
||||||
|
assert!(!self.resolve);
|
||||||
|
self.finish()
|
||||||
|
.into_iter()
|
||||||
|
.map(|(label, edit)| {
|
||||||
|
assert!(edit.is_none());
|
||||||
|
label
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn finish_resolved(self) -> Vec<ResolvedAssist> {
|
||||||
|
assert!(self.resolve);
|
||||||
|
self.finish()
|
||||||
|
.into_iter()
|
||||||
|
.map(|(label, edit)| ResolvedAssist { label, source_change: edit.unwrap() })
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: AssistId,
|
id: AssistId,
|
||||||
label: impl Into<String>,
|
label: impl Into<String>,
|
||||||
target: TextRange,
|
target: TextRange,
|
||||||
f: impl FnOnce(&mut ActionBuilder),
|
f: impl FnOnce(&mut AssistBuilder),
|
||||||
) {
|
) -> Option<()> {
|
||||||
let label = AssistLabel::new(id, label.into(), Some(self.group.clone()), target);
|
let label = AssistLabel::new(id, label.into(), None, target);
|
||||||
|
self.add_impl(label, f)
|
||||||
|
}
|
||||||
|
pub(crate) fn add_group(
|
||||||
|
&mut self,
|
||||||
|
group: &GroupLabel,
|
||||||
|
id: AssistId,
|
||||||
|
label: impl Into<String>,
|
||||||
|
target: TextRange,
|
||||||
|
f: impl FnOnce(&mut AssistBuilder),
|
||||||
|
) -> Option<()> {
|
||||||
|
let label = AssistLabel::new(id, label.into(), Some(group.clone()), target);
|
||||||
|
self.add_impl(label, f)
|
||||||
|
}
|
||||||
|
fn add_impl(&mut self, label: AssistLabel, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> {
|
||||||
let change_label = label.label.clone();
|
let change_label = label.label.clone();
|
||||||
let mut info = AssistInfo::new(label).with_group(self.group.clone());
|
let source_change = if self.resolve {
|
||||||
if self.ctx.should_compute_edit {
|
let mut builder = AssistBuilder::new(self.file);
|
||||||
let source_change = {
|
f(&mut builder);
|
||||||
let mut edit = ActionBuilder::new(&self.ctx);
|
Some(builder.finish(change_label))
|
||||||
f(&mut edit);
|
} else {
|
||||||
edit.build(change_label)
|
None
|
||||||
};
|
|
||||||
info = info.resolved(source_change)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.assists.push(info)
|
self.buf.push((label, source_change));
|
||||||
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn finish(self) -> Option<Assist> {
|
fn finish(mut self) -> Vec<(AssistLabel, Option<SourceChange>)> {
|
||||||
if self.assists.is_empty() {
|
self.buf.sort_by_key(|(label, _edit)| label.target.len());
|
||||||
None
|
self.buf
|
||||||
} else {
|
|
||||||
Some(Assist(self.assists))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct ActionBuilder<'a, 'b> {
|
pub(crate) struct AssistBuilder {
|
||||||
edit: TextEditBuilder,
|
edit: TextEditBuilder,
|
||||||
cursor_position: Option<TextSize>,
|
cursor_position: Option<TextSize>,
|
||||||
file: FileId,
|
file: FileId,
|
||||||
ctx: &'a AssistCtx<'b>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> ActionBuilder<'a, 'b> {
|
impl AssistBuilder {
|
||||||
fn new(ctx: &'a AssistCtx<'b>) -> Self {
|
pub(crate) fn new(file: FileId) -> AssistBuilder {
|
||||||
Self {
|
AssistBuilder { edit: TextEditBuilder::default(), cursor_position: None, file }
|
||||||
edit: TextEditBuilder::default(),
|
|
||||||
cursor_position: None,
|
|
||||||
file: ctx.frange.file_id,
|
|
||||||
ctx,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn ctx(&self) -> &AssistCtx<'b> {
|
/// Remove specified `range` of text.
|
||||||
&self.ctx
|
pub(crate) fn delete(&mut self, range: TextRange) {
|
||||||
|
self.edit.delete(range)
|
||||||
|
}
|
||||||
|
/// Append specified `text` at the given `offset`
|
||||||
|
pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) {
|
||||||
|
self.edit.insert(offset, text.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replaces specified `range` of text with a given string.
|
/// Replaces specified `range` of text with a given string.
|
||||||
pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
|
pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
|
||||||
self.edit.replace(range, replace_with.into())
|
self.edit.replace(range, replace_with.into())
|
||||||
}
|
}
|
||||||
|
pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) {
|
||||||
|
algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
|
||||||
|
}
|
||||||
/// Replaces specified `node` of text with a given string, reindenting the
|
/// Replaces specified `node` of text with a given string, reindenting the
|
||||||
/// string to maintain `node`'s existing indent.
|
/// string to maintain `node`'s existing indent.
|
||||||
// FIXME: remove in favor of ra_syntax::edit::IndentLevel::increase_indent
|
// FIXME: remove in favor of ra_syntax::edit::IndentLevel::increase_indent
|
||||||
|
@ -223,42 +202,28 @@ impl<'a, 'b> ActionBuilder<'a, 'b> {
|
||||||
}
|
}
|
||||||
self.replace(node.text_range(), replace_with)
|
self.replace(node.text_range(), replace_with)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove specified `range` of text.
|
|
||||||
#[allow(unused)]
|
|
||||||
pub(crate) fn delete(&mut self, range: TextRange) {
|
|
||||||
self.edit.delete(range)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Append specified `text` at the given `offset`
|
|
||||||
pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) {
|
|
||||||
self.edit.insert(offset, text.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Specify desired position of the cursor after the assist is applied.
|
|
||||||
pub(crate) fn set_cursor(&mut self, offset: TextSize) {
|
|
||||||
self.cursor_position = Some(offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get access to the raw `TextEditBuilder`.
|
|
||||||
pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder {
|
|
||||||
&mut self.edit
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) {
|
|
||||||
algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
|
|
||||||
}
|
|
||||||
pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) {
|
pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) {
|
||||||
let node = rewriter.rewrite_root().unwrap();
|
let node = rewriter.rewrite_root().unwrap();
|
||||||
let new = rewriter.rewrite(&node);
|
let new = rewriter.rewrite(&node);
|
||||||
algo::diff(&node, &new).into_text_edit(&mut self.edit)
|
algo::diff(&node, &new).into_text_edit(&mut self.edit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Specify desired position of the cursor after the assist is applied.
|
||||||
|
pub(crate) fn set_cursor(&mut self, offset: TextSize) {
|
||||||
|
self.cursor_position = Some(offset)
|
||||||
|
}
|
||||||
|
// FIXME: better API
|
||||||
pub(crate) fn set_file(&mut self, assist_file: FileId) {
|
pub(crate) fn set_file(&mut self, assist_file: FileId) {
|
||||||
self.file = assist_file;
|
self.file = assist_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build(self, change_label: String) -> SourceChange {
|
// FIXME: kill this API
|
||||||
|
/// Get access to the raw `TextEditBuilder`.
|
||||||
|
pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder {
|
||||||
|
&mut self.edit
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(self, change_label: String) -> SourceChange {
|
||||||
let edit = self.edit.finish();
|
let edit = self.edit.finish();
|
||||||
if edit.is_empty() && self.cursor_position.is_none() {
|
if edit.is_empty() && self.cursor_position.is_none() {
|
||||||
panic!("Only call `add_assist` if the assist can be applied")
|
panic!("Only call `add_assist` if the assist can be applied")
|
|
@ -6,7 +6,10 @@ use ra_syntax::{
|
||||||
};
|
};
|
||||||
use stdx::SepBy;
|
use stdx::SepBy;
|
||||||
|
|
||||||
use crate::{Assist, AssistCtx, AssistId};
|
use crate::{
|
||||||
|
assist_context::{AssistContext, Assists},
|
||||||
|
AssistId,
|
||||||
|
};
|
||||||
|
|
||||||
// Assist: add_custom_impl
|
// Assist: add_custom_impl
|
||||||
//
|
//
|
||||||
|
@ -25,7 +28,7 @@ use crate::{Assist, AssistCtx, AssistId};
|
||||||
//
|
//
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let input = ctx.find_node_at_offset::<ast::AttrInput>()?;
|
let input = ctx.find_node_at_offset::<ast::AttrInput>()?;
|
||||||
let attr = input.syntax().parent().and_then(ast::Attr::cast)?;
|
let attr = input.syntax().parent().and_then(ast::Attr::cast)?;
|
||||||
|
|
||||||
|
@ -49,7 +52,7 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option<Assist> {
|
||||||
format!("Add custom impl '{}' for '{}'", trait_token.text().as_str(), annotated_name);
|
format!("Add custom impl '{}' for '{}'", trait_token.text().as_str(), annotated_name);
|
||||||
|
|
||||||
let target = attr.syntax().text_range();
|
let target = attr.syntax().text_range();
|
||||||
ctx.add_assist(AssistId("add_custom_impl"), label, target, |edit| {
|
acc.add(AssistId("add_custom_impl"), label, target, |edit| {
|
||||||
let new_attr_input = input
|
let new_attr_input = input
|
||||||
.syntax()
|
.syntax()
|
||||||
.descendants_with_tokens()
|
.descendants_with_tokens()
|
||||||
|
|
|
@ -4,7 +4,7 @@ use ra_syntax::{
|
||||||
TextSize,
|
TextSize,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Assist, AssistCtx, AssistId};
|
use crate::{AssistContext, AssistId, Assists};
|
||||||
|
|
||||||
// Assist: add_derive
|
// Assist: add_derive
|
||||||
//
|
//
|
||||||
|
@ -24,11 +24,11 @@ use crate::{Assist, AssistCtx, AssistId};
|
||||||
// y: u32,
|
// y: u32,
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn add_derive(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn add_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
|
let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
|
||||||
let node_start = derive_insertion_offset(&nominal)?;
|
let node_start = derive_insertion_offset(&nominal)?;
|
||||||
let target = nominal.syntax().text_range();
|
let target = nominal.syntax().text_range();
|
||||||
ctx.add_assist(AssistId("add_derive"), "Add `#[derive]`", target, |edit| {
|
acc.add(AssistId("add_derive"), "Add `#[derive]`", target, |edit| {
|
||||||
let derive_attr = nominal
|
let derive_attr = nominal
|
||||||
.attrs()
|
.attrs()
|
||||||
.filter_map(|x| x.as_simple_call())
|
.filter_map(|x| x.as_simple_call())
|
||||||
|
@ -57,9 +57,10 @@ fn derive_insertion_offset(nominal: &ast::NominalDef) -> Option<TextSize> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
|
||||||
use crate::tests::{check_assist, check_assist_target};
|
use crate::tests::{check_assist, check_assist_target};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_derive_new() {
|
fn add_derive_new() {
|
||||||
check_assist(
|
check_assist(
|
||||||
|
|
|
@ -4,7 +4,7 @@ use ra_syntax::{
|
||||||
TextRange,
|
TextRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Assist, AssistCtx, AssistId};
|
use crate::{AssistContext, AssistId, Assists};
|
||||||
|
|
||||||
// Assist: add_explicit_type
|
// Assist: add_explicit_type
|
||||||
//
|
//
|
||||||
|
@ -21,7 +21,7 @@ use crate::{Assist, AssistCtx, AssistId};
|
||||||
// let x: i32 = 92;
|
// let x: i32 = 92;
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let stmt = ctx.find_node_at_offset::<LetStmt>()?;
|
let stmt = ctx.find_node_at_offset::<LetStmt>()?;
|
||||||
let expr = stmt.initializer()?;
|
let expr = stmt.initializer()?;
|
||||||
let pat = stmt.pat()?;
|
let pat = stmt.pat()?;
|
||||||
|
@ -59,7 +59,7 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> {
|
||||||
|
|
||||||
let db = ctx.db;
|
let db = ctx.db;
|
||||||
let new_type_string = ty.display_truncated(db, None).to_string();
|
let new_type_string = ty.display_truncated(db, None).to_string();
|
||||||
ctx.add_assist(
|
acc.add(
|
||||||
AssistId("add_explicit_type"),
|
AssistId("add_explicit_type"),
|
||||||
format!("Insert explicit type '{}'", new_type_string),
|
format!("Insert explicit type '{}'", new_type_string),
|
||||||
pat_range,
|
pat_range,
|
||||||
|
|
|
@ -4,10 +4,10 @@ use ra_syntax::{
|
||||||
TextSize,
|
TextSize,
|
||||||
};
|
};
|
||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
|
|
||||||
use crate::{utils::FamousDefs, Assist, AssistCtx, AssistId};
|
|
||||||
use test_utils::tested_by;
|
use test_utils::tested_by;
|
||||||
|
|
||||||
|
use crate::{utils::FamousDefs, AssistContext, AssistId, Assists};
|
||||||
|
|
||||||
// Assist add_from_impl_for_enum
|
// Assist add_from_impl_for_enum
|
||||||
//
|
//
|
||||||
// Adds a From impl for an enum variant with one tuple field
|
// Adds a From impl for an enum variant with one tuple field
|
||||||
|
@ -25,7 +25,7 @@ use test_utils::tested_by;
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn add_from_impl_for_enum(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?;
|
let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?;
|
||||||
let variant_name = variant.name()?;
|
let variant_name = variant.name()?;
|
||||||
let enum_name = variant.parent_enum().name()?;
|
let enum_name = variant.parent_enum().name()?;
|
||||||
|
@ -42,13 +42,13 @@ pub(crate) fn add_from_impl_for_enum(ctx: AssistCtx) -> Option<Assist> {
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if existing_from_impl(ctx.sema, &variant).is_some() {
|
if existing_from_impl(&ctx.sema, &variant).is_some() {
|
||||||
tested_by!(test_add_from_impl_already_exists);
|
tested_by!(test_add_from_impl_already_exists);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let target = variant.syntax().text_range();
|
let target = variant.syntax().text_range();
|
||||||
ctx.add_assist(
|
acc.add(
|
||||||
AssistId("add_from_impl_for_enum"),
|
AssistId("add_from_impl_for_enum"),
|
||||||
"Add From impl for this enum variant",
|
"Add From impl for this enum variant",
|
||||||
target,
|
target,
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
use ra_syntax::{
|
|
||||||
ast::{self, AstNode},
|
|
||||||
SyntaxKind, SyntaxNode, TextSize,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{Assist, AssistCtx, AssistId};
|
|
||||||
use ast::{edit::IndentLevel, ArgListOwner, ModuleItemOwner};
|
|
||||||
use hir::HirDisplay;
|
use hir::HirDisplay;
|
||||||
use ra_db::FileId;
|
use ra_db::FileId;
|
||||||
|
use ra_syntax::{
|
||||||
|
ast::{self, edit::IndentLevel, ArgListOwner, AstNode, ModuleItemOwner},
|
||||||
|
SyntaxKind, SyntaxNode, TextSize,
|
||||||
|
};
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
|
|
||||||
|
use crate::{AssistContext, AssistId, Assists};
|
||||||
|
|
||||||
// Assist: add_function
|
// Assist: add_function
|
||||||
//
|
//
|
||||||
// Adds a stub function with a signature matching the function under the cursor.
|
// Adds a stub function with a signature matching the function under the cursor.
|
||||||
|
@ -34,7 +33,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn add_function(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let path_expr: ast::PathExpr = ctx.find_node_at_offset()?;
|
let path_expr: ast::PathExpr = ctx.find_node_at_offset()?;
|
||||||
let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
|
let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
|
||||||
let path = path_expr.path()?;
|
let path = path_expr.path()?;
|
||||||
|
@ -59,7 +58,7 @@ pub(crate) fn add_function(ctx: AssistCtx) -> Option<Assist> {
|
||||||
let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?;
|
let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?;
|
||||||
|
|
||||||
let target = call.syntax().text_range();
|
let target = call.syntax().text_range();
|
||||||
ctx.add_assist(AssistId("add_function"), "Add function", target, |edit| {
|
acc.add(AssistId("add_function"), "Add function", target, |edit| {
|
||||||
let function_template = function_builder.render();
|
let function_template = function_builder.render();
|
||||||
edit.set_file(function_template.file);
|
edit.set_file(function_template.file);
|
||||||
edit.set_cursor(function_template.cursor_offset);
|
edit.set_cursor(function_template.cursor_offset);
|
||||||
|
@ -87,7 +86,7 @@ impl FunctionBuilder {
|
||||||
/// Prepares a generated function that matches `call` in `generate_in`
|
/// Prepares a generated function that matches `call` in `generate_in`
|
||||||
/// (or as close to `call` as possible, if `generate_in` is `None`)
|
/// (or as close to `call` as possible, if `generate_in` is `None`)
|
||||||
fn from_call(
|
fn from_call(
|
||||||
ctx: &AssistCtx,
|
ctx: &AssistContext,
|
||||||
call: &ast::CallExpr,
|
call: &ast::CallExpr,
|
||||||
path: &ast::Path,
|
path: &ast::Path,
|
||||||
target_module: Option<hir::InFile<hir::ModuleSource>>,
|
target_module: Option<hir::InFile<hir::ModuleSource>>,
|
||||||
|
@ -152,7 +151,7 @@ fn fn_name(call: &ast::Path) -> Option<ast::Name> {
|
||||||
|
|
||||||
/// Computes the type variables and arguments required for the generated function
|
/// Computes the type variables and arguments required for the generated function
|
||||||
fn fn_args(
|
fn fn_args(
|
||||||
ctx: &AssistCtx,
|
ctx: &AssistContext,
|
||||||
call: &ast::CallExpr,
|
call: &ast::CallExpr,
|
||||||
) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> {
|
) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> {
|
||||||
let mut arg_names = Vec::new();
|
let mut arg_names = Vec::new();
|
||||||
|
@ -219,7 +218,7 @@ fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fn_arg_type(ctx: &AssistCtx, fn_arg: &ast::Expr) -> Option<String> {
|
fn fn_arg_type(ctx: &AssistContext, fn_arg: &ast::Expr) -> Option<String> {
|
||||||
let ty = ctx.sema.type_of_expr(fn_arg)?;
|
let ty = ctx.sema.type_of_expr(fn_arg)?;
|
||||||
if ty.is_unknown() {
|
if ty.is_unknown() {
|
||||||
return None;
|
return None;
|
||||||
|
|
|
@ -4,7 +4,7 @@ use ra_syntax::{
|
||||||
};
|
};
|
||||||
use stdx::{format_to, SepBy};
|
use stdx::{format_to, SepBy};
|
||||||
|
|
||||||
use crate::{Assist, AssistCtx, AssistId};
|
use crate::{AssistContext, AssistId, Assists};
|
||||||
|
|
||||||
// Assist: add_impl
|
// Assist: add_impl
|
||||||
//
|
//
|
||||||
|
@ -25,43 +25,36 @@ use crate::{Assist, AssistCtx, AssistId};
|
||||||
//
|
//
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn add_impl(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
|
let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
|
||||||
let name = nominal.name()?;
|
let name = nominal.name()?;
|
||||||
let target = nominal.syntax().text_range();
|
let target = nominal.syntax().text_range();
|
||||||
ctx.add_assist(
|
acc.add(AssistId("add_impl"), format!("Implement {}", name.text().as_str()), target, |edit| {
|
||||||
AssistId("add_impl"),
|
let type_params = nominal.type_param_list();
|
||||||
format!("Implement {}", name.text().as_str()),
|
let start_offset = nominal.syntax().text_range().end();
|
||||||
target,
|
let mut buf = String::new();
|
||||||
|edit| {
|
buf.push_str("\n\nimpl");
|
||||||
let type_params = nominal.type_param_list();
|
if let Some(type_params) = &type_params {
|
||||||
let start_offset = nominal.syntax().text_range().end();
|
format_to!(buf, "{}", type_params.syntax());
|
||||||
let mut buf = String::new();
|
}
|
||||||
buf.push_str("\n\nimpl");
|
buf.push_str(" ");
|
||||||
if let Some(type_params) = &type_params {
|
buf.push_str(name.text().as_str());
|
||||||
format_to!(buf, "{}", type_params.syntax());
|
if let Some(type_params) = type_params {
|
||||||
}
|
let lifetime_params = type_params
|
||||||
buf.push_str(" ");
|
.lifetime_params()
|
||||||
buf.push_str(name.text().as_str());
|
.filter_map(|it| it.lifetime_token())
|
||||||
if let Some(type_params) = type_params {
|
.map(|it| it.text().clone());
|
||||||
let lifetime_params = type_params
|
let type_params =
|
||||||
.lifetime_params()
|
type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone());
|
||||||
.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(", ");
|
let generic_params = lifetime_params.chain(type_params).sep_by(", ");
|
||||||
format_to!(buf, "<{}>", generic_params)
|
format_to!(buf, "<{}>", generic_params)
|
||||||
}
|
}
|
||||||
buf.push_str(" {\n");
|
buf.push_str(" {\n");
|
||||||
edit.set_cursor(start_offset + TextSize::of(&buf));
|
edit.set_cursor(start_offset + TextSize::of(&buf));
|
||||||
buf.push_str("\n}");
|
buf.push_str("\n}");
|
||||||
edit.insert(start_offset, buf);
|
edit.insert(start_offset, buf);
|
||||||
},
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -9,9 +9,10 @@ use ra_syntax::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
assist_context::{AssistContext, Assists},
|
||||||
ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
|
ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
|
||||||
utils::{get_missing_assoc_items, resolve_target_trait},
|
utils::{get_missing_assoc_items, resolve_target_trait},
|
||||||
Assist, AssistCtx, AssistId,
|
AssistId,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
|
@ -50,8 +51,9 @@ enum AddMissingImplMembersMode {
|
||||||
//
|
//
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn add_missing_impl_members(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
add_missing_impl_members_inner(
|
add_missing_impl_members_inner(
|
||||||
|
acc,
|
||||||
ctx,
|
ctx,
|
||||||
AddMissingImplMembersMode::NoDefaultMethods,
|
AddMissingImplMembersMode::NoDefaultMethods,
|
||||||
"add_impl_missing_members",
|
"add_impl_missing_members",
|
||||||
|
@ -91,8 +93,9 @@ pub(crate) fn add_missing_impl_members(ctx: AssistCtx) -> Option<Assist> {
|
||||||
//
|
//
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn add_missing_default_members(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn add_missing_default_members(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
add_missing_impl_members_inner(
|
add_missing_impl_members_inner(
|
||||||
|
acc,
|
||||||
ctx,
|
ctx,
|
||||||
AddMissingImplMembersMode::DefaultMethodsOnly,
|
AddMissingImplMembersMode::DefaultMethodsOnly,
|
||||||
"add_impl_default_members",
|
"add_impl_default_members",
|
||||||
|
@ -101,11 +104,12 @@ pub(crate) fn add_missing_default_members(ctx: AssistCtx) -> Option<Assist> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_missing_impl_members_inner(
|
fn add_missing_impl_members_inner(
|
||||||
ctx: AssistCtx,
|
acc: &mut Assists,
|
||||||
|
ctx: &AssistContext,
|
||||||
mode: AddMissingImplMembersMode,
|
mode: AddMissingImplMembersMode,
|
||||||
assist_id: &'static str,
|
assist_id: &'static str,
|
||||||
label: &'static str,
|
label: &'static str,
|
||||||
) -> Option<Assist> {
|
) -> Option<()> {
|
||||||
let _p = ra_prof::profile("add_missing_impl_members_inner");
|
let _p = ra_prof::profile("add_missing_impl_members_inner");
|
||||||
let impl_def = ctx.find_node_at_offset::<ast::ImplDef>()?;
|
let impl_def = ctx.find_node_at_offset::<ast::ImplDef>()?;
|
||||||
let impl_item_list = impl_def.item_list()?;
|
let impl_item_list = impl_def.item_list()?;
|
||||||
|
@ -142,12 +146,11 @@ fn add_missing_impl_members_inner(
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let sema = ctx.sema;
|
|
||||||
let target = impl_def.syntax().text_range();
|
let target = impl_def.syntax().text_range();
|
||||||
ctx.add_assist(AssistId(assist_id), label, target, |edit| {
|
acc.add(AssistId(assist_id), label, target, |edit| {
|
||||||
let n_existing_items = impl_item_list.assoc_items().count();
|
let n_existing_items = impl_item_list.assoc_items().count();
|
||||||
let source_scope = sema.scope_for_def(trait_);
|
let source_scope = ctx.sema.scope_for_def(trait_);
|
||||||
let target_scope = sema.scope(impl_item_list.syntax());
|
let target_scope = ctx.sema.scope(impl_item_list.syntax());
|
||||||
let ast_transform = QualifyPaths::new(&target_scope, &source_scope)
|
let ast_transform = QualifyPaths::new(&target_scope, &source_scope)
|
||||||
.or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def));
|
.or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def));
|
||||||
let items = missing_items
|
let items = missing_items
|
||||||
|
@ -170,13 +173,12 @@ fn add_missing_impl_members_inner(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_body(fn_def: ast::FnDef) -> ast::FnDef {
|
fn add_body(fn_def: ast::FnDef) -> ast::FnDef {
|
||||||
if fn_def.body().is_none() {
|
if fn_def.body().is_some() {
|
||||||
let body = make::block_expr(None, Some(make::expr_todo()));
|
return fn_def;
|
||||||
let body = IndentLevel(1).increase_indent(body);
|
|
||||||
fn_def.with_body(body)
|
|
||||||
} else {
|
|
||||||
fn_def
|
|
||||||
}
|
}
|
||||||
|
let body = make::block_expr(None, Some(make::expr_todo()));
|
||||||
|
let body = IndentLevel(1).increase_indent(body);
|
||||||
|
fn_def.with_body(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -7,7 +7,7 @@ use ra_syntax::{
|
||||||
};
|
};
|
||||||
use stdx::{format_to, SepBy};
|
use stdx::{format_to, SepBy};
|
||||||
|
|
||||||
use crate::{Assist, AssistCtx, AssistId};
|
use crate::{AssistContext, AssistId, Assists};
|
||||||
|
|
||||||
// Assist: add_new
|
// Assist: add_new
|
||||||
//
|
//
|
||||||
|
@ -29,7 +29,7 @@ use crate::{Assist, AssistCtx, AssistId};
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn add_new(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let strukt = ctx.find_node_at_offset::<ast::StructDef>()?;
|
let strukt = ctx.find_node_at_offset::<ast::StructDef>()?;
|
||||||
|
|
||||||
// We want to only apply this to non-union structs with named fields
|
// We want to only apply this to non-union structs with named fields
|
||||||
|
@ -42,7 +42,7 @@ pub(crate) fn add_new(ctx: AssistCtx) -> Option<Assist> {
|
||||||
let impl_def = find_struct_impl(&ctx, &strukt)?;
|
let impl_def = find_struct_impl(&ctx, &strukt)?;
|
||||||
|
|
||||||
let target = strukt.syntax().text_range();
|
let target = strukt.syntax().text_range();
|
||||||
ctx.add_assist(AssistId("add_new"), "Add default constructor", target, |edit| {
|
acc.add(AssistId("add_new"), "Add default constructor", target, |edit| {
|
||||||
let mut buf = String::with_capacity(512);
|
let mut buf = String::with_capacity(512);
|
||||||
|
|
||||||
if impl_def.is_some() {
|
if impl_def.is_some() {
|
||||||
|
@ -123,7 +123,7 @@ fn generate_impl_text(strukt: &ast::StructDef, code: &str) -> String {
|
||||||
//
|
//
|
||||||
// FIXME: change the new fn checking to a more semantic approach when that's more
|
// FIXME: change the new fn checking to a more semantic approach when that's more
|
||||||
// viable (e.g. we process proc macros, etc)
|
// viable (e.g. we process proc macros, etc)
|
||||||
fn find_struct_impl(ctx: &AssistCtx, strukt: &ast::StructDef) -> Option<Option<ast::ImplDef>> {
|
fn find_struct_impl(ctx: &AssistContext, strukt: &ast::StructDef) -> Option<Option<ast::ImplDef>> {
|
||||||
let db = ctx.db;
|
let db = ctx.db;
|
||||||
let module = strukt.syntax().ancestors().find(|node| {
|
let module = strukt.syntax().ancestors().find(|node| {
|
||||||
ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind())
|
ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind())
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ra_syntax::ast::{self, AstNode};
|
use ra_syntax::ast::{self, AstNode};
|
||||||
|
|
||||||
use crate::{utils::invert_boolean_expression, Assist, AssistCtx, AssistId};
|
use crate::{utils::invert_boolean_expression, AssistContext, AssistId, Assists};
|
||||||
|
|
||||||
// Assist: apply_demorgan
|
// Assist: apply_demorgan
|
||||||
//
|
//
|
||||||
|
@ -21,7 +21,7 @@ use crate::{utils::invert_boolean_expression, Assist, AssistCtx, AssistId};
|
||||||
// if !(x == 4 && y) {}
|
// if !(x == 4 && y) {}
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn apply_demorgan(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
|
let expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
|
||||||
let op = expr.op_kind()?;
|
let op = expr.op_kind()?;
|
||||||
let op_range = expr.op_token()?.text_range();
|
let op_range = expr.op_token()?.text_range();
|
||||||
|
@ -39,7 +39,7 @@ pub(crate) fn apply_demorgan(ctx: AssistCtx) -> Option<Assist> {
|
||||||
let rhs_range = rhs.syntax().text_range();
|
let rhs_range = rhs.syntax().text_range();
|
||||||
let not_rhs = invert_boolean_expression(rhs);
|
let not_rhs = invert_boolean_expression(rhs);
|
||||||
|
|
||||||
ctx.add_assist(AssistId("apply_demorgan"), "Apply De Morgan's law", op_range, |edit| {
|
acc.add(AssistId("apply_demorgan"), "Apply De Morgan's law", op_range, |edit| {
|
||||||
edit.replace(op_range, opposite_op);
|
edit.replace(op_range, opposite_op);
|
||||||
edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text()));
|
edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text()));
|
||||||
edit.replace(rhs_range, format!("{})", not_rhs.syntax().text()));
|
edit.replace(rhs_range, format!("{})", not_rhs.syntax().text()));
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
|
use either::Either;
|
||||||
use hir::{
|
use hir::{
|
||||||
AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait,
|
AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait,
|
||||||
Type,
|
Type,
|
||||||
|
@ -12,12 +13,7 @@ use ra_syntax::{
|
||||||
};
|
};
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use crate::{
|
use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists, GroupLabel};
|
||||||
assist_ctx::{Assist, AssistCtx},
|
|
||||||
utils::insert_use_statement,
|
|
||||||
AssistId,
|
|
||||||
};
|
|
||||||
use either::Either;
|
|
||||||
|
|
||||||
// Assist: auto_import
|
// Assist: auto_import
|
||||||
//
|
//
|
||||||
|
@ -38,7 +34,7 @@ use either::Either;
|
||||||
// }
|
// }
|
||||||
// # pub mod std { pub mod collections { pub struct HashMap { } } }
|
// # pub mod std { pub mod collections { pub struct HashMap { } } }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let auto_import_assets = AutoImportAssets::new(&ctx)?;
|
let auto_import_assets = AutoImportAssets::new(&ctx)?;
|
||||||
let proposed_imports = auto_import_assets.search_for_imports(ctx.db);
|
let proposed_imports = auto_import_assets.search_for_imports(ctx.db);
|
||||||
if proposed_imports.is_empty() {
|
if proposed_imports.is_empty() {
|
||||||
|
@ -46,13 +42,19 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let range = ctx.sema.original_range(&auto_import_assets.syntax_under_caret).range;
|
let range = ctx.sema.original_range(&auto_import_assets.syntax_under_caret).range;
|
||||||
let mut group = ctx.add_assist_group(auto_import_assets.get_import_group_message());
|
let group = auto_import_assets.get_import_group_message();
|
||||||
for import in proposed_imports {
|
for import in proposed_imports {
|
||||||
group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), range, |edit| {
|
acc.add_group(
|
||||||
insert_use_statement(&auto_import_assets.syntax_under_caret, &import, edit);
|
&group,
|
||||||
});
|
AssistId("auto_import"),
|
||||||
|
format!("Import `{}`", &import),
|
||||||
|
range,
|
||||||
|
|builder| {
|
||||||
|
insert_use_statement(&auto_import_assets.syntax_under_caret, &import, ctx, builder);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
group.finish()
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -63,7 +65,7 @@ struct AutoImportAssets {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AutoImportAssets {
|
impl AutoImportAssets {
|
||||||
fn new(ctx: &AssistCtx) -> Option<Self> {
|
fn new(ctx: &AssistContext) -> Option<Self> {
|
||||||
if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() {
|
if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() {
|
||||||
Self::for_regular_path(path_under_caret, &ctx)
|
Self::for_regular_path(path_under_caret, &ctx)
|
||||||
} else {
|
} else {
|
||||||
|
@ -71,7 +73,7 @@ impl AutoImportAssets {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistCtx) -> Option<Self> {
|
fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistContext) -> Option<Self> {
|
||||||
let syntax_under_caret = method_call.syntax().to_owned();
|
let syntax_under_caret = method_call.syntax().to_owned();
|
||||||
let module_with_name_to_import = ctx.sema.scope(&syntax_under_caret).module()?;
|
let module_with_name_to_import = ctx.sema.scope(&syntax_under_caret).module()?;
|
||||||
Some(Self {
|
Some(Self {
|
||||||
|
@ -81,7 +83,7 @@ impl AutoImportAssets {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn for_regular_path(path_under_caret: ast::Path, ctx: &AssistCtx) -> Option<Self> {
|
fn for_regular_path(path_under_caret: ast::Path, ctx: &AssistContext) -> Option<Self> {
|
||||||
let syntax_under_caret = path_under_caret.syntax().to_owned();
|
let syntax_under_caret = path_under_caret.syntax().to_owned();
|
||||||
if syntax_under_caret.ancestors().find_map(ast::UseItem::cast).is_some() {
|
if syntax_under_caret.ancestors().find_map(ast::UseItem::cast).is_some() {
|
||||||
return None;
|
return None;
|
||||||
|
@ -104,8 +106,8 @@ impl AutoImportAssets {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_import_group_message(&self) -> String {
|
fn get_import_group_message(&self) -> GroupLabel {
|
||||||
match &self.import_candidate {
|
let name = match &self.import_candidate {
|
||||||
ImportCandidate::UnqualifiedName(name) => format!("Import {}", name),
|
ImportCandidate::UnqualifiedName(name) => format!("Import {}", name),
|
||||||
ImportCandidate::QualifierStart(qualifier_start) => {
|
ImportCandidate::QualifierStart(qualifier_start) => {
|
||||||
format!("Import {}", qualifier_start)
|
format!("Import {}", qualifier_start)
|
||||||
|
@ -116,7 +118,8 @@ impl AutoImportAssets {
|
||||||
ImportCandidate::TraitMethod(_, trait_method_name) => {
|
ImportCandidate::TraitMethod(_, trait_method_name) => {
|
||||||
format!("Import a trait for method {}", trait_method_name)
|
format!("Import a trait for method {}", trait_method_name)
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
GroupLabel(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> {
|
fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> {
|
||||||
|
@ -383,7 +386,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
r"
|
r"
|
||||||
use PubMod1::PubStruct;
|
use PubMod3::PubStruct;
|
||||||
|
|
||||||
PubSt<|>ruct
|
PubSt<|>ruct
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
ast, AstNode,
|
ast::{self, BlockExpr, Expr, LoopBodyOwner},
|
||||||
|
AstNode,
|
||||||
SyntaxKind::{COMMENT, WHITESPACE},
|
SyntaxKind::{COMMENT, WHITESPACE},
|
||||||
SyntaxNode, TextSize,
|
SyntaxNode, TextSize,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Assist, AssistCtx, AssistId};
|
use crate::{AssistContext, AssistId, Assists};
|
||||||
use ast::{BlockExpr, Expr, LoopBodyOwner};
|
|
||||||
|
|
||||||
// Assist: change_return_type_to_result
|
// Assist: change_return_type_to_result
|
||||||
//
|
//
|
||||||
|
@ -18,7 +18,7 @@ use ast::{BlockExpr, Expr, LoopBodyOwner};
|
||||||
// ```
|
// ```
|
||||||
// fn foo() -> Result<i32, > { Ok(42i32) }
|
// fn foo() -> Result<i32, > { Ok(42i32) }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn change_return_type_to_result(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let fn_def = ctx.find_node_at_offset::<ast::FnDef>();
|
let fn_def = ctx.find_node_at_offset::<ast::FnDef>();
|
||||||
let fn_def = &mut fn_def?;
|
let fn_def = &mut fn_def?;
|
||||||
let ret_type = &fn_def.ret_type()?.type_ref()?;
|
let ret_type = &fn_def.ret_type()?.type_ref()?;
|
||||||
|
@ -33,7 +33,7 @@ pub(crate) fn change_return_type_to_result(ctx: AssistCtx) -> Option<Assist> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.add_assist(
|
acc.add(
|
||||||
AssistId("change_return_type_to_result"),
|
AssistId("change_return_type_to_result"),
|
||||||
"Change return type to Result",
|
"Change return type to Result",
|
||||||
ret_type.syntax().text_range(),
|
ret_type.syntax().text_range(),
|
||||||
|
|
|
@ -7,10 +7,10 @@ use ra_syntax::{
|
||||||
},
|
},
|
||||||
SyntaxNode, TextSize, T,
|
SyntaxNode, TextSize, T,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Assist, AssistCtx, AssistId};
|
|
||||||
use test_utils::tested_by;
|
use test_utils::tested_by;
|
||||||
|
|
||||||
|
use crate::{AssistContext, AssistId, Assists};
|
||||||
|
|
||||||
// Assist: change_visibility
|
// Assist: change_visibility
|
||||||
//
|
//
|
||||||
// Adds or changes existing visibility specifier.
|
// Adds or changes existing visibility specifier.
|
||||||
|
@ -22,14 +22,14 @@ use test_utils::tested_by;
|
||||||
// ```
|
// ```
|
||||||
// pub(crate) fn frobnicate() {}
|
// pub(crate) fn frobnicate() {}
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn change_visibility(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
if let Some(vis) = ctx.find_node_at_offset::<ast::Visibility>() {
|
if let Some(vis) = ctx.find_node_at_offset::<ast::Visibility>() {
|
||||||
return change_vis(ctx, vis);
|
return change_vis(acc, vis);
|
||||||
}
|
}
|
||||||
add_vis(ctx)
|
add_vis(acc, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_vis(ctx: AssistCtx) -> Option<Assist> {
|
fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let item_keyword = ctx.token_at_offset().find(|leaf| match leaf.kind() {
|
let item_keyword = ctx.token_at_offset().find(|leaf| match leaf.kind() {
|
||||||
T![const] | T![fn] | T![mod] | T![struct] | T![enum] | T![trait] => true,
|
T![const] | T![fn] | T![mod] | T![struct] | T![enum] | T![trait] => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
|
@ -66,15 +66,10 @@ fn add_vis(ctx: AssistCtx) -> Option<Assist> {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.add_assist(
|
acc.add(AssistId("change_visibility"), "Change visibility to pub(crate)", target, |edit| {
|
||||||
AssistId("change_visibility"),
|
edit.insert(offset, "pub(crate) ");
|
||||||
"Change visibility to pub(crate)",
|
edit.set_cursor(offset);
|
||||||
target,
|
})
|
||||||
|edit| {
|
|
||||||
edit.insert(offset, "pub(crate) ");
|
|
||||||
edit.set_cursor(offset);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vis_offset(node: &SyntaxNode) -> TextSize {
|
fn vis_offset(node: &SyntaxNode) -> TextSize {
|
||||||
|
@ -88,10 +83,10 @@ fn vis_offset(node: &SyntaxNode) -> TextSize {
|
||||||
.unwrap_or_else(|| node.text_range().start())
|
.unwrap_or_else(|| node.text_range().start())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn change_vis(ctx: AssistCtx, vis: ast::Visibility) -> Option<Assist> {
|
fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
|
||||||
if vis.syntax().text() == "pub" {
|
if vis.syntax().text() == "pub" {
|
||||||
let target = vis.syntax().text_range();
|
let target = vis.syntax().text_range();
|
||||||
return ctx.add_assist(
|
return acc.add(
|
||||||
AssistId("change_visibility"),
|
AssistId("change_visibility"),
|
||||||
"Change Visibility to pub(crate)",
|
"Change Visibility to pub(crate)",
|
||||||
target,
|
target,
|
||||||
|
@ -103,7 +98,7 @@ fn change_vis(ctx: AssistCtx, vis: ast::Visibility) -> Option<Assist> {
|
||||||
}
|
}
|
||||||
if vis.syntax().text() == "pub(crate)" {
|
if vis.syntax().text() == "pub(crate)" {
|
||||||
let target = vis.syntax().text_range();
|
let target = vis.syntax().text_range();
|
||||||
return ctx.add_assist(
|
return acc.add(
|
||||||
AssistId("change_visibility"),
|
AssistId("change_visibility"),
|
||||||
"Change visibility to pub",
|
"Change visibility to pub",
|
||||||
target,
|
target,
|
||||||
|
|
|
@ -9,7 +9,7 @@ use ra_syntax::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
assist_ctx::{Assist, AssistCtx},
|
assist_context::{AssistContext, Assists},
|
||||||
utils::invert_boolean_expression,
|
utils::invert_boolean_expression,
|
||||||
AssistId,
|
AssistId,
|
||||||
};
|
};
|
||||||
|
@ -36,7 +36,7 @@ use crate::{
|
||||||
// bar();
|
// bar();
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
|
let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
|
||||||
if if_expr.else_branch().is_some() {
|
if if_expr.else_branch().is_some() {
|
||||||
return None;
|
return None;
|
||||||
|
@ -96,93 +96,88 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Option<Assist> {
|
||||||
let cursor_position = ctx.frange.range.start();
|
let cursor_position = ctx.frange.range.start();
|
||||||
|
|
||||||
let target = if_expr.syntax().text_range();
|
let target = if_expr.syntax().text_range();
|
||||||
ctx.add_assist(
|
acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| {
|
||||||
AssistId("convert_to_guarded_return"),
|
let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
|
||||||
"Convert to guarded return",
|
let new_block = match if_let_pat {
|
||||||
target,
|
None => {
|
||||||
|edit| {
|
// If.
|
||||||
let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
|
let new_expr = {
|
||||||
let new_block = match if_let_pat {
|
let then_branch =
|
||||||
None => {
|
make::block_expr(once(make::expr_stmt(early_expression).into()), None);
|
||||||
// If.
|
let cond = invert_boolean_expression(cond_expr);
|
||||||
let new_expr = {
|
let e = make::expr_if(make::condition(cond, None), then_branch);
|
||||||
let then_branch =
|
if_indent_level.increase_indent(e)
|
||||||
make::block_expr(once(make::expr_stmt(early_expression).into()), None);
|
};
|
||||||
let cond = invert_boolean_expression(cond_expr);
|
replace(new_expr.syntax(), &then_block, &parent_block, &if_expr)
|
||||||
let e = make::expr_if(make::condition(cond, None), then_branch);
|
|
||||||
if_indent_level.increase_indent(e)
|
|
||||||
};
|
|
||||||
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)
|
|
||||||
};
|
|
||||||
|
|
||||||
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 let_stmt = make::let_stmt(
|
|
||||||
make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(),
|
|
||||||
Some(match_expr),
|
|
||||||
);
|
|
||||||
let let_stmt = if_indent_level.increase_indent(let_stmt);
|
|
||||||
replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap());
|
|
||||||
edit.set_cursor(cursor_position);
|
|
||||||
|
|
||||||
fn replace(
|
|
||||||
new_expr: &SyntaxNode,
|
|
||||||
then_block: &ast::BlockExpr,
|
|
||||||
parent_block: &ast::BlockExpr,
|
|
||||||
if_expr: &ast::IfExpr,
|
|
||||||
) -> SyntaxNode {
|
|
||||||
let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone());
|
|
||||||
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,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
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)
|
||||||
|
};
|
||||||
|
|
||||||
|
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 let_stmt = make::let_stmt(
|
||||||
|
make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(),
|
||||||
|
Some(match_expr),
|
||||||
|
);
|
||||||
|
let let_stmt = if_indent_level.increase_indent(let_stmt);
|
||||||
|
replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap());
|
||||||
|
edit.set_cursor(cursor_position);
|
||||||
|
|
||||||
|
fn replace(
|
||||||
|
new_expr: &SyntaxNode,
|
||||||
|
then_block: &ast::BlockExpr,
|
||||||
|
parent_block: &ast::BlockExpr,
|
||||||
|
if_expr: &ast::IfExpr,
|
||||||
|
) -> SyntaxNode {
|
||||||
|
let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone());
|
||||||
|
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)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -5,7 +5,7 @@ use itertools::Itertools;
|
||||||
use ra_ide_db::RootDatabase;
|
use ra_ide_db::RootDatabase;
|
||||||
use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
|
use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
|
||||||
|
|
||||||
use crate::{Assist, AssistCtx, AssistId};
|
use crate::{AssistContext, AssistId, Assists};
|
||||||
|
|
||||||
// Assist: fill_match_arms
|
// Assist: fill_match_arms
|
||||||
//
|
//
|
||||||
|
@ -31,7 +31,7 @@ use crate::{Assist, AssistCtx, AssistId};
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let match_expr = ctx.find_node_at_offset::<ast::MatchExpr>()?;
|
let match_expr = ctx.find_node_at_offset::<ast::MatchExpr>()?;
|
||||||
let match_arm_list = match_expr.match_arm_list()?;
|
let match_arm_list = match_expr.match_arm_list()?;
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let target = match_expr.syntax().text_range();
|
let target = match_expr.syntax().text_range();
|
||||||
ctx.add_assist(AssistId("fill_match_arms"), "Fill match arms", target, |edit| {
|
acc.add(AssistId("fill_match_arms"), "Fill match arms", target, |edit| {
|
||||||
let new_arm_list = match_arm_list.remove_placeholder().append_arms(missing_arms);
|
let new_arm_list = match_arm_list.remove_placeholder().append_arms(missing_arms);
|
||||||
edit.set_cursor(expr.syntax().text_range().start());
|
edit.set_cursor(expr.syntax().text_range().start());
|
||||||
edit.replace_ast(match_arm_list, new_arm_list);
|
edit.replace_ast(match_arm_list, new_arm_list);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ra_syntax::ast::{AstNode, BinExpr, BinOp};
|
use ra_syntax::ast::{AstNode, BinExpr, BinOp};
|
||||||
|
|
||||||
use crate::{Assist, AssistCtx, AssistId};
|
use crate::{AssistContext, AssistId, Assists};
|
||||||
|
|
||||||
// Assist: flip_binexpr
|
// Assist: flip_binexpr
|
||||||
//
|
//
|
||||||
|
@ -17,7 +17,7 @@ use crate::{Assist, AssistCtx, AssistId};
|
||||||
// let _ = 2 + 90;
|
// let _ = 2 + 90;
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn flip_binexpr(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let expr = ctx.find_node_at_offset::<BinExpr>()?;
|
let expr = ctx.find_node_at_offset::<BinExpr>()?;
|
||||||
let lhs = expr.lhs()?.syntax().clone();
|
let lhs = expr.lhs()?.syntax().clone();
|
||||||
let rhs = expr.rhs()?.syntax().clone();
|
let rhs = expr.rhs()?.syntax().clone();
|
||||||
|
@ -33,7 +33,7 @@ pub(crate) fn flip_binexpr(ctx: AssistCtx) -> Option<Assist> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.add_assist(AssistId("flip_binexpr"), "Flip binary expression", op_range, |edit| {
|
acc.add(AssistId("flip_binexpr"), "Flip binary expression", op_range, |edit| {
|
||||||
if let FlipAction::FlipAndReplaceOp(new_op) = action {
|
if let FlipAction::FlipAndReplaceOp(new_op) = action {
|
||||||
edit.replace(op_range, new_op);
|
edit.replace(op_range, new_op);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ra_syntax::{algo::non_trivia_sibling, Direction, T};
|
use ra_syntax::{algo::non_trivia_sibling, Direction, T};
|
||||||
|
|
||||||
use crate::{Assist, AssistCtx, AssistId};
|
use crate::{AssistContext, AssistId, Assists};
|
||||||
|
|
||||||
// Assist: flip_comma
|
// Assist: flip_comma
|
||||||
//
|
//
|
||||||
|
@ -17,7 +17,7 @@ use crate::{Assist, AssistCtx, AssistId};
|
||||||
// ((3, 4), (1, 2));
|
// ((3, 4), (1, 2));
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn flip_comma(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let comma = ctx.find_token_at_offset(T![,])?;
|
let comma = ctx.find_token_at_offset(T![,])?;
|
||||||
let prev = non_trivia_sibling(comma.clone().into(), Direction::Prev)?;
|
let prev = non_trivia_sibling(comma.clone().into(), Direction::Prev)?;
|
||||||
let next = non_trivia_sibling(comma.clone().into(), Direction::Next)?;
|
let next = non_trivia_sibling(comma.clone().into(), Direction::Next)?;
|
||||||
|
@ -28,7 +28,7 @@ pub(crate) fn flip_comma(ctx: AssistCtx) -> Option<Assist> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.add_assist(AssistId("flip_comma"), "Flip comma", comma.text_range(), |edit| {
|
acc.add(AssistId("flip_comma"), "Flip comma", comma.text_range(), |edit| {
|
||||||
edit.replace(prev.text_range(), next.to_string());
|
edit.replace(prev.text_range(), next.to_string());
|
||||||
edit.replace(next.text_range(), prev.to_string());
|
edit.replace(next.text_range(), prev.to_string());
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,7 +4,7 @@ use ra_syntax::{
|
||||||
Direction, T,
|
Direction, T,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Assist, AssistCtx, AssistId};
|
use crate::{AssistContext, AssistId, Assists};
|
||||||
|
|
||||||
// Assist: flip_trait_bound
|
// Assist: flip_trait_bound
|
||||||
//
|
//
|
||||||
|
@ -17,7 +17,7 @@ use crate::{Assist, AssistCtx, AssistId};
|
||||||
// ```
|
// ```
|
||||||
// fn foo<T: Copy + Clone>() { }
|
// fn foo<T: Copy + Clone>() { }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn flip_trait_bound(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
// We want to replicate the behavior of `flip_binexpr` by only suggesting
|
// We want to replicate the behavior of `flip_binexpr` by only suggesting
|
||||||
// the assist when the cursor is on a `+`
|
// the assist when the cursor is on a `+`
|
||||||
let plus = ctx.find_token_at_offset(T![+])?;
|
let plus = ctx.find_token_at_offset(T![+])?;
|
||||||
|
@ -33,7 +33,7 @@ pub(crate) fn flip_trait_bound(ctx: AssistCtx) -> Option<Assist> {
|
||||||
);
|
);
|
||||||
|
|
||||||
let target = plus.text_range();
|
let target = plus.text_range();
|
||||||
ctx.add_assist(AssistId("flip_trait_bound"), "Flip trait bounds", target, |edit| {
|
acc.add(AssistId("flip_trait_bound"), "Flip trait bounds", target, |edit| {
|
||||||
edit.replace(before.text_range(), after.to_string());
|
edit.replace(before.text_range(), after.to_string());
|
||||||
edit.replace(after.text_range(), before.to_string());
|
edit.replace(after.text_range(), before.to_string());
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,7 +5,10 @@ use ra_syntax::{
|
||||||
};
|
};
|
||||||
use test_utils::tested_by;
|
use test_utils::tested_by;
|
||||||
|
|
||||||
use crate::{assist_ctx::ActionBuilder, Assist, AssistCtx, AssistId};
|
use crate::{
|
||||||
|
assist_context::{AssistContext, Assists},
|
||||||
|
AssistId,
|
||||||
|
};
|
||||||
|
|
||||||
// Assist: inline_local_variable
|
// Assist: inline_local_variable
|
||||||
//
|
//
|
||||||
|
@ -23,7 +26,7 @@ use crate::{assist_ctx::ActionBuilder, Assist, AssistCtx, AssistId};
|
||||||
// (1 + 2) * 4;
|
// (1 + 2) * 4;
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let let_stmt = ctx.find_node_at_offset::<ast::LetStmt>()?;
|
let let_stmt = ctx.find_node_at_offset::<ast::LetStmt>()?;
|
||||||
let bind_pat = match let_stmt.pat()? {
|
let bind_pat = match let_stmt.pat()? {
|
||||||
ast::Pat::BindPat(pat) => pat,
|
ast::Pat::BindPat(pat) => pat,
|
||||||
|
@ -33,7 +36,7 @@ pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option<Assist> {
|
||||||
tested_by!(test_not_inline_mut_variable);
|
tested_by!(test_not_inline_mut_variable);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if !bind_pat.syntax().text_range().contains_inclusive(ctx.frange.range.start()) {
|
if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) {
|
||||||
tested_by!(not_applicable_outside_of_bind_pat);
|
tested_by!(not_applicable_outside_of_bind_pat);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -107,20 +110,14 @@ pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option<Assist> {
|
||||||
let init_in_paren = format!("({})", &init_str);
|
let init_in_paren = format!("({})", &init_str);
|
||||||
|
|
||||||
let target = bind_pat.syntax().text_range();
|
let target = bind_pat.syntax().text_range();
|
||||||
ctx.add_assist(
|
acc.add(AssistId("inline_local_variable"), "Inline variable", target, move |builder| {
|
||||||
AssistId("inline_local_variable"),
|
builder.delete(delete_range);
|
||||||
"Inline variable",
|
for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) {
|
||||||
target,
|
let replacement = if should_wrap { init_in_paren.clone() } else { init_str.clone() };
|
||||||
move |edit: &mut ActionBuilder| {
|
builder.replace(desc.file_range.range, replacement)
|
||||||
edit.delete(delete_range);
|
}
|
||||||
for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) {
|
builder.set_cursor(delete_range.start())
|
||||||
let replacement =
|
})
|
||||||
if should_wrap { init_in_paren.clone() } else { init_str.clone() };
|
|
||||||
edit.replace(desc.file_range.range, replacement)
|
|
||||||
}
|
|
||||||
edit.set_cursor(delete_range.start())
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -9,7 +9,7 @@ use ra_syntax::{
|
||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
use test_utils::tested_by;
|
use test_utils::tested_by;
|
||||||
|
|
||||||
use crate::{Assist, AssistCtx, AssistId};
|
use crate::{AssistContext, AssistId, Assists};
|
||||||
|
|
||||||
// Assist: introduce_variable
|
// Assist: introduce_variable
|
||||||
//
|
//
|
||||||
|
@ -27,7 +27,7 @@ use crate::{Assist, AssistCtx, AssistId};
|
||||||
// var_name * 4;
|
// var_name * 4;
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn introduce_variable(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
if ctx.frange.range.is_empty() {
|
if ctx.frange.range.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ pub(crate) fn introduce_variable(ctx: AssistCtx) -> Option<Assist> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let target = expr.syntax().text_range();
|
let target = expr.syntax().text_range();
|
||||||
ctx.add_assist(AssistId("introduce_variable"), "Extract into variable", target, move |edit| {
|
acc.add(AssistId("introduce_variable"), "Extract into variable", target, move |edit| {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
|
|
||||||
let cursor_offset = if wrap_in_block {
|
let cursor_offset = if wrap_in_block {
|
||||||
|
|
|
@ -3,7 +3,11 @@ use ra_syntax::{
|
||||||
T,
|
T,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{utils::invert_boolean_expression, Assist, AssistCtx, AssistId};
|
use crate::{
|
||||||
|
assist_context::{AssistContext, Assists},
|
||||||
|
utils::invert_boolean_expression,
|
||||||
|
AssistId,
|
||||||
|
};
|
||||||
|
|
||||||
// Assist: invert_if
|
// Assist: invert_if
|
||||||
//
|
//
|
||||||
|
@ -24,7 +28,7 @@ use crate::{utils::invert_boolean_expression, Assist, AssistCtx, AssistId};
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
|
|
||||||
pub(crate) fn invert_if(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let if_keyword = ctx.find_token_at_offset(T![if])?;
|
let if_keyword = ctx.find_token_at_offset(T![if])?;
|
||||||
let expr = ast::IfExpr::cast(if_keyword.parent())?;
|
let expr = ast::IfExpr::cast(if_keyword.parent())?;
|
||||||
let if_range = if_keyword.text_range();
|
let if_range = if_keyword.text_range();
|
||||||
|
@ -40,21 +44,21 @@ pub(crate) fn invert_if(ctx: AssistCtx) -> Option<Assist> {
|
||||||
|
|
||||||
let cond = expr.condition()?.expr()?;
|
let cond = expr.condition()?.expr()?;
|
||||||
let then_node = expr.then_branch()?.syntax().clone();
|
let then_node = expr.then_branch()?.syntax().clone();
|
||||||
|
let else_block = match expr.else_branch()? {
|
||||||
|
ast::ElseBranch::Block(it) => it,
|
||||||
|
ast::ElseBranch::IfExpr(_) => return None,
|
||||||
|
};
|
||||||
|
|
||||||
if let ast::ElseBranch::Block(else_block) = expr.else_branch()? {
|
let cond_range = cond.syntax().text_range();
|
||||||
let cond_range = cond.syntax().text_range();
|
let flip_cond = invert_boolean_expression(cond);
|
||||||
let flip_cond = invert_boolean_expression(cond);
|
let else_node = else_block.syntax();
|
||||||
let else_node = else_block.syntax();
|
let else_range = else_node.text_range();
|
||||||
let else_range = else_node.text_range();
|
let then_range = then_node.text_range();
|
||||||
let then_range = then_node.text_range();
|
acc.add(AssistId("invert_if"), "Invert if", if_range, |edit| {
|
||||||
return ctx.add_assist(AssistId("invert_if"), "Invert if", if_range, |edit| {
|
edit.replace(cond_range, flip_cond.syntax().text());
|
||||||
edit.replace(cond_range, flip_cond.syntax().text());
|
edit.replace(else_range, then_node.text());
|
||||||
edit.replace(else_range, then_node.text());
|
edit.replace(then_range, else_node.text());
|
||||||
edit.replace(then_range, else_node.text());
|
})
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -6,7 +6,10 @@ use ra_syntax::{
|
||||||
AstNode, Direction, InsertPosition, SyntaxElement, T,
|
AstNode, Direction, InsertPosition, SyntaxElement, T,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Assist, AssistCtx, AssistId};
|
use crate::{
|
||||||
|
assist_context::{AssistContext, Assists},
|
||||||
|
AssistId,
|
||||||
|
};
|
||||||
|
|
||||||
// Assist: merge_imports
|
// Assist: merge_imports
|
||||||
//
|
//
|
||||||
|
@ -20,7 +23,7 @@ use crate::{Assist, AssistCtx, AssistId};
|
||||||
// ```
|
// ```
|
||||||
// use std::{fmt::Formatter, io};
|
// use std::{fmt::Formatter, io};
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn merge_imports(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let tree: ast::UseTree = ctx.find_node_at_offset()?;
|
let tree: ast::UseTree = ctx.find_node_at_offset()?;
|
||||||
let mut rewriter = SyntaxRewriter::default();
|
let mut rewriter = SyntaxRewriter::default();
|
||||||
let mut offset = ctx.frange.range.start();
|
let mut offset = ctx.frange.range.start();
|
||||||
|
@ -53,10 +56,10 @@ pub(crate) fn merge_imports(ctx: AssistCtx) -> Option<Assist> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let target = tree.syntax().text_range();
|
let target = tree.syntax().text_range();
|
||||||
ctx.add_assist(AssistId("merge_imports"), "Merge imports", target, |edit| {
|
acc.add(AssistId("merge_imports"), "Merge imports", target, |builder| {
|
||||||
edit.rewrite(rewriter);
|
builder.rewrite(rewriter);
|
||||||
// FIXME: we only need because our diff is imprecise
|
// FIXME: we only need because our diff is imprecise
|
||||||
edit.set_cursor(offset);
|
builder.set_cursor(offset);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use ra_syntax::{
|
||||||
Direction, TextSize,
|
Direction, TextSize,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Assist, AssistCtx, AssistId, TextRange};
|
use crate::{AssistContext, AssistId, Assists, TextRange};
|
||||||
|
|
||||||
// Assist: merge_match_arms
|
// Assist: merge_match_arms
|
||||||
//
|
//
|
||||||
|
@ -32,7 +32,7 @@ use crate::{Assist, AssistCtx, AssistId, TextRange};
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn merge_match_arms(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let current_arm = ctx.find_node_at_offset::<ast::MatchArm>()?;
|
let current_arm = ctx.find_node_at_offset::<ast::MatchArm>()?;
|
||||||
// Don't try to handle arms with guards for now - can add support for this later
|
// Don't try to handle arms with guards for now - can add support for this later
|
||||||
if current_arm.guard().is_some() {
|
if current_arm.guard().is_some() {
|
||||||
|
@ -70,7 +70,7 @@ pub(crate) fn merge_match_arms(ctx: AssistCtx) -> Option<Assist> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.add_assist(AssistId("merge_match_arms"), "Merge match arms", current_text_range, |edit| {
|
acc.add(AssistId("merge_match_arms"), "Merge match arms", current_text_range, |edit| {
|
||||||
let pats = if arms_to_merge.iter().any(contains_placeholder) {
|
let pats = if arms_to_merge.iter().any(contains_placeholder) {
|
||||||
"_".into()
|
"_".into()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -5,7 +5,7 @@ use ra_syntax::{
|
||||||
T,
|
T,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Assist, AssistCtx, AssistId};
|
use crate::{AssistContext, AssistId, Assists};
|
||||||
|
|
||||||
// Assist: move_bounds_to_where_clause
|
// Assist: move_bounds_to_where_clause
|
||||||
//
|
//
|
||||||
|
@ -22,7 +22,7 @@ use crate::{Assist, AssistCtx, AssistId};
|
||||||
// f(x)
|
// f(x)
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn move_bounds_to_where_clause(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let type_param_list = ctx.find_node_at_offset::<ast::TypeParamList>()?;
|
let type_param_list = ctx.find_node_at_offset::<ast::TypeParamList>()?;
|
||||||
|
|
||||||
let mut type_params = type_param_list.type_params();
|
let mut type_params = type_param_list.type_params();
|
||||||
|
@ -50,36 +50,29 @@ pub(crate) fn move_bounds_to_where_clause(ctx: AssistCtx) -> Option<Assist> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let target = type_param_list.syntax().text_range();
|
let target = type_param_list.syntax().text_range();
|
||||||
ctx.add_assist(
|
acc.add(AssistId("move_bounds_to_where_clause"), "Move to where clause", target, |edit| {
|
||||||
AssistId("move_bounds_to_where_clause"),
|
let new_params = type_param_list
|
||||||
"Move to where clause",
|
.type_params()
|
||||||
target,
|
.filter(|it| it.type_bound_list().is_some())
|
||||||
|edit| {
|
.map(|type_param| {
|
||||||
let new_params = type_param_list
|
let without_bounds = type_param.remove_bounds();
|
||||||
.type_params()
|
(type_param, without_bounds)
|
||||||
.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);
|
let new_type_param_list = type_param_list.replace_descendants(new_params);
|
||||||
edit.replace_ast(type_param_list.clone(), new_type_param_list);
|
edit.replace_ast(type_param_list.clone(), new_type_param_list);
|
||||||
|
|
||||||
let where_clause = {
|
let where_clause = {
|
||||||
let predicates = type_param_list.type_params().filter_map(build_predicate);
|
let predicates = type_param_list.type_params().filter_map(build_predicate);
|
||||||
make::where_clause(predicates)
|
make::where_clause(predicates)
|
||||||
};
|
};
|
||||||
|
|
||||||
let to_insert = match anchor.prev_sibling_or_token() {
|
let to_insert = match anchor.prev_sibling_or_token() {
|
||||||
Some(ref elem) if elem.kind() == WHITESPACE => {
|
Some(ref elem) if elem.kind() == WHITESPACE => format!("{} ", where_clause.syntax()),
|
||||||
format!("{} ", where_clause.syntax())
|
_ => format!(" {}", where_clause.syntax()),
|
||||||
}
|
};
|
||||||
_ => format!(" {}", where_clause.syntax()),
|
edit.insert(anchor.text_range().start(), to_insert);
|
||||||
};
|
})
|
||||||
edit.insert(anchor.text_range().start(), to_insert);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> {
|
fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> {
|
||||||
|
|
|
@ -4,7 +4,7 @@ use ra_syntax::{
|
||||||
TextSize,
|
TextSize,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Assist, AssistCtx, AssistId};
|
use crate::{AssistContext, AssistId, Assists};
|
||||||
|
|
||||||
// Assist: move_guard_to_arm_body
|
// Assist: move_guard_to_arm_body
|
||||||
//
|
//
|
||||||
|
@ -31,7 +31,7 @@ use crate::{Assist, AssistCtx, AssistId};
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn move_guard_to_arm_body(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let match_arm = ctx.find_node_at_offset::<MatchArm>()?;
|
let match_arm = ctx.find_node_at_offset::<MatchArm>()?;
|
||||||
let guard = match_arm.guard()?;
|
let guard = match_arm.guard()?;
|
||||||
let space_before_guard = guard.syntax().prev_sibling_or_token();
|
let space_before_guard = guard.syntax().prev_sibling_or_token();
|
||||||
|
@ -41,7 +41,7 @@ pub(crate) fn move_guard_to_arm_body(ctx: AssistCtx) -> Option<Assist> {
|
||||||
let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text());
|
let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text());
|
||||||
|
|
||||||
let target = guard.syntax().text_range();
|
let target = guard.syntax().text_range();
|
||||||
ctx.add_assist(AssistId("move_guard_to_arm_body"), "Move guard to arm body", target, |edit| {
|
acc.add(AssistId("move_guard_to_arm_body"), "Move guard to arm body", target, |edit| {
|
||||||
let offseting_amount = match space_before_guard.and_then(|it| it.into_token()) {
|
let offseting_amount = match space_before_guard.and_then(|it| it.into_token()) {
|
||||||
Some(tok) => {
|
Some(tok) => {
|
||||||
if ast::Whitespace::cast(tok.clone()).is_some() {
|
if ast::Whitespace::cast(tok.clone()).is_some() {
|
||||||
|
@ -88,7 +88,7 @@ pub(crate) fn move_guard_to_arm_body(ctx: AssistCtx) -> Option<Assist> {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?;
|
let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?;
|
||||||
let match_pat = match_arm.pat()?;
|
let match_pat = match_arm.pat()?;
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx) -> Option<Assist> {
|
||||||
let buf = format!(" if {}", cond.syntax().text());
|
let buf = format!(" if {}", cond.syntax().text());
|
||||||
|
|
||||||
let target = if_expr.syntax().text_range();
|
let target = if_expr.syntax().text_range();
|
||||||
ctx.add_assist(
|
acc.add(
|
||||||
AssistId("move_arm_cond_to_match_guard"),
|
AssistId("move_arm_cond_to_match_guard"),
|
||||||
"Move condition to match guard",
|
"Move condition to match guard",
|
||||||
target,
|
target,
|
||||||
|
|
|
@ -5,7 +5,7 @@ use ra_syntax::{
|
||||||
TextSize,
|
TextSize,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Assist, AssistCtx, AssistId};
|
use crate::{AssistContext, AssistId, Assists};
|
||||||
|
|
||||||
// Assist: make_raw_string
|
// Assist: make_raw_string
|
||||||
//
|
//
|
||||||
|
@ -22,11 +22,11 @@ use crate::{Assist, AssistCtx, AssistId};
|
||||||
// r#"Hello, World!"#;
|
// r#"Hello, World!"#;
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn make_raw_string(ctx: AssistCtx) -> Option<Assist> {
|
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 token = ctx.find_token_at_offset(STRING).and_then(ast::String::cast)?;
|
||||||
let value = token.value()?;
|
let value = token.value()?;
|
||||||
let target = token.syntax().text_range();
|
let target = token.syntax().text_range();
|
||||||
ctx.add_assist(AssistId("make_raw_string"), "Rewrite as raw string", target, |edit| {
|
acc.add(AssistId("make_raw_string"), "Rewrite as raw string", target, |edit| {
|
||||||
let max_hash_streak = count_hashes(&value);
|
let max_hash_streak = count_hashes(&value);
|
||||||
let mut hashes = String::with_capacity(max_hash_streak + 1);
|
let mut hashes = String::with_capacity(max_hash_streak + 1);
|
||||||
for _ in 0..hashes.capacity() {
|
for _ in 0..hashes.capacity() {
|
||||||
|
@ -51,11 +51,11 @@ pub(crate) fn make_raw_string(ctx: AssistCtx) -> Option<Assist> {
|
||||||
// "Hello, \"World!\"";
|
// "Hello, \"World!\"";
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn make_usual_string(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let token = ctx.find_token_at_offset(RAW_STRING).and_then(ast::RawString::cast)?;
|
let token = ctx.find_token_at_offset(RAW_STRING).and_then(ast::RawString::cast)?;
|
||||||
let value = token.value()?;
|
let value = token.value()?;
|
||||||
let target = token.syntax().text_range();
|
let target = token.syntax().text_range();
|
||||||
ctx.add_assist(AssistId("make_usual_string"), "Rewrite as regular string", target, |edit| {
|
acc.add(AssistId("make_usual_string"), "Rewrite as regular string", target, |edit| {
|
||||||
// parse inside string to escape `"`
|
// parse inside string to escape `"`
|
||||||
let escaped = value.escape_default().to_string();
|
let escaped = value.escape_default().to_string();
|
||||||
edit.replace(token.syntax().text_range(), format!("\"{}\"", escaped));
|
edit.replace(token.syntax().text_range(), format!("\"{}\"", escaped));
|
||||||
|
@ -77,10 +77,10 @@ pub(crate) fn make_usual_string(ctx: AssistCtx) -> Option<Assist> {
|
||||||
// r##"Hello, World!"##;
|
// r##"Hello, World!"##;
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn add_hash(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let token = ctx.find_token_at_offset(RAW_STRING)?;
|
let token = ctx.find_token_at_offset(RAW_STRING)?;
|
||||||
let target = token.text_range();
|
let target = token.text_range();
|
||||||
ctx.add_assist(AssistId("add_hash"), "Add # to raw string", target, |edit| {
|
acc.add(AssistId("add_hash"), "Add # to raw string", target, |edit| {
|
||||||
edit.insert(token.text_range().start() + TextSize::of('r'), "#");
|
edit.insert(token.text_range().start() + TextSize::of('r'), "#");
|
||||||
edit.insert(token.text_range().end(), "#");
|
edit.insert(token.text_range().end(), "#");
|
||||||
})
|
})
|
||||||
|
@ -101,7 +101,7 @@ pub(crate) fn add_hash(ctx: AssistCtx) -> Option<Assist> {
|
||||||
// r"Hello, World!";
|
// r"Hello, World!";
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn remove_hash(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let token = ctx.find_token_at_offset(RAW_STRING)?;
|
let token = ctx.find_token_at_offset(RAW_STRING)?;
|
||||||
let text = token.text().as_str();
|
let text = token.text().as_str();
|
||||||
if text.starts_with("r\"") {
|
if text.starts_with("r\"") {
|
||||||
|
@ -109,7 +109,7 @@ pub(crate) fn remove_hash(ctx: AssistCtx) -> Option<Assist> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let target = token.text_range();
|
let target = token.text_range();
|
||||||
ctx.add_assist(AssistId("remove_hash"), "Remove hash from raw string", target, |edit| {
|
acc.add(AssistId("remove_hash"), "Remove hash from raw string", target, |edit| {
|
||||||
let result = &text[2..text.len() - 1];
|
let result = &text[2..text.len() - 1];
|
||||||
let result = if result.starts_with('\"') {
|
let result = if result.starts_with('\"') {
|
||||||
// FIXME: this logic is wrong, not only the last has has to handled specially
|
// FIXME: this logic is wrong, not only the last has has to handled specially
|
||||||
|
|
|
@ -3,7 +3,7 @@ use ra_syntax::{
|
||||||
TextSize, T,
|
TextSize, T,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Assist, AssistCtx, AssistId};
|
use crate::{AssistContext, AssistId, Assists};
|
||||||
|
|
||||||
// Assist: remove_dbg
|
// Assist: remove_dbg
|
||||||
//
|
//
|
||||||
|
@ -20,7 +20,7 @@ use crate::{Assist, AssistCtx, AssistId};
|
||||||
// 92;
|
// 92;
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn remove_dbg(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?;
|
let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?;
|
||||||
|
|
||||||
if !is_valid_macrocall(¯o_call, "dbg")? {
|
if !is_valid_macrocall(¯o_call, "dbg")? {
|
||||||
|
@ -58,7 +58,7 @@ pub(crate) fn remove_dbg(ctx: AssistCtx) -> Option<Assist> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let target = macro_call.syntax().text_range();
|
let target = macro_call.syntax().text_range();
|
||||||
ctx.add_assist(AssistId("remove_dbg"), "Remove dbg!()", target, |edit| {
|
acc.add(AssistId("remove_dbg"), "Remove dbg!()", target, |edit| {
|
||||||
edit.replace(macro_range, macro_content);
|
edit.replace(macro_range, macro_content);
|
||||||
edit.set_cursor(cursor_pos);
|
edit.set_cursor(cursor_pos);
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ra_syntax::{SyntaxKind, TextRange, T};
|
use ra_syntax::{SyntaxKind, TextRange, T};
|
||||||
|
|
||||||
use crate::{Assist, AssistCtx, AssistId};
|
use crate::{AssistContext, AssistId, Assists};
|
||||||
|
|
||||||
// Assist: remove_mut
|
// Assist: remove_mut
|
||||||
//
|
//
|
||||||
|
@ -17,7 +17,7 @@ use crate::{Assist, AssistCtx, AssistId};
|
||||||
// fn feed(&self, amount: u32) {}
|
// fn feed(&self, amount: u32) {}
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn remove_mut(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn remove_mut(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let mut_token = ctx.find_token_at_offset(T![mut])?;
|
let mut_token = ctx.find_token_at_offset(T![mut])?;
|
||||||
let delete_from = mut_token.text_range().start();
|
let delete_from = mut_token.text_range().start();
|
||||||
let delete_to = match mut_token.next_token() {
|
let delete_to = match mut_token.next_token() {
|
||||||
|
@ -26,7 +26,7 @@ pub(crate) fn remove_mut(ctx: AssistCtx) -> Option<Assist> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let target = mut_token.text_range();
|
let target = mut_token.text_range();
|
||||||
ctx.add_assist(AssistId("remove_mut"), "Remove `mut` keyword", target, |edit| {
|
acc.add(AssistId("remove_mut"), "Remove `mut` keyword", target, |edit| {
|
||||||
edit.set_cursor(delete_from);
|
edit.set_cursor(delete_from);
|
||||||
edit.delete(TextRange::new(delete_from, delete_to));
|
edit.delete(TextRange::new(delete_from, delete_to));
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,18 +3,9 @@ use std::collections::HashMap;
|
||||||
use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct};
|
use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ra_ide_db::RootDatabase;
|
use ra_ide_db::RootDatabase;
|
||||||
use ra_syntax::{
|
use ra_syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode};
|
||||||
algo,
|
|
||||||
ast::{self, Path, RecordLit, RecordPat},
|
|
||||||
match_ast, AstNode, SyntaxKind,
|
|
||||||
SyntaxKind::*,
|
|
||||||
SyntaxNode,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{AssistContext, AssistId, Assists};
|
||||||
assist_ctx::{Assist, AssistCtx},
|
|
||||||
AssistId,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Assist: reorder_fields
|
// Assist: reorder_fields
|
||||||
//
|
//
|
||||||
|
@ -31,13 +22,13 @@ use crate::{
|
||||||
// const test: Foo = Foo {foo: 1, bar: 0}
|
// const test: Foo = Foo {foo: 1, bar: 0}
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
pub(crate) fn reorder_fields(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
reorder::<RecordLit>(ctx.clone()).or_else(|| reorder::<RecordPat>(ctx))
|
reorder::<ast::RecordLit>(acc, ctx.clone()).or_else(|| reorder::<ast::RecordPat>(acc, ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reorder<R: AstNode>(ctx: AssistCtx) -> Option<Assist> {
|
fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let record = ctx.find_node_at_offset::<R>()?;
|
let record = ctx.find_node_at_offset::<R>()?;
|
||||||
let path = record.syntax().children().find_map(Path::cast)?;
|
let path = record.syntax().children().find_map(ast::Path::cast)?;
|
||||||
|
|
||||||
let ranks = compute_fields_ranks(&path, &ctx)?;
|
let ranks = compute_fields_ranks(&path, &ctx)?;
|
||||||
|
|
||||||
|
@ -51,7 +42,7 @@ fn reorder<R: AstNode>(ctx: AssistCtx) -> Option<Assist> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let target = record.syntax().text_range();
|
let target = record.syntax().text_range();
|
||||||
ctx.add_assist(AssistId("reorder_fields"), "Reorder record fields", target, |edit| {
|
acc.add(AssistId("reorder_fields"), "Reorder record fields", target, |edit| {
|
||||||
for (old, new) in fields.iter().zip(&sorted_fields) {
|
for (old, new) in fields.iter().zip(&sorted_fields) {
|
||||||
algo::diff(old, new).into_text_edit(edit.text_edit_builder());
|
algo::diff(old, new).into_text_edit(edit.text_edit_builder());
|
||||||
}
|
}
|
||||||
|
@ -96,9 +87,9 @@ fn struct_definition(path: &ast::Path, sema: &Semantics<RootDatabase>) -> Option
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_fields_ranks(path: &Path, ctx: &AssistCtx) -> Option<HashMap<String, usize>> {
|
fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<HashMap<String, usize>> {
|
||||||
Some(
|
Some(
|
||||||
struct_definition(path, ctx.sema)?
|
struct_definition(path, &ctx.sema)?
|
||||||
.fields(ctx.db)
|
.fields(ctx.db)
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
|
|
@ -4,7 +4,7 @@ use ra_syntax::{
|
||||||
AstNode,
|
AstNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{utils::TryEnum, Assist, AssistCtx, AssistId};
|
use crate::{utils::TryEnum, AssistContext, AssistId, Assists};
|
||||||
|
|
||||||
// Assist: replace_if_let_with_match
|
// Assist: replace_if_let_with_match
|
||||||
//
|
//
|
||||||
|
@ -32,7 +32,7 @@ use crate::{utils::TryEnum, Assist, AssistCtx, AssistId};
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
|
let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
|
||||||
let cond = if_expr.condition()?;
|
let cond = if_expr.condition()?;
|
||||||
let pat = cond.pat()?;
|
let pat = cond.pat()?;
|
||||||
|
@ -43,36 +43,31 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option<Assist> {
|
||||||
ast::ElseBranch::IfExpr(_) => return None,
|
ast::ElseBranch::IfExpr(_) => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let sema = ctx.sema;
|
|
||||||
let target = if_expr.syntax().text_range();
|
let target = if_expr.syntax().text_range();
|
||||||
ctx.add_assist(
|
acc.add(AssistId("replace_if_let_with_match"), "Replace with match", target, move |edit| {
|
||||||
AssistId("replace_if_let_with_match"),
|
let match_expr = {
|
||||||
"Replace with match",
|
let then_arm = {
|
||||||
target,
|
let then_expr = unwrap_trivial_block(then_block);
|
||||||
move |edit| {
|
make::match_arm(vec![pat.clone()], then_expr)
|
||||||
let match_expr = {
|
|
||||||
let then_arm = {
|
|
||||||
let then_expr = unwrap_trivial_block(then_block);
|
|
||||||
make::match_arm(vec![pat.clone()], then_expr)
|
|
||||||
};
|
|
||||||
let else_arm = {
|
|
||||||
let pattern = sema
|
|
||||||
.type_of_pat(&pat)
|
|
||||||
.and_then(|ty| TryEnum::from_ty(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)
|
|
||||||
};
|
|
||||||
make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm]))
|
|
||||||
};
|
};
|
||||||
|
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)
|
||||||
|
};
|
||||||
|
make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm]))
|
||||||
|
};
|
||||||
|
|
||||||
let match_expr = IndentLevel::from_node(if_expr.syntax()).increase_indent(match_expr);
|
let match_expr = IndentLevel::from_node(if_expr.syntax()).increase_indent(match_expr);
|
||||||
|
|
||||||
edit.set_cursor(if_expr.syntax().text_range().start());
|
edit.set_cursor(if_expr.syntax().text_range().start());
|
||||||
edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr);
|
edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr);
|
||||||
},
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -9,11 +9,7 @@ use ra_syntax::{
|
||||||
AstNode, T,
|
AstNode, T,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{utils::TryEnum, AssistContext, AssistId, Assists};
|
||||||
assist_ctx::{Assist, AssistCtx},
|
|
||||||
utils::TryEnum,
|
|
||||||
AssistId,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Assist: replace_let_with_if_let
|
// Assist: replace_let_with_if_let
|
||||||
//
|
//
|
||||||
|
@ -39,16 +35,16 @@ use crate::{
|
||||||
//
|
//
|
||||||
// fn compute() -> Option<i32> { None }
|
// fn compute() -> Option<i32> { None }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn replace_let_with_if_let(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let let_kw = ctx.find_token_at_offset(T![let])?;
|
let let_kw = ctx.find_token_at_offset(T![let])?;
|
||||||
let let_stmt = let_kw.ancestors().find_map(ast::LetStmt::cast)?;
|
let let_stmt = let_kw.ancestors().find_map(ast::LetStmt::cast)?;
|
||||||
let init = let_stmt.initializer()?;
|
let init = let_stmt.initializer()?;
|
||||||
let original_pat = let_stmt.pat()?;
|
let original_pat = let_stmt.pat()?;
|
||||||
let ty = ctx.sema.type_of_expr(&init)?;
|
let ty = ctx.sema.type_of_expr(&init)?;
|
||||||
let happy_variant = TryEnum::from_ty(ctx.sema, &ty).map(|it| it.happy_case());
|
let happy_variant = TryEnum::from_ty(&ctx.sema, &ty).map(|it| it.happy_case());
|
||||||
|
|
||||||
let target = let_kw.text_range();
|
let target = let_kw.text_range();
|
||||||
ctx.add_assist(AssistId("replace_let_with_if_let"), "Replace with if-let", target, |edit| {
|
acc.add(AssistId("replace_let_with_if_let"), "Replace with if-let", target, |edit| {
|
||||||
let with_placeholder: ast::Pat = match happy_variant {
|
let with_placeholder: ast::Pat = match happy_variant {
|
||||||
None => make::placeholder_pat().into(),
|
None => make::placeholder_pat().into(),
|
||||||
Some(var_name) => make::tuple_struct_pat(
|
Some(var_name) => make::tuple_struct_pat(
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
use hir;
|
use hir;
|
||||||
use ra_syntax::{ast, AstNode, SmolStr, TextRange};
|
use ra_syntax::{ast, AstNode, SmolStr, TextRange};
|
||||||
|
|
||||||
use crate::{
|
use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists};
|
||||||
assist_ctx::{Assist, AssistCtx},
|
|
||||||
utils::insert_use_statement,
|
|
||||||
AssistId,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Assist: replace_qualified_name_with_use
|
// Assist: replace_qualified_name_with_use
|
||||||
//
|
//
|
||||||
|
@ -20,7 +16,10 @@ use crate::{
|
||||||
//
|
//
|
||||||
// fn process(map: HashMap<String, String>) {}
|
// fn process(map: HashMap<String, String>) {}
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn replace_qualified_name_with_use(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn replace_qualified_name_with_use(
|
||||||
|
acc: &mut Assists,
|
||||||
|
ctx: &AssistContext,
|
||||||
|
) -> Option<()> {
|
||||||
let path: ast::Path = ctx.find_node_at_offset()?;
|
let path: ast::Path = ctx.find_node_at_offset()?;
|
||||||
// We don't want to mess with use statements
|
// We don't want to mess with use statements
|
||||||
if path.syntax().ancestors().find_map(ast::UseItem::cast).is_some() {
|
if path.syntax().ancestors().find_map(ast::UseItem::cast).is_some() {
|
||||||
|
@ -34,18 +33,18 @@ pub(crate) fn replace_qualified_name_with_use(ctx: AssistCtx) -> Option<Assist>
|
||||||
}
|
}
|
||||||
|
|
||||||
let target = path.syntax().text_range();
|
let target = path.syntax().text_range();
|
||||||
ctx.add_assist(
|
acc.add(
|
||||||
AssistId("replace_qualified_name_with_use"),
|
AssistId("replace_qualified_name_with_use"),
|
||||||
"Replace qualified path with use",
|
"Replace qualified path with use",
|
||||||
target,
|
target,
|
||||||
|edit| {
|
|builder| {
|
||||||
let path_to_import = hir_path.mod_path().clone();
|
let path_to_import = hir_path.mod_path().clone();
|
||||||
insert_use_statement(path.syntax(), &path_to_import, edit);
|
insert_use_statement(path.syntax(), &path_to_import, ctx, builder);
|
||||||
|
|
||||||
if let Some(last) = path.segment() {
|
if let Some(last) = path.segment() {
|
||||||
// Here we are assuming the assist will provide a correct use statement
|
// Here we are assuming the assist will provide a correct use statement
|
||||||
// so we can delete the path qualifier
|
// so we can delete the path qualifier
|
||||||
edit.delete(TextRange::new(
|
builder.delete(TextRange::new(
|
||||||
path.syntax().text_range().start(),
|
path.syntax().text_range().start(),
|
||||||
last.syntax().text_range().start(),
|
last.syntax().text_range().start(),
|
||||||
));
|
));
|
||||||
|
|
|
@ -5,7 +5,7 @@ use ra_syntax::{
|
||||||
AstNode,
|
AstNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{utils::TryEnum, Assist, AssistCtx, AssistId};
|
use crate::{utils::TryEnum, AssistContext, AssistId, Assists};
|
||||||
|
|
||||||
// Assist: replace_unwrap_with_match
|
// Assist: replace_unwrap_with_match
|
||||||
//
|
//
|
||||||
|
@ -29,7 +29,7 @@ use crate::{utils::TryEnum, Assist, AssistCtx, AssistId};
|
||||||
// };
|
// };
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn replace_unwrap_with_match(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
|
let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
|
||||||
let name = method_call.name_ref()?;
|
let name = method_call.name_ref()?;
|
||||||
if name.text() != "unwrap" {
|
if name.text() != "unwrap" {
|
||||||
|
@ -37,33 +37,26 @@ pub(crate) fn replace_unwrap_with_match(ctx: AssistCtx) -> Option<Assist> {
|
||||||
}
|
}
|
||||||
let caller = method_call.expr()?;
|
let caller = method_call.expr()?;
|
||||||
let ty = ctx.sema.type_of_expr(&caller)?;
|
let ty = ctx.sema.type_of_expr(&caller)?;
|
||||||
let happy_variant = TryEnum::from_ty(ctx.sema, &ty)?.happy_case();
|
let happy_variant = TryEnum::from_ty(&ctx.sema, &ty)?.happy_case();
|
||||||
let target = method_call.syntax().text_range();
|
let target = method_call.syntax().text_range();
|
||||||
ctx.add_assist(
|
acc.add(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", target, |edit| {
|
||||||
AssistId("replace_unwrap_with_match"),
|
let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant)));
|
||||||
"Replace unwrap with match",
|
let it = make::bind_pat(make::name("a")).into();
|
||||||
target,
|
let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into();
|
||||||
|edit| {
|
|
||||||
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 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 ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path));
|
||||||
|
|
||||||
let unreachable_call = make::unreachable_macro_call().into();
|
let unreachable_call = make::unreachable_macro_call().into();
|
||||||
let err_arm =
|
let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call);
|
||||||
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_arm_list = make::match_arm_list(vec![ok_arm, err_arm]);
|
||||||
let match_expr = make::expr_match(caller.clone(), match_arm_list);
|
let match_expr = make::expr_match(caller.clone(), match_arm_list);
|
||||||
let match_expr =
|
let match_expr = IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr);
|
||||||
IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr);
|
|
||||||
|
|
||||||
edit.set_cursor(caller.syntax().text_range().start());
|
edit.set_cursor(caller.syntax().text_range().start());
|
||||||
edit.replace_ast::<ast::Expr>(method_call.into(), match_expr);
|
edit.replace_ast::<ast::Expr>(method_call.into(), match_expr);
|
||||||
},
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::iter::successors;
|
||||||
|
|
||||||
use ra_syntax::{ast, AstNode, T};
|
use ra_syntax::{ast, AstNode, T};
|
||||||
|
|
||||||
use crate::{Assist, AssistCtx, AssistId};
|
use crate::{AssistContext, AssistId, Assists};
|
||||||
|
|
||||||
// Assist: split_import
|
// Assist: split_import
|
||||||
//
|
//
|
||||||
|
@ -15,7 +15,7 @@ use crate::{Assist, AssistCtx, AssistId};
|
||||||
// ```
|
// ```
|
||||||
// use std::{collections::HashMap};
|
// use std::{collections::HashMap};
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn split_import(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let colon_colon = ctx.find_token_at_offset(T![::])?;
|
let colon_colon = ctx.find_token_at_offset(T![::])?;
|
||||||
let path = ast::Path::cast(colon_colon.parent())?.qualifier()?;
|
let path = ast::Path::cast(colon_colon.parent())?.qualifier()?;
|
||||||
let top_path = successors(Some(path.clone()), |it| it.parent_path()).last()?;
|
let top_path = successors(Some(path.clone()), |it| it.parent_path()).last()?;
|
||||||
|
@ -29,7 +29,7 @@ pub(crate) fn split_import(ctx: AssistCtx) -> Option<Assist> {
|
||||||
let cursor = ctx.frange.range.start();
|
let cursor = ctx.frange.range.start();
|
||||||
|
|
||||||
let target = colon_colon.text_range();
|
let target = colon_colon.text_range();
|
||||||
ctx.add_assist(AssistId("split_import"), "Split import", target, |edit| {
|
acc.add(AssistId("split_import"), "Split import", target, |edit| {
|
||||||
edit.replace_ast(use_tree, new_tree);
|
edit.replace_ast(use_tree, new_tree);
|
||||||
edit.set_cursor(cursor);
|
edit.set_cursor(cursor);
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{Assist, AssistCtx, AssistId};
|
use crate::{AssistContext, AssistId, Assists};
|
||||||
|
|
||||||
use ast::LoopBodyOwner;
|
use ast::LoopBodyOwner;
|
||||||
use ra_fmt::unwrap_trivial_block;
|
use ra_fmt::unwrap_trivial_block;
|
||||||
|
@ -21,7 +21,7 @@ use ra_syntax::{ast, match_ast, AstNode, TextRange, T};
|
||||||
// println!("foo");
|
// println!("foo");
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option<Assist> {
|
pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let l_curly_token = ctx.find_token_at_offset(T!['{'])?;
|
let l_curly_token = ctx.find_token_at_offset(T!['{'])?;
|
||||||
let block = ast::BlockExpr::cast(l_curly_token.parent())?;
|
let block = ast::BlockExpr::cast(l_curly_token.parent())?;
|
||||||
let parent = block.syntax().parent()?;
|
let parent = block.syntax().parent()?;
|
||||||
|
@ -58,7 +58,7 @@ pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option<Assist> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let target = expr_to_unwrap.syntax().text_range();
|
let target = expr_to_unwrap.syntax().text_range();
|
||||||
ctx.add_assist(AssistId("unwrap_block"), "Unwrap block", target, |edit| {
|
acc.add(AssistId("unwrap_block"), "Unwrap block", target, |edit| {
|
||||||
edit.set_cursor(expr.syntax().text_range().start());
|
edit.set_cursor(expr.syntax().text_range().start());
|
||||||
|
|
||||||
let pat_start: &[_] = &[' ', '{', '\n'];
|
let pat_start: &[_] = &[' ', '{', '\n'];
|
||||||
|
|
|
@ -10,7 +10,7 @@ macro_rules! eprintln {
|
||||||
($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
|
($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
|
||||||
}
|
}
|
||||||
|
|
||||||
mod assist_ctx;
|
mod assist_context;
|
||||||
mod marks;
|
mod marks;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
@ -22,7 +22,7 @@ use ra_db::FileRange;
|
||||||
use ra_ide_db::{source_change::SourceChange, RootDatabase};
|
use ra_ide_db::{source_change::SourceChange, RootDatabase};
|
||||||
use ra_syntax::TextRange;
|
use ra_syntax::TextRange;
|
||||||
|
|
||||||
pub(crate) use crate::assist_ctx::{Assist, AssistCtx};
|
pub(crate) use crate::assist_context::{AssistContext, Assists};
|
||||||
|
|
||||||
/// Unique identifier of the assist, should not be shown to the user
|
/// Unique identifier of the assist, should not be shown to the user
|
||||||
/// directly.
|
/// directly.
|
||||||
|
@ -68,13 +68,12 @@ pub struct ResolvedAssist {
|
||||||
/// returned, without actual edits.
|
/// returned, without actual edits.
|
||||||
pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> {
|
pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> {
|
||||||
let sema = Semantics::new(db);
|
let sema = Semantics::new(db);
|
||||||
let ctx = AssistCtx::new(&sema, range, false);
|
let ctx = AssistContext::new(sema, range);
|
||||||
handlers::all()
|
let mut acc = Assists::new_unresolved(&ctx);
|
||||||
.iter()
|
handlers::all().iter().for_each(|handler| {
|
||||||
.filter_map(|f| f(ctx.clone()))
|
handler(&mut acc, &ctx);
|
||||||
.flat_map(|it| it.0)
|
});
|
||||||
.map(|a| a.label)
|
acc.finish_unresolved()
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return all the assists applicable at the given position.
|
/// Return all the assists applicable at the given position.
|
||||||
|
@ -83,31 +82,30 @@ pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabe
|
||||||
/// computed.
|
/// computed.
|
||||||
pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> {
|
pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> {
|
||||||
let sema = Semantics::new(db);
|
let sema = Semantics::new(db);
|
||||||
let ctx = AssistCtx::new(&sema, range, true);
|
let ctx = AssistContext::new(sema, range);
|
||||||
let mut a = handlers::all()
|
let mut acc = Assists::new_resolved(&ctx);
|
||||||
.iter()
|
handlers::all().iter().for_each(|handler| {
|
||||||
.filter_map(|f| f(ctx.clone()))
|
handler(&mut acc, &ctx);
|
||||||
.flat_map(|it| it.0)
|
});
|
||||||
.map(|it| it.into_resolved().unwrap())
|
acc.finish_resolved()
|
||||||
.collect::<Vec<_>>();
|
|
||||||
a.sort_by_key(|it| it.label.target.len());
|
|
||||||
a
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mod handlers {
|
mod handlers {
|
||||||
use crate::{Assist, AssistCtx};
|
use crate::{AssistContext, Assists};
|
||||||
|
|
||||||
pub(crate) type Handler = fn(AssistCtx) -> Option<Assist>;
|
pub(crate) type Handler = fn(&mut Assists, &AssistContext) -> Option<()>;
|
||||||
|
|
||||||
mod add_custom_impl;
|
mod add_custom_impl;
|
||||||
mod add_derive;
|
mod add_derive;
|
||||||
mod add_explicit_type;
|
mod add_explicit_type;
|
||||||
|
mod add_from_impl_for_enum;
|
||||||
mod add_function;
|
mod add_function;
|
||||||
mod add_impl;
|
mod add_impl;
|
||||||
mod add_missing_impl_members;
|
mod add_missing_impl_members;
|
||||||
mod add_new;
|
mod add_new;
|
||||||
mod apply_demorgan;
|
mod apply_demorgan;
|
||||||
mod auto_import;
|
mod auto_import;
|
||||||
|
mod change_return_type_to_result;
|
||||||
mod change_visibility;
|
mod change_visibility;
|
||||||
mod early_return;
|
mod early_return;
|
||||||
mod fill_match_arms;
|
mod fill_match_arms;
|
||||||
|
@ -124,14 +122,12 @@ mod handlers {
|
||||||
mod raw_string;
|
mod raw_string;
|
||||||
mod remove_dbg;
|
mod remove_dbg;
|
||||||
mod remove_mut;
|
mod remove_mut;
|
||||||
|
mod reorder_fields;
|
||||||
mod replace_if_let_with_match;
|
mod replace_if_let_with_match;
|
||||||
mod replace_let_with_if_let;
|
mod replace_let_with_if_let;
|
||||||
mod replace_qualified_name_with_use;
|
mod replace_qualified_name_with_use;
|
||||||
mod replace_unwrap_with_match;
|
mod replace_unwrap_with_match;
|
||||||
mod split_import;
|
mod split_import;
|
||||||
mod change_return_type_to_result;
|
|
||||||
mod add_from_impl_for_enum;
|
|
||||||
mod reorder_fields;
|
|
||||||
mod unwrap_block;
|
mod unwrap_block;
|
||||||
|
|
||||||
pub(crate) fn all() -> &'static [Handler] {
|
pub(crate) fn all() -> &'static [Handler] {
|
||||||
|
|
|
@ -11,7 +11,7 @@ use test_utils::{
|
||||||
RangeOrOffset,
|
RangeOrOffset,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{handlers::Handler, resolved_assists, AssistCtx};
|
use crate::{handlers::Handler, resolved_assists, AssistContext, Assists};
|
||||||
|
|
||||||
pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
|
pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
|
||||||
let (mut db, file_id) = RootDatabase::with_single_file(text);
|
let (mut db, file_id) = RootDatabase::with_single_file(text);
|
||||||
|
@ -71,7 +71,7 @@ enum ExpectedResult<'a> {
|
||||||
Target(&'a str),
|
Target(&'a str),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check(assist: Handler, before: &str, expected: ExpectedResult) {
|
fn check(handler: Handler, before: &str, expected: ExpectedResult) {
|
||||||
let (text_without_caret, file_with_caret_id, range_or_offset, db) = if before.contains("//-") {
|
let (text_without_caret, file_with_caret_id, range_or_offset, db) = if before.contains("//-") {
|
||||||
let (mut db, position) = RootDatabase::with_position(before);
|
let (mut db, position) = RootDatabase::with_position(before);
|
||||||
db.set_local_roots(Arc::new(vec![db.file_source_root(position.file_id)]));
|
db.set_local_roots(Arc::new(vec![db.file_source_root(position.file_id)]));
|
||||||
|
@ -90,17 +90,20 @@ fn check(assist: Handler, before: &str, expected: ExpectedResult) {
|
||||||
let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() };
|
let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() };
|
||||||
|
|
||||||
let sema = Semantics::new(&db);
|
let sema = Semantics::new(&db);
|
||||||
let assist_ctx = AssistCtx::new(&sema, frange, true);
|
let ctx = AssistContext::new(sema, frange);
|
||||||
|
let mut acc = Assists::new_resolved(&ctx);
|
||||||
match (assist(assist_ctx), expected) {
|
handler(&mut acc, &ctx);
|
||||||
|
let mut res = acc.finish_resolved();
|
||||||
|
let assist = res.pop();
|
||||||
|
match (assist, expected) {
|
||||||
(Some(assist), ExpectedResult::After(after)) => {
|
(Some(assist), ExpectedResult::After(after)) => {
|
||||||
let mut action = assist.0[0].source_change.clone().unwrap();
|
let mut source_change = assist.source_change;
|
||||||
let change = action.source_file_edits.pop().unwrap();
|
let change = source_change.source_file_edits.pop().unwrap();
|
||||||
|
|
||||||
let mut actual = db.file_text(change.file_id).as_ref().to_owned();
|
let mut actual = db.file_text(change.file_id).as_ref().to_owned();
|
||||||
change.edit.apply(&mut actual);
|
change.edit.apply(&mut actual);
|
||||||
|
|
||||||
match action.cursor_position {
|
match source_change.cursor_position {
|
||||||
None => {
|
None => {
|
||||||
if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset {
|
if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset {
|
||||||
let off = change
|
let off = change
|
||||||
|
@ -116,7 +119,7 @@ fn check(assist: Handler, before: &str, expected: ExpectedResult) {
|
||||||
assert_eq_text!(after, &actual);
|
assert_eq_text!(after, &actual);
|
||||||
}
|
}
|
||||||
(Some(assist), ExpectedResult::Target(target)) => {
|
(Some(assist), ExpectedResult::Target(target)) => {
|
||||||
let range = assist.0[0].label.target;
|
let range = assist.label.target;
|
||||||
assert_eq_text!(&text_without_caret[range], target);
|
assert_eq_text!(&text_without_caret[range], target);
|
||||||
}
|
}
|
||||||
(Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"),
|
(Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"),
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
// FIXME: rewrite according to the plan, outlined in
|
// FIXME: rewrite according to the plan, outlined in
|
||||||
// https://github.com/rust-analyzer/rust-analyzer/issues/3301#issuecomment-592931553
|
// https://github.com/rust-analyzer/rust-analyzer/issues/3301#issuecomment-592931553
|
||||||
|
|
||||||
use crate::assist_ctx::ActionBuilder;
|
|
||||||
use hir::{self, ModPath};
|
use hir::{self, ModPath};
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
ast::{self, NameOwner},
|
ast::{self, NameOwner},
|
||||||
|
@ -12,6 +11,8 @@ use ra_syntax::{
|
||||||
};
|
};
|
||||||
use ra_text_edit::TextEditBuilder;
|
use ra_text_edit::TextEditBuilder;
|
||||||
|
|
||||||
|
use crate::assist_context::{AssistBuilder, AssistContext};
|
||||||
|
|
||||||
/// Creates and inserts a use statement for the given path to import.
|
/// Creates and inserts a use statement for the given path to import.
|
||||||
/// The use statement is inserted in the scope most appropriate to the
|
/// The use statement is inserted in the scope most appropriate to the
|
||||||
/// the cursor position given, additionally merged with the existing use imports.
|
/// the cursor position given, additionally merged with the existing use imports.
|
||||||
|
@ -19,10 +20,11 @@ pub(crate) fn insert_use_statement(
|
||||||
// Ideally the position of the cursor, used to
|
// Ideally the position of the cursor, used to
|
||||||
position: &SyntaxNode,
|
position: &SyntaxNode,
|
||||||
path_to_import: &ModPath,
|
path_to_import: &ModPath,
|
||||||
edit: &mut ActionBuilder,
|
ctx: &AssistContext,
|
||||||
|
builder: &mut AssistBuilder,
|
||||||
) {
|
) {
|
||||||
let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::<Vec<_>>();
|
let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::<Vec<_>>();
|
||||||
let container = edit.ctx().sema.ancestors_with_macros(position.clone()).find_map(|n| {
|
let container = ctx.sema.ancestors_with_macros(position.clone()).find_map(|n| {
|
||||||
if let Some(module) = ast::Module::cast(n.clone()) {
|
if let Some(module) = ast::Module::cast(n.clone()) {
|
||||||
return module.item_list().map(|it| it.syntax().clone());
|
return module.item_list().map(|it| it.syntax().clone());
|
||||||
}
|
}
|
||||||
|
@ -31,7 +33,7 @@ pub(crate) fn insert_use_statement(
|
||||||
|
|
||||||
if let Some(container) = container {
|
if let Some(container) = container {
|
||||||
let action = best_action_for_target(container, position.clone(), &target);
|
let action = best_action_for_target(container, position.clone(), &target);
|
||||||
make_assist(&action, &target, edit.text_edit_builder());
|
make_assist(&action, &target, builder.text_edit_builder());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue