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/libanalysis/src/imp.rs | 4 ++- crates/libanalysis/src/lib.rs | 4 +-- crates/libeditor/src/code_actions.rs | 48 ++++++++++++++++++++++++++++++--- crates/libeditor/src/lib.rs | 1 + crates/libeditor/src/test_utils.rs | 19 ++++++++++++- crates/server/src/main_loop/handlers.rs | 6 ++--- 6 files changed, 72 insertions(+), 10 deletions(-) (limited to 'crates') diff --git a/crates/libanalysis/src/imp.rs b/crates/libanalysis/src/imp.rs index e3ccffbf0..47b0d79ff 100644 --- a/crates/libanalysis/src/imp.rs +++ b/crates/libanalysis/src/imp.rs @@ -256,12 +256,14 @@ impl AnalysisImpl { res } - pub fn assists(&self, file_id: FileId, offset: TextUnit) -> Vec { + pub fn assists(&self, file_id: FileId, range: TextRange) -> Vec { let file = self.file_syntax(file_id); + let offset = range.start(); let actions = vec![ ("flip comma", libeditor::flip_comma(&file, offset).map(|f| f())), ("add `#[derive]`", libeditor::add_derive(&file, offset).map(|f| f())), ("add impl", libeditor::add_impl(&file, offset).map(|f| f())), + ("introduce variable", libeditor::introduce_variable(&file, range).map(|f| f())), ]; actions.into_iter() .filter_map(|(name, local_edit)| { diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs index a8152939b..4e63813f9 100644 --- a/crates/libanalysis/src/lib.rs +++ b/crates/libanalysis/src/lib.rs @@ -209,8 +209,8 @@ impl Analysis { let file = self.file_syntax(file_id); libeditor::scope_completion(&file, offset) } - pub fn assists(&self, file_id: FileId, offset: TextUnit) -> Vec { - self.imp.assists(file_id, offset) + pub fn assists(&self, file_id: FileId, range: TextRange) -> Vec { + self.imp.assists(file_id, range) } pub fn diagnostics(&self, file_id: FileId) -> Vec { self.imp.diagnostics(file_id) 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); +} diff --git a/crates/server/src/main_loop/handlers.rs b/crates/server/src/main_loop/handlers.rs index 323d4e95e..3e02227d5 100644 --- a/crates/server/src/main_loop/handlers.rs +++ b/crates/server/src/main_loop/handlers.rs @@ -372,12 +372,12 @@ pub fn handle_code_action( ) -> Result>> { let file_id = params.text_document.try_conv_with(&world)?; let line_index = world.analysis().file_line_index(file_id); - let offset = params.range.conv_with(&line_index).start(); + let range = params.range.conv_with(&line_index); - let assists = world.analysis().assists(file_id, offset).into_iter(); + let assists = world.analysis().assists(file_id, range).into_iter(); let fixes = world.analysis().diagnostics(file_id).into_iter() .filter_map(|d| Some((d.range, d.fix?))) - .filter(|(range, _fix)| contains_offset_nonstrict(*range, offset)) + .filter(|(range, _fix)| contains_offset_nonstrict(*range, range.start())) .map(|(_range, fix)| fix); let mut res = Vec::new(); -- cgit v1.2.3