migrate ra_assists to the new AST
This commit is contained in:
parent
e2b28f5bb8
commit
0343c4a815
@ -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()
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
},
|
||||
);
|
||||
|
@ -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());
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
})
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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(¯o_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()?;
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user