Skip to content

Commit 3af3036

Browse files
Merge #8510 #8533
8510: Move cursor position when using item movers r=jonas-schievink a=jonas-schievink This updates the cursor position when moving items around to stay in the same location within the moved node. I changed the `moveItem` response to `SnippetTextEdit[]`, since that made more sense to me (the file was ignored by the client anyways, since the edits always apply to the current document). It also matches `onEnter`, which seems logical to me, but please let me know if this doesn't make sense. There's still a bug in the client-side snippet code that will cause the cursor position to be slightly off when moving parameters in the same line (presumably we don't track the column correctly after deleting `$0`). Not really sure how to fix that immediately, but this PR should already be an improvement despite that bug. 8533: Fix typo in style guide r=jonas-schievink a=jonas-schievink Fixes bold text rendering bors r+ Co-authored-by: Jonas Schievink <[email protected]>
3 parents 8d17d0c + 30aae2c + 6e575d8 commit 3af3036

File tree

8 files changed

+84
-75
lines changed

8 files changed

+84
-75
lines changed

crates/ide/src/move_item.rs

Lines changed: 68 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::iter::once;
1+
use std::{iter::once, mem};
22

33
use hir::Semantics;
44
use ide_db::{base_db::FileRange, RootDatabase};
@@ -102,7 +102,7 @@ fn move_in_direction(
102102
ast::GenericArgList(it) => swap_sibling_in_list(node, it.generic_args(), range, direction),
103103
ast::VariantList(it) => swap_sibling_in_list(node, it.variants(), range, direction),
104104
ast::TypeBoundList(it) => swap_sibling_in_list(node, it.bounds(), range, direction),
105-
_ => Some(replace_nodes(node, &match direction {
105+
_ => Some(replace_nodes(range, node, &match direction {
106106
Direction::Up => node.prev_sibling(),
107107
Direction::Down => node.next_sibling(),
108108
}?))
@@ -125,7 +125,7 @@ fn swap_sibling_in_list<A: AstNode + Clone, I: Iterator<Item = A>>(
125125
.next();
126126

127127
if let Some((l, r)) = list_lookup {
128-
Some(replace_nodes(l.syntax(), r.syntax()))
128+
Some(replace_nodes(range, l.syntax(), r.syntax()))
129129
} else {
130130
// Cursor is beyond any movable list item (for example, on curly brace in enum).
131131
// 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<A: AstNode + Clone, I: Iterator<Item = A>>(
134134
}
135135
}
136136

137-
fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit {
137+
fn 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+
138165
let mut edit = TextEditBuilder::default();
139166

140167
algo::diff(first, second).into_text_edit(&mut edit);
141-
algo::diff(second, first).into_text_edit(&mut edit);
168+
edit.replace(second.text_range(), first_with_cursor);
142169

143170
edit.finish()
144171
}
@@ -188,7 +215,7 @@ fn main() {
188215
expect![[r#"
189216
fn main() {
190217
match true {
191-
false => {
218+
false =>$0 {
192219
println!("Test");
193220
},
194221
true => {
@@ -222,7 +249,7 @@ fn main() {
222249
false => {
223250
println!("Test");
224251
},
225-
true => {
252+
true =>$0 {
226253
println!("Hello, world");
227254
}
228255
};
@@ -274,7 +301,7 @@ fn main() {
274301
"#,
275302
expect![[r#"
276303
fn main() {
277-
let test2 = 456;
304+
let test2$0 = 456;
278305
let test = 123;
279306
}
280307
"#]],
@@ -293,7 +320,7 @@ fn main() {
293320
"#,
294321
expect![[r#"
295322
fn main() {
296-
println!("All I want to say is...");
323+
println!("All I want to say is...");$0
297324
println!("Hello, world");
298325
}
299326
"#]],
@@ -313,7 +340,7 @@ fn main() {
313340
fn main() {
314341
if true {
315342
println!("Test");
316-
}
343+
}$0
317344
318345
println!("Hello, world");
319346
}
@@ -334,7 +361,7 @@ fn main() {
334361
fn main() {
335362
for i in 0..10 {
336363
println!("Test");
337-
}
364+
}$0
338365
339366
println!("Hello, world");
340367
}
@@ -355,7 +382,7 @@ fn main() {
355382
fn main() {
356383
loop {
357384
println!("Test");
358-
}
385+
}$0
359386
360387
println!("Hello, world");
361388
}
@@ -376,7 +403,7 @@ fn main() {
376403
fn main() {
377404
while true {
378405
println!("Test");
379-
}
406+
}$0
380407
381408
println!("Hello, world");
382409
}
@@ -393,7 +420,7 @@ fn main() {
393420
"#,
394421
expect![[r#"
395422
fn main() {
396-
return 123;
423+
return 123;$0
397424
398425
println!("Hello, world");
399426
}
@@ -430,7 +457,7 @@ fn main() {}
430457
fn foo() {}$0$0
431458
"#,
432459
expect![[r#"
433-
fn foo() {}
460+
fn foo() {}$0
434461
435462
fn main() {}
436463
"#]],
@@ -451,7 +478,7 @@ impl Wow for Yay $0$0{}
451478
expect![[r#"
452479
struct Yay;
453480
454-
impl Wow for Yay {}
481+
impl Wow for Yay $0{}
455482
456483
trait Wow {}
457484
"#]],
@@ -467,7 +494,7 @@ use std::vec::Vec;
467494
use std::collections::HashMap$0$0;
468495
"#,
469496
expect![[r#"
470-
use std::collections::HashMap;
497+
use std::collections::HashMap$0;
471498
use std::vec::Vec;
472499
"#]],
473500
Direction::Up,
@@ -502,7 +529,7 @@ fn main() {
502529
}
503530

504531
#[test]
505-
fn test_moves_param_up() {
532+
fn test_moves_param() {
506533
check(
507534
r#"
508535
fn test(one: i32, two$0$0: u32) {}
@@ -512,14 +539,23 @@ fn main() {
512539
}
513540
"#,
514541
expect![[r#"
515-
fn test(two: u32, one: i32) {}
542+
fn test(two$0: u32, one: i32) {}
516543
517544
fn main() {
518545
test(123, 456);
519546
}
520547
"#]],
521548
Direction::Up,
522549
);
550+
check(
551+
r#"
552+
fn f($0$0arg: u8, arg2: u16) {}
553+
"#,
554+
expect![[r#"
555+
fn f(arg2: u16, $0arg: u8) {}
556+
"#]],
557+
Direction::Down,
558+
);
523559
}
524560

525561
#[test]
@@ -536,7 +572,7 @@ fn main() {
536572
fn test(one: i32, two: u32) {}
537573
538574
fn main() {
539-
test(456, 123);
575+
test(456$0, 123);
540576
}
541577
"#]],
542578
Direction::Up,
@@ -557,7 +593,7 @@ fn main() {
557593
fn test(one: i32, two: u32) {}
558594
559595
fn main() {
560-
test(456, 123);
596+
test(456, 123$0);
561597
}
562598
"#]],
563599
Direction::Down,
@@ -594,7 +630,7 @@ struct Test<A, B$0$0>(A, B);
594630
fn main() {}
595631
"#,
596632
expect![[r#"
597-
struct Test<B, A>(A, B);
633+
struct Test<B$0, A>(A, B);
598634
599635
fn main() {}
600636
"#]],
@@ -616,7 +652,7 @@ fn main() {
616652
struct Test<A, B>(A, B);
617653
618654
fn main() {
619-
let t = Test::<&str, i32>(123, "yay");
655+
let t = Test::<&str$0, i32>(123, "yay");
620656
}
621657
"#]],
622658
Direction::Up,
@@ -636,7 +672,7 @@ fn main() {}
636672
"#,
637673
expect![[r#"
638674
enum Hello {
639-
Two,
675+
Two$0,
640676
One
641677
}
642678
@@ -663,7 +699,7 @@ trait One {}
663699
664700
trait Two {}
665701
666-
fn test<T: Two + One>(t: T) {}
702+
fn test<T: Two$0 + One>(t: T) {}
667703
668704
fn main() {}
669705
"#]],
@@ -709,7 +745,7 @@ trait Yay {
709745
impl Yay for Test {
710746
type One = i32;
711747
712-
fn inner() {
748+
fn inner() {$0
713749
println!("Mmmm");
714750
}
715751
@@ -736,7 +772,7 @@ fn test() {
736772
"#,
737773
expect![[r#"
738774
fn test() {
739-
mod hi {
775+
mod hi {$0
740776
fn inner() {}
741777
}
742778
@@ -764,7 +800,7 @@ fn main() {}
764800
expect![[r#"
765801
fn main() {}
766802
767-
#[derive(Debug)]
803+
$0#[derive(Debug)]
768804
enum FooBar {
769805
Foo,
770806
Bar,
@@ -784,7 +820,7 @@ fn main() {}
784820
expect![[r#"
785821
fn main() {}
786822
787-
enum FooBar {
823+
$0enum FooBar {
788824
Foo,
789825
Bar,
790826
}
@@ -804,7 +840,7 @@ fn main() {}
804840
expect![[r#"
805841
struct Test;
806842
807-
impl SomeTrait for Test {}
843+
$0impl SomeTrait for Test {}
808844
809845
trait SomeTrait {}
810846
@@ -831,7 +867,7 @@ fn main() {}
831867
enum FooBar {
832868
Foo,
833869
Bar,
834-
}
870+
}$0
835871
"#]],
836872
Direction::Down,
837873
);
@@ -848,7 +884,7 @@ fn main() {}
848884
expect![[r#"
849885
struct Test;
850886
851-
impl SomeTrait for Test {}
887+
impl SomeTrait for Test {}$0
852888
853889
trait SomeTrait {}
854890

crates/rust-analyzer/src/handlers.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,7 +1410,7 @@ pub(crate) fn handle_open_cargo_toml(
14101410
pub(crate) fn handle_move_item(
14111411
snap: GlobalStateSnapshot,
14121412
params: lsp_ext::MoveItemParams,
1413-
) -> Result<Option<lsp_types::TextDocumentEdit>> {
1413+
) -> Result<Vec<lsp_ext::SnippetTextEdit>> {
14141414
let _p = profile::span("handle_move_item");
14151415
let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
14161416
let range = from_proto::file_range(&snap, params.text_document, params.range)?;
@@ -1421,8 +1421,11 @@ pub(crate) fn handle_move_item(
14211421
};
14221422

14231423
match snap.analysis.move_item(range, direction)? {
1424-
Some(text_edit) => Ok(Some(to_proto::text_document_edit(&snap, file_id, text_edit)?)),
1425-
None => Ok(None),
1424+
Some(text_edit) => {
1425+
let line_index = snap.file_line_index(file_id)?;
1426+
Ok(to_proto::snippet_text_edit_vec(&line_index, true, text_edit))
1427+
}
1428+
None => Ok(vec![]),
14261429
}
14271430
}
14281431

crates/rust-analyzer/src/lsp_ext.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ pub enum MoveItem {}
407407

408408
impl Request for MoveItem {
409409
type Params = MoveItemParams;
410-
type Result = Option<lsp_types::TextDocumentEdit>;
410+
type Result = Vec<SnippetTextEdit>;
411411
const METHOD: &'static str = "experimental/moveItem";
412412
}
413413

crates/rust-analyzer/src/to_proto.rs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -688,18 +688,6 @@ pub(crate) fn goto_definition_response(
688688
}
689689
}
690690

691-
pub(crate) fn text_document_edit(
692-
snap: &GlobalStateSnapshot,
693-
file_id: FileId,
694-
edit: TextEdit,
695-
) -> Result<lsp_types::TextDocumentEdit> {
696-
let text_document = optional_versioned_text_document_identifier(snap, file_id);
697-
let line_index = snap.file_line_index(file_id)?;
698-
let edits =
699-
edit.into_iter().map(|it| lsp_types::OneOf::Left(text_edit(&line_index, it))).collect();
700-
Ok(lsp_types::TextDocumentEdit { text_document, edits })
701-
}
702-
703691
pub(crate) fn snippet_text_document_edit(
704692
snap: &GlobalStateSnapshot,
705693
is_snippet: bool,

docs/dev/lsp-extensions.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<!---
2-
lsp_ext.rs hash: faae991334a151d0
2+
lsp_ext.rs hash: b19ddc3ab8767af9
33
44
If you need to change the above hash to make the test pass, please check if you
5-
need to adjust this doc as well and ping this issue:
5+
need to adjust this doc as well and ping this issue:
66
77
https://github.com/rust-analyzer/rust-analyzer/issues/4604
88
@@ -620,7 +620,7 @@ This request is sent from client to server to move item under cursor or selectio
620620

621621
**Request:** `MoveItemParams`
622622

623-
**Response:** `TextDocumentEdit | null`
623+
**Response:** `SnippetTextEdit[]`
624624

625625
```typescript
626626
export interface MoveItemParams {

0 commit comments

Comments
 (0)