Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle result type as a builtin #860

Merged
merged 2 commits into from
Dec 8, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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(<fieldName>)` 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
67 changes: 67 additions & 0 deletions analysis/src/CompletionBackEnd.ml
Original file line number Diff line number Diff line change
@@ -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])
5 changes: 5 additions & 0 deletions analysis/src/SharedTypes.ml
Original file line number Diff line number Diff line change
@@ -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
55 changes: 55 additions & 0 deletions analysis/src/TypeUtils.ml
Original file line number Diff line number Diff line change
@@ -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

3 changes: 3 additions & 0 deletions analysis/tests/src/CompletionPattern.res
Original file line number Diff line number Diff line change
@@ -216,3 +216,6 @@ let res: result<someVariant, somePolyVariant> = Ok(One)

// switch res { | Ok() }
// ^com

// switch res { | Error() }
// ^com
36 changes: 36 additions & 0 deletions analysis/tests/src/expected/CompletionPattern.res.txt
Original file line number Diff line number Diff line change
@@ -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
}]