1use std::{iter::once, mem};
2
3use hir::Semantics;
4use ide_db::syntax_helpers::tree_diff::diff;
5use ide_db::text_edit::{TextEdit, TextEditBuilder};
6use ide_db::{FileRange, RootDatabase, helpers::pick_best_token};
7use itertools::Itertools;
8use syntax::{AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, ast, match_ast};
9
10#[derive(Copy, Clone, Debug)]
11pub enum Direction {
12 Up,
13 Down,
14}
15
16pub(crate) fn move_item(
27 db: &RootDatabase,
28 range: FileRange,
29 direction: Direction,
30) -> Option<TextEdit> {
31 let sema = Semantics::new(db);
32 let file = sema.parse_guess_edition(range.file_id);
33
34 let item = if range.range.is_empty() {
35 SyntaxElement::Token(pick_best_token(
36 file.syntax().token_at_offset(range.range.start()),
37 |kind| match kind {
38 SyntaxKind::IDENT | SyntaxKind::LIFETIME_IDENT => 2,
39 kind if kind.is_trivia() => 0,
40 _ => 1,
41 },
42 )?)
43 } else {
44 file.syntax().covering_element(range.range)
45 };
46
47 find_ancestors(item, direction, range.range)
48}
49
50fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -> Option<TextEdit> {
51 let root = match item {
52 SyntaxElement::Node(node) => node,
53 SyntaxElement::Token(token) => token.parent()?,
54 };
55
56 let movable = [
57 SyntaxKind::ARG_LIST,
58 SyntaxKind::GENERIC_PARAM_LIST,
59 SyntaxKind::GENERIC_ARG_LIST,
60 SyntaxKind::VARIANT_LIST,
61 SyntaxKind::TYPE_BOUND_LIST,
62 SyntaxKind::MATCH_ARM,
63 SyntaxKind::PARAM,
64 SyntaxKind::LET_STMT,
65 SyntaxKind::EXPR_STMT,
66 SyntaxKind::IF_EXPR,
67 SyntaxKind::FOR_EXPR,
68 SyntaxKind::LOOP_EXPR,
69 SyntaxKind::WHILE_EXPR,
70 SyntaxKind::RETURN_EXPR,
71 SyntaxKind::MATCH_EXPR,
72 SyntaxKind::MACRO_CALL,
73 SyntaxKind::TYPE_ALIAS,
74 SyntaxKind::TRAIT,
75 SyntaxKind::IMPL,
76 SyntaxKind::MACRO_DEF,
77 SyntaxKind::STRUCT,
78 SyntaxKind::UNION,
79 SyntaxKind::ENUM,
80 SyntaxKind::FN,
81 SyntaxKind::MODULE,
82 SyntaxKind::USE,
83 SyntaxKind::STATIC,
84 SyntaxKind::CONST,
85 SyntaxKind::MACRO_RULES,
86 SyntaxKind::MACRO_DEF,
87 ];
88
89 let ancestor = once(root.clone())
90 .chain(root.ancestors())
91 .find(|ancestor| movable.contains(&ancestor.kind()))?;
92
93 move_in_direction(&ancestor, direction, range)
94}
95
96fn move_in_direction(
97 node: &SyntaxNode,
98 direction: Direction,
99 range: TextRange,
100) -> Option<TextEdit> {
101 match_ast! {
102 match node {
103 ast::ArgList(it) => swap_sibling_in_list(node, it.args(), range, direction),
104 ast::GenericParamList(it) => swap_sibling_in_list(node, it.generic_params(), range, direction),
105 ast::GenericArgList(it) => swap_sibling_in_list(node, it.generic_args(), range, direction),
106 ast::VariantList(it) => swap_sibling_in_list(node, it.variants(), range, direction),
107 ast::TypeBoundList(it) => swap_sibling_in_list(node, it.bounds(), range, direction),
108 _ => Some(replace_nodes(range, node, &match direction {
109 Direction::Up => node.prev_sibling(),
110 Direction::Down => node.next_sibling(),
111 }?))
112 }
113 }
114}
115
116fn swap_sibling_in_list<A: AstNode + Clone, I: Iterator<Item = A>>(
117 node: &SyntaxNode,
118 list: I,
119 range: TextRange,
120 direction: Direction,
121) -> Option<TextEdit> {
122 let list_lookup = list.tuple_windows().find(|(l, r)| match direction {
123 Direction::Up => r.syntax().text_range().contains_range(range),
124 Direction::Down => l.syntax().text_range().contains_range(range),
125 });
126
127 if let Some((l, r)) = list_lookup {
128 Some(replace_nodes(range, l.syntax(), r.syntax()))
129 } else {
130 find_ancestors(SyntaxElement::Node(node.parent()?), direction, range)
134 }
135}
136
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 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
165 let mut edit = TextEditBuilder::default();
166
167 diff(first, second).into_text_edit(&mut edit);
168 edit.replace(second.text_range(), first_with_cursor);
169
170 edit.finish()
171}
172
173#[cfg(test)]
174mod tests {
175 use crate::fixture;
176 use expect_test::{Expect, expect};
177
178 use crate::Direction;
179
180 fn check(
181 #[rust_analyzer::rust_fixture] ra_fixture: &str,
182 expect: Expect,
183 direction: Direction,
184 ) {
185 let (analysis, range) = fixture::range(ra_fixture);
186 let edit = analysis.move_item(range, direction).unwrap().unwrap_or_default();
187 let mut file = analysis.file_text(range.file_id).unwrap().to_string();
188 edit.apply(&mut file);
189 expect.assert_eq(&file);
190 }
191
192 #[test]
193 fn test_moves_match_arm_up() {
194 check(
195 r#"
196fn main() {
197 match true {
198 true => {
199 println!("Hello, world");
200 },
201 false =>$0$0 {
202 println!("Test");
203 }
204 };
205}
206"#,
207 expect![[r#"
208 fn main() {
209 match true {
210 false =>$0 {
211 println!("Test");
212 }
213 true => {
214 println!("Hello, world");
215 },
216 };
217 }
218 "#]],
219 Direction::Up,
220 );
221 }
222
223 #[test]
224 fn test_moves_match_arm_down() {
225 check(
226 r#"
227fn main() {
228 match true {
229 true =>$0$0 {
230 println!("Hello, world");
231 },
232 false => {
233 println!("Test");
234 }
235 };
236}
237"#,
238 expect![[r#"
239 fn main() {
240 match true {
241 false => {
242 println!("Test");
243 }
244 true =>$0 {
245 println!("Hello, world");
246 },
247 };
248 }
249 "#]],
250 Direction::Down,
251 );
252 }
253
254 #[test]
255 fn test_nowhere_to_move() {
256 check(
257 r#"
258fn main() {
259 match true {
260 true =>$0$0 {
261 println!("Hello, world");
262 },
263 false => {
264 println!("Test");
265 }
266 };
267}
268"#,
269 expect![[r#"
270 fn main() {
271 match true {
272 true => {
273 println!("Hello, world");
274 },
275 false => {
276 println!("Test");
277 }
278 };
279 }
280 "#]],
281 Direction::Up,
282 );
283 }
284
285 #[test]
286 fn test_moves_let_stmt_up() {
287 check(
288 r#"
289fn main() {
290 let test = 123;
291 let test2$0$0 = 456;
292}
293"#,
294 expect![[r#"
295 fn main() {
296 let test2$0 = 456;
297 let test = 123;
298 }
299 "#]],
300 Direction::Up,
301 );
302 }
303
304 #[test]
305 fn test_moves_expr_up() {
306 check(
307 r#"
308fn main() {
309 println!("Hello, world");
310 println!("All I want to say is...");$0$0
311}
312"#,
313 expect![[r#"
314 fn main() {
315 println!("All I want to say is...");$0
316 println!("Hello, world");
317 }
318 "#]],
319 Direction::Up,
320 );
321 check(
322 r#"
323fn main() {
324 println!("Hello, world");
325
326 if true {
327 println!("Test");
328 }$0$0
329}
330"#,
331 expect![[r#"
332 fn main() {
333 if true {
334 println!("Test");
335 }$0
336
337 println!("Hello, world");
338 }
339 "#]],
340 Direction::Up,
341 );
342 check(
343 r#"
344fn main() {
345 println!("Hello, world");
346
347 for i in 0..10 {
348 println!("Test");
349 }$0$0
350}
351"#,
352 expect![[r#"
353 fn main() {
354 for i in 0..10 {
355 println!("Test");
356 }$0
357
358 println!("Hello, world");
359 }
360 "#]],
361 Direction::Up,
362 );
363 check(
364 r#"
365fn main() {
366 println!("Hello, world");
367
368 loop {
369 println!("Test");
370 }$0$0
371}
372"#,
373 expect![[r#"
374 fn main() {
375 loop {
376 println!("Test");
377 }$0
378
379 println!("Hello, world");
380 }
381 "#]],
382 Direction::Up,
383 );
384 check(
385 r#"
386fn main() {
387 println!("Hello, world");
388
389 while true {
390 println!("Test");
391 }$0$0
392}
393"#,
394 expect![[r#"
395 fn main() {
396 while true {
397 println!("Test");
398 }$0
399
400 println!("Hello, world");
401 }
402 "#]],
403 Direction::Up,
404 );
405 check(
406 r#"
407fn main() {
408 println!("Hello, world");
409
410 return 123;$0$0
411}
412"#,
413 expect![[r#"
414 fn main() {
415 return 123;$0
416
417 println!("Hello, world");
418 }
419 "#]],
420 Direction::Up,
421 );
422 }
423
424 #[test]
425 fn test_nowhere_to_move_stmt() {
426 check(
427 r#"
428fn main() {
429 println!("All I want to say is...");$0$0
430 println!("Hello, world");
431}
432"#,
433 expect![[r#"
434 fn main() {
435 println!("All I want to say is...");
436 println!("Hello, world");
437 }
438 "#]],
439 Direction::Up,
440 );
441 }
442
443 #[test]
444 fn test_move_item() {
445 check(
446 r#"
447fn main() {}
448
449fn foo() {}$0$0
450"#,
451 expect![[r#"
452 fn foo() {}$0
453
454 fn main() {}
455 "#]],
456 Direction::Up,
457 );
458 }
459
460 #[test]
461 fn test_move_impl_up() {
462 check(
463 r#"
464struct Yay;
465
466trait Wow {}
467
468impl Wow for Yay $0$0{}
469"#,
470 expect![[r#"
471 struct Yay;
472
473 impl Wow for Yay $0{}
474
475 trait Wow {}
476 "#]],
477 Direction::Up,
478 );
479 }
480
481 #[test]
482 fn test_move_use_up() {
483 check(
484 r#"
485use std::vec::Vec;
486use std::collections::HashMap$0$0;
487"#,
488 expect![[r#"
489 use std::collections::HashMap$0;
490 use std::vec::Vec;
491 "#]],
492 Direction::Up,
493 );
494 }
495
496 #[test]
497 fn test_moves_match_expr_up() {
498 check(
499 r#"
500fn main() {
501 let test = 123;
502
503 $0match test {
504 456 => {},
505 _ => {}
506 };$0
507}
508"#,
509 expect![[r#"
510 fn main() {
511 match test {
512 456 => {},
513 _ => {}
514 };
515
516 let test = 123;
517 }
518 "#]],
519 Direction::Up,
520 );
521 }
522
523 #[test]
524 fn test_moves_param() {
525 check(
526 r#"
527fn test(one: i32, two$0$0: u32) {}
528
529fn main() {
530 test(123, 456);
531}
532"#,
533 expect![[r#"
534 fn test(two$0: u32, one: i32) {}
535
536 fn main() {
537 test(123, 456);
538 }
539 "#]],
540 Direction::Up,
541 );
542 check(
543 r#"
544fn f($0$0arg: u8, arg2: u16) {}
545"#,
546 expect![[r#"
547 fn f(arg2: u16, $0arg: u8) {}
548 "#]],
549 Direction::Down,
550 );
551 }
552
553 #[test]
554 fn test_moves_arg_up() {
555 check(
556 r#"
557fn test(one: i32, two: u32) {}
558
559fn main() {
560 test(123, 456$0$0);
561}
562"#,
563 expect![[r#"
564 fn test(one: i32, two: u32) {}
565
566 fn main() {
567 test(456$0, 123);
568 }
569 "#]],
570 Direction::Up,
571 );
572 }
573
574 #[test]
575 fn test_moves_arg_down() {
576 check(
577 r#"
578fn test(one: i32, two: u32) {}
579
580fn main() {
581 test(123$0$0, 456);
582}
583"#,
584 expect![[r#"
585 fn test(one: i32, two: u32) {}
586
587 fn main() {
588 test(456, 123$0);
589 }
590 "#]],
591 Direction::Down,
592 );
593 }
594
595 #[test]
596 fn test_nowhere_to_move_arg() {
597 check(
598 r#"
599fn test(one: i32, two: u32) {}
600
601fn main() {
602 test(123$0$0, 456);
603}
604"#,
605 expect![[r#"
606 fn test(one: i32, two: u32) {}
607
608 fn main() {
609 test(123, 456);
610 }
611 "#]],
612 Direction::Up,
613 );
614 }
615
616 #[test]
617 fn test_moves_generic_param_up() {
618 check(
619 r#"
620struct Test<A, B$0$0>(A, B);
621
622fn main() {}
623"#,
624 expect![[r#"
625 struct Test<B$0, A>(A, B);
626
627 fn main() {}
628 "#]],
629 Direction::Up,
630 );
631 }
632
633 #[test]
634 fn test_moves_generic_arg_up() {
635 check(
636 r#"
637struct Test<A, B>(A, B);
638
639fn main() {
640 let t = Test::<i32, &str$0$0>(123, "yay");
641}
642"#,
643 expect![[r#"
644 struct Test<A, B>(A, B);
645
646 fn main() {
647 let t = Test::<&str$0, i32>(123, "yay");
648 }
649 "#]],
650 Direction::Up,
651 );
652 }
653
654 #[test]
655 fn test_moves_variant_up() {
656 check(
657 r#"
658enum Hello {
659 One,
660 Two$0$0
661}
662
663fn main() {}
664"#,
665 expect![[r#"
666 enum Hello {
667 Two$0,
668 One
669 }
670
671 fn main() {}
672 "#]],
673 Direction::Up,
674 );
675 }
676
677 #[test]
678 fn test_moves_type_bound_up() {
679 check(
680 r#"
681trait One {}
682
683trait Two {}
684
685fn test<T: One + Two$0$0>(t: T) {}
686
687fn main() {}
688"#,
689 expect![[r#"
690 trait One {}
691
692 trait Two {}
693
694 fn test<T: Two$0 + One>(t: T) {}
695
696 fn main() {}
697 "#]],
698 Direction::Up,
699 );
700 }
701
702 #[test]
703 fn test_prioritizes_trait_items() {
704 check(
705 r#"
706struct Test;
707
708trait Yay {
709 type One;
710
711 type Two;
712
713 fn inner();
714}
715
716impl Yay for Test {
717 type One = i32;
718
719 type Two = u32;
720
721 fn inner() {$0$0
722 println!("Mmmm");
723 }
724}
725"#,
726 expect![[r#"
727 struct Test;
728
729 trait Yay {
730 type One;
731
732 type Two;
733
734 fn inner();
735 }
736
737 impl Yay for Test {
738 type One = i32;
739
740 fn inner() {$0
741 println!("Mmmm");
742 }
743
744 type Two = u32;
745 }
746 "#]],
747 Direction::Up,
748 );
749 }
750
751 #[test]
752 fn test_weird_nesting() {
753 check(
754 r#"
755fn test() {
756 mod hello {
757 fn inner() {}
758 }
759
760 mod hi {$0$0
761 fn inner() {}
762 }
763}
764"#,
765 expect![[r#"
766 fn test() {
767 mod hi {$0
768 fn inner() {}
769 }
770
771 mod hello {
772 fn inner() {}
773 }
774 }
775 "#]],
776 Direction::Up,
777 );
778 }
779
780 #[test]
781 fn test_cursor_at_item_start() {
782 check(
783 r#"
784$0$0#[derive(Debug)]
785enum FooBar {
786 Foo,
787 Bar,
788}
789
790fn main() {}
791"#,
792 expect![[r##"
793 fn main() {}
794
795 $0#[derive(Debug)]
796 enum FooBar {
797 Foo,
798 Bar,
799 }
800 "##]],
801 Direction::Down,
802 );
803 check(
804 r#"
805$0$0enum FooBar {
806 Foo,
807 Bar,
808}
809
810fn main() {}
811"#,
812 expect![[r#"
813 fn main() {}
814
815 $0enum FooBar {
816 Foo,
817 Bar,
818 }
819 "#]],
820 Direction::Down,
821 );
822 check(
823 r#"
824struct Test;
825
826trait SomeTrait {}
827
828$0$0impl SomeTrait for Test {}
829
830fn main() {}
831"#,
832 expect![[r#"
833 struct Test;
834
835 $0impl SomeTrait for Test {}
836
837 trait SomeTrait {}
838
839 fn main() {}
840 "#]],
841 Direction::Up,
842 );
843 }
844
845 #[test]
846 fn test_cursor_at_item_end() {
847 check(
848 r#"
849enum FooBar {
850 Foo,
851 Bar,
852}$0$0
853
854fn main() {}
855"#,
856 expect![[r#"
857 fn main() {}
858
859 enum FooBar {
860 Foo,
861 Bar,
862 }$0
863 "#]],
864 Direction::Down,
865 );
866 check(
867 r#"
868struct Test;
869
870trait SomeTrait {}
871
872impl SomeTrait for Test {}$0$0
873
874fn main() {}
875"#,
876 expect![[r#"
877 struct Test;
878
879 impl SomeTrait for Test {}$0
880
881 trait SomeTrait {}
882
883 fn main() {}
884 "#]],
885 Direction::Up,
886 );
887 }
888
889 #[test]
890 fn handles_empty_file() {
891 check(r#"$0$0"#, expect![[r#""#]], Direction::Up);
892 }
893}