Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 24 additions & 5 deletions crates/ty_ide/src/docstring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,10 @@ impl Docstring {
/// Render the docstring for markdown display
pub fn render_markdown(&self) -> String {
let trimmed = documentation_trim(&self.0);
// TODO: now actually parse it and "render" it to markdown.
//
// For now we just wrap the content in a plaintext codeblock
// to avoid the contents erroneously being interpreted as markdown.
format!("```text\n{trimmed}\n```")

// Try to parse and render the contents as markdown,
// and if we fail, wrap it in a codeblock and display it raw.
try_render_markdown(&trimmed).unwrap_or_else(|| format!("```text\n{trimmed}\n```"))
}

/// Extract parameter documentation from popular docstring formats.
Expand Down Expand Up @@ -153,6 +152,26 @@ fn documentation_trim(docs: &str) -> String {
output
}

fn try_render_markdown(docstring: &str) -> Option<String> {
let mut output = String::new();
let mut first_line = true;
for line in docstring.lines() {
// We can assume leading whitespace has been normalized
let trimmed_line = line.trim_start_matches(' ');
let num_leading_spaces = line.len() - trimmed_line.len();

if !first_line {
output.push_str(" \n");
}
for _ in 0..num_leading_spaces {
output.push_str("&nbsp;");
}
output.push_str(trimmed_line);
first_line = false;
}
Some(output)
}

/// Extract parameter documentation from Google-style docstrings.
fn extract_google_style_params(docstring: &str) -> HashMap<String, String> {
let mut param_docs = HashMap::new();
Expand Down
14 changes: 13 additions & 1 deletion crates/ty_ide/src/goto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use ruff_text_size::{Ranged, TextRange, TextSize};
use ty_python_semantic::ResolvedDefinition;
use ty_python_semantic::types::Type;
use ty_python_semantic::types::ide_support::{
call_signature_details, definitions_for_keyword_argument,
call_signature_details, call_type_simplified_by_overloads, definitions_for_keyword_argument,
};
use ty_python_semantic::{
HasDefinition, HasType, ImportAliasResolution, SemanticModel, definitions_for_imported_symbol,
Expand Down Expand Up @@ -326,6 +326,18 @@ impl GotoTarget<'_> {
Some(ty)
}

/// Try to get a simplified display of this callable type by resolving overloads
pub(crate) fn call_type_simplified_by_overloads(
&self,
model: &SemanticModel,
) -> Option<String> {
if let GotoTarget::Call { call, .. } = self {
call_type_simplified_by_overloads(model.db(), model, call)
} else {
None
}
}

/// Gets the definitions for this goto target.
///
/// The `alias_resolution` parameter controls whether import aliases
Expand Down
80 changes: 23 additions & 57 deletions crates/ty_ide/src/hover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ pub fn hover(db: &dyn Db, file: File, offset: TextSize) -> Option<RangedValue<Ho
}

let model = SemanticModel::new(db, file);
let ty = goto_target.inferred_type(&model);
let docs = goto_target
.get_definition_targets(
file,
Expand All @@ -30,9 +29,10 @@ pub fn hover(db: &dyn Db, file: File, offset: TextSize) -> Option<RangedValue<Ho
.and_then(|definitions| definitions.docstring(db))
.map(HoverContent::Docstring);

// TODO: Render the symbol's signature instead of just its type.
let mut contents = Vec::new();
if let Some(ty) = ty {
if let Some(signature) = goto_target.call_type_simplified_by_overloads(&model) {
contents.push(HoverContent::Signature(signature));
} else if let Some(ty) = goto_target.inferred_type(&model) {
tracing::debug!("Inferred type of covering node is {}", ty.display(db));
contents.push(match ty {
Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) => typevar
Expand Down Expand Up @@ -62,7 +62,7 @@ pub struct Hover<'db> {

impl<'db> Hover<'db> {
/// Renders the hover to a string using the specified markup kind.
pub const fn display<'a>(&'a self, db: &'a dyn Db, kind: MarkupKind) -> DisplayHover<'a> {
pub const fn display<'a>(&'a self, db: &'db dyn Db, kind: MarkupKind) -> DisplayHover<'db, 'a> {
DisplayHover {
db,
hover: self,
Expand Down Expand Up @@ -93,13 +93,13 @@ impl<'a, 'db> IntoIterator for &'a Hover<'db> {
}
}

pub struct DisplayHover<'a> {
db: &'a dyn Db,
hover: &'a Hover<'a>,
pub struct DisplayHover<'db, 'a> {
db: &'db dyn Db,
hover: &'a Hover<'db>,
kind: MarkupKind,
}

impl fmt::Display for DisplayHover<'_> {
impl fmt::Display for DisplayHover<'_, '_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let mut first = true;
for content in &self.hover.contents {
Expand All @@ -115,8 +115,9 @@ impl fmt::Display for DisplayHover<'_> {
}
}

#[derive(Debug, Clone, Eq, PartialEq)]
#[derive(Debug, Clone)]
pub enum HoverContent<'db> {
Signature(String),
Type(Type<'db>, Option<TypeVarVariance>),
Docstring(Docstring),
}
Expand All @@ -140,6 +141,9 @@ pub(crate) struct DisplayHoverContent<'a, 'db> {
impl fmt::Display for DisplayHoverContent<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.content {
HoverContent::Signature(signature) => {
self.kind.fenced_code_block(&signature, "python").fmt(f)
}
HoverContent::Type(ty, variance) => {
let variance = match variance {
Some(TypeVarVariance::Covariant) => " (covariant)",
Expand Down Expand Up @@ -961,14 +965,12 @@ def ab(a: str): ...

assert_snapshot!(test.hover(), @r"
(a: int) -> Unknown
(a: str) -> Unknown
---------------------------------------------
the int overload

---------------------------------------------
```python
(a: int) -> Unknown
(a: str) -> Unknown
```
---
```text
Expand Down Expand Up @@ -1025,14 +1027,12 @@ def ab(a: str):
.build();

assert_snapshot!(test.hover(), @r#"
(a: int) -> Unknown
(a: str) -> Unknown
---------------------------------------------
the int overload

---------------------------------------------
```python
(a: int) -> Unknown
(a: str) -> Unknown
```
---
Expand Down Expand Up @@ -1090,21 +1090,19 @@ def ab(a: int):
.build();

assert_snapshot!(test.hover(), @r"
(
def ab(
a: int,
b: int
) -> Unknown
(a: int) -> Unknown
---------------------------------------------
the two arg overload

---------------------------------------------
```python
(
def ab(
a: int,
b: int
) -> Unknown
(a: int) -> Unknown
```
---
```text
Expand Down Expand Up @@ -1161,20 +1159,12 @@ def ab(a: int):
.build();

assert_snapshot!(test.hover(), @r"
(
a: int,
b: int
) -> Unknown
(a: int) -> Unknown
---------------------------------------------
the two arg overload

---------------------------------------------
```python
(
a: int,
b: int
) -> Unknown
(a: int) -> Unknown
```
---
Expand Down Expand Up @@ -1236,33 +1226,21 @@ def ab(a: int, *, c: int):
.build();

assert_snapshot!(test.hover(), @r"
(a: int) -> Unknown
(
def ab(
a: int,
*,
b: int
) -> Unknown
(
a: int,
*,
c: int
) -> Unknown
---------------------------------------------
keywordless overload

---------------------------------------------
```python
(a: int) -> Unknown
(
def ab(
a: int,
*,
b: int
) -> Unknown
(
a: int,
*,
c: int
) -> Unknown
```
---
```text
Expand Down Expand Up @@ -1323,13 +1301,7 @@ def ab(a: int, *, c: int):
.build();

assert_snapshot!(test.hover(), @r"
(a: int) -> Unknown
(
a: int,
*,
b: int
) -> Unknown
(
def ab(
a: int,
*,
c: int
Expand All @@ -1339,13 +1311,7 @@ def ab(a: int, *, c: int):

---------------------------------------------
```python
(a: int) -> Unknown
(
a: int,
*,
b: int
) -> Unknown
(
def ab(
a: int,
*,
c: int
Expand Down Expand Up @@ -1397,11 +1363,11 @@ def ab(a: int, *, c: int):
);

assert_snapshot!(test.hover(), @r#"
(
def foo(
a: int,
b
) -> Unknown
(
def foo(
a: str,
b
) -> Unknown
Expand All @@ -1410,11 +1376,11 @@ def ab(a: int, *, c: int):

---------------------------------------------
```python
(
def foo(
a: int,
b
) -> Unknown
(
def foo(
a: str,
b
) -> Unknown
Expand Down
33 changes: 33 additions & 0 deletions crates/ty_python_semantic/src/types/call/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,39 @@ impl<'a, 'db> CallArguments<'a, 'db> {
.collect()
}

/// Like [`Self::from_arguments`] but fills as much typing info in as possible.
///
/// This currently only exists for the LSP usecase, and shouldn't be used in normal
/// typechecking.
pub(crate) fn from_arguments_typed(
arguments: &'a ast::Arguments,
mut infer_argument_type: impl FnMut(Option<&ast::Expr>, &ast::Expr) -> Type<'db>,
) -> Self {
arguments
.arguments_source_order()
.map(|arg_or_keyword| match arg_or_keyword {
ast::ArgOrKeyword::Arg(arg) => match arg {
ast::Expr::Starred(ast::ExprStarred { value, .. }) => {
let ty = infer_argument_type(Some(arg), value);
(Argument::Variadic, Some(ty))
}
_ => {
let ty = infer_argument_type(None, arg);
(Argument::Positional, Some(ty))
}
},
ast::ArgOrKeyword::Keyword(ast::Keyword { arg, value, .. }) => {
let ty = infer_argument_type(None, value);
if let Some(arg) = arg {
(Argument::Keyword(&arg.id), Some(ty))
} else {
(Argument::Keywords, Some(ty))
}
}
})
.collect()
}

/// Create a [`CallArguments`] with no arguments.
pub(crate) fn none() -> Self {
Self::default()
Expand Down
Loading
Loading