diff options
Diffstat (limited to 'crates/assists')
-rw-r--r-- | crates/assists/src/handlers/qualify_path.rs | 182 | ||||
-rw-r--r-- | crates/assists/src/utils/import_assets.rs | 6 | ||||
-rw-r--r-- | crates/assists/src/utils/insert_use.rs | 4 |
3 files changed, 174 insertions, 18 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/assists/src/utils/import_assets.rs b/crates/assists/src/utils/import_assets.rs index 23db3a74b..f47edbb76 100644 --- a/crates/assists/src/utils/import_assets.rs +++ b/crates/assists/src/utils/import_assets.rs | |||
@@ -26,13 +26,13 @@ pub(crate) enum ImportCandidate { | |||
26 | 26 | ||
27 | #[derive(Debug)] | 27 | #[derive(Debug)] |
28 | pub(crate) struct TraitImportCandidate { | 28 | pub(crate) struct TraitImportCandidate { |
29 | pub ty: hir::Type, | 29 | pub(crate) ty: hir::Type, |
30 | pub name: ast::NameRef, | 30 | pub(crate) name: ast::NameRef, |
31 | } | 31 | } |
32 | 32 | ||
33 | #[derive(Debug)] | 33 | #[derive(Debug)] |
34 | pub(crate) struct PathImportCandidate { | 34 | pub(crate) struct PathImportCandidate { |
35 | pub name: ast::NameRef, | 35 | pub(crate) name: ast::NameRef, |
36 | } | 36 | } |
37 | 37 | ||
38 | #[derive(Debug)] | 38 | #[derive(Debug)] |
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs index 033fbcedc..a76bd5ebf 100644 --- a/crates/assists/src/utils/insert_use.rs +++ b/crates/assists/src/utils/insert_use.rs | |||
@@ -17,13 +17,13 @@ use syntax::{ | |||
17 | use test_utils::mark; | 17 | use test_utils::mark; |
18 | 18 | ||
19 | #[derive(Debug)] | 19 | #[derive(Debug)] |
20 | pub enum ImportScope { | 20 | pub(crate) enum ImportScope { |
21 | File(ast::SourceFile), | 21 | File(ast::SourceFile), |
22 | Module(ast::ItemList), | 22 | Module(ast::ItemList), |
23 | } | 23 | } |
24 | 24 | ||
25 | impl ImportScope { | 25 | impl ImportScope { |
26 | pub fn from(syntax: SyntaxNode) -> Option<Self> { | 26 | pub(crate) fn from(syntax: SyntaxNode) -> Option<Self> { |
27 | if let Some(module) = ast::Module::cast(syntax.clone()) { | 27 | if let Some(module) = ast::Module::cast(syntax.clone()) { |
28 | module.item_list().map(ImportScope::Module) | 28 | module.item_list().map(ImportScope::Module) |
29 | } else if let this @ Some(_) = ast::SourceFile::cast(syntax.clone()) { | 29 | } else if let this @ Some(_) = ast::SourceFile::cast(syntax.clone()) { |