aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src')
-rw-r--r--crates/ide/src/move_item.rs114
1 files changed, 102 insertions, 12 deletions
diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs
index 906d0f268..de4e57b6a 100644
--- a/crates/ide/src/move_item.rs
+++ b/crates/ide/src/move_item.rs
@@ -2,7 +2,10 @@ use std::iter::once;
2 2
3use hir::Semantics; 3use hir::Semantics;
4use ide_db::{base_db::FileRange, RootDatabase}; 4use ide_db::{base_db::FileRange, RootDatabase};
5use syntax::{algo, AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode}; 5use itertools::Itertools;
6use syntax::{
7 algo, ast, match_ast, AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, TextRange,
8};
6use text_edit::{TextEdit, TextEditBuilder}; 9use text_edit::{TextEdit, TextEditBuilder};
7 10
8pub enum Direction { 11pub enum Direction {
@@ -29,16 +32,17 @@ pub(crate) fn move_item(
29 let file = sema.parse(range.file_id); 32 let file = sema.parse(range.file_id);
30 33
31 let item = file.syntax().covering_element(range.range); 34 let item = file.syntax().covering_element(range.range);
32 find_ancestors(item, direction) 35 find_ancestors(item, direction, range.range)
33} 36}
34 37
35fn find_ancestors(item: SyntaxElement, direction: Direction) -> Option<TextEdit> { 38fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -> Option<TextEdit> {
36 let root = match item { 39 let root = match item {
37 NodeOrToken::Node(node) => node, 40 NodeOrToken::Node(node) => node,
38 NodeOrToken::Token(token) => token.parent()?, 41 NodeOrToken::Token(token) => token.parent()?,
39 }; 42 };
40 43
41 let movable = [ 44 let movable = [
45 SyntaxKind::ARG_LIST,
42 SyntaxKind::MATCH_ARM, 46 SyntaxKind::MATCH_ARM,
43 SyntaxKind::PARAM, 47 SyntaxKind::PARAM,
44 SyntaxKind::LET_STMT, 48 SyntaxKind::LET_STMT,
@@ -64,16 +68,39 @@ fn find_ancestors(item: SyntaxElement, direction: Direction) -> Option<TextEdit>
64 .chain(root.ancestors()) 68 .chain(root.ancestors())
65 .find(|ancestor| movable.contains(&ancestor.kind()))?; 69 .find(|ancestor| movable.contains(&ancestor.kind()))?;
66 70
67 move_in_direction(&ancestor, direction) 71 move_in_direction(&ancestor, direction, range)
68} 72}
69 73
70fn move_in_direction(node: &SyntaxNode, direction: Direction) -> Option<TextEdit> { 74fn move_in_direction(
71 let sibling = match direction { 75 node: &SyntaxNode,
72 Direction::Up => node.prev_sibling(), 76 direction: Direction,
73 Direction::Down => node.next_sibling(), 77 range: TextRange,
74 }?; 78) -> Option<TextEdit> {
79 match_ast! {
80 match node {
81 ast::ArgList(it) => swap_sibling_in_list(it.args(), range, direction),
82 _ => Some(replace_nodes(node, &match direction {
83 Direction::Up => node.prev_sibling(),
84 Direction::Down => node.next_sibling(),
85 }?))
86 }
87 }
88}
75 89
76 Some(replace_nodes(node, &sibling)) 90fn swap_sibling_in_list<'i, A: AstNode + Clone, I: Iterator<Item = A>>(
91 list: I,
92 range: TextRange,
93 direction: Direction,
94) -> Option<TextEdit> {
95 let (l, r) = list
96 .tuple_windows()
97 .filter(|(l, r)| match direction {
98 Direction::Up => r.syntax().text_range().contains_range(range),
99 Direction::Down => l.syntax().text_range().contains_range(range),
100 })
101 .next()?;
102
103 Some(replace_nodes(l.syntax(), r.syntax()))
77} 104}
78 105
79fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit { 106fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit {
@@ -304,7 +331,7 @@ use std::vec::Vec;
304 } 331 }
305 332
306 #[test] 333 #[test]
307 fn moves_match_expr_up() { 334 fn test_moves_match_expr_up() {
308 check( 335 check(
309 r#" 336 r#"
310fn main() { 337fn main() {
@@ -331,7 +358,7 @@ fn main() {
331 } 358 }
332 359
333 #[test] 360 #[test]
334 fn moves_param_up() { 361 fn test_moves_param_up() {
335 check( 362 check(
336 r#" 363 r#"
337fn test(one: i32, two$0$0: u32) {} 364fn test(one: i32, two$0$0: u32) {}
@@ -352,6 +379,69 @@ fn main() {
352 } 379 }
353 380
354 #[test] 381 #[test]
382 fn test_moves_arg_up() {
383 check(
384 r#"
385fn test(one: i32, two: u32) {}
386
387fn main() {
388 test(123, 456$0$0);
389}
390 "#,
391 expect![[r#"
392fn test(one: i32, two: u32) {}
393
394fn main() {
395 test(456, 123);
396}
397 "#]],
398 Direction::Up,
399 );
400 }
401
402 #[test]
403 fn test_moves_arg_down() {
404 check(
405 r#"
406fn test(one: i32, two: u32) {}
407
408fn main() {
409 test(123$0$0, 456);
410}
411 "#,
412 expect![[r#"
413fn test(one: i32, two: u32) {}
414
415fn main() {
416 test(456, 123);
417}
418 "#]],
419 Direction::Down,
420 );
421 }
422
423 #[test]
424 fn test_nowhere_to_move_arg() {
425 check(
426 r#"
427fn test(one: i32, two: u32) {}
428
429fn main() {
430 test(123$0$0, 456);
431}
432 "#,
433 expect![[r#"
434fn test(one: i32, two: u32) {}
435
436fn main() {
437 test(123, 456);
438}
439 "#]],
440 Direction::Up,
441 );
442 }
443
444 #[test]
355 fn test_prioritizes_trait_items() { 445 fn test_prioritizes_trait_items() {
356 check( 446 check(
357 r#" 447 r#"