diff options
Diffstat (limited to 'crates/ide_assists/src/utils.rs')
-rw-r--r-- | crates/ide_assists/src/utils.rs | 74 |
1 files changed, 64 insertions, 10 deletions
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs index 0074da741..880ab6fe3 100644 --- a/crates/ide_assists/src/utils.rs +++ b/crates/ide_assists/src/utils.rs | |||
@@ -3,8 +3,11 @@ | |||
3 | use std::ops; | 3 | use std::ops; |
4 | 4 | ||
5 | use ast::TypeBoundsOwner; | 5 | use ast::TypeBoundsOwner; |
6 | use hir::{Adt, HasSource}; | 6 | use hir::{Adt, HasSource, Semantics}; |
7 | use ide_db::{helpers::SnippetCap, RootDatabase}; | 7 | use ide_db::{ |
8 | helpers::{FamousDefs, SnippetCap}, | ||
9 | RootDatabase, | ||
10 | }; | ||
8 | use itertools::Itertools; | 11 | use itertools::Itertools; |
9 | use stdx::format_to; | 12 | use stdx::format_to; |
10 | use syntax::{ | 13 | use syntax::{ |
@@ -18,7 +21,7 @@ use syntax::{ | |||
18 | }; | 21 | }; |
19 | 22 | ||
20 | use crate::{ | 23 | use crate::{ |
21 | assist_context::AssistContext, | 24 | assist_context::{AssistBuilder, AssistContext}, |
22 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, | 25 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, |
23 | }; | 26 | }; |
24 | 27 | ||
@@ -205,23 +208,36 @@ pub(crate) fn vis_offset(node: &SyntaxNode) -> TextSize { | |||
205 | .unwrap_or_else(|| node.text_range().start()) | 208 | .unwrap_or_else(|| node.text_range().start()) |
206 | } | 209 | } |
207 | 210 | ||
208 | pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr { | 211 | pub(crate) fn invert_boolean_expression( |
209 | if let Some(expr) = invert_special_case(&expr) { | 212 | sema: &Semantics<RootDatabase>, |
213 | expr: ast::Expr, | ||
214 | ) -> ast::Expr { | ||
215 | if let Some(expr) = invert_special_case(sema, &expr) { | ||
210 | return expr; | 216 | return expr; |
211 | } | 217 | } |
212 | make::expr_prefix(T![!], expr) | 218 | make::expr_prefix(T![!], expr) |
213 | } | 219 | } |
214 | 220 | ||
215 | fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> { | 221 | fn invert_special_case(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ast::Expr> { |
216 | match expr { | 222 | match expr { |
217 | ast::Expr::BinExpr(bin) => match bin.op_kind()? { | 223 | ast::Expr::BinExpr(bin) => match bin.op_kind()? { |
218 | ast::BinOp::NegatedEqualityTest => bin.replace_op(T![==]).map(|it| it.into()), | 224 | ast::BinOp::NegatedEqualityTest => bin.replace_op(T![==]).map(|it| it.into()), |
219 | ast::BinOp::EqualityTest => bin.replace_op(T![!=]).map(|it| it.into()), | 225 | ast::BinOp::EqualityTest => bin.replace_op(T![!=]).map(|it| it.into()), |
220 | // Parenthesize composite boolean expressions before prefixing `!` | 226 | // Swap `<` with `>=`, `<=` with `>`, ... if operands `impl Ord` |
221 | ast::BinOp::BooleanAnd | ast::BinOp::BooleanOr => { | 227 | ast::BinOp::LesserTest if bin_impls_ord(sema, bin) => { |
222 | Some(make::expr_prefix(T![!], make::expr_paren(expr.clone()))) | 228 | bin.replace_op(T![>=]).map(|it| it.into()) |
229 | } | ||
230 | ast::BinOp::LesserEqualTest if bin_impls_ord(sema, bin) => { | ||
231 | bin.replace_op(T![>]).map(|it| it.into()) | ||
232 | } | ||
233 | ast::BinOp::GreaterTest if bin_impls_ord(sema, bin) => { | ||
234 | bin.replace_op(T![<=]).map(|it| it.into()) | ||
235 | } | ||
236 | ast::BinOp::GreaterEqualTest if bin_impls_ord(sema, bin) => { | ||
237 | bin.replace_op(T![<]).map(|it| it.into()) | ||
223 | } | 238 | } |
224 | _ => None, | 239 | // Parenthesize other expressions before prefixing `!` |
240 | _ => Some(make::expr_prefix(T![!], make::expr_paren(expr.clone()))), | ||
225 | }, | 241 | }, |
226 | ast::Expr::MethodCallExpr(mce) => { | 242 | ast::Expr::MethodCallExpr(mce) => { |
227 | let receiver = mce.receiver()?; | 243 | let receiver = mce.receiver()?; |
@@ -250,6 +266,22 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> { | |||
250 | } | 266 | } |
251 | } | 267 | } |
252 | 268 | ||
269 | fn bin_impls_ord(sema: &Semantics<RootDatabase>, bin: &ast::BinExpr) -> bool { | ||
270 | match ( | ||
271 | bin.lhs().and_then(|lhs| sema.type_of_expr(&lhs)), | ||
272 | bin.rhs().and_then(|rhs| sema.type_of_expr(&rhs)), | ||
273 | ) { | ||
274 | (Some(lhs_ty), Some(rhs_ty)) if lhs_ty == rhs_ty => { | ||
275 | let krate = sema.scope(bin.syntax()).module().map(|it| it.krate()); | ||
276 | let ord_trait = FamousDefs(sema, krate).core_cmp_Ord(); | ||
277 | ord_trait.map_or(false, |ord_trait| { | ||
278 | lhs_ty.autoderef(sema.db).any(|ty| ty.impls_trait(sema.db, ord_trait, &[])) | ||
279 | }) | ||
280 | } | ||
281 | _ => false, | ||
282 | } | ||
283 | } | ||
284 | |||
253 | pub(crate) fn next_prev() -> impl Iterator<Item = Direction> { | 285 | pub(crate) fn next_prev() -> impl Iterator<Item = Direction> { |
254 | [Direction::Next, Direction::Prev].iter().copied() | 286 | [Direction::Next, Direction::Prev].iter().copied() |
255 | } | 287 | } |
@@ -432,3 +464,25 @@ fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str | |||
432 | 464 | ||
433 | buf | 465 | buf |
434 | } | 466 | } |
467 | |||
468 | pub(crate) fn add_method_to_adt( | ||
469 | builder: &mut AssistBuilder, | ||
470 | adt: &ast::Adt, | ||
471 | impl_def: Option<ast::Impl>, | ||
472 | method: &str, | ||
473 | ) { | ||
474 | let mut buf = String::with_capacity(method.len() + 2); | ||
475 | if impl_def.is_some() { | ||
476 | buf.push('\n'); | ||
477 | } | ||
478 | buf.push_str(method); | ||
479 | |||
480 | let start_offset = impl_def | ||
481 | .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) | ||
482 | .unwrap_or_else(|| { | ||
483 | buf = generate_impl_text(&adt, &buf); | ||
484 | adt.syntax().text_range().end() | ||
485 | }); | ||
486 | |||
487 | builder.insert(start_offset, buf); | ||
488 | } | ||