Skip to content

Commit 8b2f3f5

Browse files
authored
Improve completions for inline record fields (#7601)
* Show completion for inline record fields * Extract shared record completion logic into helper * Add CHANGELOG entry
1 parent 6e6fe0a commit 8b2f3f5

File tree

6 files changed

+172
-78
lines changed

6 files changed

+172
-78
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
- Better error message for when trying to await something that is not a promise. https://github.com/rescript-lang/rescript/pull/7561
5151
- Better error messages for object field missing and object field type mismatches. https://github.com/rescript-lang/rescript/pull/7580
5252
- Better error messages for when polymorphic variants does not match for various reasons. https://github.com/rescript-lang/rescript/pull/7596
53+
- Improved completions for inline records. https://github.com/rescript-lang/rescript/pull/7601
5354

5455
#### :house: Internal
5556

analysis/src/CompletionBackEnd.ml

Lines changed: 46 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1443,6 +1443,48 @@ let rec completeTypedValue ?(typeArgContext : typeArgContext option) ~rawOpens
14431443
let emptyCase = emptyCase ~mode in
14441444
let printConstructorArgs = printConstructorArgs ~mode in
14451445
let create = Completion.create ?typeArgContext in
1446+
let getRecordCompletions ~env ~fields ~extractedType =
1447+
(* As we're completing for a record, we'll need a hint (completionContext)
1448+
here to figure out whether we should complete for a record field, or
1449+
the record body itself. *)
1450+
match completionContext with
1451+
| Some (Completable.RecordField {seenFields}) ->
1452+
fields
1453+
|> List.filter (fun (field : field) ->
1454+
List.mem field.fname.txt seenFields = false)
1455+
|> List.map (fun (field : field) ->
1456+
match (field.optional, mode) with
1457+
| true, Pattern Destructuring ->
1458+
create ("?" ^ field.fname.txt) ?deprecated:field.deprecated
1459+
~docstring:
1460+
[
1461+
field.fname.txt
1462+
^ " is an optional field, and needs to be destructured \
1463+
using '?'.";
1464+
]
1465+
~kind:
1466+
(Field (field, TypeUtils.extractedTypeToString extractedType))
1467+
~env
1468+
| _ ->
1469+
create field.fname.txt ?deprecated:field.deprecated
1470+
~kind:
1471+
(Field (field, TypeUtils.extractedTypeToString extractedType))
1472+
~env)
1473+
|> filterItems ~prefix
1474+
| _ ->
1475+
if prefix = "" then
1476+
[
1477+
create "{}" ~includesSnippets:true ~insertText:"{$0}" ~sortText:"A"
1478+
~kind:
1479+
(ExtractedType
1480+
( extractedType,
1481+
match mode with
1482+
| Pattern _ -> `Type
1483+
| Expression -> `Value ))
1484+
~env;
1485+
]
1486+
else []
1487+
in
14461488
match t with
14471489
| TtypeT {env; path} when mode = Expression ->
14481490
if Debug.verbose () then
@@ -1710,67 +1752,13 @@ let rec completeTypedValue ?(typeArgContext : typeArgContext option) ~rawOpens
17101752
~insertText:(printConstructorArgs numExprs ~asSnippet:true)
17111753
~kind:(Value typ) ~env;
17121754
]
1713-
| Trecord {env; fields} as extractedType -> (
1755+
| Trecord {env; fields} as extractedType ->
17141756
if Debug.verbose () then print_endline "[complete_typed_value]--> Trecord";
1715-
(* As we're completing for a record, we'll need a hint (completionContext)
1716-
here to figure out whether we should complete for a record field, or
1717-
the record body itself. *)
1718-
match completionContext with
1719-
| Some (Completable.RecordField {seenFields}) ->
1720-
fields
1721-
|> List.filter (fun (field : field) ->
1722-
List.mem field.fname.txt seenFields = false)
1723-
|> List.map (fun (field : field) ->
1724-
match (field.optional, mode) with
1725-
| true, Pattern Destructuring ->
1726-
create ("?" ^ field.fname.txt) ?deprecated:field.deprecated
1727-
~docstring:
1728-
[
1729-
field.fname.txt
1730-
^ " is an optional field, and needs to be destructured \
1731-
using '?'.";
1732-
]
1733-
~kind:
1734-
(Field (field, TypeUtils.extractedTypeToString extractedType))
1735-
~env
1736-
| _ ->
1737-
create field.fname.txt ?deprecated:field.deprecated
1738-
~kind:
1739-
(Field (field, TypeUtils.extractedTypeToString extractedType))
1740-
~env)
1741-
|> filterItems ~prefix
1742-
| _ ->
1743-
if prefix = "" then
1744-
[
1745-
create "{}" ~includesSnippets:true ~insertText:"{$0}" ~sortText:"A"
1746-
~kind:
1747-
(ExtractedType
1748-
( extractedType,
1749-
match mode with
1750-
| Pattern _ -> `Type
1751-
| Expression -> `Value ))
1752-
~env;
1753-
]
1754-
else [])
1755-
| TinlineRecord {env; fields} -> (
1757+
getRecordCompletions ~env ~fields ~extractedType
1758+
| TinlineRecord {env; fields} as extractedType ->
17561759
if Debug.verbose () then
17571760
print_endline "[complete_typed_value]--> TinlineRecord";
1758-
match completionContext with
1759-
| Some (Completable.RecordField {seenFields}) ->
1760-
fields
1761-
|> List.filter (fun (field : field) ->
1762-
List.mem field.fname.txt seenFields = false)
1763-
|> List.map (fun (field : field) ->
1764-
create field.fname.txt ~kind:(Label "Inline record")
1765-
?deprecated:field.deprecated ~env)
1766-
|> filterItems ~prefix
1767-
| _ ->
1768-
if prefix = "" then
1769-
[
1770-
create "{}" ~includesSnippets:true ~insertText:"{$0}" ~sortText:"A"
1771-
~kind:(Label "Inline record") ~env;
1772-
]
1773-
else [])
1761+
getRecordCompletions ~env ~fields ~extractedType
17741762
| Tarray (env, typ) ->
17751763
if Debug.verbose () then print_endline "[complete_typed_value]--> Tarray";
17761764
if prefix = "" then

