diff --git a/CHANGELOG.md b/CHANGELOG.md index 98d114866..47a20f1a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - Add support for pattern completion of unsaved tuples. https://github.com/rescript-lang/rescript-vscode/pull/679 - Add support for completion in typed expressions. https://github.com/rescript-lang/rescript-vscode/pull/682 - Complete for `React.element` creator functions (`React.string` etc) when in JSX context. https://github.com/rescript-lang/rescript-vscode/pull/681 +- Handle optional record fields in expression/pattern completion. https://github.com/rescript-lang/rescript-vscode/pull/691 #### :nail_care: Polish diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index bf9bf65f0..1302a374f 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1925,7 +1925,9 @@ let rec resolveNested typ ~env ~package ~nested = |> List.find_opt (fun (field : field) -> field.fname.txt = fieldName) with | None -> None - | Some {typ} -> typ |> resolveNested ~env ~package ~nested) + | Some {typ; optional} -> + let typ = if optional then Utils.unwrapIfOption typ else typ in + typ |> resolveNested ~env ~package ~nested) | NRecordBody {seenFields}, Some (Trecord {env; typeExpr}) -> Some (typeExpr, env, Some (Completable.RecordField {seenFields})) | ( NVariantPayload {constructorName = "Some"; itemNum = 0}, diff --git a/analysis/src/ProcessCmt.ml b/analysis/src/ProcessCmt.ml index dab690758..901c19a12 100644 --- a/analysis/src/ProcessCmt.ml +++ b/analysis/src/ProcessCmt.ml @@ -89,13 +89,16 @@ let rec forTypeSignatureItem ~(env : SharedTypes.Env.t) ~(exported : Exported.t) | Type_record (fields, _) -> Record (fields - |> List.map (fun {Types.ld_id; ld_type} -> + |> List.map (fun {Types.ld_id; ld_type; ld_attributes} -> let astamp = Ident.binding_time ld_id in let name = Ident.name ld_id in { stamp = astamp; fname = Location.mknoloc name; typ = ld_type; + optional = + Res_parsetree_viewer.hasOptionalAttribute + ld_attributes; }))); } ~name ~stamp:(Ident.binding_time ident) ~env type_attributes @@ -212,10 +215,22 @@ let forTypeDeclaration ~env ~(exported : Exported.t) (fields |> List.map (fun - {Typedtree.ld_id; ld_name = fname; ld_type = {ctyp_type}} + { + Typedtree.ld_id; + ld_name = fname; + ld_type = {ctyp_type}; + ld_attributes; + } -> let fstamp = Ident.binding_time ld_id in - {stamp = fstamp; fname; typ = ctyp_type}))); + { + stamp = fstamp; + fname; + typ = ctyp_type; + optional = + Res_parsetree_viewer.hasOptionalAttribute + ld_attributes; + }))); } ~name ~stamp ~env typ_attributes (Exported.add exported Exported.Type) diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index a9962c62f..768a7bb7e 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -24,7 +24,12 @@ module ModulePath = struct loop modulePath [tipName] end -type field = {stamp: int; fname: string Location.loc; typ: Types.type_expr} +type field = { + stamp: int; + fname: string Location.loc; + typ: Types.type_expr; + optional: bool; +} module Constructor = struct type t = { diff --git a/analysis/tests/src/CompletionExpressions.res b/analysis/tests/src/CompletionExpressions.res index 2cd24682b..408467075 100644 --- a/analysis/tests/src/CompletionExpressions.res +++ b/analysis/tests/src/CompletionExpressions.res @@ -96,3 +96,15 @@ let fnTakingOtherRecord = (r: otherRecord) => { // let _ = fnTakingOtherRecord({otherField: }) // ^com + +type recordWithOptionalField = { + someField: int, + someOptField?: bool, +} + +let fnTakingRecordWithOptionalField = (r: recordWithOptionalField) => { + ignore(r) +} + +// let _ = fnTakingRecordWithOptionalField({someOptField: }) +// ^com diff --git a/analysis/tests/src/expected/CompletionExpressions.res.txt b/analysis/tests/src/expected/CompletionExpressions.res.txt index 0a9c2fc89..0d014613d 100644 --- a/analysis/tests/src/expected/CompletionExpressions.res.txt +++ b/analysis/tests/src/expected/CompletionExpressions.res.txt @@ -427,3 +427,21 @@ Completable: Cexpression CArgument Value[fnTakingOtherRecord]($0)->recordField(o "insertTextFormat": 2 }] +Complete src/CompletionExpressions.res 108:57 +posCursor:[108:57] posNoWhite:[108:56] Found expr:[108:11->108:60] +Pexp_apply ...[108:11->108:42] (...[108:43->108:60]) +Completable: Cexpression CArgument Value[fnTakingRecordWithOptionalField]($0)->recordField(someOptField) +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "false", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] +