ide/
typing.rs

1//! This module handles auto-magic editing actions applied together with users
2//! edits. For example, if the user typed
3//!
4//! ```text
5//!     foo
6//!         .bar()
7//!         .baz()
8//!     |   // <- cursor is here
9//! ```
10//!
11//! and types `.` next, we want to indent the dot.
12//!
13//! Language server executes such typing assists synchronously. That is, they
14//! block user's typing and should be pretty fast for this reason!
15
16mod on_enter;
17
18use either::Either;
19use hir::EditionedFileId;
20use ide_db::{
21    FilePosition, RootDatabase,
22    base_db::{RootQueryDb, SourceDatabase},
23};
24use span::Edition;
25use std::iter;
26
27use syntax::{
28    AstNode, Parse, SourceFile, SyntaxKind, TextRange, TextSize,
29    algo::{ancestors_at_offset, find_node_at_offset},
30    ast::{self, AstToken, edit::IndentLevel},
31};
32
33use ide_db::text_edit::TextEdit;
34
35use crate::SourceChange;
36
37pub(crate) use on_enter::on_enter;
38
39// Don't forget to add new trigger characters to `server_capabilities` in `caps.rs`.
40pub(crate) const TRIGGER_CHARS: &[char] = &['.', '=', '<', '>', '{', '(', '|', '+'];
41
42struct ExtendedTextEdit {
43    edit: TextEdit,
44    is_snippet: bool,
45}
46
47// Feature: On Typing Assists
48//
49// Some features trigger on typing certain characters:
50//
51// - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression
52// - typing `=` between two expressions adds `;` when in statement position
53// - typing `=` to turn an assignment into an equality comparison removes `;` when in expression position
54// - typing `.` in a chain method call auto-indents
55// - typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression
56// - typing `{` in a use item adds a closing `}` in the right place
57// - typing `>` to complete a return type `->` will insert a whitespace after it
58//
59// #### VS Code
60//
61// Add the following to `settings.json`:
62// ```json
63// "editor.formatOnType": true,
64// ```
65//
66// ![On Typing Assists](https://user-images.githubusercontent.com/48062697/113166163-69758500-923a-11eb-81ee-eb33ec380399.gif)
67// ![On Typing Assists](https://user-images.githubusercontent.com/48062697/113171066-105c2000-923f-11eb-87ab-f4a263346567.gif)
68pub(crate) fn on_char_typed(
69    db: &RootDatabase,
70    position: FilePosition,
71    char_typed: char,
72) -> Option<SourceChange> {
73    if !TRIGGER_CHARS.contains(&char_typed) {
74        return None;
75    }
76    let edition = db
77        .source_root_crates(db.file_source_root(position.file_id).source_root_id(db))
78        .first()
79        .map_or(Edition::CURRENT, |crates| crates.data(db).edition);
80    // FIXME: We are hitting the database here, if we are unlucky this call might block momentarily
81    // causing the editor to feel sluggish! We need to make this bail if it would block too long?
82    let editioned_file_id_wrapper = EditionedFileId::from_span_guess_origin(
83        db,
84        span::EditionedFileId::new(position.file_id, edition),
85    );
86    let file = &db.parse(editioned_file_id_wrapper);
87    let char_matches_position =
88        file.tree().syntax().text().char_at(position.offset) == Some(char_typed);
89    if !stdx::always!(char_matches_position) {
90        return None;
91    }
92
93    let edit = on_char_typed_(file, position.offset, char_typed, edition)?;
94
95    let mut sc = SourceChange::from_text_edit(position.file_id, edit.edit);
96    sc.is_snippet = edit.is_snippet;
97    Some(sc)
98}
99
100fn on_char_typed_(
101    file: &Parse<SourceFile>,
102    offset: TextSize,
103    char_typed: char,
104    edition: Edition,
105) -> Option<ExtendedTextEdit> {
106    match char_typed {
107        '.' => on_dot_typed(&file.tree(), offset),
108        '=' => on_eq_typed(&file.tree(), offset),
109        '>' => on_right_angle_typed(&file.tree(), offset),
110        '{' | '(' | '<' => on_opening_delimiter_typed(file, offset, char_typed, edition),
111        '|' => on_pipe_typed(&file.tree(), offset),
112        '+' => on_plus_typed(&file.tree(), offset),
113        _ => None,
114    }
115    .map(conv)
116}
117
118fn conv(edit: TextEdit) -> ExtendedTextEdit {
119    ExtendedTextEdit { edit, is_snippet: false }
120}
121
122/// Inserts a closing delimiter when the user types an opening bracket, wrapping an existing expression in a
123/// block, or a part of a `use` item (for `{`).
124fn on_opening_delimiter_typed(
125    file: &Parse<SourceFile>,
126    offset: TextSize,
127    opening_bracket: char,
128    edition: Edition,
129) -> Option<TextEdit> {
130    type FilterFn = fn(SyntaxKind) -> bool;
131    let (closing_bracket, expected_ast_bracket, allowed_kinds) = match opening_bracket {
132        '{' => ('}', SyntaxKind::L_CURLY, &[ast::Expr::can_cast as FilterFn] as &[FilterFn]),
133        '(' => (
134            ')',
135            SyntaxKind::L_PAREN,
136            &[ast::Expr::can_cast as FilterFn, ast::Pat::can_cast, ast::Type::can_cast]
137                as &[FilterFn],
138        ),
139        '<' => ('>', SyntaxKind::L_ANGLE, &[ast::Type::can_cast as FilterFn] as &[FilterFn]),
140        _ => return None,
141    };
142
143    let brace_token = file.tree().syntax().token_at_offset(offset).right_biased()?;
144    if brace_token.kind() != expected_ast_bracket {
145        return None;
146    }
147
148    // Remove the opening bracket to get a better parse tree, and reparse.
149    let range = brace_token.text_range();
150    if !stdx::always!(range.len() == TextSize::of(opening_bracket)) {
151        return None;
152    }
153    let reparsed = file.reparse(range, "", edition).tree();
154
155    if let Some(edit) =
156        on_delimited_node_typed(&reparsed, offset, opening_bracket, closing_bracket, allowed_kinds)
157    {
158        return Some(edit);
159    }
160
161    match opening_bracket {
162        '{' => on_left_brace_typed(&reparsed, offset),
163        '<' => on_left_angle_typed(&file.tree(), &reparsed, offset),
164        _ => None,
165    }
166}
167
168fn on_left_brace_typed(reparsed: &SourceFile, offset: TextSize) -> Option<TextEdit> {
169    let segment: ast::PathSegment = find_node_at_offset(reparsed.syntax(), offset)?;
170    if segment.syntax().text_range().start() != offset {
171        return None;
172    }
173
174    let tree: ast::UseTree = find_node_at_offset(reparsed.syntax(), offset)?;
175
176    Some(TextEdit::insert(tree.syntax().text_range().end() + TextSize::of("{"), "}".to_owned()))
177}
178
179fn on_delimited_node_typed(
180    reparsed: &SourceFile,
181    offset: TextSize,
182    opening_bracket: char,
183    closing_bracket: char,
184    kinds: &[fn(SyntaxKind) -> bool],
185) -> Option<TextEdit> {
186    let t = reparsed.syntax().token_at_offset(offset).right_biased()?;
187    if t.prev_token().is_some_and(|t| t.kind().is_any_identifier()) {
188        return None;
189    }
190    let (filter, node) = t
191        .parent_ancestors()
192        .take_while(|n| n.text_range().start() == offset)
193        .find_map(|n| kinds.iter().find(|&kind_filter| kind_filter(n.kind())).zip(Some(n)))?;
194    let mut node = node
195        .ancestors()
196        .take_while(|n| n.text_range().start() == offset && filter(n.kind()))
197        .last()?;
198
199    if let Some(parent) = node.parent().filter(|it| filter(it.kind())) {
200        let all_prev_sib_attr = {
201            let mut node = node.clone();
202            loop {
203                match node.prev_sibling() {
204                    Some(sib) if sib.kind().is_trivia() || sib.kind() == SyntaxKind::ATTR => {
205                        node = sib
206                    }
207                    Some(_) => break false,
208                    None => break true,
209                };
210            }
211        };
212
213        if all_prev_sib_attr {
214            node = parent;
215        }
216    }
217
218    // Insert the closing bracket right after the node.
219    Some(TextEdit::insert(
220        node.text_range().end() + TextSize::of(opening_bracket),
221        closing_bracket.to_string(),
222    ))
223}
224/// Returns an edit which should be applied after `=` was typed. Primarily,
225/// this works when adding `let =`.
226// FIXME: use a snippet completion instead of this hack here.
227fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
228    let text = file.syntax().text();
229    let has_newline = iter::successors(Some(offset), |&offset| Some(offset + TextSize::new(1)))
230        .filter_map(|offset| text.char_at(offset))
231        .find(|&c| !c.is_whitespace() || c == '\n')
232        == Some('n');
233    // don't attempt to add `;` if there is a newline after the `=`, the intent is likely to write
234    // out the expression afterwards!
235    if has_newline {
236        return None;
237    }
238
239    if let Some(edit) = let_stmt(file, offset) {
240        return Some(edit);
241    }
242    if let Some(edit) = assign_expr(file, offset) {
243        return Some(edit);
244    }
245    if let Some(edit) = assign_to_eq(file, offset) {
246        return Some(edit);
247    }
248
249    return None;
250
251    fn assign_expr(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
252        let binop: ast::BinExpr = find_node_at_offset(file.syntax(), offset)?;
253        if !matches!(binop.op_kind(), Some(ast::BinaryOp::Assignment { op: None })) {
254            return None;
255        }
256
257        // Parent must be `ExprStmt` or `StmtList` for `;` to be valid.
258        if let Some(expr_stmt) = ast::ExprStmt::cast(binop.syntax().parent()?) {
259            if expr_stmt.semicolon_token().is_some() {
260                return None;
261            }
262        } else if !ast::StmtList::can_cast(binop.syntax().parent()?.kind()) {
263            return None;
264        }
265
266        let expr = binop.rhs()?;
267        let expr_range = expr.syntax().text_range();
268        if expr_range.contains(offset) && offset != expr_range.start() {
269            return None;
270        }
271        if file.syntax().text().slice(offset..expr_range.start()).contains_char('\n') {
272            return None;
273        }
274        let offset = expr.syntax().text_range().end();
275        Some(TextEdit::insert(offset, ";".to_owned()))
276    }
277
278    /// `a =$0 b;` removes the semicolon if an expression is valid in this context.
279    fn assign_to_eq(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
280        let binop: ast::BinExpr = find_node_at_offset(file.syntax(), offset)?;
281        if !matches!(binop.op_kind(), Some(ast::BinaryOp::CmpOp(ast::CmpOp::Eq { negated: false })))
282        {
283            return None;
284        }
285
286        let expr_stmt = ast::ExprStmt::cast(binop.syntax().parent()?)?;
287        let semi = expr_stmt.semicolon_token()?;
288
289        if expr_stmt.syntax().next_sibling().is_some() {
290            // Not the last statement in the list.
291            return None;
292        }
293
294        Some(TextEdit::delete(semi.text_range()))
295    }
296
297    fn let_stmt(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
298        let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?;
299        if let_stmt.semicolon_token().is_some() {
300            return None;
301        }
302        let expr = let_stmt.initializer()?;
303        let expr_range = expr.syntax().text_range();
304        if expr_range.contains(offset) && offset != expr_range.start() {
305            return None;
306        }
307        if file.syntax().text().slice(offset..expr_range.start()).contains_char('\n') {
308            return None;
309        }
310        // Good indicator that we will insert into a bad spot, so bail out.
311        if expr.syntax().descendants().any(|it| it.kind() == SyntaxKind::ERROR) {
312            return None;
313        }
314        let offset = let_stmt.syntax().text_range().end();
315        Some(TextEdit::insert(offset, ";".to_owned()))
316    }
317}
318
319/// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately.
320fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
321    let whitespace =
322        file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?;
323
324    // if prior is fn call over multiple lines dont indent
325    // or if previous is method call over multiples lines keep that indent
326    let current_indent = {
327        let text = whitespace.text();
328        let (_prefix, suffix) = text.rsplit_once('\n')?;
329        suffix
330    };
331    let current_indent_len = TextSize::of(current_indent);
332
333    let parent = whitespace.syntax().parent()?;
334    // Make sure dot is a part of call chain
335    let receiver = if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
336        field_expr.expr()?
337    } else if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent.clone()) {
338        method_call_expr.receiver()?
339    } else {
340        return None;
341    };
342
343    let receiver_is_multiline = receiver.syntax().text().find_char('\n').is_some();
344    let target_indent = match (receiver, receiver_is_multiline) {
345        // if receiver is multiline field or method call, just take the previous `.` indentation
346        (ast::Expr::MethodCallExpr(expr), true) => {
347            expr.dot_token().as_ref().map(IndentLevel::from_token)
348        }
349        (ast::Expr::FieldExpr(expr), true) => {
350            expr.dot_token().as_ref().map(IndentLevel::from_token)
351        }
352        // if receiver is multiline expression, just keeps its indentation
353        (_, true) => Some(IndentLevel::from_node(&parent)),
354        _ => None,
355    };
356    let target_indent = match target_indent {
357        Some(x) => x,
358        // in all other cases, take previous indentation and indent once
359        None => IndentLevel::from_node(&parent) + 1,
360    }
361    .to_string();
362
363    if current_indent_len == TextSize::of(&target_indent) {
364        return None;
365    }
366
367    Some(TextEdit::replace(TextRange::new(offset - current_indent_len, offset), target_indent))
368}
369
370/// Add closing `>` for generic arguments/parameters.
371fn on_left_angle_typed(
372    file: &SourceFile,
373    reparsed: &SourceFile,
374    offset: TextSize,
375) -> Option<TextEdit> {
376    let file_text = reparsed.syntax().text();
377
378    // Find the next non-whitespace char in the line, check if its a `>`
379    let mut next_offset = offset;
380    while file_text.char_at(next_offset) == Some(' ') {
381        next_offset += TextSize::of(' ')
382    }
383    if file_text.char_at(next_offset) == Some('>') {
384        return None;
385    }
386
387    if ancestors_at_offset(file.syntax(), offset)
388        .take_while(|n| !ast::Item::can_cast(n.kind()))
389        .any(|n| {
390            ast::GenericParamList::can_cast(n.kind())
391                || ast::GenericArgList::can_cast(n.kind())
392                || ast::UseBoundGenericArgs::can_cast(n.kind())
393        })
394    {
395        // Insert the closing bracket right after
396        Some(TextEdit::insert(offset + TextSize::of('<'), '>'.to_string()))
397    } else {
398        None
399    }
400}
401
402fn on_pipe_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
403    let pipe_token = file.syntax().token_at_offset(offset).right_biased()?;
404    if pipe_token.kind() != SyntaxKind::PIPE {
405        return None;
406    }
407    if pipe_token.parent().and_then(ast::ParamList::cast)?.r_paren_token().is_some() {
408        return None;
409    }
410    let after_lpipe = offset + TextSize::of('|');
411    Some(TextEdit::insert(after_lpipe, "|".to_owned()))
412}
413
414fn on_plus_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
415    let plus_token = file.syntax().token_at_offset(offset).right_biased()?;
416    if plus_token.kind() != SyntaxKind::PLUS {
417        return None;
418    }
419    let mut ancestors = plus_token.parent_ancestors();
420    ancestors.next().and_then(ast::TypeBoundList::cast)?;
421    let trait_type =
422        ancestors.next().and_then(<Either<ast::DynTraitType, ast::ImplTraitType>>::cast)?;
423    let kind = ancestors.next()?.kind();
424
425    if ast::RefType::can_cast(kind) || ast::PtrType::can_cast(kind) || ast::RetType::can_cast(kind)
426    {
427        let mut builder = TextEdit::builder();
428        builder.insert(trait_type.syntax().text_range().start(), "(".to_owned());
429        builder.insert(trait_type.syntax().text_range().end(), ")".to_owned());
430        Some(builder.finish())
431    } else {
432        None
433    }
434}
435
436/// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }`
437fn on_right_angle_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
438    let file_text = file.syntax().text();
439    let after_arrow = offset + TextSize::of('>');
440    if file_text.char_at(after_arrow) != Some('{') {
441        return None;
442    }
443    find_node_at_offset::<ast::RetType>(file.syntax(), offset)?;
444
445    Some(TextEdit::insert(after_arrow, " ".to_owned()))
446}
447
448#[cfg(test)]
449mod tests {
450    use test_utils::{assert_eq_text, extract_offset};
451
452    use super::*;
453
454    impl ExtendedTextEdit {
455        fn apply(&self, text: &mut String) {
456            self.edit.apply(text);
457        }
458    }
459
460    fn do_type_char(char_typed: char, before: &str) -> Option<String> {
461        let (offset, mut before) = extract_offset(before);
462        let edit = TextEdit::insert(offset, char_typed.to_string());
463        edit.apply(&mut before);
464        let parse = SourceFile::parse(&before, span::Edition::CURRENT);
465        on_char_typed_(&parse, offset, char_typed, span::Edition::CURRENT).map(|it| {
466            it.apply(&mut before);
467            before.to_string()
468        })
469    }
470
471    fn type_char(
472        char_typed: char,
473        #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
474        #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
475    ) {
476        let actual = do_type_char(char_typed, ra_fixture_before)
477            .unwrap_or_else(|| panic!("typing `{char_typed}` did nothing"));
478
479        assert_eq_text!(ra_fixture_after, &actual);
480    }
481
482    fn type_char_noop(char_typed: char, #[rust_analyzer::rust_fixture] ra_fixture_before: &str) {
483        let file_change = do_type_char(char_typed, ra_fixture_before);
484        assert_eq!(file_change, None)
485    }
486
487    #[test]
488    fn test_semi_after_let() {
489        type_char_noop(
490            '=',
491            r"
492fn foo() {
493    let foo =$0
494}
495",
496        );
497        type_char(
498            '=',
499            r#"
500fn foo() {
501    let foo $0 1 + 1
502}
503"#,
504            r#"
505fn foo() {
506    let foo = 1 + 1;
507}
508"#,
509        );
510        type_char_noop(
511            '=',
512            r#"
513fn foo() {
514    let difference $0(counts: &HashMap<(char, char), u64>, last: char) -> u64 {
515        // ...
516    }
517}
518"#,
519        );
520        type_char_noop(
521            '=',
522            r"
523fn foo() {
524    let foo =$0
525    let bar = 1;
526}
527",
528        );
529        type_char_noop(
530            '=',
531            r"
532fn foo() {
533    let foo =$0
534     1 + 1
535}
536",
537        );
538    }
539
540    #[test]
541    fn test_semi_after_assign() {
542        type_char(
543            '=',
544            r#"
545fn f() {
546    i $0 0
547}
548"#,
549            r#"
550fn f() {
551    i = 0;
552}
553"#,
554        );
555        type_char(
556            '=',
557            r#"
558fn f() {
559    i $0 0
560    i
561}
562"#,
563            r#"
564fn f() {
565    i = 0;
566    i
567}
568"#,
569        );
570        type_char_noop(
571            '=',
572            r#"
573fn f(x: u8) {
574    if x $0
575}
576"#,
577        );
578        type_char_noop(
579            '=',
580            r#"
581fn f(x: u8) {
582    if x $0 {}
583}
584"#,
585        );
586        type_char_noop(
587            '=',
588            r#"
589fn f(x: u8) {
590    if x $0 0 {}
591}
592"#,
593        );
594        type_char_noop(
595            '=',
596            r#"
597fn f() {
598    g(i $0 0);
599}
600"#,
601        );
602    }
603
604    #[test]
605    fn assign_to_eq() {
606        type_char(
607            '=',
608            r#"
609fn f(a: u8) {
610    a =$0 0;
611}
612"#,
613            r#"
614fn f(a: u8) {
615    a == 0
616}
617"#,
618        );
619        type_char(
620            '=',
621            r#"
622fn f(a: u8) {
623    a $0= 0;
624}
625"#,
626            r#"
627fn f(a: u8) {
628    a == 0
629}
630"#,
631        );
632        type_char_noop(
633            '=',
634            r#"
635fn f(a: u8) {
636    let e = a =$0 0;
637}
638"#,
639        );
640        type_char_noop(
641            '=',
642            r#"
643fn f(a: u8) {
644    let e = a =$0 0;
645    e
646}
647"#,
648        );
649    }
650
651    #[test]
652    fn indents_new_chain_call() {
653        type_char(
654            '.',
655            r#"
656fn main() {
657    xs.foo()
658    $0
659}
660            "#,
661            r#"
662fn main() {
663    xs.foo()
664        .
665}
666            "#,
667        );
668        type_char_noop(
669            '.',
670            r#"
671fn main() {
672    xs.foo()
673        $0
674}
675            "#,
676        )
677    }
678
679    #[test]
680    fn indents_new_chain_call_with_semi() {
681        type_char(
682            '.',
683            r"
684fn main() {
685    xs.foo()
686    $0;
687}
688            ",
689            r#"
690fn main() {
691    xs.foo()
692        .;
693}
694            "#,
695        );
696        type_char_noop(
697            '.',
698            r#"
699fn main() {
700    xs.foo()
701        $0;
702}
703            "#,
704        )
705    }
706
707    #[test]
708    fn indents_new_chain_call_with_let() {
709        type_char(
710            '.',
711            r#"
712fn main() {
713    let _ = foo
714    $0
715    bar()
716}
717"#,
718            r#"
719fn main() {
720    let _ = foo
721        .
722    bar()
723}
724"#,
725        );
726    }
727
728    #[test]
729    fn indents_continued_chain_call() {
730        type_char(
731            '.',
732            r#"
733fn main() {
734    xs.foo()
735        .first()
736    $0
737}
738            "#,
739            r#"
740fn main() {
741    xs.foo()
742        .first()
743        .
744}
745            "#,
746        );
747        type_char_noop(
748            '.',
749            r#"
750fn main() {
751    xs.foo()
752        .first()
753        $0
754}
755            "#,
756        );
757    }
758
759    #[test]
760    fn indents_middle_of_chain_call() {
761        type_char(
762            '.',
763            r#"
764fn source_impl() {
765    let var = enum_defvariant_list().unwrap()
766    $0
767        .nth(92)
768        .unwrap();
769}
770            "#,
771            r#"
772fn source_impl() {
773    let var = enum_defvariant_list().unwrap()
774        .
775        .nth(92)
776        .unwrap();
777}
778            "#,
779        );
780        type_char_noop(
781            '.',
782            r#"
783fn source_impl() {
784    let var = enum_defvariant_list().unwrap()
785        $0
786        .nth(92)
787        .unwrap();
788}
789            "#,
790        );
791    }
792
793    #[test]
794    fn dont_indent_freestanding_dot() {
795        type_char_noop(
796            '.',
797            r#"
798fn main() {
799    $0
800}
801            "#,
802        );
803        type_char_noop(
804            '.',
805            r#"
806fn main() {
807$0
808}
809            "#,
810        );
811    }
812
813    #[test]
814    fn adds_space_after_return_type() {
815        type_char(
816            '>',
817            r#"
818fn foo() -$0{ 92 }
819"#,
820            r#"
821fn foo() -> { 92 }
822"#,
823        );
824    }
825
826    #[test]
827    fn adds_closing_brace_for_expr() {
828        type_char(
829            '{',
830            r#"
831fn f() { match () { _ => $0() } }
832            "#,
833            r#"
834fn f() { match () { _ => {()} } }
835            "#,
836        );
837        type_char(
838            '{',
839            r#"
840fn f() { $0() }
841            "#,
842            r#"
843fn f() { {()} }
844            "#,
845        );
846        type_char(
847            '{',
848            r#"
849fn f() { let x = $0(); }
850            "#,
851            r#"
852fn f() { let x = {()}; }
853            "#,
854        );
855        type_char(
856            '{',
857            r#"
858fn f() { let x = $0a.b(); }
859            "#,
860            r#"
861fn f() { let x = {a.b()}; }
862            "#,
863        );
864        type_char(
865            '{',
866            r#"
867const S: () = $0();
868fn f() {}
869            "#,
870            r#"
871const S: () = {()};
872fn f() {}
873            "#,
874        );
875        type_char(
876            '{',
877            r#"
878const S: () = $0a.b();
879fn f() {}
880            "#,
881            r#"
882const S: () = {a.b()};
883fn f() {}
884            "#,
885        );
886        type_char(
887            '{',
888            r#"
889fn f() {
890    match x {
891        0 => $0(),
892        1 => (),
893    }
894}
895            "#,
896            r#"
897fn f() {
898    match x {
899        0 => {()},
900        1 => (),
901    }
902}
903            "#,
904        );
905        type_char(
906            '{',
907            r#"
908fn main() {
909    #[allow(unreachable_code)]
910    $0g();
911}
912            "#,
913            r#"
914fn main() {
915    #[allow(unreachable_code)]
916    {g()};
917}
918            "#,
919        );
920    }
921
922    #[test]
923    fn noop_in_string_literal() {
924        // Regression test for #9351
925        type_char_noop(
926            '{',
927            r##"
928fn check_with(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
929    let base = r#"
930enum E { T(), R$0, C }
931use self::E::X;
932const Z: E = E::C;
933mod m {}
934asdasdasdasdasdasda
935sdasdasdasdasdasda
936sdasdasdasdasd
937"#;
938    let actual = completion_list(&format!("{}\n{}", base, ra_fixture));
939    expect.assert_eq(&actual)
940}
941            "##,
942        );
943    }
944
945    #[test]
946    fn noop_in_item_position_with_macro() {
947        type_char_noop('{', r#"$0println!();"#);
948        type_char_noop(
949            '{',
950            r#"
951fn main() $0println!("hello");
952}"#,
953        );
954    }
955
956    #[test]
957    fn adds_closing_brace_for_use_tree() {
958        type_char(
959            '{',
960            r#"
961use some::$0Path;
962            "#,
963            r#"
964use some::{Path};
965            "#,
966        );
967        type_char(
968            '{',
969            r#"
970use some::{Path, $0Other};
971            "#,
972            r#"
973use some::{Path, {Other}};
974            "#,
975        );
976        type_char(
977            '{',
978            r#"
979use some::{$0Path, Other};
980            "#,
981            r#"
982use some::{{Path}, Other};
983            "#,
984        );
985        type_char(
986            '{',
987            r#"
988use some::path::$0to::Item;
989            "#,
990            r#"
991use some::path::{to::Item};
992            "#,
993        );
994        type_char(
995            '{',
996            r#"
997use some::$0path::to::Item;
998            "#,
999            r#"
1000use some::{path::to::Item};
1001            "#,
1002        );
1003        type_char(
1004            '{',
1005            r#"
1006use $0some::path::to::Item;
1007            "#,
1008            r#"
1009use {some::path::to::Item};
1010            "#,
1011        );
1012        type_char(
1013            '{',
1014            r#"
1015use some::path::$0to::{Item};
1016            "#,
1017            r#"
1018use some::path::{to::{Item}};
1019            "#,
1020        );
1021        type_char(
1022            '{',
1023            r#"
1024use $0Thing as _;
1025            "#,
1026            r#"
1027use {Thing as _};
1028            "#,
1029        );
1030
1031        type_char_noop(
1032            '{',
1033            r#"
1034use some::pa$0th::to::Item;
1035            "#,
1036        );
1037    }
1038
1039    #[test]
1040    fn adds_closing_parenthesis_for_expr() {
1041        type_char(
1042            '(',
1043            r#"
1044fn f() { match () { _ => $0() } }
1045            "#,
1046            r#"
1047fn f() { match () { _ => (()) } }
1048            "#,
1049        );
1050        type_char(
1051            '(',
1052            r#"
1053fn f() { $0() }
1054            "#,
1055            r#"
1056fn f() { (()) }
1057            "#,
1058        );
1059        type_char(
1060            '(',
1061            r#"
1062fn f() { let x = $0(); }
1063            "#,
1064            r#"
1065fn f() { let x = (()); }
1066            "#,
1067        );
1068        type_char(
1069            '(',
1070            r#"
1071fn f() { let x = $0a.b(); }
1072            "#,
1073            r#"
1074fn f() { let x = (a.b()); }
1075            "#,
1076        );
1077        type_char(
1078            '(',
1079            r#"
1080const S: () = $0();
1081fn f() {}
1082            "#,
1083            r#"
1084const S: () = (());
1085fn f() {}
1086            "#,
1087        );
1088        type_char(
1089            '(',
1090            r#"
1091const S: () = $0a.b();
1092fn f() {}
1093            "#,
1094            r#"
1095const S: () = (a.b());
1096fn f() {}
1097            "#,
1098        );
1099        type_char(
1100            '(',
1101            r#"
1102fn f() {
1103    match x {
1104        0 => $0(),
1105        1 => (),
1106    }
1107}
1108            "#,
1109            r#"
1110fn f() {
1111    match x {
1112        0 => (()),
1113        1 => (),
1114    }
1115}
1116            "#,
1117        );
1118        type_char(
1119            '(',
1120            r#"
1121        fn f() {
1122            let z = Some($03);
1123        }
1124                    "#,
1125            r#"
1126        fn f() {
1127            let z = Some((3));
1128        }
1129                    "#,
1130        );
1131    }
1132
1133    #[test]
1134    fn preceding_whitespace_is_significant_for_closing_brackets() {
1135        type_char_noop(
1136            '(',
1137            r#"
1138fn f() { a.b$0if true {} }
1139"#,
1140        );
1141        type_char_noop(
1142            '(',
1143            r#"
1144fn f() { foo$0{} }
1145"#,
1146        );
1147    }
1148
1149    #[test]
1150    fn adds_closing_parenthesis_for_pat() {
1151        type_char(
1152            '(',
1153            r#"
1154fn f() { match () { $0() => () } }
1155"#,
1156            r#"
1157fn f() { match () { (()) => () } }
1158"#,
1159        );
1160        type_char(
1161            '(',
1162            r#"
1163fn f($0n: ()) {}
1164"#,
1165            r#"
1166fn f((n): ()) {}
1167"#,
1168        );
1169    }
1170
1171    #[test]
1172    fn adds_closing_parenthesis_for_ty() {
1173        type_char(
1174            '(',
1175            r#"
1176fn f(n: $0()) {}
1177"#,
1178            r#"
1179fn f(n: (())) {}
1180"#,
1181        );
1182        type_char(
1183            '(',
1184            r#"
1185fn f(n: $0a::b::<d>::c) {}
1186"#,
1187            r#"
1188fn f(n: (a::b::<d>::c)) {}
1189"#,
1190        );
1191    }
1192
1193    #[test]
1194    fn adds_closing_angles_for_ty() {
1195        type_char(
1196            '<',
1197            r#"
1198fn f(n: $0()) {}
1199"#,
1200            r#"
1201fn f(n: <()>) {}
1202"#,
1203        );
1204        type_char(
1205            '<',
1206            r#"
1207fn f(n: $0a::b::<d>::c) {}
1208"#,
1209            r#"
1210fn f(n: <a::b::<d>::c>) {}
1211"#,
1212        );
1213        type_char(
1214            '<',
1215            r#"
1216fn f(n: a$0b::<d>::c) {}
1217"#,
1218            r#"
1219fn f(n: a<>b::<d>::c) {}
1220"#,
1221        );
1222    }
1223
1224    #[test]
1225    fn parenthesis_noop_in_string_literal() {
1226        // Regression test for #9351
1227        type_char_noop(
1228            '(',
1229            r##"
1230fn check_with(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
1231    let base = r#"
1232enum E { T(), R$0, C }
1233use self::E::X;
1234const Z: E = E::C;
1235mod m {}
1236asdasdasdasdasdasda
1237sdasdasdasdasdasda
1238sdasdasdasdasd
1239"#;
1240    let actual = completion_list(&format!("{}\n{}", base, ra_fixture));
1241    expect.assert_eq(&actual)
1242}
1243            "##,
1244        );
1245    }
1246
1247    #[test]
1248    fn parenthesis_noop_in_item_position_with_macro() {
1249        type_char_noop('(', r#"$0println!();"#);
1250        type_char_noop(
1251            '(',
1252            r#"
1253fn main() $0println!("hello");
1254}"#,
1255        );
1256    }
1257
1258    #[test]
1259    fn parenthesis_noop_in_use_tree() {
1260        type_char_noop(
1261            '(',
1262            r#"
1263use some::$0Path;
1264            "#,
1265        );
1266        type_char_noop(
1267            '(',
1268            r#"
1269use some::{Path, $0Other};
1270            "#,
1271        );
1272        type_char_noop(
1273            '(',
1274            r#"
1275use some::{$0Path, Other};
1276            "#,
1277        );
1278        type_char_noop(
1279            '(',
1280            r#"
1281use some::path::$0to::Item;
1282            "#,
1283        );
1284        type_char_noop(
1285            '(',
1286            r#"
1287use some::$0path::to::Item;
1288            "#,
1289        );
1290        type_char_noop(
1291            '(',
1292            r#"
1293use $0some::path::to::Item;
1294            "#,
1295        );
1296        type_char_noop(
1297            '(',
1298            r#"
1299use some::path::$0to::{Item};
1300            "#,
1301        );
1302        type_char_noop(
1303            '(',
1304            r#"
1305use $0Thing as _;
1306            "#,
1307        );
1308
1309        type_char_noop(
1310            '(',
1311            r#"
1312use some::pa$0th::to::Item;
1313            "#,
1314        );
1315        type_char_noop(
1316            '<',
1317            r#"
1318use some::pa$0th::to::Item;
1319            "#,
1320        );
1321    }
1322
1323    #[test]
1324    fn adds_closing_angle_bracket_for_generic_args() {
1325        type_char(
1326            '<',
1327            r#"
1328fn foo() {
1329    bar::$0
1330}
1331            "#,
1332            r#"
1333fn foo() {
1334    bar::<>
1335}
1336            "#,
1337        );
1338
1339        type_char(
1340            '<',
1341            r#"
1342fn foo(bar: &[u64]) {
1343    bar.iter().collect::$0();
1344}
1345            "#,
1346            r#"
1347fn foo(bar: &[u64]) {
1348    bar.iter().collect::<>();
1349}
1350            "#,
1351        );
1352    }
1353
1354    #[test]
1355    fn adds_closing_angle_bracket_for_generic_params() {
1356        type_char(
1357            '<',
1358            r#"
1359fn foo$0() {}
1360            "#,
1361            r#"
1362fn foo<>() {}
1363            "#,
1364        );
1365        type_char(
1366            '<',
1367            r#"
1368fn foo$0
1369            "#,
1370            r#"
1371fn foo<>
1372            "#,
1373        );
1374        type_char(
1375            '<',
1376            r#"
1377struct Foo$0 {}
1378            "#,
1379            r#"
1380struct Foo<> {}
1381            "#,
1382        );
1383        type_char(
1384            '<',
1385            r#"
1386struct Foo$0();
1387            "#,
1388            r#"
1389struct Foo<>();
1390            "#,
1391        );
1392        type_char(
1393            '<',
1394            r#"
1395struct Foo$0
1396            "#,
1397            r#"
1398struct Foo<>
1399            "#,
1400        );
1401        type_char(
1402            '<',
1403            r#"
1404enum Foo$0
1405            "#,
1406            r#"
1407enum Foo<>
1408            "#,
1409        );
1410        type_char(
1411            '<',
1412            r#"
1413trait Foo$0
1414            "#,
1415            r#"
1416trait Foo<>
1417            "#,
1418        );
1419        type_char(
1420            '<',
1421            r#"
1422type Foo$0 = Bar;
1423            "#,
1424            r#"
1425type Foo<> = Bar;
1426            "#,
1427        );
1428        type_char(
1429            '<',
1430            r#"
1431impl<T> Foo$0 {}
1432            "#,
1433            r#"
1434impl<T> Foo<> {}
1435            "#,
1436        );
1437        type_char(
1438            '<',
1439            r#"
1440impl Foo$0 {}
1441            "#,
1442            r#"
1443impl Foo<> {}
1444            "#,
1445        );
1446    }
1447
1448    #[test]
1449    fn dont_add_closing_angle_bracket_for_comparison() {
1450        type_char_noop(
1451            '<',
1452            r#"
1453fn main() {
1454    42$0
1455}
1456            "#,
1457        );
1458        type_char_noop(
1459            '<',
1460            r#"
1461fn main() {
1462    42 $0
1463}
1464            "#,
1465        );
1466        type_char_noop(
1467            '<',
1468            r#"
1469fn main() {
1470    let foo = 42;
1471    foo $0
1472}
1473            "#,
1474        );
1475    }
1476
1477    #[test]
1478    fn dont_add_closing_angle_bracket_if_it_is_already_there() {
1479        type_char_noop(
1480            '<',
1481            r#"
1482fn foo() {
1483    bar::$0>
1484}
1485            "#,
1486        );
1487        type_char_noop(
1488            '<',
1489            r#"
1490fn foo(bar: &[u64]) {
1491    bar.iter().collect::$0   >();
1492}
1493            "#,
1494        );
1495        type_char_noop(
1496            '<',
1497            r#"
1498fn foo$0>() {}
1499            "#,
1500        );
1501        type_char_noop(
1502            '<',
1503            r#"
1504fn foo$0>
1505            "#,
1506        );
1507        type_char_noop(
1508            '<',
1509            r#"
1510struct Foo$0> {}
1511            "#,
1512        );
1513        type_char_noop(
1514            '<',
1515            r#"
1516struct Foo$0>();
1517            "#,
1518        );
1519        type_char_noop(
1520            '<',
1521            r#"
1522struct Foo$0>
1523            "#,
1524        );
1525        type_char_noop(
1526            '<',
1527            r#"
1528enum Foo$0>
1529            "#,
1530        );
1531        type_char_noop(
1532            '<',
1533            r#"
1534trait Foo$0>
1535            "#,
1536        );
1537        type_char_noop(
1538            '<',
1539            r#"
1540type Foo$0> = Bar;
1541            "#,
1542        );
1543        type_char_noop(
1544            '<',
1545            r#"
1546impl$0> Foo {}
1547            "#,
1548        );
1549        type_char_noop(
1550            '<',
1551            r#"
1552impl<T> Foo$0> {}
1553            "#,
1554        );
1555        type_char_noop(
1556            '<',
1557            r#"
1558impl Foo$0> {}
1559            "#,
1560        );
1561    }
1562
1563    #[test]
1564    fn regression_629() {
1565        type_char_noop(
1566            '.',
1567            r#"
1568fn foo() {
1569    CompletionItem::new(
1570        CompletionKind::Reference,
1571        ctx.source_range(),
1572        field.name().to_string(),
1573    )
1574    .foo()
1575    $0
1576}
1577"#,
1578        );
1579        type_char_noop(
1580            '.',
1581            r#"
1582fn foo() {
1583    CompletionItem::new(
1584        CompletionKind::Reference,
1585        ctx.source_range(),
1586        field.name().to_string(),
1587    )
1588    $0
1589}
1590"#,
1591        );
1592    }
1593
1594    #[test]
1595    fn completes_pipe_param_list() {
1596        type_char(
1597            '|',
1598            r#"
1599fn foo() {
1600    $0
1601}
1602"#,
1603            r#"
1604fn foo() {
1605    ||
1606}
1607"#,
1608        );
1609        type_char(
1610            '|',
1611            r#"
1612fn foo() {
1613    $0 a
1614}
1615"#,
1616            r#"
1617fn foo() {
1618    || a
1619}
1620"#,
1621        );
1622        type_char_noop(
1623            '|',
1624            r#"
1625fn foo() {
1626    let $0
1627}
1628"#,
1629        );
1630    }
1631
1632    #[test]
1633    fn adds_parentheses_around_trait_object_in_ref_type() {
1634        type_char(
1635            '+',
1636            r#"
1637fn foo(x: &dyn A$0) {}
1638"#,
1639            r#"
1640fn foo(x: &(dyn A+)) {}
1641"#,
1642        );
1643        type_char(
1644            '+',
1645            r#"
1646fn foo(x: &'static dyn A$0B) {}
1647"#,
1648            r#"
1649fn foo(x: &'static (dyn A+B)) {}
1650"#,
1651        );
1652        type_char_noop(
1653            '+',
1654            r#"
1655fn foo(x: &(dyn A$0)) {}
1656"#,
1657        );
1658        type_char_noop(
1659            '+',
1660            r#"
1661fn foo(x: Box<dyn A$0>) {}
1662"#,
1663        );
1664    }
1665
1666    #[test]
1667    fn adds_parentheses_around_trait_object_in_ptr_type() {
1668        type_char(
1669            '+',
1670            r#"
1671fn foo(x: *const dyn A$0) {}
1672"#,
1673            r#"
1674fn foo(x: *const (dyn A+)) {}
1675"#,
1676        );
1677    }
1678
1679    #[test]
1680    fn adds_parentheses_around_trait_object_in_return_type() {
1681        type_char(
1682            '+',
1683            r#"
1684fn foo(x: fn() -> dyn A$0) {}
1685"#,
1686            r#"
1687fn foo(x: fn() -> (dyn A+)) {}
1688"#,
1689        );
1690    }
1691}