Skip to content

Commit 1d353ce

Browse files
test
1 parent 81212b4 commit 1d353ce

File tree

6 files changed

+137
-75
lines changed

6 files changed

+137
-75
lines changed

crates/pyrefly_types/src/display.rs

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -92,19 +92,12 @@ pub struct TypeDisplayContext<'a> {
9292
/// Should we display for IDE Hover? This makes type names more readable but less precise.
9393
hover: bool,
9494
always_display_module_name: bool,
95-
tuple_qname: Option<&'a QName>,
95+
extra_symbol_qnames: SmallMap<&'static str, &'a QName>,
9696
}
9797

9898
impl<'a> TypeDisplayContext<'a> {
9999
pub fn new(xs: &[&'a Type]) -> Self {
100-
Self::new_with_tuple(xs, None)
101-
}
102-
103-
pub fn new_with_tuple(xs: &[&'a Type], tuple_qname: Option<&'a QName>) -> Self {
104-
let mut res = Self {
105-
tuple_qname,
106-
..Self::default()
107-
};
100+
let mut res = Self::default();
108101
for x in xs {
109102
res.add(x);
110103
}
@@ -128,8 +121,13 @@ impl<'a> TypeDisplayContext<'a> {
128121
})
129122
}
130123

131-
pub(crate) fn tuple_qname(&self) -> Option<&'a QName> {
132-
self.tuple_qname
124+
pub fn add_symbol_qname(&mut self, symbol: &'static str, qname: &'a QName) {
125+
self.add_qname(qname);
126+
self.extra_symbol_qnames.insert(symbol, qname);
127+
}
128+
129+
pub(crate) fn symbol_qname(&self, symbol: &'static str) -> Option<&'a QName> {
130+
self.extra_symbol_qnames.get(symbol).copied()
133131
}
134132

135133
/// Force that we always display at least the module name for qualified names.
@@ -558,9 +556,11 @@ impl<'a> TypeDisplayContext<'a> {
558556
}
559557
}
560558
Type::Intersect(x) => self.fmt_type_sequence(x.0.iter(), " & ", true, output),
561-
Type::Tuple(t) => {
562-
t.fmt_with_type(output, &|ty, o| self.fmt_helper_generic(ty, false, o))
563-
}
559+
Type::Tuple(t) => t.fmt_with_type(
560+
output,
561+
&|ty, o| self.fmt_helper_generic(ty, false, o),
562+
self.symbol_qname("tuple"),
563+
),
564564
Type::Forall(box Forall {
565565
tparams,
566566
body: body @ Forallable::Callable(c),
@@ -754,22 +754,12 @@ impl Type {
754754
c.display(self).to_string()
755755
}
756756

757-
pub fn get_types_with_locations(&self) -> Vec<(String, Option<TextRangeWithModule>)> {
758-
self.get_types_with_locations_with_tuple_qname(None)
759-
}
760-
761-
pub fn get_types_with_locations_with_stdlib(
757+
pub fn get_types_with_locations(
762758
&self,
763759
stdlib: &Stdlib,
764760
) -> Vec<(String, Option<TextRangeWithModule>)> {
765-
self.get_types_with_locations_with_tuple_qname(Some(stdlib.tuple_object().qname()))
766-
}
767-
768-
fn get_types_with_locations_with_tuple_qname(
769-
&self,
770-
tuple_qname: Option<&QName>,
771-
) -> Vec<(String, Option<TextRangeWithModule>)> {
772-
let ctx = TypeDisplayContext::new_with_tuple(&[self], tuple_qname);
761+
let mut ctx = TypeDisplayContext::new(&[self]);
762+
ctx.add_symbol_qname("tuple", stdlib.tuple_object().qname());
773763
let mut output = OutputWithLocations::new(&ctx);
774764
ctx.fmt_helper_generic(self, false, &mut output).unwrap();
775765
output.parts().to_vec()
@@ -1640,7 +1630,8 @@ def overloaded_func[T](
16401630
t: &Type,
16411631
tuple_qname: &pyrefly_python::qname::QName,
16421632
) -> Vec<(String, Option<TextRangeWithModule>)> {
1643-
let ctx = TypeDisplayContext::new_with_tuple(&[t], Some(tuple_qname));
1633+
let mut ctx = TypeDisplayContext::new(&[t]);
1634+
ctx.add_symbol_qname("tuple", tuple_qname);
16441635
let output = ctx.get_types_with_location(t, false);
16451636
output.parts().to_vec()
16461637
}

crates/pyrefly_types/src/tuple.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::fmt;
1010
use pyrefly_derive::TypeEq;
1111
use pyrefly_derive::Visit;
1212
use pyrefly_derive::VisitMut;
13+
use pyrefly_python::qname::QName;
1314

1415
use crate::type_output::TypeOutput;
1516
use crate::types::Type;
@@ -63,8 +64,13 @@ impl Tuple {
6364
&self,
6465
output: &mut O,
6566
write_type: &impl Fn(&Type, &mut O) -> fmt::Result,
67+
tuple_qname: Option<&QName>,
6668
) -> fmt::Result {
67-
output.write_tuple_keyword()?;
69+
if let Some(qname) = tuple_qname {
70+
output.write_qname(qname)?;
71+
} else {
72+
output.write_str("tuple")?;
73+
}
6874
output.write_str("[")?;
6975
match self {
7076
Self::Concrete(elts) => {

crates/pyrefly_types/src/type_output.rs

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@ pub trait TypeOutput {
2626
fn write_lit(&mut self, lit: &Lit) -> fmt::Result;
2727
fn write_targs(&mut self, targs: &TArgs) -> fmt::Result;
2828
fn write_type(&mut self, ty: &Type) -> fmt::Result;
29-
fn write_tuple_keyword(&mut self) -> fmt::Result {
30-
self.write_str("tuple")
31-
}
3229
}
3330

3431
pub struct DisplayOutput<'a, 'b, 'f> {
@@ -139,16 +136,6 @@ impl TypeOutput for OutputWithLocations<'_> {
139136
// Format the type and extract location if it has a qname
140137
self.context.fmt_helper_generic(ty, false, self)
141138
}
142-
143-
fn write_tuple_keyword(&mut self) -> fmt::Result {
144-
if let Some(qname) = self.context.tuple_qname() {
145-
let location = TextRangeWithModule::new(qname.module().clone(), qname.range());
146-
self.parts.push((qname.id().to_string(), Some(location)));
147-
Ok(())
148-
} else {
149-
self.write_str("tuple")
150-
}
151-
}
152139
}
153140

154141
#[cfg(test)]
@@ -537,7 +524,8 @@ mod tests {
537524
let int_type = Type::ClassType(ClassType::new(int_class, TArgs::default()));
538525
let tuple_type = Type::Tuple(Tuple::Concrete(vec![int_type]));
539526

540-
let ctx = TypeDisplayContext::new_with_tuple(&[&tuple_type], Some(tuple_cls.qname()));
527+
let mut ctx = TypeDisplayContext::new(&[&tuple_type]);
528+
ctx.add_symbol_qname("tuple", tuple_cls.qname());
541529
let mut output = OutputWithLocations::new(&ctx);
542530

543531
ctx.fmt_helper_generic(&tuple_type, false, &mut output)

pyrefly/lib/lsp/wasm/inlay_hints.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,8 @@ impl<'a> Transaction<'a> {
140140
{
141141
ty = return_ty;
142142
}
143-
// Use get_types_with_locations_with_stdlib to get type parts with location info
144-
let type_parts =
145-
ty.get_types_with_locations_with_stdlib(stdlib.as_ref());
143+
// Use get_types_with_locations to get type parts with location info
144+
let type_parts = ty.get_types_with_locations(stdlib.as_ref());
146145
let label_parts = once((" -> ".to_owned(), None))
147146
.chain(
148147
type_parts
@@ -183,8 +182,8 @@ impl<'a> Transaction<'a> {
183182
if let Some(e) = e
184183
&& is_interesting(e, &ty, class_name)
185184
{
186-
// Use get_types_with_locations_with_stdlib to get type parts with location info
187-
let type_parts = ty.get_types_with_locations_with_stdlib(stdlib.as_ref());
185+
// Use get_types_with_locations to get type parts with location info
186+
let type_parts = ty.get_types_with_locations(stdlib.as_ref());
188187
let label_parts = once((": ".to_owned(), None))
189188
.chain(
190189
type_parts

pyrefly/lib/test/lsp/lsp_interaction/inlay_hint.rs

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8+
use lsp_types::request::InlayHintRequest;
89
use serde_json::json;
10+
use serde_json::Value;
911

12+
use crate::test::lsp::lsp_interaction::object_model::ClientRequestHandle;
1013
use crate::test::lsp::lsp_interaction::object_model::InitializeSettings;
1114
use crate::test::lsp::lsp_interaction::object_model::LspInteraction;
1215
use crate::test::lsp::lsp_interaction::util::get_test_files_root;
@@ -23,10 +26,11 @@ fn test_inlay_hint_default_config() {
2326

2427
interaction.client.did_open("inlay_hint_test.py");
2528

26-
interaction
27-
.client
28-
.inlay_hint("inlay_hint_test.py", 0, 0, 100, 0)
29-
.expect_response(json!([
29+
expect_inlay_hint_response(
30+
interaction
31+
.client
32+
.inlay_hint("inlay_hint_test.py", 0, 0, 100, 0),
33+
json!([
3034
{
3135
"label":[
3236
{"value":" -> "},
@@ -85,7 +89,8 @@ fn test_inlay_hint_default_config() {
8589
"range":{"end":{"character":15,"line":14},"start":{"character":15,"line":14}}
8690
}]
8791
}
88-
]));
92+
]),
93+
);
8994

9095
interaction.shutdown();
9196
}
@@ -167,10 +172,11 @@ fn test_inlay_hint_disable_variables() {
167172

168173
interaction.client.did_open("inlay_hint_test.py");
169174

170-
interaction
171-
.client
172-
.inlay_hint("inlay_hint_test.py", 0, 0, 100, 0)
173-
.expect_response(json!([{
175+
expect_inlay_hint_response(
176+
interaction
177+
.client
178+
.inlay_hint("inlay_hint_test.py", 0, 0, 100, 0),
179+
json!([{
174180
"label":[
175181
{"value":" -> "},
176182
{"value":"tuple"},
@@ -205,7 +211,8 @@ fn test_inlay_hint_disable_variables() {
205211
"newText":" -> Literal[0]",
206212
"range":{"end":{"character":15,"line":14},"start":{"character":15,"line":14}}
207213
}]
208-
}]));
214+
}]),
215+
);
209216

210217
interaction.shutdown();
211218
}
@@ -228,10 +235,11 @@ fn test_inlay_hint_disable_returns() {
228235

229236
interaction.client.did_open("inlay_hint_test.py");
230237

231-
interaction
232-
.client
233-
.inlay_hint("inlay_hint_test.py", 0, 0, 100, 0)
234-
.expect_response(json!([{
238+
expect_inlay_hint_response(
239+
interaction
240+
.client
241+
.inlay_hint("inlay_hint_test.py", 0, 0, 100, 0),
242+
json!([{
235243
"label":[
236244
{"value":": "},
237245
{"value":"tuple"},
@@ -252,7 +260,8 @@ fn test_inlay_hint_disable_returns() {
252260
"newText":": tuple[Literal[1], Literal[2]]",
253261
"range":{"end":{"character":6,"line":11},"start":{"character":6,"line":11}}
254262
}]
255-
}]));
263+
}]),
264+
);
256265

257266
interaction.shutdown();
258267
}
@@ -307,3 +316,33 @@ fn test_inlay_hint_labels_support_goto_type_definition() {
307316

308317
interaction.shutdown();
309318
}
319+
320+
fn expect_inlay_hint_response(
321+
handle: ClientRequestHandle<'_, InlayHintRequest>,
322+
expected: Value,
323+
) {
324+
let mut expected = expected;
325+
strip_inlay_hint_locations(&mut expected);
326+
handle.expect_response_with(move |result| {
327+
let mut actual_json = serde_json::to_value(&result).unwrap();
328+
strip_inlay_hint_locations(&mut actual_json);
329+
actual_json == expected
330+
});
331+
}
332+
333+
fn strip_inlay_hint_locations(value: &mut Value) {
334+
match value {
335+
Value::Object(map) => {
336+
map.remove("location");
337+
for inner in map.values_mut() {
338+
strip_inlay_hint_locations(inner);
339+
}
340+
}
341+
Value::Array(items) => {
342+
for item in items {
343+
strip_inlay_hint_locations(item);
344+
}
345+
}
346+
_ => {}
347+
}
348+
}

pyrefly/lib/test/lsp/lsp_interaction/notebook_inlay_hint.rs

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8+
use lsp_types::request::InlayHintRequest;
89
use serde_json::json;
10+
use serde_json::Value;
911

12+
use crate::test::lsp::lsp_interaction::object_model::ClientRequestHandle;
1013
use crate::test::lsp::lsp_interaction::object_model::InitializeSettings;
1114
use crate::test::lsp::lsp_interaction::object_model::LspInteraction;
1215
use crate::test::lsp::lsp_interaction::util::get_test_files_root;
@@ -29,9 +32,10 @@ fn test_inlay_hints() {
2932
],
3033
);
3134

32-
interaction
33-
.inlay_hint_cell("notebook.ipynb", "cell1", 0, 0, 100, 0)
34-
.expect_response(json!([{
35+
expect_inlay_hint_response(
36+
interaction
37+
.inlay_hint_cell("notebook.ipynb", "cell1", 0, 0, 100, 0),
38+
json!([{
3539
"label": [
3640
{"value": " -> "},
3741
{"value": "tuple"},
@@ -52,11 +56,13 @@ fn test_inlay_hints() {
5256
"newText": " -> tuple[Literal[1], Literal[2]]",
5357
"range": {"end": {"character": 21, "line": 0}, "start": {"character": 21, "line": 0}}
5458
}]
55-
}]));
59+
}]),
60+
);
5661

57-
interaction
58-
.inlay_hint_cell("notebook.ipynb", "cell2", 0, 0, 100, 0)
59-
.expect_response(json!([{
62+
expect_inlay_hint_response(
63+
interaction
64+
.inlay_hint_cell("notebook.ipynb", "cell2", 0, 0, 100, 0),
65+
json!([{
6066
"label": [
6167
{"value": ": "},
6268
{"value": "tuple"},
@@ -77,11 +83,13 @@ fn test_inlay_hints() {
7783
"newText": ": tuple[Literal[1], Literal[2]]",
7884
"range": {"end": {"character": 6, "line": 0}, "start": {"character": 6, "line": 0}}
7985
}]
80-
}]));
86+
}]),
87+
);
8188

82-
interaction
83-
.inlay_hint_cell("notebook.ipynb", "cell3", 0, 0, 100, 0)
84-
.expect_response(json!([{
89+
expect_inlay_hint_response(
90+
interaction
91+
.inlay_hint_cell("notebook.ipynb", "cell3", 0, 0, 100, 0),
92+
json!([{
8593
"label": [
8694
{"value": " -> "},
8795
{"value": "Literal"},
@@ -94,6 +102,37 @@ fn test_inlay_hints() {
94102
"newText": " -> Literal[0]",
95103
"range": {"end": {"character": 15, "line": 0}, "start": {"character": 15, "line": 0}}
96104
}]
97-
}]));
105+
}]),
106+
);
98107
interaction.shutdown();
99108
}
109+
110+
fn expect_inlay_hint_response(
111+
handle: ClientRequestHandle<'_, InlayHintRequest>,
112+
expected: Value,
113+
) {
114+
let mut expected = expected;
115+
strip_inlay_hint_locations(&mut expected);
116+
handle.expect_response_with(move |result| {
117+
let mut actual_json = serde_json::to_value(&result).unwrap();
118+
strip_inlay_hint_locations(&mut actual_json);
119+
actual_json == expected
120+
});
121+
}
122+
123+
fn strip_inlay_hint_locations(value: &mut Value) {
124+
match value {
125+
Value::Object(map) => {
126+
map.remove("location");
127+
for inner in map.values_mut() {
128+
strip_inlay_hint_locations(inner);
129+
}
130+
}
131+
Value::Array(items) => {
132+
for item in items {
133+
strip_inlay_hint_locations(item);
134+
}
135+
}
136+
_ => {}
137+
}
138+
}

0 commit comments

Comments
 (0)