Compare commits
4 Commits
david/embe
...
gankra/got
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97af9d8466 | ||
|
|
5cc6762c23 | ||
|
|
1bee527cb2 | ||
|
|
d7b7b835e1 |
@@ -1,12 +1,13 @@
|
|||||||
use crate::find_node::covering_node;
|
use crate::find_node::covering_node;
|
||||||
use crate::{Db, HasNavigationTargets, NavigationTargets, RangedValue};
|
use crate::{Db, HasNavigationTargets, NavigationTarget, NavigationTargets, RangedValue};
|
||||||
use ruff_db::files::{File, FileRange};
|
use ruff_db::files::{File, FileRange};
|
||||||
use ruff_db::parsed::{ParsedModuleRef, parsed_module};
|
use ruff_db::parsed::{ParsedModuleRef, parsed_module};
|
||||||
use ruff_python_ast::{self as ast, AnyNodeRef};
|
use ruff_python_ast::{self as ast, AnyNodeRef};
|
||||||
use ruff_python_parser::TokenKind;
|
use ruff_python_parser::TokenKind;
|
||||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||||
|
use ty_python_semantic::semantic_index::definition::Definition;
|
||||||
use ty_python_semantic::types::Type;
|
use ty_python_semantic::types::Type;
|
||||||
use ty_python_semantic::{HasType, SemanticModel};
|
use ty_python_semantic::{HasDefinition, HasType, SemanticModel};
|
||||||
|
|
||||||
pub fn goto_type_definition(
|
pub fn goto_type_definition(
|
||||||
db: &dyn Db,
|
db: &dyn Db,
|
||||||
@@ -29,6 +30,35 @@ pub fn goto_type_definition(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn goto_definition(
|
||||||
|
db: &dyn Db,
|
||||||
|
file: File,
|
||||||
|
offset: TextSize,
|
||||||
|
) -> Option<RangedValue<NavigationTargets>> {
|
||||||
|
let module = parsed_module(db, file).load(db);
|
||||||
|
let goto_target = find_goto_target(&module, offset)?;
|
||||||
|
|
||||||
|
let model = SemanticModel::new(db, file);
|
||||||
|
let definitions = goto_target.definitions(&model)?;
|
||||||
|
|
||||||
|
tracing::debug!("Definitions of covering node is found");
|
||||||
|
|
||||||
|
let targets = definitions.into_iter().map(|definition| {
|
||||||
|
let full_range = definition.full_range(db, &module);
|
||||||
|
|
||||||
|
NavigationTarget {
|
||||||
|
file: full_range.file(),
|
||||||
|
focus_range: definition.focus_range(db, &module).range(),
|
||||||
|
full_range: full_range.range(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(RangedValue {
|
||||||
|
range: FileRange::new(file, goto_target.range()),
|
||||||
|
value: NavigationTargets::unique(targets),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub(crate) enum GotoTarget<'a> {
|
pub(crate) enum GotoTarget<'a> {
|
||||||
Expression(ast::ExprRef<'a>),
|
Expression(ast::ExprRef<'a>),
|
||||||
@@ -154,6 +184,16 @@ impl GotoTarget<'_> {
|
|||||||
|
|
||||||
Some(ty)
|
Some(ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn definitions<'db>(
|
||||||
|
self,
|
||||||
|
model: &SemanticModel<'db>,
|
||||||
|
) -> Option<Vec<Definition<'db>>> {
|
||||||
|
match self {
|
||||||
|
GotoTarget::Expression(expr_ref) => expr_ref.definitions(model),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ranged for GotoTarget<'_> {
|
impl Ranged for GotoTarget<'_> {
|
||||||
@@ -254,7 +294,7 @@ pub(crate) fn find_goto_target(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::tests::{CursorTest, IntoDiagnostic, cursor_test};
|
use crate::tests::{CursorTest, IntoDiagnostic, cursor_test};
|
||||||
use crate::{NavigationTarget, goto_type_definition};
|
use crate::{NavigationTarget, goto_definition, goto_type_definition};
|
||||||
use insta::assert_snapshot;
|
use insta::assert_snapshot;
|
||||||
use ruff_db::diagnostic::{
|
use ruff_db::diagnostic::{
|
||||||
Annotation, Diagnostic, DiagnosticId, LintName, Severity, Span, SubDiagnostic,
|
Annotation, Diagnostic, DiagnosticId, LintName, Severity, Span, SubDiagnostic,
|
||||||
@@ -828,6 +868,516 @@ f(**kwargs<CURSOR>)
|
|||||||
");
|
");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_function_call() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
def ab(a, b): ...
|
||||||
|
|
||||||
|
a<CURSOR>b(1, 2)
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.goto_definition(), @r"
|
||||||
|
info[goto-type-definition]: Type definition
|
||||||
|
--> main.py:2:17
|
||||||
|
|
|
||||||
|
2 | def ab(a, b): ...
|
||||||
|
| ^^
|
||||||
|
3 |
|
||||||
|
4 | ab(1, 2)
|
||||||
|
|
|
||||||
|
info: Source
|
||||||
|
--> main.py:4:13
|
||||||
|
|
|
||||||
|
2 | def ab(a, b): ...
|
||||||
|
3 |
|
||||||
|
4 | ab(1, 2)
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_local_load() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
ab = 1
|
||||||
|
print(a<CURSOR>b)
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.goto_definition(), @r"
|
||||||
|
info[goto-type-definition]: Type definition
|
||||||
|
--> main.py:2:13
|
||||||
|
|
|
||||||
|
2 | ab = 1
|
||||||
|
| ^^
|
||||||
|
3 | print(ab)
|
||||||
|
|
|
||||||
|
info: Source
|
||||||
|
--> main.py:3:19
|
||||||
|
|
|
||||||
|
2 | ab = 1
|
||||||
|
3 | print(ab)
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_local_load_rebind() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
ab = 1
|
||||||
|
ab = 2
|
||||||
|
ab = 3
|
||||||
|
print(a<CURSOR>b)
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.goto_definition(), @r"
|
||||||
|
info[goto-type-definition]: Type definition
|
||||||
|
--> main.py:4:13
|
||||||
|
|
|
||||||
|
2 | ab = 1
|
||||||
|
3 | ab = 2
|
||||||
|
4 | ab = 3
|
||||||
|
| ^^
|
||||||
|
5 | print(ab)
|
||||||
|
|
|
||||||
|
info: Source
|
||||||
|
--> main.py:5:19
|
||||||
|
|
|
||||||
|
3 | ab = 2
|
||||||
|
4 | ab = 3
|
||||||
|
5 | print(ab)
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_local_load_cond_rebind() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
ab = 1
|
||||||
|
if cond:
|
||||||
|
ab = 2
|
||||||
|
print(a<CURSOR>b)
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.goto_definition(), @r"
|
||||||
|
info[goto-type-definition]: Type definition
|
||||||
|
--> main.py:2:13
|
||||||
|
|
|
||||||
|
2 | ab = 1
|
||||||
|
| ^^
|
||||||
|
3 | if cond:
|
||||||
|
4 | ab = 2
|
||||||
|
|
|
||||||
|
info: Source
|
||||||
|
--> main.py:5:19
|
||||||
|
|
|
||||||
|
3 | if cond:
|
||||||
|
4 | ab = 2
|
||||||
|
5 | print(ab)
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
|
||||||
|
info[goto-type-definition]: Type definition
|
||||||
|
--> main.py:4:17
|
||||||
|
|
|
||||||
|
2 | ab = 1
|
||||||
|
3 | if cond:
|
||||||
|
4 | ab = 2
|
||||||
|
| ^^
|
||||||
|
5 | print(ab)
|
||||||
|
|
|
||||||
|
info: Source
|
||||||
|
--> main.py:5:19
|
||||||
|
|
|
||||||
|
3 | if cond:
|
||||||
|
4 | ab = 2
|
||||||
|
5 | print(ab)
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_local_load_exhaustive_bind() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
if cond:
|
||||||
|
ab = 2
|
||||||
|
else:
|
||||||
|
ab = 1
|
||||||
|
print(a<CURSOR>b)
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.goto_definition(), @r"
|
||||||
|
info[goto-type-definition]: Type definition
|
||||||
|
--> main.py:3:17
|
||||||
|
|
|
||||||
|
2 | if cond:
|
||||||
|
3 | ab = 2
|
||||||
|
| ^^
|
||||||
|
4 | else:
|
||||||
|
5 | ab = 1
|
||||||
|
|
|
||||||
|
info: Source
|
||||||
|
--> main.py:6:19
|
||||||
|
|
|
||||||
|
4 | else:
|
||||||
|
5 | ab = 1
|
||||||
|
6 | print(ab)
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
|
||||||
|
info[goto-type-definition]: Type definition
|
||||||
|
--> main.py:5:17
|
||||||
|
|
|
||||||
|
3 | ab = 2
|
||||||
|
4 | else:
|
||||||
|
5 | ab = 1
|
||||||
|
| ^^
|
||||||
|
6 | print(ab)
|
||||||
|
|
|
||||||
|
info: Source
|
||||||
|
--> main.py:6:19
|
||||||
|
|
|
||||||
|
4 | else:
|
||||||
|
5 | ab = 1
|
||||||
|
6 | print(ab)
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_local_load_only_decl() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
ab: int
|
||||||
|
print(a<CURSOR>b)
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.goto_definition(), @"No definitions found");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_local_load_exhaustive_bind_decl() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
ab: int
|
||||||
|
if cond:
|
||||||
|
ab = 2
|
||||||
|
else:
|
||||||
|
ab = 1
|
||||||
|
print(a<CURSOR>b)
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.goto_definition(), @r"
|
||||||
|
info[goto-type-definition]: Type definition
|
||||||
|
--> main.py:4:17
|
||||||
|
|
|
||||||
|
2 | ab: int
|
||||||
|
3 | if cond:
|
||||||
|
4 | ab = 2
|
||||||
|
| ^^
|
||||||
|
5 | else:
|
||||||
|
6 | ab = 1
|
||||||
|
|
|
||||||
|
info: Source
|
||||||
|
--> main.py:7:19
|
||||||
|
|
|
||||||
|
5 | else:
|
||||||
|
6 | ab = 1
|
||||||
|
7 | print(ab)
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
|
||||||
|
info[goto-type-definition]: Type definition
|
||||||
|
--> main.py:6:17
|
||||||
|
|
|
||||||
|
4 | ab = 2
|
||||||
|
5 | else:
|
||||||
|
6 | ab = 1
|
||||||
|
| ^^
|
||||||
|
7 | print(ab)
|
||||||
|
|
|
||||||
|
info: Source
|
||||||
|
--> main.py:7:19
|
||||||
|
|
|
||||||
|
5 | else:
|
||||||
|
6 | ab = 1
|
||||||
|
7 | print(ab)
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_local_load_bind_decl() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
ab: int
|
||||||
|
ab = 1
|
||||||
|
print(a<CURSOR>b)
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.goto_definition(), @r"
|
||||||
|
info[goto-type-definition]: Type definition
|
||||||
|
--> main.py:3:13
|
||||||
|
|
|
||||||
|
2 | ab: int
|
||||||
|
3 | ab = 1
|
||||||
|
| ^^
|
||||||
|
4 | print(ab)
|
||||||
|
|
|
||||||
|
info: Source
|
||||||
|
--> main.py:4:19
|
||||||
|
|
|
||||||
|
2 | ab: int
|
||||||
|
3 | ab = 1
|
||||||
|
4 | print(ab)
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_local_first_store() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
a<CURSOR>b = 1
|
||||||
|
print(ab)
|
||||||
|
ab = 2
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.goto_definition(), @"No goto target found");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_local_second_store() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
ab = 1
|
||||||
|
print(ab)
|
||||||
|
a<CURSOR>b = 2
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.goto_definition(), @"No goto target found");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_local_loadstore() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
ab = 1
|
||||||
|
print(ab)
|
||||||
|
a<CURSOR>b += 2
|
||||||
|
print(ab)
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.goto_definition(), @"No goto target found");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_class() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
class AB:
|
||||||
|
def __init__(self, val: int):
|
||||||
|
self.myval = val
|
||||||
|
|
||||||
|
x = A<CURSOR>B(5)
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.goto_definition(), @r"
|
||||||
|
info[goto-type-definition]: Type definition
|
||||||
|
--> main.py:2:19
|
||||||
|
|
|
||||||
|
2 | class AB:
|
||||||
|
| ^^
|
||||||
|
3 | def __init__(self, val: int):
|
||||||
|
4 | self.myval = val
|
||||||
|
|
|
||||||
|
info: Source
|
||||||
|
--> main.py:6:17
|
||||||
|
|
|
||||||
|
4 | self.myval = val
|
||||||
|
5 |
|
||||||
|
6 | x = AB(5)
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_class_implicit_instance_variable() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
class AB:
|
||||||
|
def __init__(self, val: int):
|
||||||
|
self.myval = val
|
||||||
|
|
||||||
|
x = AB(5)
|
||||||
|
print(x.my<CURSOR>val)
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.goto_definition(), @"No goto target found");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_class_explicit_instance_variable() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
class AB:
|
||||||
|
myval: int
|
||||||
|
def __init__(self, val: int):
|
||||||
|
self.myval = val
|
||||||
|
|
||||||
|
x = AB(5)
|
||||||
|
print(x.my<CURSOR>val)
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.goto_definition(), @"No goto target found");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_path_parent() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
class AB:
|
||||||
|
def __init__(self, val: int):
|
||||||
|
self.myval = val
|
||||||
|
|
||||||
|
xyz = AB(5)
|
||||||
|
print(x<CURSOR>yz.myval)
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.goto_definition(), @r"
|
||||||
|
info[goto-type-definition]: Type definition
|
||||||
|
--> main.py:6:13
|
||||||
|
|
|
||||||
|
4 | self.myval = val
|
||||||
|
5 |
|
||||||
|
6 | xyz = AB(5)
|
||||||
|
| ^^^
|
||||||
|
7 | print(xyz.myval)
|
||||||
|
|
|
||||||
|
info: Source
|
||||||
|
--> main.py:7:19
|
||||||
|
|
|
||||||
|
6 | xyz = AB(5)
|
||||||
|
7 | print(xyz.myval)
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_class_class_variable() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
class AB:
|
||||||
|
RED = "red"
|
||||||
|
BLUE = "blue"
|
||||||
|
|
||||||
|
x = AB.RE<CURSOR>D
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.goto_definition(), @"No goto target found");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_class_path_parent() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
class AB:
|
||||||
|
RED = "red"
|
||||||
|
BLUE = "blue"
|
||||||
|
|
||||||
|
x = A<CURSOR>B.RED
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.goto_definition(), @r#"
|
||||||
|
info[goto-type-definition]: Type definition
|
||||||
|
--> main.py:2:19
|
||||||
|
|
|
||||||
|
2 | class AB:
|
||||||
|
| ^^
|
||||||
|
3 | RED = "red"
|
||||||
|
4 | BLUE = "blue"
|
||||||
|
|
|
||||||
|
info: Source
|
||||||
|
--> main.py:6:17
|
||||||
|
|
|
||||||
|
4 | BLUE = "blue"
|
||||||
|
5 |
|
||||||
|
6 | x = AB.RED
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
"#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_global_decl() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
ab = 1
|
||||||
|
def myfunc():
|
||||||
|
global a<CURSOR>b
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.goto_definition(), @"No goto target found");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_global_load() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
ab = 1
|
||||||
|
def myfunc():
|
||||||
|
global ab
|
||||||
|
print(a<CURSOR>b)
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.goto_definition(), @"No definitions found");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_global_store() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
ab = 1
|
||||||
|
def myfunc():
|
||||||
|
global ab
|
||||||
|
a<CURSOR>b = 2
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.goto_definition(), @"No goto target found");
|
||||||
|
}
|
||||||
|
|
||||||
impl CursorTest {
|
impl CursorTest {
|
||||||
fn goto_type_definition(&self) -> String {
|
fn goto_type_definition(&self) -> String {
|
||||||
let Some(targets) =
|
let Some(targets) =
|
||||||
@@ -847,6 +1397,24 @@ f(**kwargs<CURSOR>)
|
|||||||
.map(|target| GotoTypeDefinitionDiagnostic::new(source, &target)),
|
.map(|target| GotoTypeDefinitionDiagnostic::new(source, &target)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn goto_definition(&self) -> String {
|
||||||
|
let Some(targets) = goto_definition(&self.db, self.cursor.file, self.cursor.offset)
|
||||||
|
else {
|
||||||
|
return "No goto target found".to_string();
|
||||||
|
};
|
||||||
|
|
||||||
|
if targets.is_empty() {
|
||||||
|
return "No definitions found".to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
let source = targets.range;
|
||||||
|
self.render_diagnostics(
|
||||||
|
targets
|
||||||
|
.into_iter()
|
||||||
|
.map(|target| GotoTypeDefinitionDiagnostic::new(source, &target)),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GotoTypeDefinitionDiagnostic {
|
struct GotoTypeDefinitionDiagnostic {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ mod markup;
|
|||||||
|
|
||||||
pub use completion::completion;
|
pub use completion::completion;
|
||||||
pub use db::Db;
|
pub use db::Db;
|
||||||
pub use goto::goto_type_definition;
|
pub use goto::{goto_definition, goto_type_definition};
|
||||||
pub use hover::hover;
|
pub use hover::hover;
|
||||||
pub use inlay_hints::inlay_hints;
|
pub use inlay_hints::inlay_hints;
|
||||||
pub use markup::MarkupKind;
|
pub use markup::MarkupKind;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pub use program::{
|
|||||||
PythonVersionWithSource, SearchPathSettings,
|
PythonVersionWithSource, SearchPathSettings,
|
||||||
};
|
};
|
||||||
pub use python_platform::PythonPlatform;
|
pub use python_platform::PythonPlatform;
|
||||||
pub use semantic_model::{Completion, HasType, NameKind, SemanticModel};
|
pub use semantic_model::{Completion, HasDefinition, HasType, NameKind, SemanticModel};
|
||||||
pub use site_packages::{PythonEnvironment, SitePackagesPaths, SysPrefixPathOrigin};
|
pub use site_packages::{PythonEnvironment, SitePackagesPaths, SysPrefixPathOrigin};
|
||||||
pub use util::diagnostics::add_inferred_python_version_hint_to_diagnostic;
|
pub use util::diagnostics::add_inferred_python_version_hint_to_diagnostic;
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
use ruff_db::files::{File, FilePath};
|
use ruff_db::files::{File, FilePath};
|
||||||
use ruff_db::source::line_index;
|
use ruff_db::source::line_index;
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast::{self as ast, ExprContext};
|
||||||
use ruff_python_ast::{Expr, ExprRef, name::Name};
|
use ruff_python_ast::{Expr, ExprRef, name::Name};
|
||||||
use ruff_source_file::LineIndex;
|
use ruff_source_file::LineIndex;
|
||||||
|
|
||||||
use crate::Db;
|
use crate::Db;
|
||||||
use crate::module_name::ModuleName;
|
use crate::module_name::ModuleName;
|
||||||
use crate::module_resolver::{KnownModule, Module, resolve_module};
|
use crate::module_resolver::{KnownModule, Module, resolve_module};
|
||||||
|
use crate::semantic_index::ast_ids::HasScopedUseId;
|
||||||
|
use crate::semantic_index::definition::Definition;
|
||||||
use crate::semantic_index::place::FileScopeId;
|
use crate::semantic_index::place::FileScopeId;
|
||||||
use crate::semantic_index::semantic_index;
|
use crate::semantic_index::semantic_index;
|
||||||
use crate::types::ide_support::all_declarations_and_bindings;
|
use crate::types::ide_support::all_declarations_and_bindings;
|
||||||
@@ -175,6 +177,41 @@ pub struct Completion {
|
|||||||
pub builtin: bool,
|
pub builtin: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait HasDefinition {
|
||||||
|
/// Returns the definitions of `self`.
|
||||||
|
///
|
||||||
|
/// ## Panics
|
||||||
|
/// May panic if `self` is from another file than `model`.
|
||||||
|
fn definitions<'db>(&self, model: &SemanticModel<'db>) -> Option<Vec<Definition<'db>>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasDefinition for ast::ExprRef<'_> {
|
||||||
|
fn definitions<'db>(&self, model: &SemanticModel<'db>) -> Option<Vec<Definition<'db>>> {
|
||||||
|
match self {
|
||||||
|
ExprRef::Name(name) => match name.ctx {
|
||||||
|
ExprContext::Load => {
|
||||||
|
let index = semantic_index(model.db, model.file);
|
||||||
|
let file_scope = index.expression_scope_id(*self);
|
||||||
|
let scope = file_scope.to_scope_id(model.db, model.file);
|
||||||
|
let use_def = index.use_def_map(file_scope);
|
||||||
|
let use_id = self.scoped_use_id(model.db, scope);
|
||||||
|
|
||||||
|
Some(
|
||||||
|
use_def
|
||||||
|
.bindings_at_use(use_id)
|
||||||
|
.filter_map(|binding| binding.binding.definition())
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ExprContext::Store => None,
|
||||||
|
ExprContext::Del => None,
|
||||||
|
ExprContext::Invalid => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait HasType {
|
pub trait HasType {
|
||||||
/// Returns the inferred type of `self`.
|
/// Returns the inferred type of `self`.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::session::{AllOptions, ClientOptions, Session};
|
|||||||
use lsp_server::Connection;
|
use lsp_server::Connection;
|
||||||
use lsp_types::{
|
use lsp_types::{
|
||||||
ClientCapabilities, DiagnosticOptions, DiagnosticServerCapabilities, HoverProviderCapability,
|
ClientCapabilities, DiagnosticOptions, DiagnosticServerCapabilities, HoverProviderCapability,
|
||||||
InlayHintOptions, InlayHintServerCapabilities, MessageType, ServerCapabilities,
|
InlayHintOptions, InlayHintServerCapabilities, MessageType, OneOf, ServerCapabilities,
|
||||||
TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions,
|
TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions,
|
||||||
TypeDefinitionProviderCapability, Url,
|
TypeDefinitionProviderCapability, Url,
|
||||||
};
|
};
|
||||||
@@ -183,6 +183,7 @@ impl Server {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
|
definition_provider: Some(OneOf::Left(true)),
|
||||||
type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
|
type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
|
||||||
hover_provider: Some(HoverProviderCapability::Simple(true)),
|
hover_provider: Some(HoverProviderCapability::Simple(true)),
|
||||||
inlay_hint_provider: Some(lsp_types::OneOf::Right(
|
inlay_hint_provider: Some(lsp_types::OneOf::Right(
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ pub(super) fn request(req: server::Request) -> Task {
|
|||||||
>(
|
>(
|
||||||
req, BackgroundSchedule::Worker
|
req, BackgroundSchedule::Worker
|
||||||
),
|
),
|
||||||
|
requests::GotoDefinitionRequestHandler::METHOD => background_document_request_task::<
|
||||||
|
requests::GotoDefinitionRequestHandler,
|
||||||
|
>(req, BackgroundSchedule::Worker),
|
||||||
requests::HoverRequestHandler::METHOD => background_document_request_task::<
|
requests::HoverRequestHandler::METHOD => background_document_request_task::<
|
||||||
requests::HoverRequestHandler,
|
requests::HoverRequestHandler,
|
||||||
>(req, BackgroundSchedule::Worker),
|
>(req, BackgroundSchedule::Worker),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
mod completion;
|
mod completion;
|
||||||
mod diagnostic;
|
mod diagnostic;
|
||||||
|
mod goto_definition;
|
||||||
mod goto_type_definition;
|
mod goto_type_definition;
|
||||||
mod hover;
|
mod hover;
|
||||||
mod inlay_hints;
|
mod inlay_hints;
|
||||||
@@ -8,6 +9,7 @@ mod workspace_diagnostic;
|
|||||||
|
|
||||||
pub(super) use completion::CompletionRequestHandler;
|
pub(super) use completion::CompletionRequestHandler;
|
||||||
pub(super) use diagnostic::DocumentDiagnosticRequestHandler;
|
pub(super) use diagnostic::DocumentDiagnosticRequestHandler;
|
||||||
|
pub(super) use goto_definition::GotoDefinitionRequestHandler;
|
||||||
pub(super) use goto_type_definition::GotoTypeDefinitionRequestHandler;
|
pub(super) use goto_type_definition::GotoTypeDefinitionRequestHandler;
|
||||||
pub(super) use hover::HoverRequestHandler;
|
pub(super) use hover::HoverRequestHandler;
|
||||||
pub(super) use inlay_hints::InlayHintRequestHandler;
|
pub(super) use inlay_hints::InlayHintRequestHandler;
|
||||||
|
|||||||
77
crates/ty_server/src/server/api/requests/goto_definition.rs
Normal file
77
crates/ty_server/src/server/api/requests/goto_definition.rs
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use lsp_types::GotoDefinitionParams;
|
||||||
|
use lsp_types::request::GotoDefinition;
|
||||||
|
use lsp_types::{GotoDefinitionResponse, Url};
|
||||||
|
use ruff_db::source::{line_index, source_text};
|
||||||
|
use ty_ide::goto_definition;
|
||||||
|
use ty_project::ProjectDatabase;
|
||||||
|
|
||||||
|
use crate::DocumentSnapshot;
|
||||||
|
use crate::document::{PositionExt, ToLink};
|
||||||
|
use crate::server::api::traits::{
|
||||||
|
BackgroundDocumentRequestHandler, RequestHandler, RetriableRequestHandler,
|
||||||
|
};
|
||||||
|
use crate::session::client::Client;
|
||||||
|
|
||||||
|
pub(crate) struct GotoDefinitionRequestHandler;
|
||||||
|
|
||||||
|
impl RequestHandler for GotoDefinitionRequestHandler {
|
||||||
|
type RequestType = GotoDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BackgroundDocumentRequestHandler for GotoDefinitionRequestHandler {
|
||||||
|
fn document_url(params: &GotoDefinitionParams) -> Cow<Url> {
|
||||||
|
Cow::Borrowed(¶ms.text_document_position_params.text_document.uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_with_snapshot(
|
||||||
|
db: &ProjectDatabase,
|
||||||
|
snapshot: DocumentSnapshot,
|
||||||
|
_client: &Client,
|
||||||
|
params: GotoDefinitionParams,
|
||||||
|
) -> crate::server::Result<Option<GotoDefinitionResponse>> {
|
||||||
|
if snapshot.client_settings().is_language_services_disabled() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(file) = snapshot.file(db) else {
|
||||||
|
tracing::debug!("Failed to resolve file for {:?}", params);
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
let source = source_text(db, file);
|
||||||
|
let line_index = line_index(db, file);
|
||||||
|
let offset = params.text_document_position_params.position.to_text_size(
|
||||||
|
&source,
|
||||||
|
&line_index,
|
||||||
|
snapshot.encoding(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let Some(ranged) = goto_definition(db, file, offset) else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
if snapshot
|
||||||
|
.resolved_client_capabilities()
|
||||||
|
.type_definition_link_support
|
||||||
|
{
|
||||||
|
let src = Some(ranged.range);
|
||||||
|
let links: Vec<_> = ranged
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|target| target.to_link(db, src, snapshot.encoding()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(Some(GotoDefinitionResponse::Link(links)))
|
||||||
|
} else {
|
||||||
|
let locations: Vec<_> = ranged
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|target| target.to_location(db, snapshot.encoding()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(Some(GotoDefinitionResponse::Array(locations)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RetriableRequestHandler for GotoDefinitionRequestHandler {}
|
||||||
Reference in New Issue
Block a user