tests/analysis_tests/tests-incremental-typechecking/src/expected/ConstructorCompletion__Own.res.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ Resolved opens 1 Stdlib
88
ContextPath CTypeAtPos()
99
[{
1010
"label": "{}",
11-
"kind": 4,
11+
"kind": 12,
1212
"tags": [],
13-
"detail": "Inline record",
14-
"documentation": null,
13+
"detail": "{miss: bool}",
14+
"documentation": {"kind": "markdown", "value": "```rescript\n{miss: bool}\n```"},
1515
"sortText": "A",
1616
"insertText": "{$0}",
1717
"insertTextFormat": 2

tests/analysis_tests/tests/src/Completion.res

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,3 +465,20 @@ type withUncurried = {fn: int => unit}
465465

466466
// let someRecord = { FAR. }
467467
// ^com
468+
469+
type someRecord = {field1: string, field2: int}
470+
type someVariantWithRecord = HasRecord(someRecord)
471+
472+
// let v: someVariantWithRecord = HasRecord({})
473+
// ^com
474+
475+
// let v: someVariantWithRecord = HasRecord({fie})
476+
// ^com
477+
478+
type someVariantWithInlineRecord = HasInlineRecord({field1: string, field2: int})
479+
480+
// let v: someVariantWithInlineRecord = HasInlineRecord({})
481+
// ^com
482+
483+
// let v: someVariantWithInlineRecord = HasInlineRecord({fie})
484+
// ^com

tests/analysis_tests/tests/src/expected/Completion.res.txt

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2342,8 +2342,8 @@ Path red
23422342
}]
23432343

