Skip to content

Commit 0b68e03

Browse files
bors[bot]ivan770
andauthored
Merge #8256
8256: Item movers improvements r=ivan770 a=ivan770 Closes #8236 Co-authored-by: ivan770 <[email protected]>
2 parents 563464b + 4fcba8b commit 0b68e03

File tree

2 files changed

+256
-13
lines changed

2 files changed

+256
-13
lines changed

crates/ide/src/hover.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module>
470470

471471
fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
472472
return tokens.max_by_key(priority);
473+
473474
fn priority(n: &SyntaxToken) -> usize {
474475
match n.kind() {
475476
IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3,

crates/ide/src/move_item.rs

Lines changed: 255 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ use hir::Semantics;
44
use ide_db::{base_db::FileRange, RootDatabase};
55
use itertools::Itertools;
66
use 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,
89
};
910
use text_edit::{TextEdit, TextEditBuilder};
1011

12+
#[derive(Copy, Clone, Debug)]
1113
pub enum Direction {
1214
Up,
1315
Down,
@@ -31,14 +33,19 @@ pub(crate) fn move_item(
3133
let sema = Semantics::new(db);
3234
let file = sema.parse(range.file_id);
3335

34-
let item = file.syntax().covering_element(range.range);
36+
let item = if range.range.is_empty() {
37+
SyntaxElement::Token(pick_best(file.syntax().token_at_offset(range.range.start()))?)
38+
} else {
39+
file.syntax().covering_element(range.range)
40+
};
41+
3542
find_ancestors(item, direction, range.range)
3643
}
3744

3845
fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -> Option<TextEdit> {
3946
let root = match item {
40-
NodeOrToken::Node(node) => node,
41-
NodeOrToken::Token(token) => token.parent()?,
47+
SyntaxElement::Node(node) => node,
48+
SyntaxElement::Token(token) => token.parent()?,
4249
};
4350

4451
let movable = [
@@ -51,6 +58,11 @@ fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -
5158
SyntaxKind::PARAM,
5259
SyntaxKind::LET_STMT,
5360
SyntaxKind::EXPR_STMT,
61+
SyntaxKind::IF_EXPR,
62+
SyntaxKind::FOR_EXPR,
63+
SyntaxKind::LOOP_EXPR,
64+
SyntaxKind::WHILE_EXPR,
65+
SyntaxKind::RETURN_EXPR,
5466
SyntaxKind::MATCH_EXPR,
5567
SyntaxKind::MACRO_CALL,
5668
SyntaxKind::TYPE_ALIAS,
@@ -83,11 +95,11 @@ fn move_in_direction(
8395
) -> Option<TextEdit> {
8496
match_ast! {
8597
match node {
86-
ast::ArgList(it) => swap_sibling_in_list(it.args(), range, direction),
87-
ast::GenericParamList(it) => swap_sibling_in_list(it.generic_params(), range, direction),
88-
ast::GenericArgList(it) => swap_sibling_in_list(it.generic_args(), range, direction),
89-
ast::VariantList(it) => swap_sibling_in_list(it.variants(), range, direction),
90-
ast::TypeBoundList(it) => swap_sibling_in_list(it.bounds(), range, direction),
98+
ast::ArgList(it) => swap_sibling_in_list(node, it.args(), range, direction),
99+
ast::GenericParamList(it) => swap_sibling_in_list(node, it.generic_params(), range, direction),
100+
ast::GenericArgList(it) => swap_sibling_in_list(node, it.generic_args(), range, direction),
101+
ast::VariantList(it) => swap_sibling_in_list(node, it.variants(), range, direction),
102+
ast::TypeBoundList(it) => swap_sibling_in_list(node, it.bounds(), range, direction),
91103
_ => Some(replace_nodes(node, &match direction {
92104
Direction::Up => node.prev_sibling(),
93105
Direction::Down => node.next_sibling(),
@@ -97,19 +109,27 @@ fn move_in_direction(
97109
}
98110

99111
fn swap_sibling_in_list<A: AstNode + Clone, I: Iterator<Item = A>>(
112+
node: &SyntaxNode,
100113
list: I,
101114
range: TextRange,
102115
direction: Direction,
103116
) -> Option<TextEdit> {
104-
let (l, r) = list
117+
let list_lookup = list
105118
.tuple_windows()
106119
.filter(|(l, r)| match direction {
107120
Direction::Up => r.syntax().text_range().contains_range(range),
108121
Direction::Down => l.syntax().text_range().contains_range(range),
109122
})
110-
.next()?;
111-
112-
Some(replace_nodes(l.syntax(), r.syntax()))
123+
.next();
124+
125+
if let Some((l, r)) = list_lookup {
126+
Some(replace_nodes(l.syntax(), r.syntax()))
127+
} else {
128+
// Cursor is beyond any movable list item (for example, on curly brace in enum).
129+
// It's not necessary, that parent of list is movable (arg list's parent is not, for example),
130+
// and we have to continue tree traversal to find suitable node.
131+
find_ancestors(SyntaxElement::Node(node.parent()?), direction, range)
132+
}
113133
}
114134

115135
fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit {
@@ -121,6 +141,18 @@ fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit {
121141
edit.finish()
122142
}
123143

144+
fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
145+
return tokens.max_by_key(priority);
146+
147+
fn priority(n: &SyntaxToken) -> usize {
148+
match n.kind() {
149+
SyntaxKind::IDENT | SyntaxKind::LIFETIME_IDENT => 2,
150+
kind if kind.is_trivia() => 0,
151+
_ => 1,
152+
}
153+
}
154+
}
155+
124156
#[cfg(test)]
125157
mod tests {
126158
use crate::fixture;
@@ -260,6 +292,107 @@ fn main() {
260292
expect![[r#"
261293
fn main() {
262294
println!("All I want to say is...");
295+
println!("Hello, world");
296+
}
297+
"#]],
298+
Direction::Up,
299+
);
300+
check(
301+
r#"
302+
fn main() {
303+
println!("Hello, world");
304+
305+
if true {
306+
println!("Test");
307+
}$0$0
308+
}
309+
"#,
310+
expect![[r#"
311+
fn main() {
312+
if true {
313+
println!("Test");
314+
}
315+
316+
println!("Hello, world");
317+
}
318+
"#]],
319+
Direction::Up,
320+
);
321+
check(
322+
r#"
323+
fn main() {
324+
println!("Hello, world");
325+
326+
for i in 0..10 {
327+
println!("Test");
328+
}$0$0
329+
}
330+
"#,
331+
expect![[r#"
332+
fn main() {
333+
for i in 0..10 {
334+
println!("Test");
335+
}
336+
337+
println!("Hello, world");
338+
}
339+
"#]],
340+
Direction::Up,
341+
);
342+
check(
343+
r#"
344+
fn main() {
345+
println!("Hello, world");
346+
347+
loop {
348+
println!("Test");
349+
}$0$0
350+
}
351+
"#,
352+
expect![[r#"
353+
fn main() {
354+
loop {
355+
println!("Test");
356+
}
357+
358+
println!("Hello, world");
359+
}
360+
"#]],
361+
Direction::Up,
362+
);
363+
check(
364+
r#"
365+
fn main() {
366+
println!("Hello, world");
367+
368+
while true {
369+
println!("Test");
370+
}$0$0
371+
}
372+
"#,
373+
expect![[r#"
374+
fn main() {
375+
while true {
376+
println!("Test");
377+
}
378+
379+
println!("Hello, world");
380+
}
381+
"#]],
382+
Direction::Up,
383+
);
384+
check(
385+
r#"
386+
fn main() {
387+
println!("Hello, world");
388+
389+
return 123;$0$0
390+
}
391+
"#,
392+
expect![[r#"
393+
fn main() {
394+
return 123;
395+
263396
println!("Hello, world");
264397
}
265398
"#]],
@@ -614,6 +747,115 @@ fn test() {
614747
);
615748
}
616749

750+
#[test]
751+
fn test_cursor_at_item_start() {
752+
check(
753+
r#"
754+
$0$0#[derive(Debug)]
755+
enum FooBar {
756+
Foo,
757+
Bar,
758+
}
759+
760+
fn main() {}
761+
"#,
762+
expect![[r#"
763+
fn main() {}
764+
765+
#[derive(Debug)]
766+
enum FooBar {
767+
Foo,
768+
Bar,
769+
}
770+
"#]],
771+
Direction::Down,
772+
);
773+
check(
774+
r#"
775+
$0$0enum FooBar {
776+
Foo,
777+
Bar,
778+
}
779+
780+
fn main() {}
781+
"#,
782+
expect![[r#"
783+
fn main() {}
784+
785+
enum FooBar {
786+
Foo,
787+
Bar,
788+
}
789+
"#]],
790+
Direction::Down,
791+
);
792+
check(
793+
r#"
794+
struct Test;
795+
796+
trait SomeTrait {}
797+
798+
$0$0impl SomeTrait for Test {}
799+
800+
fn main() {}
801+
"#,
802+
expect![[r#"
803+
struct Test;
804+
805+
impl SomeTrait for Test {}
806+
807+
trait SomeTrait {}
808+
809+
fn main() {}
810+
"#]],
811+
Direction::Up,
812+
);
813+
}
814+
815+
#[test]
816+
fn test_cursor_at_item_end() {
817+
check(
818+
r#"
819+
enum FooBar {
820+
Foo,
821+
Bar,
822+
}$0$0
823+
824+
fn main() {}
825+
"#,
826+
expect![[r#"
827+
fn main() {}
828+
829+
enum FooBar {
830+
Foo,
831+
Bar,
832+
}
833+
"#]],
834+
Direction::Down,
835+
);
836+
check(
837+
r#"
838+
struct Test;
839+
840+
trait SomeTrait {}
841+
842+
impl SomeTrait for Test {}$0$0
843+
844+
fn main() {}
845+
"#,
846+
expect![[r#"
847+
struct Test;
848+
849+
impl SomeTrait for Test {}
850+
851+
trait SomeTrait {}
852+
853+
fn main() {}
854+
"#]],
855+
Direction::Up,
856+
);
857+
}
858+
617859
#[test]
618860
fn handles_empty_file() {
619861
check(r#"$0$0"#, expect![[r#""#]], Direction::Up);

0 commit comments

Comments
 (0)