diff options
Diffstat (limited to 'crates/libeditor')
-rw-r--r-- | crates/libeditor/src/code_actions.rs | 48 | ||||
-rw-r--r-- | crates/libeditor/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/libeditor/src/test_utils.rs | 19 |
3 files changed, 64 insertions, 4 deletions
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 @@ | |||
1 | use join_to_string::join; | 1 | use join_to_string::join; |
2 | 2 | ||
3 | use libsyntax2::{ | 3 | use libsyntax2::{ |
4 | File, TextUnit, | 4 | File, TextUnit, TextRange, |
5 | ast::{self, AstNode, AttrsOwner, TypeParamsOwner, NameOwner}, | 5 | ast::{self, AstNode, AttrsOwner, TypeParamsOwner, NameOwner}, |
6 | SyntaxKind::COMMA, | 6 | SyntaxKind::{COMMA, WHITESPACE}, |
7 | SyntaxNodeRef, | 7 | SyntaxNodeRef, |
8 | algo::{ | 8 | algo::{ |
9 | Direction, siblings, | 9 | Direction, siblings, |
10 | find_leaf_at_offset, | 10 | find_leaf_at_offset, |
11 | find_covering_node, | ||
12 | ancestors, | ||
11 | }, | 13 | }, |
12 | }; | 14 | }; |
13 | 15 | ||
@@ -97,6 +99,31 @@ pub fn add_impl<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() -> | |||
97 | }) | 99 | }) |
98 | } | 100 | } |
99 | 101 | ||
102 | pub fn introduce_variable<'a>(file: &'a File, range: TextRange) -> Option<impl FnOnce() -> LocalEdit + 'a> { | ||
103 | let node = find_covering_node(file.syntax(), range); | ||
104 | let expr = ancestors(node).filter_map(ast::Expr::cast).next()?; | ||
105 | let anchor_stmt = ancestors(expr.syntax()).filter_map(ast::Stmt::cast).next()?; | ||
106 | let indent = anchor_stmt.syntax().prev_sibling()?; | ||
107 | if indent.kind() != WHITESPACE { | ||
108 | return None; | ||
109 | } | ||
110 | Some(move || { | ||
111 | let mut buf = String::new(); | ||
112 | buf.push_str("let var_name = "); | ||
113 | expr.syntax().text().push_to(&mut buf); | ||
114 | buf.push_str(";"); | ||
115 | indent.text().push_to(&mut buf); | ||
116 | |||
117 | let mut edit = EditBuilder::new(); | ||
118 | edit.replace(expr.syntax().range(), "var_name".to_string()); | ||
119 | edit.insert(anchor_stmt.syntax().range().start(), buf); | ||
120 | LocalEdit { | ||
121 | edit: edit.finish(), | ||
122 | cursor_position: Some(anchor_stmt.syntax().range().start() + TextUnit::of_str("let ")), | ||
123 | } | ||
124 | }) | ||
125 | } | ||
126 | |||
100 | fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option<SyntaxNodeRef> { | 127 | fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option<SyntaxNodeRef> { |
101 | siblings(node, direction) | 128 | siblings(node, direction) |
102 | .skip(1) | 129 | .skip(1) |
@@ -106,7 +133,7 @@ fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option<Synta | |||
106 | #[cfg(test)] | 133 | #[cfg(test)] |
107 | mod tests { | 134 | mod tests { |
108 | use super::*; | 135 | use super::*; |
109 | use test_utils::check_action; | 136 | use test_utils::{check_action, check_action_range}; |
110 | 137 | ||
111 | #[test] | 138 | #[test] |
112 | fn test_swap_comma() { | 139 | fn test_swap_comma() { |
@@ -155,4 +182,19 @@ mod tests { | |||
155 | ); | 182 | ); |
156 | } | 183 | } |
157 | 184 | ||
185 | #[test] | ||
186 | fn test_intrdoduce_var() { | ||
187 | check_action_range( | ||
188 | " | ||
189 | fn foo() { | ||
190 | foo(<|>1 + 1<|>); | ||
191 | }", " | ||
192 | fn foo() { | ||
193 | let <|>var_name = 1 + 1; | ||
194 | foo(var_name); | ||
195 | }", | ||
196 | |file, range| introduce_variable(file, range).map(|f| f()), | ||
197 | ); | ||
198 | } | ||
199 | |||
158 | } | 200 | } |
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::{ | |||
32 | code_actions::{ | 32 | code_actions::{ |
33 | LocalEdit, | 33 | LocalEdit, |
34 | flip_comma, add_derive, add_impl, | 34 | flip_comma, add_derive, add_impl, |
35 | introduce_variable, | ||
35 | }, | 36 | }, |
36 | typing::{join_lines, on_eq_typed}, | 37 | typing::{join_lines, on_eq_typed}, |
37 | completion::{scope_completion, CompletionItem}, | 38 | 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 @@ | |||
1 | use libsyntax2::{File, TextUnit}; | 1 | use libsyntax2::{File, TextUnit, TextRange}; |
2 | pub use _test_utils::*; | 2 | pub use _test_utils::*; |
3 | use LocalEdit; | 3 | use LocalEdit; |
4 | 4 | ||
@@ -18,3 +18,20 @@ pub fn check_action<F: Fn(&File, TextUnit) -> Option<LocalEdit>> ( | |||
18 | let actual = add_cursor(&actual, actual_cursor_pos); | 18 | let actual = add_cursor(&actual, actual_cursor_pos); |
19 | assert_eq_text!(after, &actual); | 19 | assert_eq_text!(after, &actual); |
20 | } | 20 | } |
21 | |||
22 | pub fn check_action_range<F: Fn(&File, TextRange) -> Option<LocalEdit>> ( | ||
23 | before: &str, | ||
24 | after: &str, | ||
25 | f: F, | ||
26 | ) { | ||
27 | let (range, before) = extract_range(before); | ||
28 | let file = File::parse(&before); | ||
29 | let result = f(&file, range).expect("code action is not applicable"); | ||
30 | let actual = result.edit.apply(&before); | ||
31 | let actual_cursor_pos = match result.cursor_position { | ||
32 | None => result.edit.apply_to_offset(range.start()).unwrap(), | ||
33 | Some(off) => off, | ||
34 | }; | ||
35 | let actual = add_cursor(&actual, actual_cursor_pos); | ||
36 | assert_eq_text!(after, &actual); | ||
37 | } | ||