diff --git a/CHANGELOG.md b/CHANGELOG.md index c1e5a6240..be38e45b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ #### :rocket: New Feature - Docstring template Code Action. https://github.com/rescript-lang/rescript-vscode/pull/764 +- Add `Some(fieldName)` case when completing in a pattern with an option on a record field. https://github.com/rescript-lang/rescript-vscode/pull/766 ## 1.16.0 diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 08e8f8d45..f5316319d 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1235,13 +1235,26 @@ let rec completeTypedValue ~full ~prefix ~completionContext ~mode | Some insertText -> Some ("Some(" ^ insertText ^ ")")); }) in - [ - Completion.create "None" ~kind:(kindFromInnerType t) ~env; + let noneCase = Completion.create "None" ~kind:(kindFromInnerType t) ~env in + let someAnyCase = Completion.createWithSnippet ~name:"Some(_)" ~kind:(kindFromInnerType t) - ~env ~insertText:"Some(${1:_})" (); - ] - @ expandedCompletions - |> filterItems ~prefix + ~env ~insertText:"Some(${1:_})" () + in + let completions = + match completionContext with + | Some (Completable.CameFromRecordField fieldName) -> + [ + Completion.createWithSnippet + ~name:("Some(" ^ fieldName ^ ")") + ~kind:(kindFromInnerType t) ~env + ~insertText:("Some(${1:" ^ fieldName ^ "})") + (); + someAnyCase; + noneCase; + ] + | _ -> [noneCase; someAnyCase] + in + completions @ expandedCompletions |> filterItems ~prefix | Tuple (env, exprs, typ) -> let numExprs = List.length exprs in [ @@ -1279,7 +1292,7 @@ let rec completeTypedValue ~full ~prefix ~completionContext ~mode (Field (field, TypeUtils.extractedTypeToString extractedType)) ~env) |> filterItems ~prefix - | None -> + | _ -> if prefix = "" then [ Completion.createWithSnippet ~name:"{}" @@ -1304,7 +1317,7 @@ let rec completeTypedValue ~full ~prefix ~completionContext ~mode Completion.create field.fname.txt ~kind:(Label "Inline record") ?deprecated:field.deprecated ~env) |> filterItems ~prefix - | None -> + | _ -> if prefix = "" then [ Completion.createWithSnippet ~name:"{}" diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index df2a36e2f..13c64d59f 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -529,7 +529,12 @@ module Completable = struct | Optional of string (** Additional context for nested completion where needed. *) - type nestedContext = RecordField of {seenFields: string list} + type nestedContext = + | RecordField of {seenFields: string list} + (** Completing for a record field, and we already saw the following fields... *) + | CameFromRecordField of string + (** We just came from this field (we leverage use this for better + completion names etc) *) type nestedPath = | NTupleItem of {itemNum: int} diff --git a/analysis/src/TypeUtils.ml b/analysis/src/TypeUtils.ml index 4911e4abb..43377fdcf 100644 --- a/analysis/src/TypeUtils.ml +++ b/analysis/src/TypeUtils.ml @@ -257,10 +257,20 @@ let extractTypeFromResolvedType (typ : Type.t) ~env ~full = | None -> None | Some t -> t |> extractType ~env ~package:full.package) +(** The context we just came from as we resolve the nested structure. *) +type ctx = Rfield of string (** A record field of name *) + (** This moves through a nested path via a set of instructions, trying to resolve the type at the end of the path. *) -let rec resolveNested (typ : completionType) ~env ~full ~nested = +let rec resolveNested ~env ~full ~nested ?ctx (typ : completionType) = match nested with - | [] -> Some (typ, env, None) + | [] -> + Some + ( typ, + env, + match ctx with + | None -> None + | Some (Rfield fieldName) -> + Some (Completable.CameFromRecordField fieldName) ) | patternPath :: nested -> ( match (patternPath, typ) with | Completable.NTupleItem {itemNum}, Tuple (env, tupleItems, _) -> ( @@ -283,7 +293,7 @@ let rec resolveNested (typ : completionType) ~env ~full ~nested = typ |> extractType ~env ~package:full.package |> Utils.Option.flatMap (fun typ -> - typ |> resolveNested ~env ~full ~nested)) + typ |> resolveNested ~ctx:(Rfield fieldName) ~env ~full ~nested)) | NRecordBody {seenFields}, Trecord {env; definition = `TypeExpr typeExpr} -> typeExpr diff --git a/analysis/tests/src/expected/CompletionExpressions.res.txt b/analysis/tests/src/expected/CompletionExpressions.res.txt index cb80dfcb7..89c0c3013 100644 --- a/analysis/tests/src/expected/CompletionExpressions.res.txt +++ b/analysis/tests/src/expected/CompletionExpressions.res.txt @@ -191,11 +191,13 @@ ContextPath CArgument Value[fnTakingRecord]($0) ContextPath Value[fnTakingRecord] Path fnTakingRecord [{ - "label": "None", + "label": "Some(nested)", "kind": 12, "tags": [], "detail": "otherRecord", - "documentation": null + "documentation": null, + "insertText": "Some(${1:nested})", + "insertTextFormat": 2 }, { "label": "Some(_)", "kind": 12, @@ -204,6 +206,12 @@ Path fnTakingRecord "documentation": null, "insertText": "Some(${1:_})", "insertTextFormat": 2 + }, { + "label": "None", + "kind": 12, + "tags": [], + "detail": "otherRecord", + "documentation": null }, { "label": "Some({})", "kind": 12, @@ -298,21 +306,8 @@ Path fnTakingRecord "tags": [], "detail": "One\n\ntype someVariant = One | Two | Three(int, string)", "documentation": null, - "sortText": "A One", "insertText": "One", "insertTextFormat": 2 - }, { - "label": "Obj", - "kind": 9, - "tags": [], - "detail": "file module", - "documentation": null - }, { - "label": "Objects", - "kind": 9, - "tags": [], - "detail": "file module", - "documentation": null }] Complete src/CompletionExpressions.res 56:57 @@ -616,11 +611,13 @@ ContextPath CArgument Value[fnTakingRecordWithOptVariant]($0) ContextPath Value[fnTakingRecordWithOptVariant] Path fnTakingRecordWithOptVariant [{ - "label": "None", + "label": "Some(someVariant)", "kind": 12, "tags": [], "detail": "someVariant", - "documentation": null + "documentation": null, + "insertText": "Some(${1:someVariant})", + "insertTextFormat": 2 }, { "label": "Some(_)", "kind": 12, @@ -629,6 +626,12 @@ Path fnTakingRecordWithOptVariant "documentation": null, "insertText": "Some(${1:_})", "insertTextFormat": 2 + }, { + "label": "None", + "kind": 12, + "tags": [], + "detail": "someVariant", + "documentation": null }, { "label": "Some(One)", "kind": 4,