From 4408471cd1d7f71a9909fd26e72eae8191aa92e9 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 7 Dec 2023 21:27:09 +0100 Subject: [PATCH 1/2] handle result type as a builtin --- analysis/src/CompletionBackEnd.ml | 67 +++++++++++++++++++ analysis/src/SharedTypes.ml | 5 ++ analysis/src/TypeUtils.ml | 55 +++++++++++++++ analysis/tests/src/CompletionPattern.res | 3 + .../src/expected/CompletionPattern.res.txt | 36 ++++++++++ 5 files changed, 166 insertions(+) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index f3846c4f7..04a0634bf 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1283,6 +1283,71 @@ let rec completeTypedValue ~full ~prefix ~completionContext ~mode | _ -> [noneCase; someAnyCase] in completions @ expandedCompletions |> filterItems ~prefix + | Tresult {env; okType; errorType} -> + let okInnerType = + okType |> TypeUtils.extractType ~env ~package:full.package + in + let errorInnerType = + errorType |> TypeUtils.extractType ~env ~package:full.package + in + let expandedOkCompletions = + match okInnerType with + | None -> [] + | Some innerType -> + innerType + |> completeTypedValue ~full ~prefix ~completionContext ~mode + |> List.map (fun (c : Completion.t) -> + { + c with + name = "Ok(" ^ c.name ^ ")"; + sortText = None; + insertText = + (match c.insertText with + | None -> None + | Some insertText -> Some ("Ok(" ^ insertText ^ ")")); + }) + in + let expandedErrorCompletions = + match errorInnerType with + | None -> [] + | Some innerType -> + innerType + |> completeTypedValue ~full ~prefix ~completionContext ~mode + |> List.map (fun (c : Completion.t) -> + { + c with + name = "Error(" ^ c.name ^ ")"; + sortText = None; + insertText = + (match c.insertText with + | None -> None + | Some insertText -> Some ("Error(" ^ insertText ^ ")")); + }) + in + let okAnyCase = + Completion.createWithSnippet ~name:"Ok(_)" ~kind:(Value okType) ~env + ~insertText:"Ok(${1:_})" () + in + let errorAnyCase = + Completion.createWithSnippet ~name:"Error(_)" ~kind:(Value errorType) ~env + ~insertText:"Error(${1:_})" () + in + let completions = + match completionContext with + | Some (Completable.CameFromRecordField fieldName) -> + [ + Completion.createWithSnippet + ~name:("Ok(" ^ fieldName ^ ")") + ~kind:(Value okType) ~env + ~insertText:("Ok(" ^ fieldName ^ ")$0") + (); + okAnyCase; + errorAnyCase; + ] + | _ -> [okAnyCase; errorAnyCase] + in + completions @ expandedOkCompletions @ expandedErrorCompletions + |> filterItems ~prefix | Tuple (env, exprs, typ) -> let numExprs = List.length exprs in [ @@ -1715,6 +1780,8 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable = | _ -> "(_)")) | Some (Toption (_env, _typ)) -> withExhaustiveItem c ~cases:["Some($1)"; "None"] ~startIndex:1 + | Some (Tresult _) -> + withExhaustiveItem c ~cases:["Ok($1)"; "Error($1)"] ~startIndex:1 | Some (Tbool _) -> withExhaustiveItem c ~cases:["true"; "false"] | _ -> [c]) | _ -> [c]) diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index a32576e2c..5cbbce741 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -319,6 +319,11 @@ and completionType = | Texn of QueryEnv.t | Tpromise of QueryEnv.t * Types.type_expr | Toption of QueryEnv.t * innerType + | Tresult of { + env: QueryEnv.t; + okType: Types.type_expr; + errorType: Types.type_expr; + } | Tbool of QueryEnv.t | Tarray of QueryEnv.t * innerType | Tstring of QueryEnv.t diff --git a/analysis/src/TypeUtils.ml b/analysis/src/TypeUtils.ml index a0ea3210a..63940302e 100644 --- a/analysis/src/TypeUtils.ml +++ b/analysis/src/TypeUtils.ml @@ -119,6 +119,8 @@ let rec extractType ~env ~package (t : Types.type_expr) = Some (Tpromise (env, payloadTypeExpr)) | Tconstr (Path.Pident {name = "array"}, [payloadTypeExpr], _) -> Some (Tarray (env, TypeExpr payloadTypeExpr)) + | Tconstr (Path.Pident {name = "result"}, [okType; errorType], _) -> + Some (Tresult {env; okType; errorType}) | Tconstr (Path.Pident {name = "bool"}, [], _) -> Some (Tbool env) | Tconstr (Path.Pident {name = "string"}, [], _) -> Some (Tstring env) | Tconstr (Path.Pident {name = "exn"}, [], _) -> Some (Texn env) @@ -362,6 +364,15 @@ let rec resolveNested ~env ~full ~nested ?ctx (typ : completionType) = typ |> extractType ~env ~package:full.package |> Utils.Option.flatMap (fun t -> t |> resolveNested ~env ~full ~nested) + | NVariantPayload {constructorName = "Ok"; itemNum = 0}, Tresult {okType} -> + okType + |> extractType ~env ~package:full.package + |> Utils.Option.flatMap (fun t -> t |> resolveNested ~env ~full ~nested) + | ( NVariantPayload {constructorName = "Error"; itemNum = 0}, + Tresult {errorType} ) -> + errorType + |> extractType ~env ~package:full.package + |> Utils.Option.flatMap (fun t -> t |> resolveNested ~env ~full ~nested) | ( NVariantPayload {constructorName; itemNum}, Tvariant {env; constructors; typeParams; typeArgs} ) -> ( match @@ -481,6 +492,12 @@ let rec resolveNestedPatternPath (typ : innerType) ~env ~full ~nested = | ( NVariantPayload {constructorName = "Some"; itemNum = 0}, Toption (env, typ) ) -> Some (typ, env) + | ( NVariantPayload {constructorName = "Ok"; itemNum = 0}, + Tresult {env; okType} ) -> + Some (TypeExpr okType, env) + | ( NVariantPayload {constructorName = "Error"; itemNum = 0}, + Tresult {env; errorType} ) -> + Some (TypeExpr errorType, env) | NArray, Tarray (env, typ) -> Some (typ, env) | _ -> None)) | patternPath :: nested -> ( @@ -527,6 +544,12 @@ let rec resolveNestedPatternPath (typ : innerType) ~env ~full ~nested = | ( NVariantPayload {constructorName = "Some"; itemNum = 0}, Toption (env, typ) ) -> typ |> resolveNestedPatternPath ~env ~full ~nested + | ( NVariantPayload {constructorName = "Ok"; itemNum = 0}, + Tresult {env; okType} ) -> + TypeExpr okType |> resolveNestedPatternPath ~env ~full ~nested + | ( NVariantPayload {constructorName = "Error"; itemNum = 0}, + Tresult {env; errorType} ) -> + TypeExpr errorType |> resolveNestedPatternPath ~env ~full ~nested | NArray, Tarray (env, typ) -> typ |> resolveNestedPatternPath ~env ~full ~nested | _ -> None)) @@ -613,6 +636,10 @@ let rec extractedTypeToString ?(inner = false) = function "array<" ^ extractedTypeToString ~inner:true innerTyp ^ ">" | Toption (_, TypeExpr innerTyp) -> "option<" ^ Shared.typeToString innerTyp ^ ">" + | Tresult {okType; errorType} -> + "result<" ^ Shared.typeToString okType ^ ", " + ^ Shared.typeToString errorType + ^ ">" | Toption (_, ExtractedType innerTyp) -> "option<" ^ extractedTypeToString ~inner:true innerTyp ^ ">" | Tpromise (_, innerTyp) -> "promise<" ^ Shared.typeToString innerTyp ^ ">" @@ -687,6 +714,34 @@ module Codegen = struct @ (expandedBranches |> List.map (fun (pat : Parsetree.pattern) -> mkConstructPat ~payload:pat "Some"))) + | Tresult {okType; errorType} -> + let extractedOkType = okType |> extractType ~env ~package:full.package in + let extractedErrorType = + errorType |> extractType ~env ~package:full.package + in + let expandedOkBranches = + match extractedOkType with + | None -> [] + | Some extractedType -> ( + match extractedTypeToExhaustivePatterns ~env ~full extractedType with + | None -> [] + | Some patterns -> patterns) + in + let expandedErrorBranches = + match extractedErrorType with + | None -> [] + | Some extractedType -> ( + match extractedTypeToExhaustivePatterns ~env ~full extractedType with + | None -> [] + | Some patterns -> patterns) + in + Some + ((expandedOkBranches + |> List.map (fun (pat : Parsetree.pattern) -> + mkConstructPat ~payload:pat "Ok")) + @ (expandedErrorBranches + |> List.map (fun (pat : Parsetree.pattern) -> + mkConstructPat ~payload:pat "Error"))) | Tbool _ -> Some [mkConstructPat "true"; mkConstructPat "false"] | _ -> None diff --git a/analysis/tests/src/CompletionPattern.res b/analysis/tests/src/CompletionPattern.res index 3fed00c55..82e5f3566 100644 --- a/analysis/tests/src/CompletionPattern.res +++ b/analysis/tests/src/CompletionPattern.res @@ -216,3 +216,6 @@ let res: result = Ok(One) // switch res { | Ok() } // ^com + +// switch res { | Error() } +// ^com diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index 028058000..56f0ad0f9 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -1141,3 +1141,39 @@ Path res "insertTextFormat": 2 }] +Complete src/CompletionPattern.res 219:24 +posCursor:[219:24] posNoWhite:[219:23] Found pattern:[219:18->219:25] +Ppat_construct Error:[219:18->219:23] +posCursor:[219:24] posNoWhite:[219:23] Found pattern:[219:23->219:25] +Ppat_construct ():[219:23->219:25] +Completable: Cpattern Value[res]->variantPayload::Error($0) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[res] +Path res +[{ + "label": "#one", + "kind": 4, + "tags": [], + "detail": "#one\n\n[#one | #three(someRecord, bool) | #two(bool)]", + "documentation": null, + "insertText": "#one", + "insertTextFormat": 2 + }, { + "label": "#three(_, _)", + "kind": 4, + "tags": [], + "detail": "#three(someRecord, bool)\n\n[#one | #three(someRecord, bool) | #two(bool)]", + "documentation": null, + "insertText": "#three(${1:_}, ${2:_})", + "insertTextFormat": 2 + }, { + "label": "#two(_)", + "kind": 4, + "tags": [], + "detail": "#two(bool)\n\n[#one | #three(someRecord, bool) | #two(bool)]", + "documentation": null, + "insertText": "#two(${1:_})", + "insertTextFormat": 2 + }] + From 6a96f59bc515c2ab293a5118464f54da91075f5e Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 7 Dec 2023 21:29:56 +0100 Subject: [PATCH 2/2] changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bfe96c89..0795f2536 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,11 +12,14 @@ ## master +#### :bug: Bug Fix + +- Treat `result` type as a proper built in type. https://github.com/rescript-lang/rescript-vscode/pull/860 + #### :nail_care: Polish - Change end position of cursor when completing `Some()` in patterns. https://github.com/rescript-lang/rescript-vscode/pull/857 - #### :bug: Bug Fix - Add support for detecting dead fields inside inline records. https://github.com/rescript-lang/rescript-vscode/pull/858