From 30aae2cefb9d068055ca8d250d04a288e3684394 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 13 Apr 2021 20:32:45 +0200 Subject: Move cursor position when using item movers --- crates/ide/src/move_item.rs | 100 ++++++++++++++++++++++++----------- crates/rust-analyzer/src/handlers.rs | 9 ++-- crates/rust-analyzer/src/lsp_ext.rs | 2 +- crates/rust-analyzer/src/to_proto.rs | 12 ----- docs/dev/lsp-extensions.md | 6 +-- editors/code/src/commands.ts | 26 ++------- editors/code/src/lsp_ext.ts | 2 +- 7 files changed, 83 insertions(+), 74 deletions(-) diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs index 8d37f4f92..246f10a0a 100644 --- a/crates/ide/src/move_item.rs +++ b/crates/ide/src/move_item.rs @@ -1,4 +1,4 @@ -use std::iter::once; +use std::{iter::once, mem}; use hir::Semantics; use ide_db::{base_db::FileRange, RootDatabase}; @@ -102,7 +102,7 @@ fn move_in_direction( ast::GenericArgList(it) => swap_sibling_in_list(node, it.generic_args(), range, direction), ast::VariantList(it) => swap_sibling_in_list(node, it.variants(), range, direction), ast::TypeBoundList(it) => swap_sibling_in_list(node, it.bounds(), range, direction), - _ => Some(replace_nodes(node, &match direction { + _ => Some(replace_nodes(range, node, &match direction { Direction::Up => node.prev_sibling(), Direction::Down => node.next_sibling(), }?)) @@ -125,7 +125,7 @@ fn swap_sibling_in_list>( .next(); if let Some((l, r)) = list_lookup { - Some(replace_nodes(l.syntax(), r.syntax())) + Some(replace_nodes(range, l.syntax(), r.syntax())) } else { // Cursor is beyond any movable list item (for example, on curly brace in enum). // It's not necessary, that parent of list is movable (arg list's parent is not, for example), @@ -134,11 +134,38 @@ fn swap_sibling_in_list>( } } -fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit { +fn replace_nodes<'a>( + range: TextRange, + mut first: &'a SyntaxNode, + mut second: &'a SyntaxNode, +) -> TextEdit { + let cursor_offset = if range.is_empty() { + // FIXME: `applySnippetTextEdits` does not support non-empty selection ranges + if first.text_range().contains_range(range) { + Some(range.start() - first.text_range().start()) + } else if second.text_range().contains_range(range) { + mem::swap(&mut first, &mut second); + Some(range.start() - first.text_range().start()) + } else { + None + } + } else { + None + }; + + let first_with_cursor = match cursor_offset { + Some(offset) => { + let mut item_text = first.text().to_string(); + item_text.insert_str(offset.into(), "$0"); + item_text + } + None => first.text().to_string(), + }; + let mut edit = TextEditBuilder::default(); algo::diff(first, second).into_text_edit(&mut edit); - algo::diff(second, first).into_text_edit(&mut edit); + edit.replace(second.text_range(), first_with_cursor); edit.finish() } @@ -188,7 +215,7 @@ fn main() { expect![[r#" fn main() { match true { - false => { + false =>$0 { println!("Test"); }, true => { @@ -222,7 +249,7 @@ fn main() { false => { println!("Test"); }, - true => { + true =>$0 { println!("Hello, world"); } }; @@ -274,7 +301,7 @@ fn main() { "#, expect![[r#" fn main() { - let test2 = 456; + let test2$0 = 456; let test = 123; } "#]], @@ -293,7 +320,7 @@ fn main() { "#, expect![[r#" fn main() { - println!("All I want to say is..."); + println!("All I want to say is...");$0 println!("Hello, world"); } "#]], @@ -313,7 +340,7 @@ fn main() { fn main() { if true { println!("Test"); - } + }$0 println!("Hello, world"); } @@ -334,7 +361,7 @@ fn main() { fn main() { for i in 0..10 { println!("Test"); - } + }$0 println!("Hello, world"); } @@ -355,7 +382,7 @@ fn main() { fn main() { loop { println!("Test"); - } + }$0 println!("Hello, world"); } @@ -376,7 +403,7 @@ fn main() { fn main() { while true { println!("Test"); - } + }$0 println!("Hello, world"); } @@ -393,7 +420,7 @@ fn main() { "#, expect![[r#" fn main() { - return 123; + return 123;$0 println!("Hello, world"); } @@ -430,7 +457,7 @@ fn main() {} fn foo() {}$0$0 "#, expect![[r#" -fn foo() {} +fn foo() {}$0 fn main() {} "#]], @@ -451,7 +478,7 @@ impl Wow for Yay $0$0{} expect![[r#" struct Yay; -impl Wow for Yay {} +impl Wow for Yay $0{} trait Wow {} "#]], @@ -467,7 +494,7 @@ use std::vec::Vec; use std::collections::HashMap$0$0; "#, expect![[r#" -use std::collections::HashMap; +use std::collections::HashMap$0; use std::vec::Vec; "#]], Direction::Up, @@ -502,7 +529,7 @@ fn main() { } #[test] - fn test_moves_param_up() { + fn test_moves_param() { check( r#" fn test(one: i32, two$0$0: u32) {} @@ -512,7 +539,7 @@ fn main() { } "#, expect![[r#" -fn test(two: u32, one: i32) {} +fn test(two$0: u32, one: i32) {} fn main() { test(123, 456); @@ -520,6 +547,15 @@ fn main() { "#]], Direction::Up, ); + check( + r#" +fn f($0$0arg: u8, arg2: u16) {} + "#, + expect![[r#" +fn f(arg2: u16, $0arg: u8) {} + "#]], + Direction::Down, + ); } #[test] @@ -536,7 +572,7 @@ fn main() { fn test(one: i32, two: u32) {} fn main() { - test(456, 123); + test(456$0, 123); } "#]], Direction::Up, @@ -557,7 +593,7 @@ fn main() { fn test(one: i32, two: u32) {} fn main() { - test(456, 123); + test(456, 123$0); } "#]], Direction::Down, @@ -594,7 +630,7 @@ struct Test(A, B); fn main() {} "#, expect![[r#" -struct Test(A, B); +struct Test(A, B); fn main() {} "#]], @@ -616,7 +652,7 @@ fn main() { struct Test(A, B); fn main() { - let t = Test::<&str, i32>(123, "yay"); + let t = Test::<&str$0, i32>(123, "yay"); } "#]], Direction::Up, @@ -636,7 +672,7 @@ fn main() {} "#, expect![[r#" enum Hello { - Two, + Two$0, One } @@ -663,7 +699,7 @@ trait One {} trait Two {} -fn test(t: T) {} +fn test(t: T) {} fn main() {} "#]], @@ -709,7 +745,7 @@ trait Yay { impl Yay for Test { type One = i32; - fn inner() { + fn inner() {$0 println!("Mmmm"); } @@ -736,7 +772,7 @@ fn test() { "#, expect![[r#" fn test() { - mod hi { + mod hi {$0 fn inner() {} } @@ -764,7 +800,7 @@ fn main() {} expect![[r#" fn main() {} -#[derive(Debug)] +$0#[derive(Debug)] enum FooBar { Foo, Bar, @@ -784,7 +820,7 @@ fn main() {} expect![[r#" fn main() {} -enum FooBar { +$0enum FooBar { Foo, Bar, } @@ -804,7 +840,7 @@ fn main() {} expect![[r#" struct Test; -impl SomeTrait for Test {} +$0impl SomeTrait for Test {} trait SomeTrait {} @@ -831,7 +867,7 @@ fn main() {} enum FooBar { Foo, Bar, -} +}$0 "#]], Direction::Down, ); @@ -848,7 +884,7 @@ fn main() {} expect![[r#" struct Test; -impl SomeTrait for Test {} +impl SomeTrait for Test {}$0 trait SomeTrait {} diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 4f0c9d23c..1f59402e5 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -1410,7 +1410,7 @@ pub(crate) fn handle_open_cargo_toml( pub(crate) fn handle_move_item( snap: GlobalStateSnapshot, params: lsp_ext::MoveItemParams, -) -> Result> { +) -> 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)?; @@ -1421,8 +1421,11 @@ pub(crate) fn handle_move_item( }; 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), + Some(text_edit) => { + let line_index = snap.file_line_index(file_id)?; + Ok(to_proto::snippet_text_edit_vec(&line_index, true, text_edit)) + } + None => Ok(vec![]), } } diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 81a6f22f1..d648cda32 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -407,7 +407,7 @@ pub enum MoveItem {} impl Request for MoveItem { type Params = MoveItemParams; - type Result = Option; + type Result = Vec; const METHOD: &'static str = "experimental/moveItem"; } diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 8d7cb9b74..1a1f65f3b 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -688,18 +688,6 @@ 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, diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index a46121bb2..a4d92242b 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -1,8 +1,8 @@