Add derive handles cursor

This commit is contained in:
Aleksey Kladov 2018-08-16 13:11:20 +03:00
parent 7094291573
commit a5515d9d6f
5 changed files with 51 additions and 20 deletions

View file

@ -1,6 +1,6 @@
use {TextUnit, File, EditBuilder, Edit}; use {TextUnit, File, EditBuilder, Edit};
use libsyntax2::{ use libsyntax2::{
ast::{self, AstNode}, ast::{self, AstNode, AttrsOwner},
SyntaxKind::COMMA, SyntaxKind::COMMA,
SyntaxNodeRef, SyntaxNodeRef,
SyntaxRoot, SyntaxRoot,
@ -39,18 +39,28 @@ pub fn flip_comma<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce()
} }
pub fn add_derive<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> ActionResult + 'a> { pub fn add_derive<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> ActionResult + 'a> {
let syntax = file.syntax(); let nominal = find_node::<ast::NominalDef<_>>(file.syntax_ref(), offset)?;
let syntax = syntax.as_ref();
let nominal = find_node::<ast::NominalDef<_>>(syntax, offset)?;
Some(move || { Some(move || {
let derive_attr = nominal
.attrs()
.filter_map(|x| x.as_call())
.filter(|(name, _arg)| name == "derive")
.map(|(_name, arg)| arg)
.next();
let mut edit = EditBuilder::new(); let mut edit = EditBuilder::new();
let node_start = nominal.syntax().range().start(); let offset = match derive_attr {
edit.insert(node_start, "#[derive()]\n".to_string()); None => {
let node_start = nominal.syntax().range().start();
edit.insert(node_start, "#[derive()]\n".to_string());
node_start + TextUnit::of_str("#[derive(")
}
Some(tt) => {
tt.syntax().range().end() - TextUnit::of_char(')')
}
};
ActionResult { ActionResult {
edit: edit.finish(), edit: edit.finish(),
cursor_position: CursorPosition::Offset( cursor_position: CursorPosition::Offset(offset),
node_start + TextUnit::of_str("#[derive(")
),
} }
}) })
} }

View file

@ -116,7 +116,12 @@ fn test_add_derive() {
"struct Foo { a: i32, <|>}", "struct Foo { a: i32, <|>}",
"#[derive(<|>)]\nstruct Foo { a: i32, }", "#[derive(<|>)]\nstruct Foo { a: i32, }",
|file, off| add_derive(file, off).map(|f| f()), |file, off| add_derive(file, off).map(|f| f()),
) );
check_action(
"#[derive(Clone)]\nstruct Foo { a: i32<|>, }",
"#[derive(Clone<|>)]\nstruct Foo { a: i32, }",
|file, off| add_derive(file, off).map(|f| f()),
);
} }
#[test] #[test]

View file

@ -339,6 +339,7 @@ impl<R: TreeRoot> AstNode<R> for NominalDef<R> {
} }
} }
impl<R: TreeRoot> ast::AttrsOwner<R> for NominalDef<R> {}
impl<R: TreeRoot> NominalDef<R> {} impl<R: TreeRoot> NominalDef<R> {}
// ParenType // ParenType

View file

@ -52,19 +52,31 @@ impl<R: TreeRoot> File<R> {
impl<R: TreeRoot> FnDef<R> { impl<R: TreeRoot> FnDef<R> {
pub fn has_atom_attr(&self, atom: &str) -> bool { pub fn has_atom_attr(&self, atom: &str) -> bool {
self.attrs() self.attrs()
.filter_map(|x| x.value()) .filter_map(|x| x.as_atom())
.filter_map(|x| as_atom(x))
.any(|x| x == atom) .any(|x| x == atom)
} }
} }
fn as_atom<R: TreeRoot>(tt: TokenTree<R>) -> Option<SmolStr> { impl<R: TreeRoot> Attr<R> {
let syntax = tt.syntax_ref(); pub fn as_atom(&self) -> Option<SmolStr> {
let (_bra, attr, _ket) = syntax.children().collect_tuple()?; let tt = self.value()?;
if attr.kind() == IDENT { let (_bra, attr, _ket) = tt.syntax().children().collect_tuple()?;
Some(attr.leaf_text().unwrap()) if attr.kind() == IDENT {
} else { Some(attr.leaf_text().unwrap())
None } else {
None
}
}
pub fn as_call(&self) -> Option<(SmolStr, TokenTree<R>)> {
let tt = self.value()?;
let (_bra, attr, args, _ket) = tt.syntax().children().collect_tuple()?;
let args = TokenTree::cast(args)?;
if attr.kind() == IDENT {
Some((attr.leaf_text().unwrap(), args))
} else {
None
}
} }
} }

View file

@ -272,6 +272,9 @@ Grammar(
"DynTraitType", "DynTraitType",
]), ]),
"NominalDef": ( enum: ["StructDef", "EnumDef"]), "NominalDef": (
enum: ["StructDef", "EnumDef"],
traits: [ "AttrsOwner" ],
),
}, },
) )