Skip to content

Commit 47541c4

Browse files
test
1 parent 43c8254 commit 47541c4

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
@@ -93,19 +93,12 @@ pub struct TypeDisplayContext<'a> {
9393
/// Should we display for IDE Hover? This makes type names more readable but less precise.
9494
hover: bool,
9595
always_display_module_name: bool,
96-
tuple_qname: Option<&'a QName>,
96+
extra_symbol_qnames: SmallMap<&'static str, &'a QName>,
9797
}
9898

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

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

136134
/// Force that we always display at least the module name for qualified names.
@@ -585,9 +583,11 @@ impl<'a> TypeDisplayContext<'a> {
585583
}
586584
}
587585
Type::Intersect(x) => self.fmt_type_sequence(x.0.iter(), " & ", true, output),
588-
Type::Tuple(t) => {
589-
t.fmt_with_type(output, &|ty, o| self.fmt_helper_generic(ty, false, o))
590-
}
586+
Type::Tuple(t) => t.fmt_with_type(
587+
output,
588+
&|ty, o| self.fmt_helper_generic(ty, false, o),
589+
self.symbol_qname("tuple"),
590+
),
591591
Type::Forall(box Forall {
592592
tparams,
593593
body: body @ Forallable::Callable(c),
@@ -798,22 +798,12 @@ impl Type {
798798
c.display(self).to_string()
799799
}
800800

801-
pub fn get_types_with_locations(&self) -> Vec<(String, Option<TextRangeWithModule>)> {
802-
self.get_types_with_locations_with_tuple_qname(None)
803-
}
804-
805-
pub fn get_types_with_locations_with_stdlib(
801+
pub fn get_types_with_locations(
806802
&self,
807803
stdlib: &Stdlib,
808804
) -> Vec<(String, Option<TextRangeWithModule>)> {
809-
self.get_types_with_locations_with_tuple_qname(Some(stdlib.tuple_object().qname()))
810-
}
811-
812-
fn get_types_with_locations_with_tuple_qname(
813-
&self,
814-
tuple_qname: Option<&QName>,
815-
) -> Vec<(String, Option<TextRangeWithModule>)> {
816-
let ctx = TypeDisplayContext::new_with_tuple(&[self], tuple_qname);
805+
let mut ctx = TypeDisplayContext::new(&[self]);
806+
ctx.add_symbol_qname("tuple", stdlib.tuple_object().qname());
817807
let mut output = OutputWithLocations::new(&ctx);
818808
ctx.fmt_helper_generic(self, false, &mut output).unwrap();
819809
output.parts().to_vec()
@@ -1692,7 +1682,8 @@ def overloaded_func[T](
16921682
t: &Type,
16931683
tuple_qname: &pyrefly_python::qname::QName,
16941684
) -> Vec<(String, Option<TextRangeWithModule>)> {
1695-
let ctx = TypeDisplayContext::new_with_tuple(&[t], Some(tuple_qname));
1685+
let mut ctx = TypeDisplayContext::new(&[t]);
1686+
ctx.add_symbol_qname("tuple", tuple_qname);
16961687
let output = ctx.get_types_with_location(t, false);
16971688
output.parts().to_vec()
16981689
}

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
/// Implementation of `TypeOutput` that writes formatted types to plain text.
@@ -143,16 +140,6 @@ impl TypeOutput for OutputWithLocations<'_> {
143140
// Format the type and extract location if it has a qname
144141
self.context.fmt_helper_generic(ty, false, self)
145142
}
146-
147-
fn write_tuple_keyword(&mut self) -> fmt::Result {
148-
if let Some(qname) = self.context.tuple_qname() {
149-
let location = TextRangeWithModule::new(qname.module().clone(), qname.range());
150-
self.parts.push((qname.id().to_string(), Some(location)));
151-
Ok(())
152-
} else {
153-
self.write_str("tuple")
154-
}
155-
}
156143
}
157144

158145
#[cfg(test)]
@@ -541,7 +528,8 @@ mod tests {
541528
let int_type = Type::ClassType(ClassType::new(int_class, TArgs::default()));
542529
let tuple_type = Type::Tuple(Tuple::Concrete(vec![int_type]));
543530

544-
let ctx = TypeDisplayContext::new_with_tuple(&[&tuple_type], Some(tuple_cls.qname()));
531+
let mut ctx = TypeDisplayContext::new(&[&tuple_type]);
532+
ctx.add_symbol_qname("tuple", tuple_cls.qname());
545533
let mut output = OutputWithLocations::new(&ctx);
546534

547535
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;
@@ -25,10 +28,11 @@ fn test_inlay_hint_default_config() {
2528

2629
interaction.client.did_open("inlay_hint_test.py");
2730

28-
interaction
29-
.client
30-
.inlay_hint("inlay_hint_test.py", 0, 0, 100, 0)
31-
.expect_response(json!([
31+
expect_inlay_hint_response(
32+
interaction
33+
.client
34+
.inlay_hint("inlay_hint_test.py", 0, 0, 100, 0),
35+
json!([
3236
{
3337
"label":[
3438
{"value":" -> "},
@@ -87,7 +91,8 @@ fn test_inlay_hint_default_config() {
8791
"range":{"end":{"character":15,"line":14},"start":{"character":15,"line":14}}
8892
}]
8993
}
90-
]))
94+
]),
95+
)
9196
.unwrap();
9297

9398
interaction.shutdown().unwrap();
@@ -178,10 +183,11 @@ fn test_inlay_hint_disable_variables() {
178183

179184
interaction.client.did_open("inlay_hint_test.py");
180185

181-
interaction
182-
.client
183-
.inlay_hint("inlay_hint_test.py", 0, 0, 100, 0)
184-
.expect_response(json!([{
186+
expect_inlay_hint_response(
187+
interaction
188+
.client
189+
.inlay_hint("inlay_hint_test.py", 0, 0, 100, 0),
190+
json!([{
185191
"label":[
186192
{"value":" -> "},
187193
{"value":"tuple"},
@@ -216,7 +222,8 @@ fn test_inlay_hint_disable_variables() {
216222
"newText":" -> Literal[0]",
217223
"range":{"end":{"character":15,"line":14},"start":{"character":15,"line":14}}
218224
}]
219-
}]))
225+
}]),
226+
)
220227
.unwrap();
221228

222229
interaction.shutdown().unwrap();
@@ -242,10 +249,11 @@ fn test_inlay_hint_disable_returns() {
242249

243250
interaction.client.did_open("inlay_hint_test.py");
244251

245-
interaction
246-
.client
247-
.inlay_hint("inlay_hint_test.py", 0, 0, 100, 0)
248-
.expect_response(json!([{
252+
expect_inlay_hint_response(
253+
interaction
254+
.client
255+
.inlay_hint("inlay_hint_test.py", 0, 0, 100, 0),
256+
json!([{
249257
"label":[
250258
{"value":": "},
251259
{"value":"tuple"},
@@ -266,7 +274,8 @@ fn test_inlay_hint_disable_returns() {
266274
"newText":": tuple[Literal[1], Literal[2]]",
267275
"range":{"end":{"character":6,"line":11},"start":{"character":6,"line":11}}
268276
}]
269-
}]))
277+
}]),
278+
)
270279
.unwrap();
271280

272281
interaction.shutdown().unwrap();
@@ -325,3 +334,33 @@ fn test_inlay_hint_labels_support_goto_type_definition() {
325334

326335
interaction.shutdown().unwrap();
327336
}
337+
338+
fn expect_inlay_hint_response(
339+
handle: ClientRequestHandle<'_, InlayHintRequest>,
340+
expected: Value,
341+
) {
342+
let mut expected = expected;
343+
strip_inlay_hint_locations(&mut expected);
344+
handle.expect_response_with(move |result| {
345+
let mut actual_json = serde_json::to_value(&result).unwrap();
346+
strip_inlay_hint_locations(&mut actual_json);
347+
actual_json == expected
348+
});
349+
}
350+
351+
fn strip_inlay_hint_locations(value: &mut Value) {
352+
match value {
353+
Value::Object(map) => {
354+
map.remove("location");
355+
for inner in map.values_mut() {
356+
strip_inlay_hint_locations(inner);
357+
}
358+
}
359+
Value::Array(items) => {
360+
for item in items {
361+
strip_inlay_hint_locations(item);
362+
}
363+
}
364+
_ => {}
365+
}
366+
}

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;
@@ -31,9 +34,10 @@ fn test_inlay_hints() {
3134
],
3235
);
3336

34-
interaction
35-
.inlay_hint_cell("notebook.ipynb", "cell1", 0, 0, 100, 0)
36-
.expect_response(json!([{
37+
expect_inlay_hint_response(
38+
interaction
39+
.inlay_hint_cell("notebook.ipynb", "cell1", 0, 0, 100, 0),
40+
json!([{
3741
"label": [
3842
{"value": " -> "},
3943
{"value": "tuple"},
@@ -54,12 +58,14 @@ fn test_inlay_hints() {
5458
"newText": " -> tuple[Literal[1], Literal[2]]",
5559
"range": {"end": {"character": 21, "line": 0}, "start": {"character": 21, "line": 0}}
5660
}]
57-
}]))
61+
}]),
62+
)
5863
.unwrap();
5964

60-
interaction
61-
.inlay_hint_cell("notebook.ipynb", "cell2", 0, 0, 100, 0)
62-
.expect_response(json!([{
65+
expect_inlay_hint_response(
66+
interaction
67+
.inlay_hint_cell("notebook.ipynb", "cell2", 0, 0, 100, 0),
68+
json!([{
6369
"label": [
6470
{"value": ": "},
6571
{"value": "tuple"},
@@ -80,12 +86,14 @@ fn test_inlay_hints() {
8086
"newText": ": tuple[Literal[1], Literal[2]]",
8187
"range": {"end": {"character": 6, "line": 0}, "start": {"character": 6, "line": 0}}
8288
}]
83-
}]))
89+
}]),
90+
)
8491
.unwrap();
8592

86-
interaction
87-
.inlay_hint_cell("notebook.ipynb", "cell3", 0, 0, 100, 0)
88-
.expect_response(json!([{
93+
expect_inlay_hint_response(
94+
interaction
95+
.inlay_hint_cell("notebook.ipynb", "cell3", 0, 0, 100, 0),
96+
json!([{
8997
"label": [
9098
{"value": " -> "},
9199
{"value": "Literal"},
@@ -98,7 +106,38 @@ fn test_inlay_hints() {
98106
"newText": " -> Literal[0]",
99107
"range": {"end": {"character": 15, "line": 0}, "start": {"character": 15, "line": 0}}
100108
}]
101-
}]))
109+
}]),
110+
)
102111
.unwrap();
103112
interaction.shutdown().unwrap();
104113
}
114+
115+
fn expect_inlay_hint_response(
116+
handle: ClientRequestHandle<'_, InlayHintRequest>,
117+
expected: Value,
118+
) {
119+
let mut expected = expected;
120+
strip_inlay_hint_locations(&mut expected);
121+
handle.expect_response_with(move |result| {
122+
let mut actual_json = serde_json::to_value(&result).unwrap();
123+
strip_inlay_hint_locations(&mut actual_json);
124+
actual_json == expected
125+
});
126+
}
127+
128+
fn strip_inlay_hint_locations(value: &mut Value) {
129+
match value {
130+
Value::Object(map) => {
131+
map.remove("location");
132+
for inner in map.values_mut() {
133+
strip_inlay_hint_locations(inner);
134+
}
135+
}
136+
Value::Array(items) => {
137+
for item in items {
138+
strip_inlay_hint_locations(item);
139+
}
140+
}
141+
_ => {}
142+
}
143+
}

0 commit comments

Comments
 (0)