migrate ra_assists to the new AST

This commit is contained in:
Aleksey Kladov 2019-07-19 11:24:41 +03:00
parent e2b28f5bb8
commit 0343c4a815
17 changed files with 213 additions and 204 deletions

View file

@ -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()

View file

@ -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;

View file

@ -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");

View file

@ -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() { _ => strip_docstring(it),
ImplItemKind::FnDef(def) => { });
strip_docstring(ImplItem::cast(add_body(def).syntax()).unwrap())
}
_ => 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),

View file

@ -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)
} }

View file

@ -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()
} }
} }

View file

@ -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);
}, },
); );

View file

@ -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());

View file

@ -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,

View file

@ -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);

View file

@ -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());

View file

@ -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(

View file

@ -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
}) })
} }

View file

@ -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())
} }

View file

@ -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(&macro_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()?;

View file

@ -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
} }

View file

@ -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;