23442344
Complete src/Completion.res 407:22
2345-
posCursor:[407:22] posNoWhite:[407:21] Found expr:[407:11->468:0]
2346-
Pexp_apply ...__ghost__[0:-1->0:-1] (...[407:11->425:17], ...[430:0->468:0])
2345+
posCursor:[407:22] posNoWhite:[407:21] Found expr:[407:11->485:0]
2346+
Pexp_apply ...__ghost__[0:-1->0:-1] (...[407:11->425:17], ...[430:0->485:0])
23472347
posCursor:[407:22] posNoWhite:[407:21] Found expr:[407:11->425:17]
23482348
Pexp_apply ...__ghost__[0:-1->0:-1] (...[407:11->407:19], ...[407:21->425:17])
23492349
posCursor:[407:22] posNoWhite:[407:21] Found expr:[407:21->425:17]
@@ -2747,3 +2747,91 @@ Path FAR.
27472747
"documentation": {"kind": "markdown", "value": "```rescript\nsomething: option<int>\n```\n\n```rescript\ntype forAutoRecord = {\n forAuto: ForAuto.t,\n something: option<int>,\n}\n```"}
27482748
}]
27492749

2750+
Complete src/Completion.res 471:45
2751+
XXX Not found!
2752+
Completable: Cexpression Type[someVariantWithRecord]->variantPayload::HasRecord($0), recordBody
2753+
Raw opens: 2 Shadow.B.place holder ... Shadow.A.place holder
2754+
Package opens Stdlib.place holder Pervasives.JsxModules.place holder
2755+
Resolved opens 3 Stdlib Completion Completion
2756+
ContextPath Type[someVariantWithRecord]
2757+
Path someVariantWithRecord
2758+
[{
2759+
"label": "field1",
2760+
"kind": 5,
2761+
"tags": [],
2762+
"detail": "string",
2763+
"documentation": {"kind": "markdown", "value": "```rescript\nfield1: string\n```\n\n```rescript\ntype someRecord = {field1: string, field2: int}\n```"}
2764+
}, {
2765+
"label": "field2",
2766+
"kind": 5,
2767+
"tags": [],
2768+
"detail": "int",
2769+
"documentation": {"kind": "markdown", "value": "```rescript\nfield2: int\n```\n\n```rescript\ntype someRecord = {field1: string, field2: int}\n```"}
2770+
}]
2771+
2772+
Complete src/Completion.res 474:48
2773+
XXX Not found!
2774+
Completable: Cexpression Type[someVariantWithRecord]=fie->variantPayload::HasRecord($0), recordBody
2775+
Raw opens: 2 Shadow.B.place holder ... Shadow.A.place holder
2776+
Package opens Stdlib.place holder Pervasives.JsxModules.place holder
2777+
Resolved opens 3 Stdlib Completion Completion
2778+
ContextPath Type[someVariantWithRecord]
2779+
Path someVariantWithRecord
2780+
[{
2781+
"label": "field1",
2782+
"kind": 5,
2783+
"tags": [],
2784+
"detail": "string",
2785+
"documentation": {"kind": "markdown", "value": "```rescript\nfield1: string\n```\n\n```rescript\ntype someRecord = {field1: string, field2: int}\n```"}
2786+
}, {
2787+
"label": "field2",
2788+
"kind": 5,
2789+
"tags": [],
2790+
"detail": "int",
2791+
"documentation": {"kind": "markdown", "value": "```rescript\nfield2: int\n```\n\n```rescript\ntype someRecord = {field1: string, field2: int}\n```"}
2792+
}]
2793+
2794+
Complete src/Completion.res 479:57
2795+
XXX Not found!
2796+
Completable: Cexpression Type[someVariantWithInlineRecord]->variantPayload::HasInlineRecord($0), recordBody
2797+
Raw opens: 2 Shadow.B.place holder ... Shadow.A.place holder
2798+
Package opens Stdlib.place holder Pervasives.JsxModules.place holder
2799+
Resolved opens 3 Stdlib Completion Completion
2800+
ContextPath Type[someVariantWithInlineRecord]
2801+
Path someVariantWithInlineRecord
2802+
[{
2803+
"label": "field1",
2804+
"kind": 5,
2805+
"tags": [],
2806+
"detail": "string",
2807+
"documentation": {"kind": "markdown", "value": "```rescript\nfield1: string\n```\n\n```rescript\n{field1: string, field2: int}\n```"}
2808+
}, {
2809+
"label": "field2",
2810+
"kind": 5,
2811+
"tags": [],
2812+
"detail": "int",
2813+
"documentation": {"kind": "markdown", "value": "```rescript\nfield2: int\n```\n\n```rescript\n{field1: string, field2: int}\n```"}
2814+
}]
2815+
2816+
Complete src/Completion.res 482:60
2817+
XXX Not found!
2818+
Completable: Cexpression Type[someVariantWithInlineRecord]=fie->variantPayload::HasInlineRecord($0), recordBody
2819+
Raw opens: 2 Shadow.B.place holder ... Shadow.A.place holder
2820+
Package opens Stdlib.place holder Pervasives.JsxModules.place holder
2821+
Resolved opens 3 Stdlib Completion Completion
2822+
ContextPath Type[someVariantWithInlineRecord]
2823+
Path someVariantWithInlineRecord
2824+
[{
2825+
"label": "field1",
2826+
"kind": 5,
2827+
"tags": [],
2828+
"detail": "string",
2829+
"documentation": {"kind": "markdown", "value": "```rescript\nfield1: string\n```\n\n```rescript\n{field1: string, field2: int}\n```"}
2830+
}, {
2831+
"label": "field2",
2832+
"kind": 5,
2833+
"tags": [],
2834+
"detail": "int",
2835+
"documentation": {"kind": "markdown", "value": "```rescript\nfield2: int\n```\n\n```rescript\n{field1: string, field2: int}\n```"}
2836+
}]
2837+

