Skip to content

Commit 3be3a10

Browse files
[ty] Don't provide completions when in class or function definition (#21146)
1 parent 13375d0 commit 3be3a10

File tree

1 file changed

+106
-5
lines changed

1 file changed

+106
-5
lines changed

crates/ty_ide/src/completion.rs

Lines changed: 106 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,10 @@ pub fn completion<'db>(
212212
offset: TextSize,
213213
) -> Vec<Completion<'db>> {
214214
let parsed = parsed_module(db, file).load(db);
215-
if is_in_comment(&parsed, offset) || is_in_string(&parsed, offset) {
215+
216+
let tokens = tokens_start_before(parsed.tokens(), offset);
217+
218+
if is_in_comment(tokens) || is_in_string(tokens) || is_in_definition_place(db, tokens, file) {
216219
return vec![];
217220
}
218221

@@ -829,8 +832,7 @@ fn find_typed_text(
829832

830833
/// Whether the given offset within the parsed module is within
831834
/// a comment or not.
832-
fn is_in_comment(parsed: &ParsedModuleRef, offset: TextSize) -> bool {
833-
let tokens = tokens_start_before(parsed.tokens(), offset);
835+
fn is_in_comment(tokens: &[Token]) -> bool {
834836
tokens.last().is_some_and(|t| t.kind().is_comment())
835837
}
836838

@@ -839,8 +841,7 @@ fn is_in_comment(parsed: &ParsedModuleRef, offset: TextSize) -> bool {
839841
///
840842
/// Note that this will return `false` when positioned within an
841843
/// interpolation block in an f-string or a t-string.
842-
fn is_in_string(parsed: &ParsedModuleRef, offset: TextSize) -> bool {
843-
let tokens = tokens_start_before(parsed.tokens(), offset);
844+
fn is_in_string(tokens: &[Token]) -> bool {
844845
tokens.last().is_some_and(|t| {
845846
matches!(
846847
t.kind(),
@@ -849,6 +850,29 @@ fn is_in_string(parsed: &ParsedModuleRef, offset: TextSize) -> bool {
849850
})
850851
}
851852

853+
/// If the tokens end with `class f` or `def f` we return true.
854+
/// If the tokens end with `class` or `def`, we return false.
855+
/// This is fine because we don't provide completions anyway.
856+
fn is_in_definition_place(db: &dyn Db, tokens: &[Token], file: File) -> bool {
857+
tokens
858+
.len()
859+
.checked_sub(2)
860+
.and_then(|i| tokens.get(i))
861+
.is_some_and(|t| {
862+
if matches!(
863+
t.kind(),
864+
TokenKind::Def | TokenKind::Class | TokenKind::Type
865+
) {
866+
true
867+
} else if t.kind() == TokenKind::Name {
868+
let source = source_text(db, file);
869+
&source[t.range()] == "type"
870+
} else {
871+
false
872+
}
873+
})
874+
}
875+
852876
/// Order completions according to the following rules:
853877
///
854878
/// 1) Names with no underscore prefix
@@ -4058,6 +4082,83 @@ def f[T](x: T):
40584082
test.build().contains("__repr__");
40594083
}
40604084

4085+
#[test]
4086+
fn no_completions_in_function_def_name() {
4087+
let builder = completion_test_builder(
4088+
"\
4089+
def f<CURSOR>
4090+
",
4091+
);
4092+
4093+
builder.auto_import().build().not_contains("fabs");
4094+
}
4095+
4096+
#[test]
4097+
fn no_completions_in_function_def_empty_name() {
4098+
let builder = completion_test_builder(
4099+
"\
4100+
def <CURSOR>
4101+
",
4102+
);
4103+
4104+
builder.auto_import().build().not_contains("fabs");
4105+
}
4106+
4107+
#[test]
4108+
fn no_completions_in_class_def_name() {
4109+
let builder = completion_test_builder(
4110+
"\
4111+
class f<CURSOR>
4112+
",
4113+
);
4114+
4115+
builder.auto_import().build().not_contains("fabs");
4116+
}
4117+
4118+
#[test]
4119+
fn no_completions_in_class_def_empty_name() {
4120+
let builder = completion_test_builder(
4121+
"\
4122+
class <CURSOR>
4123+
",
4124+
);
4125+
4126+
builder.auto_import().build().not_contains("fabs");
4127+
}
4128+
4129+
#[test]
4130+
fn no_completions_in_type_def_name() {
4131+
let builder = completion_test_builder(
4132+
"\
4133+
type f<CURSOR> = int
4134+
",
4135+
);
4136+
4137+
builder.auto_import().build().not_contains("fabs");
4138+
}
4139+
4140+
#[test]
4141+
fn no_completions_in_maybe_type_def_name() {
4142+
let builder = completion_test_builder(
4143+
"\
4144+
type f<CURSOR>
4145+
",
4146+
);
4147+
4148+
builder.auto_import().build().not_contains("fabs");
4149+
}
4150+
4151+
#[test]
4152+
fn no_completions_in_type_def_empty_name() {
4153+
let builder = completion_test_builder(
4154+
"\
4155+
type <CURSOR>
4156+
",
4157+
);
4158+
4159+
builder.auto_import().build().not_contains("fabs");
4160+
}
4161+
40614162
/// A way to create a simple single-file (named `main.py`) completion test
40624163
/// builder.
40634164
///

0 commit comments

Comments
 (0)