From b5021411a84822cb3f1e3aeffad9550dd15bdeb6 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 16 Sep 2018 12:54:24 +0300 Subject: rename all things --- crates/ra_editor/src/code_actions.rs | 218 +++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 crates/ra_editor/src/code_actions.rs (limited to 'crates/ra_editor/src/code_actions.rs') diff --git a/crates/ra_editor/src/code_actions.rs b/crates/ra_editor/src/code_actions.rs new file mode 100644 index 000000000..83f7956d2 --- /dev/null +++ b/crates/ra_editor/src/code_actions.rs @@ -0,0 +1,218 @@ +use join_to_string::join; + +use ra_syntax::{ + File, TextUnit, TextRange, + ast::{self, AstNode, AttrsOwner, TypeParamsOwner, NameOwner}, + SyntaxKind::{COMMA, WHITESPACE}, + SyntaxNodeRef, + algo::{ + Direction, siblings, + find_leaf_at_offset, + find_covering_node, + ancestors, + }, +}; + +use {EditBuilder, Edit, find_node_at_offset}; + +#[derive(Debug)] +pub struct LocalEdit { + pub edit: Edit, + pub cursor_position: Option, +} + +pub fn flip_comma<'a>(file: &'a File, offset: TextUnit) -> Option LocalEdit + 'a> { + let syntax = file.syntax(); + + let comma = find_leaf_at_offset(syntax, offset).find(|leaf| leaf.kind() == COMMA)?; + let left = non_trivia_sibling(comma, Direction::Backward)?; + let right = non_trivia_sibling(comma, Direction::Forward)?; + Some(move || { + let mut edit = EditBuilder::new(); + edit.replace(left.range(), right.text().to_string()); + edit.replace(right.range(), left.text().to_string()); + LocalEdit { + edit: edit.finish(), + cursor_position: None, + } + }) +} + +pub fn add_derive<'a>(file: &'a File, offset: TextUnit) -> Option LocalEdit + 'a> { + let nominal = find_node_at_offset::(file.syntax(), offset)?; + 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 offset = match derive_attr { + 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(')') + } + }; + LocalEdit { + edit: edit.finish(), + cursor_position: Some(offset), + } + }) +} + +pub fn add_impl<'a>(file: &'a File, offset: TextUnit) -> Option LocalEdit + 'a> { + let nominal = find_node_at_offset::(file.syntax(), offset)?; + let name = nominal.name()?; + + Some(move || { + let type_params = nominal.type_param_list(); + let mut edit = EditBuilder::new(); + let start_offset = nominal.syntax().range().end(); + let mut buf = String::new(); + buf.push_str("\n\nimpl"); + if let Some(type_params) = type_params { + type_params.syntax().text() + .push_to(&mut buf); + } + buf.push_str(" "); + buf.push_str(name.text().as_str()); + if let Some(type_params) = type_params { + let lifetime_params = type_params.lifetime_params().filter_map(|it| it.lifetime()).map(|it| it.text()); + let type_params = type_params.type_params().filter_map(|it| it.name()).map(|it| it.text()); + join(lifetime_params.chain(type_params)) + .surround_with("<", ">") + .to_buf(&mut buf); + } + buf.push_str(" {\n"); + let offset = start_offset + TextUnit::of_str(&buf); + buf.push_str("\n}"); + edit.insert(start_offset, buf); + LocalEdit { + edit: edit.finish(), + cursor_position: Some(offset), + } + }) +} + +pub fn introduce_variable<'a>(file: &'a File, range: TextRange) -> Option LocalEdit + 'a> { + let node = find_covering_node(file.syntax(), range); + let expr = ancestors(node).filter_map(ast::Expr::cast).next()?; + let anchor_stmt = ancestors(expr.syntax()).filter_map(ast::Stmt::cast).next()?; + let indent = anchor_stmt.syntax().prev_sibling()?; + if indent.kind() != WHITESPACE { + return None; + } + Some(move || { + let mut buf = String::new(); + let mut edit = EditBuilder::new(); + + buf.push_str("let var_name = "); + expr.syntax().text().push_to(&mut buf); + if expr.syntax().range().start() == anchor_stmt.syntax().range().start() { + edit.replace(expr.syntax().range(), buf); + } else { + buf.push_str(";"); + indent.text().push_to(&mut buf); + edit.replace(expr.syntax().range(), "var_name".to_string()); + edit.insert(anchor_stmt.syntax().range().start(), buf); + } + let cursor_position = anchor_stmt.syntax().range().start() + TextUnit::of_str("let "); + LocalEdit { + edit: edit.finish(), + cursor_position: Some(cursor_position), + } + }) +} + +fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option { + siblings(node, direction) + .skip(1) + .find(|node| !node.kind().is_trivia()) +} + +#[cfg(test)] +mod tests { + use super::*; + use test_utils::{check_action, check_action_range}; + + #[test] + fn test_swap_comma() { + check_action( + "fn foo(x: i32,<|> y: Result<(), ()>) {}", + "fn foo(y: Result<(), ()>,<|> x: i32) {}", + |file, off| flip_comma(file, off).map(|f| f()), + ) + } + + #[test] + fn test_add_derive() { + check_action( + "struct Foo { a: i32, <|>}", + "#[derive(<|>)]\nstruct Foo { a: i32, }", + |file, off| add_derive(file, off).map(|f| f()), + ); + check_action( + "struct Foo { <|> a: i32, }", + "#[derive(<|>)]\nstruct Foo { a: i32, }", + |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] + fn test_add_impl() { + check_action( + "struct Foo {<|>}\n", + "struct Foo {}\n\nimpl Foo {\n<|>\n}\n", + |file, off| add_impl(file, off).map(|f| f()), + ); + check_action( + "struct Foo {<|>}", + "struct Foo {}\n\nimpl Foo {\n<|>\n}", + |file, off| add_impl(file, off).map(|f| f()), + ); + check_action( + "struct Foo<'a, T: Foo<'a>> {<|>}", + "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n<|>\n}", + |file, off| add_impl(file, off).map(|f| f()), + ); + } + + #[test] + fn test_intrdoduce_var_simple() { + check_action_range( + " +fn foo() { + foo(<|>1 + 1<|>); +}", " +fn foo() { + let <|>var_name = 1 + 1; + foo(var_name); +}", + |file, range| introduce_variable(file, range).map(|f| f()), + ); + } + #[test] + fn test_intrdoduce_var_expr_stmt() { +check_action_range( + " +fn foo() { + <|>1 + 1<|>; +}", " +fn foo() { + let <|>var_name = 1 + 1; +}", + |file, range| introduce_variable(file, range).map(|f| f()), + ); + } + +} -- cgit v1.2.3