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