tests/analysis_tests/tests/src/expected/CompletionExpressions.res.txt

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -700,10 +700,10 @@ ContextPath Value[fnTakingInlineRecord]
700700
Path fnTakingInlineRecord
701701
[{
702702
"label": "{}",
703-
"kind": 4,
703+
"kind": 12,
704704
"tags": [],
705-
"detail": "Inline record",
706-
"documentation": null,
705+
"detail": "{someBoolField: bool, otherField: option<bool>, nestedRecord: otherRecord}",
706+
"documentation": {"kind": "markdown", "value": "```rescript\n{someBoolField: bool, otherField: option<bool>, nestedRecord: otherRecord}\n```"},
707707
"sortText": "A",
708708
"insertText": "{$0}",
709709
"insertTextFormat": 2
@@ -720,22 +720,22 @@ ContextPath Value[fnTakingInlineRecord]
720720
Path fnTakingInlineRecord
721721
[{
722722
"label": "someBoolField",
723-
"kind": 4,
723+
"kind": 5,
724724
"tags": [],
725-
"detail": "Inline record",
726-
"documentation": null
725+
"detail": "bool",
726+
"documentation": {"kind": "markdown", "value": "```rescript\nsomeBoolField: bool\n```\n\n```rescript\n{someBoolField: bool, otherField: option<bool>, nestedRecord: otherRecord}\n```"}
727727
}, {
728728
"label": "otherField",
729-
"kind": 4,
729+
"kind": 5,
730730
"tags": [],
731-
"detail": "Inline record",
732-
"documentation": null
731+
"detail": "option<bool>",
732+
"documentation": {"kind": "markdown", "value": "```rescript\notherField: option<bool>\n```\n\n```rescript\n{someBoolField: bool, otherField: option<bool>, nestedRecord: otherRecord}\n```"}
733733
}, {
734734
"label": "nestedRecord",
735-
"kind": 4,
735+
"kind": 5,
736736
"tags": [],
737-
"detail": "Inline record",
738-
"documentation": null
737+
"detail": "otherRecord",
738+
"documentation": {"kind": "markdown", "value": "```rescript\nnestedRecord: otherRecord\n```\n\n```rescript\n{someBoolField: bool, otherField: option<bool>, nestedRecord: otherRecord}\n```"}
739739
}]
740740

741741
Complete src/CompletionExpressions.res 132:51
@@ -749,10 +749,10 @@ ContextPath Value[fnTakingInlineRecord]
749749
Path fnTakingInlineRecord
750750
[{
751751
"label": "someBoolField",
752-
"kind": 4,
752+
"kind": 5,
753753
"tags": [],
754-
"detail": "Inline record",
755-
"documentation": null
754+
"detail": "bool",
755+
"documentation": {"kind": "markdown", "value": "```rescript\nsomeBoolField: bool\n```\n\n```rescript\n{someBoolField: bool, otherField: option<bool>, nestedRecord: otherRecord}\n```"}
756756
}]
757757

758758
Complete src/CompletionExpressions.res 135:63

0 commit comments

Comments
 (0)