diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/assists/src/handlers/add_custom_impl.rs | 161 | ||||
-rw-r--r-- | crates/assists/src/handlers/add_missing_impl_members.rs | 92 | ||||
-rw-r--r-- | crates/assists/src/handlers/convert_integer_literal.rs | 370 | ||||
-rw-r--r-- | crates/assists/src/utils.rs | 92 | ||||
-rw-r--r-- | crates/completion/src/completions/postfix.rs | 12 | ||||
-rw-r--r-- | crates/syntax/src/ast/make.rs | 4 |
6 files changed, 253 insertions, 478 deletions
diff --git a/crates/assists/src/handlers/add_custom_impl.rs b/crates/assists/src/handlers/add_custom_impl.rs index 669dd9b21..c13493fd8 100644 --- a/crates/assists/src/handlers/add_custom_impl.rs +++ b/crates/assists/src/handlers/add_custom_impl.rs | |||
@@ -4,13 +4,15 @@ use syntax::{ | |||
4 | ast::{self, make, AstNode}, | 4 | ast::{self, make, AstNode}, |
5 | Direction, SmolStr, | 5 | Direction, SmolStr, |
6 | SyntaxKind::{IDENT, WHITESPACE}, | 6 | SyntaxKind::{IDENT, WHITESPACE}, |
7 | TextRange, TextSize, | 7 | TextSize, |
8 | }; | 8 | }; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | assist_config::SnippetCap, | ||
12 | assist_context::{AssistBuilder, AssistContext, Assists}, | 11 | assist_context::{AssistBuilder, AssistContext, Assists}, |
13 | utils::mod_path_to_ast, | 12 | utils::{ |
13 | add_trait_assoc_items_to_impl, filter_assoc_items, mod_path_to_ast, render_snippet, Cursor, | ||
14 | DefaultMethods, | ||
15 | }, | ||
14 | AssistId, AssistKind, | 16 | AssistId, AssistKind, |
15 | }; | 17 | }; |
16 | 18 | ||
@@ -47,11 +49,10 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
47 | ctx.token_at_offset().find(|t| t.kind() == IDENT && *t.text() != attr_name)?; | 49 | ctx.token_at_offset().find(|t| t.kind() == IDENT && *t.text() != attr_name)?; |
48 | let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text()))); | 50 | let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text()))); |
49 | 51 | ||
50 | let annotated = attr.syntax().siblings(Direction::Next).find_map(ast::Name::cast)?; | 52 | let annotated_name = attr.syntax().siblings(Direction::Next).find_map(ast::Name::cast)?; |
51 | let annotated_name = annotated.syntax().text().to_string(); | 53 | let insert_pos = annotated_name.syntax().parent()?.text_range().end(); |
52 | let insert_pos = annotated.syntax().parent()?.text_range().end(); | ||
53 | 54 | ||
54 | let current_module = ctx.sema.scope(annotated.syntax()).module()?; | 55 | let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; |
55 | let current_crate = current_module.krate(); | 56 | let current_crate = current_module.krate(); |
56 | 57 | ||
57 | let found_traits = imports_locator::find_imports(&ctx.sema, current_crate, trait_token.text()) | 58 | let found_traits = imports_locator::find_imports(&ctx.sema, current_crate, trait_token.text()) |
@@ -69,21 +70,22 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
69 | }); | 70 | }); |
70 | 71 | ||
71 | let mut no_traits_found = true; | 72 | let mut no_traits_found = true; |
72 | for (trait_path, _trait) in found_traits.inspect(|_| no_traits_found = false) { | 73 | for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { |
73 | add_assist(acc, ctx.config.snippet_cap, &attr, &trait_path, &annotated_name, insert_pos)?; | 74 | add_assist(acc, ctx, &attr, &trait_path, Some(trait_), &annotated_name, insert_pos)?; |
74 | } | 75 | } |
75 | if no_traits_found { | 76 | if no_traits_found { |
76 | add_assist(acc, ctx.config.snippet_cap, &attr, &trait_path, &annotated_name, insert_pos)?; | 77 | add_assist(acc, ctx, &attr, &trait_path, None, &annotated_name, insert_pos)?; |
77 | } | 78 | } |
78 | Some(()) | 79 | Some(()) |
79 | } | 80 | } |
80 | 81 | ||
81 | fn add_assist( | 82 | fn add_assist( |
82 | acc: &mut Assists, | 83 | acc: &mut Assists, |
83 | snippet_cap: Option<SnippetCap>, | 84 | ctx: &AssistContext, |
84 | attr: &ast::Attr, | 85 | attr: &ast::Attr, |
85 | trait_path: &ast::Path, | 86 | trait_path: &ast::Path, |
86 | annotated_name: &str, | 87 | trait_: Option<hir::Trait>, |
88 | annotated_name: &ast::Name, | ||
87 | insert_pos: TextSize, | 89 | insert_pos: TextSize, |
88 | ) -> Option<()> { | 90 | ) -> Option<()> { |
89 | let target = attr.syntax().text_range(); | 91 | let target = attr.syntax().text_range(); |
@@ -92,25 +94,62 @@ fn add_assist( | |||
92 | let trait_name = trait_path.segment().and_then(|seg| seg.name_ref())?; | 94 | let trait_name = trait_path.segment().and_then(|seg| seg.name_ref())?; |
93 | 95 | ||
94 | acc.add(AssistId("add_custom_impl", AssistKind::Refactor), label, target, |builder| { | 96 | acc.add(AssistId("add_custom_impl", AssistKind::Refactor), label, target, |builder| { |
97 | let impl_def_with_items = | ||
98 | impl_def_from_trait(&ctx.sema, annotated_name, trait_, trait_path); | ||
95 | update_attribute(builder, &input, &trait_name, &attr); | 99 | update_attribute(builder, &input, &trait_name, &attr); |
96 | match snippet_cap { | 100 | match (ctx.config.snippet_cap, impl_def_with_items) { |
97 | Some(cap) => { | 101 | (None, _) => builder.insert( |
102 | insert_pos, | ||
103 | format!("\n\nimpl {} for {} {{\n\n}}", trait_path, annotated_name), | ||
104 | ), | ||
105 | (Some(cap), None) => builder.insert_snippet( | ||
106 | cap, | ||
107 | insert_pos, | ||
108 | format!("\n\nimpl {} for {} {{\n $0\n}}", trait_path, annotated_name), | ||
109 | ), | ||
110 | (Some(cap), Some((impl_def, first_assoc_item))) => { | ||
111 | let mut cursor = Cursor::Before(first_assoc_item.syntax()); | ||
112 | let placeholder; | ||
113 | if let ast::AssocItem::Fn(ref func) = first_assoc_item { | ||
114 | if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) { | ||
115 | if m.syntax().text() == "todo!()" { | ||
116 | placeholder = m; | ||
117 | cursor = Cursor::Replace(placeholder.syntax()); | ||
118 | } | ||
119 | } | ||
120 | } | ||
121 | |||
98 | builder.insert_snippet( | 122 | builder.insert_snippet( |
99 | cap, | 123 | cap, |
100 | insert_pos, | 124 | insert_pos, |
101 | format!("\n\nimpl {} for {} {{\n $0\n}}", trait_path, annotated_name), | 125 | format!("\n\n{}", render_snippet(cap, impl_def.syntax(), cursor)), |
102 | ); | 126 | ) |
103 | } | ||
104 | None => { | ||
105 | builder.insert( | ||
106 | insert_pos, | ||
107 | format!("\n\nimpl {} for {} {{\n\n}}", trait_path, annotated_name), | ||
108 | ); | ||
109 | } | 127 | } |
110 | } | 128 | }; |
111 | }) | 129 | }) |
112 | } | 130 | } |
113 | 131 | ||
132 | fn impl_def_from_trait( | ||
133 | sema: &hir::Semantics<ide_db::RootDatabase>, | ||
134 | annotated_name: &ast::Name, | ||
135 | trait_: Option<hir::Trait>, | ||
136 | trait_path: &ast::Path, | ||
137 | ) -> Option<(ast::Impl, ast::AssocItem)> { | ||
138 | let trait_ = trait_?; | ||
139 | let target_scope = sema.scope(annotated_name.syntax()); | ||
140 | let trait_items = filter_assoc_items(sema.db, &trait_.items(sema.db), DefaultMethods::No); | ||
141 | if trait_items.is_empty() { | ||
142 | return None; | ||
143 | } | ||
144 | let impl_def = make::impl_trait( | ||
145 | trait_path.clone(), | ||
146 | make::path_unqualified(make::path_segment(make::name_ref(annotated_name.text()))), | ||
147 | ); | ||
148 | let (impl_def, first_assoc_item) = | ||
149 | add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope); | ||
150 | Some((impl_def, first_assoc_item)) | ||
151 | } | ||
152 | |||
114 | fn update_attribute( | 153 | fn update_attribute( |
115 | builder: &mut AssistBuilder, | 154 | builder: &mut AssistBuilder, |
116 | input: &ast::TokenTree, | 155 | input: &ast::TokenTree, |
@@ -133,13 +172,14 @@ fn update_attribute( | |||
133 | let attr_range = attr.syntax().text_range(); | 172 | let attr_range = attr.syntax().text_range(); |
134 | builder.delete(attr_range); | 173 | builder.delete(attr_range); |
135 | 174 | ||
136 | let line_break_range = attr | 175 | if let Some(line_break_range) = attr |
137 | .syntax() | 176 | .syntax() |
138 | .next_sibling_or_token() | 177 | .next_sibling_or_token() |
139 | .filter(|t| t.kind() == WHITESPACE) | 178 | .filter(|t| t.kind() == WHITESPACE) |
140 | .map(|t| t.text_range()) | 179 | .map(|t| t.text_range()) |
141 | .unwrap_or_else(|| TextRange::new(TextSize::from(0), TextSize::from(0))); | 180 | { |
142 | builder.delete(line_break_range); | 181 | builder.delete(line_break_range); |
182 | } | ||
143 | } | 183 | } |
144 | } | 184 | } |
145 | 185 | ||
@@ -150,12 +190,17 @@ mod tests { | |||
150 | use super::*; | 190 | use super::*; |
151 | 191 | ||
152 | #[test] | 192 | #[test] |
153 | fn add_custom_impl_qualified() { | 193 | fn add_custom_impl_debug() { |
154 | check_assist( | 194 | check_assist( |
155 | add_custom_impl, | 195 | add_custom_impl, |
156 | " | 196 | " |
157 | mod fmt { | 197 | mod fmt { |
158 | pub trait Debug {} | 198 | pub struct Error; |
199 | pub type Result = Result<(), Error>; | ||
200 | pub struct Formatter<'a>; | ||
201 | pub trait Debug { | ||
202 | fn fmt(&self, f: &mut Formatter<'_>) -> Result; | ||
203 | } | ||
159 | } | 204 | } |
160 | 205 | ||
161 | #[derive(Debu<|>g)] | 206 | #[derive(Debu<|>g)] |
@@ -165,7 +210,12 @@ struct Foo { | |||
165 | ", | 210 | ", |
166 | " | 211 | " |
167 | mod fmt { | 212 | mod fmt { |
168 | pub trait Debug {} | 213 | pub struct Error; |
214 | pub type Result = Result<(), Error>; | ||
215 | pub struct Formatter<'a>; | ||
216 | pub trait Debug { | ||
217 | fn fmt(&self, f: &mut Formatter<'_>) -> Result; | ||
218 | } | ||
169 | } | 219 | } |
170 | 220 | ||
171 | struct Foo { | 221 | struct Foo { |
@@ -173,7 +223,58 @@ struct Foo { | |||
173 | } | 223 | } |
174 | 224 | ||
175 | impl fmt::Debug for Foo { | 225 | impl fmt::Debug for Foo { |
176 | $0 | 226 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
227 | ${0:todo!()} | ||
228 | } | ||
229 | } | ||
230 | ", | ||
231 | ) | ||
232 | } | ||
233 | #[test] | ||
234 | fn add_custom_impl_all() { | ||
235 | check_assist( | ||
236 | add_custom_impl, | ||
237 | " | ||
238 | mod foo { | ||
239 | pub trait Bar { | ||
240 | type Qux; | ||
241 | const Baz: usize = 42; | ||
242 | const Fez: usize; | ||
243 | fn foo(); | ||
244 | fn bar() {} | ||
245 | } | ||
246 | } | ||
247 | |||
248 | #[derive(<|>Bar)] | ||
249 | struct Foo { | ||
250 | bar: String, | ||
251 | } | ||
252 | ", | ||
253 | " | ||
254 | mod foo { | ||
255 | pub trait Bar { | ||
256 | type Qux; | ||
257 | const Baz: usize = 42; | ||
258 | const Fez: usize; | ||
259 | fn foo(); | ||
260 | fn bar() {} | ||
261 | } | ||
262 | } | ||
263 | |||
264 | struct Foo { | ||
265 | bar: String, | ||
266 | } | ||
267 | |||
268 | impl foo::Bar for Foo { | ||
269 | $0type Qux; | ||
270 | |||
271 | const Baz: usize = 42; | ||
272 | |||
273 | const Fez: usize; | ||
274 | |||
275 | fn foo() { | ||
276 | todo!() | ||
277 | } | ||
177 | } | 278 | } |
178 | ", | 279 | ", |
179 | ) | 280 | ) |
diff --git a/crates/assists/src/handlers/add_missing_impl_members.rs b/crates/assists/src/handlers/add_missing_impl_members.rs index b82fb30ad..bbb71e261 100644 --- a/crates/assists/src/handlers/add_missing_impl_members.rs +++ b/crates/assists/src/handlers/add_missing_impl_members.rs | |||
@@ -1,27 +1,14 @@ | |||
1 | use hir::HasSource; | 1 | use ide_db::traits::resolve_target_trait; |
2 | use ide_db::traits::{get_missing_assoc_items, resolve_target_trait}; | 2 | use syntax::ast::{self, AstNode}; |
3 | use syntax::{ | ||
4 | ast::{ | ||
5 | self, | ||
6 | edit::{self, AstNodeEdit, IndentLevel}, | ||
7 | make, AstNode, NameOwner, | ||
8 | }, | ||
9 | SmolStr, | ||
10 | }; | ||
11 | 3 | ||
12 | use crate::{ | 4 | use crate::{ |
13 | assist_context::{AssistContext, Assists}, | 5 | assist_context::{AssistContext, Assists}, |
14 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, | 6 | utils::add_trait_assoc_items_to_impl, |
15 | utils::{render_snippet, Cursor}, | 7 | utils::DefaultMethods, |
8 | utils::{filter_assoc_items, render_snippet, Cursor}, | ||
16 | AssistId, AssistKind, | 9 | AssistId, AssistKind, |
17 | }; | 10 | }; |
18 | 11 | ||
19 | #[derive(PartialEq)] | ||
20 | enum AddMissingImplMembersMode { | ||
21 | DefaultMethodsOnly, | ||
22 | NoDefaultMethods, | ||
23 | } | ||
24 | |||
25 | // Assist: add_impl_missing_members | 12 | // Assist: add_impl_missing_members |
26 | // | 13 | // |
27 | // Adds scaffold for required impl members. | 14 | // Adds scaffold for required impl members. |
@@ -55,7 +42,7 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) - | |||
55 | add_missing_impl_members_inner( | 42 | add_missing_impl_members_inner( |
56 | acc, | 43 | acc, |
57 | ctx, | 44 | ctx, |
58 | AddMissingImplMembersMode::NoDefaultMethods, | 45 | DefaultMethods::No, |
59 | "add_impl_missing_members", | 46 | "add_impl_missing_members", |
60 | "Implement missing members", | 47 | "Implement missing members", |
61 | ) | 48 | ) |
@@ -97,7 +84,7 @@ pub(crate) fn add_missing_default_members(acc: &mut Assists, ctx: &AssistContext | |||
97 | add_missing_impl_members_inner( | 84 | add_missing_impl_members_inner( |
98 | acc, | 85 | acc, |
99 | ctx, | 86 | ctx, |
100 | AddMissingImplMembersMode::DefaultMethodsOnly, | 87 | DefaultMethods::Only, |
101 | "add_impl_default_members", | 88 | "add_impl_default_members", |
102 | "Implement default members", | 89 | "Implement default members", |
103 | ) | 90 | ) |
@@ -106,7 +93,7 @@ pub(crate) fn add_missing_default_members(acc: &mut Assists, ctx: &AssistContext | |||
106 | fn add_missing_impl_members_inner( | 93 | fn add_missing_impl_members_inner( |
107 | acc: &mut Assists, | 94 | acc: &mut Assists, |
108 | ctx: &AssistContext, | 95 | ctx: &AssistContext, |
109 | mode: AddMissingImplMembersMode, | 96 | mode: DefaultMethods, |
110 | assist_id: &'static str, | 97 | assist_id: &'static str, |
111 | label: &'static str, | 98 | label: &'static str, |
112 | ) -> Option<()> { | 99 | ) -> Option<()> { |
@@ -114,32 +101,11 @@ fn add_missing_impl_members_inner( | |||
114 | let impl_def = ctx.find_node_at_offset::<ast::Impl>()?; | 101 | let impl_def = ctx.find_node_at_offset::<ast::Impl>()?; |
115 | let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?; | 102 | let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?; |
116 | 103 | ||
117 | let def_name = |item: &ast::AssocItem| -> Option<SmolStr> { | 104 | let missing_items = filter_assoc_items( |
118 | match item { | 105 | ctx.db(), |
119 | ast::AssocItem::Fn(def) => def.name(), | 106 | &ide_db::traits::get_missing_assoc_items(&ctx.sema, &impl_def), |
120 | ast::AssocItem::TypeAlias(def) => def.name(), | 107 | mode, |
121 | ast::AssocItem::Const(def) => def.name(), | 108 | ); |
122 | ast::AssocItem::MacroCall(_) => None, | ||
123 | } | ||
124 | .map(|it| it.text().clone()) | ||
125 | }; | ||
126 | |||
127 | let missing_items = get_missing_assoc_items(&ctx.sema, &impl_def) | ||
128 | .iter() | ||
129 | .map(|i| match i { | ||
130 | hir::AssocItem::Function(i) => ast::AssocItem::Fn(i.source(ctx.db()).value), | ||
131 | hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(i.source(ctx.db()).value), | ||
132 | hir::AssocItem::Const(i) => ast::AssocItem::Const(i.source(ctx.db()).value), | ||
133 | }) | ||
134 | .filter(|t| def_name(&t).is_some()) | ||
135 | .filter(|t| match t { | ||
136 | ast::AssocItem::Fn(def) => match mode { | ||
137 | AddMissingImplMembersMode::DefaultMethodsOnly => def.body().is_some(), | ||
138 | AddMissingImplMembersMode::NoDefaultMethods => def.body().is_none(), | ||
139 | }, | ||
140 | _ => mode == AddMissingImplMembersMode::NoDefaultMethods, | ||
141 | }) | ||
142 | .collect::<Vec<_>>(); | ||
143 | 109 | ||
144 | if missing_items.is_empty() { | 110 | if missing_items.is_empty() { |
145 | return None; | 111 | return None; |
@@ -147,29 +113,9 @@ fn add_missing_impl_members_inner( | |||
147 | 113 | ||
148 | let target = impl_def.syntax().text_range(); | 114 | let target = impl_def.syntax().text_range(); |
149 | acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| { | 115 | acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| { |
150 | let impl_item_list = impl_def.assoc_item_list().unwrap_or_else(make::assoc_item_list); | ||
151 | |||
152 | let n_existing_items = impl_item_list.assoc_items().count(); | ||
153 | let source_scope = ctx.sema.scope_for_def(trait_); | ||
154 | let target_scope = ctx.sema.scope(impl_def.syntax()); | 116 | let target_scope = ctx.sema.scope(impl_def.syntax()); |
155 | let ast_transform = QualifyPaths::new(&target_scope, &source_scope) | 117 | let (new_impl_def, first_new_item) = |
156 | .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def.clone())); | 118 | add_trait_assoc_items_to_impl(&ctx.sema, missing_items, trait_, impl_def, target_scope); |
157 | |||
158 | let items = missing_items | ||
159 | .into_iter() | ||
160 | .map(|it| ast_transform::apply(&*ast_transform, it)) | ||
161 | .map(|it| match it { | ||
162 | ast::AssocItem::Fn(def) => ast::AssocItem::Fn(add_body(def)), | ||
163 | ast::AssocItem::TypeAlias(def) => ast::AssocItem::TypeAlias(def.remove_bounds()), | ||
164 | _ => it, | ||
165 | }) | ||
166 | .map(|it| edit::remove_attrs_and_docs(&it)); | ||
167 | |||
168 | let new_impl_item_list = impl_item_list.append_items(items); | ||
169 | let new_impl_def = impl_def.with_assoc_item_list(new_impl_item_list); | ||
170 | let first_new_item = | ||
171 | new_impl_def.assoc_item_list().unwrap().assoc_items().nth(n_existing_items).unwrap(); | ||
172 | |||
173 | match ctx.config.snippet_cap { | 119 | match ctx.config.snippet_cap { |
174 | None => builder.replace(target, new_impl_def.to_string()), | 120 | None => builder.replace(target, new_impl_def.to_string()), |
175 | Some(cap) => { | 121 | Some(cap) => { |
@@ -193,14 +139,6 @@ fn add_missing_impl_members_inner( | |||
193 | }) | 139 | }) |
194 | } | 140 | } |
195 | 141 | ||
196 | fn add_body(fn_def: ast::Fn) -> ast::Fn { | ||
197 | if fn_def.body().is_some() { | ||
198 | return fn_def; | ||
199 | } | ||
200 | let body = make::block_expr(None, Some(make::expr_todo())).indent(IndentLevel(1)); | ||
201 | fn_def.with_body(body) | ||
202 | } | ||
203 | |||
204 | #[cfg(test)] | 142 | #[cfg(test)] |
205 | mod tests { | 143 | mod tests { |
206 | use crate::tests::{check_assist, check_assist_not_applicable}; | 144 | use crate::tests::{check_assist, check_assist_not_applicable}; |
diff --git a/crates/assists/src/handlers/convert_integer_literal.rs b/crates/assists/src/handlers/convert_integer_literal.rs index 1094ed3f3..667115382 100644 --- a/crates/assists/src/handlers/convert_integer_literal.rs +++ b/crates/assists/src/handlers/convert_integer_literal.rs | |||
@@ -129,34 +129,6 @@ mod tests { | |||
129 | ); | 129 | ); |
130 | } | 130 | } |
131 | 131 | ||
132 | // Decimal numbers under 3 digits have a special case where they return early because we can't fit a | ||
133 | // other base's prefix, so we have a separate test for that. | ||
134 | #[test] | ||
135 | fn convert_small_decimal_integer() { | ||
136 | let before = "const _: i32 = 10<|>;"; | ||
137 | |||
138 | check_assist_by_label( | ||
139 | convert_integer_literal, | ||
140 | before, | ||
141 | "const _: i32 = 0b1010;", | ||
142 | "Convert 10 to 0b1010", | ||
143 | ); | ||
144 | |||
145 | check_assist_by_label( | ||
146 | convert_integer_literal, | ||
147 | before, | ||
148 | "const _: i32 = 0o12;", | ||
149 | "Convert 10 to 0o12", | ||
150 | ); | ||
151 | |||
152 | check_assist_by_label( | ||
153 | convert_integer_literal, | ||
154 | before, | ||
155 | "const _: i32 = 0xA;", | ||
156 | "Convert 10 to 0xA", | ||
157 | ); | ||
158 | } | ||
159 | |||
160 | #[test] | 132 | #[test] |
161 | fn convert_hexadecimal_integer() { | 133 | fn convert_hexadecimal_integer() { |
162 | let before = "const _: i32 = 0xFF<|>;"; | 134 | let before = "const _: i32 = 0xFF<|>;"; |
@@ -236,7 +208,7 @@ mod tests { | |||
236 | } | 208 | } |
237 | 209 | ||
238 | #[test] | 210 | #[test] |
239 | fn convert_decimal_integer_with_underscores() { | 211 | fn convert_integer_with_underscores() { |
240 | let before = "const _: i32 = 1_00_0<|>;"; | 212 | let before = "const _: i32 = 1_00_0<|>;"; |
241 | 213 | ||
242 | check_assist_by_label( | 214 | check_assist_by_label( |
@@ -262,111 +234,7 @@ mod tests { | |||
262 | } | 234 | } |
263 | 235 | ||
264 | #[test] | 236 | #[test] |
265 | fn convert_small_decimal_integer_with_underscores() { | 237 | fn convert_integer_with_suffix() { |
266 | let before = "const _: i32 = 1_0<|>;"; | ||
267 | |||
268 | check_assist_by_label( | ||
269 | convert_integer_literal, | ||
270 | before, | ||
271 | "const _: i32 = 0b1010;", | ||
272 | "Convert 1_0 to 0b1010", | ||
273 | ); | ||
274 | |||
275 | check_assist_by_label( | ||
276 | convert_integer_literal, | ||
277 | before, | ||
278 | "const _: i32 = 0o12;", | ||
279 | "Convert 1_0 to 0o12", | ||
280 | ); | ||
281 | |||
282 | check_assist_by_label( | ||
283 | convert_integer_literal, | ||
284 | before, | ||
285 | "const _: i32 = 0xA;", | ||
286 | "Convert 1_0 to 0xA", | ||
287 | ); | ||
288 | } | ||
289 | |||
290 | #[test] | ||
291 | fn convert_hexadecimal_integer_with_underscores() { | ||
292 | let before = "const _: i32 = 0x_F_F<|>;"; | ||
293 | |||
294 | check_assist_by_label( | ||
295 | convert_integer_literal, | ||
296 | before, | ||
297 | "const _: i32 = 0b11111111;", | ||
298 | "Convert 0x_F_F to 0b11111111", | ||
299 | ); | ||
300 | |||
301 | check_assist_by_label( | ||
302 | convert_integer_literal, | ||
303 | before, | ||
304 | "const _: i32 = 0o377;", | ||
305 | "Convert 0x_F_F to 0o377", | ||
306 | ); | ||
307 | |||
308 | check_assist_by_label( | ||
309 | convert_integer_literal, | ||
310 | before, | ||
311 | "const _: i32 = 255;", | ||
312 | "Convert 0x_F_F to 255", | ||
313 | ); | ||
314 | } | ||
315 | |||
316 | #[test] | ||
317 | fn convert_binary_integer_with_underscores() { | ||
318 | let before = "const _: i32 = 0b1111_1111<|>;"; | ||
319 | |||
320 | check_assist_by_label( | ||
321 | convert_integer_literal, | ||
322 | before, | ||
323 | "const _: i32 = 0o377;", | ||
324 | "Convert 0b1111_1111 to 0o377", | ||
325 | ); | ||
326 | |||
327 | check_assist_by_label( | ||
328 | convert_integer_literal, | ||
329 | before, | ||
330 | "const _: i32 = 255;", | ||
331 | "Convert 0b1111_1111 to 255", | ||
332 | ); | ||
333 | |||
334 | check_assist_by_label( | ||
335 | convert_integer_literal, | ||
336 | before, | ||
337 | "const _: i32 = 0xFF;", | ||
338 | "Convert 0b1111_1111 to 0xFF", | ||
339 | ); | ||
340 | } | ||
341 | |||
342 | #[test] | ||
343 | fn convert_octal_integer_with_underscores() { | ||
344 | let before = "const _: i32 = 0o3_77<|>;"; | ||
345 | |||
346 | check_assist_by_label( | ||
347 | convert_integer_literal, | ||
348 | before, | ||
349 | "const _: i32 = 0b11111111;", | ||
350 | "Convert 0o3_77 to 0b11111111", | ||
351 | ); | ||
352 | |||
353 | check_assist_by_label( | ||
354 | convert_integer_literal, | ||
355 | before, | ||
356 | "const _: i32 = 255;", | ||
357 | "Convert 0o3_77 to 255", | ||
358 | ); | ||
359 | |||
360 | check_assist_by_label( | ||
361 | convert_integer_literal, | ||
362 | before, | ||
363 | "const _: i32 = 0xFF;", | ||
364 | "Convert 0o3_77 to 0xFF", | ||
365 | ); | ||
366 | } | ||
367 | |||
368 | #[test] | ||
369 | fn convert_decimal_integer_with_suffix() { | ||
370 | let before = "const _: i32 = 1000i32<|>;"; | 238 | let before = "const _: i32 = 1000i32<|>;"; |
371 | 239 | ||
372 | check_assist_by_label( | 240 | check_assist_by_label( |
@@ -392,240 +260,6 @@ mod tests { | |||
392 | } | 260 | } |
393 | 261 | ||
394 | #[test] | 262 | #[test] |
395 | fn convert_small_decimal_integer_with_suffix() { | ||
396 | let before = "const _: i32 = 10i32<|>;"; | ||
397 | |||
398 | check_assist_by_label( | ||
399 | convert_integer_literal, | ||
400 | before, | ||
401 | "const _: i32 = 0b1010i32;", | ||
402 | "Convert 10i32 to 0b1010i32", | ||
403 | ); | ||
404 | |||
405 | check_assist_by_label( | ||
406 | convert_integer_literal, | ||
407 | before, | ||
408 | "const _: i32 = 0o12i32;", | ||
409 | "Convert 10i32 to 0o12i32", | ||
410 | ); | ||
411 | |||
412 | check_assist_by_label( | ||
413 | convert_integer_literal, | ||
414 | before, | ||
415 | "const _: i32 = 0xAi32;", | ||
416 | "Convert 10i32 to 0xAi32", | ||
417 | ); | ||
418 | } | ||
419 | |||
420 | #[test] | ||
421 | fn convert_hexadecimal_integer_with_suffix() { | ||
422 | let before = "const _: i32 = 0xFFi32<|>;"; | ||
423 | |||
424 | check_assist_by_label( | ||
425 | convert_integer_literal, | ||
426 | before, | ||
427 | "const _: i32 = 0b11111111i32;", | ||
428 | "Convert 0xFFi32 to 0b11111111i32", | ||
429 | ); | ||
430 | |||
431 | check_assist_by_label( | ||
432 | convert_integer_literal, | ||
433 | before, | ||
434 | "const _: i32 = 0o377i32;", | ||
435 | "Convert 0xFFi32 to 0o377i32", | ||
436 | ); | ||
437 | |||
438 | check_assist_by_label( | ||
439 | convert_integer_literal, | ||
440 | before, | ||
441 | "const _: i32 = 255i32;", | ||
442 | "Convert 0xFFi32 to 255i32", | ||
443 | ); | ||
444 | } | ||
445 | |||
446 | #[test] | ||
447 | fn convert_binary_integer_with_suffix() { | ||
448 | let before = "const _: i32 = 0b11111111i32<|>;"; | ||
449 | |||
450 | check_assist_by_label( | ||
451 | convert_integer_literal, | ||
452 | before, | ||
453 | "const _: i32 = 0o377i32;", | ||
454 | "Convert 0b11111111i32 to 0o377i32", | ||
455 | ); | ||
456 | |||
457 | check_assist_by_label( | ||
458 | convert_integer_literal, | ||
459 | before, | ||
460 | "const _: i32 = 255i32;", | ||
461 | "Convert 0b11111111i32 to 255i32", | ||
462 | ); | ||
463 | |||
464 | check_assist_by_label( | ||
465 | convert_integer_literal, | ||
466 | before, | ||
467 | "const _: i32 = 0xFFi32;", | ||
468 | "Convert 0b11111111i32 to 0xFFi32", | ||
469 | ); | ||
470 | } | ||
471 | |||
472 | #[test] | ||
473 | fn convert_octal_integer_with_suffix() { | ||
474 | let before = "const _: i32 = 0o377i32<|>;"; | ||
475 | |||
476 | check_assist_by_label( | ||
477 | convert_integer_literal, | ||
478 | before, | ||
479 | "const _: i32 = 0b11111111i32;", | ||
480 | "Convert 0o377i32 to 0b11111111i32", | ||
481 | ); | ||
482 | |||
483 | check_assist_by_label( | ||
484 | convert_integer_literal, | ||
485 | before, | ||
486 | "const _: i32 = 255i32;", | ||
487 | "Convert 0o377i32 to 255i32", | ||
488 | ); | ||
489 | |||
490 | check_assist_by_label( | ||
491 | convert_integer_literal, | ||
492 | before, | ||
493 | "const _: i32 = 0xFFi32;", | ||
494 | "Convert 0o377i32 to 0xFFi32", | ||
495 | ); | ||
496 | } | ||
497 | |||
498 | #[test] | ||
499 | fn convert_decimal_integer_with_underscores_and_suffix() { | ||
500 | let before = "const _: i32 = 1_00_0i32<|>;"; | ||
501 | |||
502 | check_assist_by_label( | ||
503 | convert_integer_literal, | ||
504 | before, | ||
505 | "const _: i32 = 0b1111101000i32;", | ||
506 | "Convert 1_00_0i32 to 0b1111101000i32", | ||
507 | ); | ||
508 | |||
509 | check_assist_by_label( | ||
510 | convert_integer_literal, | ||
511 | before, | ||
512 | "const _: i32 = 0o1750i32;", | ||
513 | "Convert 1_00_0i32 to 0o1750i32", | ||
514 | ); | ||
515 | |||
516 | check_assist_by_label( | ||
517 | convert_integer_literal, | ||
518 | before, | ||
519 | "const _: i32 = 0x3E8i32;", | ||
520 | "Convert 1_00_0i32 to 0x3E8i32", | ||
521 | ); | ||
522 | } | ||
523 | |||
524 | #[test] | ||
525 | fn convert_small_decimal_integer_with_underscores_and_suffix() { | ||
526 | let before = "const _: i32 = 1_0i32<|>;"; | ||
527 | |||
528 | check_assist_by_label( | ||
529 | convert_integer_literal, | ||
530 | before, | ||
531 | "const _: i32 = 0b1010i32;", | ||
532 | "Convert 1_0i32 to 0b1010i32", | ||
533 | ); | ||
534 | |||
535 | check_assist_by_label( | ||
536 | convert_integer_literal, | ||
537 | before, | ||
538 | "const _: i32 = 0o12i32;", | ||
539 | "Convert 1_0i32 to 0o12i32", | ||
540 | ); | ||
541 | |||
542 | check_assist_by_label( | ||
543 | convert_integer_literal, | ||
544 | before, | ||
545 | "const _: i32 = 0xAi32;", | ||
546 | "Convert 1_0i32 to 0xAi32", | ||
547 | ); | ||
548 | } | ||
549 | |||
550 | #[test] | ||
551 | fn convert_hexadecimal_integer_with_underscores_and_suffix() { | ||
552 | let before = "const _: i32 = 0x_F_Fi32<|>;"; | ||
553 | |||
554 | check_assist_by_label( | ||
555 | convert_integer_literal, | ||
556 | before, | ||
557 | "const _: i32 = 0b11111111i32;", | ||
558 | "Convert 0x_F_Fi32 to 0b11111111i32", | ||
559 | ); | ||
560 | |||
561 | check_assist_by_label( | ||
562 | convert_integer_literal, | ||
563 | before, | ||
564 | "const _: i32 = 0o377i32;", | ||
565 | "Convert 0x_F_Fi32 to 0o377i32", | ||
566 | ); | ||
567 | |||
568 | check_assist_by_label( | ||
569 | convert_integer_literal, | ||
570 | before, | ||
571 | "const _: i32 = 255i32;", | ||
572 | "Convert 0x_F_Fi32 to 255i32", | ||
573 | ); | ||
574 | } | ||
575 | |||
576 | #[test] | ||
577 | fn convert_binary_integer_with_underscores_and_suffix() { | ||
578 | let before = "const _: i32 = 0b1111_1111i32<|>;"; | ||
579 | |||
580 | check_assist_by_label( | ||
581 | convert_integer_literal, | ||
582 | before, | ||
583 | "const _: i32 = 0o377i32;", | ||
584 | "Convert 0b1111_1111i32 to 0o377i32", | ||
585 | ); | ||
586 | |||
587 | check_assist_by_label( | ||
588 | convert_integer_literal, | ||
589 | before, | ||
590 | "const _: i32 = 255i32;", | ||
591 | "Convert 0b1111_1111i32 to 255i32", | ||
592 | ); | ||
593 | |||
594 | check_assist_by_label( | ||
595 | convert_integer_literal, | ||
596 | before, | ||
597 | "const _: i32 = 0xFFi32;", | ||
598 | "Convert 0b1111_1111i32 to 0xFFi32", | ||
599 | ); | ||
600 | } | ||
601 | |||
602 | #[test] | ||
603 | fn convert_octal_integer_with_underscores_and_suffix() { | ||
604 | let before = "const _: i32 = 0o3_77i32<|>;"; | ||
605 | |||
606 | check_assist_by_label( | ||
607 | convert_integer_literal, | ||
608 | before, | ||
609 | "const _: i32 = 0b11111111i32;", | ||
610 | "Convert 0o3_77i32 to 0b11111111i32", | ||
611 | ); | ||
612 | |||
613 | check_assist_by_label( | ||
614 | convert_integer_literal, | ||
615 | before, | ||
616 | "const _: i32 = 255i32;", | ||
617 | "Convert 0o3_77i32 to 255i32", | ||
618 | ); | ||
619 | |||
620 | check_assist_by_label( | ||
621 | convert_integer_literal, | ||
622 | before, | ||
623 | "const _: i32 = 0xFFi32;", | ||
624 | "Convert 0o3_77i32 to 0xFFi32", | ||
625 | ); | ||
626 | } | ||
627 | |||
628 | #[test] | ||
629 | fn convert_overflowing_literal() { | 263 | fn convert_overflowing_literal() { |
630 | let before = "const _: i32 = | 264 | let before = "const _: i32 = |
631 | 111111111111111111111111111111111111111111111111111111111111111111111111<|>;"; | 265 | 111111111111111111111111111111111111111111111111111111111111111111111111<|>;"; |
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs index 56f925ee6..7071fe96b 100644 --- a/crates/assists/src/utils.rs +++ b/crates/assists/src/utils.rs | |||
@@ -4,17 +4,22 @@ pub(crate) mod import_assets; | |||
4 | 4 | ||
5 | use std::ops; | 5 | use std::ops; |
6 | 6 | ||
7 | use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait}; | 7 | use hir::{Crate, Enum, HasSource, Module, ScopeDef, Semantics, Trait}; |
8 | use ide_db::RootDatabase; | 8 | use ide_db::RootDatabase; |
9 | use itertools::Itertools; | 9 | use itertools::Itertools; |
10 | use syntax::{ | 10 | use syntax::{ |
11 | ast::{self, make, ArgListOwner}, | 11 | ast::edit::AstNodeEdit, |
12 | ast::NameOwner, | ||
13 | ast::{self, edit, make, ArgListOwner}, | ||
12 | AstNode, Direction, | 14 | AstNode, Direction, |
13 | SyntaxKind::*, | 15 | SyntaxKind::*, |
14 | SyntaxNode, TextSize, T, | 16 | SyntaxNode, TextSize, T, |
15 | }; | 17 | }; |
16 | 18 | ||
17 | use crate::assist_config::SnippetCap; | 19 | use crate::{ |
20 | assist_config::SnippetCap, | ||
21 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, | ||
22 | }; | ||
18 | 23 | ||
19 | pub use insert_use::MergeBehaviour; | 24 | pub use insert_use::MergeBehaviour; |
20 | pub(crate) use insert_use::{insert_use, ImportScope}; | 25 | pub(crate) use insert_use::{insert_use, ImportScope}; |
@@ -77,6 +82,87 @@ pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> { | |||
77 | None | 82 | None |
78 | } | 83 | } |
79 | 84 | ||
85 | #[derive(Copy, Clone, PartialEq)] | ||
86 | pub enum DefaultMethods { | ||
87 | Only, | ||
88 | No, | ||
89 | } | ||
90 | |||
91 | pub fn filter_assoc_items( | ||
92 | db: &RootDatabase, | ||
93 | items: &[hir::AssocItem], | ||
94 | default_methods: DefaultMethods, | ||
95 | ) -> Vec<ast::AssocItem> { | ||
96 | fn has_def_name(item: &ast::AssocItem) -> bool { | ||
97 | match item { | ||
98 | ast::AssocItem::Fn(def) => def.name(), | ||
99 | ast::AssocItem::TypeAlias(def) => def.name(), | ||
100 | ast::AssocItem::Const(def) => def.name(), | ||
101 | ast::AssocItem::MacroCall(_) => None, | ||
102 | } | ||
103 | .is_some() | ||
104 | }; | ||
105 | |||
106 | items | ||
107 | .iter() | ||
108 | .map(|i| match i { | ||
109 | hir::AssocItem::Function(i) => ast::AssocItem::Fn(i.source(db).value), | ||
110 | hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(i.source(db).value), | ||
111 | hir::AssocItem::Const(i) => ast::AssocItem::Const(i.source(db).value), | ||
112 | }) | ||
113 | .filter(has_def_name) | ||
114 | .filter(|it| match it { | ||
115 | ast::AssocItem::Fn(def) => matches!( | ||
116 | (default_methods, def.body()), | ||
117 | (DefaultMethods::Only, Some(_)) | (DefaultMethods::No, None) | ||
118 | ), | ||
119 | _ => default_methods == DefaultMethods::No, | ||
120 | }) | ||
121 | .collect::<Vec<_>>() | ||
122 | } | ||
123 | |||
124 | pub fn add_trait_assoc_items_to_impl( | ||
125 | sema: &hir::Semantics<ide_db::RootDatabase>, | ||
126 | items: Vec<ast::AssocItem>, | ||
127 | trait_: hir::Trait, | ||
128 | impl_def: ast::Impl, | ||
129 | target_scope: hir::SemanticsScope, | ||
130 | ) -> (ast::Impl, ast::AssocItem) { | ||
131 | let impl_item_list = impl_def.assoc_item_list().unwrap_or_else(make::assoc_item_list); | ||
132 | |||
133 | let n_existing_items = impl_item_list.assoc_items().count(); | ||
134 | let source_scope = sema.scope_for_def(trait_); | ||
135 | let ast_transform = QualifyPaths::new(&target_scope, &source_scope) | ||
136 | .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def.clone())); | ||
137 | |||
138 | let items = items | ||
139 | .into_iter() | ||
140 | .map(|it| ast_transform::apply(&*ast_transform, it)) | ||
141 | .map(|it| match it { | ||
142 | ast::AssocItem::Fn(def) => ast::AssocItem::Fn(add_body(def)), | ||
143 | ast::AssocItem::TypeAlias(def) => ast::AssocItem::TypeAlias(def.remove_bounds()), | ||
144 | _ => it, | ||
145 | }) | ||
146 | .map(|it| edit::remove_attrs_and_docs(&it)); | ||
147 | |||
148 | let new_impl_item_list = impl_item_list.append_items(items); | ||
149 | let new_impl_def = impl_def.with_assoc_item_list(new_impl_item_list); | ||
150 | let first_new_item = | ||
151 | new_impl_def.assoc_item_list().unwrap().assoc_items().nth(n_existing_items).unwrap(); | ||
152 | return (new_impl_def, first_new_item); | ||
153 | |||
154 | fn add_body(fn_def: ast::Fn) -> ast::Fn { | ||
155 | match fn_def.body() { | ||
156 | Some(_) => fn_def, | ||
157 | None => { | ||
158 | let body = | ||
159 | make::block_expr(None, Some(make::expr_todo())).indent(edit::IndentLevel(1)); | ||
160 | fn_def.with_body(body) | ||
161 | } | ||
162 | } | ||
163 | } | ||
164 | } | ||
165 | |||
80 | #[derive(Clone, Copy, Debug)] | 166 | #[derive(Clone, Copy, Debug)] |
81 | pub(crate) enum Cursor<'a> { | 167 | pub(crate) enum Cursor<'a> { |
82 | Replace(&'a SyntaxNode), | 168 | Replace(&'a SyntaxNode), |
diff --git a/crates/completion/src/completions/postfix.rs b/crates/completion/src/completions/postfix.rs index 348f017bd..7fbda7a6b 100644 --- a/crates/completion/src/completions/postfix.rs +++ b/crates/completion/src/completions/postfix.rs | |||
@@ -184,6 +184,16 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
184 | ctx, | 184 | ctx, |
185 | cap, | 185 | cap, |
186 | &dot_receiver, | 186 | &dot_receiver, |
187 | "some", | ||
188 | "Some(expr)", | ||
189 | &format!("Some({})", receiver_text), | ||
190 | ) | ||
191 | .add_to(acc); | ||
192 | |||
193 | postfix_snippet( | ||
194 | ctx, | ||
195 | cap, | ||
196 | &dot_receiver, | ||
187 | "dbg", | 197 | "dbg", |
188 | "dbg!(expr)", | 198 | "dbg!(expr)", |
189 | &format!("dbg!({})", receiver_text), | 199 | &format!("dbg!({})", receiver_text), |
@@ -291,6 +301,7 @@ fn main() { | |||
291 | sn ok Ok(expr) | 301 | sn ok Ok(expr) |
292 | sn ref &expr | 302 | sn ref &expr |
293 | sn refm &mut expr | 303 | sn refm &mut expr |
304 | sn some Some(expr) | ||
294 | sn while while expr {} | 305 | sn while while expr {} |
295 | "#]], | 306 | "#]], |
296 | ); | 307 | ); |
@@ -314,6 +325,7 @@ fn main() { | |||
314 | sn ok Ok(expr) | 325 | sn ok Ok(expr) |
315 | sn ref &expr | 326 | sn ref &expr |
316 | sn refm &mut expr | 327 | sn refm &mut expr |
328 | sn some Some(expr) | ||
317 | "#]], | 329 | "#]], |
318 | ) | 330 | ) |
319 | } | 331 | } |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index b1578820f..876659a2b 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -25,6 +25,10 @@ pub fn assoc_item_list() -> ast::AssocItemList { | |||
25 | ast_from_text("impl C for D {};") | 25 | ast_from_text("impl C for D {};") |
26 | } | 26 | } |
27 | 27 | ||
28 | pub fn impl_trait(trait_: ast::Path, ty: ast::Path) -> ast::Impl { | ||
29 | ast_from_text(&format!("impl {} for {} {{}}", trait_, ty)) | ||
30 | } | ||
31 | |||
28 | pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { | 32 | pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { |
29 | ast_from_text(&format!("use {};", name_ref)) | 33 | ast_from_text(&format!("use {};", name_ref)) |
30 | } | 34 | } |