From 7d604584954660d255ad0929d3be8ce03f879d0c Mon Sep 17 00:00:00 2001 From: ivan770 Date: Tue, 16 Mar 2021 14:37:00 +0200 Subject: Item up and down movers --- crates/ide/src/lib.rs | 10 + crates/ide/src/move_item.rs | 392 ++++++++++++++++++++++++++++++++++ crates/rust-analyzer/src/handlers.rs | 19 ++ crates/rust-analyzer/src/lsp_ext.rs | 22 ++ crates/rust-analyzer/src/main_loop.rs | 1 + crates/rust-analyzer/src/to_proto.rs | 12 ++ 6 files changed, 456 insertions(+) create mode 100644 crates/ide/src/move_item.rs (limited to 'crates') diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 662da5a96..3f73c0632 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -37,6 +37,7 @@ mod hover; mod inlay_hints; mod join_lines; mod matching_brace; +mod move_item; mod parent_module; mod references; mod fn_references; @@ -76,6 +77,7 @@ pub use crate::{ hover::{HoverAction, HoverConfig, HoverGotoTypeData, HoverResult}, inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, markup::Markup, + move_item::Direction, prime_caches::PrimeCachesProgress, references::{rename::RenameError, ReferenceSearchResult}, runnables::{Runnable, RunnableKind, TestId}, @@ -583,6 +585,14 @@ impl Analysis { self.with_db(|db| annotations::resolve_annotation(db, annotation)) } + pub fn move_item( + &self, + range: FileRange, + direction: Direction, + ) -> Cancelable> { + self.with_db(|db| move_item::move_item(db, range, direction)) + } + /// Performs an operation on that may be Canceled. fn with_db(&self, f: F) -> Cancelable where diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs new file mode 100644 index 000000000..be62d008d --- /dev/null +++ b/crates/ide/src/move_item.rs @@ -0,0 +1,392 @@ +use std::iter::once; + +use hir::Semantics; +use ide_db::{base_db::FileRange, RootDatabase}; +use syntax::{algo, AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode}; +use text_edit::{TextEdit, TextEditBuilder}; + +pub enum Direction { + Up, + Down, +} + +// Feature: Move Item +// +// Move item under cursor or selection up and down. +// +// |=== +// | Editor | Action Name +// +// | VS Code | **Rust Analyzer: Move item up** +// | VS Code | **Rust Analyzer: Move item down** +// |=== +pub(crate) fn move_item( + db: &RootDatabase, + range: FileRange, + direction: Direction, +) -> Option { + let sema = Semantics::new(db); + let file = sema.parse(range.file_id); + + let item = file.syntax().covering_element(range.range); + find_ancestors(item, direction) +} + +fn find_ancestors(item: SyntaxElement, direction: Direction) -> Option { + let movable = [ + SyntaxKind::MATCH_ARM, + // https://github.com/intellij-rust/intellij-rust/blob/master/src/main/kotlin/org/rust/ide/actions/mover/RsStatementUpDownMover.kt + SyntaxKind::LET_STMT, + SyntaxKind::EXPR_STMT, + SyntaxKind::MATCH_EXPR, + // https://github.com/intellij-rust/intellij-rust/blob/master/src/main/kotlin/org/rust/ide/actions/mover/RsItemUpDownMover.kt + SyntaxKind::TRAIT, + SyntaxKind::IMPL, + SyntaxKind::MACRO_CALL, + SyntaxKind::MACRO_DEF, + SyntaxKind::STRUCT, + SyntaxKind::ENUM, + SyntaxKind::MODULE, + SyntaxKind::USE, + SyntaxKind::FN, + SyntaxKind::CONST, + SyntaxKind::TYPE_ALIAS, + ]; + + let root = match item { + NodeOrToken::Node(node) => node, + NodeOrToken::Token(token) => token.parent(), + }; + + let ancestor = once(root.clone()) + .chain(root.ancestors()) + .filter(|ancestor| movable.contains(&ancestor.kind())) + .max_by_key(|ancestor| kind_priority(ancestor.kind()))?; + + move_in_direction(&ancestor, direction) +} + +fn kind_priority(kind: SyntaxKind) -> i32 { + match kind { + SyntaxKind::MATCH_ARM => 4, + + SyntaxKind::LET_STMT | SyntaxKind::EXPR_STMT | SyntaxKind::MATCH_EXPR => 3, + + SyntaxKind::TRAIT + | SyntaxKind::IMPL + | SyntaxKind::MACRO_CALL + | SyntaxKind::MACRO_DEF + | SyntaxKind::STRUCT + | SyntaxKind::ENUM + | SyntaxKind::MODULE + | SyntaxKind::USE + | SyntaxKind::FN + | SyntaxKind::CONST + | SyntaxKind::TYPE_ALIAS => 2, + + // Placeholder for items, that are non-movable, and filtered even before kind_priority call + _ => 1, + } +} + +fn move_in_direction(node: &SyntaxNode, direction: Direction) -> Option { + let sibling = match direction { + Direction::Up => node.prev_sibling(), + Direction::Down => node.next_sibling(), + }?; + + Some(replace_nodes(&sibling, node)) +} + +fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit { + let mut edit = TextEditBuilder::default(); + + algo::diff(first, second).into_text_edit(&mut edit); + algo::diff(second, first).into_text_edit(&mut edit); + + edit.finish() +} + +#[cfg(test)] +mod tests { + use crate::fixture; + use expect_test::{expect, Expect}; + + use crate::Direction; + + fn check(ra_fixture: &str, expect: Expect, direction: Direction) { + let (analysis, range) = fixture::range(ra_fixture); + let edit = analysis.move_item(range, direction).unwrap().unwrap_or_default(); + let mut file = analysis.file_text(range.file_id).unwrap().to_string(); + edit.apply(&mut file); + expect.assert_eq(&file); + } + + #[test] + fn test_moves_match_arm_up() { + check( + r#" +fn main() { + match true { + true => { + println!("Hello, world"); + }, + false =>$0$0 { + println!("Test"); + } + }; +} + "#, + expect![[r#" +fn main() { + match true { + false => { + println!("Test"); + }, + true => { + println!("Hello, world"); + } + }; +} + "#]], + Direction::Up, + ); + } + + #[test] + fn test_moves_match_arm_down() { + check( + r#" +fn main() { + match true { + true =>$0$0 { + println!("Hello, world"); + }, + false => { + println!("Test"); + } + }; +} + "#, + expect![[r#" +fn main() { + match true { + false => { + println!("Test"); + }, + true => { + println!("Hello, world"); + } + }; +} + "#]], + Direction::Down, + ); + } + + #[test] + fn test_nowhere_to_move() { + check( + r#" +fn main() { + match true { + true =>$0$0 { + println!("Hello, world"); + }, + false => { + println!("Test"); + } + }; +} + "#, + expect![[r#" +fn main() { + match true { + true => { + println!("Hello, world"); + }, + false => { + println!("Test"); + } + }; +} + "#]], + Direction::Up, + ); + } + + #[test] + fn test_moves_let_stmt_up() { + check( + r#" +fn main() { + let test = 123; + let test2$0$0 = 456; +} + "#, + expect![[r#" +fn main() { + let test2 = 456; + let test = 123; +} + "#]], + Direction::Up, + ); + } + + #[test] + fn test_prioritizes_match_arm() { + check( + r#" +fn main() { + match true { + true => { + let test = 123;$0$0 + let test2 = 456; + }, + false => { + println!("Test"); + } + }; +} + "#, + expect![[r#" +fn main() { + match true { + false => { + println!("Test"); + }, + true => { + let test = 123; + let test2 = 456; + } + }; +} + "#]], + Direction::Down, + ); + } + + #[test] + fn test_moves_expr_up() { + check( + r#" +fn main() { + println!("Hello, world"); + println!("All I want to say is...");$0$0 +} + "#, + expect![[r#" +fn main() { + println!("All I want to say is..."); + println!("Hello, world"); +} + "#]], + Direction::Up, + ); + } + + #[test] + fn test_nowhere_to_move_stmt() { + check( + r#" +fn main() { + println!("All I want to say is...");$0$0 + println!("Hello, world"); +} + "#, + expect![[r#" +fn main() { + println!("All I want to say is..."); + println!("Hello, world"); +} + "#]], + Direction::Up, + ); + } + + #[test] + fn test_move_item() { + check( + r#" +fn main() {} + +fn foo() {}$0$0 + "#, + expect![[r#" +fn foo() {} + +fn main() {} + "#]], + Direction::Up, + ); + } + + #[test] + fn test_move_impl_up() { + check( + r#" +struct Yay; + +trait Wow {} + +impl Wow for Yay {}$0$0 + "#, + expect![[r#" +struct Yay; + +impl Wow for Yay {} + +trait Wow {} + "#]], + Direction::Up, + ); + } + + #[test] + fn test_move_use_up() { + check( + r#" +use std::vec::Vec; +use std::collections::HashMap$0$0; + "#, + expect![[r#" +use std::collections::HashMap; +use std::vec::Vec; + "#]], + Direction::Up, + ); + } + + #[test] + fn moves_match_expr_up() { + check( + r#" +fn main() { + let test = 123; + + $0match test { + 456 => {}, + _ => {} + }$0; +} + "#, + expect![[r#" +fn main() { + match test { + 456 => {}, + _ => {} + }; + + let test = 123; +} + "#]], + Direction::Up, + ); + } + + #[test] + fn handles_empty_file() { + check(r#"$0$0"#, expect![[r#""#]], Direction::Up); + } +} diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index b6f484e51..8daf27867 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -1424,6 +1424,25 @@ pub(crate) fn handle_open_cargo_toml( Ok(Some(res)) } +pub(crate) fn handle_move_item( + snap: GlobalStateSnapshot, + params: lsp_ext::MoveItemParams, +) -> Result> { + let _p = profile::span("handle_move_item"); + let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let range = from_proto::file_range(&snap, params.text_document, params.range)?; + + let direction = match params.direction { + lsp_ext::MoveItemDirection::Up => ide::Direction::Up, + lsp_ext::MoveItemDirection::Down => ide::Direction::Down, + }; + + match snap.analysis.move_item(range, direction)? { + Some(text_edit) => Ok(Some(to_proto::text_document_edit(&snap, file_id, text_edit)?)), + None => Ok(None), + } +} + fn to_command_link(command: lsp_types::Command, tooltip: String) -> lsp_ext::CommandLink { lsp_ext::CommandLink { tooltip: Some(tooltip), command } } diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index efcdcd1d9..0e1fec209 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -402,3 +402,25 @@ pub(crate) enum CodeLensResolveData { pub fn supports_utf8(caps: &lsp_types::ClientCapabilities) -> bool { caps.offset_encoding.as_deref().unwrap_or_default().iter().any(|it| it == "utf-8") } + +pub enum MoveItem {} + +impl Request for MoveItem { + type Params = MoveItemParams; + type Result = Option; + const METHOD: &'static str = "experimental/moveItem"; +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct MoveItemParams { + pub direction: MoveItemDirection, + pub text_document: TextDocumentIdentifier, + pub range: Range, +} + +#[derive(Serialize, Deserialize, Debug)] +pub enum MoveItemDirection { + Up, + Down, +} diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 984790d35..022a20851 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -507,6 +507,7 @@ impl GlobalState { .on::(handlers::handle_hover) .on::(handlers::handle_open_docs) .on::(handlers::handle_open_cargo_toml) + .on::(handlers::handle_move_item) .on::(handlers::handle_on_type_formatting) .on::(handlers::handle_document_symbol) .on::(handlers::handle_workspace_symbol) diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 70501618e..3171708d5 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -658,6 +658,18 @@ pub(crate) fn goto_definition_response( } } +pub(crate) fn text_document_edit( + snap: &GlobalStateSnapshot, + file_id: FileId, + edit: TextEdit, +) -> Result { + let text_document = optional_versioned_text_document_identifier(snap, file_id); + let line_index = snap.file_line_index(file_id)?; + let edits = + edit.into_iter().map(|it| lsp_types::OneOf::Left(text_edit(&line_index, it))).collect(); + Ok(lsp_types::TextDocumentEdit { text_document, edits }) +} + pub(crate) fn snippet_text_document_edit( snap: &GlobalStateSnapshot, is_snippet: bool, -- cgit v1.2.3 From a154ef7ca1010b890e7dbe913d00140ed029945c Mon Sep 17 00:00:00 2001 From: ivan770 Date: Tue, 16 Mar 2021 16:57:14 +0200 Subject: Remove movable array, improve client code --- crates/ide/src/move_item.rs | 36 ++++++++---------------------------- 1 file changed, 8 insertions(+), 28 deletions(-) (limited to 'crates') diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs index be62d008d..5a0faaf7b 100644 --- a/crates/ide/src/move_item.rs +++ b/crates/ide/src/move_item.rs @@ -33,26 +33,6 @@ pub(crate) fn move_item( } fn find_ancestors(item: SyntaxElement, direction: Direction) -> Option { - let movable = [ - SyntaxKind::MATCH_ARM, - // https://github.com/intellij-rust/intellij-rust/blob/master/src/main/kotlin/org/rust/ide/actions/mover/RsStatementUpDownMover.kt - SyntaxKind::LET_STMT, - SyntaxKind::EXPR_STMT, - SyntaxKind::MATCH_EXPR, - // https://github.com/intellij-rust/intellij-rust/blob/master/src/main/kotlin/org/rust/ide/actions/mover/RsItemUpDownMover.kt - SyntaxKind::TRAIT, - SyntaxKind::IMPL, - SyntaxKind::MACRO_CALL, - SyntaxKind::MACRO_DEF, - SyntaxKind::STRUCT, - SyntaxKind::ENUM, - SyntaxKind::MODULE, - SyntaxKind::USE, - SyntaxKind::FN, - SyntaxKind::CONST, - SyntaxKind::TYPE_ALIAS, - ]; - let root = match item { NodeOrToken::Node(node) => node, NodeOrToken::Token(token) => token.parent(), @@ -60,17 +40,18 @@ fn find_ancestors(item: SyntaxElement, direction: Direction) -> Option let ancestor = once(root.clone()) .chain(root.ancestors()) - .filter(|ancestor| movable.contains(&ancestor.kind())) - .max_by_key(|ancestor| kind_priority(ancestor.kind()))?; + .filter_map(|ancestor| kind_priority(ancestor.kind()).map(|priority| (priority, ancestor))) + .max_by_key(|(priority, _)| *priority) + .map(|(_, ancestor)| ancestor)?; move_in_direction(&ancestor, direction) } -fn kind_priority(kind: SyntaxKind) -> i32 { +fn kind_priority(kind: SyntaxKind) -> Option { match kind { - SyntaxKind::MATCH_ARM => 4, + SyntaxKind::MATCH_ARM => Some(4), - SyntaxKind::LET_STMT | SyntaxKind::EXPR_STMT | SyntaxKind::MATCH_EXPR => 3, + SyntaxKind::LET_STMT | SyntaxKind::EXPR_STMT | SyntaxKind::MATCH_EXPR => Some(3), SyntaxKind::TRAIT | SyntaxKind::IMPL @@ -82,10 +63,9 @@ fn kind_priority(kind: SyntaxKind) -> i32 { | SyntaxKind::USE | SyntaxKind::FN | SyntaxKind::CONST - | SyntaxKind::TYPE_ALIAS => 2, + | SyntaxKind::TYPE_ALIAS => Some(2), - // Placeholder for items, that are non-movable, and filtered even before kind_priority call - _ => 1, + _ => None, } } -- cgit v1.2.3 From b67a91b96043fb750698f909354a5b73c1929e5d Mon Sep 17 00:00:00 2001 From: ivan770 Date: Tue, 16 Mar 2021 17:39:49 +0200 Subject: Fix code after rowan version bump --- crates/ide/src/move_item.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates') diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs index 5a0faaf7b..dbbf2f4c9 100644 --- a/crates/ide/src/move_item.rs +++ b/crates/ide/src/move_item.rs @@ -35,7 +35,7 @@ pub(crate) fn move_item( fn find_ancestors(item: SyntaxElement, direction: Direction) -> Option { let root = match item { NodeOrToken::Node(node) => node, - NodeOrToken::Token(token) => token.parent(), + NodeOrToken::Token(token) => token.parent()?, }; let ancestor = once(root.clone()) -- cgit v1.2.3 From f62944f416e733e01a304efe43c1e66e237d905f Mon Sep 17 00:00:00 2001 From: ivan770 Date: Wed, 17 Mar 2021 17:38:40 +0200 Subject: Remove prioritization, add more movable items --- crates/ide/src/move_item.rs | 184 +++++++++++++++++++++++++++++--------------- 1 file changed, 124 insertions(+), 60 deletions(-) (limited to 'crates') diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs index dbbf2f4c9..906d0f268 100644 --- a/crates/ide/src/move_item.rs +++ b/crates/ide/src/move_item.rs @@ -38,44 +38,42 @@ fn find_ancestors(item: SyntaxElement, direction: Direction) -> Option NodeOrToken::Token(token) => token.parent()?, }; + let movable = [ + SyntaxKind::MATCH_ARM, + SyntaxKind::PARAM, + SyntaxKind::LET_STMT, + SyntaxKind::EXPR_STMT, + SyntaxKind::MATCH_EXPR, + SyntaxKind::MACRO_CALL, + SyntaxKind::TYPE_ALIAS, + SyntaxKind::TRAIT, + SyntaxKind::IMPL, + SyntaxKind::MACRO_DEF, + SyntaxKind::STRUCT, + SyntaxKind::UNION, + SyntaxKind::ENUM, + SyntaxKind::FN, + SyntaxKind::MODULE, + SyntaxKind::USE, + SyntaxKind::STATIC, + SyntaxKind::CONST, + SyntaxKind::MACRO_RULES, + ]; + let ancestor = once(root.clone()) .chain(root.ancestors()) - .filter_map(|ancestor| kind_priority(ancestor.kind()).map(|priority| (priority, ancestor))) - .max_by_key(|(priority, _)| *priority) - .map(|(_, ancestor)| ancestor)?; + .find(|ancestor| movable.contains(&ancestor.kind()))?; move_in_direction(&ancestor, direction) } -fn kind_priority(kind: SyntaxKind) -> Option { - match kind { - SyntaxKind::MATCH_ARM => Some(4), - - SyntaxKind::LET_STMT | SyntaxKind::EXPR_STMT | SyntaxKind::MATCH_EXPR => Some(3), - - SyntaxKind::TRAIT - | SyntaxKind::IMPL - | SyntaxKind::MACRO_CALL - | SyntaxKind::MACRO_DEF - | SyntaxKind::STRUCT - | SyntaxKind::ENUM - | SyntaxKind::MODULE - | SyntaxKind::USE - | SyntaxKind::FN - | SyntaxKind::CONST - | SyntaxKind::TYPE_ALIAS => Some(2), - - _ => None, - } -} - fn move_in_direction(node: &SyntaxNode, direction: Direction) -> Option { let sibling = match direction { Direction::Up => node.prev_sibling(), Direction::Down => node.next_sibling(), }?; - Some(replace_nodes(&sibling, node)) + Some(replace_nodes(node, &sibling)) } fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit { @@ -214,39 +212,6 @@ fn main() { ); } - #[test] - fn test_prioritizes_match_arm() { - check( - r#" -fn main() { - match true { - true => { - let test = 123;$0$0 - let test2 = 456; - }, - false => { - println!("Test"); - } - }; -} - "#, - expect![[r#" -fn main() { - match true { - false => { - println!("Test"); - }, - true => { - let test = 123; - let test2 = 456; - } - }; -} - "#]], - Direction::Down, - ); - } - #[test] fn test_moves_expr_up() { check( @@ -348,7 +313,7 @@ fn main() { $0match test { 456 => {}, _ => {} - }$0; + };$0 } "#, expect![[r#" @@ -365,6 +330,105 @@ fn main() { ); } + #[test] + fn moves_param_up() { + check( + r#" +fn test(one: i32, two$0$0: u32) {} + +fn main() { + test(123, 456); +} + "#, + expect![[r#" +fn test(two: u32, one: i32) {} + +fn main() { + test(123, 456); +} + "#]], + Direction::Up, + ); + } + + #[test] + fn test_prioritizes_trait_items() { + check( + r#" +struct Test; + +trait Yay { + type One; + + type Two; + + fn inner(); +} + +impl Yay for Test { + type One = i32; + + type Two = u32; + + fn inner() {$0$0 + println!("Mmmm"); + } +} + "#, + expect![[r#" +struct Test; + +trait Yay { + type One; + + type Two; + + fn inner(); +} + +impl Yay for Test { + type One = i32; + + fn inner() { + println!("Mmmm"); + } + + type Two = u32; +} + "#]], + Direction::Up, + ); + } + + #[test] + fn test_weird_nesting() { + check( + r#" +fn test() { + mod hello { + fn inner() {} + } + + mod hi {$0$0 + fn inner() {} + } +} + "#, + expect![[r#" +fn test() { + mod hi { + fn inner() {} + } + + mod hello { + fn inner() {} + } +} + "#]], + Direction::Up, + ); + } + #[test] fn handles_empty_file() { check(r#"$0$0"#, expect![[r#""#]], Direction::Up); -- cgit v1.2.3 From 0a2d221d47e1113d8d73bb11d225c347bd83570b Mon Sep 17 00:00:00 2001 From: ivan770 Date: Fri, 19 Mar 2021 10:28:09 +0200 Subject: Added arglist mover --- crates/ide/src/move_item.rs | 114 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 102 insertions(+), 12 deletions(-) (limited to 'crates') diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs index 906d0f268..de4e57b6a 100644 --- a/crates/ide/src/move_item.rs +++ b/crates/ide/src/move_item.rs @@ -2,7 +2,10 @@ use std::iter::once; use hir::Semantics; use ide_db::{base_db::FileRange, RootDatabase}; -use syntax::{algo, AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode}; +use itertools::Itertools; +use syntax::{ + algo, ast, match_ast, AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, +}; use text_edit::{TextEdit, TextEditBuilder}; pub enum Direction { @@ -29,16 +32,17 @@ pub(crate) fn move_item( let file = sema.parse(range.file_id); let item = file.syntax().covering_element(range.range); - find_ancestors(item, direction) + find_ancestors(item, direction, range.range) } -fn find_ancestors(item: SyntaxElement, direction: Direction) -> Option { +fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -> Option { let root = match item { NodeOrToken::Node(node) => node, NodeOrToken::Token(token) => token.parent()?, }; let movable = [ + SyntaxKind::ARG_LIST, SyntaxKind::MATCH_ARM, SyntaxKind::PARAM, SyntaxKind::LET_STMT, @@ -64,16 +68,39 @@ fn find_ancestors(item: SyntaxElement, direction: Direction) -> Option .chain(root.ancestors()) .find(|ancestor| movable.contains(&ancestor.kind()))?; - move_in_direction(&ancestor, direction) + move_in_direction(&ancestor, direction, range) } -fn move_in_direction(node: &SyntaxNode, direction: Direction) -> Option { - let sibling = match direction { - Direction::Up => node.prev_sibling(), - Direction::Down => node.next_sibling(), - }?; +fn move_in_direction( + node: &SyntaxNode, + direction: Direction, + range: TextRange, +) -> Option { + match_ast! { + match node { + ast::ArgList(it) => swap_sibling_in_list(it.args(), range, direction), + _ => Some(replace_nodes(node, &match direction { + Direction::Up => node.prev_sibling(), + Direction::Down => node.next_sibling(), + }?)) + } + } +} - Some(replace_nodes(node, &sibling)) +fn swap_sibling_in_list<'i, A: AstNode + Clone, I: Iterator>( + list: I, + range: TextRange, + direction: Direction, +) -> Option { + let (l, r) = list + .tuple_windows() + .filter(|(l, r)| match direction { + Direction::Up => r.syntax().text_range().contains_range(range), + Direction::Down => l.syntax().text_range().contains_range(range), + }) + .next()?; + + Some(replace_nodes(l.syntax(), r.syntax())) } fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit { @@ -304,7 +331,7 @@ use std::vec::Vec; } #[test] - fn moves_match_expr_up() { + fn test_moves_match_expr_up() { check( r#" fn main() { @@ -331,7 +358,7 @@ fn main() { } #[test] - fn moves_param_up() { + fn test_moves_param_up() { check( r#" fn test(one: i32, two$0$0: u32) {} @@ -343,6 +370,69 @@ fn main() { expect![[r#" fn test(two: u32, one: i32) {} +fn main() { + test(123, 456); +} + "#]], + Direction::Up, + ); + } + + #[test] + fn test_moves_arg_up() { + check( + r#" +fn test(one: i32, two: u32) {} + +fn main() { + test(123, 456$0$0); +} + "#, + expect![[r#" +fn test(one: i32, two: u32) {} + +fn main() { + test(456, 123); +} + "#]], + Direction::Up, + ); + } + + #[test] + fn test_moves_arg_down() { + check( + r#" +fn test(one: i32, two: u32) {} + +fn main() { + test(123$0$0, 456); +} + "#, + expect![[r#" +fn test(one: i32, two: u32) {} + +fn main() { + test(456, 123); +} + "#]], + Direction::Down, + ); + } + + #[test] + fn test_nowhere_to_move_arg() { + check( + r#" +fn test(one: i32, two: u32) {} + +fn main() { + test(123$0$0, 456); +} + "#, + expect![[r#" +fn test(one: i32, two: u32) {} + fn main() { test(123, 456); } -- cgit v1.2.3 From 2794cc7b00658e96e2fab405f1bb5aaf1d18c2ef Mon Sep 17 00:00:00 2001 From: ivan770 Date: Fri, 19 Mar 2021 16:43:47 +0200 Subject: Added more movable lists --- crates/ide/src/move_item.rs | 96 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) (limited to 'crates') diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs index de4e57b6a..806fd58da 100644 --- a/crates/ide/src/move_item.rs +++ b/crates/ide/src/move_item.rs @@ -43,6 +43,10 @@ fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) - let movable = [ SyntaxKind::ARG_LIST, + SyntaxKind::GENERIC_PARAM_LIST, + SyntaxKind::GENERIC_ARG_LIST, + SyntaxKind::VARIANT_LIST, + SyntaxKind::TYPE_BOUND_LIST, SyntaxKind::MATCH_ARM, SyntaxKind::PARAM, SyntaxKind::LET_STMT, @@ -79,6 +83,10 @@ fn move_in_direction( match_ast! { match node { ast::ArgList(it) => swap_sibling_in_list(it.args(), range, direction), + ast::GenericParamList(it) => swap_sibling_in_list(it.generic_params(), range, direction), + ast::GenericArgList(it) => swap_sibling_in_list(it.generic_args(), range, direction), + ast::VariantList(it) => swap_sibling_in_list(it.variants(), range, direction), + ast::TypeBoundList(it) => swap_sibling_in_list(it.bounds(), range, direction), _ => Some(replace_nodes(node, &match direction { Direction::Up => node.prev_sibling(), Direction::Down => node.next_sibling(), @@ -302,7 +310,7 @@ struct Yay; trait Wow {} -impl Wow for Yay {}$0$0 +impl Wow for Yay $0$0{} "#, expect![[r#" struct Yay; @@ -441,6 +449,92 @@ fn main() { ); } + #[test] + fn test_moves_generic_param_up() { + check( + r#" +struct Test(A, B); + +fn main() {} + "#, + expect![[r#" +struct Test(A, B); + +fn main() {} + "#]], + Direction::Up, + ); + } + + #[test] + fn test_moves_generic_arg_up() { + check( + r#" +struct Test(A, B); + +fn main() { + let t = Test::(123, "yay"); +} + "#, + expect![[r#" +struct Test(A, B); + +fn main() { + let t = Test::<&str, i32>(123, "yay"); +} + "#]], + Direction::Up, + ); + } + + #[test] + fn test_moves_variant_up() { + check( + r#" +enum Hello { + One, + Two$0$0 +} + +fn main() {} + "#, + expect![[r#" +enum Hello { + Two, + One +} + +fn main() {} + "#]], + Direction::Up, + ); + } + + #[test] + fn test_moves_type_bound_up() { + check( + r#" +trait One {} + +trait Two {} + +fn test(t: T) {} + +fn main() {} + "#, + expect![[r#" +trait One {} + +trait Two {} + +fn test(t: T) {} + +fn main() {} + "#]], + Direction::Up, + ); + } + #[test] fn test_prioritizes_trait_items() { check( -- cgit v1.2.3 From d331155f8db056a0f7a406498c96f759f620d2c7 Mon Sep 17 00:00:00 2001 From: ivan770 Date: Fri, 19 Mar 2021 17:01:39 +0200 Subject: Remove needless lifetime --- crates/ide/src/move_item.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates') diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs index 806fd58da..48690b073 100644 --- a/crates/ide/src/move_item.rs +++ b/crates/ide/src/move_item.rs @@ -95,7 +95,7 @@ fn move_in_direction( } } -fn swap_sibling_in_list<'i, A: AstNode + Clone, I: Iterator>( +fn swap_sibling_in_list>( list: I, range: TextRange, direction: Direction, -- cgit v1.2.3