ide/
move_item.rs

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
16// Feature: Move Item
17//
18// Move item under cursor or selection up and down.
19//
20// | Editor  | Action Name |
21// |---------|-------------|
22// | VS Code | **rust-analyzer: Move item up**
23// | VS Code | **rust-analyzer: Move item down**
24//
25// ![Move Item](https://user-images.githubusercontent.com/48062697/113065576-04298180-91b1-11eb-91ce-4505e99ed598.gif)
26pub(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        // 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    }
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        // 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
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}