aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/assists/src/handlers/qualify_path.rs182
-rw-r--r--crates/ide/src/diagnostics.rs2
-rw-r--r--crates/ide/src/inlay_hints.rs108
-rw-r--r--crates/syntax/src/algo.rs160
4 files changed, 396 insertions, 56 deletions
diff --git a/crates/assists/src/handlers/qualify_path.rs b/crates/assists/src/handlers/qualify_path.rs
index f436bdbbf..d5bc4e574 100644
--- a/crates/assists/src/handlers/qualify_path.rs
+++ b/crates/assists/src/handlers/qualify_path.rs
@@ -56,12 +56,14 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
56 ImportCandidate::QualifierStart(_) => { 56 ImportCandidate::QualifierStart(_) => {
57 mark::hit!(qualify_path_qualifier_start); 57 mark::hit!(qualify_path_qualifier_start);
58 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; 58 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?;
59 let segment = path.segment()?; 59 let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
60 QualifyCandidate::QualifierStart(segment) 60 QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
61 } 61 }
62 ImportCandidate::UnqualifiedName(_) => { 62 ImportCandidate::UnqualifiedName(_) => {
63 mark::hit!(qualify_path_unqualified_name); 63 mark::hit!(qualify_path_unqualified_name);
64 QualifyCandidate::UnqualifiedName 64 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?;
65 let generics = path.segment()?.generic_arg_list();
66 QualifyCandidate::UnqualifiedName(generics)
65 } 67 }
66 ImportCandidate::TraitAssocItem(_) => { 68 ImportCandidate::TraitAssocItem(_) => {
67 mark::hit!(qualify_path_trait_assoc_item); 69 mark::hit!(qualify_path_trait_assoc_item);
@@ -96,22 +98,25 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
96} 98}
97 99
98enum QualifyCandidate<'db> { 100enum QualifyCandidate<'db> {
99 QualifierStart(ast::PathSegment), 101 QualifierStart(ast::PathSegment, Option<ast::GenericArgList>),
100 UnqualifiedName, 102 UnqualifiedName(Option<ast::GenericArgList>),
101 TraitAssocItem(ast::Path, ast::PathSegment), 103 TraitAssocItem(ast::Path, ast::PathSegment),
102 TraitMethod(&'db RootDatabase, ast::MethodCallExpr), 104 TraitMethod(&'db RootDatabase, ast::MethodCallExpr),
103} 105}
104 106
105impl QualifyCandidate<'_> { 107impl QualifyCandidate<'_> {
106 fn qualify(&self, mut replacer: impl FnMut(String), import: hir::ModPath, item: hir::ItemInNs) { 108 fn qualify(&self, mut replacer: impl FnMut(String), import: hir::ModPath, item: hir::ItemInNs) {
109 let import = mod_path_to_ast(&import);
107 match self { 110 match self {
108 QualifyCandidate::QualifierStart(segment) => { 111 QualifyCandidate::QualifierStart(segment, generics) => {
109 let import = mod_path_to_ast(&import); 112 let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
110 replacer(format!("{}::{}", import, segment)); 113 replacer(format!("{}{}::{}", import, generics, segment));
114 }
115 QualifyCandidate::UnqualifiedName(generics) => {
116 let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
117 replacer(format!("{}{}", import.to_string(), generics));
111 } 118 }
112 QualifyCandidate::UnqualifiedName => replacer(mod_path_to_ast(&import).to_string()),
113 QualifyCandidate::TraitAssocItem(qualifier, segment) => { 119 QualifyCandidate::TraitAssocItem(qualifier, segment) => {
114 let import = mod_path_to_ast(&import);
115 replacer(format!("<{} as {}>::{}", qualifier, import, segment)); 120 replacer(format!("<{} as {}>::{}", qualifier, import, segment));
116 } 121 }
117 &QualifyCandidate::TraitMethod(db, ref mcall_expr) => { 122 &QualifyCandidate::TraitMethod(db, ref mcall_expr) => {
@@ -124,25 +129,27 @@ impl QualifyCandidate<'_> {
124 db: &RootDatabase, 129 db: &RootDatabase,
125 mcall_expr: &ast::MethodCallExpr, 130 mcall_expr: &ast::MethodCallExpr,
126 mut replacer: impl FnMut(String), 131 mut replacer: impl FnMut(String),
127 import: hir::ModPath, 132 import: ast::Path,
128 item: hir::ItemInNs, 133 item: hir::ItemInNs,
129 ) -> Option<()> { 134 ) -> Option<()> {
130 let receiver = mcall_expr.receiver()?; 135 let receiver = mcall_expr.receiver()?;
131 let trait_method_name = mcall_expr.name_ref()?; 136 let trait_method_name = mcall_expr.name_ref()?;
137 let generics =
138 mcall_expr.generic_arg_list().as_ref().map_or_else(String::new, ToString::to_string);
132 let arg_list = mcall_expr.arg_list().map(|arg_list| arg_list.args()); 139 let arg_list = mcall_expr.arg_list().map(|arg_list| arg_list.args());
133 let trait_ = item_as_trait(item)?; 140 let trait_ = item_as_trait(item)?;
134 let method = find_trait_method(db, trait_, &trait_method_name)?; 141 let method = find_trait_method(db, trait_, &trait_method_name)?;
135 if let Some(self_access) = method.self_param(db).map(|sp| sp.access(db)) { 142 if let Some(self_access) = method.self_param(db).map(|sp| sp.access(db)) {
136 let import = mod_path_to_ast(&import);
137 let receiver = match self_access { 143 let receiver = match self_access {
138 hir::Access::Shared => make::expr_ref(receiver, false), 144 hir::Access::Shared => make::expr_ref(receiver, false),
139 hir::Access::Exclusive => make::expr_ref(receiver, true), 145 hir::Access::Exclusive => make::expr_ref(receiver, true),
140 hir::Access::Owned => receiver, 146 hir::Access::Owned => receiver,
141 }; 147 };
142 replacer(format!( 148 replacer(format!(
143 "{}::{}{}", 149 "{}::{}{}{}",
144 import, 150 import,
145 trait_method_name, 151 trait_method_name,
152 generics,
146 match arg_list.clone() { 153 match arg_list.clone() {
147 Some(args) => make::arg_list(iter::once(receiver).chain(args)), 154 Some(args) => make::arg_list(iter::once(receiver).chain(args)),
148 None => make::arg_list(iter::once(receiver)), 155 None => make::arg_list(iter::once(receiver)),
@@ -1045,4 +1052,153 @@ fn main() {
1045", 1052",
1046 ); 1053 );
1047 } 1054 }
1055
1056 #[test]
1057 fn keep_generic_annotations() {
1058 check_assist(
1059 qualify_path,
1060 r"
1061//- /lib.rs crate:dep
1062pub mod generic { pub struct Thing<'a, T>(&'a T); }
1063
1064//- /main.rs crate:main deps:dep
1065fn foo() -> Thin<|>g<'static, ()> {}
1066
1067fn main() {}
1068",
1069 r"
1070fn foo() -> dep::generic::Thing<'static, ()> {}
1071
1072fn main() {}
1073",
1074 );
1075 }
1076
1077 #[test]
1078 fn keep_generic_annotations_leading_colon() {
1079 check_assist(
1080 qualify_path,
1081 r"
1082//- /lib.rs crate:dep
1083pub mod generic { pub struct Thing<'a, T>(&'a T); }
1084
1085//- /main.rs crate:main deps:dep
1086fn foo() -> Thin<|>g::<'static, ()> {}
1087
1088fn main() {}
1089",
1090 r"
1091fn foo() -> dep::generic::Thing::<'static, ()> {}
1092
1093fn main() {}
1094",
1095 );
1096 }
1097
1098 #[test]
1099 fn associated_struct_const_generic() {
1100 check_assist(
1101 qualify_path,
1102 r"
1103 mod test_mod {
1104 pub struct TestStruct<T> {}
1105 impl<T> TestStruct<T> {
1106 const TEST_CONST: u8 = 42;
1107 }
1108 }
1109
1110 fn main() {
1111 TestStruct::<()>::TEST_CONST<|>
1112 }
1113 ",
1114 r"
1115 mod test_mod {
1116 pub struct TestStruct<T> {}
1117 impl<T> TestStruct<T> {
1118 const TEST_CONST: u8 = 42;
1119 }
1120 }
1121
1122 fn main() {
1123 test_mod::TestStruct::<()>::TEST_CONST
1124 }
1125 ",
1126 );
1127 }
1128
1129 #[test]
1130 fn associated_trait_const_generic() {
1131 check_assist(
1132 qualify_path,
1133 r"
1134 mod test_mod {
1135 pub trait TestTrait {
1136 const TEST_CONST: u8;
1137 }
1138 pub struct TestStruct<T> {}
1139 impl<T> TestTrait for TestStruct<T> {
1140 const TEST_CONST: u8 = 42;
1141 }
1142 }
1143
1144 fn main() {
1145 test_mod::TestStruct::<()>::TEST_CONST<|>
1146 }
1147 ",
1148 r"
1149 mod test_mod {
1150 pub trait TestTrait {
1151 const TEST_CONST: u8;
1152 }
1153 pub struct TestStruct<T> {}
1154 impl<T> TestTrait for TestStruct<T> {
1155 const TEST_CONST: u8 = 42;
1156 }
1157 }
1158
1159 fn main() {
1160 <test_mod::TestStruct::<()> as test_mod::TestTrait>::TEST_CONST
1161 }
1162 ",
1163 );
1164 }
1165
1166 #[test]
1167 fn trait_method_generic() {
1168 check_assist(
1169 qualify_path,
1170 r"
1171 mod test_mod {
1172 pub trait TestTrait {
1173 fn test_method<T>(&self);
1174 }
1175 pub struct TestStruct {}
1176 impl TestTrait for TestStruct {
1177 fn test_method<T>(&self) {}
1178 }
1179 }
1180
1181 fn main() {
1182 let test_struct = test_mod::TestStruct {};
1183 test_struct.test_meth<|>od::<()>()
1184 }
1185 ",
1186 r"
1187 mod test_mod {
1188 pub trait TestTrait {
1189 fn test_method<T>(&self);
1190 }
1191 pub struct TestStruct {}
1192 impl TestTrait for TestStruct {
1193 fn test_method<T>(&self) {}
1194 }
1195 }
1196
1197 fn main() {
1198 let test_struct = test_mod::TestStruct {};
1199 test_mod::TestTrait::test_method::<()>(&test_struct)
1200 }
1201 ",
1202 );
1203 }
1048} 1204}
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index d0ee58858..1c7f02763 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -613,7 +613,7 @@ fn main() {
613pub struct Foo { pub a: i32, pub b: i32 } 613pub struct Foo { pub a: i32, pub b: i32 }
614"#, 614"#,
615 r#" 615 r#"
616fn some(, b: ()} {} 616fn some(, b: ()) {}
617fn items() {} 617fn items() {}
618fn here() {} 618fn here() {}
619 619
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index adb93efd7..ac704ae21 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -1,6 +1,6 @@
1use assists::utils::FamousDefs; 1use assists::utils::FamousDefs;
2use either::Either; 2use either::Either;
3use hir::{known, HirDisplay, Semantics}; 3use hir::{known, Callable, HirDisplay, Semantics};
4use ide_db::RootDatabase; 4use ide_db::RootDatabase;
5use stdx::to_lower_snake_case; 5use stdx::to_lower_snake_case;
6use syntax::{ 6use syntax::{
@@ -170,7 +170,7 @@ fn get_param_name_hints(
170 }; 170 };
171 Some((param_name, arg)) 171 Some((param_name, arg))
172 }) 172 })
173 .filter(|(param_name, arg)| should_show_param_name_hint(sema, &callable, &param_name, &arg)) 173 .filter(|(param_name, arg)| should_show_param_name_hint(sema, &callable, param_name, &arg))
174 .map(|(param_name, arg)| InlayHint { 174 .map(|(param_name, arg)| InlayHint {
175 range: arg.syntax().text_range(), 175 range: arg.syntax().text_range(),
176 kind: InlayKind::ParameterHint, 176 kind: InlayKind::ParameterHint,
@@ -334,9 +334,11 @@ fn should_show_param_name_hint(
334 | hir::CallableKind::TupleEnumVariant(_) 334 | hir::CallableKind::TupleEnumVariant(_)
335 | hir::CallableKind::Closure => None, 335 | hir::CallableKind::Closure => None,
336 }; 336 };
337
337 if param_name.is_empty() 338 if param_name.is_empty()
338 || Some(param_name) == fn_name.as_ref().map(|s| s.trim_start_matches('_')) 339 || Some(param_name) == fn_name.as_ref().map(|s| s.trim_start_matches('_'))
339 || is_argument_similar_to_param_name(sema, argument, param_name) 340 || is_argument_similar_to_param_name(sema, argument, param_name)
341 || is_param_name_similar_to_fn_name(param_name, callable, fn_name.as_ref())
340 || param_name.starts_with("ra_fixture") 342 || param_name.starts_with("ra_fixture")
341 { 343 {
342 return false; 344 return false;
@@ -364,6 +366,26 @@ fn is_argument_similar_to_param_name(
364 } 366 }
365} 367}
366 368
369fn is_param_name_similar_to_fn_name(
370 param_name: &str,
371 callable: &Callable,
372 fn_name: Option<&String>,
373) -> bool {
374 // if it's the only parameter, don't show it if:
375 // - is the same as the function name, or
376 // - the function ends with '_' + param_name
377
378 match (callable.n_params(), fn_name) {
379 (1, Some(function)) => {
380 function == param_name
381 || (function.len() > param_name.len()
382 && function.ends_with(param_name)
383 && function[..function.len() - param_name.len()].ends_with('_'))
384 }
385 _ => false,
386 }
387}
388
367fn is_enum_name_similar_to_param_name( 389fn is_enum_name_similar_to_param_name(
368 sema: &Semantics<RootDatabase>, 390 sema: &Semantics<RootDatabase>,
369 argument: &ast::Expr, 391 argument: &ast::Expr,
@@ -457,6 +479,88 @@ fn main() {
457 } 479 }
458 480
459 #[test] 481 #[test]
482 fn param_name_similar_to_fn_name_still_hints() {
483 check_with_config(
484 InlayHintsConfig {
485 parameter_hints: true,
486 type_hints: false,
487 chaining_hints: false,
488 max_length: None,
489 },
490 r#"
491fn max(x: i32, y: i32) -> i32 { x + y }
492fn main() {
493 let _x = max(
494 4,
495 //^ x
496 4,
497 //^ y
498 );
499}"#,
500 );
501 }
502
503 #[test]
504 fn param_name_similar_to_fn_name() {
505 check_with_config(
506 InlayHintsConfig {
507 parameter_hints: true,
508 type_hints: false,
509 chaining_hints: false,
510 max_length: None,
511 },
512 r#"
513fn param_with_underscore(with_underscore: i32) -> i32 { with_underscore }
514fn main() {
515 let _x = param_with_underscore(
516 4,
517 );
518}"#,
519 );
520 }
521
522 #[test]
523 fn param_name_same_as_fn_name() {
524 check_with_config(
525 InlayHintsConfig {
526 parameter_hints: true,
527 type_hints: false,
528 chaining_hints: false,
529 max_length: None,
530 },
531 r#"
532fn foo(foo: i32) -> i32 { foo }
533fn main() {
534 let _x = foo(
535 4,
536 );
537}"#,
538 );
539 }
540
541 #[test]
542 fn never_hide_param_when_multiple_params() {
543 check_with_config(
544 InlayHintsConfig {
545 parameter_hints: true,
546 type_hints: false,
547 chaining_hints: false,
548 max_length: None,
549 },
550 r#"
551fn foo(bar: i32, baz: i32) -> i32 { bar + baz }
552fn main() {
553 let _x = foo(
554 4,
555 //^ bar
556 8,
557 //^ baz
558 );
559}"#,
560 );
561 }
562
563 #[test]
460 fn hints_disabled() { 564 fn hints_disabled() {
461 check_with_config( 565 check_with_config(
462 InlayHintsConfig { 566 InlayHintsConfig {
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs
index 065035fe6..9dc7182bd 100644
--- a/crates/syntax/src/algo.rs
+++ b/crates/syntax/src/algo.rs
@@ -137,7 +137,7 @@ impl TreeDiff {
137 } 137 }
138} 138}
139 139
140/// Finds minimal the diff, which, applied to `from`, will result in `to`. 140/// Finds a (potentially minimal) diff, which, applied to `from`, will result in `to`.
141/// 141///
142/// Specifically, returns a structure that consists of a replacements, insertions and deletions 142/// Specifically, returns a structure that consists of a replacements, insertions and deletions
143/// such that applying this map on `from` will result in `to`. 143/// such that applying this map on `from` will result in `to`.
@@ -151,7 +151,6 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
151 }; 151 };
152 let (from, to) = (from.clone().into(), to.clone().into()); 152 let (from, to) = (from.clone().into(), to.clone().into());
153 153
154 // FIXME: this is horrible inefficient. I bet there's a cool algorithm to diff trees properly.
155 if !syntax_element_eq(&from, &to) { 154 if !syntax_element_eq(&from, &to) {
156 go(&mut diff, from, to); 155 go(&mut diff, from, to);
157 } 156 }
@@ -169,6 +168,7 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
169 } 168 }
170 } 169 }
171 170
171 // FIXME: this is horrible inefficient. I bet there's a cool algorithm to diff trees properly.
172 fn go(diff: &mut TreeDiff, lhs: SyntaxElement, rhs: SyntaxElement) { 172 fn go(diff: &mut TreeDiff, lhs: SyntaxElement, rhs: SyntaxElement) {
173 let (lhs, rhs) = match lhs.as_node().zip(rhs.as_node()) { 173 let (lhs, rhs) = match lhs.as_node().zip(rhs.as_node()) {
174 Some((lhs, rhs)) => (lhs, rhs), 174 Some((lhs, rhs)) => (lhs, rhs),
@@ -179,6 +179,8 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
179 } 179 }
180 }; 180 };
181 181
182 let mut look_ahead_scratch = Vec::default();
183
182 let mut rhs_children = rhs.children_with_tokens(); 184 let mut rhs_children = rhs.children_with_tokens();
183 let mut lhs_children = lhs.children_with_tokens(); 185 let mut lhs_children = lhs.children_with_tokens();
184 let mut last_lhs = None; 186 let mut last_lhs = None;
@@ -204,7 +206,31 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
204 diff.deletions.push(element); 206 diff.deletions.push(element);
205 } 207 }
206 (Some(ref lhs_ele), Some(ref rhs_ele)) if syntax_element_eq(lhs_ele, rhs_ele) => {} 208 (Some(ref lhs_ele), Some(ref rhs_ele)) if syntax_element_eq(lhs_ele, rhs_ele) => {}
207 (Some(lhs_ele), Some(rhs_ele)) => go(diff, lhs_ele, rhs_ele), 209 (Some(lhs_ele), Some(rhs_ele)) => {
210 // nodes differ, look for lhs_ele in rhs, if its found we can mark everything up
211 // until that element as insertions. This is important to keep the diff minimal
212 // in regards to insertions that have been actually done, this is important for
213 // use insertions as we do not want to replace the entire module node.
214 look_ahead_scratch.push(rhs_ele.clone());
215 let mut rhs_children_clone = rhs_children.clone();
216 let mut insert = false;
217 while let Some(rhs_child) = rhs_children_clone.next() {
218 if syntax_element_eq(&lhs_ele, &rhs_child) {
219 mark::hit!(diff_insertions);
220 insert = true;
221 break;
222 } else {
223 look_ahead_scratch.push(rhs_child);
224 }
225 }
226 let drain = look_ahead_scratch.drain(..);
227 if let Some(prev) = last_lhs.clone().filter(|_| insert) {
228 diff.insertions.entry(prev).or_insert_with(Vec::new).extend(drain);
229 rhs_children = rhs_children_clone;
230 } else {
231 go(diff, lhs_ele, rhs_ele)
232 }
233 }
208 } 234 }
209 last_lhs = lhs_child.or(last_lhs); 235 last_lhs = lhs_child.or(last_lhs);
210 } 236 }
@@ -292,7 +318,6 @@ fn _replace_children(
292#[derive(Debug, PartialEq, Eq, Hash)] 318#[derive(Debug, PartialEq, Eq, Hash)]
293enum InsertPos { 319enum InsertPos {
294 FirstChildOf(SyntaxNode), 320 FirstChildOf(SyntaxNode),
295 // Before(SyntaxElement),
296 After(SyntaxElement), 321 After(SyntaxElement),
297} 322}
298 323
@@ -603,18 +628,44 @@ mod tests {
603 } 628 }
604 629
605 #[test] 630 #[test]
606 fn insert() { 631 fn replace_parent() {
632 mark::check!(diff_replace_parent);
633 check_diff(
634 r#""#,
635 r#"use foo::bar;"#,
636 expect![[r#"
637 insertions:
638
639
640
641 replacements:
642
643 Line 0: Node([email protected]) -> use foo::bar;
644
645 deletions:
646
647
648 "#]],
649 );
650 }
651
652 #[test]
653 fn insert_last() {
607 mark::check!(diff_insert); 654 mark::check!(diff_insert);
608 check_diff( 655 check_diff(
609 r#"use foo;"#, 656 r#"
610 r#"use foo; 657use foo;
611use bar;"#, 658use bar;"#,
659 r#"
660use foo;
661use bar;
662use baz;"#,
612 expect![[r#" 663 expect![[r#"
613 insertions: 664 insertions:
614 665
615 Line 0: Node([email protected]) 666 Line 2: Node(USE@10..18)
616 -> "\n" 667 -> "\n"
617 -> use bar; 668 -> use baz;
618 669
619 replacements: 670 replacements:
620 671
@@ -628,29 +679,63 @@ use bar;"#,
628 } 679 }
629 680
630 #[test] 681 #[test]
631 fn replace_parent() { 682 fn insert_middle() {
632 mark::check!(diff_replace_parent);
633 check_diff( 683 check_diff(
634 r#""#, 684 r#"
635 r#"use foo::bar;"#, 685use foo;
686use baz;"#,
687 r#"
688use foo;
689use bar;
690use baz;"#,
636 expect![[r#" 691 expect![[r#"
637 insertions: 692 insertions:
638 693
694 Line 2: Token([email protected] "\n")
695 -> use bar;
696 -> "\n"
697
698 replacements:
699
700
701
702 deletions:
703
704
705 "#]],
706 )
707 }
708
709 #[test]
710 fn insert_first() {
711 check_diff(
712 r#"
713use bar;
714use baz;"#,
715 r#"
716use foo;
717use bar;
718use baz;"#,
719 expect![[r#"
720 insertions:
639 721
722 Line 0: Token([email protected] "\n")
723 -> use foo;
724 -> "\n"
640 725
641 replacements: 726 replacements:
642 727
643 Line 0: Node([email protected]) -> use foo::bar; 728
644 729
645 deletions: 730 deletions:
646 731
647 732
648 "#]], 733 "#]],
649 ); 734 )
650 } 735 }
651 736
652 #[test] 737 #[test]
653 fn delete() { 738 fn delete_last() {
654 mark::check!(diff_delete); 739 mark::check!(diff_delete);
655 check_diff( 740 check_diff(
656 r#"use foo; 741 r#"use foo;
@@ -674,52 +759,50 @@ use bar;"#,
674 } 759 }
675 760
676 #[test] 761 #[test]
677 fn insert_use() { 762 fn delete_middle() {
763 mark::check!(diff_insertions);
678 check_diff( 764 check_diff(
679 r#" 765 r#"
680use expect_test::{expect, Expect}; 766use expect_test::{expect, Expect};
767use text_edit::TextEdit;
681 768
682use crate::AstNode; 769use crate::AstNode;
683"#, 770"#,
684 r#" 771 r#"
685use expect_test::{expect, Expect}; 772use expect_test::{expect, Expect};
686use text_edit::TextEdit;
687 773
688use crate::AstNode; 774use crate::AstNode;
689"#, 775"#,
690 expect![[r#" 776 expect![[r#"
691 insertions: 777 insertions:
692 778
693 Line 4: Token([email protected] "\n") 779 Line 1: Node([email protected])
780 -> "\n\n"
694 -> use crate::AstNode; 781 -> use crate::AstNode;
695 -> "\n"
696 782
697 replacements: 783 replacements:
698 784
699 Line 2: Token([email protected] "\n\n") -> "\n"
700 Line 4: Token([email protected] "crate") -> text_edit
701 Line 4: Token([email protected] "AstNode") -> TextEdit
702 Line 4: Token([email protected] "\n") -> "\n\n"
703 785
704 deletions:
705 786
787 deletions:
706 788
789 Line 2: use text_edit::TextEdit;
790 Line 3: "\n\n"
791 Line 4: use crate::AstNode;
792 Line 5: "\n"
707 "#]], 793 "#]],
708 ) 794 )
709 } 795 }
710 796
711 #[test] 797 #[test]
712 fn remove_use() { 798 fn delete_first() {
713 check_diff( 799 check_diff(
714 r#" 800 r#"
715use expect_test::{expect, Expect};
716use text_edit::TextEdit; 801use text_edit::TextEdit;
717 802
718use crate::AstNode; 803use crate::AstNode;
719"#, 804"#,
720 r#" 805 r#"
721use expect_test::{expect, Expect};
722
723use crate::AstNode; 806use crate::AstNode;
724"#, 807"#,
725 expect![[r#" 808 expect![[r#"
@@ -729,15 +812,14 @@ use crate::AstNode;
729 812
730 replacements: 813 replacements:
731 814
732 Line 2: Token([email protected] "\n") -> "\n\n" 815 Line 2: Node([email protected]) -> crate
733 Line 3: Node([email protected]) -> crate 816 Line 2: Token([email protected] "TextEdit") -> AstNode
734 Line 3: Token([email protected] "TextEdit") -> AstNode 817 Line 2: Token([email protected] "\n\n") -> "\n"
735 Line 3: Token([email protected] "\n\n") -> "\n"
736 818
737 deletions: 819 deletions:
738 820
739 Line 4: use crate::AstNode; 821 Line 3: use crate::AstNode;
740 Line 5: "\n" 822 Line 4: "\n"
741 "#]], 823 "#]],
742 ) 824 )
743 } 825 }
@@ -814,17 +896,15 @@ fn main() {
814 _ => return, 896 _ => return,
815 } 897 }
816 -> ; 898 -> ;
817 Line 5: Token(R_CURLY@64..65 "}") 899 Line 3: Node(IF_EXPR@17..63)
818 -> "\n" 900 -> "\n "
819 -> } 901 -> foo(x);
820 902
821 replacements: 903 replacements:
822 904
823 Line 3: Token([email protected] "if") -> let 905 Line 3: Token([email protected] "if") -> let
824 Line 3: Token([email protected] "let") -> x 906 Line 3: Token([email protected] "let") -> x
825 Line 3: Node([email protected]) -> = 907 Line 3: Node([email protected]) -> =
826 Line 5: Token([email protected] "\n") -> "\n "
827 Line 5: Token([email protected] "}") -> foo(x);
828 908
829 deletions: 909 deletions:
830 910