mirror of https://github.com/astral-sh/ruff
[ty] Implement patterns and typevars in the LSP (#21671)
## Summary
**This is the final goto-targets with missing
goto-definition/declaration implementations!
You can now theoretically click on all the user-defined names in all the
syntax. 🎉**
This adds:
* goto definition/declaration on patterns/typevars
* find-references/rename on patterns/typevars
* fixes syntax highlighting of `*rest` patterns
This notably *does not* add:
* goto-type for patterns/typevars
* hover for patterns/typevars (because that's just goto-type for names)
Also I realized we were at the precipice of one of the great GotoTarget
sins being resolved, and so I made import aliases also resolve to a
ResolvedDefinition. This removes a ton of cruft and prevents further
backsliding.
Note however that import aliases are, in general, completely jacked up
when it comes to find-references/renames (both before and after this
PR). Previously you could try to rename an import alias and it just
wouldn't do anything. With this change we instead refuse to even let you
try to rename it.
Sorting out why import aliases are jacked up is an ongoing thing I hope
to handle in a followup.
## Test Plan
You'll surely not regret checking in 86 snapshot tests
This commit is contained in:
parent
5e1b2eef57
commit
c534bfaf01
|
|
@ -17,7 +17,7 @@ use ty_python_semantic::{
|
|||
|
||||
use crate::docstring::Docstring;
|
||||
use crate::find_node::covering_node;
|
||||
use crate::goto::DefinitionsOrTargets;
|
||||
use crate::goto::Definitions;
|
||||
use crate::importer::{ImportRequest, Importer};
|
||||
use crate::symbols::QueryPattern;
|
||||
use crate::{Db, all_symbols};
|
||||
|
|
@ -220,9 +220,7 @@ impl<'db> Completion<'db> {
|
|||
db: &'db dyn Db,
|
||||
semantic: SemanticCompletion<'db>,
|
||||
) -> Completion<'db> {
|
||||
let definition = semantic
|
||||
.ty
|
||||
.and_then(|ty| DefinitionsOrTargets::from_ty(db, ty));
|
||||
let definition = semantic.ty.and_then(|ty| Definitions::from_ty(db, ty));
|
||||
let documentation = definition.and_then(|def| def.docstring(db));
|
||||
let is_type_check_only = semantic.is_type_check_only(db);
|
||||
Completion {
|
||||
|
|
|
|||
|
|
@ -212,16 +212,9 @@ pub(crate) enum GotoTarget<'a> {
|
|||
|
||||
/// The resolved definitions for a `GotoTarget`
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum DefinitionsOrTargets<'db> {
|
||||
/// We computed actual Definitions we can do followup queries on.
|
||||
Definitions(Vec<ResolvedDefinition<'db>>),
|
||||
/// We directly computed a navigation.
|
||||
///
|
||||
/// We can't get docs or usefully compute goto-definition for this.
|
||||
Targets(crate::NavigationTargets),
|
||||
}
|
||||
pub(crate) struct Definitions<'db>(pub Vec<ResolvedDefinition<'db>>);
|
||||
|
||||
impl<'db> DefinitionsOrTargets<'db> {
|
||||
impl<'db> Definitions<'db> {
|
||||
pub(crate) fn from_ty(db: &'db dyn crate::Db, ty: Type<'db>) -> Option<Self> {
|
||||
let ty_def = ty.definition(db)?;
|
||||
let resolved = match ty_def {
|
||||
|
|
@ -237,7 +230,7 @@ impl<'db> DefinitionsOrTargets<'db> {
|
|||
ResolvedDefinition::Definition(definition)
|
||||
}
|
||||
};
|
||||
Some(DefinitionsOrTargets::Definitions(vec![resolved]))
|
||||
Some(Definitions(vec![resolved]))
|
||||
}
|
||||
|
||||
/// Get the "goto-declaration" interpretation of this definition
|
||||
|
|
@ -247,12 +240,7 @@ impl<'db> DefinitionsOrTargets<'db> {
|
|||
self,
|
||||
db: &'db dyn ty_python_semantic::Db,
|
||||
) -> Option<crate::NavigationTargets> {
|
||||
match self {
|
||||
DefinitionsOrTargets::Definitions(definitions) => {
|
||||
definitions_to_navigation_targets(db, None, definitions)
|
||||
}
|
||||
DefinitionsOrTargets::Targets(targets) => Some(targets),
|
||||
}
|
||||
definitions_to_navigation_targets(db, None, self.0)
|
||||
}
|
||||
|
||||
/// Get the "goto-definition" interpretation of this definition
|
||||
|
|
@ -263,12 +251,7 @@ impl<'db> DefinitionsOrTargets<'db> {
|
|||
self,
|
||||
db: &'db dyn ty_python_semantic::Db,
|
||||
) -> Option<crate::NavigationTargets> {
|
||||
match self {
|
||||
DefinitionsOrTargets::Definitions(definitions) => {
|
||||
definitions_to_navigation_targets(db, Some(&StubMapper::new(db)), definitions)
|
||||
}
|
||||
DefinitionsOrTargets::Targets(targets) => Some(targets),
|
||||
}
|
||||
definitions_to_navigation_targets(db, Some(&StubMapper::new(db)), self.0)
|
||||
}
|
||||
|
||||
/// Get the docstring for this definition
|
||||
|
|
@ -277,13 +260,7 @@ impl<'db> DefinitionsOrTargets<'db> {
|
|||
/// so this will check both the goto-declarations and goto-definitions (in that order)
|
||||
/// and return the first one found.
|
||||
pub(crate) fn docstring(self, db: &'db dyn crate::Db) -> Option<Docstring> {
|
||||
let definitions = match self {
|
||||
DefinitionsOrTargets::Definitions(definitions) => definitions,
|
||||
// Can't find docs for these
|
||||
// (make more cases DefinitionOrTargets::Definitions to get more docs!)
|
||||
DefinitionsOrTargets::Targets(_) => return None,
|
||||
};
|
||||
for definition in &definitions {
|
||||
for definition in &self.0 {
|
||||
// If we got a docstring from the original definition, use it
|
||||
if let Some(docstring) = definition.docstring(db) {
|
||||
return Some(Docstring::new(docstring));
|
||||
|
|
@ -296,7 +273,7 @@ impl<'db> DefinitionsOrTargets<'db> {
|
|||
let stub_mapper = StubMapper::new(db);
|
||||
|
||||
// Try to find the corresponding implementation definition
|
||||
for definition in stub_mapper.map_definitions(definitions) {
|
||||
for definition in stub_mapper.map_definitions(self.0) {
|
||||
if let Some(docstring) = definition.docstring(db) {
|
||||
return Some(Docstring::new(docstring));
|
||||
}
|
||||
|
|
@ -399,36 +376,32 @@ impl GotoTarget<'_> {
|
|||
&self,
|
||||
model: &SemanticModel<'db>,
|
||||
alias_resolution: ImportAliasResolution,
|
||||
) -> Option<DefinitionsOrTargets<'db>> {
|
||||
use crate::NavigationTarget;
|
||||
match self {
|
||||
GotoTarget::Expression(expression) => definitions_for_expression(model, *expression)
|
||||
.map(DefinitionsOrTargets::Definitions),
|
||||
) -> Option<Definitions<'db>> {
|
||||
let definitions = match self {
|
||||
GotoTarget::Expression(expression) => definitions_for_expression(model, *expression),
|
||||
// For already-defined symbols, they are their own definitions
|
||||
GotoTarget::FunctionDef(function) => Some(DefinitionsOrTargets::Definitions(vec![
|
||||
ResolvedDefinition::Definition(function.definition(model)),
|
||||
])),
|
||||
GotoTarget::FunctionDef(function) => Some(vec![ResolvedDefinition::Definition(
|
||||
function.definition(model),
|
||||
)]),
|
||||
|
||||
GotoTarget::ClassDef(class) => Some(DefinitionsOrTargets::Definitions(vec![
|
||||
ResolvedDefinition::Definition(class.definition(model)),
|
||||
])),
|
||||
GotoTarget::ClassDef(class) => Some(vec![ResolvedDefinition::Definition(
|
||||
class.definition(model),
|
||||
)]),
|
||||
|
||||
GotoTarget::Parameter(parameter) => Some(DefinitionsOrTargets::Definitions(vec![
|
||||
ResolvedDefinition::Definition(parameter.definition(model)),
|
||||
])),
|
||||
GotoTarget::Parameter(parameter) => Some(vec![ResolvedDefinition::Definition(
|
||||
parameter.definition(model),
|
||||
)]),
|
||||
|
||||
// For import aliases (offset within 'y' or 'z' in "from x import y as z")
|
||||
GotoTarget::ImportSymbolAlias {
|
||||
alias, import_from, ..
|
||||
} => {
|
||||
let symbol_name = alias.name.as_str();
|
||||
Some(DefinitionsOrTargets::Definitions(
|
||||
definitions_for_imported_symbol(
|
||||
Some(definitions_for_imported_symbol(
|
||||
model,
|
||||
import_from,
|
||||
symbol_name,
|
||||
alias_resolution,
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
|
|
@ -448,14 +421,9 @@ impl GotoTarget<'_> {
|
|||
if alias_resolution == ImportAliasResolution::ResolveAliases {
|
||||
definitions_for_module(model, Some(alias.name.as_str()), 0)
|
||||
} else {
|
||||
let alias_range = alias.asname.as_ref().unwrap().range;
|
||||
Some(DefinitionsOrTargets::Targets(
|
||||
crate::NavigationTargets::single(NavigationTarget {
|
||||
file: model.file(),
|
||||
focus_range: alias_range,
|
||||
full_range: alias.range(),
|
||||
}),
|
||||
))
|
||||
alias.asname.as_ref().map(|name| {
|
||||
definitions_for_name(model, name.as_str(), AnyNodeRef::Identifier(name))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -463,45 +431,44 @@ impl GotoTarget<'_> {
|
|||
GotoTarget::KeywordArgument {
|
||||
keyword,
|
||||
call_expression,
|
||||
} => Some(DefinitionsOrTargets::Definitions(
|
||||
definitions_for_keyword_argument(model, keyword, call_expression),
|
||||
} => Some(definitions_for_keyword_argument(
|
||||
model,
|
||||
keyword,
|
||||
call_expression,
|
||||
)),
|
||||
|
||||
// For exception variables, they are their own definitions (like parameters)
|
||||
GotoTarget::ExceptVariable(except_handler) => {
|
||||
Some(DefinitionsOrTargets::Definitions(vec![
|
||||
ResolvedDefinition::Definition(except_handler.definition(model)),
|
||||
]))
|
||||
Some(vec![ResolvedDefinition::Definition(
|
||||
except_handler.definition(model),
|
||||
)])
|
||||
}
|
||||
|
||||
// For pattern match rest variables, they are their own definitions
|
||||
// Patterns are glorified assignments but we have to look them up by ident
|
||||
// because they're not expressions
|
||||
GotoTarget::PatternMatchRest(pattern_mapping) => {
|
||||
if let Some(rest_name) = &pattern_mapping.rest {
|
||||
let range = rest_name.range;
|
||||
Some(DefinitionsOrTargets::Targets(
|
||||
crate::NavigationTargets::single(NavigationTarget::new(
|
||||
model.file(),
|
||||
range,
|
||||
)),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pattern_mapping.rest.as_ref().map(|name| {
|
||||
definitions_for_name(model, name.as_str(), AnyNodeRef::Identifier(name))
|
||||
})
|
||||
}
|
||||
|
||||
// For pattern match as names, they are their own definitions
|
||||
GotoTarget::PatternMatchAsName(pattern_as) => {
|
||||
if let Some(name) = &pattern_as.name {
|
||||
let range = name.range;
|
||||
Some(DefinitionsOrTargets::Targets(
|
||||
crate::NavigationTargets::single(NavigationTarget::new(
|
||||
model.file(),
|
||||
range,
|
||||
)),
|
||||
GotoTarget::PatternMatchAsName(pattern_as) => pattern_as.name.as_ref().map(|name| {
|
||||
definitions_for_name(model, name.as_str(), AnyNodeRef::Identifier(name))
|
||||
}),
|
||||
|
||||
GotoTarget::PatternKeywordArgument(pattern_keyword) => {
|
||||
let name = &pattern_keyword.attr;
|
||||
Some(definitions_for_name(
|
||||
model,
|
||||
name.as_str(),
|
||||
AnyNodeRef::Identifier(name),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
GotoTarget::PatternMatchStarName(pattern_star) => {
|
||||
pattern_star.name.as_ref().map(|name| {
|
||||
definitions_for_name(model, name.as_str(), AnyNodeRef::Identifier(name))
|
||||
})
|
||||
}
|
||||
|
||||
// For callables, both the definition of the callable and the actual function impl are relevant.
|
||||
|
|
@ -516,7 +483,7 @@ impl GotoTarget<'_> {
|
|||
if definitions.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(DefinitionsOrTargets::Definitions(definitions))
|
||||
Some(definitions)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -524,14 +491,14 @@ impl GotoTarget<'_> {
|
|||
let (definitions, _) =
|
||||
ty_python_semantic::definitions_for_bin_op(model, expression)?;
|
||||
|
||||
Some(DefinitionsOrTargets::Definitions(definitions))
|
||||
Some(definitions)
|
||||
}
|
||||
|
||||
GotoTarget::UnaryOp { expression, .. } => {
|
||||
let (definitions, _) =
|
||||
ty_python_semantic::definitions_for_unary_op(model, expression)?;
|
||||
|
||||
Some(DefinitionsOrTargets::Definitions(definitions))
|
||||
Some(definitions)
|
||||
}
|
||||
|
||||
// String annotations sub-expressions require us to recurse into the sub-AST
|
||||
|
|
@ -545,23 +512,47 @@ impl GotoTarget<'_> {
|
|||
.node()
|
||||
.as_expr_ref()?;
|
||||
definitions_for_expression(&submodel, subexpr)
|
||||
.map(DefinitionsOrTargets::Definitions)
|
||||
}
|
||||
|
||||
// nonlocal and global are essentially loads, but again they're statements,
|
||||
// so we need to look them up by ident
|
||||
GotoTarget::NonLocal { identifier } | GotoTarget::Globals { identifier } => {
|
||||
Some(DefinitionsOrTargets::Definitions(definitions_for_name(
|
||||
Some(definitions_for_name(
|
||||
model,
|
||||
identifier.as_str(),
|
||||
AnyNodeRef::Identifier(identifier),
|
||||
)))
|
||||
))
|
||||
}
|
||||
|
||||
// TODO: implement these
|
||||
GotoTarget::PatternKeywordArgument(..)
|
||||
| GotoTarget::PatternMatchStarName(..)
|
||||
| GotoTarget::TypeParamTypeVarName(..)
|
||||
| GotoTarget::TypeParamParamSpecName(..)
|
||||
| GotoTarget::TypeParamTypeVarTupleName(..) => None,
|
||||
// These are declarations of sorts, but they're stmts and not exprs, so look up by ident.
|
||||
GotoTarget::TypeParamTypeVarName(type_var) => {
|
||||
let name = &type_var.name;
|
||||
Some(definitions_for_name(
|
||||
model,
|
||||
name.as_str(),
|
||||
AnyNodeRef::Identifier(name),
|
||||
))
|
||||
}
|
||||
|
||||
GotoTarget::TypeParamParamSpecName(name) => {
|
||||
let name = &name.name;
|
||||
Some(definitions_for_name(
|
||||
model,
|
||||
name.as_str(),
|
||||
AnyNodeRef::Identifier(name),
|
||||
))
|
||||
}
|
||||
|
||||
GotoTarget::TypeParamTypeVarTupleName(name) => {
|
||||
let name = &name.name;
|
||||
Some(definitions_for_name(
|
||||
model,
|
||||
name.as_str(),
|
||||
AnyNodeRef::Identifier(name),
|
||||
))
|
||||
}
|
||||
};
|
||||
definitions.map(Definitions)
|
||||
}
|
||||
|
||||
/// Returns the text representation of this goto target.
|
||||
|
|
@ -1050,12 +1041,10 @@ fn definitions_for_module<'db>(
|
|||
model: &SemanticModel<'db>,
|
||||
module: Option<&str>,
|
||||
level: u32,
|
||||
) -> Option<DefinitionsOrTargets<'db>> {
|
||||
) -> Option<Vec<ResolvedDefinition<'db>>> {
|
||||
let module = model.resolve_module(module, level)?;
|
||||
let file = module.file(model.db())?;
|
||||
Some(DefinitionsOrTargets::Definitions(vec![
|
||||
ResolvedDefinition::Module(file),
|
||||
]))
|
||||
Some(vec![ResolvedDefinition::Module(file)])
|
||||
}
|
||||
|
||||
/// Helper function to extract module component information from a dotted module name
|
||||
|
|
|
|||
|
|
@ -1397,6 +1397,486 @@ def function():
|
|||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_match_name_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", a<CURSOR>b]:
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @r#"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:4:22
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
|
|
||||
info: Source
|
||||
--> main.py:4:22
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_match_name_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", ab]:
|
||||
x = a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @r#"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:4:22
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
|
|
||||
info: Source
|
||||
--> main.py:5:17
|
||||
|
|
||||
3 | match command.split():
|
||||
4 | case ["get", ab]:
|
||||
5 | x = ab
|
||||
| ^^
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_match_rest_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", *a<CURSOR>b]:
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @r#"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:4:23
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", *ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
|
|
||||
info: Source
|
||||
--> main.py:4:23
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", *ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_match_rest_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", *ab]:
|
||||
x = a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @r#"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:4:23
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", *ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
|
|
||||
info: Source
|
||||
--> main.py:5:17
|
||||
|
|
||||
3 | match command.split():
|
||||
4 | case ["get", *ab]:
|
||||
5 | x = ab
|
||||
| ^^
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_match_as_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", ("a" | "b") as a<CURSOR>b]:
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @r#"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:4:37
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", ("a" | "b") as ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
|
|
||||
info: Source
|
||||
--> main.py:4:37
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", ("a" | "b") as ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_match_as_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", ("a" | "b") as ab]:
|
||||
x = a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @r#"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:4:37
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", ("a" | "b") as ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
|
|
||||
info: Source
|
||||
--> main.py:5:17
|
||||
|
|
||||
3 | match command.split():
|
||||
4 | case ["get", ("a" | "b") as ab]:
|
||||
5 | x = ab
|
||||
| ^^
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_match_keyword_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Click:
|
||||
__match_args__ = ("position", "button")
|
||||
def __init__(self, pos, btn):
|
||||
self.position: int = pos
|
||||
self.button: str = btn
|
||||
|
||||
def my_func(event: Click):
|
||||
match event:
|
||||
case Click(x, button=a<CURSOR>b):
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @r"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:10:30
|
||||
|
|
||||
8 | def my_func(event: Click):
|
||||
9 | match event:
|
||||
10 | case Click(x, button=ab):
|
||||
| ^^
|
||||
11 | x = ab
|
||||
|
|
||||
info: Source
|
||||
--> main.py:10:30
|
||||
|
|
||||
8 | def my_func(event: Click):
|
||||
9 | match event:
|
||||
10 | case Click(x, button=ab):
|
||||
| ^^
|
||||
11 | x = ab
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_match_keyword_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Click:
|
||||
__match_args__ = ("position", "button")
|
||||
def __init__(self, pos, btn):
|
||||
self.position: int = pos
|
||||
self.button: str = btn
|
||||
|
||||
def my_func(event: Click):
|
||||
match event:
|
||||
case Click(x, button=ab):
|
||||
x = a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @r"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:10:30
|
||||
|
|
||||
8 | def my_func(event: Click):
|
||||
9 | match event:
|
||||
10 | case Click(x, button=ab):
|
||||
| ^^
|
||||
11 | x = ab
|
||||
|
|
||||
info: Source
|
||||
--> main.py:11:17
|
||||
|
|
||||
9 | match event:
|
||||
10 | case Click(x, button=ab):
|
||||
11 | x = ab
|
||||
| ^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_match_class_name() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Click:
|
||||
__match_args__ = ("position", "button")
|
||||
def __init__(self, pos, btn):
|
||||
self.position: int = pos
|
||||
self.button: str = btn
|
||||
|
||||
def my_func(event: Click):
|
||||
match event:
|
||||
case Cl<CURSOR>ick(x, button=ab):
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @r#"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:2:7
|
||||
|
|
||||
2 | class Click:
|
||||
| ^^^^^
|
||||
3 | __match_args__ = ("position", "button")
|
||||
4 | def __init__(self, pos, btn):
|
||||
|
|
||||
info: Source
|
||||
--> main.py:10:14
|
||||
|
|
||||
8 | def my_func(event: Click):
|
||||
9 | match event:
|
||||
10 | case Click(x, button=ab):
|
||||
| ^^^^^
|
||||
11 | x = ab
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_match_class_field_name() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Click:
|
||||
__match_args__ = ("position", "button")
|
||||
def __init__(self, pos, btn):
|
||||
self.position: int = pos
|
||||
self.button: str = btn
|
||||
|
||||
def my_func(event: Click):
|
||||
match event:
|
||||
case Click(x, but<CURSOR>ton=ab):
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @"No goto target found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_typevar_name_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
type Alias1[A<CURSOR>B: int = bool] = tuple[AB, list[AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @r"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:2:13
|
||||
|
|
||||
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
|
||||
| ^^
|
||||
|
|
||||
info: Source
|
||||
--> main.py:2:13
|
||||
|
|
||||
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
|
||||
| ^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_typevar_name_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
type Alias1[AB: int = bool] = tuple[A<CURSOR>B, list[AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @r"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:2:13
|
||||
|
|
||||
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
|
||||
| ^^
|
||||
|
|
||||
info: Source
|
||||
--> main.py:2:37
|
||||
|
|
||||
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
|
||||
| ^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_typevar_spec_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
from typing import Callable
|
||||
type Alias2[**A<CURSOR>B = [int, str]] = Callable[AB, tuple[AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @r"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:3:15
|
||||
|
|
||||
2 | from typing import Callable
|
||||
3 | type Alias2[**AB = [int, str]] = Callable[AB, tuple[AB]]
|
||||
| ^^
|
||||
|
|
||||
info: Source
|
||||
--> main.py:3:15
|
||||
|
|
||||
2 | from typing import Callable
|
||||
3 | type Alias2[**AB = [int, str]] = Callable[AB, tuple[AB]]
|
||||
| ^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_typevar_spec_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
from typing import Callable
|
||||
type Alias2[**AB = [int, str]] = Callable[A<CURSOR>B, tuple[AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @r"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:3:15
|
||||
|
|
||||
2 | from typing import Callable
|
||||
3 | type Alias2[**AB = [int, str]] = Callable[AB, tuple[AB]]
|
||||
| ^^
|
||||
|
|
||||
info: Source
|
||||
--> main.py:3:43
|
||||
|
|
||||
2 | from typing import Callable
|
||||
3 | type Alias2[**AB = [int, str]] = Callable[AB, tuple[AB]]
|
||||
| ^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_typevar_tuple_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
type Alias3[*A<CURSOR>B = ()] = tuple[tuple[*AB], tuple[*AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @r"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:2:14
|
||||
|
|
||||
2 | type Alias3[*AB = ()] = tuple[tuple[*AB], tuple[*AB]]
|
||||
| ^^
|
||||
|
|
||||
info: Source
|
||||
--> main.py:2:14
|
||||
|
|
||||
2 | type Alias3[*AB = ()] = tuple[tuple[*AB], tuple[*AB]]
|
||||
| ^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_typevar_tuple_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
type Alias3[*AB = ()] = tuple[tuple[*A<CURSOR>B], tuple[*AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @r"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:2:14
|
||||
|
|
||||
2 | type Alias3[*AB = ()] = tuple[tuple[*AB], tuple[*AB]]
|
||||
| ^^
|
||||
|
|
||||
info: Source
|
||||
--> main.py:2:38
|
||||
|
|
||||
2 | type Alias3[*AB = ()] = tuple[tuple[*AB], tuple[*AB]]
|
||||
| ^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_property_getter_setter() {
|
||||
let test = cursor_test(
|
||||
|
|
|
|||
|
|
@ -898,6 +898,552 @@ cls = MyClass
|
|||
assert_snapshot!(test.references(), @"No references found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn references_match_name_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", a<CURSOR>b]:
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.references(), @r#"
|
||||
info[references]: Reference 1
|
||||
--> main.py:4:22
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
|
|
||||
|
||||
info[references]: Reference 2
|
||||
--> main.py:5:17
|
||||
|
|
||||
3 | match command.split():
|
||||
4 | case ["get", ab]:
|
||||
5 | x = ab
|
||||
| ^^
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn references_match_name_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", ab]:
|
||||
x = a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.references(), @r#"
|
||||
info[references]: Reference 1
|
||||
--> main.py:4:22
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
|
|
||||
|
||||
info[references]: Reference 2
|
||||
--> main.py:5:17
|
||||
|
|
||||
3 | match command.split():
|
||||
4 | case ["get", ab]:
|
||||
5 | x = ab
|
||||
| ^^
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn references_match_rest_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", *a<CURSOR>b]:
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.references(), @r#"
|
||||
info[references]: Reference 1
|
||||
--> main.py:4:23
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", *ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
|
|
||||
|
||||
info[references]: Reference 2
|
||||
--> main.py:5:17
|
||||
|
|
||||
3 | match command.split():
|
||||
4 | case ["get", *ab]:
|
||||
5 | x = ab
|
||||
| ^^
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn references_match_rest_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", *ab]:
|
||||
x = a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.references(), @r#"
|
||||
info[references]: Reference 1
|
||||
--> main.py:4:23
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", *ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
|
|
||||
|
||||
info[references]: Reference 2
|
||||
--> main.py:5:17
|
||||
|
|
||||
3 | match command.split():
|
||||
4 | case ["get", *ab]:
|
||||
5 | x = ab
|
||||
| ^^
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn references_match_as_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", ("a" | "b") as a<CURSOR>b]:
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.references(), @r#"
|
||||
info[references]: Reference 1
|
||||
--> main.py:4:37
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", ("a" | "b") as ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
|
|
||||
|
||||
info[references]: Reference 2
|
||||
--> main.py:5:17
|
||||
|
|
||||
3 | match command.split():
|
||||
4 | case ["get", ("a" | "b") as ab]:
|
||||
5 | x = ab
|
||||
| ^^
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn references_match_as_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", ("a" | "b") as ab]:
|
||||
x = a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.references(), @r#"
|
||||
info[references]: Reference 1
|
||||
--> main.py:4:37
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", ("a" | "b") as ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
|
|
||||
|
||||
info[references]: Reference 2
|
||||
--> main.py:5:17
|
||||
|
|
||||
3 | match command.split():
|
||||
4 | case ["get", ("a" | "b") as ab]:
|
||||
5 | x = ab
|
||||
| ^^
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn references_match_keyword_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Click:
|
||||
__match_args__ = ("position", "button")
|
||||
def __init__(self, pos, btn):
|
||||
self.position: int = pos
|
||||
self.button: str = btn
|
||||
|
||||
def my_func(event: Click):
|
||||
match event:
|
||||
case Click(x, button=a<CURSOR>b):
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.references(), @r"
|
||||
info[references]: Reference 1
|
||||
--> main.py:10:30
|
||||
|
|
||||
8 | def my_func(event: Click):
|
||||
9 | match event:
|
||||
10 | case Click(x, button=ab):
|
||||
| ^^
|
||||
11 | x = ab
|
||||
|
|
||||
|
||||
info[references]: Reference 2
|
||||
--> main.py:11:17
|
||||
|
|
||||
9 | match event:
|
||||
10 | case Click(x, button=ab):
|
||||
11 | x = ab
|
||||
| ^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn references_match_keyword_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Click:
|
||||
__match_args__ = ("position", "button")
|
||||
def __init__(self, pos, btn):
|
||||
self.position: int = pos
|
||||
self.button: str = btn
|
||||
|
||||
def my_func(event: Click):
|
||||
match event:
|
||||
case Click(x, button=ab):
|
||||
x = a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.references(), @r"
|
||||
info[references]: Reference 1
|
||||
--> main.py:10:30
|
||||
|
|
||||
8 | def my_func(event: Click):
|
||||
9 | match event:
|
||||
10 | case Click(x, button=ab):
|
||||
| ^^
|
||||
11 | x = ab
|
||||
|
|
||||
|
||||
info[references]: Reference 2
|
||||
--> main.py:11:17
|
||||
|
|
||||
9 | match event:
|
||||
10 | case Click(x, button=ab):
|
||||
11 | x = ab
|
||||
| ^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn references_match_class_name() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Click:
|
||||
__match_args__ = ("position", "button")
|
||||
def __init__(self, pos, btn):
|
||||
self.position: int = pos
|
||||
self.button: str = btn
|
||||
|
||||
def my_func(event: Click):
|
||||
match event:
|
||||
case Cl<CURSOR>ick(x, button=ab):
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.references(), @r#"
|
||||
info[references]: Reference 1
|
||||
--> main.py:2:7
|
||||
|
|
||||
2 | class Click:
|
||||
| ^^^^^
|
||||
3 | __match_args__ = ("position", "button")
|
||||
4 | def __init__(self, pos, btn):
|
||||
|
|
||||
|
||||
info[references]: Reference 2
|
||||
--> main.py:8:20
|
||||
|
|
||||
6 | self.button: str = btn
|
||||
7 |
|
||||
8 | def my_func(event: Click):
|
||||
| ^^^^^
|
||||
9 | match event:
|
||||
10 | case Click(x, button=ab):
|
||||
|
|
||||
|
||||
info[references]: Reference 3
|
||||
--> main.py:10:14
|
||||
|
|
||||
8 | def my_func(event: Click):
|
||||
9 | match event:
|
||||
10 | case Click(x, button=ab):
|
||||
| ^^^^^
|
||||
11 | x = ab
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn references_match_class_field_name() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Click:
|
||||
__match_args__ = ("position", "button")
|
||||
def __init__(self, pos, btn):
|
||||
self.position: int = pos
|
||||
self.button: str = btn
|
||||
|
||||
def my_func(event: Click):
|
||||
match event:
|
||||
case Click(x, but<CURSOR>ton=ab):
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.references(), @"No references found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn references_typevar_name_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
type Alias1[A<CURSOR>B: int = bool] = tuple[AB, list[AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.references(), @r"
|
||||
info[references]: Reference 1
|
||||
--> main.py:2:13
|
||||
|
|
||||
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
|
||||
| ^^
|
||||
|
|
||||
|
||||
info[references]: Reference 2
|
||||
--> main.py:2:37
|
||||
|
|
||||
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
|
||||
| ^^
|
||||
|
|
||||
|
||||
info[references]: Reference 3
|
||||
--> main.py:2:46
|
||||
|
|
||||
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
|
||||
| ^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn references_typevar_name_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
type Alias1[AB: int = bool] = tuple[A<CURSOR>B, list[AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.references(), @r"
|
||||
info[references]: Reference 1
|
||||
--> main.py:2:13
|
||||
|
|
||||
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
|
||||
| ^^
|
||||
|
|
||||
|
||||
info[references]: Reference 2
|
||||
--> main.py:2:37
|
||||
|
|
||||
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
|
||||
| ^^
|
||||
|
|
||||
|
||||
info[references]: Reference 3
|
||||
--> main.py:2:46
|
||||
|
|
||||
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
|
||||
| ^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn references_typevar_spec_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
from typing import Callable
|
||||
type Alias2[**A<CURSOR>B = [int, str]] = Callable[AB, tuple[AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.references(), @r"
|
||||
info[references]: Reference 1
|
||||
--> main.py:3:15
|
||||
|
|
||||
2 | from typing import Callable
|
||||
3 | type Alias2[**AB = [int, str]] = Callable[AB, tuple[AB]]
|
||||
| ^^
|
||||
|
|
||||
|
||||
info[references]: Reference 2
|
||||
--> main.py:3:43
|
||||
|
|
||||
2 | from typing import Callable
|
||||
3 | type Alias2[**AB = [int, str]] = Callable[AB, tuple[AB]]
|
||||
| ^^
|
||||
|
|
||||
|
||||
info[references]: Reference 3
|
||||
--> main.py:3:53
|
||||
|
|
||||
2 | from typing import Callable
|
||||
3 | type Alias2[**AB = [int, str]] = Callable[AB, tuple[AB]]
|
||||
| ^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn references_typevar_spec_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
from typing import Callable
|
||||
type Alias2[**AB = [int, str]] = Callable[A<CURSOR>B, tuple[AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.references(), @r"
|
||||
info[references]: Reference 1
|
||||
--> main.py:3:15
|
||||
|
|
||||
2 | from typing import Callable
|
||||
3 | type Alias2[**AB = [int, str]] = Callable[AB, tuple[AB]]
|
||||
| ^^
|
||||
|
|
||||
|
||||
info[references]: Reference 2
|
||||
--> main.py:3:43
|
||||
|
|
||||
2 | from typing import Callable
|
||||
3 | type Alias2[**AB = [int, str]] = Callable[AB, tuple[AB]]
|
||||
| ^^
|
||||
|
|
||||
|
||||
info[references]: Reference 3
|
||||
--> main.py:3:53
|
||||
|
|
||||
2 | from typing import Callable
|
||||
3 | type Alias2[**AB = [int, str]] = Callable[AB, tuple[AB]]
|
||||
| ^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn references_typevar_tuple_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
type Alias3[*A<CURSOR>B = ()] = tuple[tuple[*AB], tuple[*AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.references(), @r"
|
||||
info[references]: Reference 1
|
||||
--> main.py:2:14
|
||||
|
|
||||
2 | type Alias3[*AB = ()] = tuple[tuple[*AB], tuple[*AB]]
|
||||
| ^^
|
||||
|
|
||||
|
||||
info[references]: Reference 2
|
||||
--> main.py:2:38
|
||||
|
|
||||
2 | type Alias3[*AB = ()] = tuple[tuple[*AB], tuple[*AB]]
|
||||
| ^^
|
||||
|
|
||||
|
||||
info[references]: Reference 3
|
||||
--> main.py:2:50
|
||||
|
|
||||
2 | type Alias3[*AB = ()] = tuple[tuple[*AB], tuple[*AB]]
|
||||
| ^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn references_typevar_tuple_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
type Alias3[*AB = ()] = tuple[tuple[*A<CURSOR>B], tuple[*AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.references(), @r"
|
||||
info[references]: Reference 1
|
||||
--> main.py:2:14
|
||||
|
|
||||
2 | type Alias3[*AB = ()] = tuple[tuple[*AB], tuple[*AB]]
|
||||
| ^^
|
||||
|
|
||||
|
||||
info[references]: Reference 2
|
||||
--> main.py:2:38
|
||||
|
|
||||
2 | type Alias3[*AB = ()] = tuple[tuple[*AB], tuple[*AB]]
|
||||
| ^^
|
||||
|
|
||||
|
||||
info[references]: Reference 3
|
||||
--> main.py:2:50
|
||||
|
|
||||
2 | type Alias3[*AB = ()] = tuple[tuple[*AB], tuple[*AB]]
|
||||
| ^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_file_function_references() {
|
||||
let test = CursorTest::builder()
|
||||
|
|
|
|||
|
|
@ -964,6 +964,282 @@ mod tests {
|
|||
assert_snapshot!(test.goto_type_definition(), @"No goto target found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_type_match_name_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", a<CURSOR>b]:
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @"No goto target found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_type_match_name_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", ab]:
|
||||
x = a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @"No type definitions found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_type_match_rest_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", *a<CURSOR>b]:
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @"No goto target found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_type_match_rest_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", *ab]:
|
||||
x = a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @"No type definitions found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_type_match_as_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", ("a" | "b") as a<CURSOR>b]:
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @"No goto target found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_type_match_as_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", ("a" | "b") as ab]:
|
||||
x = a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @"No type definitions found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_type_match_keyword_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Click:
|
||||
__match_args__ = ("position", "button")
|
||||
def __init__(self, pos, btn):
|
||||
self.position: int = pos
|
||||
self.button: str = btn
|
||||
|
||||
def my_func(event: Click):
|
||||
match event:
|
||||
case Click(x, button=a<CURSOR>b):
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @"No goto target found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_type_match_keyword_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Click:
|
||||
__match_args__ = ("position", "button")
|
||||
def __init__(self, pos, btn):
|
||||
self.position: int = pos
|
||||
self.button: str = btn
|
||||
|
||||
def my_func(event: Click):
|
||||
match event:
|
||||
case Click(x, button=ab):
|
||||
x = a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @"No type definitions found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_type_match_class_name() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Click:
|
||||
__match_args__ = ("position", "button")
|
||||
def __init__(self, pos, btn):
|
||||
self.position: int = pos
|
||||
self.button: str = btn
|
||||
|
||||
def my_func(event: Click):
|
||||
match event:
|
||||
case Cl<CURSOR>ick(x, button=ab):
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @r#"
|
||||
info[goto-type-definition]: Type definition
|
||||
--> main.py:2:7
|
||||
|
|
||||
2 | class Click:
|
||||
| ^^^^^
|
||||
3 | __match_args__ = ("position", "button")
|
||||
4 | def __init__(self, pos, btn):
|
||||
|
|
||||
info: Source
|
||||
--> main.py:10:14
|
||||
|
|
||||
8 | def my_func(event: Click):
|
||||
9 | match event:
|
||||
10 | case Click(x, button=ab):
|
||||
| ^^^^^
|
||||
11 | x = ab
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_type_match_class_field_name() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Click:
|
||||
__match_args__ = ("position", "button")
|
||||
def __init__(self, pos, btn):
|
||||
self.position: int = pos
|
||||
self.button: str = btn
|
||||
|
||||
def my_func(event: Click):
|
||||
match event:
|
||||
case Click(x, but<CURSOR>ton=ab):
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @"No goto target found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_type_typevar_name_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
type Alias1[A<CURSOR>B: int = bool] = tuple[AB, list[AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @r"
|
||||
info[goto-type-definition]: Type definition
|
||||
--> main.py:2:13
|
||||
|
|
||||
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
|
||||
| ^^
|
||||
|
|
||||
info: Source
|
||||
--> main.py:2:13
|
||||
|
|
||||
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
|
||||
| ^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_type_typevar_name_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
type Alias1[AB: int = bool] = tuple[A<CURSOR>B, list[AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @r"
|
||||
info[goto-type-definition]: Type definition
|
||||
--> main.py:2:13
|
||||
|
|
||||
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
|
||||
| ^^
|
||||
|
|
||||
info: Source
|
||||
--> main.py:2:37
|
||||
|
|
||||
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
|
||||
| ^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_type_typevar_spec_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
from typing import Callable
|
||||
type Alias2[**A<CURSOR>B = [int, str]] = Callable[AB, tuple[AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @"No goto target found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_type_typevar_spec_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
from typing import Callable
|
||||
type Alias2[**AB = [int, str]] = Callable[A<CURSOR>B, tuple[AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @"No type definitions found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_type_typevar_tuple_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
type Alias3[*A<CURSOR>B = ()] = tuple[tuple[*AB], tuple[*AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @"No goto target found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_type_typevar_tuple_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
type Alias3[*AB = ()] = tuple[tuple[*A<CURSOR>B], tuple[*AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @"No type definitions found");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_type_on_keyword_argument() {
|
||||
let test = cursor_test(
|
||||
|
|
|
|||
|
|
@ -1759,6 +1759,398 @@ def function():
|
|||
assert_snapshot!(test.hover(), @"Hover provided no content");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_match_name_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", a<CURSOR>b]:
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @"Hover provided no content");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_match_name_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", ab]:
|
||||
x = a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r#"
|
||||
@Todo
|
||||
---------------------------------------------
|
||||
```python
|
||||
@Todo
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
--> main.py:5:17
|
||||
|
|
||||
3 | match command.split():
|
||||
4 | case ["get", ab]:
|
||||
5 | x = ab
|
||||
| ^-
|
||||
| ||
|
||||
| |Cursor offset
|
||||
| source
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_match_rest_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", *a<CURSOR>b]:
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @"Hover provided no content");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_match_rest_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", *ab]:
|
||||
x = a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r#"
|
||||
@Todo
|
||||
---------------------------------------------
|
||||
```python
|
||||
@Todo
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
--> main.py:5:17
|
||||
|
|
||||
3 | match command.split():
|
||||
4 | case ["get", *ab]:
|
||||
5 | x = ab
|
||||
| ^-
|
||||
| ||
|
||||
| |Cursor offset
|
||||
| source
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_match_as_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", ("a" | "b") as a<CURSOR>b]:
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @"Hover provided no content");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_match_as_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", ("a" | "b") as ab]:
|
||||
x = a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r#"
|
||||
@Todo
|
||||
---------------------------------------------
|
||||
```python
|
||||
@Todo
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
--> main.py:5:17
|
||||
|
|
||||
3 | match command.split():
|
||||
4 | case ["get", ("a" | "b") as ab]:
|
||||
5 | x = ab
|
||||
| ^-
|
||||
| ||
|
||||
| |Cursor offset
|
||||
| source
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_match_keyword_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Click:
|
||||
__match_args__ = ("position", "button")
|
||||
def __init__(self, pos, btn):
|
||||
self.position: int = pos
|
||||
self.button: str = btn
|
||||
|
||||
def my_func(event: Click):
|
||||
match event:
|
||||
case Click(x, button=a<CURSOR>b):
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @"Hover provided no content");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_match_keyword_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Click:
|
||||
__match_args__ = ("position", "button")
|
||||
def __init__(self, pos, btn):
|
||||
self.position: int = pos
|
||||
self.button: str = btn
|
||||
|
||||
def my_func(event: Click):
|
||||
match event:
|
||||
case Click(x, button=ab):
|
||||
x = a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
@Todo
|
||||
---------------------------------------------
|
||||
```python
|
||||
@Todo
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
--> main.py:11:17
|
||||
|
|
||||
9 | match event:
|
||||
10 | case Click(x, button=ab):
|
||||
11 | x = ab
|
||||
| ^-
|
||||
| ||
|
||||
| |Cursor offset
|
||||
| source
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_match_class_name() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Click:
|
||||
__match_args__ = ("position", "button")
|
||||
def __init__(self, pos, btn):
|
||||
self.position: int = pos
|
||||
self.button: str = btn
|
||||
|
||||
def my_func(event: Click):
|
||||
match event:
|
||||
case Cl<CURSOR>ick(x, button=ab):
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
<class 'Click'>
|
||||
---------------------------------------------
|
||||
```python
|
||||
<class 'Click'>
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
--> main.py:10:14
|
||||
|
|
||||
8 | def my_func(event: Click):
|
||||
9 | match event:
|
||||
10 | case Click(x, button=ab):
|
||||
| ^^-^^
|
||||
| | |
|
||||
| | Cursor offset
|
||||
| source
|
||||
11 | x = ab
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_match_class_field_name() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Click:
|
||||
__match_args__ = ("position", "button")
|
||||
def __init__(self, pos, btn):
|
||||
self.position: int = pos
|
||||
self.button: str = btn
|
||||
|
||||
def my_func(event: Click):
|
||||
match event:
|
||||
case Click(x, but<CURSOR>ton=ab):
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @"Hover provided no content");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_typevar_name_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
type Alias1[A<CURSOR>B: int = bool] = tuple[AB, list[AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
AB@Alias1 (invariant)
|
||||
---------------------------------------------
|
||||
```python
|
||||
AB@Alias1 (invariant)
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
--> main.py:2:13
|
||||
|
|
||||
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
|
||||
| ^-
|
||||
| ||
|
||||
| |Cursor offset
|
||||
| source
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_typevar_name_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
type Alias1[AB: int = bool] = tuple[A<CURSOR>B, list[AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
AB@Alias1 (invariant)
|
||||
---------------------------------------------
|
||||
```python
|
||||
AB@Alias1 (invariant)
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
--> main.py:2:37
|
||||
|
|
||||
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
|
||||
| ^-
|
||||
| ||
|
||||
| |Cursor offset
|
||||
| source
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_typevar_spec_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
from typing import Callable
|
||||
type Alias2[**A<CURSOR>B = [int, str]] = Callable[AB, tuple[AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @"Hover provided no content");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_typevar_spec_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
from typing import Callable
|
||||
type Alias2[**AB = [int, str]] = Callable[A<CURSOR>B, tuple[AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
(
|
||||
...
|
||||
) -> tuple[typing.ParamSpec]
|
||||
---------------------------------------------
|
||||
```python
|
||||
(
|
||||
...
|
||||
) -> tuple[typing.ParamSpec]
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
--> main.py:3:43
|
||||
|
|
||||
2 | from typing import Callable
|
||||
3 | type Alias2[**AB = [int, str]] = Callable[AB, tuple[AB]]
|
||||
| ^-
|
||||
| ||
|
||||
| |Cursor offset
|
||||
| source
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_typevar_tuple_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
type Alias3[*A<CURSOR>B = ()] = tuple[tuple[*AB], tuple[*AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @"Hover provided no content");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_typevar_tuple_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
type Alias3[*AB = ()] = tuple[tuple[*A<CURSOR>B], tuple[*AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
@Todo
|
||||
---------------------------------------------
|
||||
```python
|
||||
@Todo
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
--> main.py:2:38
|
||||
|
|
||||
2 | type Alias3[*AB = ()] = tuple[tuple[*AB], tuple[*AB]]
|
||||
| ^-
|
||||
| ||
|
||||
| |Cursor offset
|
||||
| source
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_module_import() {
|
||||
let mut test = cursor_test(
|
||||
|
|
|
|||
|
|
@ -1946,6 +1946,131 @@ mod tests {
|
|||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_match_name_binding() {
|
||||
let mut test = inlay_hint_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", ab]:
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.inlay_hints(), @r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", ab]:
|
||||
x[: @Todo] = ab
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_match_rest_binding() {
|
||||
let mut test = inlay_hint_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", *ab]:
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.inlay_hints(), @r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", *ab]:
|
||||
x[: @Todo] = ab
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_match_as_binding() {
|
||||
let mut test = inlay_hint_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", ("a" | "b") as ab]:
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.inlay_hints(), @r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", ("a" | "b") as ab]:
|
||||
x[: @Todo] = ab
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_match_keyword_binding() {
|
||||
let mut test = inlay_hint_test(
|
||||
r#"
|
||||
class Click:
|
||||
__match_args__ = ("position", "button")
|
||||
def __init__(self, pos, btn):
|
||||
self.position: int = pos
|
||||
self.button: str = btn
|
||||
|
||||
def my_func(event: Click):
|
||||
match event:
|
||||
case Click(x, button=ab):
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.inlay_hints(), @r#"
|
||||
class Click:
|
||||
__match_args__ = ("position", "button")
|
||||
def __init__(self, pos, btn):
|
||||
self.position: int = pos
|
||||
self.button: str = btn
|
||||
|
||||
def my_func(event: Click):
|
||||
match event:
|
||||
case Click(x, button=ab):
|
||||
x[: @Todo] = ab
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_typevar_name_binding() {
|
||||
let mut test = inlay_hint_test(
|
||||
r#"
|
||||
type Alias1[AB: int = bool] = tuple[AB, list[AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.inlay_hints(), @"type Alias1[AB: int = bool] = tuple[AB, list[AB]]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_typevar_spec_binding() {
|
||||
let mut test = inlay_hint_test(
|
||||
r#"
|
||||
from typing import Callable
|
||||
type Alias2[**AB = [int, str]] = Callable[AB, tuple[AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.inlay_hints(), @r"
|
||||
from typing import Callable
|
||||
type Alias2[**AB = [int, str]] = Callable[AB, tuple[AB]]
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_typevar_tuple_binding() {
|
||||
let mut test = inlay_hint_test(
|
||||
r#"
|
||||
type Alias3[*AB = ()] = tuple[tuple[*AB], tuple[*AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.inlay_hints(), @"type Alias3[*AB = ()] = tuple[tuple[*AB], tuple[*AB]]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_many_literals() {
|
||||
let mut test = inlay_hint_test(
|
||||
|
|
|
|||
|
|
@ -219,6 +219,11 @@ impl<'a> SourceOrderVisitor<'a> for LocalReferencesFinder<'a> {
|
|||
self.check_identifier_reference(name);
|
||||
}
|
||||
}
|
||||
AnyNodeRef::PatternMatchStar(pattern_star) if self.should_include_declaration() => {
|
||||
if let Some(name) = &pattern_star.name {
|
||||
self.check_identifier_reference(name);
|
||||
}
|
||||
}
|
||||
AnyNodeRef::PatternMatchMapping(pattern_mapping)
|
||||
if self.should_include_declaration() =>
|
||||
{
|
||||
|
|
@ -226,6 +231,15 @@ impl<'a> SourceOrderVisitor<'a> for LocalReferencesFinder<'a> {
|
|||
self.check_identifier_reference(rest_name);
|
||||
}
|
||||
}
|
||||
AnyNodeRef::TypeParamParamSpec(param_spec) if self.should_include_declaration() => {
|
||||
self.check_identifier_reference(¶m_spec.name);
|
||||
}
|
||||
AnyNodeRef::TypeParamTypeVarTuple(param_tuple) if self.should_include_declaration() => {
|
||||
self.check_identifier_reference(¶m_tuple.name);
|
||||
}
|
||||
AnyNodeRef::TypeParamTypeVar(param_var) if self.should_include_declaration() => {
|
||||
self.check_identifier_reference(¶m_var.name);
|
||||
}
|
||||
AnyNodeRef::ExprStringLiteral(string_expr) if self.should_include_declaration() => {
|
||||
// Highlight the sub-AST of a string annotation
|
||||
if let Some((sub_ast, sub_model)) = self.model.enter_string_annotation(string_expr)
|
||||
|
|
|
|||
|
|
@ -495,6 +495,390 @@ class DataProcessor:
|
|||
assert_snapshot!(test.rename("MyNewClass"), @"Cannot rename");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_match_name_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", a<CURSOR>b]:
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.rename("XY"), @r#"
|
||||
info[rename]: Rename symbol (found 2 locations)
|
||||
--> main.py:4:22
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
| --
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_match_name_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", ab]:
|
||||
x = a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.rename("XY"), @r#"
|
||||
info[rename]: Rename symbol (found 2 locations)
|
||||
--> main.py:4:22
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
| --
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_match_rest_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", *a<CURSOR>b]:
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.rename("XY"), @r#"
|
||||
info[rename]: Rename symbol (found 2 locations)
|
||||
--> main.py:4:23
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", *ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
| --
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_match_rest_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", *ab]:
|
||||
x = a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.rename("XY"), @r#"
|
||||
info[rename]: Rename symbol (found 2 locations)
|
||||
--> main.py:4:23
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", *ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
| --
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_match_as_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", ("a" | "b") as a<CURSOR>b]:
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.rename("XY"), @r#"
|
||||
info[rename]: Rename symbol (found 2 locations)
|
||||
--> main.py:4:37
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", ("a" | "b") as ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
| --
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_match_as_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_func(command: str):
|
||||
match command.split():
|
||||
case ["get", ("a" | "b") as ab]:
|
||||
x = a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.rename("XY"), @r#"
|
||||
info[rename]: Rename symbol (found 2 locations)
|
||||
--> main.py:4:37
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", ("a" | "b") as ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
| --
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_match_keyword_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Click:
|
||||
__match_args__ = ("position", "button")
|
||||
def __init__(self, pos, btn):
|
||||
self.position: int = pos
|
||||
self.button: str = btn
|
||||
|
||||
def my_func(event: Click):
|
||||
match event:
|
||||
case Click(x, button=a<CURSOR>b):
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.rename("XY"), @r"
|
||||
info[rename]: Rename symbol (found 2 locations)
|
||||
--> main.py:10:30
|
||||
|
|
||||
8 | def my_func(event: Click):
|
||||
9 | match event:
|
||||
10 | case Click(x, button=ab):
|
||||
| ^^
|
||||
11 | x = ab
|
||||
| --
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_match_keyword_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Click:
|
||||
__match_args__ = ("position", "button")
|
||||
def __init__(self, pos, btn):
|
||||
self.position: int = pos
|
||||
self.button: str = btn
|
||||
|
||||
def my_func(event: Click):
|
||||
match event:
|
||||
case Click(x, button=ab):
|
||||
x = a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.rename("XY"), @r"
|
||||
info[rename]: Rename symbol (found 2 locations)
|
||||
--> main.py:10:30
|
||||
|
|
||||
8 | def my_func(event: Click):
|
||||
9 | match event:
|
||||
10 | case Click(x, button=ab):
|
||||
| ^^
|
||||
11 | x = ab
|
||||
| --
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_match_class_name() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Click:
|
||||
__match_args__ = ("position", "button")
|
||||
def __init__(self, pos, btn):
|
||||
self.position: int = pos
|
||||
self.button: str = btn
|
||||
|
||||
def my_func(event: Click):
|
||||
match event:
|
||||
case Cl<CURSOR>ick(x, button=ab):
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.rename("XY"), @r#"
|
||||
info[rename]: Rename symbol (found 3 locations)
|
||||
--> main.py:2:7
|
||||
|
|
||||
2 | class Click:
|
||||
| ^^^^^
|
||||
3 | __match_args__ = ("position", "button")
|
||||
4 | def __init__(self, pos, btn):
|
||||
|
|
||||
::: main.py:8:20
|
||||
|
|
||||
6 | self.button: str = btn
|
||||
7 |
|
||||
8 | def my_func(event: Click):
|
||||
| -----
|
||||
9 | match event:
|
||||
10 | case Click(x, button=ab):
|
||||
| -----
|
||||
11 | x = ab
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_match_class_field_name() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
class Click:
|
||||
__match_args__ = ("position", "button")
|
||||
def __init__(self, pos, btn):
|
||||
self.position: int = pos
|
||||
self.button: str = btn
|
||||
|
||||
def my_func(event: Click):
|
||||
match event:
|
||||
case Click(x, but<CURSOR>ton=ab):
|
||||
x = ab
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.rename("XY"), @"Cannot rename");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_typevar_name_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
type Alias1[A<CURSOR>B: int = bool] = tuple[AB, list[AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.rename("XY"), @r"
|
||||
info[rename]: Rename symbol (found 3 locations)
|
||||
--> main.py:2:13
|
||||
|
|
||||
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
|
||||
| ^^ -- --
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_typevar_name_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
type Alias1[AB: int = bool] = tuple[A<CURSOR>B, list[AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.rename("XY"), @r"
|
||||
info[rename]: Rename symbol (found 3 locations)
|
||||
--> main.py:2:13
|
||||
|
|
||||
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
|
||||
| ^^ -- --
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_typevar_spec_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
from typing import Callable
|
||||
type Alias2[**A<CURSOR>B = [int, str]] = Callable[AB, tuple[AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.rename("XY"), @r"
|
||||
info[rename]: Rename symbol (found 3 locations)
|
||||
--> main.py:3:15
|
||||
|
|
||||
2 | from typing import Callable
|
||||
3 | type Alias2[**AB = [int, str]] = Callable[AB, tuple[AB]]
|
||||
| ^^ -- --
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_typevar_spec_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
from typing import Callable
|
||||
type Alias2[**AB = [int, str]] = Callable[A<CURSOR>B, tuple[AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.rename("XY"), @r"
|
||||
info[rename]: Rename symbol (found 3 locations)
|
||||
--> main.py:3:15
|
||||
|
|
||||
2 | from typing import Callable
|
||||
3 | type Alias2[**AB = [int, str]] = Callable[AB, tuple[AB]]
|
||||
| ^^ -- --
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_typevar_tuple_stmt() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
type Alias3[*A<CURSOR>B = ()] = tuple[tuple[*AB], tuple[*AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.rename("XY"), @r"
|
||||
info[rename]: Rename symbol (found 3 locations)
|
||||
--> main.py:2:14
|
||||
|
|
||||
2 | type Alias3[*AB = ()] = tuple[tuple[*AB], tuple[*AB]]
|
||||
| ^^ -- --
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_typevar_tuple_binding() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
type Alias3[*AB = ()] = tuple[tuple[*A<CURSOR>B], tuple[*AB]]
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.rename("XY"), @r"
|
||||
info[rename]: Rename symbol (found 3 locations)
|
||||
--> main.py:2:14
|
||||
|
|
||||
2 | type Alias3[*AB = ()] = tuple[tuple[*AB], tuple[*AB]]
|
||||
| ^^ -- --
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cannot_rename_import_module_component() {
|
||||
// Test that we cannot rename parts of module names in import statements
|
||||
|
|
|
|||
|
|
@ -1060,6 +1060,16 @@ impl SourceOrderVisitor<'_> for SemanticTokenVisitor<'_> {
|
|||
);
|
||||
}
|
||||
}
|
||||
ast::Pattern::MatchStar(pattern_star) => {
|
||||
// Just the one ident here
|
||||
if let Some(rest_name) = &pattern_star.name {
|
||||
self.add_token(
|
||||
rest_name.range(),
|
||||
SemanticTokenType::Variable,
|
||||
SemanticTokenModifier::empty(),
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// For all other pattern types, use the default walker
|
||||
ruff_python_ast::visitor::source_order::walk_pattern(self, pattern);
|
||||
|
|
@ -2485,6 +2495,7 @@ def process_data(data):
|
|||
"rest" @ 154..158: Variable
|
||||
"person" @ 181..187: Variable
|
||||
"first" @ 202..207: Variable
|
||||
"remaining" @ 210..219: Variable
|
||||
"sequence" @ 224..232: Variable
|
||||
"print" @ 246..251: Function
|
||||
"First: " @ 254..261: String
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
//! and overloads.
|
||||
|
||||
use crate::docstring::Docstring;
|
||||
use crate::goto::DefinitionsOrTargets;
|
||||
use crate::goto::Definitions;
|
||||
use crate::{Db, find_node::covering_node};
|
||||
use ruff_db::files::File;
|
||||
use ruff_db::parsed::parsed_module;
|
||||
|
|
@ -214,8 +214,7 @@ fn get_callable_documentation(
|
|||
db: &dyn crate::Db,
|
||||
definition: Option<Definition>,
|
||||
) -> Option<Docstring> {
|
||||
DefinitionsOrTargets::Definitions(vec![ResolvedDefinition::Definition(definition?)])
|
||||
.docstring(db)
|
||||
Definitions(vec![ResolvedDefinition::Definition(definition?)]).docstring(db)
|
||||
}
|
||||
|
||||
/// Create `ParameterDetails` objects from parameter label offsets.
|
||||
|
|
|
|||
|
|
@ -1064,6 +1064,8 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
|
|||
..
|
||||
}) => (name, &None, default),
|
||||
};
|
||||
self.scopes_by_expression
|
||||
.record_expression(name, self.current_scope());
|
||||
let symbol = self.add_symbol(name.id.clone());
|
||||
// TODO create Definition for PEP 695 typevars
|
||||
// note that the "bound" on the typevar is a totally different thing than whether
|
||||
|
|
|
|||
Loading…
Reference in New Issue