aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/move_item.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src/move_item.rs')
-rw-r--r--crates/ide/src/move_item.rs348
1 files changed, 314 insertions, 34 deletions
diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs
index 05fa8fc13..246f10a0a 100644
--- a/crates/ide/src/move_item.rs
+++ b/crates/ide/src/move_item.rs
@@ -1,13 +1,15 @@
1use std::iter::once; 1use std::{iter::once, mem};
2 2
3use hir::Semantics; 3use hir::Semantics;
4use ide_db::{base_db::FileRange, RootDatabase}; 4use ide_db::{base_db::FileRange, RootDatabase};
5use itertools::Itertools; 5use itertools::Itertools;
6use syntax::{ 6use syntax::{
7 algo, ast, match_ast, AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, 7 algo, ast, match_ast, AstNode, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange,
8 TokenAtOffset,
8}; 9};
9use text_edit::{TextEdit, TextEditBuilder}; 10use text_edit::{TextEdit, TextEditBuilder};
10 11
12#[derive(Copy, Clone, Debug)]
11pub enum Direction { 13pub enum Direction {
12 Up, 14 Up,
13 Down, 15 Down,
@@ -23,6 +25,8 @@ pub enum Direction {
23// | VS Code | **Rust Analyzer: Move item up** 25// | VS Code | **Rust Analyzer: Move item up**
24// | VS Code | **Rust Analyzer: Move item down** 26// | VS Code | **Rust Analyzer: Move item down**
25// |=== 27// |===
28//
29// image::https://user-images.githubusercontent.com/48062697/113065576-04298180-91b1-11eb-91ce-4505e99ed598.gif[]
26pub(crate) fn move_item( 30pub(crate) fn move_item(
27 db: &RootDatabase, 31 db: &RootDatabase,
28 range: FileRange, 32 range: FileRange,
@@ -31,14 +35,19 @@ pub(crate) fn move_item(
31 let sema = Semantics::new(db); 35 let sema = Semantics::new(db);
32 let file = sema.parse(range.file_id); 36 let file = sema.parse(range.file_id);
33 37
34 let item = file.syntax().covering_element(range.range); 38 let item = if range.range.is_empty() {
39 SyntaxElement::Token(pick_best(file.syntax().token_at_offset(range.range.start()))?)
40 } else {
41 file.syntax().covering_element(range.range)
42 };
43
35 find_ancestors(item, direction, range.range) 44 find_ancestors(item, direction, range.range)
36} 45}
37 46
38fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -> Option<TextEdit> { 47fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -> Option<TextEdit> {
39 let root = match item { 48 let root = match item {
40 NodeOrToken::Node(node) => node, 49 SyntaxElement::Node(node) => node,
41 NodeOrToken::Token(token) => token.parent()?, 50 SyntaxElement::Token(token) => token.parent()?,
42 }; 51 };
43 52
44 let movable = [ 53 let movable = [
@@ -51,6 +60,11 @@ fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -
51 SyntaxKind::PARAM, 60 SyntaxKind::PARAM,
52 SyntaxKind::LET_STMT, 61 SyntaxKind::LET_STMT,
53 SyntaxKind::EXPR_STMT, 62 SyntaxKind::EXPR_STMT,
63 SyntaxKind::IF_EXPR,
64 SyntaxKind::FOR_EXPR,
65 SyntaxKind::LOOP_EXPR,
66 SyntaxKind::WHILE_EXPR,
67 SyntaxKind::RETURN_EXPR,
54 SyntaxKind::MATCH_EXPR, 68 SyntaxKind::MATCH_EXPR,
55 SyntaxKind::MACRO_CALL, 69 SyntaxKind::MACRO_CALL,
56 SyntaxKind::TYPE_ALIAS, 70 SyntaxKind::TYPE_ALIAS,
@@ -83,12 +97,12 @@ fn move_in_direction(
83) -> Option<TextEdit> { 97) -> Option<TextEdit> {
84 match_ast! { 98 match_ast! {
85 match node { 99 match node {
86 ast::ArgList(it) => swap_sibling_in_list(it.args(), range, direction), 100 ast::ArgList(it) => swap_sibling_in_list(node, it.args(), range, direction),
87 ast::GenericParamList(it) => swap_sibling_in_list(it.generic_params(), range, direction), 101 ast::GenericParamList(it) => swap_sibling_in_list(node, it.generic_params(), range, direction),
88 ast::GenericArgList(it) => swap_sibling_in_list(it.generic_args(), range, direction), 102 ast::GenericArgList(it) => swap_sibling_in_list(node, it.generic_args(), range, direction),
89 ast::VariantList(it) => swap_sibling_in_list(it.variants(), range, direction), 103 ast::VariantList(it) => swap_sibling_in_list(node, it.variants(), range, direction),
90 ast::TypeBoundList(it) => swap_sibling_in_list(it.bounds(), range, direction), 104 ast::TypeBoundList(it) => swap_sibling_in_list(node, it.bounds(), range, direction),
91 _ => Some(replace_nodes(node, &match direction { 105 _ => Some(replace_nodes(range, node, &match direction {
92 Direction::Up => node.prev_sibling(), 106 Direction::Up => node.prev_sibling(),
93 Direction::Down => node.next_sibling(), 107 Direction::Down => node.next_sibling(),
94 }?)) 108 }?))
@@ -97,30 +111,77 @@ fn move_in_direction(
97} 111}
98 112
99fn swap_sibling_in_list<A: AstNode + Clone, I: Iterator<Item = A>>( 113fn swap_sibling_in_list<A: AstNode + Clone, I: Iterator<Item = A>>(
114 node: &SyntaxNode,
100 list: I, 115 list: I,
101 range: TextRange, 116 range: TextRange,
102 direction: Direction, 117 direction: Direction,
103) -> Option<TextEdit> { 118) -> Option<TextEdit> {
104 let (l, r) = list 119 let list_lookup = list
105 .tuple_windows() 120 .tuple_windows()
106 .filter(|(l, r)| match direction { 121 .filter(|(l, r)| match direction {
107 Direction::Up => r.syntax().text_range().contains_range(range), 122 Direction::Up => r.syntax().text_range().contains_range(range),
108 Direction::Down => l.syntax().text_range().contains_range(range), 123 Direction::Down => l.syntax().text_range().contains_range(range),
109 }) 124 })
110 .next()?; 125 .next();
111 126
112 Some(replace_nodes(l.syntax(), r.syntax())) 127 if let Some((l, r)) = list_lookup {
128 Some(replace_nodes(range, l.syntax(), r.syntax()))
129 } else {
130 // Cursor is beyond any movable list item (for example, on curly brace in enum).
131 // It's not necessary, that parent of list is movable (arg list's parent is not, for example),
132 // and we have to continue tree traversal to find suitable node.
133 find_ancestors(SyntaxElement::Node(node.parent()?), direction, range)
134 }
113} 135}
114 136
115fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit { 137fn replace_nodes<'a>(
138 range: TextRange,
139 mut first: &'a SyntaxNode,
140 mut second: &'a SyntaxNode,
141) -> TextEdit {
142 let cursor_offset = if range.is_empty() {
143 // FIXME: `applySnippetTextEdits` does not support non-empty selection ranges
144 if first.text_range().contains_range(range) {
145 Some(range.start() - first.text_range().start())
146 } else if second.text_range().contains_range(range) {
147 mem::swap(&mut first, &mut second);
148 Some(range.start() - first.text_range().start())
149 } else {
150 None
151 }
152 } else {
153 None
154 };
155
156 let first_with_cursor = match cursor_offset {
157 Some(offset) => {
158 let mut item_text = first.text().to_string();
159 item_text.insert_str(offset.into(), "$0");
160 item_text
161 }
162 None => first.text().to_string(),
163 };
164
116 let mut edit = TextEditBuilder::default(); 165 let mut edit = TextEditBuilder::default();
117 166
118 algo::diff(first, second).into_text_edit(&mut edit); 167 algo::diff(first, second).into_text_edit(&mut edit);
119 algo::diff(second, first).into_text_edit(&mut edit); 168 edit.replace(second.text_range(), first_with_cursor);
120 169
121 edit.finish() 170 edit.finish()
122} 171}
123 172
173fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
174 return tokens.max_by_key(priority);
175
176 fn priority(n: &SyntaxToken) -> usize {
177 match n.kind() {
178 SyntaxKind::IDENT | SyntaxKind::LIFETIME_IDENT => 2,
179 kind if kind.is_trivia() => 0,
180 _ => 1,
181 }
182 }
183}
184
124#[cfg(test)] 185#[cfg(test)]
125mod tests { 186mod tests {
126 use crate::fixture; 187 use crate::fixture;
@@ -154,7 +215,7 @@ fn main() {
154 expect![[r#" 215 expect![[r#"
155fn main() { 216fn main() {
156 match true { 217 match true {
157 false => { 218 false =>$0 {
158 println!("Test"); 219 println!("Test");
159 }, 220 },
160 true => { 221 true => {
@@ -188,7 +249,7 @@ fn main() {
188 false => { 249 false => {
189 println!("Test"); 250 println!("Test");
190 }, 251 },
191 true => { 252 true =>$0 {
192 println!("Hello, world"); 253 println!("Hello, world");
193 } 254 }
194 }; 255 };
@@ -240,7 +301,7 @@ fn main() {
240 "#, 301 "#,
241 expect![[r#" 302 expect![[r#"
242fn main() { 303fn main() {
243 let test2 = 456; 304 let test2$0 = 456;
244 let test = 123; 305 let test = 123;
245} 306}
246 "#]], 307 "#]],
@@ -259,7 +320,108 @@ fn main() {
259 "#, 320 "#,
260 expect![[r#" 321 expect![[r#"
261fn main() { 322fn main() {
262 println!("All I want to say is..."); 323 println!("All I want to say is...");$0
324 println!("Hello, world");
325}
326 "#]],
327 Direction::Up,
328 );
329 check(
330 r#"
331fn main() {
332 println!("Hello, world");
333
334 if true {
335 println!("Test");
336 }$0$0
337}
338 "#,
339 expect![[r#"
340fn main() {
341 if true {
342 println!("Test");
343 }$0
344
345 println!("Hello, world");
346}
347 "#]],
348 Direction::Up,
349 );
350 check(
351 r#"
352fn main() {
353 println!("Hello, world");
354
355 for i in 0..10 {
356 println!("Test");
357 }$0$0
358}
359 "#,
360 expect![[r#"
361fn main() {
362 for i in 0..10 {
363 println!("Test");
364 }$0
365
366 println!("Hello, world");
367}
368 "#]],
369 Direction::Up,
370 );
371 check(
372 r#"
373fn main() {
374 println!("Hello, world");
375
376 loop {
377 println!("Test");
378 }$0$0
379}
380 "#,
381 expect![[r#"
382fn main() {
383 loop {
384 println!("Test");
385 }$0
386
387 println!("Hello, world");
388}
389 "#]],
390 Direction::Up,
391 );
392 check(
393 r#"
394fn main() {
395 println!("Hello, world");
396
397 while true {
398 println!("Test");
399 }$0$0
400}
401 "#,
402 expect![[r#"
403fn main() {
404 while true {
405 println!("Test");
406 }$0
407
408 println!("Hello, world");
409}
410 "#]],
411 Direction::Up,
412 );
413 check(
414 r#"
415fn main() {
416 println!("Hello, world");
417
418 return 123;$0$0
419}
420 "#,
421 expect![[r#"
422fn main() {
423 return 123;$0
424
263 println!("Hello, world"); 425 println!("Hello, world");
264} 426}
265 "#]], 427 "#]],
@@ -295,7 +457,7 @@ fn main() {}
295fn foo() {}$0$0 457fn foo() {}$0$0
296 "#, 458 "#,
297 expect![[r#" 459 expect![[r#"
298fn foo() {} 460fn foo() {}$0
299 461
300fn main() {} 462fn main() {}
301 "#]], 463 "#]],
@@ -316,7 +478,7 @@ impl Wow for Yay $0$0{}
316 expect![[r#" 478 expect![[r#"
317struct Yay; 479struct Yay;
318 480
319impl Wow for Yay {} 481impl Wow for Yay $0{}
320 482
321trait Wow {} 483trait Wow {}
322 "#]], 484 "#]],
@@ -332,7 +494,7 @@ use std::vec::Vec;
332use std::collections::HashMap$0$0; 494use std::collections::HashMap$0$0;
333 "#, 495 "#,
334 expect![[r#" 496 expect![[r#"
335use std::collections::HashMap; 497use std::collections::HashMap$0;
336use std::vec::Vec; 498use std::vec::Vec;
337 "#]], 499 "#]],
338 Direction::Up, 500 Direction::Up,
@@ -367,7 +529,7 @@ fn main() {
367 } 529 }
368 530
369 #[test] 531 #[test]
370 fn test_moves_param_up() { 532 fn test_moves_param() {
371 check( 533 check(
372 r#" 534 r#"
373fn test(one: i32, two$0$0: u32) {} 535fn test(one: i32, two$0$0: u32) {}
@@ -377,7 +539,7 @@ fn main() {
377} 539}
378 "#, 540 "#,
379 expect![[r#" 541 expect![[r#"
380fn test(two: u32, one: i32) {} 542fn test(two$0: u32, one: i32) {}
381 543
382fn main() { 544fn main() {
383 test(123, 456); 545 test(123, 456);
@@ -385,6 +547,15 @@ fn main() {
385 "#]], 547 "#]],
386 Direction::Up, 548 Direction::Up,
387 ); 549 );
550 check(
551 r#"
552fn f($0$0arg: u8, arg2: u16) {}
553 "#,
554 expect![[r#"
555fn f(arg2: u16, $0arg: u8) {}
556 "#]],
557 Direction::Down,
558 );
388 } 559 }
389 560
390 #[test] 561 #[test]
@@ -401,7 +572,7 @@ fn main() {
401fn test(one: i32, two: u32) {} 572fn test(one: i32, two: u32) {}
402 573
403fn main() { 574fn main() {
404 test(456, 123); 575 test(456$0, 123);
405} 576}
406 "#]], 577 "#]],
407 Direction::Up, 578 Direction::Up,
@@ -422,7 +593,7 @@ fn main() {
422fn test(one: i32, two: u32) {} 593fn test(one: i32, two: u32) {}
423 594
424fn main() { 595fn main() {
425 test(456, 123); 596 test(456, 123$0);
426} 597}
427 "#]], 598 "#]],
428 Direction::Down, 599 Direction::Down,
@@ -459,7 +630,7 @@ struct Test<A, B$0$0>(A, B);
459fn main() {} 630fn main() {}
460 "#, 631 "#,
461 expect![[r#" 632 expect![[r#"
462struct Test<B, A>(A, B); 633struct Test<B$0, A>(A, B);
463 634
464fn main() {} 635fn main() {}
465 "#]], 636 "#]],
@@ -481,7 +652,7 @@ fn main() {
481struct Test<A, B>(A, B); 652struct Test<A, B>(A, B);
482 653
483fn main() { 654fn main() {
484 let t = Test::<&str, i32>(123, "yay"); 655 let t = Test::<&str$0, i32>(123, "yay");
485} 656}
486 "#]], 657 "#]],
487 Direction::Up, 658 Direction::Up,
@@ -501,7 +672,7 @@ fn main() {}
501 "#, 672 "#,
502 expect![[r#" 673 expect![[r#"
503enum Hello { 674enum Hello {
504 Two, 675 Two$0,
505 One 676 One
506} 677}
507 678
@@ -528,7 +699,7 @@ trait One {}
528 699
529trait Two {} 700trait Two {}
530 701
531fn test<T: Two + One>(t: T) {} 702fn test<T: Two$0 + One>(t: T) {}
532 703
533fn main() {} 704fn main() {}
534 "#]], 705 "#]],
@@ -574,7 +745,7 @@ trait Yay {
574impl Yay for Test { 745impl Yay for Test {
575 type One = i32; 746 type One = i32;
576 747
577 fn inner() { 748 fn inner() {$0
578 println!("Mmmm"); 749 println!("Mmmm");
579 } 750 }
580 751
@@ -601,7 +772,7 @@ fn test() {
601 "#, 772 "#,
602 expect![[r#" 773 expect![[r#"
603fn test() { 774fn test() {
604 mod hi { 775 mod hi {$0
605 fn inner() {} 776 fn inner() {}
606 } 777 }
607 778
@@ -615,6 +786,115 @@ fn test() {
615 } 786 }
616 787
617 #[test] 788 #[test]
789 fn test_cursor_at_item_start() {
790 check(
791 r#"
792$0$0#[derive(Debug)]
793enum FooBar {
794 Foo,
795 Bar,
796}
797
798fn main() {}
799 "#,
800 expect![[r#"
801fn main() {}
802
803$0#[derive(Debug)]
804enum FooBar {
805 Foo,
806 Bar,
807}
808 "#]],
809 Direction::Down,
810 );
811 check(
812 r#"
813$0$0enum FooBar {
814 Foo,
815 Bar,
816}
817
818fn main() {}
819 "#,
820 expect![[r#"
821fn main() {}
822
823$0enum FooBar {
824 Foo,
825 Bar,
826}
827 "#]],
828 Direction::Down,
829 );
830 check(
831 r#"
832struct Test;
833
834trait SomeTrait {}
835
836$0$0impl SomeTrait for Test {}
837
838fn main() {}
839 "#,
840 expect![[r#"
841struct Test;
842
843$0impl SomeTrait for Test {}
844
845trait SomeTrait {}
846
847fn main() {}
848 "#]],
849 Direction::Up,
850 );
851 }
852
853 #[test]
854 fn test_cursor_at_item_end() {
855 check(
856 r#"
857enum FooBar {
858 Foo,
859 Bar,
860}$0$0
861
862fn main() {}
863 "#,
864 expect![[r#"
865fn main() {}
866
867enum FooBar {
868 Foo,
869 Bar,
870}$0
871 "#]],
872 Direction::Down,
873 );
874 check(
875 r#"
876struct Test;
877
878trait SomeTrait {}
879
880impl SomeTrait for Test {}$0$0
881
882fn main() {}
883 "#,
884 expect![[r#"
885struct Test;
886
887impl SomeTrait for Test {}$0
888
889trait SomeTrait {}
890
891fn main() {}
892 "#]],
893 Direction::Up,
894 );
895 }
896
897 #[test]
618 fn handles_empty_file() { 898 fn handles_empty_file() {
619 check(r#"$0$0"#, expect![[r#""#]], Direction::Up); 899 check(r#"$0$0"#, expect![[r#""#]], Direction::Up);
620 } 900 }