From cf8a27b4ccaa89d4e6e78f1b7235b33b4bfc1b70 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 4 Nov 2024 11:03:36 +0100 Subject: [PATCH] Make bracket typing handler work on more things --- crates/ide/src/typing.rs | 240 +++++++++++++++++++++++++-------------- 1 file changed, 153 insertions(+), 87 deletions(-) diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs index 9bb5de9f2e3f..08387818d915 100644 --- a/crates/ide/src/typing.rs +++ b/crates/ide/src/typing.rs @@ -20,7 +20,7 @@ use span::EditionedFileId; use syntax::{ algo::{ancestors_at_offset, find_node_at_offset}, ast::{self, edit::IndentLevel, AstToken}, - AstNode, Parse, SourceFile, SyntaxKind, TextRange, TextSize, T, + AstNode, Parse, SourceFile, SyntaxKind, TextRange, TextSize, }; use ide_db::text_edit::TextEdit; @@ -84,30 +84,35 @@ fn on_char_typed_inner( if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) { return None; } - let conv = |text_edit: Option| { - Some(ExtendedTextEdit { edit: text_edit?, is_snippet: false }) - }; match char_typed { - '.' => conv(on_dot_typed(&file.tree(), offset)), - '=' => conv(on_eq_typed(&file.tree(), offset)), - '<' => on_left_angle_typed(&file.tree(), offset), - '>' => conv(on_right_angle_typed(&file.tree(), offset)), - '{' => conv(on_opening_bracket_typed(file, offset, '{')), - '(' => conv(on_opening_bracket_typed(file, offset, '(')), + '.' => on_dot_typed(&file.tree(), offset).map(conv), + '=' => on_eq_typed(&file.tree(), offset).map(conv), + '>' => on_right_angle_typed(&file.tree(), offset).map(conv), + '{' | '(' | '<' => on_opening_bracket_typed(file, offset, char_typed), _ => None, } } +fn conv(edit: TextEdit) -> ExtendedTextEdit { + ExtendedTextEdit { edit, is_snippet: false } +} + /// Inserts a closing bracket when the user types an opening bracket, wrapping an existing expression in a /// block, or a part of a `use` item (for `{`). fn on_opening_bracket_typed( file: &Parse, offset: TextSize, opening_bracket: char, -) -> Option { - let (closing_bracket, expected_ast_bracket) = match opening_bracket { - '{' => ('}', SyntaxKind::L_CURLY), - '(' => (')', SyntaxKind::L_PAREN), +) -> Option { + type FilterFn = fn(SyntaxKind) -> bool; + let (closing_bracket, expected_ast_bracket, allowed_kinds) = match opening_bracket { + '{' => ('}', SyntaxKind::L_CURLY, &[ast::Expr::can_cast as FilterFn] as &[FilterFn]), + '(' => ( + ')', + SyntaxKind::L_PAREN, + &[ast::Expr::can_cast, ast::Pat::can_cast, ast::Type::can_cast] as &[FilterFn], + ), + '<' => ('>', SyntaxKind::L_ANGLE, &[ast::Type::can_cast as FilterFn] as &[FilterFn]), _ => return None, }; @@ -126,15 +131,23 @@ fn on_opening_bracket_typed( return None; } // FIXME: Edition - let file = file.reparse(range, "", span::Edition::CURRENT_FIXME); + let reparsed = file.reparse(range, "", span::Edition::CURRENT_FIXME); - if let Some(edit) = bracket_expr(&file.tree(), offset, opening_bracket, closing_bracket) { - return Some(edit); + if let Some(edit) = + bracket_node(&reparsed.tree(), offset, opening_bracket, closing_bracket, allowed_kinds) + { + return Some(edit).map(conv); + } + + if opening_bracket == '{' { + if let Some(edit) = brace_use_path(&reparsed.tree(), offset) { + return Some(edit).map(conv); + } } - if closing_bracket == '}' { - if let Some(edit) = brace_use_path(&file.tree(), offset) { - return Some(edit); + if opening_bracket == '<' { + if let Some(edit) = on_left_angle_typed(&file.tree(), offset) { + return Some(edit).map(conv); } } @@ -151,49 +164,45 @@ fn on_opening_bracket_typed( Some(TextEdit::insert(tree.syntax().text_range().end() + TextSize::of("{"), "}".to_owned())) } - fn bracket_expr( + fn bracket_node( file: &SourceFile, offset: TextSize, opening_bracket: char, closing_bracket: char, + kinds: &[fn(SyntaxKind) -> bool], ) -> Option { - let mut expr: ast::Expr = find_node_at_offset(file.syntax(), offset)?; - if expr.syntax().text_range().start() != offset { - return None; - } - - // Enclose the outermost expression starting at `offset` - while let Some(parent) = expr.syntax().parent() { - if parent.text_range().start() != expr.syntax().text_range().start() { - break; - } - - match ast::Expr::cast(parent) { - Some(parent) => expr = parent, - None => break, - } - } - - if let Some(parent) = expr.syntax().parent().and_then(ast::Expr::cast) { - let mut node = expr.syntax().clone(); - let all_prev_sib_attr = loop { - match node.prev_sibling() { - Some(sib) if sib.kind().is_trivia() || sib.kind() == SyntaxKind::ATTR => { - node = sib - } - Some(_) => break false, - None => break true, - }; + let t = file.syntax().token_at_offset(offset).right_biased()?; + let (filter, node) = t + .parent_ancestors() + .take_while(|n| n.text_range().start() == offset) + .find_map(|n| kinds.iter().find(|&kind_filter| kind_filter(n.kind())).zip(Some(n)))?; + let mut node = node + .ancestors() + .take_while(|n| n.text_range().start() == offset && filter(n.kind())) + .last()?; + + if let Some(parent) = node.parent().filter(|it| filter(it.kind())) { + let all_prev_sib_attr = { + let mut node = node.clone(); + loop { + match node.prev_sibling() { + Some(sib) if sib.kind().is_trivia() || sib.kind() == SyntaxKind::ATTR => { + node = sib + } + Some(_) => break false, + None => break true, + }; + } }; if all_prev_sib_attr { - expr = parent; + node = parent; } } - // Insert the closing bracket right after the expression. + // Insert the closing bracket right after the node. Some(TextEdit::insert( - expr.syntax().text_range().end() + TextSize::of(opening_bracket), + node.text_range().end() + TextSize::of(opening_bracket), closing_bracket.to_string(), )) } @@ -342,7 +351,7 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option { } /// Add closing `>` for generic arguments/parameters. -fn on_left_angle_typed(file: &SourceFile, offset: TextSize) -> Option { +fn on_left_angle_typed(file: &SourceFile, offset: TextSize) -> Option { let file_text = file.syntax().text(); if !stdx::always!(file_text.char_at(offset) == Some('<')) { return None; @@ -357,23 +366,11 @@ fn on_left_angle_typed(file: &SourceFile, offset: TextSize) -> Option".to_owned()), - is_snippet: true, - }); - } - } - if ancestors_at_offset(file.syntax(), offset).any(|n| { ast::GenericParamList::can_cast(n.kind()) || ast::GenericArgList::can_cast(n.kind()) }) { - Some(ExtendedTextEdit { - edit: TextEdit::replace(range, "<$0>".to_owned()), - is_snippet: true, - }) + // Insert the closing bracket right after + Some(TextEdit::insert(offset + TextSize::of('<'), '>'.to_string())) } else { None } @@ -1066,6 +1063,78 @@ fn f() { ); } + #[test] + fn adds_closing_parenthesis_for_pat() { + type_char( + '(', + r#" +fn f() { match () { $0() => () } } +"#, + r#" +fn f() { match () { (()) => () } } +"#, + ); + type_char( + '(', + r#" +fn f($0n: ()) {} +"#, + r#" +fn f((n): ()) {} +"#, + ); + } + + #[test] + fn adds_closing_parenthesis_for_ty() { + type_char( + '(', + r#" +fn f(n: $0()) {} +"#, + r#" +fn f(n: (())) {} +"#, + ); + type_char( + '(', + r#" +fn f(n: $0a::b::::c) {} +"#, + r#" +fn f(n: (a::b::::c)) {} +"#, + ); + } + + #[test] + fn adds_closing_angles_for_ty() { + type_char( + '<', + r#" +fn f(n: $0()) {} +"#, + r#" +fn f(n: <()>) {} +"#, + ); + type_char( + '<', + r#" +fn f(n: $0a::b::::c) {} +"#, + r#" +fn f(n: ::c>) {} +"#, + ); + type_char_noop( + '<', + r#" +fn f(n: a$0b::::c) {} +"#, + ); + } + #[test] fn parenthesis_noop_in_string_literal() { // Regression test for #9351 @@ -1154,6 +1223,12 @@ use $0Thing as _; type_char_noop( '(', r#" +use some::pa$0th::to::Item; + "#, + ); + type_char_noop( + '<', + r#" use some::pa$0th::to::Item; "#, ); @@ -1170,7 +1245,7 @@ fn foo() { "#, r#" fn foo() { - bar::<$0> + bar::<> } "#, ); @@ -1184,7 +1259,7 @@ fn foo(bar: &[u64]) { "#, r#" fn foo(bar: &[u64]) { - bar.iter().collect::<$0>(); + bar.iter().collect::<>(); } "#, ); @@ -1198,7 +1273,7 @@ fn foo(bar: &[u64]) { fn foo$0() {} "#, r#" -fn foo<$0>() {} +fn foo<>() {} "#, ); type_char( @@ -1207,7 +1282,7 @@ fn foo<$0>() {} fn foo$0 "#, r#" -fn foo<$0> +fn foo<> "#, ); type_char( @@ -1216,7 +1291,7 @@ fn foo<$0> struct Foo$0 {} "#, r#" -struct Foo<$0> {} +struct Foo<> {} "#, ); type_char( @@ -1225,7 +1300,7 @@ struct Foo<$0> {} struct Foo$0(); "#, r#" -struct Foo<$0>(); +struct Foo<>(); "#, ); type_char( @@ -1234,7 +1309,7 @@ struct Foo<$0>(); struct Foo$0 "#, r#" -struct Foo<$0> +struct Foo<> "#, ); type_char( @@ -1243,7 +1318,7 @@ struct Foo<$0> enum Foo$0 "#, r#" -enum Foo<$0> +enum Foo<> "#, ); type_char( @@ -1252,7 +1327,7 @@ enum Foo<$0> trait Foo$0 "#, r#" -trait Foo<$0> +trait Foo<> "#, ); type_char( @@ -1261,16 +1336,7 @@ trait Foo<$0> type Foo$0 = Bar; "#, r#" -type Foo<$0> = Bar; - "#, - ); - type_char( - '<', - r#" -impl$0 Foo {} - "#, - r#" -impl<$0> Foo {} +type Foo<> = Bar; "#, ); type_char( @@ -1279,7 +1345,7 @@ impl<$0> Foo {} impl Foo$0 {} "#, r#" -impl Foo<$0> {} +impl Foo<> {} "#, ); type_char( @@ -1288,7 +1354,7 @@ impl Foo<$0> {} impl Foo$0 {} "#, r#" -impl Foo<$0> {} +impl Foo<> {} "#, ); }