From 46d1539d98ec4eb36887772f6bd1b21965fc91ba Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Tue, 21 Apr 2026 20:33:14 +0900 Subject: [PATCH 1/2] fix --- crates/pyrefly_types/src/display.rs | 20 +++++++++++++++----- pyrefly/lib/test/lsp/hover.rs | 21 +++++++++++++++++++++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/crates/pyrefly_types/src/display.rs b/crates/pyrefly_types/src/display.rs index 7b9493c265..6689325f17 100644 --- a/crates/pyrefly_types/src/display.rs +++ b/crates/pyrefly_types/src/display.rs @@ -753,7 +753,9 @@ impl<'a> TypeDisplayContext<'a> { output.write_builtin("LiteralString", qname) } Type::Callable(c) => { - if self.lsp_display_mode == LspDisplayMode::Hover && is_toplevel { + // Hover output should be readable even when callables appear inside unions + // (e.g. constructor display that unions __new__ and __init__). + if self.lsp_display_mode == LspDisplayMode::Hover { c.fmt_with_type_with_newlines(output, &|t, o| { self.fmt_helper_generic(t, false, o) }) @@ -824,8 +826,16 @@ impl<'a> TypeDisplayContext<'a> { output.write_str(": ...") } } - _ => signature - .fmt_with_type(output, &|t, o| self.fmt_helper_generic(t, false, o)), + _ => { + if self.lsp_display_mode == LspDisplayMode::Hover { + signature.fmt_with_type_with_newlines(output, &|t, o| { + self.fmt_helper_generic(t, false, o) + }) + } else { + signature + .fmt_with_type(output, &|t, o| self.fmt_helper_generic(t, false, o)) + } + } } } Type::Overload(overload) => { @@ -838,8 +848,8 @@ impl<'a> TypeDisplayContext<'a> { } Ok(()) } else { - let multiline = - is_toplevel && self.lsp_display_mode != LspDisplayMode::ProvideType; + let multiline = self.lsp_display_mode == LspDisplayMode::Hover + || (is_toplevel && self.lsp_display_mode != LspDisplayMode::ProvideType); if multiline { output.write_str("Overload[\n ")?; } else { diff --git a/pyrefly/lib/test/lsp/hover.rs b/pyrefly/lib/test/lsp/hover.rs index bbab562542..e4ebfc1734 100644 --- a/pyrefly/lib/test/lsp/hover.rs +++ b/pyrefly/lib/test/lsp/hover.rs @@ -1102,6 +1102,27 @@ Widget docstring"# ); } +#[test] +fn hover_on_dict_constructor_is_multiline() { + let code = r#" +x: dict[str, int] +# ^ +"#; + let report = get_batched_lsp_operations_report(&[("main", code)], get_test_report); + assert!( + report.contains("(class) dict:"), + "Expected dict hover to show class constructor, got: {report}" + ); + assert!( + report.contains("Overload[\n "), + "Expected overload constructor signatures to be multi-line in hover, got: {report}" + ); + assert!( + report.contains("(\n "), + "Expected callable constructor signatures to include newlines in hover, got: {report}" + ); +} + #[test] fn hover_on_first_component_of_multi_part_import() { let mymod_init = r#"# mymod/__init__.py From f9afd514648f6c672222e5fcca3f94e41cb473bd Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Wed, 8 Jul 2026 19:08:03 +0900 Subject: [PATCH 2/2] update test --- crates/pyrefly_types/src/display.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pyrefly_types/src/display.rs b/crates/pyrefly_types/src/display.rs index 6689325f17..2bff104cf3 100644 --- a/crates/pyrefly_types/src/display.rs +++ b/crates/pyrefly_types/src/display.rs @@ -2078,7 +2078,7 @@ pub mod tests { ctx.set_lsp_display_mode(LspDisplayMode::Hover); assert_eq!( ctx.display(&tuple).to_string(), - "tuple[(hello: None, *, world: None) -> None]" + "tuple[(\n hello: None,\n *,\n world: None\n) -> None]" ); }