diff --git a/crates/ty_ide/src/completion.rs b/crates/ty_ide/src/completion.rs index 29f4992c05..03ef65f14a 100644 --- a/crates/ty_ide/src/completion.rs +++ b/crates/ty_ide/src/completion.rs @@ -1407,6 +1407,24 @@ fn is_in_variable_binding(parsed: &ParsedModuleRef, offset: TextSize, typed: Opt type_param.name.range.contains_range(range) } ast::AnyNodeRef::StmtFor(stmt_for) => stmt_for.target.range().contains_range(range), + // The AST does not produce `ast::AnyNodeRef::Parameter` nodes for keywords + // or otherwise invalid syntax. Rather they are captured in a + // `ast::AnyNodeRef::Parameters` node as "empty space". To ensure + // we still suppress suggestions even when the syntax is technically + // invalid we extract the token under the cursor and check if it makes + // up that "empty space" inside the Parameters Node. If it does, we know + // that we are still binding variables, just that the current state is + // syntatically invalid. Hence we suppress autocomplete suggestons + // also in those cases. + ast::AnyNodeRef::Parameters(params) => { + if !params.range.contains_range(range) { + return false; + } + params + .iter() + .map(|param| param.range()) + .all(|r| !r.contains_range(range)) + } _ => false, }) } @@ -5363,6 +5381,45 @@ def foo(p ); } + #[test] + fn no_completions_in_function_param_keyword() { + let builder = completion_test_builder( + "\ +def foo(in +", + ); + assert_snapshot!( + builder.build().snapshot(), + @"", + ); + } + + #[test] + fn no_completions_in_function_param_multi_keyword() { + let builder = completion_test_builder( + "\ +def foo(param, in +", + ); + assert_snapshot!( + builder.build().snapshot(), + @"", + ); + } + + #[test] + fn no_completions_in_function_param_multi_keyword_middle() { + let builder = completion_test_builder( + "\ +def foo(param, in, param_two +", + ); + assert_snapshot!( + builder.build().snapshot(), + @"", + ); + } + #[test] fn no_completions_in_function_type_param() { let builder = completion_test_builder(