Compare commits
4 Commits
brent/lamb
...
gankra/got
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97af9d8466 | ||
|
|
5cc6762c23 | ||
|
|
1bee527cb2 | ||
|
|
d7b7b835e1 |
@@ -1,12 +1,13 @@
|
||||
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::parsed::{ParsedModuleRef, parsed_module};
|
||||
use ruff_python_ast::{self as ast, AnyNodeRef};
|
||||
use ruff_python_parser::TokenKind;
|
||||
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::{HasType, SemanticModel};
|
||||
use ty_python_semantic::{HasDefinition, HasType, SemanticModel};
|
||||
|
||||
pub fn goto_type_definition(
|
||||
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)]
|
||||
pub(crate) enum GotoTarget<'a> {
|
||||
Expression(ast::ExprRef<'a>),
|
||||
@@ -154,6 +184,16 @@ impl GotoTarget<'_> {
|
||||
|
||||
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<'_> {
|
||||
@@ -254,7 +294,7 @@ pub(crate) fn find_goto_target(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
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 ruff_db::diagnostic::{
|
||||
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 {
|
||||
fn goto_type_definition(&self) -> String {
|
||||
let Some(targets) =
|
||||
@@ -847,6 +1397,24 @@ f(**kwargs<CURSOR>)
|
||||
.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 {
|
||||
|
||||
@@ -8,7 +8,7 @@ mod markup;
|
||||
|
||||
pub use completion::completion;
|
||||
pub use db::Db;
|
||||
pub use goto::goto_type_definition;
|
||||
pub use goto::{goto_definition, goto_type_definition};
|
||||
pub use hover::hover;
|
||||
pub use inlay_hints::inlay_hints;
|
||||
pub use markup::MarkupKind;
|
||||
|
||||
@@ -15,7 +15,7 @@ pub use program::{
|
||||
PythonVersionWithSource, SearchPathSettings,
|
||||
};
|
||||
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 util::diagnostics::add_inferred_python_version_hint_to_diagnostic;
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
use ruff_db::files::{File, FilePath};
|
||||
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_source_file::LineIndex;
|
||||
|
||||
use crate::Db;
|
||||
use crate::module_name::ModuleName;
|
||||
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::semantic_index;
|
||||
use crate::types::ide_support::all_declarations_and_bindings;
|
||||
@@ -175,6 +177,41 @@ pub struct Completion {
|
||||
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 {
|
||||
/// Returns the inferred type of `self`.
|
||||
///
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::session::{AllOptions, ClientOptions, Session};
|
||||
use lsp_server::Connection;
|
||||
use lsp_types::{
|
||||
ClientCapabilities, DiagnosticOptions, DiagnosticServerCapabilities, HoverProviderCapability,
|
||||
InlayHintOptions, InlayHintServerCapabilities, MessageType, ServerCapabilities,
|
||||
InlayHintOptions, InlayHintServerCapabilities, MessageType, OneOf, ServerCapabilities,
|
||||
TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions,
|
||||
TypeDefinitionProviderCapability, Url,
|
||||
};
|
||||
@@ -183,6 +183,7 @@ impl Server {
|
||||
..Default::default()
|
||||
},
|
||||
)),
|
||||
definition_provider: Some(OneOf::Left(true)),
|
||||
type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
|
||||
hover_provider: Some(HoverProviderCapability::Simple(true)),
|
||||
inlay_hint_provider: Some(lsp_types::OneOf::Right(
|
||||
|
||||
@@ -43,6 +43,9 @@ pub(super) fn request(req: server::Request) -> Task {
|
||||
>(
|
||||
req, BackgroundSchedule::Worker
|
||||
),
|
||||
requests::GotoDefinitionRequestHandler::METHOD => background_document_request_task::<
|
||||
requests::GotoDefinitionRequestHandler,
|
||||
>(req, BackgroundSchedule::Worker),
|
||||
requests::HoverRequestHandler::METHOD => background_document_request_task::<
|
||||
requests::HoverRequestHandler,
|
||||
>(req, BackgroundSchedule::Worker),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
mod completion;
|
||||
mod diagnostic;
|
||||
mod goto_definition;
|
||||
mod goto_type_definition;
|
||||
mod hover;
|
||||
mod inlay_hints;
|
||||
@@ -8,6 +9,7 @@ mod workspace_diagnostic;
|
||||
|
||||
pub(super) use completion::CompletionRequestHandler;
|
||||
pub(super) use diagnostic::DocumentDiagnosticRequestHandler;
|
||||
pub(super) use goto_definition::GotoDefinitionRequestHandler;
|
||||
pub(super) use goto_type_definition::GotoTypeDefinitionRequestHandler;
|
||||
pub(super) use hover::HoverRequestHandler;
|
||||
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