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> {
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| {
let derive_attr = nominal
.attrs()

View File

@ -27,7 +27,7 @@ pub(crate) fn add_explicit_type(mut ctx: AssistCtx<impl HirDatabase>) -> Option<
// Infer type
let db = ctx.db;
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
if is_unknown(&ty) {
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 mut buf = String::new();
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);
}
buf.push_str(" ");
@ -25,9 +25,9 @@ pub(crate) fn add_impl(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
let lifetime_params = type_params
.lifetime_params()
.filter_map(|it| it.lifetime_token())
.map(|it| it.text());
.map(|it| it.text().clone());
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);
}
buf.push_str(" {\n");

View File

@ -5,8 +5,8 @@ use crate::{
use hir::{db::HirDatabase, HasSource};
use ra_db::FilePosition;
use ra_syntax::ast::{self, AstNode, ImplItem, ImplItemKind, NameOwner};
use ra_syntax::{SmolStr, TreeArc};
use ra_syntax::ast::{self, AstNode, ImplItemKind, NameOwner};
use ra_syntax::SmolStr;
#[derive(PartialEq)]
enum AddMissingImplMembersMode {
@ -46,16 +46,16 @@ fn add_missing_impl_members_inner(
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);
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 {
ImplItemKind::FnDef(def) => def.name(),
ImplItemKind::TypeAliasDef(def) => def.name(),
ImplItemKind::ConstDef(def) => def.name(),
ast::ImplItemKind::FnDef(def) => def.name(),
ast::ImplItemKind::TypeAliasDef(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();
@ -78,18 +78,13 @@ fn add_missing_impl_members_inner(
ctx.add_action(AssistId(assist_id), label, |edit| {
let n_existing_items = impl_item_list.impl_items().count();
let items: Vec<_> = missing_items
.into_iter()
.map(|it| match it.kind() {
ImplItemKind::FnDef(def) => {
strip_docstring(ImplItem::cast(add_body(def).syntax()).unwrap())
}
_ => strip_docstring(it),
})
.collect();
let items = missing_items.into_iter().map(|it| match it.kind() {
ImplItemKind::FnDef(def) => strip_docstring(add_body(def).into()),
_ => strip_docstring(it),
});
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 cursor_position = first_new_item.syntax().range().start();
@ -101,14 +96,14 @@ fn add_missing_impl_members_inner(
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);
ast_editor.strip_attrs_and_docs();
ast_editor.ast().to_owned()
}
fn add_body(fn_def: &ast::FnDef) -> TreeArc<ast::FnDef> {
let mut ast_editor = AstEditor::new(fn_def);
fn add_body(fn_def: ast::FnDef) -> ast::FnDef {
let mut ast_editor = AstEditor::new(fn_def.clone());
if fn_def.body().is_none() {
ast_editor.set_body(&AstBuilder::<ast::Block>::single_expr(
&AstBuilder::<ast::Expr>::unimplemented(),
@ -123,9 +118,12 @@ fn resolve_target_trait_def(
db: &impl HirDatabase,
analyzer: &hir::SourceAnalyzer,
impl_block: &ast::ImplBlock,
) -> Option<TreeArc<ast::TraitDef>> {
let ast_path =
impl_block.target_trait().map(AstNode::syntax).and_then(ast::PathType::cast)?.path()?;
) -> Option<ast::TraitDef> {
let ast_path = impl_block
.target_trait()
.map(|it| it.syntax().clone())
.and_then(ast::PathType::cast)?
.path()?;
match analyzer.resolve_path(db, &ast_path) {
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) db: &'a DB,
pub(crate) frange: FileRange,
source_file: &'a SourceFile,
source_file: SourceFile,
should_compute_edit: bool,
assist: Assist,
}
@ -59,7 +59,7 @@ impl<'a, DB> Clone for AssistCtx<'a, DB> {
AssistCtx {
db: self.db,
frange: self.frange,
source_file: self.source_file,
source_file: self.source_file.clone(),
should_compute_edit: self.should_compute_edit,
assist: self.assist.clone(),
}
@ -104,18 +104,18 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> {
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())
}
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())
}
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)
}
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)
}
}
@ -139,7 +139,7 @@ impl AssistBuilder {
) {
let mut replace_with = replace_with.into();
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)
}

View File

@ -4,18 +4,18 @@ use arrayvec::ArrayVec;
use hir::Name;
use ra_fmt::leading_indent;
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;
pub struct AstEditor<N: AstNode> {
original_ast: TreeArc<N>,
ast: TreeArc<N>,
original_ast: N,
ast: N,
}
impl<N: AstNode> AstEditor<N> {
pub fn new(node: &N) -> AstEditor<N> {
AstEditor { original_ast: node.to_owned(), ast: node.to_owned() }
pub fn new(node: N) -> AstEditor<N> {
AstEditor { original_ast: node.clone(), ast: node }
}
pub fn into_text_edit(self, builder: &mut TextEditBuilder) {
@ -26,27 +26,27 @@ impl<N: AstNode> AstEditor<N> {
}
pub fn ast(&self) -> &N {
&*self.ast
&self.ast
}
#[must_use]
fn insert_children<'a>(
fn insert_children(
&self,
position: InsertPosition<SyntaxElement<'_>>,
to_insert: impl Iterator<Item = SyntaxElement<'a>>,
) -> TreeArc<N> {
position: InsertPosition<SyntaxElement>,
to_insert: impl Iterator<Item = SyntaxElement>,
) -> N {
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]
fn replace_children<'a>(
fn replace_children(
&self,
to_delete: RangeInclusive<SyntaxElement<'_>>,
to_insert: impl Iterator<Item = SyntaxElement<'a>>,
) -> TreeArc<N> {
to_delete: RangeInclusive<SyntaxElement>,
to_insert: impl Iterator<Item = SyntaxElement>,
) -> N {
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) {
@ -66,16 +66,18 @@ impl<N: AstNode> AstEditor<N> {
if ws.text().contains('\n') {
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 to_insert = iter::once(ws.ws().into());
self.ast = match existing_ws {
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 {
ws = tokens::WsBuilder::new(&format!(
"\n{} ",
leading_indent(self.ast().syntax()).unwrap_or("")
leading_indent(self.ast().syntax()).unwrap_or("".into())
));
ws.ws()
} else {
@ -104,7 +106,7 @@ impl AstEditor<ast::NamedFieldList> {
let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new();
to_insert.push(space.into());
to_insert.push(field.syntax().into());
to_insert.push(field.syntax().clone().into());
to_insert.push(tokens::comma().into());
macro_rules! after_l_curly {
@ -127,7 +129,7 @@ impl AstEditor<ast::NamedFieldList> {
InsertPosition::After(comma)
} else {
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!(),
}
}
InsertPosition::Before(anchor) => InsertPosition::Before(anchor.syntax().into()),
InsertPosition::Before(anchor) => {
InsertPosition::Before(anchor.syntax().clone().into())
}
InsertPosition::After(anchor) => after_field!(anchor),
};
@ -157,7 +161,7 @@ impl AstEditor<ast::NamedFieldList> {
}
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();
if n_existing_items == 0 {
self.do_make_multiline();
@ -165,22 +169,23 @@ impl AstEditor<ast::ItemList> {
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() {
Some(it) => (
leading_indent(it.syntax()).unwrap_or("").to_string(),
InsertPosition::After(it.syntax().into()),
leading_indent(it.syntax()).unwrap_or_default().to_string(),
InsertPosition::After(it.syntax().clone().into()),
),
None => match self.l_curly() {
Some(it) => (
" ".to_string() + leading_indent(self.ast().syntax()).unwrap_or(""),
" ".to_string() + &leading_indent(self.ast().syntax()).unwrap_or_default(),
InsertPosition::After(it),
),
None => return,
},
};
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());
}
@ -197,9 +202,9 @@ impl AstEditor<ast::ImplItem> {
.children_with_tokens()
.find(|it| it.kind() == ATTR || it.kind() == COMMENT)
{
let end = match start.next_sibling_or_token() {
Some(el) if el.kind() == WHITESPACE => el,
Some(_) | None => start,
let end = match &start.next_sibling_or_token() {
Some(el) if el.kind() == WHITESPACE => el.clone(),
Some(_) | None => start.clone(),
};
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) {
let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new();
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() {
to_insert.push(tokens::single_space().into());
semi.into()
} else {
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());
return;
};
to_insert.push(body.syntax().into());
let replace_range = RangeInclusive::new(old_body_or_semi, old_body_or_semi);
to_insert.push(body.syntax().clone().into());
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())
}
}
@ -231,15 +236,15 @@ pub struct AstBuilder<N: AstNode> {
}
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))
}
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))
}
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 {
Some(expr) => Self::from_text(&format!("{}: {}", name.syntax(), expr.syntax())),
None => Self::from_text(&name.syntax().to_string()),
@ -248,36 +253,36 @@ impl AstBuilder<ast::NamedField> {
}
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))
}
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()))
}
}
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))
}
pub fn unit() -> TreeArc<ast::Expr> {
pub fn unit() -> ast::Expr {
Self::from_text("()")
}
pub fn unimplemented() -> TreeArc<ast::Expr> {
pub fn unimplemented() -> ast::Expr {
Self::from_text("unimplemented!()")
}
}
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))
}
}
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 res = parse.tree().syntax().descendants().find_map(N::cast).unwrap().to_owned();
res
@ -285,47 +290,49 @@ fn ast_node_from_file_text<N: AstNode>(text: &str) -> TreeArc<N> {
mod tokens {
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>> =
Lazy::new(|| SourceFile::parse(",\n; ;").tree().to_owned());
static SOURCE_FILE: Lazy<Parse<SourceFile>> = Lazy::new(|| SourceFile::parse(",\n; ;"));
pub(crate) fn comma() -> SyntaxToken<'static> {
pub(crate) fn comma() -> SyntaxToken {
SOURCE_FILE
.tree()
.syntax()
.descendants_with_tokens()
.filter_map(|it| it.as_token())
.filter_map(|it| it.as_token().cloned())
.find(|it| it.kind() == T![,])
.unwrap()
}
pub(crate) fn single_space() -> SyntaxToken<'static> {
pub(crate) fn single_space() -> SyntaxToken {
SOURCE_FILE
.tree()
.syntax()
.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() == " ")
.unwrap()
}
#[allow(unused)]
pub(crate) fn single_newline() -> SyntaxToken<'static> {
pub(crate) fn single_newline() -> SyntaxToken {
SOURCE_FILE
.tree()
.syntax()
.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")
.unwrap()
}
pub(crate) struct WsBuilder(TreeArc<SourceFile>);
pub(crate) struct WsBuilder(SourceFile);
impl WsBuilder {
pub(crate) fn new(text: &str) -> WsBuilder {
WsBuilder(SourceFile::parse(text).ok().unwrap())
}
pub(crate) fn ws(&self) -> SyntaxToken<'_> {
self.0.syntax().first_child_or_token().unwrap().as_token().unwrap()
pub(crate) fn ws(&self) -> SyntaxToken {
self.0.syntax().first_child_or_token().unwrap().as_token().cloned().unwrap()
}
}

View File

@ -12,25 +12,25 @@ use ra_syntax::{
SyntaxNode, TextRange, T,
};
fn collect_path_segments_raw<'a>(
segments: &mut Vec<&'a ast::PathSegment>,
mut path: &'a ast::Path,
fn collect_path_segments_raw(
segments: &mut Vec<ast::PathSegment>,
mut path: ast::Path,
) -> Option<usize> {
let oldlen = segments.len();
loop {
let mut children = path.syntax().children_with_tokens();
let (first, second, third) = (
children.next().map(|n| (n, n.kind())),
children.next().map(|n| (n, n.kind())),
children.next().map(|n| (n, n.kind())),
children.next().map(|n| (n.clone(), n.kind())),
children.next().map(|n| (n.clone(), n.kind())),
children.next().map(|n| (n.clone(), n.kind())),
);
match (first, second, third) {
(Some((subpath, PATH)), Some((_, T![::])), Some((segment, PATH_SEGMENT))) => {
path = ast::Path::cast(subpath.as_node()?)?;
segments.push(ast::PathSegment::cast(segment.as_node()?)?);
path = ast::Path::cast(subpath.as_node()?.clone())?;
segments.push(ast::PathSegment::cast(segment.as_node()?.clone())?);
}
(Some((segment, PATH_SEGMENT)), _, _) => {
segments.push(ast::PathSegment::cast(segment.as_node()?)?);
segments.push(ast::PathSegment::cast(segment.as_node()?.clone())?);
break;
}
(_, _, _) => return None,
@ -60,7 +60,7 @@ fn fmt_segments_raw(segments: &[SmolStr], buf: &mut String) {
}
// 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()
}
@ -81,12 +81,12 @@ fn compare_path_segment_with_name(a: &SmolStr, b: &ast::Name) -> bool {
a == b.text()
}
#[derive(Copy, Clone)]
enum ImportAction<'a> {
#[derive(Clone)]
enum ImportAction {
Nothing,
// Add a brand new use statement.
AddNewUse {
anchor: Option<&'a SyntaxNode>, // anchor node
anchor: Option<SyntaxNode>, // anchor node
add_after_anchor: bool,
},
@ -94,9 +94,9 @@ enum ImportAction<'a> {
AddNestedImport {
// how may segments matched with the target path
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
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
add_self: bool,
},
@ -104,20 +104,20 @@ enum ImportAction<'a> {
AddInTreeList {
common_segments: usize,
// The UseTreeList where to add the target path
tree_list: &'a ast::UseTreeList,
tree_list: ast::UseTreeList,
add_self: bool,
},
}
impl<'a> ImportAction<'a> {
fn add_new_use(anchor: Option<&'a SyntaxNode>, add_after_anchor: bool) -> Self {
impl ImportAction {
fn add_new_use(anchor: Option<SyntaxNode>, add_after_anchor: bool) -> Self {
ImportAction::AddNewUse { anchor, add_after_anchor }
}
fn add_nested_import(
common_segments: usize,
path_to_split: &'a ast::Path,
first_segment_to_split: Option<&'a ast::PathSegment>,
path_to_split: ast::Path,
first_segment_to_split: Option<ast::PathSegment>,
add_self: bool,
) -> Self {
ImportAction::AddNestedImport {
@ -130,14 +130,14 @@ impl<'a> ImportAction<'a> {
fn add_in_tree_list(
common_segments: usize,
tree_list: &'a ast::UseTreeList,
tree_list: ast::UseTreeList,
add_self: bool,
) -> Self {
ImportAction::AddInTreeList { common_segments, tree_list, add_self }
}
fn better<'b>(left: &'b ImportAction<'a>, right: &'b ImportAction<'a>) -> &'b ImportAction<'a> {
if left.is_better(right) {
fn better(left: ImportAction, right: ImportAction) -> ImportAction {
if left.is_better(&right) {
left
} else {
right
@ -166,12 +166,12 @@ impl<'a> ImportAction<'a> {
// 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.
fn walk_use_tree_for_best_action<'a>(
current_path_segments: &mut Vec<&'a 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_use_tree: &'a ast::UseTree, // the use tree we are currently examinating
target: &[SmolStr], // the path we want to import
) -> ImportAction<'a> {
fn walk_use_tree_for_best_action(
current_path_segments: &mut Vec<ast::PathSegment>, // buffer containing path segments
current_parent_use_tree_list: Option<ast::UseTreeList>, // will be Some value if we are in a nested import
current_use_tree: ast::UseTree, // the use tree we are currently examinating
target: &[SmolStr], // the path we want to import
) -> ImportAction {
// 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.
let prev_len = current_path_segments.len();
@ -188,32 +188,36 @@ fn walk_use_tree_for_best_action<'a>(
.syntax()
.ancestors()
.find_map(ast::UseItem::cast)
.map(AstNode::syntax),
.map(|it| it.syntax().clone()),
true,
);
}
};
// 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 compare_path_segment_with_name(&target[0], name) {
if let Some(name) = alias.and_then(|it| it.name()) {
if compare_path_segment_with_name(&target[0], &name) {
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.
// The first prev_len segments were already compared in 'parent' recursive calls.
let left = target.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 {
0 => ImportAction::add_new_use(
// e.g: target is std::fmt and we can have
// use foo::bar
// 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,
),
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 {
// In case 2 we need to add self to the nested list
// unless it's already there
let has_self = list.use_trees().map(ast::UseTree::path).any(|p| {
p.and_then(ast::Path::segment)
.and_then(ast::PathSegment::kind)
let has_self = list.use_trees().map(|it| it.path()).any(|p| {
p.and_then(|it| it.segment())
.and_then(|it| it.kind())
.filter(|k| *k == ast::PathSegmentKind::SelfKw)
.is_some()
});
@ -248,7 +252,7 @@ fn walk_use_tree_for_best_action<'a>(
ImportAction::add_nested_import(
prev_len + common,
path,
Some(segments_to_split[0]),
Some(segments_to_split[0].clone()),
false,
)
}
@ -263,14 +267,18 @@ fn walk_use_tree_for_best_action<'a>(
.syntax()
.ancestors()
.find_map(ast::UseItem::cast)
.map(AstNode::syntax),
.map(|it| it.syntax().clone()),
true,
);
if let Some(list) = tree_list {
// Case 2, check recursively if the path is already imported in the nested list
for u in list.use_trees() {
let child_action =
walk_use_tree_for_best_action(current_path_segments, Some(list), u, target);
let child_action = walk_use_tree_for_best_action(
current_path_segments,
Some(list.clone()),
u,
target,
);
if child_action.is_better(&better_action) {
better_action = child_action;
if let ImportAction::Nothing = better_action {
@ -291,7 +299,7 @@ fn walk_use_tree_for_best_action<'a>(
ImportAction::add_nested_import(
prev_len + common,
path,
Some(segments_to_split[0]),
Some(segments_to_split[0].clone()),
true,
)
}
@ -302,7 +310,7 @@ fn walk_use_tree_for_best_action<'a>(
ImportAction::add_nested_import(
prev_len + common,
path,
Some(segments_to_split[0]),
Some(segments_to_split[0].clone()),
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
// 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 { .. }) => {
ImportAction::add_in_tree_list(prev_len, use_tree_list, false)
}
@ -323,19 +331,20 @@ fn walk_use_tree_for_best_action<'a>(
action
}
fn best_action_for_target<'b, 'a: 'b>(
container: &'a SyntaxNode,
anchor: &'a SyntaxNode,
target: &'b [SmolStr],
) -> ImportAction<'a> {
fn best_action_for_target(
container: SyntaxNode,
anchor: SyntaxNode,
target: &[SmolStr],
) -> ImportAction {
let mut storage = Vec::with_capacity(16); // this should be the only allocation
let best_action = container
.children()
.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))
.fold(None, |best, a| {
best.and_then(|best| Some(*ImportAction::better(&best, &a))).or_else(|| Some(a))
.fold(None, |best, a| match best {
Some(best) => Some(ImportAction::better(best, a)),
None => Some(a),
});
match best_action {
@ -386,7 +395,7 @@ fn make_assist(action: &ImportAction, target: &[SmolStr], edit: &mut TextEditBui
}
fn make_assist_add_new_use(
anchor: &Option<&SyntaxNode>,
anchor: &Option<SyntaxNode>,
after: bool,
target: &[SmolStr],
edit: &mut TextEditBuilder,
@ -396,7 +405,7 @@ fn make_assist_add_new_use(
let mut buf = String::new();
if after {
buf.push_str("\n");
if let Some(spaces) = indent {
if let Some(spaces) = &indent {
buf.push_str(spaces);
}
}
@ -405,8 +414,8 @@ fn make_assist_add_new_use(
buf.push_str(";");
if !after {
buf.push_str("\n\n");
if let Some(spaces) = indent {
buf.push_str(spaces);
if let Some(spaces) = &indent {
buf.push_str(&spaces);
}
}
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(
path: &ast::Path,
first_segment_to_split: &Option<&ast::PathSegment>,
first_segment_to_split: &Option<ast::PathSegment>,
target: &[SmolStr],
add_self: bool,
edit: &mut TextEditBuilder,
@ -482,7 +491,7 @@ fn apply_auto_import(
target: &[SmolStr],
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);
if let Some(last) = path.segment() {
// 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,
) {
let container = position.ancestors().find_map(|n| {
if let Some(module) = ast::Module::cast(n) {
return module.item_list().map(ast::AstNode::syntax);
if let Some(module) = ast::Module::cast(n.clone()) {
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 {
let action = best_action_for_target(container, anchor, target);
let action = best_action_for_target(container, anchor.clone(), target);
make_assist(&action, target, edit);
}
}
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
if path.syntax().ancestors().find_map(ast::UseItem::cast).is_some() {
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);
if segments.len() < 2 {
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()),
|edit| {
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);
},
);
@ -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)),
|edit| {
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);
},
);

View File

@ -35,7 +35,7 @@ fn add_vis(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
if parent.children().any(|child| child.kind() == VISIBILITY) {
return None;
}
(vis_offset(parent), keyword.range())
(vis_offset(&parent), keyword.range())
} else {
let ident = ctx.token_at_offset().find(|leaf| leaf.kind() == IDENT)?;
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())
}
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" {
ctx.add_action(AssistId("change_visibility"), "change to pub(crate)", |edit| {
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 first = arm_iter.next();
match first {
match &first {
// If there arm list is empty or there is only one trivial arm, then proceed.
Some(arm) if is_trivial_arm(arm) => {
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 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() {
Some((AdtDef::Enum(e), _)) => Some(e),
_ => None,

View File

@ -6,8 +6,8 @@ use crate::{Assist, AssistCtx, AssistId};
/// Flip binary expression assist.
pub(crate) fn flip_binexpr(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
let expr = ctx.node_at_offset::<BinExpr>()?;
let lhs = expr.lhs()?.syntax();
let rhs = expr.rhs()?.syntax();
let lhs = expr.lhs()?.syntax().clone();
let rhs = expr.rhs()?.syntax().clone();
let op_range = expr.op_token()?.range();
// The assist should be applied only if the cursor is on the operator
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> {
let comma = ctx.token_at_offset().find(|leaf| leaf.kind() == T![,])?;
let prev = non_trivia_sibling(comma.into(), Direction::Prev)?;
let next = non_trivia_sibling(comma.into(), Direction::Next)?;
let prev = non_trivia_sibling(comma.clone().into(), Direction::Prev)?;
let next = non_trivia_sibling(comma.clone().into(), Direction::Next)?;
ctx.add_action(AssistId("flip_comma"), "flip comma", |edit| {
edit.target(comma.range());
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() {
return None;
}
let initializer_expr = let_stmt.initializer();
let initializer_expr = let_stmt.initializer()?;
let delete_range = if let Some(whitespace) = let_stmt
.syntax()
.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())
} else {
let_stmt.syntax().range()
};
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()];
@ -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::IndexExpr(_), _)
| (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);
ctx.add_action(

View File

@ -20,8 +20,8 @@ pub(crate) fn introduce_variable(mut ctx: AssistCtx<impl HirDatabase>) -> Option
return None;
}
let expr = node.ancestors().find_map(valid_target_expr)?;
let (anchor_stmt, wrap_in_block) = anchor_stmt(expr)?;
let indent = anchor_stmt.prev_sibling_or_token()?.as_token()?;
let (anchor_stmt, wrap_in_block) = anchor_stmt(expr.clone())?;
let indent = anchor_stmt.prev_sibling_or_token()?.as_token()?.clone();
if indent.kind() != WHITESPACE {
return None;
}
@ -37,9 +37,9 @@ pub(crate) fn introduce_variable(mut ctx: AssistCtx<impl HirDatabase>) -> Option
};
expr.syntax().text().push_to(&mut buf);
let full_stmt = ast::ExprStmt::cast(anchor_stmt);
let is_full_stmt = if let Some(expr_stmt) = full_stmt {
Some(expr.syntax()) == expr_stmt.expr().map(|e| e.syntax())
let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone());
let is_full_stmt = if let Some(expr_stmt) = &full_stmt {
Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone())
} else {
false
};
@ -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.
/// 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() {
PATH_EXPR => None,
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.
/// It can be a statement, the last in a block expression or a wanna be block
/// 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| {
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 expr.syntax() == node {
if expr.syntax() == &node {
tested_by!(test_introduce_var_last_expr);
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
})
}

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| {
edit.target(guard.syntax().range());
let offseting_amount = match space_before_guard {
let offseting_amount = match &space_before_guard {
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();
edit.delete(ele);
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> {
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 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 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());
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 => {
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> {
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;
}
@ -35,7 +35,7 @@ pub(crate) fn remove_dbg(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist>
};
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 start = range.start() + 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;
}
let node = macro_call.token_tree()?.syntax();
let node = macro_call.token_tree()?.syntax().clone();
let first_child = node.first_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};
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 pat = cond.pat()?;
let expr = cond.expr()?;
@ -25,16 +25,11 @@ pub(crate) fn replace_if_let_with_match(mut ctx: AssistCtx<impl HirDatabase>) ->
ctx.build()
}
fn build_match_expr(
expr: &ast::Expr,
pat1: &ast::Pat,
arm1: &ast::Block,
arm2: &ast::Block,
) -> String {
fn build_match_expr(expr: ast::Expr, pat1: ast::Pat, arm1: ast::Block, arm2: ast::Block) -> String {
let mut buf = String::new();
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", format_arm(arm2)));
buf.push_str(&format!(" {} => {}\n", pat1.syntax().text(), format_arm(&arm1)));
buf.push_str(&format!(" _ => {}\n", format_arm(&arm2)));
buf.push_str("}");
buf
}

View File

@ -25,7 +25,7 @@ pub use self::{
/// 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
/// node itself.
pub trait AstNode {
pub trait AstNode: Clone {
fn cast(syntax: SyntaxNode) -> Option<Self>
where
Self: Sized;