diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/assists/src/handlers/qualify_path.rs | 182 | ||||
-rw-r--r-- | crates/ide/src/diagnostics.rs | 2 | ||||
-rw-r--r-- | crates/ide/src/inlay_hints.rs | 108 | ||||
-rw-r--r-- | crates/syntax/src/algo.rs | 160 |
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 | ||
98 | enum QualifyCandidate<'db> { | 100 | enum 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 | ||
105 | impl QualifyCandidate<'_> { | 107 | impl 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 | ||
1062 | pub mod generic { pub struct Thing<'a, T>(&'a T); } | ||
1063 | |||
1064 | //- /main.rs crate:main deps:dep | ||
1065 | fn foo() -> Thin<|>g<'static, ()> {} | ||
1066 | |||
1067 | fn main() {} | ||
1068 | ", | ||
1069 | r" | ||
1070 | fn foo() -> dep::generic::Thing<'static, ()> {} | ||
1071 | |||
1072 | fn 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 | ||
1083 | pub mod generic { pub struct Thing<'a, T>(&'a T); } | ||
1084 | |||
1085 | //- /main.rs crate:main deps:dep | ||
1086 | fn foo() -> Thin<|>g::<'static, ()> {} | ||
1087 | |||
1088 | fn main() {} | ||
1089 | ", | ||
1090 | r" | ||
1091 | fn foo() -> dep::generic::Thing::<'static, ()> {} | ||
1092 | |||
1093 | fn 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() { | |||
613 | pub struct Foo { pub a: i32, pub b: i32 } | 613 | pub struct Foo { pub a: i32, pub b: i32 } |
614 | "#, | 614 | "#, |
615 | r#" | 615 | r#" |
616 | fn some(, b: ()} {} | 616 | fn some(, b: ()) {} |
617 | fn items() {} | 617 | fn items() {} |
618 | fn here() {} | 618 | fn 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 @@ | |||
1 | use assists::utils::FamousDefs; | 1 | use assists::utils::FamousDefs; |
2 | use either::Either; | 2 | use either::Either; |
3 | use hir::{known, HirDisplay, Semantics}; | 3 | use hir::{known, Callable, HirDisplay, Semantics}; |
4 | use ide_db::RootDatabase; | 4 | use ide_db::RootDatabase; |
5 | use stdx::to_lower_snake_case; | 5 | use stdx::to_lower_snake_case; |
6 | use syntax::{ | 6 | use 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, ¶m_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 | ||
369 | fn 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 | |||
367 | fn is_enum_name_similar_to_param_name( | 389 | fn 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#" | ||
491 | fn max(x: i32, y: i32) -> i32 { x + y } | ||
492 | fn 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#" | ||
513 | fn param_with_underscore(with_underscore: i32) -> i32 { with_underscore } | ||
514 | fn 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#" | ||
532 | fn foo(foo: i32) -> i32 { foo } | ||
533 | fn 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#" | ||
551 | fn foo(bar: i32, baz: i32) -> i32 { bar + baz } | ||
552 | fn 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)] |
293 | enum InsertPos { | 319 | enum 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; | 657 | use foo; |
611 | use bar;"#, | 658 | use bar;"#, |
659 | r#" | ||
660 | use foo; | ||
661 | use bar; | ||
662 | use 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;"#, | 685 | use foo; |
686 | use baz;"#, | ||
687 | r#" | ||
688 | use foo; | ||
689 | use bar; | ||
690 | use 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#" | ||
713 | use bar; | ||
714 | use baz;"#, | ||
715 | r#" | ||
716 | use foo; | ||
717 | use bar; | ||
718 | use 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#" |
680 | use expect_test::{expect, Expect}; | 766 | use expect_test::{expect, Expect}; |
767 | use text_edit::TextEdit; | ||
681 | 768 | ||
682 | use crate::AstNode; | 769 | use crate::AstNode; |
683 | "#, | 770 | "#, |
684 | r#" | 771 | r#" |
685 | use expect_test::{expect, Expect}; | 772 | use expect_test::{expect, Expect}; |
686 | use text_edit::TextEdit; | ||
687 | 773 | ||
688 | use crate::AstNode; | 774 | use 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#" |
715 | use expect_test::{expect, Expect}; | ||
716 | use text_edit::TextEdit; | 801 | use text_edit::TextEdit; |
717 | 802 | ||
718 | use crate::AstNode; | 803 | use crate::AstNode; |
719 | "#, | 804 | "#, |
720 | r#" | 805 | r#" |
721 | use expect_test::{expect, Expect}; | ||
722 | |||
723 | use crate::AstNode; | 806 | use 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 | ||