diff --git a/crates/ty_ide/src/completion.rs b/crates/ty_ide/src/completion.rs index 26175462a3d32..03ef65f14a802 100644 --- a/crates/ty_ide/src/completion.rs +++ b/crates/ty_ide/src/completion.rs @@ -1315,7 +1315,8 @@ fn find_typed_text( if last.end() < offset || last.range().is_empty() { return None; } - Some(source[last.range()].to_string()) + let range = TextRange::new(last.start(), offset); + Some(source[range].to_string()) } /// Whether the last token is in a place where we should not provide completions. @@ -1406,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, }) } @@ -1633,6 +1652,21 @@ mod tests { ); } + #[test] + fn inside_token() { + let test = completion_test_builder( + "\ +foo_bar_baz = 1 +x = foobad +", + ); + + assert_snapshot!( + test.skip_builtins().build().snapshot(), + @"foo_bar_baz", + ); + } + #[test] fn type_keyword_dedup() { let test = completion_test_builder( @@ -5347,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(