From bb64edf8babe617ca6219e53520ce87a2dd00769 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 6 Sep 2018 00:59:07 +0300 Subject: introduce variable --- crates/libeditor/src/code_actions.rs | 48 +++++++++++++++++++++++++++++++++--- crates/libeditor/src/lib.rs | 1 + crates/libeditor/src/test_utils.rs | 19 +++++++++++++- 3 files changed, 64 insertions(+), 4 deletions(-) (limited to 'crates/libeditor') diff --git a/crates/libeditor/src/code_actions.rs b/crates/libeditor/src/code_actions.rs index 522b605ed..ebe70681b 100644 --- a/crates/libeditor/src/code_actions.rs +++ b/crates/libeditor/src/code_actions.rs @@ -1,13 +1,15 @@ use join_to_string::join; use libsyntax2::{ - File, TextUnit, + File, TextUnit, TextRange, ast::{self, AstNode, AttrsOwner, TypeParamsOwner, NameOwner}, - SyntaxKind::COMMA, + SyntaxKind::{COMMA, WHITESPACE}, SyntaxNodeRef, algo::{ Direction, siblings, find_leaf_at_offset, + find_covering_node, + ancestors, }, }; @@ -97,6 +99,31 @@ pub fn add_impl<'a>(file: &'a File, offset: TextUnit) -> Option }) } +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(); + buf.push_str("let var_name = "); + expr.syntax().text().push_to(&mut buf); + buf.push_str(";"); + indent.text().push_to(&mut buf); + + let mut edit = EditBuilder::new(); + edit.replace(expr.syntax().range(), "var_name".to_string()); + edit.insert(anchor_stmt.syntax().range().start(), buf); + LocalEdit { + edit: edit.finish(), + cursor_position: Some(anchor_stmt.syntax().range().start() + TextUnit::of_str("let ")), + } + }) +} + fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option { siblings(node, direction) .skip(1) @@ -106,7 +133,7 @@ fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option1 + 1<|>); +}", " +fn foo() { + let <|>var_name = 1 + 1; + foo(var_name); +}", + |file, range| introduce_variable(file, range).map(|f| f()), + ); + } + } diff --git a/crates/libeditor/src/lib.rs b/crates/libeditor/src/lib.rs index 4700ef328..b3cf2ef55 100644 --- a/crates/libeditor/src/lib.rs +++ b/crates/libeditor/src/lib.rs @@ -32,6 +32,7 @@ pub use self::{ code_actions::{ LocalEdit, flip_comma, add_derive, add_impl, + introduce_variable, }, typing::{join_lines, on_eq_typed}, completion::{scope_completion, CompletionItem}, diff --git a/crates/libeditor/src/test_utils.rs b/crates/libeditor/src/test_utils.rs index 037319cd0..9c1279991 100644 --- a/crates/libeditor/src/test_utils.rs +++ b/crates/libeditor/src/test_utils.rs @@ -1,4 +1,4 @@ -use libsyntax2::{File, TextUnit}; +use libsyntax2::{File, TextUnit, TextRange}; pub use _test_utils::*; use LocalEdit; @@ -18,3 +18,20 @@ pub fn check_action Option> ( let actual = add_cursor(&actual, actual_cursor_pos); assert_eq_text!(after, &actual); } + +pub fn check_action_range Option> ( + before: &str, + after: &str, + f: F, +) { + let (range, before) = extract_range(before); + let file = File::parse(&before); + let result = f(&file, range).expect("code action is not applicable"); + let actual = result.edit.apply(&before); + let actual_cursor_pos = match result.cursor_position { + None => result.edit.apply_to_offset(range.start()).unwrap(), + Some(off) => off, + }; + let actual = add_cursor(&actual, actual_cursor_pos); + assert_eq_text!(after, &actual); +} -- cgit v1.2.3