mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-29 05:15:04 +00:00
migrate ra_assists to the new AST
This commit is contained in:
parent
e2b28f5bb8
commit
0343c4a815
17 changed files with 213 additions and 204 deletions
|
@ -9,7 +9,7 @@ use crate::{Assist, AssistCtx, AssistId};
|
||||||
|
|
||||||
pub(crate) fn add_derive(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
pub(crate) fn add_derive(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||||
let nominal = ctx.node_at_offset::<ast::NominalDef>()?;
|
let nominal = ctx.node_at_offset::<ast::NominalDef>()?;
|
||||||
let node_start = derive_insertion_offset(nominal)?;
|
let node_start = derive_insertion_offset(&nominal)?;
|
||||||
ctx.add_action(AssistId("add_derive"), "add `#[derive]`", |edit| {
|
ctx.add_action(AssistId("add_derive"), "add `#[derive]`", |edit| {
|
||||||
let derive_attr = nominal
|
let derive_attr = nominal
|
||||||
.attrs()
|
.attrs()
|
||||||
|
|
|
@ -27,7 +27,7 @@ pub(crate) fn add_explicit_type(mut ctx: AssistCtx<impl HirDatabase>) -> Option<
|
||||||
// Infer type
|
// Infer type
|
||||||
let db = ctx.db;
|
let db = ctx.db;
|
||||||
let analyzer = hir::SourceAnalyzer::new(db, ctx.frange.file_id, stmt.syntax(), None);
|
let analyzer = hir::SourceAnalyzer::new(db, ctx.frange.file_id, stmt.syntax(), None);
|
||||||
let ty = analyzer.type_of(db, expr)?;
|
let ty = analyzer.type_of(db, &expr)?;
|
||||||
// Assist not applicable if the type is unknown
|
// Assist not applicable if the type is unknown
|
||||||
if is_unknown(&ty) {
|
if is_unknown(&ty) {
|
||||||
return None;
|
return None;
|
||||||
|
|
|
@ -16,7 +16,7 @@ pub(crate) fn add_impl(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||||
let start_offset = nominal.syntax().range().end();
|
let start_offset = nominal.syntax().range().end();
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
buf.push_str("\n\nimpl");
|
buf.push_str("\n\nimpl");
|
||||||
if let Some(type_params) = type_params {
|
if let Some(type_params) = &type_params {
|
||||||
type_params.syntax().text().push_to(&mut buf);
|
type_params.syntax().text().push_to(&mut buf);
|
||||||
}
|
}
|
||||||
buf.push_str(" ");
|
buf.push_str(" ");
|
||||||
|
@ -25,9 +25,9 @@ pub(crate) fn add_impl(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||||
let lifetime_params = type_params
|
let lifetime_params = type_params
|
||||||
.lifetime_params()
|
.lifetime_params()
|
||||||
.filter_map(|it| it.lifetime_token())
|
.filter_map(|it| it.lifetime_token())
|
||||||
.map(|it| it.text());
|
.map(|it| it.text().clone());
|
||||||
let type_params =
|
let type_params =
|
||||||
type_params.type_params().filter_map(|it| it.name()).map(|it| it.text());
|
type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone());
|
||||||
join(lifetime_params.chain(type_params)).surround_with("<", ">").to_buf(&mut buf);
|
join(lifetime_params.chain(type_params)).surround_with("<", ">").to_buf(&mut buf);
|
||||||
}
|
}
|
||||||
buf.push_str(" {\n");
|
buf.push_str(" {\n");
|
||||||
|
|
|
@ -5,8 +5,8 @@ use crate::{
|
||||||
|
|
||||||
use hir::{db::HirDatabase, HasSource};
|
use hir::{db::HirDatabase, HasSource};
|
||||||
use ra_db::FilePosition;
|
use ra_db::FilePosition;
|
||||||
use ra_syntax::ast::{self, AstNode, ImplItem, ImplItemKind, NameOwner};
|
use ra_syntax::ast::{self, AstNode, ImplItemKind, NameOwner};
|
||||||
use ra_syntax::{SmolStr, TreeArc};
|
use ra_syntax::SmolStr;
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
enum AddMissingImplMembersMode {
|
enum AddMissingImplMembersMode {
|
||||||
|
@ -46,16 +46,16 @@ fn add_missing_impl_members_inner(
|
||||||
let position = FilePosition { file_id, offset: impl_node.syntax().range().start() };
|
let position = FilePosition { file_id, offset: impl_node.syntax().range().start() };
|
||||||
let analyzer = hir::SourceAnalyzer::new(ctx.db, position.file_id, impl_node.syntax(), None);
|
let analyzer = hir::SourceAnalyzer::new(ctx.db, position.file_id, impl_node.syntax(), None);
|
||||||
|
|
||||||
resolve_target_trait_def(ctx.db, &analyzer, impl_node)?
|
resolve_target_trait_def(ctx.db, &analyzer, &impl_node)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let def_name = |kind| -> Option<&SmolStr> {
|
let def_name = |kind| -> Option<SmolStr> {
|
||||||
match kind {
|
match kind {
|
||||||
ImplItemKind::FnDef(def) => def.name(),
|
ast::ImplItemKind::FnDef(def) => def.name(),
|
||||||
ImplItemKind::TypeAliasDef(def) => def.name(),
|
ast::ImplItemKind::TypeAliasDef(def) => def.name(),
|
||||||
ImplItemKind::ConstDef(def) => def.name(),
|
ast::ImplItemKind::ConstDef(def) => def.name(),
|
||||||
}
|
}
|
||||||
.map(ast::Name::text)
|
.map(|it| it.text().clone())
|
||||||
};
|
};
|
||||||
|
|
||||||
let trait_items = trait_def.item_list()?.impl_items();
|
let trait_items = trait_def.item_list()?.impl_items();
|
||||||
|
@ -78,18 +78,13 @@ fn add_missing_impl_members_inner(
|
||||||
|
|
||||||
ctx.add_action(AssistId(assist_id), label, |edit| {
|
ctx.add_action(AssistId(assist_id), label, |edit| {
|
||||||
let n_existing_items = impl_item_list.impl_items().count();
|
let n_existing_items = impl_item_list.impl_items().count();
|
||||||
let items: Vec<_> = missing_items
|
let items = missing_items.into_iter().map(|it| match it.kind() {
|
||||||
.into_iter()
|
ImplItemKind::FnDef(def) => strip_docstring(add_body(def).into()),
|
||||||
.map(|it| match it.kind() {
|
|
||||||
ImplItemKind::FnDef(def) => {
|
|
||||||
strip_docstring(ImplItem::cast(add_body(def).syntax()).unwrap())
|
|
||||||
}
|
|
||||||
_ => strip_docstring(it),
|
_ => strip_docstring(it),
|
||||||
})
|
});
|
||||||
.collect();
|
|
||||||
let mut ast_editor = AstEditor::new(impl_item_list);
|
let mut ast_editor = AstEditor::new(impl_item_list);
|
||||||
|
|
||||||
ast_editor.append_items(items.iter().map(|it| &**it));
|
ast_editor.append_items(items);
|
||||||
|
|
||||||
let first_new_item = ast_editor.ast().impl_items().nth(n_existing_items).unwrap();
|
let first_new_item = ast_editor.ast().impl_items().nth(n_existing_items).unwrap();
|
||||||
let cursor_position = first_new_item.syntax().range().start();
|
let cursor_position = first_new_item.syntax().range().start();
|
||||||
|
@ -101,14 +96,14 @@ fn add_missing_impl_members_inner(
|
||||||
ctx.build()
|
ctx.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn strip_docstring(item: &ast::ImplItem) -> TreeArc<ast::ImplItem> {
|
fn strip_docstring(item: ast::ImplItem) -> ast::ImplItem {
|
||||||
let mut ast_editor = AstEditor::new(item);
|
let mut ast_editor = AstEditor::new(item);
|
||||||
ast_editor.strip_attrs_and_docs();
|
ast_editor.strip_attrs_and_docs();
|
||||||
ast_editor.ast().to_owned()
|
ast_editor.ast().to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_body(fn_def: &ast::FnDef) -> TreeArc<ast::FnDef> {
|
fn add_body(fn_def: ast::FnDef) -> ast::FnDef {
|
||||||
let mut ast_editor = AstEditor::new(fn_def);
|
let mut ast_editor = AstEditor::new(fn_def.clone());
|
||||||
if fn_def.body().is_none() {
|
if fn_def.body().is_none() {
|
||||||
ast_editor.set_body(&AstBuilder::<ast::Block>::single_expr(
|
ast_editor.set_body(&AstBuilder::<ast::Block>::single_expr(
|
||||||
&AstBuilder::<ast::Expr>::unimplemented(),
|
&AstBuilder::<ast::Expr>::unimplemented(),
|
||||||
|
@ -123,9 +118,12 @@ fn resolve_target_trait_def(
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
analyzer: &hir::SourceAnalyzer,
|
analyzer: &hir::SourceAnalyzer,
|
||||||
impl_block: &ast::ImplBlock,
|
impl_block: &ast::ImplBlock,
|
||||||
) -> Option<TreeArc<ast::TraitDef>> {
|
) -> Option<ast::TraitDef> {
|
||||||
let ast_path =
|
let ast_path = impl_block
|
||||||
impl_block.target_trait().map(AstNode::syntax).and_then(ast::PathType::cast)?.path()?;
|
.target_trait()
|
||||||
|
.map(|it| it.syntax().clone())
|
||||||
|
.and_then(ast::PathType::cast)?
|
||||||
|
.path()?;
|
||||||
|
|
||||||
match analyzer.resolve_path(db, &ast_path) {
|
match analyzer.resolve_path(db, &ast_path) {
|
||||||
Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def.source(db).ast),
|
Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def.source(db).ast),
|
||||||
|
|
|
@ -49,7 +49,7 @@ pub(crate) enum Assist {
|
||||||
pub(crate) struct AssistCtx<'a, DB> {
|
pub(crate) struct AssistCtx<'a, DB> {
|
||||||
pub(crate) db: &'a DB,
|
pub(crate) db: &'a DB,
|
||||||
pub(crate) frange: FileRange,
|
pub(crate) frange: FileRange,
|
||||||
source_file: &'a SourceFile,
|
source_file: SourceFile,
|
||||||
should_compute_edit: bool,
|
should_compute_edit: bool,
|
||||||
assist: Assist,
|
assist: Assist,
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ impl<'a, DB> Clone for AssistCtx<'a, DB> {
|
||||||
AssistCtx {
|
AssistCtx {
|
||||||
db: self.db,
|
db: self.db,
|
||||||
frange: self.frange,
|
frange: self.frange,
|
||||||
source_file: self.source_file,
|
source_file: self.source_file.clone(),
|
||||||
should_compute_edit: self.should_compute_edit,
|
should_compute_edit: self.should_compute_edit,
|
||||||
assist: self.assist.clone(),
|
assist: self.assist.clone(),
|
||||||
}
|
}
|
||||||
|
@ -104,18 +104,18 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> {
|
||||||
Some(self.assist)
|
Some(self.assist)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken<'a>> {
|
pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> {
|
||||||
find_token_at_offset(self.source_file.syntax(), self.frange.range.start())
|
find_token_at_offset(self.source_file.syntax(), self.frange.range.start())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn node_at_offset<N: AstNode>(&self) -> Option<&'a N> {
|
pub(crate) fn 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.frange.range.start())
|
||||||
}
|
}
|
||||||
pub(crate) fn covering_element(&self) -> SyntaxElement<'a> {
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement<'a> {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,7 @@ impl AssistBuilder {
|
||||||
) {
|
) {
|
||||||
let mut replace_with = replace_with.into();
|
let mut replace_with = replace_with.into();
|
||||||
if let Some(indent) = leading_indent(node) {
|
if let Some(indent) = leading_indent(node) {
|
||||||
replace_with = reindent(&replace_with, indent)
|
replace_with = reindent(&replace_with, &indent)
|
||||||
}
|
}
|
||||||
self.replace(node.range(), replace_with)
|
self.replace(node.range(), replace_with)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,18 +4,18 @@ use arrayvec::ArrayVec;
|
||||||
use hir::Name;
|
use hir::Name;
|
||||||
use ra_fmt::leading_indent;
|
use ra_fmt::leading_indent;
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
ast, AstNode, Direction, InsertPosition, SourceFile, SyntaxElement, SyntaxKind::*, TreeArc, T,
|
ast, AstNode, Direction, InsertPosition, SourceFile, SyntaxElement, SyntaxKind::*, T,
|
||||||
};
|
};
|
||||||
use ra_text_edit::TextEditBuilder;
|
use ra_text_edit::TextEditBuilder;
|
||||||
|
|
||||||
pub struct AstEditor<N: AstNode> {
|
pub struct AstEditor<N: AstNode> {
|
||||||
original_ast: TreeArc<N>,
|
original_ast: N,
|
||||||
ast: TreeArc<N>,
|
ast: N,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: AstNode> AstEditor<N> {
|
impl<N: AstNode> AstEditor<N> {
|
||||||
pub fn new(node: &N) -> AstEditor<N> {
|
pub fn new(node: N) -> AstEditor<N> {
|
||||||
AstEditor { original_ast: node.to_owned(), ast: node.to_owned() }
|
AstEditor { original_ast: node.clone(), ast: node }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_text_edit(self, builder: &mut TextEditBuilder) {
|
pub fn into_text_edit(self, builder: &mut TextEditBuilder) {
|
||||||
|
@ -26,27 +26,27 @@ impl<N: AstNode> AstEditor<N> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ast(&self) -> &N {
|
pub fn ast(&self) -> &N {
|
||||||
&*self.ast
|
&self.ast
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn insert_children<'a>(
|
fn insert_children(
|
||||||
&self,
|
&self,
|
||||||
position: InsertPosition<SyntaxElement<'_>>,
|
position: InsertPosition<SyntaxElement>,
|
||||||
to_insert: impl Iterator<Item = SyntaxElement<'a>>,
|
to_insert: impl Iterator<Item = SyntaxElement>,
|
||||||
) -> TreeArc<N> {
|
) -> N {
|
||||||
let new_syntax = self.ast().syntax().insert_children(position, to_insert);
|
let new_syntax = self.ast().syntax().insert_children(position, to_insert);
|
||||||
N::cast(&new_syntax).unwrap().to_owned()
|
N::cast(new_syntax).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn replace_children<'a>(
|
fn replace_children(
|
||||||
&self,
|
&self,
|
||||||
to_delete: RangeInclusive<SyntaxElement<'_>>,
|
to_delete: RangeInclusive<SyntaxElement>,
|
||||||
to_insert: impl Iterator<Item = SyntaxElement<'a>>,
|
to_insert: impl Iterator<Item = SyntaxElement>,
|
||||||
) -> TreeArc<N> {
|
) -> N {
|
||||||
let new_syntax = self.ast().syntax().replace_children(to_delete, to_insert);
|
let new_syntax = self.ast().syntax().replace_children(to_delete, to_insert);
|
||||||
N::cast(&new_syntax).unwrap().to_owned()
|
N::cast(new_syntax).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_make_multiline(&mut self) {
|
fn do_make_multiline(&mut self) {
|
||||||
|
@ -66,16 +66,18 @@ impl<N: AstNode> AstEditor<N> {
|
||||||
if ws.text().contains('\n') {
|
if ws.text().contains('\n') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Some(ws)
|
Some(ws.clone())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let indent = leading_indent(self.ast().syntax()).unwrap_or("");
|
let indent = leading_indent(self.ast().syntax()).unwrap_or("".into());
|
||||||
let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
|
let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
|
||||||
let to_insert = iter::once(ws.ws().into());
|
let to_insert = iter::once(ws.ws().into());
|
||||||
self.ast = match existing_ws {
|
self.ast = match existing_ws {
|
||||||
None => self.insert_children(InsertPosition::After(l_curly), to_insert),
|
None => self.insert_children(InsertPosition::After(l_curly), to_insert),
|
||||||
Some(ws) => self.replace_children(RangeInclusive::new(ws.into(), ws.into()), to_insert),
|
Some(ws) => {
|
||||||
|
self.replace_children(RangeInclusive::new(ws.clone().into(), ws.into()), to_insert)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,7 +97,7 @@ impl AstEditor<ast::NamedFieldList> {
|
||||||
let space = if is_multiline {
|
let space = if is_multiline {
|
||||||
ws = tokens::WsBuilder::new(&format!(
|
ws = tokens::WsBuilder::new(&format!(
|
||||||
"\n{} ",
|
"\n{} ",
|
||||||
leading_indent(self.ast().syntax()).unwrap_or("")
|
leading_indent(self.ast().syntax()).unwrap_or("".into())
|
||||||
));
|
));
|
||||||
ws.ws()
|
ws.ws()
|
||||||
} else {
|
} else {
|
||||||
|
@ -104,7 +106,7 @@ impl AstEditor<ast::NamedFieldList> {
|
||||||
|
|
||||||
let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new();
|
let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new();
|
||||||
to_insert.push(space.into());
|
to_insert.push(space.into());
|
||||||
to_insert.push(field.syntax().into());
|
to_insert.push(field.syntax().clone().into());
|
||||||
to_insert.push(tokens::comma().into());
|
to_insert.push(tokens::comma().into());
|
||||||
|
|
||||||
macro_rules! after_l_curly {
|
macro_rules! after_l_curly {
|
||||||
|
@ -127,7 +129,7 @@ impl AstEditor<ast::NamedFieldList> {
|
||||||
InsertPosition::After(comma)
|
InsertPosition::After(comma)
|
||||||
} else {
|
} else {
|
||||||
to_insert.insert(0, tokens::comma().into());
|
to_insert.insert(0, tokens::comma().into());
|
||||||
InsertPosition::After($anchor.syntax().into())
|
InsertPosition::After($anchor.syntax().clone().into())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -144,7 +146,9 @@ impl AstEditor<ast::NamedFieldList> {
|
||||||
None => after_l_curly!(),
|
None => after_l_curly!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InsertPosition::Before(anchor) => InsertPosition::Before(anchor.syntax().into()),
|
InsertPosition::Before(anchor) => {
|
||||||
|
InsertPosition::Before(anchor.syntax().clone().into())
|
||||||
|
}
|
||||||
InsertPosition::After(anchor) => after_field!(anchor),
|
InsertPosition::After(anchor) => after_field!(anchor),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -157,7 +161,7 @@ impl AstEditor<ast::NamedFieldList> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AstEditor<ast::ItemList> {
|
impl AstEditor<ast::ItemList> {
|
||||||
pub fn append_items<'a>(&mut self, items: impl Iterator<Item = &'a ast::ImplItem>) {
|
pub fn append_items(&mut self, items: impl Iterator<Item = ast::ImplItem>) {
|
||||||
let n_existing_items = self.ast().impl_items().count();
|
let n_existing_items = self.ast().impl_items().count();
|
||||||
if n_existing_items == 0 {
|
if n_existing_items == 0 {
|
||||||
self.do_make_multiline();
|
self.do_make_multiline();
|
||||||
|
@ -165,22 +169,23 @@ impl AstEditor<ast::ItemList> {
|
||||||
items.for_each(|it| self.append_item(it));
|
items.for_each(|it| self.append_item(it));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn append_item(&mut self, item: &ast::ImplItem) {
|
pub fn append_item(&mut self, item: ast::ImplItem) {
|
||||||
let (indent, position) = match self.ast().impl_items().last() {
|
let (indent, position) = match self.ast().impl_items().last() {
|
||||||
Some(it) => (
|
Some(it) => (
|
||||||
leading_indent(it.syntax()).unwrap_or("").to_string(),
|
leading_indent(it.syntax()).unwrap_or_default().to_string(),
|
||||||
InsertPosition::After(it.syntax().into()),
|
InsertPosition::After(it.syntax().clone().into()),
|
||||||
),
|
),
|
||||||
None => match self.l_curly() {
|
None => match self.l_curly() {
|
||||||
Some(it) => (
|
Some(it) => (
|
||||||
" ".to_string() + leading_indent(self.ast().syntax()).unwrap_or(""),
|
" ".to_string() + &leading_indent(self.ast().syntax()).unwrap_or_default(),
|
||||||
InsertPosition::After(it),
|
InsertPosition::After(it),
|
||||||
),
|
),
|
||||||
None => return,
|
None => return,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
|
let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
|
||||||
let to_insert: ArrayVec<[SyntaxElement; 2]> = [ws.ws().into(), item.syntax().into()].into();
|
let to_insert: ArrayVec<[SyntaxElement; 2]> =
|
||||||
|
[ws.ws().into(), item.syntax().clone().into()].into();
|
||||||
self.ast = self.insert_children(position, to_insert.into_iter());
|
self.ast = self.insert_children(position, to_insert.into_iter());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,9 +202,9 @@ impl AstEditor<ast::ImplItem> {
|
||||||
.children_with_tokens()
|
.children_with_tokens()
|
||||||
.find(|it| it.kind() == ATTR || it.kind() == COMMENT)
|
.find(|it| it.kind() == ATTR || it.kind() == COMMENT)
|
||||||
{
|
{
|
||||||
let end = match start.next_sibling_or_token() {
|
let end = match &start.next_sibling_or_token() {
|
||||||
Some(el) if el.kind() == WHITESPACE => el,
|
Some(el) if el.kind() == WHITESPACE => el.clone(),
|
||||||
Some(_) | None => start,
|
Some(_) | None => start.clone(),
|
||||||
};
|
};
|
||||||
self.ast = self.replace_children(RangeInclusive::new(start, end), iter::empty());
|
self.ast = self.replace_children(RangeInclusive::new(start, end), iter::empty());
|
||||||
}
|
}
|
||||||
|
@ -210,18 +215,18 @@ impl AstEditor<ast::FnDef> {
|
||||||
pub fn set_body(&mut self, body: &ast::Block) {
|
pub fn set_body(&mut self, body: &ast::Block) {
|
||||||
let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new();
|
let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new();
|
||||||
let old_body_or_semi: SyntaxElement = if let Some(old_body) = self.ast().body() {
|
let old_body_or_semi: SyntaxElement = if let Some(old_body) = self.ast().body() {
|
||||||
old_body.syntax().into()
|
old_body.syntax().clone().into()
|
||||||
} else if let Some(semi) = self.ast().semicolon_token() {
|
} else if let Some(semi) = self.ast().semicolon_token() {
|
||||||
to_insert.push(tokens::single_space().into());
|
to_insert.push(tokens::single_space().into());
|
||||||
semi.into()
|
semi.into()
|
||||||
} else {
|
} else {
|
||||||
to_insert.push(tokens::single_space().into());
|
to_insert.push(tokens::single_space().into());
|
||||||
to_insert.push(body.syntax().into());
|
to_insert.push(body.syntax().clone().into());
|
||||||
self.ast = self.insert_children(InsertPosition::Last, to_insert.into_iter());
|
self.ast = self.insert_children(InsertPosition::Last, to_insert.into_iter());
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
to_insert.push(body.syntax().into());
|
to_insert.push(body.syntax().clone().into());
|
||||||
let replace_range = RangeInclusive::new(old_body_or_semi, old_body_or_semi);
|
let replace_range = RangeInclusive::new(old_body_or_semi.clone(), old_body_or_semi);
|
||||||
self.ast = self.replace_children(replace_range, to_insert.into_iter())
|
self.ast = self.replace_children(replace_range, to_insert.into_iter())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -231,15 +236,15 @@ pub struct AstBuilder<N: AstNode> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AstBuilder<ast::NamedField> {
|
impl AstBuilder<ast::NamedField> {
|
||||||
pub fn from_name(name: &Name) -> TreeArc<ast::NamedField> {
|
pub fn from_name(name: &Name) -> ast::NamedField {
|
||||||
ast_node_from_file_text(&format!("fn f() {{ S {{ {}: (), }} }}", name))
|
ast_node_from_file_text(&format!("fn f() {{ S {{ {}: (), }} }}", name))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_text(text: &str) -> TreeArc<ast::NamedField> {
|
fn from_text(text: &str) -> ast::NamedField {
|
||||||
ast_node_from_file_text(&format!("fn f() {{ S {{ {}, }} }}", text))
|
ast_node_from_file_text(&format!("fn f() {{ S {{ {}, }} }}", text))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_pieces(name: &ast::NameRef, expr: Option<&ast::Expr>) -> TreeArc<ast::NamedField> {
|
pub fn from_pieces(name: &ast::NameRef, expr: Option<&ast::Expr>) -> ast::NamedField {
|
||||||
match expr {
|
match expr {
|
||||||
Some(expr) => Self::from_text(&format!("{}: {}", name.syntax(), expr.syntax())),
|
Some(expr) => Self::from_text(&format!("{}: {}", name.syntax(), expr.syntax())),
|
||||||
None => Self::from_text(&name.syntax().to_string()),
|
None => Self::from_text(&name.syntax().to_string()),
|
||||||
|
@ -248,36 +253,36 @@ impl AstBuilder<ast::NamedField> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AstBuilder<ast::Block> {
|
impl AstBuilder<ast::Block> {
|
||||||
fn from_text(text: &str) -> TreeArc<ast::Block> {
|
fn from_text(text: &str) -> ast::Block {
|
||||||
ast_node_from_file_text(&format!("fn f() {}", text))
|
ast_node_from_file_text(&format!("fn f() {}", text))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn single_expr(e: &ast::Expr) -> TreeArc<ast::Block> {
|
pub fn single_expr(e: &ast::Expr) -> ast::Block {
|
||||||
Self::from_text(&format!("{{ {} }}", e.syntax()))
|
Self::from_text(&format!("{{ {} }}", e.syntax()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AstBuilder<ast::Expr> {
|
impl AstBuilder<ast::Expr> {
|
||||||
fn from_text(text: &str) -> TreeArc<ast::Expr> {
|
fn from_text(text: &str) -> ast::Expr {
|
||||||
ast_node_from_file_text(&format!("fn f() {{ {}; }}", text))
|
ast_node_from_file_text(&format!("fn f() {{ {}; }}", text))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unit() -> TreeArc<ast::Expr> {
|
pub fn unit() -> ast::Expr {
|
||||||
Self::from_text("()")
|
Self::from_text("()")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unimplemented() -> TreeArc<ast::Expr> {
|
pub fn unimplemented() -> ast::Expr {
|
||||||
Self::from_text("unimplemented!()")
|
Self::from_text("unimplemented!()")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AstBuilder<ast::NameRef> {
|
impl AstBuilder<ast::NameRef> {
|
||||||
pub fn new(text: &str) -> TreeArc<ast::NameRef> {
|
pub fn new(text: &str) -> ast::NameRef {
|
||||||
ast_node_from_file_text(&format!("fn f() {{ {}; }}", text))
|
ast_node_from_file_text(&format!("fn f() {{ {}; }}", text))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ast_node_from_file_text<N: AstNode>(text: &str) -> TreeArc<N> {
|
fn ast_node_from_file_text<N: AstNode>(text: &str) -> N {
|
||||||
let parse = SourceFile::parse(text);
|
let parse = SourceFile::parse(text);
|
||||||
let res = parse.tree().syntax().descendants().find_map(N::cast).unwrap().to_owned();
|
let res = parse.tree().syntax().descendants().find_map(N::cast).unwrap().to_owned();
|
||||||
res
|
res
|
||||||
|
@ -285,47 +290,49 @@ fn ast_node_from_file_text<N: AstNode>(text: &str) -> TreeArc<N> {
|
||||||
|
|
||||||
mod tokens {
|
mod tokens {
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use ra_syntax::{AstNode, SourceFile, SyntaxKind::*, SyntaxToken, TreeArc, T};
|
use ra_syntax::{AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken, T};
|
||||||
|
|
||||||
static SOURCE_FILE: Lazy<TreeArc<SourceFile>> =
|
static SOURCE_FILE: Lazy<Parse<SourceFile>> = Lazy::new(|| SourceFile::parse(",\n; ;"));
|
||||||
Lazy::new(|| SourceFile::parse(",\n; ;").tree().to_owned());
|
|
||||||
|
|
||||||
pub(crate) fn comma() -> SyntaxToken<'static> {
|
pub(crate) fn comma() -> SyntaxToken {
|
||||||
SOURCE_FILE
|
SOURCE_FILE
|
||||||
|
.tree()
|
||||||
.syntax()
|
.syntax()
|
||||||
.descendants_with_tokens()
|
.descendants_with_tokens()
|
||||||
.filter_map(|it| it.as_token())
|
.filter_map(|it| it.as_token().cloned())
|
||||||
.find(|it| it.kind() == T![,])
|
.find(|it| it.kind() == T![,])
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn single_space() -> SyntaxToken<'static> {
|
pub(crate) fn single_space() -> SyntaxToken {
|
||||||
SOURCE_FILE
|
SOURCE_FILE
|
||||||
|
.tree()
|
||||||
.syntax()
|
.syntax()
|
||||||
.descendants_with_tokens()
|
.descendants_with_tokens()
|
||||||
.filter_map(|it| it.as_token())
|
.filter_map(|it| it.as_token().cloned())
|
||||||
.find(|it| it.kind() == WHITESPACE && it.text().as_str() == " ")
|
.find(|it| it.kind() == WHITESPACE && it.text().as_str() == " ")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub(crate) fn single_newline() -> SyntaxToken<'static> {
|
pub(crate) fn single_newline() -> SyntaxToken {
|
||||||
SOURCE_FILE
|
SOURCE_FILE
|
||||||
|
.tree()
|
||||||
.syntax()
|
.syntax()
|
||||||
.descendants_with_tokens()
|
.descendants_with_tokens()
|
||||||
.filter_map(|it| it.as_token())
|
.filter_map(|it| it.as_token().cloned())
|
||||||
.find(|it| it.kind() == WHITESPACE && it.text().as_str() == "\n")
|
.find(|it| it.kind() == WHITESPACE && it.text().as_str() == "\n")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct WsBuilder(TreeArc<SourceFile>);
|
pub(crate) struct WsBuilder(SourceFile);
|
||||||
|
|
||||||
impl WsBuilder {
|
impl WsBuilder {
|
||||||
pub(crate) fn new(text: &str) -> WsBuilder {
|
pub(crate) fn new(text: &str) -> WsBuilder {
|
||||||
WsBuilder(SourceFile::parse(text).ok().unwrap())
|
WsBuilder(SourceFile::parse(text).ok().unwrap())
|
||||||
}
|
}
|
||||||
pub(crate) fn ws(&self) -> SyntaxToken<'_> {
|
pub(crate) fn ws(&self) -> SyntaxToken {
|
||||||
self.0.syntax().first_child_or_token().unwrap().as_token().unwrap()
|
self.0.syntax().first_child_or_token().unwrap().as_token().cloned().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,25 +12,25 @@ use ra_syntax::{
|
||||||
SyntaxNode, TextRange, T,
|
SyntaxNode, TextRange, T,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn collect_path_segments_raw<'a>(
|
fn collect_path_segments_raw(
|
||||||
segments: &mut Vec<&'a ast::PathSegment>,
|
segments: &mut Vec<ast::PathSegment>,
|
||||||
mut path: &'a ast::Path,
|
mut path: ast::Path,
|
||||||
) -> Option<usize> {
|
) -> Option<usize> {
|
||||||
let oldlen = segments.len();
|
let oldlen = segments.len();
|
||||||
loop {
|
loop {
|
||||||
let mut children = path.syntax().children_with_tokens();
|
let mut children = path.syntax().children_with_tokens();
|
||||||
let (first, second, third) = (
|
let (first, second, third) = (
|
||||||
children.next().map(|n| (n, n.kind())),
|
children.next().map(|n| (n.clone(), n.kind())),
|
||||||
children.next().map(|n| (n, n.kind())),
|
children.next().map(|n| (n.clone(), n.kind())),
|
||||||
children.next().map(|n| (n, n.kind())),
|
children.next().map(|n| (n.clone(), n.kind())),
|
||||||
);
|
);
|
||||||
match (first, second, third) {
|
match (first, second, third) {
|
||||||
(Some((subpath, PATH)), Some((_, T![::])), Some((segment, PATH_SEGMENT))) => {
|
(Some((subpath, PATH)), Some((_, T![::])), Some((segment, PATH_SEGMENT))) => {
|
||||||
path = ast::Path::cast(subpath.as_node()?)?;
|
path = ast::Path::cast(subpath.as_node()?.clone())?;
|
||||||
segments.push(ast::PathSegment::cast(segment.as_node()?)?);
|
segments.push(ast::PathSegment::cast(segment.as_node()?.clone())?);
|
||||||
}
|
}
|
||||||
(Some((segment, PATH_SEGMENT)), _, _) => {
|
(Some((segment, PATH_SEGMENT)), _, _) => {
|
||||||
segments.push(ast::PathSegment::cast(segment.as_node()?)?);
|
segments.push(ast::PathSegment::cast(segment.as_node()?.clone())?);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
(_, _, _) => return None,
|
(_, _, _) => return None,
|
||||||
|
@ -60,7 +60,7 @@ fn fmt_segments_raw(segments: &[SmolStr], buf: &mut String) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the numeber of common segments.
|
// Returns the numeber of common segments.
|
||||||
fn compare_path_segments(left: &[SmolStr], right: &[&ast::PathSegment]) -> usize {
|
fn compare_path_segments(left: &[SmolStr], right: &[ast::PathSegment]) -> usize {
|
||||||
left.iter().zip(right).filter(|(l, r)| compare_path_segment(l, r)).count()
|
left.iter().zip(right).filter(|(l, r)| compare_path_segment(l, r)).count()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,12 +81,12 @@ fn compare_path_segment_with_name(a: &SmolStr, b: &ast::Name) -> bool {
|
||||||
a == b.text()
|
a == b.text()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Clone)]
|
||||||
enum ImportAction<'a> {
|
enum ImportAction {
|
||||||
Nothing,
|
Nothing,
|
||||||
// Add a brand new use statement.
|
// Add a brand new use statement.
|
||||||
AddNewUse {
|
AddNewUse {
|
||||||
anchor: Option<&'a SyntaxNode>, // anchor node
|
anchor: Option<SyntaxNode>, // anchor node
|
||||||
add_after_anchor: bool,
|
add_after_anchor: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -94,9 +94,9 @@ enum ImportAction<'a> {
|
||||||
AddNestedImport {
|
AddNestedImport {
|
||||||
// how may segments matched with the target path
|
// how may segments matched with the target path
|
||||||
common_segments: usize,
|
common_segments: usize,
|
||||||
path_to_split: &'a ast::Path,
|
path_to_split: ast::Path,
|
||||||
// the first segment of path_to_split we want to add into the new nested list
|
// the first segment of path_to_split we want to add into the new nested list
|
||||||
first_segment_to_split: Option<&'a ast::PathSegment>,
|
first_segment_to_split: Option<ast::PathSegment>,
|
||||||
// Wether to add 'self' in addition to the target path
|
// Wether to add 'self' in addition to the target path
|
||||||
add_self: bool,
|
add_self: bool,
|
||||||
},
|
},
|
||||||
|
@ -104,20 +104,20 @@ enum ImportAction<'a> {
|
||||||
AddInTreeList {
|
AddInTreeList {
|
||||||
common_segments: usize,
|
common_segments: usize,
|
||||||
// The UseTreeList where to add the target path
|
// The UseTreeList where to add the target path
|
||||||
tree_list: &'a ast::UseTreeList,
|
tree_list: ast::UseTreeList,
|
||||||
add_self: bool,
|
add_self: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ImportAction<'a> {
|
impl ImportAction {
|
||||||
fn add_new_use(anchor: Option<&'a SyntaxNode>, add_after_anchor: bool) -> Self {
|
fn add_new_use(anchor: Option<SyntaxNode>, add_after_anchor: bool) -> Self {
|
||||||
ImportAction::AddNewUse { anchor, add_after_anchor }
|
ImportAction::AddNewUse { anchor, add_after_anchor }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_nested_import(
|
fn add_nested_import(
|
||||||
common_segments: usize,
|
common_segments: usize,
|
||||||
path_to_split: &'a ast::Path,
|
path_to_split: ast::Path,
|
||||||
first_segment_to_split: Option<&'a ast::PathSegment>,
|
first_segment_to_split: Option<ast::PathSegment>,
|
||||||
add_self: bool,
|
add_self: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
ImportAction::AddNestedImport {
|
ImportAction::AddNestedImport {
|
||||||
|
@ -130,14 +130,14 @@ impl<'a> ImportAction<'a> {
|
||||||
|
|
||||||
fn add_in_tree_list(
|
fn add_in_tree_list(
|
||||||
common_segments: usize,
|
common_segments: usize,
|
||||||
tree_list: &'a ast::UseTreeList,
|
tree_list: ast::UseTreeList,
|
||||||
add_self: bool,
|
add_self: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
ImportAction::AddInTreeList { common_segments, tree_list, add_self }
|
ImportAction::AddInTreeList { common_segments, tree_list, add_self }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn better<'b>(left: &'b ImportAction<'a>, right: &'b ImportAction<'a>) -> &'b ImportAction<'a> {
|
fn better(left: ImportAction, right: ImportAction) -> ImportAction {
|
||||||
if left.is_better(right) {
|
if left.is_better(&right) {
|
||||||
left
|
left
|
||||||
} else {
|
} else {
|
||||||
right
|
right
|
||||||
|
@ -166,12 +166,12 @@ impl<'a> ImportAction<'a> {
|
||||||
|
|
||||||
// Find out the best ImportAction to import target path against current_use_tree.
|
// Find out the best ImportAction to import target path against current_use_tree.
|
||||||
// If current_use_tree has a nested import the function gets called recursively on every UseTree inside a UseTreeList.
|
// If current_use_tree has a nested import the function gets called recursively on every UseTree inside a UseTreeList.
|
||||||
fn walk_use_tree_for_best_action<'a>(
|
fn walk_use_tree_for_best_action(
|
||||||
current_path_segments: &mut Vec<&'a ast::PathSegment>, // buffer containing path segments
|
current_path_segments: &mut Vec<ast::PathSegment>, // buffer containing path segments
|
||||||
current_parent_use_tree_list: Option<&'a ast::UseTreeList>, // will be Some value if we are in a nested import
|
current_parent_use_tree_list: Option<ast::UseTreeList>, // will be Some value if we are in a nested import
|
||||||
current_use_tree: &'a ast::UseTree, // the use tree we are currently examinating
|
current_use_tree: ast::UseTree, // the use tree we are currently examinating
|
||||||
target: &[SmolStr], // the path we want to import
|
target: &[SmolStr], // the path we want to import
|
||||||
) -> ImportAction<'a> {
|
) -> ImportAction {
|
||||||
// We save the number of segments in the buffer so we can restore the correct segments
|
// We save the number of segments in the buffer so we can restore the correct segments
|
||||||
// before returning. Recursive call will add segments so we need to delete them.
|
// before returning. Recursive call will add segments so we need to delete them.
|
||||||
let prev_len = current_path_segments.len();
|
let prev_len = current_path_segments.len();
|
||||||
|
@ -188,32 +188,36 @@ fn walk_use_tree_for_best_action<'a>(
|
||||||
.syntax()
|
.syntax()
|
||||||
.ancestors()
|
.ancestors()
|
||||||
.find_map(ast::UseItem::cast)
|
.find_map(ast::UseItem::cast)
|
||||||
.map(AstNode::syntax),
|
.map(|it| it.syntax().clone()),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// This can happen only if current_use_tree is a direct child of a UseItem
|
// This can happen only if current_use_tree is a direct child of a UseItem
|
||||||
if let Some(name) = alias.and_then(ast::NameOwner::name) {
|
if let Some(name) = alias.and_then(|it| it.name()) {
|
||||||
if compare_path_segment_with_name(&target[0], name) {
|
if compare_path_segment_with_name(&target[0], &name) {
|
||||||
return ImportAction::Nothing;
|
return ImportAction::Nothing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
collect_path_segments_raw(current_path_segments, path);
|
collect_path_segments_raw(current_path_segments, path.clone());
|
||||||
|
|
||||||
// We compare only the new segments added in the line just above.
|
// We compare only the new segments added in the line just above.
|
||||||
// The first prev_len segments were already compared in 'parent' recursive calls.
|
// The first prev_len segments were already compared in 'parent' recursive calls.
|
||||||
let left = target.split_at(prev_len).1;
|
let left = target.split_at(prev_len).1;
|
||||||
let right = current_path_segments.split_at(prev_len).1;
|
let right = current_path_segments.split_at(prev_len).1;
|
||||||
let common = compare_path_segments(left, right);
|
let common = compare_path_segments(left, &right);
|
||||||
let mut action = match common {
|
let mut action = match common {
|
||||||
0 => ImportAction::add_new_use(
|
0 => ImportAction::add_new_use(
|
||||||
// e.g: target is std::fmt and we can have
|
// e.g: target is std::fmt and we can have
|
||||||
// use foo::bar
|
// use foo::bar
|
||||||
// We add a brand new use statement
|
// We add a brand new use statement
|
||||||
current_use_tree.syntax().ancestors().find_map(ast::UseItem::cast).map(AstNode::syntax),
|
current_use_tree
|
||||||
|
.syntax()
|
||||||
|
.ancestors()
|
||||||
|
.find_map(ast::UseItem::cast)
|
||||||
|
.map(|it| it.syntax().clone()),
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
common if common == left.len() && left.len() == right.len() => {
|
common if common == left.len() && left.len() == right.len() => {
|
||||||
|
@ -223,9 +227,9 @@ fn walk_use_tree_for_best_action<'a>(
|
||||||
if let Some(list) = tree_list {
|
if let Some(list) = tree_list {
|
||||||
// In case 2 we need to add self to the nested list
|
// In case 2 we need to add self to the nested list
|
||||||
// unless it's already there
|
// unless it's already there
|
||||||
let has_self = list.use_trees().map(ast::UseTree::path).any(|p| {
|
let has_self = list.use_trees().map(|it| it.path()).any(|p| {
|
||||||
p.and_then(ast::Path::segment)
|
p.and_then(|it| it.segment())
|
||||||
.and_then(ast::PathSegment::kind)
|
.and_then(|it| it.kind())
|
||||||
.filter(|k| *k == ast::PathSegmentKind::SelfKw)
|
.filter(|k| *k == ast::PathSegmentKind::SelfKw)
|
||||||
.is_some()
|
.is_some()
|
||||||
});
|
});
|
||||||
|
@ -248,7 +252,7 @@ fn walk_use_tree_for_best_action<'a>(
|
||||||
ImportAction::add_nested_import(
|
ImportAction::add_nested_import(
|
||||||
prev_len + common,
|
prev_len + common,
|
||||||
path,
|
path,
|
||||||
Some(segments_to_split[0]),
|
Some(segments_to_split[0].clone()),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -263,14 +267,18 @@ fn walk_use_tree_for_best_action<'a>(
|
||||||
.syntax()
|
.syntax()
|
||||||
.ancestors()
|
.ancestors()
|
||||||
.find_map(ast::UseItem::cast)
|
.find_map(ast::UseItem::cast)
|
||||||
.map(AstNode::syntax),
|
.map(|it| it.syntax().clone()),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
if let Some(list) = tree_list {
|
if let Some(list) = tree_list {
|
||||||
// Case 2, check recursively if the path is already imported in the nested list
|
// Case 2, check recursively if the path is already imported in the nested list
|
||||||
for u in list.use_trees() {
|
for u in list.use_trees() {
|
||||||
let child_action =
|
let child_action = walk_use_tree_for_best_action(
|
||||||
walk_use_tree_for_best_action(current_path_segments, Some(list), u, target);
|
current_path_segments,
|
||||||
|
Some(list.clone()),
|
||||||
|
u,
|
||||||
|
target,
|
||||||
|
);
|
||||||
if child_action.is_better(&better_action) {
|
if child_action.is_better(&better_action) {
|
||||||
better_action = child_action;
|
better_action = child_action;
|
||||||
if let ImportAction::Nothing = better_action {
|
if let ImportAction::Nothing = better_action {
|
||||||
|
@ -291,7 +299,7 @@ fn walk_use_tree_for_best_action<'a>(
|
||||||
ImportAction::add_nested_import(
|
ImportAction::add_nested_import(
|
||||||
prev_len + common,
|
prev_len + common,
|
||||||
path,
|
path,
|
||||||
Some(segments_to_split[0]),
|
Some(segments_to_split[0].clone()),
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -302,7 +310,7 @@ fn walk_use_tree_for_best_action<'a>(
|
||||||
ImportAction::add_nested_import(
|
ImportAction::add_nested_import(
|
||||||
prev_len + common,
|
prev_len + common,
|
||||||
path,
|
path,
|
||||||
Some(segments_to_split[0]),
|
Some(segments_to_split[0].clone()),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -311,7 +319,7 @@ fn walk_use_tree_for_best_action<'a>(
|
||||||
|
|
||||||
// If we are inside a UseTreeList adding a use statement become adding to the existing
|
// If we are inside a UseTreeList adding a use statement become adding to the existing
|
||||||
// tree list.
|
// tree list.
|
||||||
action = match (current_parent_use_tree_list, action) {
|
action = match (current_parent_use_tree_list, action.clone()) {
|
||||||
(Some(use_tree_list), ImportAction::AddNewUse { .. }) => {
|
(Some(use_tree_list), ImportAction::AddNewUse { .. }) => {
|
||||||
ImportAction::add_in_tree_list(prev_len, use_tree_list, false)
|
ImportAction::add_in_tree_list(prev_len, use_tree_list, false)
|
||||||
}
|
}
|
||||||
|
@ -323,19 +331,20 @@ fn walk_use_tree_for_best_action<'a>(
|
||||||
action
|
action
|
||||||
}
|
}
|
||||||
|
|
||||||
fn best_action_for_target<'b, 'a: 'b>(
|
fn best_action_for_target(
|
||||||
container: &'a SyntaxNode,
|
container: SyntaxNode,
|
||||||
anchor: &'a SyntaxNode,
|
anchor: SyntaxNode,
|
||||||
target: &'b [SmolStr],
|
target: &[SmolStr],
|
||||||
) -> ImportAction<'a> {
|
) -> ImportAction {
|
||||||
let mut storage = Vec::with_capacity(16); // this should be the only allocation
|
let mut storage = Vec::with_capacity(16); // this should be the only allocation
|
||||||
let best_action = container
|
let best_action = container
|
||||||
.children()
|
.children()
|
||||||
.filter_map(ast::UseItem::cast)
|
.filter_map(ast::UseItem::cast)
|
||||||
.filter_map(ast::UseItem::use_tree)
|
.filter_map(|it| it.use_tree())
|
||||||
.map(|u| walk_use_tree_for_best_action(&mut storage, None, u, target))
|
.map(|u| walk_use_tree_for_best_action(&mut storage, None, u, target))
|
||||||
.fold(None, |best, a| {
|
.fold(None, |best, a| match best {
|
||||||
best.and_then(|best| Some(*ImportAction::better(&best, &a))).or_else(|| Some(a))
|
Some(best) => Some(ImportAction::better(best, a)),
|
||||||
|
None => Some(a),
|
||||||
});
|
});
|
||||||
|
|
||||||
match best_action {
|
match best_action {
|
||||||
|
@ -386,7 +395,7 @@ fn make_assist(action: &ImportAction, target: &[SmolStr], edit: &mut TextEditBui
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_assist_add_new_use(
|
fn make_assist_add_new_use(
|
||||||
anchor: &Option<&SyntaxNode>,
|
anchor: &Option<SyntaxNode>,
|
||||||
after: bool,
|
after: bool,
|
||||||
target: &[SmolStr],
|
target: &[SmolStr],
|
||||||
edit: &mut TextEditBuilder,
|
edit: &mut TextEditBuilder,
|
||||||
|
@ -396,7 +405,7 @@ fn make_assist_add_new_use(
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
if after {
|
if after {
|
||||||
buf.push_str("\n");
|
buf.push_str("\n");
|
||||||
if let Some(spaces) = indent {
|
if let Some(spaces) = &indent {
|
||||||
buf.push_str(spaces);
|
buf.push_str(spaces);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -405,8 +414,8 @@ fn make_assist_add_new_use(
|
||||||
buf.push_str(";");
|
buf.push_str(";");
|
||||||
if !after {
|
if !after {
|
||||||
buf.push_str("\n\n");
|
buf.push_str("\n\n");
|
||||||
if let Some(spaces) = indent {
|
if let Some(spaces) = &indent {
|
||||||
buf.push_str(spaces);
|
buf.push_str(&spaces);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let position = if after { anchor.range().end() } else { anchor.range().start() };
|
let position = if after { anchor.range().end() } else { anchor.range().start() };
|
||||||
|
@ -444,7 +453,7 @@ fn make_assist_add_in_tree_list(
|
||||||
|
|
||||||
fn make_assist_add_nested_import(
|
fn make_assist_add_nested_import(
|
||||||
path: &ast::Path,
|
path: &ast::Path,
|
||||||
first_segment_to_split: &Option<&ast::PathSegment>,
|
first_segment_to_split: &Option<ast::PathSegment>,
|
||||||
target: &[SmolStr],
|
target: &[SmolStr],
|
||||||
add_self: bool,
|
add_self: bool,
|
||||||
edit: &mut TextEditBuilder,
|
edit: &mut TextEditBuilder,
|
||||||
|
@ -482,7 +491,7 @@ fn apply_auto_import(
|
||||||
target: &[SmolStr],
|
target: &[SmolStr],
|
||||||
edit: &mut TextEditBuilder,
|
edit: &mut TextEditBuilder,
|
||||||
) {
|
) {
|
||||||
let action = best_action_for_target(container, path.syntax(), target);
|
let action = best_action_for_target(container.clone(), path.syntax().clone(), target);
|
||||||
make_assist(&action, target, edit);
|
make_assist(&action, target, edit);
|
||||||
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
|
||||||
|
@ -522,26 +531,26 @@ pub fn auto_import_text_edit(
|
||||||
edit: &mut TextEditBuilder,
|
edit: &mut TextEditBuilder,
|
||||||
) {
|
) {
|
||||||
let container = position.ancestors().find_map(|n| {
|
let container = position.ancestors().find_map(|n| {
|
||||||
if let Some(module) = ast::Module::cast(n) {
|
if let Some(module) = ast::Module::cast(n.clone()) {
|
||||||
return module.item_list().map(ast::AstNode::syntax);
|
return module.item_list().map(|it| it.syntax().clone());
|
||||||
}
|
}
|
||||||
ast::SourceFile::cast(n).map(ast::AstNode::syntax)
|
ast::SourceFile::cast(n).map(|it| it.syntax().clone())
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(container) = container {
|
if let Some(container) = container {
|
||||||
let action = best_action_for_target(container, anchor, target);
|
let action = best_action_for_target(container, anchor.clone(), target);
|
||||||
make_assist(&action, target, edit);
|
make_assist(&action, target, edit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||||
let path: &ast::Path = ctx.node_at_offset()?;
|
let path: ast::Path = ctx.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() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let hir_path = hir::Path::from_ast(path)?;
|
let hir_path = hir::Path::from_ast(path.clone())?;
|
||||||
let segments = collect_hir_path_segments(&hir_path);
|
let segments = collect_hir_path_segments(&hir_path);
|
||||||
if segments.len() < 2 {
|
if segments.len() < 2 {
|
||||||
return None;
|
return None;
|
||||||
|
@ -554,7 +563,7 @@ pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
|
||||||
format!("import {} in mod {}", fmt_segments(&segments), name.text()),
|
format!("import {} in mod {}", fmt_segments(&segments), name.text()),
|
||||||
|edit| {
|
|edit| {
|
||||||
let mut text_edit = TextEditBuilder::default();
|
let mut text_edit = TextEditBuilder::default();
|
||||||
apply_auto_import(item_list.syntax(), path, &segments, &mut text_edit);
|
apply_auto_import(item_list.syntax(), &path, &segments, &mut text_edit);
|
||||||
edit.set_edit_builder(text_edit);
|
edit.set_edit_builder(text_edit);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -566,7 +575,7 @@ pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
|
||||||
format!("import {} in the current file", fmt_segments(&segments)),
|
format!("import {} in the current file", fmt_segments(&segments)),
|
||||||
|edit| {
|
|edit| {
|
||||||
let mut text_edit = TextEditBuilder::default();
|
let mut text_edit = TextEditBuilder::default();
|
||||||
apply_auto_import(current_file.syntax(), path, &segments, &mut text_edit);
|
apply_auto_import(current_file.syntax(), &path, &segments, &mut text_edit);
|
||||||
edit.set_edit_builder(text_edit);
|
edit.set_edit_builder(text_edit);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -35,7 +35,7 @@ fn add_vis(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||||
if parent.children().any(|child| child.kind() == VISIBILITY) {
|
if parent.children().any(|child| child.kind() == VISIBILITY) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
(vis_offset(parent), keyword.range())
|
(vis_offset(&parent), keyword.range())
|
||||||
} else {
|
} else {
|
||||||
let ident = ctx.token_at_offset().find(|leaf| leaf.kind() == IDENT)?;
|
let ident = ctx.token_at_offset().find(|leaf| leaf.kind() == IDENT)?;
|
||||||
let field = ident.parent().ancestors().find_map(ast::NamedFieldDef::cast)?;
|
let field = ident.parent().ancestors().find_map(ast::NamedFieldDef::cast)?;
|
||||||
|
@ -65,7 +65,7 @@ fn vis_offset(node: &SyntaxNode) -> TextUnit {
|
||||||
.unwrap_or_else(|| node.range().start())
|
.unwrap_or_else(|| node.range().start())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn change_vis(mut ctx: AssistCtx<impl HirDatabase>, vis: &ast::Visibility) -> Option<Assist> {
|
fn change_vis(mut ctx: AssistCtx<impl HirDatabase>, vis: ast::Visibility) -> Option<Assist> {
|
||||||
if vis.syntax().text() == "pub" {
|
if vis.syntax().text() == "pub" {
|
||||||
ctx.add_action(AssistId("change_visibility"), "change to pub(crate)", |edit| {
|
ctx.add_action(AssistId("change_visibility"), "change to pub(crate)", |edit| {
|
||||||
edit.target(vis.syntax().range());
|
edit.target(vis.syntax().range());
|
||||||
|
|
|
@ -27,7 +27,7 @@ pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As
|
||||||
let mut arm_iter = arm_list.arms();
|
let mut arm_iter = arm_list.arms();
|
||||||
let first = arm_iter.next();
|
let first = arm_iter.next();
|
||||||
|
|
||||||
match first {
|
match &first {
|
||||||
// If there arm list is empty or there is only one trivial arm, then proceed.
|
// If there arm list is empty or there is only one trivial arm, then proceed.
|
||||||
Some(arm) if is_trivial_arm(arm) => {
|
Some(arm) if is_trivial_arm(arm) => {
|
||||||
if arm_iter.next() != None {
|
if arm_iter.next() != None {
|
||||||
|
@ -44,7 +44,7 @@ pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As
|
||||||
|
|
||||||
let expr = match_expr.expr()?;
|
let expr = match_expr.expr()?;
|
||||||
let analyzer = hir::SourceAnalyzer::new(ctx.db, ctx.frange.file_id, expr.syntax(), None);
|
let analyzer = hir::SourceAnalyzer::new(ctx.db, ctx.frange.file_id, expr.syntax(), None);
|
||||||
let match_expr_ty = analyzer.type_of(ctx.db, expr)?;
|
let match_expr_ty = analyzer.type_of(ctx.db, &expr)?;
|
||||||
let enum_def = analyzer.autoderef(ctx.db, match_expr_ty).find_map(|ty| match ty.as_adt() {
|
let enum_def = analyzer.autoderef(ctx.db, match_expr_ty).find_map(|ty| match ty.as_adt() {
|
||||||
Some((AdtDef::Enum(e), _)) => Some(e),
|
Some((AdtDef::Enum(e), _)) => Some(e),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -6,8 +6,8 @@ use crate::{Assist, AssistCtx, AssistId};
|
||||||
/// Flip binary expression assist.
|
/// Flip binary expression assist.
|
||||||
pub(crate) fn flip_binexpr(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
pub(crate) fn flip_binexpr(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||||
let expr = ctx.node_at_offset::<BinExpr>()?;
|
let expr = ctx.node_at_offset::<BinExpr>()?;
|
||||||
let lhs = expr.lhs()?.syntax();
|
let lhs = expr.lhs()?.syntax().clone();
|
||||||
let rhs = expr.rhs()?.syntax();
|
let rhs = expr.rhs()?.syntax().clone();
|
||||||
let op_range = expr.op_token()?.range();
|
let op_range = expr.op_token()?.range();
|
||||||
// The assist should be applied only if the cursor is on the operator
|
// The assist should be applied only if the cursor is on the operator
|
||||||
let cursor_in_range = ctx.frange.range.is_subrange(&op_range);
|
let cursor_in_range = ctx.frange.range.is_subrange(&op_range);
|
||||||
|
|
|
@ -5,8 +5,8 @@ use crate::{Assist, AssistCtx, AssistId};
|
||||||
|
|
||||||
pub(crate) fn flip_comma(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
pub(crate) fn flip_comma(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||||
let comma = ctx.token_at_offset().find(|leaf| leaf.kind() == T![,])?;
|
let comma = ctx.token_at_offset().find(|leaf| leaf.kind() == T![,])?;
|
||||||
let prev = non_trivia_sibling(comma.into(), Direction::Prev)?;
|
let prev = non_trivia_sibling(comma.clone().into(), Direction::Prev)?;
|
||||||
let next = non_trivia_sibling(comma.into(), Direction::Next)?;
|
let next = non_trivia_sibling(comma.clone().into(), Direction::Next)?;
|
||||||
ctx.add_action(AssistId("flip_comma"), "flip comma", |edit| {
|
ctx.add_action(AssistId("flip_comma"), "flip comma", |edit| {
|
||||||
edit.target(comma.range());
|
edit.target(comma.range());
|
||||||
edit.replace(prev.range(), next.to_string());
|
edit.replace(prev.range(), next.to_string());
|
||||||
|
|
|
@ -16,18 +16,18 @@ pub(crate) fn inline_local_varialbe(mut ctx: AssistCtx<impl HirDatabase>) -> Opt
|
||||||
if bind_pat.is_mutable() {
|
if bind_pat.is_mutable() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let initializer_expr = let_stmt.initializer();
|
let initializer_expr = let_stmt.initializer()?;
|
||||||
let delete_range = if let Some(whitespace) = let_stmt
|
let delete_range = if let Some(whitespace) = let_stmt
|
||||||
.syntax()
|
.syntax()
|
||||||
.next_sibling_or_token()
|
.next_sibling_or_token()
|
||||||
.and_then(|it| ast::Whitespace::cast(it.as_token()?))
|
.and_then(|it| ast::Whitespace::cast(it.as_token()?.clone()))
|
||||||
{
|
{
|
||||||
TextRange::from_to(let_stmt.syntax().range().start(), whitespace.syntax().range().end())
|
TextRange::from_to(let_stmt.syntax().range().start(), whitespace.syntax().range().end())
|
||||||
} else {
|
} else {
|
||||||
let_stmt.syntax().range()
|
let_stmt.syntax().range()
|
||||||
};
|
};
|
||||||
let analyzer = hir::SourceAnalyzer::new(ctx.db, ctx.frange.file_id, bind_pat.syntax(), None);
|
let analyzer = hir::SourceAnalyzer::new(ctx.db, ctx.frange.file_id, bind_pat.syntax(), None);
|
||||||
let refs = analyzer.find_all_refs(bind_pat);
|
let refs = analyzer.find_all_refs(&bind_pat);
|
||||||
|
|
||||||
let mut wrap_in_parens = vec![true; refs.len()];
|
let mut wrap_in_parens = vec![true; refs.len()];
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ pub(crate) fn inline_local_varialbe(mut ctx: AssistCtx<impl HirDatabase>) -> Opt
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
wrap_in_parens[i] = match (initializer_expr?.kind(), usage_parent.kind()) {
|
wrap_in_parens[i] = match (initializer_expr.kind(), usage_parent.kind()) {
|
||||||
(ExprKind::CallExpr(_), _)
|
(ExprKind::CallExpr(_), _)
|
||||||
| (ExprKind::IndexExpr(_), _)
|
| (ExprKind::IndexExpr(_), _)
|
||||||
| (ExprKind::MethodCallExpr(_), _)
|
| (ExprKind::MethodCallExpr(_), _)
|
||||||
|
@ -71,7 +71,7 @@ pub(crate) fn inline_local_varialbe(mut ctx: AssistCtx<impl HirDatabase>) -> Opt
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let init_str = initializer_expr?.syntax().text().to_string();
|
let init_str = initializer_expr.syntax().text().to_string();
|
||||||
let init_in_paren = format!("({})", &init_str);
|
let init_in_paren = format!("({})", &init_str);
|
||||||
|
|
||||||
ctx.add_action(
|
ctx.add_action(
|
||||||
|
|
|
@ -20,8 +20,8 @@ pub(crate) fn introduce_variable(mut ctx: AssistCtx<impl HirDatabase>) -> Option
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let expr = node.ancestors().find_map(valid_target_expr)?;
|
let expr = node.ancestors().find_map(valid_target_expr)?;
|
||||||
let (anchor_stmt, wrap_in_block) = anchor_stmt(expr)?;
|
let (anchor_stmt, wrap_in_block) = anchor_stmt(expr.clone())?;
|
||||||
let indent = anchor_stmt.prev_sibling_or_token()?.as_token()?;
|
let indent = anchor_stmt.prev_sibling_or_token()?.as_token()?.clone();
|
||||||
if indent.kind() != WHITESPACE {
|
if indent.kind() != WHITESPACE {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -37,9 +37,9 @@ pub(crate) fn introduce_variable(mut ctx: AssistCtx<impl HirDatabase>) -> Option
|
||||||
};
|
};
|
||||||
|
|
||||||
expr.syntax().text().push_to(&mut buf);
|
expr.syntax().text().push_to(&mut buf);
|
||||||
let full_stmt = ast::ExprStmt::cast(anchor_stmt);
|
let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone());
|
||||||
let is_full_stmt = if let Some(expr_stmt) = full_stmt {
|
let is_full_stmt = if let Some(expr_stmt) = &full_stmt {
|
||||||
Some(expr.syntax()) == expr_stmt.expr().map(|e| e.syntax())
|
Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone())
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
@ -81,7 +81,7 @@ pub(crate) fn introduce_variable(mut ctx: AssistCtx<impl HirDatabase>) -> Option
|
||||||
|
|
||||||
/// Check whether the node is a valid expression which can be extracted to a variable.
|
/// Check whether the node is a valid expression which can be extracted to a variable.
|
||||||
/// In general that's true for any expression, but in some cases that would produce invalid code.
|
/// In general that's true for any expression, but in some cases that would produce invalid code.
|
||||||
fn valid_target_expr(node: &SyntaxNode) -> Option<&ast::Expr> {
|
fn valid_target_expr(node: SyntaxNode) -> Option<ast::Expr> {
|
||||||
match node.kind() {
|
match node.kind() {
|
||||||
PATH_EXPR => None,
|
PATH_EXPR => None,
|
||||||
BREAK_EXPR => ast::BreakExpr::cast(node).and_then(|e| e.expr()),
|
BREAK_EXPR => ast::BreakExpr::cast(node).and_then(|e| e.expr()),
|
||||||
|
@ -96,14 +96,10 @@ fn valid_target_expr(node: &SyntaxNode) -> Option<&ast::Expr> {
|
||||||
/// to produce correct code.
|
/// to produce correct code.
|
||||||
/// It can be a statement, the last in a block expression or a wanna be block
|
/// It can be a statement, the last in a block expression or a wanna be block
|
||||||
/// expression like a lambda or match arm.
|
/// expression like a lambda or match arm.
|
||||||
fn anchor_stmt(expr: &ast::Expr) -> Option<(&SyntaxNode, bool)> {
|
fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> {
|
||||||
expr.syntax().ancestors().find_map(|node| {
|
expr.syntax().ancestors().find_map(|node| {
|
||||||
if ast::Stmt::cast(node).is_some() {
|
|
||||||
return Some((node, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(expr) = node.parent().and_then(ast::Block::cast).and_then(|it| it.expr()) {
|
if let Some(expr) = node.parent().and_then(ast::Block::cast).and_then(|it| it.expr()) {
|
||||||
if expr.syntax() == node {
|
if expr.syntax() == &node {
|
||||||
tested_by!(test_introduce_var_last_expr);
|
tested_by!(test_introduce_var_last_expr);
|
||||||
return Some((node, false));
|
return Some((node, false));
|
||||||
}
|
}
|
||||||
|
@ -115,6 +111,10 @@ fn anchor_stmt(expr: &ast::Expr) -> Option<(&SyntaxNode, bool)> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ast::Stmt::cast(node.clone()).is_some() {
|
||||||
|
return Some((node, false));
|
||||||
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,9 @@ pub(crate) fn move_guard_to_arm_body(mut ctx: AssistCtx<impl HirDatabase>) -> Op
|
||||||
|
|
||||||
ctx.add_action(AssistId("move_guard_to_arm_body"), "move guard to arm body", |edit| {
|
ctx.add_action(AssistId("move_guard_to_arm_body"), "move guard to arm body", |edit| {
|
||||||
edit.target(guard.syntax().range());
|
edit.target(guard.syntax().range());
|
||||||
let offseting_amount = match space_before_guard {
|
let offseting_amount = match &space_before_guard {
|
||||||
Some(SyntaxElement::Token(tok)) => {
|
Some(SyntaxElement::Token(tok)) => {
|
||||||
if let Some(_) = ast::Whitespace::cast(tok) {
|
if let Some(_) = ast::Whitespace::cast(tok.clone()) {
|
||||||
let ele = space_before_guard.unwrap().range();
|
let ele = space_before_guard.unwrap().range();
|
||||||
edit.delete(ele);
|
edit.delete(ele);
|
||||||
ele.len()
|
ele.len()
|
||||||
|
@ -39,11 +39,11 @@ pub(crate) fn move_guard_to_arm_body(mut ctx: AssistCtx<impl HirDatabase>) -> Op
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn move_arm_cond_to_match_guard(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
pub(crate) fn move_arm_cond_to_match_guard(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||||
let match_arm: &MatchArm = ctx.node_at_offset::<MatchArm>()?;
|
let match_arm: MatchArm = ctx.node_at_offset::<MatchArm>()?;
|
||||||
let last_match_pat = match_arm.pats().last()?;
|
let last_match_pat = match_arm.pats().last()?;
|
||||||
|
|
||||||
let arm_body = match_arm.expr()?;
|
let arm_body = match_arm.expr()?;
|
||||||
let if_expr: &IfExpr = IfExpr::cast(arm_body.syntax())?;
|
let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone())?;
|
||||||
let cond = if_expr.condition()?;
|
let cond = if_expr.condition()?;
|
||||||
let then_block = if_expr.then_branch()?;
|
let then_block = if_expr.then_branch()?;
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ pub(crate) fn move_arm_cond_to_match_guard(mut ctx: AssistCtx<impl HirDatabase>)
|
||||||
edit.target(if_expr.syntax().range());
|
edit.target(if_expr.syntax().range());
|
||||||
let then_only_expr = then_block.statements().next().is_none();
|
let then_only_expr = then_block.statements().next().is_none();
|
||||||
|
|
||||||
match then_block.expr() {
|
match &then_block.expr() {
|
||||||
Some(then_expr) if then_only_expr => {
|
Some(then_expr) if then_only_expr => {
|
||||||
edit.replace(if_expr.syntax().range(), then_expr.syntax().text())
|
edit.replace(if_expr.syntax().range(), then_expr.syntax().text())
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ use ra_syntax::{
|
||||||
pub(crate) fn remove_dbg(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
pub(crate) fn remove_dbg(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||||
let macro_call = ctx.node_at_offset::<ast::MacroCall>()?;
|
let macro_call = ctx.node_at_offset::<ast::MacroCall>()?;
|
||||||
|
|
||||||
if !is_valid_macrocall(macro_call, "dbg")? {
|
if !is_valid_macrocall(¯o_call, "dbg")? {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ pub(crate) fn remove_dbg(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist>
|
||||||
};
|
};
|
||||||
|
|
||||||
let macro_content = {
|
let macro_content = {
|
||||||
let macro_args = macro_call.token_tree()?.syntax();
|
let macro_args = macro_call.token_tree()?.syntax().clone();
|
||||||
let range = macro_args.range();
|
let range = macro_args.range();
|
||||||
let start = range.start() + TextUnit::of_char('(');
|
let start = range.start() + TextUnit::of_char('(');
|
||||||
let end = range.end() - TextUnit::of_char(')');
|
let end = range.end() - TextUnit::of_char(')');
|
||||||
|
@ -65,7 +65,7 @@ fn is_valid_macrocall(macro_call: &ast::MacroCall, macro_name: &str) -> Option<b
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let node = macro_call.token_tree()?.syntax();
|
let node = macro_call.token_tree()?.syntax().clone();
|
||||||
let first_child = node.first_child_or_token()?;
|
let first_child = node.first_child_or_token()?;
|
||||||
let last_child = node.last_child_or_token()?;
|
let last_child = node.last_child_or_token()?;
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use ra_syntax::{ast, AstNode};
|
||||||
use crate::{Assist, AssistCtx, AssistId};
|
use crate::{Assist, AssistCtx, AssistId};
|
||||||
|
|
||||||
pub(crate) fn replace_if_let_with_match(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
pub(crate) fn replace_if_let_with_match(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||||
let if_expr: &ast::IfExpr = ctx.node_at_offset()?;
|
let if_expr: ast::IfExpr = ctx.node_at_offset()?;
|
||||||
let cond = if_expr.condition()?;
|
let cond = if_expr.condition()?;
|
||||||
let pat = cond.pat()?;
|
let pat = cond.pat()?;
|
||||||
let expr = cond.expr()?;
|
let expr = cond.expr()?;
|
||||||
|
@ -25,16 +25,11 @@ pub(crate) fn replace_if_let_with_match(mut ctx: AssistCtx<impl HirDatabase>) ->
|
||||||
ctx.build()
|
ctx.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_match_expr(
|
fn build_match_expr(expr: ast::Expr, pat1: ast::Pat, arm1: ast::Block, arm2: ast::Block) -> String {
|
||||||
expr: &ast::Expr,
|
|
||||||
pat1: &ast::Pat,
|
|
||||||
arm1: &ast::Block,
|
|
||||||
arm2: &ast::Block,
|
|
||||||
) -> String {
|
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
buf.push_str(&format!("match {} {{\n", expr.syntax().text()));
|
buf.push_str(&format!("match {} {{\n", expr.syntax().text()));
|
||||||
buf.push_str(&format!(" {} => {}\n", pat1.syntax().text(), format_arm(arm1)));
|
buf.push_str(&format!(" {} => {}\n", pat1.syntax().text(), format_arm(&arm1)));
|
||||||
buf.push_str(&format!(" _ => {}\n", format_arm(arm2)));
|
buf.push_str(&format!(" _ => {}\n", format_arm(&arm2)));
|
||||||
buf.push_str("}");
|
buf.push_str("}");
|
||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ pub use self::{
|
||||||
/// conversion itself has zero runtime cost: ast and syntax nodes have exactly
|
/// conversion itself has zero runtime cost: ast and syntax nodes have exactly
|
||||||
/// the same representation: a pointer to the tree root and a pointer to the
|
/// the same representation: a pointer to the tree root and a pointer to the
|
||||||
/// node itself.
|
/// node itself.
|
||||||
pub trait AstNode {
|
pub trait AstNode: Clone {
|
||||||
fn cast(syntax: SyntaxNode) -> Option<Self>
|
fn cast(syntax: SyntaxNode) -> Option<Self>
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue