Skip to content

Commit 4b555e5

Browse files
authored
Fix expr record field completion in existing records (#997)
* fix case in expr record field completion where the record body context was not picked up * fix case when cursor is at start of record body but in no field * changelog * dont use empty line
1 parent e0a1815 commit 4b555e5

File tree

4 files changed

+75
-2
lines changed

4 files changed

+75
-2
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
- Fix JSX prop special case in end of JSX element. https://github.com/rescript-lang/rescript-vscode/pull/984
2020
- preserve URI format in canonicalizeUri. https://github.com/rescript-lang/rescript-vscode/pull/990
2121
- Remove workaround for canonicalize function in tests https://github.com/rescript-lang/rescript-vscode/pull/992
22+
- Get completions for writing new field names in a record body expressions in more cases. https://github.com/rescript-lang/rescript-vscode/pull/997
2223

2324
#### :nail_care: Polish
2425

analysis/src/CompletionExpressions.ml

+15-2
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ let rec traverseExpr (exp : Parsetree.expression) ~exprPath ~pos
8484
(* An expression hole means for example `{someField: <com>}`. We want to complete for the type of `someField`. *)
8585
someIfHasCursor
8686
("", [Completable.NFollowRecordField {fieldName = fname}] @ exprPath)
87+
| Pexp_ident {txt = Lident txt} when fname = txt ->
88+
(* This is a heuristic for catching writing field names. ReScript has punning for record fields, but the AST doesn't,
89+
so punning is represented as the record field name and identifier being the same: {someField}. *)
90+
someIfHasCursor (txt, [Completable.NRecordBody {seenFields}] @ exprPath)
8791
| Pexp_ident {txt = Lident txt} ->
8892
(* A var means `{someField: s}` or similar. Complete for identifiers or values. *)
8993
someIfHasCursor (txt, exprPath)
@@ -94,12 +98,21 @@ let rec traverseExpr (exp : Parsetree.expression) ~exprPath ~pos
9498
([Completable.NFollowRecordField {fieldName = fname}] @ exprPath)
9599
)
96100
| None, None -> (
101+
if Debug.verbose () then (
102+
Printf.printf "[traverse_expr] No field with cursor and no expr hole.\n";
103+
104+
match firstCharBeforeCursorNoWhite with
105+
| None -> ()
106+
| Some c ->
107+
Printf.printf "[traverse_expr] firstCharBeforeCursorNoWhite: %c.\n" c);
108+
97109
(* Figure out if we're completing for a new field.
98110
If the cursor is inside of the record body, but no field has the cursor,
99111
and there's no pattern hole. Check the first char to the left of the cursor,
100-
ignoring white space. If that's a comma, we assume you're completing for a new field. *)
112+
ignoring white space. If that's a comma or {, we assume you're completing for a new field,
113+
since you're either between 2 fields (comma to the left) or at the start of the record ({). *)
101114
match firstCharBeforeCursorNoWhite with
102-
| Some ',' ->
115+
| Some (',' | '{') ->
103116
someIfHasCursor ("", [Completable.NRecordBody {seenFields}] @ exprPath)
104117
| _ -> None))
105118
| Pexp_construct

analysis/tests/src/CompletionExpressions.res

+23
Original file line numberDiff line numberDiff line change
@@ -362,3 +362,26 @@ let someTyp: someTyp = {test: true}
362362

363363
// switch someTyp. { | _ => () }
364364
// ^com
365+
366+
type config = {
367+
includeName: bool,
368+
operator?: [#"and" | #or],
369+
showMore: bool,
370+
}
371+
372+
type hookReturn = {name: string}
373+
374+
let hook = (config: config) => {
375+
ignore(config)
376+
{
377+
name: "tester",
378+
}
379+
}
380+
381+
let {name} = hook({
382+
// ^com
383+
// ope
384+
// ^com
385+
includeName: true,
386+
showMore: true,
387+
})

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

+36
Original file line numberDiff line numberDiff line change
@@ -1423,3 +1423,39 @@ Path someTyp
14231423
"documentation": {"kind": "markdown", "value": "```rescript\ntest: bool\n```\n\n```rescript\ntype someTyp = {test: bool}\n```"}
14241424
}]
14251425

1426+
Complete src/CompletionExpressions.res 380:22
1427+
posCursor:[380:22] posNoWhite:[380:18] Found expr:[380:13->386:2]
1428+
Pexp_apply ...[380:13->380:17] (...[380:18->386:1])
1429+
Completable: Cexpression CArgument Value[hook]($0)->recordBody
1430+
Raw opens: 1 CompletionSupport.place holder
1431+
Package opens Pervasives.JsxModules.place holder
1432+
Resolved opens 2 pervasives CompletionSupport.res
1433+
ContextPath CArgument Value[hook]($0)
1434+
ContextPath Value[hook]
1435+
Path hook
1436+
[{
1437+
"label": "operator",
1438+
"kind": 5,
1439+
"tags": [],
1440+
"detail": "[#\"and\" | #or]",
1441+
"documentation": {"kind": "markdown", "value": "```rescript\noperator?: [#\"and\" | #or]\n```\n\n```rescript\ntype config = {includeName: bool, operator: option<[#\"and\" | #or]>, showMore: bool}\n```"}
1442+
}]
1443+
1444+
Complete src/CompletionExpressions.res 382:8
1445+
posCursor:[382:8] posNoWhite:[382:7] Found expr:[380:13->386:2]
1446+
Pexp_apply ...[380:13->380:17] (...[380:18->386:1])
1447+
Completable: Cexpression CArgument Value[hook]($0)=ope->recordBody
1448+
Raw opens: 1 CompletionSupport.place holder
1449+
Package opens Pervasives.JsxModules.place holder
1450+
Resolved opens 2 pervasives CompletionSupport.res
1451+
ContextPath CArgument Value[hook]($0)
1452+
ContextPath Value[hook]
1453+
Path hook
1454+
[{
1455+
"label": "operator",
1456+
"kind": 5,
1457+
"tags": [],
1458+
"detail": "[#\"and\" | #or]",
1459+
"documentation": {"kind": "markdown", "value": "```rescript\noperator?: [#\"and\" | #or]\n```\n\n```rescript\ntype config = {includeName: bool, operator: option<[#\"and\" | #or]>, showMore: bool}\n```"}
1460+
}]
1461+

0 commit comments

Comments
 (0)