mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 05:44:56 +00:00
[ty] introduce multiline pretty printer (#19979)
Requires some iteration, but this includes the most tedious part -- threading a new concept of DisplaySettings through every type display impl. Currently it only holds a boolean for multiline, but in the future it could also take other things like "render to markdown" or "here's your base indent if you make a newline". For types which have exposed display functions I've left the old signature as a compatibility polyfill to avoid having to audit everywhere that prints types right off the bat (notably I originally tried doing multiline functions unconditionally and a ton of things churned that clearly weren't ready for multi-line (diagnostics). The only real use of this API in this PR is to multiline render function types in hovers, which is the highest impact (see snapshot changes). Fixes https://github.com/astral-sh/ty/issues/1000
This commit is contained in:
parent
59b078b1bf
commit
c6dcfe36d0
4 changed files with 793 additions and 106 deletions
|
@ -6,8 +6,8 @@ use ruff_db::parsed::parsed_module;
|
|||
use ruff_text_size::{Ranged, TextSize};
|
||||
use std::fmt;
|
||||
use std::fmt::Formatter;
|
||||
use ty_python_semantic::SemanticModel;
|
||||
use ty_python_semantic::types::Type;
|
||||
use ty_python_semantic::{DisplaySettings, SemanticModel};
|
||||
|
||||
pub fn hover(db: &dyn Db, file: File, offset: TextSize) -> Option<RangedValue<Hover<'_>>> {
|
||||
let parsed = parsed_module(db, file).load(db);
|
||||
|
@ -135,7 +135,10 @@ impl fmt::Display for DisplayHoverContent<'_, '_> {
|
|||
match self.content {
|
||||
HoverContent::Type(ty) => self
|
||||
.kind
|
||||
.fenced_code_block(ty.display(self.db), "python")
|
||||
.fenced_code_block(
|
||||
ty.display_with(self.db, DisplaySettings::default().multiline()),
|
||||
"python",
|
||||
)
|
||||
.fmt(f),
|
||||
HoverContent::Docstring(docstring) => docstring.render(self.kind).fmt(f),
|
||||
}
|
||||
|
@ -201,7 +204,10 @@ mod tests {
|
|||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
def my_func(a, b) -> Unknown
|
||||
def my_func(
|
||||
a,
|
||||
b
|
||||
) -> Unknown
|
||||
---------------------------------------------
|
||||
This is such a great func!!
|
||||
|
||||
|
@ -211,7 +217,10 @@ mod tests {
|
|||
|
||||
---------------------------------------------
|
||||
```python
|
||||
def my_func(a, b) -> Unknown
|
||||
def my_func(
|
||||
a,
|
||||
b
|
||||
) -> Unknown
|
||||
```
|
||||
---
|
||||
```text
|
||||
|
@ -253,7 +262,10 @@ mod tests {
|
|||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
def my_func(a, b) -> Unknown
|
||||
def my_func(
|
||||
a,
|
||||
b
|
||||
) -> Unknown
|
||||
---------------------------------------------
|
||||
This is such a great func!!
|
||||
|
||||
|
@ -263,7 +275,10 @@ mod tests {
|
|||
|
||||
---------------------------------------------
|
||||
```python
|
||||
def my_func(a, b) -> Unknown
|
||||
def my_func(
|
||||
a,
|
||||
b
|
||||
) -> Unknown
|
||||
```
|
||||
---
|
||||
```text
|
||||
|
@ -519,7 +534,10 @@ mod tests {
|
|||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
bound method MyClass.my_method(a, b) -> Unknown
|
||||
bound method MyClass.my_method(
|
||||
a,
|
||||
b
|
||||
) -> Unknown
|
||||
---------------------------------------------
|
||||
This is such a great func!!
|
||||
|
||||
|
@ -529,7 +547,10 @@ mod tests {
|
|||
|
||||
---------------------------------------------
|
||||
```python
|
||||
bound method MyClass.my_method(a, b) -> Unknown
|
||||
bound method MyClass.my_method(
|
||||
a,
|
||||
b
|
||||
) -> Unknown
|
||||
```
|
||||
---
|
||||
```text
|
||||
|
@ -601,10 +622,16 @@ mod tests {
|
|||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
def foo(a, b) -> Unknown
|
||||
def foo(
|
||||
a,
|
||||
b
|
||||
) -> Unknown
|
||||
---------------------------------------------
|
||||
```python
|
||||
def foo(a, b) -> Unknown
|
||||
def foo(
|
||||
a,
|
||||
b
|
||||
) -> Unknown
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
|
@ -763,6 +790,128 @@ mod tests {
|
|||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_overload() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
from typing import overload
|
||||
|
||||
@overload
|
||||
def foo(a: int, b):
|
||||
"""The first overload"""
|
||||
return 0
|
||||
|
||||
@overload
|
||||
def foo(a: str, b):
|
||||
"""The second overload"""
|
||||
return 1
|
||||
|
||||
if random.choice([True, False]):
|
||||
a = 1
|
||||
else:
|
||||
a = "hello"
|
||||
|
||||
foo<CURSOR>(a, 2)
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r#"
|
||||
(
|
||||
a: int,
|
||||
b
|
||||
) -> Unknown
|
||||
(
|
||||
a: str,
|
||||
b
|
||||
) -> Unknown
|
||||
---------------------------------------------
|
||||
The first overload
|
||||
|
||||
---------------------------------------------
|
||||
```python
|
||||
(
|
||||
a: int,
|
||||
b
|
||||
) -> Unknown
|
||||
(
|
||||
a: str,
|
||||
b
|
||||
) -> Unknown
|
||||
```
|
||||
---
|
||||
```text
|
||||
The first overload
|
||||
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
--> main.py:19:13
|
||||
|
|
||||
17 | a = "hello"
|
||||
18 |
|
||||
19 | foo(a, 2)
|
||||
| ^^^- Cursor offset
|
||||
| |
|
||||
| source
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_overload_compact() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
from typing import overload
|
||||
|
||||
@overload
|
||||
def foo(a: int):
|
||||
"""The first overload"""
|
||||
return 0
|
||||
|
||||
@overload
|
||||
def foo(a: str):
|
||||
"""The second overload"""
|
||||
return 1
|
||||
|
||||
if random.choice([True, False]):
|
||||
a = 1
|
||||
else:
|
||||
a = "hello"
|
||||
|
||||
foo<CURSOR>(a)
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r#"
|
||||
(a: int) -> Unknown
|
||||
(a: str) -> Unknown
|
||||
---------------------------------------------
|
||||
The first overload
|
||||
|
||||
---------------------------------------------
|
||||
```python
|
||||
(a: int) -> Unknown
|
||||
(a: str) -> Unknown
|
||||
```
|
||||
---
|
||||
```text
|
||||
The first overload
|
||||
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
--> main.py:19:13
|
||||
|
|
||||
17 | a = "hello"
|
||||
18 |
|
||||
19 | foo(a)
|
||||
| ^^^- Cursor offset
|
||||
| |
|
||||
| source
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_module() {
|
||||
let mut test = cursor_test(
|
||||
|
@ -1231,6 +1380,110 @@ mod tests {
|
|||
assert_snapshot!(test.hover(), @"Hover provided no content");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_complex_type1() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
from typing import Callable, Any, List
|
||||
def ab(x: int, y: Callable[[int, int], Any], z: List[int]) -> int: ...
|
||||
|
||||
a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
def ab(
|
||||
x: int,
|
||||
y: (int, int, /) -> Any,
|
||||
z: list[int]
|
||||
) -> int
|
||||
---------------------------------------------
|
||||
```python
|
||||
def ab(
|
||||
x: int,
|
||||
y: (int, int, /) -> Any,
|
||||
z: list[int]
|
||||
) -> int
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
--> main.py:5:9
|
||||
|
|
||||
3 | def ab(x: int, y: Callable[[int, int], Any], z: List[int]) -> int: ...
|
||||
4 |
|
||||
5 | ab
|
||||
| ^-
|
||||
| ||
|
||||
| |Cursor offset
|
||||
| source
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_complex_type2() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
from typing import Callable, Tuple, Any
|
||||
ab: Tuple[Any, int, Callable[[int, int], Any]] = ...
|
||||
|
||||
a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
tuple[Any, int, (int, int, /) -> Any]
|
||||
---------------------------------------------
|
||||
```python
|
||||
tuple[Any, int, (int, int, /) -> Any]
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
--> main.py:5:9
|
||||
|
|
||||
3 | ab: Tuple[Any, int, Callable[[int, int], Any]] = ...
|
||||
4 |
|
||||
5 | ab
|
||||
| ^-
|
||||
| ||
|
||||
| |Cursor offset
|
||||
| source
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_complex_type3() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
from typing import Callable, Any
|
||||
ab: Callable[[int, int], Any] | None = ...
|
||||
|
||||
a<CURSOR>b
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
((int, int, /) -> Any) | None
|
||||
---------------------------------------------
|
||||
```python
|
||||
((int, int, /) -> Any) | None
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
--> main.py:5:9
|
||||
|
|
||||
3 | ab: Callable[[int, int], Any] | None = ...
|
||||
4 |
|
||||
5 | ab
|
||||
| ^-
|
||||
| ||
|
||||
| |Cursor offset
|
||||
| source
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hover_docstring() {
|
||||
let test = cursor_test(
|
||||
|
|
|
@ -19,6 +19,7 @@ pub use semantic_model::{
|
|||
Completion, CompletionKind, HasDefinition, HasType, NameKind, SemanticModel,
|
||||
};
|
||||
pub use site_packages::{PythonEnvironment, SitePackagesPaths, SysPrefixPathOrigin};
|
||||
pub use types::DisplaySettings;
|
||||
pub use types::ide_support::{
|
||||
ImportAliasResolution, ResolvedDefinition, definitions_for_attribute,
|
||||
definitions_for_imported_symbol, definitions_for_name, map_stub_definition,
|
||||
|
|
|
@ -41,6 +41,7 @@ use crate::types::class::{CodeGeneratorKind, Field};
|
|||
pub(crate) use crate::types::class_base::ClassBase;
|
||||
use crate::types::context::{LintDiagnosticGuard, LintDiagnosticGuardBuilder};
|
||||
use crate::types::diagnostic::{INVALID_AWAIT, INVALID_TYPE_FORM, UNSUPPORTED_BOOL_CONVERSION};
|
||||
pub use crate::types::display::DisplaySettings;
|
||||
use crate::types::enums::{enum_metadata, is_single_member_enum};
|
||||
use crate::types::function::{
|
||||
DataclassTransformerParams, FunctionDecorators, FunctionSpans, FunctionType, KnownFunction,
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue