diff --git a/README.md b/README.md index e67a69018..e67716011 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,6 @@

The Official VSCode plugin for ReScript

-

- - - - -

-

diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 756c6fb43..db671ca08 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -15,10 +15,10 @@ let getCompletions ~debug ~path ~pos ~currentFile ~forHover = (* Only perform expensive ast operations if there are completables *) match Cmt.loadFullCmtFromPath ~path with | None -> [] - | Some {file; package} -> - let env = SharedTypes.QueryEnv.fromFile file in + | Some full -> + let env = SharedTypes.QueryEnv.fromFile full.file in completable - |> CompletionBackEnd.processCompletable ~debug ~package ~pos ~scope ~env + |> CompletionBackEnd.processCompletable ~debug ~full ~pos ~scope ~env ~forHover)) let completion ~debug ~path ~pos ~currentFile = diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 10463e787..2b588784a 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1157,8 +1157,17 @@ let completionsGetTypeEnv = function | {Completion.kind = Field ({typ}, _); env} :: _ -> Some (typ, env) | _ -> None -let rec getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos - ~env ~exact ~scope (contextPath : Completable.contextPath) = +let findReturnTypeOfFunctionAtLoc loc ~(env : QueryEnv.t) ~full ~debug = + match References.getLocItem ~full ~pos:(loc |> Loc.end_) ~debug with + | Some {locType = Typed (_, typExpr, _)} -> ( + match extractFunctionType ~env ~package:full.package typExpr with + | args, tRet when args <> [] -> Some tRet + | _ -> None) + | _ -> None + +let rec getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env + ~exact ~scope (contextPath : Completable.contextPath) = + let package = full.package in match contextPath with | CPString -> [ @@ -1181,8 +1190,8 @@ let rec getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos | CPApply (cp, labels) -> ( match cp - |> getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos - ~env ~exact:true ~scope + |> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env + ~exact:true ~scope |> completionsGetTypeEnv with | Some (typ, env) -> ( @@ -1227,8 +1236,8 @@ let rec getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos | CPField (cp, fieldName) -> ( match cp - |> getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos - ~env ~exact:true ~scope + |> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env + ~exact:true ~scope |> completionsGetTypeEnv with | Some (typ, env) -> ( @@ -1250,8 +1259,8 @@ let rec getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos | CPObj (cp, label) -> ( match cp - |> getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos - ~env ~exact:true ~scope + |> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env + ~exact:true ~scope |> completionsGetTypeEnv with | Some (typ, env) -> ( @@ -1275,14 +1284,28 @@ let rec getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos else None) | None -> []) | None -> []) - | CPPipe (cp, funNamePrefix) -> ( + | CPPipe {contextPath = cp; id = funNamePrefix; lhsLoc} -> ( match cp - |> getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos - ~env ~exact:true ~scope + |> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env + ~exact:true ~scope |> completionsGetTypeEnv with | Some (typ, envFromCompletionItem) -> ( + (* If the type we're completing on is a type parameter, we won't be able to do + completion unless we know what that type parameter is compiled as. This + attempts to look up the compiled type for that type parameter by looking + for compiled information at the loc of that expression. *) + let typ = + match typ with + | {Types.desc = Tvar _} -> ( + match + findReturnTypeOfFunctionAtLoc lhsLoc ~env ~full ~debug:false + with + | None -> typ + | Some typFromLoc -> typFromLoc) + | _ -> typ + in let { arrayModulePath; optionModulePath; @@ -1418,8 +1441,9 @@ let getOpens ~debug ~rawOpens ~package ~env = (* Last open takes priority *) List.rev resolvedOpens -let processCompletable ~debug ~package ~scope ~env ~pos ~forHover +let processCompletable ~debug ~full ~scope ~env ~pos ~forHover (completable : Completable.t) = + let package = full.package in let rawOpens = Scope.getRawOpens scope in let opens = getOpens ~debug ~rawOpens ~package ~env in let allFiles = FileSet.union package.projectFiles package.dependenciesFiles in @@ -1433,8 +1457,8 @@ let processCompletable ~debug ~package ~scope ~env ~pos ~forHover | Cnone -> [] | Cpath contextPath -> contextPath - |> getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos - ~env ~exact:forHover ~scope + |> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env + ~exact:forHover ~scope | Cjsx ([id], prefix, identsSeen) when String.uncapitalize_ascii id = id -> let mkLabel (name, typString) = Completion.create ~name ~kind:(Label typString) ~env @@ -1783,7 +1807,7 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i let labels = match cp - |> getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos + |> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env ~exact:true ~scope |> completionsGetTypeEnv with diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index d7f12cac5..d9509abde 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -180,6 +180,7 @@ let completePipeChain ~(lhs : Parsetree.expression) = pexp_loc; pexp_attributes; } + |> Option.map (fun ctxPath -> (ctxPath, d.pexp_loc)) (* When the left side of the pipe we're completing is an identifier application. Example: someArray->filterAllTheGoodStuff-> *) | Pexp_apply @@ -195,6 +196,7 @@ let completePipeChain ~(lhs : Parsetree.expression) = pexp_loc; pexp_attributes; } + |> Option.map (fun ctxPath -> (ctxPath, pexp_loc)) | _ -> None let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = @@ -436,11 +438,12 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = | None -> ( match exprToContextPath lhs with | Some pipe -> - setResult (Cpath (CPPipe (pipe, id))); + setResult + (Cpath (CPPipe {contextPath = pipe; id; lhsLoc = lhs.pexp_loc})); true | None -> false) - | Some pipe -> - setResult (Cpath (CPPipe (pipe, id))); + | Some (pipe, lhsLoc) -> + setResult (Cpath (CPPipe {contextPath = pipe; id; lhsLoc})); true in match expr.pexp_desc with diff --git a/analysis/src/Hover.ml b/analysis/src/Hover.ml index 599852273..2cfeb8348 100644 --- a/analysis/src/Hover.ml +++ b/analysis/src/Hover.ml @@ -136,12 +136,13 @@ let getHoverViaCompletions ~debug ~path ~pos ~currentFile ~forHover (* Only perform expensive ast operations if there are completables *) match Cmt.loadFullCmtFromPath ~path with | None -> None - | Some {file; package} -> ( + | Some full -> ( + let {file; package} = full in let env = SharedTypes.QueryEnv.fromFile file in let completions = completable - |> CompletionBackEnd.processCompletable ~debug ~package ~pos ~scope - ~env ~forHover + |> CompletionBackEnd.processCompletable ~debug ~full ~pos ~scope ~env + ~forHover in match completions with | {kind = Label typString; docstring} :: _ -> diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index e5ee97e9f..ac56eed2f 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -479,7 +479,12 @@ module Completable = struct | CPId of string list * completionContext | CPField of contextPath * string | CPObj of contextPath * string - | CPPipe of contextPath * string + | CPPipe of { + contextPath: contextPath; + id: string; + lhsLoc: Location.t; + (** The loc item for the left hand side of the pipe. *) + } type t = | Cdecorator of string (** e.g. @module *) @@ -513,7 +518,7 @@ module Completable = struct completionContextToString completionContext ^ list sl | CPField (cp, s) -> contextPathToString cp ^ "." ^ str s | CPObj (cp, s) -> contextPathToString cp ^ "[\"" ^ s ^ "\"]" - | CPPipe (cp, s) -> contextPathToString cp ^ "->" ^ s + | CPPipe {contextPath; id} -> contextPathToString contextPath ^ "->" ^ id in function | Cpath cp -> "Cpath " ^ contextPathToString cp diff --git a/analysis/src/SignatureHelp.ml b/analysis/src/SignatureHelp.ml index 364d463b2..69a0ff006 100644 --- a/analysis/src/SignatureHelp.ml +++ b/analysis/src/SignatureHelp.ml @@ -18,12 +18,13 @@ let findFunctionType ~currentFile ~debug ~path ~pos = | Some (completable, scope) -> ( match Cmt.loadFullCmtFromPath ~path with | None -> None - | Some {file; package} -> + | Some full -> + let {file; package} = full in let env = QueryEnv.fromFile file in Some ( completable - |> CompletionBackEnd.processCompletable ~debug ~package ~pos - ~scope ~env ~forHover:true, + |> CompletionBackEnd.processCompletable ~debug ~full ~pos ~scope + ~env ~forHover:true, env, package, file ))) diff --git a/analysis/tests/src/CompletionPipeChain.res b/analysis/tests/src/CompletionPipeChain.res index 59c1af258..f9bcd2345 100644 --- a/analysis/tests/src/CompletionPipeChain.res +++ b/analysis/tests/src/CompletionPipeChain.res @@ -58,3 +58,7 @@ let f = int->Integer.increment(2) let _ = [123]->Js.Array2.forEach(v => Js.log(v)) // -> // ^com + +let _ = [123]->Belt.Array.reduce(0, (acc, curr) => acc + curr) +// ->t +// ^com diff --git a/analysis/tests/src/expected/CompletionPipeChain.res.txt b/analysis/tests/src/expected/CompletionPipeChain.res.txt index fe947a437..c7cff492f 100644 --- a/analysis/tests/src/expected/CompletionPipeChain.res.txt +++ b/analysis/tests/src/expected/CompletionPipeChain.res.txt @@ -239,3 +239,20 @@ posCursor:[58:5] posNoWhite:[58:4] Found expr:[57:8->0:-1] Completable: Cpath Value[Js, Array2, forEach](Nolabel, Nolabel)-> [] +Complete src/CompletionPipeChain.res 62:6 +posCursor:[62:6] posNoWhite:[62:5] Found expr:[61:8->62:6] +Completable: Cpath Value[Belt, Array, reduce](Nolabel, Nolabel, Nolabel)->t +[{ + "label": "Belt.Int.toString", + "kind": 12, + "tags": [], + "detail": "int => string", + "documentation": {"kind": "markdown", "value": "\n Converts a given `int` to a `string`. Uses the JavaScript `String` constructor under the hood.\n\n ```res example\n Js.log(Belt.Int.toString(1) === \"1\") /* true */\n ```\n"} + }, { + "label": "Belt.Int.toFloat", + "kind": 12, + "tags": [], + "detail": "int => float", + "documentation": {"kind": "markdown", "value": "\n Converts a given `int` to a `float`.\n\n ```res example\n Js.log(Belt.Int.toFloat(1) === 1.0) /* true */\n ```\n"} + }] +