Skip to content

Universal dot completion #1054

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

Merged
merged 34 commits into from
Jan 2, 2025
Merged
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
565edda
wip complete pipeable functions from dot completion on record when re…
zth Nov 5, 2024
4347fa3
correctly insert completion item text removing the dot when selecting…
zth Nov 6, 2024
eefe5f6
add experimental mainTypeForModule annotation
zth Nov 6, 2024
ca1c8d1
complete modules in payload of mainTypeForModule
zth Nov 7, 2024
7110db8
rename attribute
zth Nov 12, 2024
af54200
hover for new decorator
zth Nov 12, 2024
e3d1470
allow extra module completions also for pipe
zth Nov 13, 2024
72a522c
make sure completions work across files
zth Nov 14, 2024
ce80c6c
filter pipe completions to only applicable functions
zth Nov 14, 2024
5ad0319
pipe complete only for functions that take the expected type as the f…
zth Nov 16, 2024
943b01f
start refactoring dot completion everywhere
zth Nov 20, 2024
ebc5d9e
refactor dot completion
zth Nov 28, 2024
8e2395e
add a few more synthetic
zth Nov 28, 2024
8e166ac
disable verbose log
zth Nov 28, 2024
2b3190c
cleanup
zth Nov 28, 2024
58cf089
pipe dot completion for builtins
zth Nov 28, 2024
70b7db6
Add example case (#1058)
nojaf Nov 29, 2024
931fd28
Additional completion tests (#1062)
nojaf Dec 19, 2024
e347146
make dot completion everywhere actually work
zth Dec 25, 2024
415eea9
do not care about ExtractedType now that we have incremental type che…
zth Dec 25, 2024
c715071
refactor to share pipe completion code logic
zth Dec 25, 2024
dfc3eb0
cleanup
zth Dec 25, 2024
4e4d03c
cleanup
zth Dec 25, 2024
0fa56aa
fix debug command
zth Dec 28, 2024
b6f7ccc
up rescript in test project
zth Dec 28, 2024
eb4cea5
change strategy for removing dot on completion
zth Dec 29, 2024
b1c6850
contonous dot completion
zth Dec 31, 2024
9c29710
handle dot completions on piped idents
zth Dec 31, 2024
9ac0817
handle scope
zth Dec 31, 2024
3d0a1c8
inline pipe completion logic again
zth Jan 2, 2025
bf7eeba
refactor
zth Jan 2, 2025
380e8a9
more compl spec
zth Jan 2, 2025
603b8d9
refactor
zth Jan 2, 2025
b5053bc
changelog
zth Jan 2, 2025
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -12,6 +12,10 @@
## master

#### :rocket: New Feature

- Add support for "dot completion everywhere". In addition to record fields, dots will now complete for object fields, and pipe completions applicable to the type the dot is on. You can also configure where the editor draws extra pipe completions from via the `@editor.completeFrom` attribute. https://github.com/rescript-lang/rescript-vscode/pull/1054

## 1.60.0

#### :rocket: New Feature
2 changes: 1 addition & 1 deletion analysis/src/Codemod.ml
Original file line number Diff line number Diff line change
@@ -41,7 +41,7 @@ let transform ~path ~pos ~debug ~typ ~hint =
if debug then print_endline "Found no result";
exit 1
| Some switchExpr ->
printExpr ~range:(Xform.rangeOfLoc switchExpr.pexp_loc) switchExpr)
printExpr ~range:(Loc.rangeOfLoc switchExpr.pexp_loc) switchExpr)
| _ ->
if debug then print_endline "Mismatch in expected structure";
exit 1)
444 changes: 232 additions & 212 deletions analysis/src/CompletionBackEnd.ml

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions analysis/src/CompletionDecorators.ml
Original file line number Diff line number Diff line change
@@ -259,6 +259,11 @@ You will need this decorator whenever you want to use a JSX component in ReScrip

[Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#variadic-decorator).|};
] );
( "editor.completeFrom",
None,
[
{|The `@editor.completeFrom` decorator instructs the editor where it can draw additional completions from for this type.|};
] );
]

let toplevel =
115 changes: 93 additions & 22 deletions analysis/src/CompletionFrontEnd.ml
Original file line number Diff line number Diff line change
@@ -224,19 +224,32 @@ let rec exprToContextPathInner (e : Parsetree.expression) =
(CPId {path = Utils.flattenLongIdent txt; completionContext = Value; loc})
| Pexp_field (e1, {txt = Lident name}) -> (
match exprToContextPath e1 with
| Some contextPath -> Some (CPField (contextPath, name))
| Some contextPath ->
Some
(CPField
{
contextPath;
fieldName = name;
posOfDot = None;
exprLoc = e1.pexp_loc;
})
| _ -> None)
| Pexp_field (_, {loc; txt = Ldot (lid, name)}) ->
| Pexp_field (e1, {loc; txt = Ldot (lid, name)}) ->
(* Case x.M.field ignore the x part *)
Some
(CPField
( CPId
{
path = Utils.flattenLongIdent lid;
completionContext = Module;
loc;
},
name ))
{
contextPath =
CPId
{
path = Utils.flattenLongIdent lid;
completionContext = Module;
loc;
};
fieldName = name;
posOfDot = None;
exprLoc = e1.pexp_loc;
})
| Pexp_send (e1, {txt}) -> (
match exprToContextPath e1 with
| None -> None
@@ -327,6 +340,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
Some text.[offsetNoWhite]
else None
in
let posOfDot = Pos.posOfDot text ~pos:posCursor ~offset in
let charAtCursor =
if offset < String.length text then text.[offset] else '\n'
in
@@ -941,6 +955,32 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
setResult
(Completable.CdecoratorPayload
(JsxConfig {nested = List.rev nested; prefix})))
| _ -> ()
else if id.txt = "editor.completeFrom" then
match payload with
| PStr
[
{
pstr_desc =
Pstr_eval
( {
pexp_loc;
pexp_desc = Pexp_construct ({txt = path; loc}, None);
},
_ );
};
]
when locHasCursor pexp_loc ->
if Debug.verbose () then
print_endline "[decoratorCompletion] Found @editor.completeFrom";
setResult
(Completable.Cpath
(CPId
{
path = Utils.flattenLongIdent path;
completionContext = Module;
loc;
}))
| _ -> ());
Ast_iterator.default_iterator.attribute iterator (id, payload)
in
@@ -1010,6 +1050,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
(Cpath
(CPPipe
{
synthetic = false;
contextPath = pipe;
id;
lhsLoc = lhs.pexp_loc;
@@ -1020,7 +1061,14 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
| Some (pipe, lhsLoc) ->
setResult
(Cpath
(CPPipe {contextPath = pipe; id; lhsLoc; inJsx = !inJsxContext}));
(CPPipe
{
synthetic = false;
contextPath = pipe;
id;
lhsLoc;
inJsx = !inJsxContext;
}));
true
in
typedCompletionExpr expr;
@@ -1130,29 +1178,52 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
| Lident name -> (
match exprToContextPath e with
| Some contextPath ->
let contextPath = Completable.CPField (contextPath, name) in
let contextPath =
Completable.CPField
{
contextPath;
fieldName = name;
posOfDot;
exprLoc = e.pexp_loc;
}
in
setResult (Cpath contextPath)
| None -> ())
| Ldot (id, name) ->
(* Case x.M.field ignore the x part *)
let contextPath =
Completable.CPField
( CPId
{
loc = fieldName.loc;
path = Utils.flattenLongIdent id;
completionContext = Module;
},
if blankAfterCursor = Some '.' then
(* x.M. field ---> M. *) ""
else if name = "_" then ""
else name )
{
contextPath =
CPId
{
loc = fieldName.loc;
path = Utils.flattenLongIdent id;
completionContext = Module;
};
fieldName =
(if blankAfterCursor = Some '.' then
(* x.M. field ---> M. *) ""
else if name = "_" then ""
else name);
posOfDot;
exprLoc = e.pexp_loc;
}
in
setResult (Cpath contextPath)
| Lapply _ -> ()
else if Loc.end_ e.pexp_loc = posBeforeCursor then
match exprToContextPath e with
| Some contextPath -> setResult (Cpath (CPField (contextPath, "")))
| Some contextPath ->
setResult
(Cpath
(CPField
{
contextPath;
fieldName = "";
posOfDot;
exprLoc = e.pexp_loc;
}))
| None -> ())
| Pexp_apply ({pexp_desc = Pexp_ident compName}, args)
when Res_parsetree_viewer.is_jsx_expression expr ->
42 changes: 42 additions & 0 deletions analysis/src/DotCompletionUtils.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
let filterRecordFields ~env ~recordAsString ~prefix ~exact fields =
fields
|> Utils.filterMap (fun (field : SharedTypes.field) ->
if Utils.checkName field.fname.txt ~prefix ~exact then
Some
(SharedTypes.Completion.create field.fname.txt ~env
?deprecated:field.deprecated ~docstring:field.docstring
~kind:(SharedTypes.Completion.Field (field, recordAsString)))
else None)

let fieldCompletionsForDotCompletion ?posOfDot typ ~env ~package ~prefix ~exact
=
let asObject = typ |> TypeUtils.extractObjectType ~env ~package in
match asObject with
| Some (objEnv, obj) ->
(* Handle obj completion via dot *)
if Debug.verbose () then
Printf.printf "[dot_completion]--> Obj type found:\n";
obj |> TypeUtils.getObjFields
|> Utils.filterMap (fun (field, _typ) ->
if Utils.checkName field ~prefix ~exact then
let fullObjFieldName = Printf.sprintf "[\"%s\"]" field in
Some
(SharedTypes.Completion.create fullObjFieldName ~synthetic:true
~insertText:fullObjFieldName ~env:objEnv
~kind:(SharedTypes.Completion.ObjLabel typ)
?additionalTextEdits:
(match posOfDot with
| None -> None
| Some posOfDot ->
Some
(TypeUtils.makeAdditionalTextEditsForRemovingDot
posOfDot)))
else None)
| None -> (
match typ |> TypeUtils.extractRecordType ~env ~package with
| Some (env, fields, typDecl) ->
fields
|> filterRecordFields ~env ~prefix ~exact
~recordAsString:
(typDecl.item.decl |> Shared.declToString typDecl.name.txt)
| None -> [])
9 changes: 9 additions & 0 deletions analysis/src/Loc.ml
Original file line number Diff line number Diff line change
@@ -12,3 +12,12 @@ let hasPos ~pos loc = start loc <= pos && pos < end_ loc
(** Allows the character after the end to be included. Ie when the cursor is at the
end of the word, like `someIdentifier<cursor>`. Useful in some scenarios. *)
let hasPosInclusiveEnd ~pos loc = start loc <= pos && pos <= end_ loc

let mkPosition (pos : Pos.t) =
let line, character = pos in
{Protocol.line; character}

let rangeOfLoc (loc : t) =
let start = loc |> start |> mkPosition in
let end_ = loc |> end_ |> mkPosition in
{Protocol.start; end_}
26 changes: 26 additions & 0 deletions analysis/src/PipeCompletionUtils.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
let addJsxCompletionItems ~mainTypeId ~env ~prefix ~(full : SharedTypes.full)
~rawOpens typ =
match mainTypeId with
| ("array" | "float" | "string" | "int") as builtinNameToComplete ->
if Utils.checkName builtinNameToComplete ~prefix ~exact:false then
let name =
match full.package.genericJsxModule with
| None -> "React." ^ builtinNameToComplete
| Some g ->
g ^ "." ^ builtinNameToComplete
|> String.split_on_char '.'
|> TypeUtils.removeOpensFromCompletionPath ~rawOpens
~package:full.package
|> String.concat "."
in
[
SharedTypes.Completion.create name ~synthetic:true
~includesSnippets:true ~kind:(Value typ) ~env ~sortText:"A"
~docstring:
[
"Turns `" ^ builtinNameToComplete
^ "` into a JSX element so it can be used inside of JSX.";
];
]
else []
| _ -> []
16 changes: 16 additions & 0 deletions analysis/src/Pos.ml
Original file line number Diff line number Diff line change
@@ -26,3 +26,19 @@ let positionToOffset text (line, character) =
else None

let posBeforeCursor pos = (fst pos, max 0 (snd pos - 1))

let posOfDot text ~(pos : int * int) ~offset =
let rec loop i =
if i < 0 then None
else
match text.[i] with
| '.' -> Some (i + 1)
| '\n' -> None
| _ -> loop (i - 1)
in
match loop (offset - 1) with
| None -> None
| Some offsetBeforeDot ->
let line, col = pos in
let newCol = max 0 (col - (offset - offsetBeforeDot)) in
Some (line, newCol)
25 changes: 25 additions & 0 deletions analysis/src/ProcessAttributes.ml
Original file line number Diff line number Diff line change
@@ -48,3 +48,28 @@ let newDeclared ~item ~extent ~name ~stamp ~modulePath isExported attributes =
| Some d -> [d]);
item;
}

let rec findEditorCompleteFromAttribute ?(modulePaths = []) attributes =
let open Parsetree in
match attributes with
| [] -> modulePaths
| ( {Asttypes.txt = "editor.completeFrom"},
PStr [{pstr_desc = Pstr_eval (payloadExpr, _)}] )
:: rest ->
let items =
match payloadExpr with
| {pexp_desc = Pexp_array items} -> items
| p -> [p]
in
let modulePathsFromArray =
items
|> List.filter_map (fun item ->
match item.Parsetree.pexp_desc with
| Pexp_construct ({txt = path}, None) ->
Some (Utils.flattenLongIdent path)
| _ -> None)
in
findEditorCompleteFromAttribute
~modulePaths:(modulePathsFromArray @ modulePaths)
rest
| _ :: rest -> findEditorCompleteFromAttribute ~modulePaths rest
27 changes: 17 additions & 10 deletions analysis/src/Protocol.ml
Original file line number Diff line number Diff line change
@@ -40,6 +40,8 @@ let insertTextFormatToInt f =
match f with
| Snippet -> 2

type textEdit = {range: range; newText: string}

type completionItem = {
label: string;
kind: int;
@@ -51,6 +53,7 @@ type completionItem = {
insertText: string option;
documentation: markupContent option;
data: (string * string) list option;
additionalTextEdits: textEdit list option;
}

type location = {uri: string; range: range}
@@ -61,8 +64,6 @@ type documentSymbolItem = {
children: documentSymbolItem list;
}
type renameFile = {oldUri: string; newUri: string}
type textEdit = {range: range; newText: string}

type diagnostic = {range: range; message: string; severity: int}

type optionalVersionedTextDocumentIdentifier = {
@@ -104,6 +105,14 @@ let stringifyRange r =
(stringifyPosition r.start)
(stringifyPosition r.end_)

let stringifyTextEdit (te : textEdit) =
Printf.sprintf
{|{
"range": %s,
"newText": %s
}|}
(stringifyRange te.range) (wrapInQuotes te.newText)

let stringifyMarkupContent (m : markupContent) =
Printf.sprintf {|{"kind": %s, "value": %s}|} (wrapInQuotes m.kind)
(wrapInQuotes m.value)
@@ -156,6 +165,11 @@ let stringifyCompletionItem c =
(fields
|> List.map (fun (key, value) -> (key, Some (wrapInQuotes value)))
|> stringifyObject ~indentation:2) );
( "additionalTextEdits",
match c.additionalTextEdits with
| Some additionalTextEdits ->
Some (additionalTextEdits |> List.map stringifyTextEdit |> array)
| None -> None );
]

let stringifyHover value =
@@ -215,13 +229,6 @@ let stringifyRenameFile {oldUri; newUri} =
}|}
(wrapInQuotes oldUri) (wrapInQuotes newUri)

let stringifyTextEdit (te : textEdit) =
Printf.sprintf {|{
"range": %s,
"newText": %s
}|}
(stringifyRange te.range) (wrapInQuotes te.newText)

let stringifyoptionalVersionedTextDocumentIdentifier td =
Printf.sprintf {|{
"version": %s,
@@ -282,7 +289,7 @@ let stringifyCodeAction ca =
(wrapInQuotes (codeActionKindToString ca.codeActionKind))
(ca.edit |> stringifyCodeActionEdit)

let stringifyHint hint =
let stringifyHint (hint : inlayHint) =
Printf.sprintf
{|{
"position": %s,
21 changes: 17 additions & 4 deletions analysis/src/SharedTypes.ml
Original file line number Diff line number Diff line change
@@ -611,10 +611,16 @@ module Completable = struct
completionContext: completionContext;
loc: Location.t;
}
| CPField of contextPath * string
| CPField of {
contextPath: contextPath;
fieldName: string;
posOfDot: (int * int) option;
exprLoc: Location.t;
}
| CPObj of contextPath * string
| CPAwait of contextPath
| CPPipe of {
synthetic: bool; (** Whether this pipe completion is synthetic. *)
contextPath: contextPath;
id: string;
inJsx: bool; (** Whether this pipe was found in a JSX context. *)
@@ -695,7 +701,8 @@ module Completable = struct
| CPArray None -> "array"
| CPId {path; completionContext} ->
completionContextToString completionContext ^ list path
| CPField (cp, s) -> contextPathToString cp ^ "." ^ str s
| CPField {contextPath = cp; fieldName = s} ->
contextPathToString cp ^ "." ^ str s
| CPObj (cp, s) -> contextPathToString cp ^ "[\"" ^ s ^ "\"]"
| CPPipe {contextPath; id; inJsx} ->
contextPathToString contextPath
@@ -807,10 +814,14 @@ module Completion = struct
detail: string option;
typeArgContext: typeArgContext option;
data: (string * string) list option;
additionalTextEdits: Protocol.textEdit list option;
synthetic: bool;
(** Whether this item is an made up, synthetic item or not. *)
}

let create ?data ?typeArgContext ?(includesSnippets = false) ?insertText ~kind
~env ?sortText ?deprecated ?filterText ?detail ?(docstring = []) name =
let create ?(synthetic = false) ?additionalTextEdits ?data ?typeArgContext
?(includesSnippets = false) ?insertText ~kind ~env ?sortText ?deprecated
?filterText ?detail ?(docstring = []) name =
{
name;
env;
@@ -825,6 +836,8 @@ module Completion = struct
detail;
typeArgContext;
data;
additionalTextEdits;
synthetic;
}

(* https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion *)
311 changes: 235 additions & 76 deletions analysis/src/TypeUtils.ml
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
open SharedTypes

let modulePathFromEnv env = env.QueryEnv.file.moduleName :: List.rev env.pathRev

let fullTypeIdFromDecl ~env ~name ~modulePath =
env.QueryEnv.file.moduleName :: ModulePath.toPath modulePath name
|> String.concat "."

let debugLogTypeArgContext {env; typeArgs; typeParams} =
Printf.sprintf "Type arg context. env: %s, typeArgs: %s, typeParams: %s\n"
(Debug.debugPrintEnv env)
@@ -259,6 +265,28 @@ let rec extractFunctionType ~env ~package typ =
in
loop ~env [] typ

let rec extractFunctionTypeWithEnv ~env ~package typ =
let rec loop ~env acc (t : Types.type_expr) =
match t.desc with
| Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> loop ~env acc t1
| Tarrow (label, tArg, tRet, _) -> loop ~env ((label, tArg) :: acc) tRet
| Tconstr (Pident {name = "function$"}, [t; _], _) ->
extractFunctionTypeWithEnv ~env ~package t
| Tconstr (path, typeArgs, _) -> (
match References.digConstructor ~env ~package path with
| Some
( env,
{
item = {decl = {type_manifest = Some t1; type_params = typeParams}};
} ) ->
let t1 = t1 |> instantiateType ~typeParams ~typeArgs in
loop ~env acc t1
| Some _ -> (List.rev acc, t, env)
| _ -> (List.rev acc, t, env))
| _ -> (List.rev acc, t, env)
in
loop ~env [] typ

let maybeSetTypeArgCtx ?typeArgContextFromTypeManifest ~typeParams ~typeArgs env
=
match typeArgContextFromTypeManifest with
@@ -463,6 +491,11 @@ let rec extractType ?(printOpeningDebug = true)
if Debug.verbose () then print_endline "[extract_type]--> miss";
None

let isFunctionType ~env ~package t =
match extractType ~env ~package t with
| Some (Tfunction _, _) -> true
| _ -> false

let findReturnTypeOfFunctionAtLoc loc ~(env : QueryEnv.t) ~full ~debug =
match References.getLocItem ~full ~pos:(loc |> Loc.end_) ~debug with
| Some {locType = Typed (_, typExpr, _)} -> (
@@ -471,45 +504,6 @@ let findReturnTypeOfFunctionAtLoc loc ~(env : QueryEnv.t) ~full ~debug =
| _ -> None)
| _ -> None

type builtinType =
| Array
| Option
| String
| Int
| Float
| Promise
| List
| Result
| Lazy
| Char
| RegExp

type pipeCompletionType =
| Builtin of builtinType * Types.type_expr
| TypExpr of Types.type_expr

let getBuiltinFromTypePath path =
match path with
| Path.Pident _ -> (
match Path.name path with
| "array" -> Some Array
| "option" -> Some Option
| "string" -> Some String
| "int" -> Some Int
| "float" -> Some Float
| "promise" -> Some Promise
| "list" -> Some List
| "result" -> Some Result
| "lazy_t" -> Some Lazy
| "char" -> Some Char
| _ -> None)
| Pdot (Pdot (Pident m, "Re", _), "t", _) when Ident.name m = "Js" ->
Some RegExp
| Pdot (Pident id, "result", _)
when Ident.name id = "Pervasives" || Ident.name id = "PervasivesU" ->
Some Result
| _ -> None

let rec digToRelevantTemplateNameType ~env ~package ?(suffix = "")
(t : Types.type_expr) =
match t.desc with
@@ -528,44 +522,34 @@ let rec digToRelevantTemplateNameType ~env ~package ?(suffix = "")

let rec resolveTypeForPipeCompletion ~env ~package ~lhsLoc ~full
(t : Types.type_expr) =
let builtin =
match t |> pathFromTypeExpr with
| Some path -> path |> getBuiltinFromTypePath
| None -> None
(* 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 typFromLoc =
match t with
| {Types.desc = Tvar _} ->
findReturnTypeOfFunctionAtLoc lhsLoc ~env ~full ~debug:false
| _ -> None
in
match builtin with
| Some builtin -> (env, Builtin (builtin, t))
| None -> (
(* 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 typFromLoc =
match t with
| {Types.desc = Tvar _} -> (
match findReturnTypeOfFunctionAtLoc lhsLoc ~env ~full ~debug:false with
| None -> None
| Some typFromLoc -> Some typFromLoc)
| _ -> None
match typFromLoc with
| Some typFromLoc ->
typFromLoc |> resolveTypeForPipeCompletion ~lhsLoc ~env ~package ~full
| None ->
let rec digToRelevantType ~env ~package (t : Types.type_expr) =
match t.desc with
| Tlink t1 | Tsubst t1 | Tpoly (t1, []) ->
digToRelevantType ~env ~package t1
(* Don't descend into types named "t". Type t is a convention in the ReScript ecosystem. *)
| Tconstr (path, _, _) when path |> Path.last = "t" -> (env, t)
| Tconstr (path, _, _) -> (
match References.digConstructor ~env ~package path with
| Some (env, {item = {decl = {type_manifest = Some typ}}}) ->
digToRelevantType ~env ~package typ
| _ -> (env, t))
| _ -> (env, t)
in
match typFromLoc with
| Some typFromLoc ->
typFromLoc |> resolveTypeForPipeCompletion ~lhsLoc ~env ~package ~full
| None ->
let rec digToRelevantType ~env ~package (t : Types.type_expr) =
match t.desc with
| Tlink t1 | Tsubst t1 | Tpoly (t1, []) ->
digToRelevantType ~env ~package t1
(* Don't descend into types named "t". Type t is a convention in the ReScript ecosystem. *)
| Tconstr (path, _, _) when path |> Path.last = "t" -> (env, TypExpr t)
| Tconstr (path, _, _) -> (
match References.digConstructor ~env ~package path with
| Some (env, {item = {decl = {type_manifest = Some typ}}}) ->
digToRelevantType ~env ~package typ
| _ -> (env, TypExpr t))
| _ -> (env, TypExpr t)
in
digToRelevantType ~env ~package t)
digToRelevantType ~env ~package t

let extractTypeFromResolvedType (typ : Type.t) ~env ~full =
match typ.kind with
@@ -1074,7 +1058,7 @@ module Codegen = struct
Ast_helper.Exp.case pat (mkFailWithExp ())))
end

let getPathRelativeToEnv ~debug ~(env : QueryEnv.t) ~envFromItem path =
let getModulePathRelativeToEnv ~debug ~(env : QueryEnv.t) ~envFromItem path =
match path with
| _ :: pathRev ->
(* type path is relative to the completion environment
@@ -1123,3 +1107,178 @@ let pathToElementProps package =
match package.genericJsxModule with
| None -> ["ReactDOM"; "domProps"]
| Some g -> (g |> String.split_on_char '.') @ ["Elements"; "props"]

module StringSet = Set.Make (String)

let getExtraModulesToCompleteFromForType ~env ~full (t : Types.type_expr) =
let foundModulePaths = ref StringSet.empty in
let addToModulePaths attributes =
ProcessAttributes.findEditorCompleteFromAttribute attributes
|> List.iter (fun e ->
foundModulePaths :=
StringSet.add (e |> String.concat ".") !foundModulePaths)
in
let rec inner ~env ~full (t : Types.type_expr) =
match t |> Shared.digConstructor with
| Some path -> (
match References.digConstructor ~env ~package:full.package path with
| None -> ()
| Some (env, {item = {decl = {type_manifest = Some t}; attributes}}) ->
addToModulePaths attributes;
inner ~env ~full t
| Some (_, {item = {attributes}}) -> addToModulePaths attributes)
| None -> ()
in
inner ~env ~full t;
!foundModulePaths |> StringSet.elements
|> List.map (fun l -> String.split_on_char '.' l)

let getFirstFnUnlabelledArgType ~env ~full t =
let labels, _, env =
extractFunctionTypeWithEnv ~env ~package:full.package t
in
let rec findFirstUnlabelledArgType labels =
match labels with
| (Asttypes.Nolabel, t) :: _ -> Some t
| _ :: rest -> findFirstUnlabelledArgType rest
| [] -> None
in
match findFirstUnlabelledArgType labels with
| Some t -> Some (t, env)
| _ -> None

let makeAdditionalTextEditsForRemovingDot posOfDot =
[
{
Protocol.range =
{
start = {line = fst posOfDot; character = snd posOfDot - 1};
end_ = {line = fst posOfDot; character = snd posOfDot};
};
newText = "";
};
]

(** Turns a completion into a pipe completion. *)
let transformCompletionToPipeCompletion ?(synthetic = false) ~env ?posOfDot
(completion : Completion.t) =
let name = completion.name in
let nameWithPipe = "->" ^ name in
Some
{
completion with
name = nameWithPipe;
sortText = Some (name |> String.split_on_char '.' |> List.rev |> List.hd);
insertText = Some nameWithPipe;
env;
synthetic;
additionalTextEdits =
(match posOfDot with
| None -> None
| Some posOfDot -> Some (makeAdditionalTextEditsForRemovingDot posOfDot));
}

(** This takes a type expr and the env that type expr was found in, and produces a globally unique
id for that specific type. The globally unique id is the full path to the type as seen from the root
of the project. Example: type x in module SomeModule in file SomeFile would get the globally
unique id `SomeFile.SomeModule.x`.*)
let rec findRootTypeId ~full ~env (t : Types.type_expr) =
let debug = false in
match t.desc with
| Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> findRootTypeId ~full ~env t1
| Tconstr (Pident {name = "function$"}, [t; _], _) ->
findRootTypeId ~full ~env t
| Tconstr (path, _, _) -> (
(* We have a path. Try to dig to its declaration *)
if debug then
Printf.printf "[findRootTypeId] path %s, dig\n" (Path.name path);
match References.digConstructor ~env ~package:full.package path with
| Some (env, {item = {decl = {type_manifest = Some t1}}}) ->
if debug then
Printf.printf "[findRootTypeId] dug up type alias at module path %s \n"
(modulePathFromEnv env |> String.concat ".");
findRootTypeId ~full ~env t1
| Some (env, {item = {name}; modulePath}) ->
(* if it's a named type, then we know its name will be its module path from the env + its name.*)
if debug then
Printf.printf
"[findRootTypeId] dug up named type at module path %s, from item: %s \n"
(modulePathFromEnv env |> String.concat ".")
(ModulePath.toPath modulePath name |> String.concat ".");
Some (fullTypeIdFromDecl ~env ~name ~modulePath)
| None ->
(* If we didn't find anything, then it might be a builtin type. Check it.*)
if debug then Printf.printf "[findRootTypeId] dug up non-type alias\n";
if
Predef.builtin_idents
|> List.find_opt (fun (_, i) -> Ident.same i (Path.head path))
|> Option.is_some
then
Some
(if debug then Printf.printf "[findRootTypeId] returning builtin\n";
Path.name path)
else None)
| _ -> None

(** Filters out completions that are not pipeable from a list of completions. *)
let filterPipeableFunctions ~env ~full ?synthetic ?targetTypeId ?posOfDot
completions =
match targetTypeId with
| None -> completions
| Some targetTypeId ->
completions
|> List.filter_map (fun (completion : Completion.t) ->
let thisCompletionItemTypeId =
match completion.kind with
| Value t -> (
match
getFirstFnUnlabelledArgType ~full ~env:completion.env t
with
| None -> None
| Some (t, envFromLabelledArg) ->
findRootTypeId ~full ~env:envFromLabelledArg t)
| _ -> None
in
match thisCompletionItemTypeId with
| Some mainTypeId when mainTypeId = targetTypeId -> (
match posOfDot with
| None -> Some completion
| Some posOfDot ->
transformCompletionToPipeCompletion ?synthetic ~env ~posOfDot
completion)
| _ -> None)

let removeCurrentModuleIfNeeded ~envCompletionIsMadeFrom completionPath =
if
List.length completionPath > 0
&& List.hd completionPath = envCompletionIsMadeFrom.QueryEnv.file.moduleName
then List.tl completionPath
else completionPath

let rec getObjFields (texp : Types.type_expr) =
match texp.desc with
| Tfield (name, _, t1, t2) ->
let fields = t2 |> getObjFields in
(name, t1) :: fields
| Tlink te | Tsubst te | Tpoly (te, []) -> te |> getObjFields
| Tvar None -> []
| _ -> []

let pathToBuiltin path =
Predef.builtin_idents
|> List.find_opt (fun (_, i) -> Ident.same i (Path.head path))

let completionPathFromMaybeBuiltin path ~package =
match pathToBuiltin path with
| Some ("array", _) -> Some package.builtInCompletionModules.arrayModulePath
| Some ("option", _) -> Some package.builtInCompletionModules.optionModulePath
| Some ("string", _) -> Some package.builtInCompletionModules.stringModulePath
| Some ("int", _) -> Some package.builtInCompletionModules.intModulePath
| Some ("float", _) -> Some package.builtInCompletionModules.floatModulePath
| Some ("promise", _) ->
Some package.builtInCompletionModules.promiseModulePath
| Some ("list", _) -> Some package.builtInCompletionModules.listModulePath
| Some ("result", _) -> Some package.builtInCompletionModules.resultModulePath
| Some ("dict", _) -> Some ["Dict"]
| Some ("char", _) -> Some ["Char"]
| _ -> None
33 changes: 12 additions & 21 deletions analysis/src/Xform.ml
Original file line number Diff line number Diff line change
@@ -2,15 +2,6 @@

let isBracedExpr = Res_parsetree_viewer.is_braced_expr

let mkPosition (pos : Pos.t) =
let line, character = pos in
{Protocol.line; character}

let rangeOfLoc (loc : Location.t) =
let start = loc |> Loc.start |> mkPosition in
let end_ = loc |> Loc.end_ |> mkPosition in
{Protocol.start; end_}

let extractTypeFromExpr expr ~debug ~path ~currentFile ~full ~pos =
match
expr.Parsetree.pexp_loc
@@ -144,7 +135,7 @@ module IfThenElse = struct
match !changed with
| None -> ()
| Some newExpr ->
let range = rangeOfLoc newExpr.pexp_loc in
let range = Loc.rangeOfLoc newExpr.pexp_loc in
let newText = printExpr ~range newExpr in
let codeAction =
CodeActions.make ~title:"Replace with switch" ~kind:RefactorRewrite
@@ -161,7 +152,7 @@ module ModuleToFile = struct
| Pstr_module
{pmb_loc; pmb_name; pmb_expr = {pmod_desc = Pmod_structure structure}}
when structure_item.pstr_loc |> Loc.hasPos ~pos ->
let range = rangeOfLoc structure_item.pstr_loc in
let range = Loc.rangeOfLoc structure_item.pstr_loc in
let newTextInCurrentFile = "" in
let textForExtractedFile =
printStandaloneStructure ~loc:pmb_loc structure
@@ -280,7 +271,7 @@ module AddBracesToFn = struct
match !changed with
| None -> ()
| Some newStructureItem ->
let range = rangeOfLoc newStructureItem.pstr_loc in
let range = Loc.rangeOfLoc newStructureItem.pstr_loc in
let newText = printStructureItem ~range newStructureItem in
let codeAction =
CodeActions.make ~title:"Add braces to function" ~kind:RefactorRewrite
@@ -349,10 +340,10 @@ module AddTypeAnnotation = struct
let range, newText =
match annotation with
| Plain ->
( rangeOfLoc {locItem.loc with loc_start = locItem.loc.loc_end},
( Loc.rangeOfLoc {locItem.loc with loc_start = locItem.loc.loc_end},
": " ^ (typ |> Shared.typeToString) )
| WithParens ->
( rangeOfLoc locItem.loc,
( Loc.rangeOfLoc locItem.loc,
"(" ^ name ^ ": " ^ (typ |> Shared.typeToString) ^ ")" )
in
let codeAction =
@@ -439,7 +430,7 @@ module ExpandCatchAllForVariants = struct
| Args _ | InlineRecord _ -> "(_)")
|> String.concat " | "
in
let range = rangeOfLoc catchAllCase.pc_lhs.ppat_loc in
let range = Loc.rangeOfLoc catchAllCase.pc_lhs.ppat_loc in
let codeAction =
CodeActions.make ~title:"Expand catch-all" ~kind:RefactorRewrite
~uri:path ~newText ~range
@@ -463,7 +454,7 @@ module ExpandCatchAllForVariants = struct
| _ -> "(_)")
|> String.concat " | "
in
let range = rangeOfLoc catchAllCase.pc_lhs.ppat_loc in
let range = Loc.rangeOfLoc catchAllCase.pc_lhs.ppat_loc in
let codeAction =
CodeActions.make ~title:"Expand catch-all" ~kind:RefactorRewrite
~uri:path ~newText ~range
@@ -532,7 +523,7 @@ module ExpandCatchAllForVariants = struct
let newText =
if hasNoneCase then newText else newText ^ " | None"
in
let range = rangeOfLoc catchAllCase.pc_lhs.ppat_loc in
let range = Loc.rangeOfLoc catchAllCase.pc_lhs.ppat_loc in
let codeAction =
CodeActions.make ~title:"Expand catch-all" ~kind:RefactorRewrite
~uri:path ~newText ~range
@@ -627,7 +618,7 @@ module ExhaustiveSwitch = struct
match exhaustiveSwitch with
| None -> ()
| Some cases ->
let range = rangeOfLoc expr.pexp_loc in
let range = Loc.rangeOfLoc expr.pexp_loc in
let newText =
printExpr ~range {expr with pexp_desc = Pexp_match (expr, cases)}
in
@@ -652,7 +643,7 @@ module ExhaustiveSwitch = struct
match exhaustiveSwitch with
| None -> ()
| Some cases ->
let range = rangeOfLoc switchExpr.pexp_loc in
let range = Loc.rangeOfLoc switchExpr.pexp_loc in
let newText =
printExpr ~range
{switchExpr with pexp_desc = Pexp_match (completionExpr, cases)}
@@ -752,7 +743,7 @@ module AddDocTemplate = struct

match newSignatureItem with
| Some signatureItem ->
let range = rangeOfLoc signatureItem.psig_loc in
let range = Loc.rangeOfLoc signatureItem.psig_loc in
let newText = printSignatureItem ~range signatureItem in
let codeAction =
CodeActions.make ~title:"Add Documentation template"
@@ -837,7 +828,7 @@ module AddDocTemplate = struct

match newStructureItem with
| Some structureItem ->
let range = rangeOfLoc structureItem.pstr_loc in
let range = Loc.rangeOfLoc structureItem.pstr_loc in
let newText = printStructureItem ~range structureItem in
let codeAction =
CodeActions.make ~title:"Add Documentation template"
Original file line number Diff line number Diff line change
@@ -63,8 +63,8 @@ Resolved opens 1 pervasives
ContextPath Value[someString]->st <<jsx>>
ContextPath Value[someString]
Path someString
CPPipe env:GenericJsxCompletion
Path Js.String2.st
CPPipe pathFromEnv: found:true
Path GenericJsxCompletion.st
[{
"label": "GenericJsx.string",
"kind": 12,
@@ -73,18 +73,6 @@ Path Js.String2.st
"documentation": {"kind": "markdown", "value": "Turns `string` into a JSX element so it can be used inside of JSX."},
"sortText": "A",
"insertTextFormat": 2
}, {
"label": "Js.String2.startsWith",
"kind": 12,
"tags": [],
"detail": "(t, t) => bool",
"documentation": {"kind": "markdown", "value": "\nES2015: `startsWith(str, substr)` returns `true` if the `str` starts with\n`substr`, `false` otherwise.\n\nSee [`String.startsWith`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith)\non MDN.\n\n## Examples\n\n```rescript\nJs.String2.startsWith(\"ReScript\", \"Re\") == true\nJs.String2.startsWith(\"ReScript\", \"\") == true\nJs.String2.startsWith(\"JavaScript\", \"Re\") == false\n```\n"}
}, {
"label": "Js.String2.startsWithFrom",
"kind": 12,
"tags": [],
"detail": "(t, t, int) => bool",
"documentation": {"kind": "markdown", "value": "\nES2015: `startsWithFrom(str, substr, n)` returns `true` if the `str` starts\nwith `substr` starting at position `n`, false otherwise. If `n` is negative,\nthe search starts at the beginning of `str`.\n\nSee [`String.startsWith`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith)\non MDN.\n\n## Examples\n\n```rescript\nJs.String2.startsWithFrom(\"ReScript\", \"Scri\", 2) == true\nJs.String2.startsWithFrom(\"ReScript\", \"\", 2) == true\nJs.String2.startsWithFrom(\"JavaScript\", \"Scri\", 2) == false\n```\n"}
}]

Complete src/GenericJsxCompletion.res 20:24
@@ -112,8 +100,8 @@ Resolved opens 2 pervasives GenericJsx.res
ContextPath Value[someString]->st <<jsx>>
ContextPath Value[someString]
Path someString
CPPipe env:GenericJsxCompletion
Path Js.String2.st
CPPipe pathFromEnv: found:true
Path GenericJsxCompletion.st
[{
"label": "string",
"kind": 12,
@@ -122,17 +110,5 @@ Path Js.String2.st
"documentation": {"kind": "markdown", "value": "Turns `string` into a JSX element so it can be used inside of JSX."},
"sortText": "A",
"insertTextFormat": 2
}, {
"label": "Js.String2.startsWith",
"kind": 12,
"tags": [],
"detail": "(t, t) => bool",
"documentation": {"kind": "markdown", "value": "\nES2015: `startsWith(str, substr)` returns `true` if the `str` starts with\n`substr`, `false` otherwise.\n\nSee [`String.startsWith`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith)\non MDN.\n\n## Examples\n\n```rescript\nJs.String2.startsWith(\"ReScript\", \"Re\") == true\nJs.String2.startsWith(\"ReScript\", \"\") == true\nJs.String2.startsWith(\"JavaScript\", \"Re\") == false\n```\n"}
}, {
"label": "Js.String2.startsWithFrom",
"kind": 12,
"tags": [],
"detail": "(t, t, int) => bool",
"documentation": {"kind": "markdown", "value": "\nES2015: `startsWithFrom(str, substr, n)` returns `true` if the `str` starts\nwith `substr` starting at position `n`, false otherwise. If `n` is negative,\nthe search starts at the beginning of `str`.\n\nSee [`String.startsWith`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith)\non MDN.\n\n## Examples\n\n```rescript\nJs.String2.startsWithFrom(\"ReScript\", \"Scri\", 2) == true\nJs.String2.startsWithFrom(\"ReScript\", \"\", 2) == true\nJs.String2.startsWithFrom(\"JavaScript\", \"Scri\", 2) == false\n```\n"}
}]

14 changes: 7 additions & 7 deletions analysis/tests/package-lock.json
2 changes: 1 addition & 1 deletion analysis/tests/package.json
Original file line number Diff line number Diff line change
@@ -8,6 +8,6 @@
"@rescript/react": "0.12.0"
},
"dependencies": {
"rescript": "^11.1.0"
"rescript": "11.1.4"
}
}
1 change: 1 addition & 0 deletions analysis/tests/src/CompletePrioritize1.res
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module Test = {
type t = {name: int}
let add = (a: float) => a +. 1.0
let name = t => t.name
}
let a: Test.t = {name: 4}
// a->
44 changes: 44 additions & 0 deletions analysis/tests/src/CompletionFromModule.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
module SomeModule = {
type t = {name: string}

@get external getName: t => string = "name"

let thisShouldNotBeCompletedFor = () => "hi"
}

let n = {SomeModule.name: "hello"}

// n.
// ^com

@editor.completeFrom(CompletionFromModule.SomeOtherModule)
type typeOutsideModule = {nname: string}

module SomeOtherModule = {
type t = typeOutsideModule
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this alias as t a hard requirement to make this work?
Being newish to ReScript type t is a little strange and it feels a bit like an artificial limitation right now.


type irrelevantType = string

@get external getNName: t => string = "nname"
@get external getNName2: typeOutsideModule => string = "nname"
@get external getNName3: irrelevantType => string = "nname"

let thisShouldNotBeCompletedFor = () => "hi"
}

let nn: SomeOtherModule.t = {nname: "hello"}

// nn.
// ^com

// @editor.completeFrom(SomeOthe) type typeOutsideModule = {nname: string}
// ^com

let nnn: typeOutsideModule = {nname: "hello"}

// nnn->
// ^com

open SomeOtherModule
// nnn->
// ^com
14 changes: 14 additions & 0 deletions analysis/tests/src/CompletionFromModule2.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Used to check completions across files

// CompletionFromModule.n.
// ^com

// CompletionFromModule.nn.
// ^com

// CompletionFromModule.nnn->
// ^com

open CompletionFromModule.SomeOtherModule
// CompletionFromModule.nnn->
// ^com
11 changes: 2 additions & 9 deletions analysis/tests/src/CompletionInferValues.res
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ let reactEventFn = (cb: ReactEvent.Mouse.t => unit) => {

@val external getSomeRecord: unit => someRecord = "getSomeRecord"

// let x = 123; let aliased = x; aliased->f
// let x = 123; let aliased = x; aliased->t
// ^com

// let x = getSomeRecord(); x.
@@ -54,9 +54,6 @@ module Div = {
// let _ = <div onMouseEnter={event => { let btn = event->JsxEvent.Mouse.button->Belt.Int.toString->Js.String2.split("/"); btn->ma }} />
// ^com

// let x: someRecord = {name: "Hello", age: 123}; x.
// ^com

type someVariant = One | Two | Three(int, string)
type somePolyVariant = [#one | #two | #three(int, string)]
type someNestedRecord = {someRecord: someRecord}
@@ -115,10 +112,6 @@ type otherNestedRecord = {
// let x: otherNestedRecord; switch x { | {optRecord:Some({name})} => name->slic }
// ^com

// Follow arrays
// let x: array<otherNestedRecord>; switch x { | [inner] => inner.s }
// ^com

// Infer top level return
// let x = 123; switch x { | 123 => () | v => v->toSt }
// ^com
@@ -166,4 +159,4 @@ let fn3 = (~cb: sameFileRecord => unit) => {
// Handles reusing the same name already in scope for bindings
let res = 1
// switch res { | res => res }
// ^hov
// ^hov
22 changes: 22 additions & 0 deletions analysis/tests/src/CompletionMultipleEditorCompleteFrom.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@@warning("-26")
@@warning("-27")
@@warning("-110")

module A = {
@editor.completeFrom(B) @editor.completeFrom(C)
type a
}

module B = {
let b = (a: A.a) => 1
}

module C = {
open A
let c = (a: a) => {'c'}
}

let a : A.a = %todo
// a.
// ^com
// B.b and C.c should be completed
9 changes: 9 additions & 0 deletions analysis/tests/src/CompletionPipeChain.res
Original file line number Diff line number Diff line change
@@ -103,3 +103,12 @@ let r = %re("/t/g")

// r->la
// ^com

module Xyz = {
type xx = One
let do = (_: xx) => ""
}

let xx = Xyz.One
// xx->
// ^com
23 changes: 23 additions & 0 deletions analysis/tests/src/CompletionPipeProperty.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module ObservablePoint = {
type op = {
mutable x: int,
mutable y: int,
}

@send
external setBoth: (op, float) => unit = "set"

@send
external set: (op, float, float) => unit = "set"
}

module Sprite = {
type s = {
anchor: ObservablePoint.op,
}
}

let sprite : Sprite.s = %todo

// sprite.anchor.
// ^com
5 changes: 4 additions & 1 deletion analysis/tests/src/CompletionPipeSubmodules.res
Original file line number Diff line number Diff line change
@@ -2,11 +2,12 @@ module A = {
module B1 = {
type b1 = B1
let xx = B1
let d = (_: b1) => ""
}
module B2 = {
let yy = 20
}
type t = {v: B1.b1}
type t2 = {v: B1.b1}
let x = {v: B1.B1}
}

@@ -20,11 +21,13 @@ module A = {

module C = {
type t = C
let do = (_: t) => ""
}

module D = {
module C2 = {
type t2 = C2
let do = (_: t2) => ""
}

type d = {v: C.t, v2: C2.t2}
146 changes: 146 additions & 0 deletions analysis/tests/src/DotPipeCompletionSpec.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
//
module SomeModule = {
type t = {name: string}

@get external getName: t => string = "name"
@send
external withUnlabelledArgumentNotFirst: (~name: string=?, t) => unit =
"withUnlabelledArgumentNotFirst"

let thisShouldNotBeCompletedFor = () => "hi"
}

let n = {SomeModule.name: "hello"}

// Type from inside of a module
// n.
// ^com

@editor.completeFrom(DotPipeCompletionSpec.SomeOtherModule)
type typeOutsideModule = {nname: string}

let doWithTypeOutsideModule = (_: typeOutsideModule) => ""

module CompleteFromThisToo = {
external a: typeOutsideModule => string = "a"
external b: unit => typeOutsideModule = "b"
}

module SomeOtherModule = {
@editor.completeFrom(DotPipeCompletionSpec.CompleteFromThisToo)
type t = typeOutsideModule

type irrelevantType = string

@get external getNName: t => string = "nname"
@get external getNName2: typeOutsideModule => string = "nname"
@get external getNName3: irrelevantType => string = "nname"

let thisShouldNotBeCompletedFor = () => "hi"
}

let nn: SomeOtherModule.t = {nname: "hello"}

// Type from module but that's an alias
// nn.
// ^com

module A = {
@editor.completeFrom(B)
type a

external withA: a => unit = "withA"
external make: unit => a = "makeA"
}

module B = {
let b = (_a: A.a) => 1
}

external a: A.a = "a"

// Main type in other module
// a.
// ^com

let xx: CompletionFromModule.SomeModule.t = {name: "hello"}
// Type from other file
// xx.
// ^com

type builtinType = array<string>

let ffff: builtinType = []

// A built in type
// ffff.u
// ^com

// Type outside of module with complete from pointing to other module
let nnn: typeOutsideModule = {nname: "hello"}
// nnn.
// ^com

// Continuous completion
let xxxx = [1, 2]

// xxxx->Js.Array2.filter(v => v > 10).filt
// ^com

// xxxx->Js.Array2.filter(v => v > 10)->Js.Array2.joinWith(",").includ
// ^com

let str = "hello"

// str->Js.String2.toLowerCase.toUpperCa
// ^com

// str->Js.String2.toLowerCase->Js.String2.toUpperCase.toLowerC
// ^com

let cc = (t: typeOutsideModule) => {
// t.
// ^com
t
}

let outOfScope = (t: typeOutsideModule) => t

// @editor.completeFrom(Dot) type t
// ^com

// @editor.completeFrom([CompletionPipe]) type t
// ^com

// @editor.completeFrom([CompletionPipe, Dot]) type t
// ^com

let someObj = {
"name": "hello",
"age": 123,
}

// someObj.
// ^com

// someObj.na
// ^com

module DOMAPI = {
type htmlElement = {prefix: string}

@editor.completeFrom(DotPipeCompletionSpec.HTMLButtonElement)
type rec htmlButtonElement = {mutable disabled: bool}
}

module HTMLButtonElement = {
open DOMAPI

@send
external checkValidity: htmlButtonElement => bool = "checkValidity"
}

external button: DOMAPI.htmlButtonElement = "button"

// button.
// ^com
51 changes: 51 additions & 0 deletions analysis/tests/src/Rxjs.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// These are bindings used in RxjsCompletion.res
// We are using a separate file to test complication for modules of external files.
type target

module Subscriber = {
type t<'t> = {next: 't => unit}
}

module Observable = {
// Complete items defined inside the parent module.
@editor.completeFrom(Rxjs)
type t<'t>

type dispose = unit => unit

@new @module("rxjs")
external make: (Subscriber.t<'t> => dispose) => t<'t> = "Observable"

type subscription

@send
external subscribe: (t<'t>, 't => unit) => subscription = "subscribe"
}

@module("rxjs")
external fromEvent: (target, string) => Observable.t<'t> = "fromEvent"

type operation<'t, 'u>

@send
external pipe: (Observable.t<'t>, operation<'t, 'u>) => Observable.t<'u> = "pipe"

@send
external pipe2: (Observable.t<'t>, operation<'t, 'u>, operation<'u, 'i>) => Observable.t<'i> =
"pipe"

@module("rxjs")
external map: ('t => 'u) => operation<'t, 'u> = "map"

@module("rxjs")
external distinctUntilChanged: unit => operation<'t, 't> = "distinctUntilChanged"

@module("rxjs")
external merge: (Observable.t<'t>, Observable.t<'t>) => Observable.t<'t> = "merge"

@module("rxjs")
external scan: (('acc, 't) => 'acc, 'acc) => operation<'t, 'acc> = "scan"

@module("rxjs")
external combineLatest: (Observable.t<'a>, Observable.t<'b>) => Observable.t<('a, 'b)> =
"combineLatest"
39 changes: 39 additions & 0 deletions analysis/tests/src/RxjsCompletion.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
@@warning("-26")
@@warning("-110")

type keyPress =
| Up(string)
| Down(string)

@val
external window: {..} = "window"

let main = async () => {
let keyMapObservable = {
open Rxjs

let keydown =
fromEvent(Obj.magic(window), "keydown")->pipe2(
map(event => Down(event["key"])),
distinctUntilChanged(),
)

let keyup =
fromEvent(Obj.magic(window), "keyup")->pipe2(
map(event => Up(event["key"])),
distinctUntilChanged(),
)

// merge(keydown, keyup).
// ^com

// Rxjs.Observable.subscribe, Rxjs.pipe and Rxjs.pipe2 should be completed
}

let (a,b) : ( Rxjs.Observable.t<string> , Rxjs.Observable.t<string>) = %todo

// Rxjs.combineLatest(a, b).
// ^com

// Rxjs.Observable.subscribe, Rxjs.pipe and Rxjs.pipe2 should be completed
}
10 changes: 4 additions & 6 deletions analysis/tests/src/expected/CompletePrioritize1.res.txt
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
Complete src/CompletePrioritize1.res 5:6
posCursor:[5:6] posNoWhite:[5:5] Found expr:[5:3->0:-1]
Complete src/CompletePrioritize1.res 6:6
posCursor:[6:6] posNoWhite:[6:5] Found expr:[6:3->0:-1]
Completable: Cpath Value[a]->
Package opens Pervasives.JsxModules.place holder
Resolved opens 1 pervasives
ContextPath Value[a]->
ContextPath Value[a]
Path a
CPPipe env:CompletePrioritize1
CPPipe type path:Test.t
CPPipe pathFromEnv:Test found:true
Path Test.
[{
"label": "Test.add",
"label": "Test.name",
"kind": 12,
"tags": [],
"detail": "float => float",
"detail": "t => int",
"documentation": null
}]

2 changes: 0 additions & 2 deletions analysis/tests/src/expected/CompletePrioritize2.res.txt
Original file line number Diff line number Diff line change
@@ -6,8 +6,6 @@ Resolved opens 1 pervasives
ContextPath Value[ax]->
ContextPath Value[ax]
Path ax
CPPipe env:CompletePrioritize2
CPPipe type path:Test.t
CPPipe pathFromEnv:Test found:true
Path Test.
[{
92 changes: 80 additions & 12 deletions analysis/tests/src/expected/Completion.res.txt
Original file line number Diff line number Diff line change
@@ -404,7 +404,6 @@ Package opens Pervasives.JsxModules.place holder
Resolved opens 1 pervasives
ContextPath array<int>->m
ContextPath array<int>
CPPipe env:Completion
Path Js.Array2.m
[{
"label": "Js.Array2.mapi",
@@ -427,7 +426,6 @@ Package opens Pervasives.JsxModules.place holder
Resolved opens 1 pervasives
ContextPath string->toU
ContextPath string
CPPipe env:Completion
Path Js.String2.toU
[{
"label": "Js.String2.toUpperCase",
@@ -445,7 +443,6 @@ Resolved opens 1 pervasives
ContextPath Value[op]->e
ContextPath Value[op]
Path op
CPPipe env:Completion
Path Belt.Option.e
[{
"label": "Belt.Option.eqU",
@@ -471,8 +468,6 @@ Resolved opens 1 pervasives
ContextPath Value[fa]->
ContextPath Value[fa]
Path fa
CPPipe env:Completion
CPPipe type path:ForAuto.t
CPPipe pathFromEnv:ForAuto found:true
Path ForAuto.
[{
@@ -728,6 +723,11 @@ Resolved opens 1 pervasives
ContextPath Value[r].""
ContextPath Value[r]
Path r
ContextPath Value[r]->
ContextPath Value[r]
Path r
CPPipe pathFromEnv: found:true
Path Completion.
[{
"label": "x",
"kind": 5,
@@ -751,6 +751,11 @@ Resolved opens 1 pervasives
ContextPath Value[Objects, Rec, recordVal].""
ContextPath Value[Objects, Rec, recordVal]
Path Objects.Rec.recordVal
ContextPath Value[Objects, Rec, recordVal]->
ContextPath Value[Objects, Rec, recordVal]
Path Objects.Rec.recordVal
CPPipe pathFromEnv:Rec found:true
Path Objects.Rec.
[{
"label": "xx",
"kind": 5,
@@ -833,6 +838,22 @@ ContextPath Value[q].aa.""
ContextPath Value[q].aa
ContextPath Value[q]
Path q
ContextPath Value[q]->aa
ContextPath Value[q]
Path q
CPPipe pathFromEnv: found:true
Path Completion.aa
ContextPath Value[q].aa->
ContextPath Value[q].aa
ContextPath Value[q]
Path q
ContextPath Value[q]->aa
ContextPath Value[q]
Path q
CPPipe pathFromEnv: found:true
Path Completion.aa
CPPipe pathFromEnv: found:true
Path Completion.
[{
"label": "x",
"kind": 5,
@@ -857,6 +878,22 @@ ContextPath Value[q].aa.n
ContextPath Value[q].aa
ContextPath Value[q]
Path q
ContextPath Value[q]->aa
ContextPath Value[q]
Path q
CPPipe pathFromEnv: found:true
Path Completion.aa
ContextPath Value[q].aa->n
ContextPath Value[q].aa
ContextPath Value[q]
Path q
ContextPath Value[q]->aa
ContextPath Value[q]
Path q
CPPipe pathFromEnv: found:true
Path Completion.aa
CPPipe pathFromEnv: found:true
Path Completion.n
[{
"label": "name",
"kind": 5,
@@ -1081,6 +1118,12 @@ ContextPath Value[FAO, forAutoObject]["forAutoLabel"].""
ContextPath Value[FAO, forAutoObject]["forAutoLabel"]
ContextPath Value[FAO, forAutoObject]
Path FAO.forAutoObject
ContextPath Value[FAO, forAutoObject]["forAutoLabel"]->
ContextPath Value[FAO, forAutoObject]["forAutoLabel"]
ContextPath Value[FAO, forAutoObject]
Path FAO.forAutoObject
CPPipe pathFromEnv:FAR found:true
Path FAR.
[{
"label": "forAuto",
"kind": 5,
@@ -1106,8 +1149,12 @@ ContextPath Value[FAO, forAutoObject]["forAutoLabel"].forAuto
ContextPath Value[FAO, forAutoObject]["forAutoLabel"]
ContextPath Value[FAO, forAutoObject]
Path FAO.forAutoObject
CPPipe env:Completion envFromCompletionItem:Completion.FAR
CPPipe type path:ForAuto.t
ContextPath Value[FAO, forAutoObject]["forAutoLabel"]->forAuto
ContextPath Value[FAO, forAutoObject]["forAutoLabel"]
ContextPath Value[FAO, forAutoObject]
Path FAO.forAutoObject
CPPipe pathFromEnv:FAR found:true
Path FAR.forAuto
CPPipe pathFromEnv:ForAuto found:false
Path ForAuto.
[{
@@ -1189,6 +1236,11 @@ Resolved opens 3 pervasives Completion.res Completion.res
ContextPath Value[_z].""
ContextPath Value[_z]
Path _z
ContextPath Value[_z]->
ContextPath Value[_z]
Path _z
CPPipe pathFromEnv: found:true
Path Completion.
[{
"label": "x",
"kind": 5,
@@ -1347,6 +1399,11 @@ Resolved opens 3 pervasives Completion.res Completion.res
ContextPath Value[funRecord].someFun
ContextPath Value[funRecord]
Path funRecord
ContextPath Value[funRecord]->someFun
ContextPath Value[funRecord]
Path funRecord
CPPipe pathFromEnv: found:true
Path Completion.someFun
Found type for function (~name: string) => unit
[{
"label": "name",
@@ -1367,6 +1424,12 @@ ContextPath Value[retAA](Nolabel).""
ContextPath Value[retAA](Nolabel)
ContextPath Value[retAA]
Path retAA
ContextPath Value[retAA](Nolabel, Nolabel)->
ContextPath Value[retAA](Nolabel, Nolabel)
ContextPath Value[retAA]
Path retAA
CPPipe pathFromEnv: found:true
Path Completion.
[{
"label": "x",
"kind": 5,
@@ -1897,6 +1960,11 @@ Resolved opens 3 pervasives Completion.res Completion.res
ContextPath Value[funRecord].""
ContextPath Value[funRecord]
Path funRecord
ContextPath Value[funRecord]->
ContextPath Value[funRecord]
Path funRecord
CPPipe pathFromEnv: found:true
Path Completion.
[{
"label": "someFun",
"kind": 5,
@@ -1923,7 +1991,6 @@ Package opens Pervasives.JsxModules.place holder
Resolved opens 4 pervasives Completion.res Completion.res js.ml
ContextPath array->ma
ContextPath array
CPPipe env:Completion
Path Js.Array2.ma
[{
"label": "Array2.mapi",
@@ -2078,7 +2145,6 @@ Package opens Pervasives.JsxModules.place holder
Resolved opens 3 pervasives Completion.res Completion.res
ContextPath int->t
ContextPath int
CPPipe env:Completion
Path Belt.Int.t
[{
"label": "Belt.Int.toString",
@@ -2102,7 +2168,6 @@ Package opens Pervasives.JsxModules.place holder
Resolved opens 3 pervasives Completion.res Completion.res
ContextPath float->t
ContextPath float
CPPipe env:Completion
Path Belt.Float.t
[{
"label": "Belt.Float.toInt",
@@ -2127,7 +2192,6 @@ Resolved opens 3 pervasives Completion.res Completion.res
ContextPath Value[ok]->g
ContextPath Value[ok]
Path ok
CPPipe env:Completion
Path Belt.Result.g
[{
"label": "Belt.Result.getExn",
@@ -2153,6 +2217,11 @@ Resolved opens 3 pervasives Completion.res Completion.res
ContextPath Value[rWithDepr].so
ContextPath Value[rWithDepr]
Path rWithDepr
ContextPath Value[rWithDepr]->so
ContextPath Value[rWithDepr]
Path rWithDepr
CPPipe pathFromEnv: found:true
Path Completion.so
[{
"label": "someInt",
"kind": 5,
@@ -2211,7 +2280,6 @@ ContextPath Value[uncurried](Nolabel)->toS
ContextPath Value[uncurried](Nolabel)
ContextPath Value[uncurried]
Path uncurried
CPPipe env:Completion
Path Belt.Int.toS
[{
"label": "Belt.Int.toString",
15 changes: 15 additions & 0 deletions analysis/tests/src/expected/CompletionExpressions.res.txt
Original file line number Diff line number Diff line change
@@ -946,6 +946,11 @@ Resolved opens 1 pervasives
ContextPath Value[fff].someOpt
ContextPath Value[fff]
Path fff
ContextPath Value[fff]->someOpt
ContextPath Value[fff]
Path fff
CPPipe pathFromEnv: found:true
Path CompletionExpressions.someOpt
[{
"label": "someOptField",
"kind": 5,
@@ -1415,6 +1420,11 @@ Resolved opens 2 pervasives CompletionSupport.res
ContextPath Value[someTyp].""
ContextPath Value[someTyp]
Path someTyp
ContextPath Value[someTyp]->
ContextPath Value[someTyp]
Path someTyp
CPPipe pathFromEnv: found:true
Path CompletionExpressions.
[{
"label": "test",
"kind": 5,
@@ -1470,6 +1480,11 @@ Resolved opens 2 pervasives CompletionSupport.res
ContextPath Value[someTyp].""
ContextPath Value[someTyp]
Path someTyp
ContextPath Value[someTyp]->
ContextPath Value[someTyp]
Path someTyp
CPPipe pathFromEnv: found:true
Path CompletionExpressions.
[{
"label": "test",
"kind": 5,
171 changes: 171 additions & 0 deletions analysis/tests/src/expected/CompletionFromModule.res.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
Complete src/CompletionFromModule.res 10:5
posCursor:[10:5] posNoWhite:[10:4] Found expr:[10:3->10:5]
Pexp_field [10:3->10:4] _:[13:0->10:5]
Completable: Cpath Value[n].""
Package opens Pervasives.JsxModules.place holder
Resolved opens 1 pervasives
ContextPath Value[n].""
ContextPath Value[n]
Path n
ContextPath Value[n]->
ContextPath Value[n]
Path n
CPPipe pathFromEnv:SomeModule found:true
Path SomeModule.
[{
"label": "name",
"kind": 5,
"tags": [],
"detail": "string",
"documentation": {"kind": "markdown", "value": "```rescript\nname: string\n```\n\n```rescript\ntype t = {name: string}\n```"}
}, {
"label": "->SomeModule.getName",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curious about the function coming before the field
also, any thoughts about making . a valid source language construct?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you expand a bit?

Copy link
Collaborator

@cristianoc cristianoc Nov 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 If I write x. I see x->getName() before x.field, which can be surprising to people working with records. Just wondering.

2 Could x.getName() be actually part of the language, or will it always need to be expressed as x->getName? Autocompletion is right now suggestive of something that does not exist in the language. Not necessarily a bad thing, just thinking aloud about possible pros and cons here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Ahh right! That's a good point. It's currently sorted on the field name and function name. We could sort it whatever way we think works best. I'd say either this (sort it together with the regular fields) or put it at the bottom. I'd favor the current way, I think.
  2. Oh, that's.... a very interesting idea! That would solve quite a few of our issues. Let's explore that!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that is about what called "uniform function call syntax"

And there was a similar discussion in ReasonML reasonml/reason#1638 early day

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the context. Will read through.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we could figure out a good way to do that, pointing out the module to use with the call (someRecord.SomeModule.someFn()), but with a small enough twist that it's still more convenient than pipe, but not ambigious wrt the current behavior of SomeModule there being a hint.

Anyway, just completing the actual pipe, but via dot, is a good start for DX imo.

"kind": 12,
"tags": [],
"detail": "t => string",
"documentation": null,
"sortText": "getName",
"insertText": "->SomeModule.getName",
"additionalTextEdits": [{
"range": {"start": {"line": 10, "character": 4}, "end": {"line": 10, "character": 5}},
"newText": ""
}]
}]

Complete src/CompletionFromModule.res 30:6
posCursor:[30:6] posNoWhite:[30:5] Found expr:[30:3->30:6]
Pexp_field [30:3->30:5] _:[36:0->30:6]
Completable: Cpath Value[nn].""
Package opens Pervasives.JsxModules.place holder
Resolved opens 1 pervasives
ContextPath Value[nn].""
ContextPath Value[nn]
Path nn
ContextPath Value[nn]->
ContextPath Value[nn]
Path nn
CPPipe pathFromEnv:SomeOtherModule found:true
Path SomeOtherModule.
Path CompletionFromModule.SomeOtherModule.
[{
"label": "nname",
"kind": 5,
"tags": [],
"detail": "string",
"documentation": {"kind": "markdown", "value": "```rescript\nnname: string\n```\n\n```rescript\ntype typeOutsideModule = {nname: string}\n```"}
}, {
"label": "->SomeOtherModule.getNName",
"kind": 12,
"tags": [],
"detail": "t => string",
"documentation": null,
"sortText": "getNName",
"insertText": "->SomeOtherModule.getNName",
"additionalTextEdits": [{
"range": {"start": {"line": 30, "character": 5}, "end": {"line": 30, "character": 6}},
"newText": ""
}]
}, {
"label": "->SomeOtherModule.getNName2",
"kind": 12,
"tags": [],
"detail": "typeOutsideModule => string",
"documentation": null,
"sortText": "getNName2",
"insertText": "->SomeOtherModule.getNName2",
"additionalTextEdits": [{
"range": {"start": {"line": 30, "character": 5}, "end": {"line": 30, "character": 6}},
"newText": ""
}]
}, {
"label": "->SomeOtherModule.getNName",
"kind": 12,
"tags": [],
"detail": "t => string",
"documentation": null,
"sortText": "getNName",
"insertText": "->SomeOtherModule.getNName",
"additionalTextEdits": [{
"range": {"start": {"line": 30, "character": 5}, "end": {"line": 30, "character": 6}},
"newText": ""
}]
}, {
"label": "->SomeOtherModule.getNName2",
"kind": 12,
"tags": [],
"detail": "typeOutsideModule => string",
"documentation": null,
"sortText": "getNName2",
"insertText": "->SomeOtherModule.getNName2",
"additionalTextEdits": [{
"range": {"start": {"line": 30, "character": 5}, "end": {"line": 30, "character": 6}},
"newText": ""
}]
}]

Complete src/CompletionFromModule.res 33:32
XXX Not found!
Completable: Cpath Module[SomeOthe]
Package opens Pervasives.JsxModules.place holder
Resolved opens 1 pervasives
ContextPath Module[SomeOthe]
Path SomeOthe
[{
"label": "SomeOtherModule",
"kind": 9,
"tags": [],
"detail": "module SomeOtherModule",
"documentation": null
}]

Complete src/CompletionFromModule.res 38:8
posCursor:[38:8] posNoWhite:[38:7] Found expr:[38:3->0:-1]
Completable: Cpath Value[nnn]->
Package opens Pervasives.JsxModules.place holder
Resolved opens 1 pervasives
ContextPath Value[nnn]->
ContextPath Value[nnn]
Path nnn
CPPipe pathFromEnv: found:true
Path CompletionFromModule.
Path CompletionFromModule.SomeOtherModule.
[{
"label": "SomeOtherModule.getNName",
"kind": 12,
"tags": [],
"detail": "t => string",
"documentation": null
}, {
"label": "SomeOtherModule.getNName2",
"kind": 12,
"tags": [],
"detail": "typeOutsideModule => string",
"documentation": null
}]

Complete src/CompletionFromModule.res 42:8
posCursor:[42:8] posNoWhite:[42:7] Found expr:[42:3->0:-1]
Completable: Cpath Value[nnn]->
Raw opens: 1 SomeOtherModule.place holder
Package opens Pervasives.JsxModules.place holder
Resolved opens 2 pervasives CompletionFromModule.res
ContextPath Value[nnn]->
ContextPath Value[nnn]
Path nnn
CPPipe pathFromEnv: found:true
Path CompletionFromModule.
Path CompletionFromModule.SomeOtherModule.
[{
"label": "getNName",
"kind": 12,
"tags": [],
"detail": "t => string",
"documentation": null
}, {
"label": "getNName2",
"kind": 12,
"tags": [],
"detail": "typeOutsideModule => string",
"documentation": null
}]

156 changes: 156 additions & 0 deletions analysis/tests/src/expected/CompletionFromModule2.res.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
Complete src/CompletionFromModule2.res 2:26
posCursor:[2:26] posNoWhite:[2:25] Found expr:[2:3->2:26]
Pexp_field [2:3->2:25] _:[11:0->2:26]
Completable: Cpath Value[CompletionFromModule, n].""
Package opens Pervasives.JsxModules.place holder
Resolved opens 1 pervasives
ContextPath Value[CompletionFromModule, n].""
ContextPath Value[CompletionFromModule, n]
Path CompletionFromModule.n
ContextPath Value[CompletionFromModule, n]->
ContextPath Value[CompletionFromModule, n]
Path CompletionFromModule.n
CPPipe pathFromEnv:SomeModule found:true
Path CompletionFromModule.SomeModule.
[{
"label": "name",
"kind": 5,
"tags": [],
"detail": "string",
"documentation": {"kind": "markdown", "value": "```rescript\nname: string\n```\n\n```rescript\ntype t = {name: string}\n```"}
}, {
"label": "->CompletionFromModule.SomeModule.getName",
"kind": 12,
"tags": [],
"detail": "t => string",
"documentation": null,
"sortText": "getName",
"insertText": "->CompletionFromModule.SomeModule.getName",
"additionalTextEdits": [{
"range": {"start": {"line": 2, "character": 25}, "end": {"line": 2, "character": 26}},
"newText": ""
}]
}]

Complete src/CompletionFromModule2.res 5:27
posCursor:[5:27] posNoWhite:[5:26] Found expr:[5:3->5:27]
Pexp_field [5:3->5:26] _:[11:0->5:27]
Completable: Cpath Value[CompletionFromModule, nn].""
Package opens Pervasives.JsxModules.place holder
Resolved opens 1 pervasives
ContextPath Value[CompletionFromModule, nn].""
ContextPath Value[CompletionFromModule, nn]
Path CompletionFromModule.nn
ContextPath Value[CompletionFromModule, nn]->
ContextPath Value[CompletionFromModule, nn]
Path CompletionFromModule.nn
CPPipe pathFromEnv:SomeOtherModule found:true
Path CompletionFromModule.SomeOtherModule.
Path CompletionFromModule.SomeOtherModule.
[{
"label": "nname",
"kind": 5,
"tags": [],
"detail": "string",
"documentation": {"kind": "markdown", "value": "```rescript\nnname: string\n```\n\n```rescript\ntype typeOutsideModule = {nname: string}\n```"}
}, {
"label": "->CompletionFromModule.SomeOtherModule.getNName",
"kind": 12,
"tags": [],
"detail": "t => string",
"documentation": null,
"sortText": "getNName",
"insertText": "->CompletionFromModule.SomeOtherModule.getNName",
"additionalTextEdits": [{
"range": {"start": {"line": 5, "character": 26}, "end": {"line": 5, "character": 27}},
"newText": ""
}]
}, {
"label": "->CompletionFromModule.SomeOtherModule.getNName2",
"kind": 12,
"tags": [],
"detail": "typeOutsideModule => string",
"documentation": null,
"sortText": "getNName2",
"insertText": "->CompletionFromModule.SomeOtherModule.getNName2",
"additionalTextEdits": [{
"range": {"start": {"line": 5, "character": 26}, "end": {"line": 5, "character": 27}},
"newText": ""
}]
}, {
"label": "->CompletionFromModule.SomeOtherModule.getNName",
"kind": 12,
"tags": [],
"detail": "t => string",
"documentation": null,
"sortText": "getNName",
"insertText": "->CompletionFromModule.SomeOtherModule.getNName",
"additionalTextEdits": [{
"range": {"start": {"line": 5, "character": 26}, "end": {"line": 5, "character": 27}},
"newText": ""
}]
}, {
"label": "->CompletionFromModule.SomeOtherModule.getNName2",
"kind": 12,
"tags": [],
"detail": "typeOutsideModule => string",
"documentation": null,
"sortText": "getNName2",
"insertText": "->CompletionFromModule.SomeOtherModule.getNName2",
"additionalTextEdits": [{
"range": {"start": {"line": 5, "character": 26}, "end": {"line": 5, "character": 27}},
"newText": ""
}]
}]

Complete src/CompletionFromModule2.res 8:29
posCursor:[8:29] posNoWhite:[8:28] Found expr:[8:3->0:-1]
Completable: Cpath Value[CompletionFromModule, nnn]->
Package opens Pervasives.JsxModules.place holder
Resolved opens 1 pervasives
ContextPath Value[CompletionFromModule, nnn]->
ContextPath Value[CompletionFromModule, nnn]
Path CompletionFromModule.nnn
CPPipe pathFromEnv: found:true
Path CompletionFromModule.
Path CompletionFromModule.SomeOtherModule.
[{
"label": "CompletionFromModule.SomeOtherModule.getNName",
"kind": 12,
"tags": [],
"detail": "t => string",
"documentation": null
}, {
"label": "CompletionFromModule.SomeOtherModule.getNName2",
"kind": 12,
"tags": [],
"detail": "typeOutsideModule => string",
"documentation": null
}]

Complete src/CompletionFromModule2.res 12:29
posCursor:[12:29] posNoWhite:[12:28] Found expr:[12:3->0:-1]
Completable: Cpath Value[CompletionFromModule, nnn]->
Raw opens: 1 CompletionFromModule.SomeOtherModule.place holder
Package opens Pervasives.JsxModules.place holder
Resolved opens 2 pervasives CompletionFromModule.res
ContextPath Value[CompletionFromModule, nnn]->
ContextPath Value[CompletionFromModule, nnn]
Path CompletionFromModule.nnn
CPPipe pathFromEnv: found:true
Path CompletionFromModule.
Path CompletionFromModule.SomeOtherModule.
[{
"label": "getNName",
"kind": 12,
"tags": [],
"detail": "t => string",
"documentation": null
}, {
"label": "getNName2",
"kind": 12,
"tags": [],
"detail": "typeOutsideModule => string",
"documentation": null
}]

Original file line number Diff line number Diff line change
@@ -400,8 +400,6 @@ Resolved opens 1 pervasives
ContextPath Value[thisGetsBrokenLoc]->a <<jsx>>
ContextPath Value[thisGetsBrokenLoc]
Path thisGetsBrokenLoc
CPPipe env:CompletionFunctionArguments
CPPipe type path:ReactEvent.Mouse.t
CPPipe pathFromEnv:ReactEvent.Mouse found:false
Path ReactEvent.Mouse.a
[{
@@ -426,8 +424,6 @@ Resolved opens 1 pervasives
ContextPath Value[reassignedWorks]->a <<jsx>>
ContextPath Value[reassignedWorks]
Path reassignedWorks
CPPipe env:CompletionFunctionArguments
CPPipe type path:ReactEvent.Mouse.t
CPPipe pathFromEnv:ReactEvent.Mouse found:false
Path ReactEvent.Mouse.a
[{
@@ -449,8 +445,6 @@ Resolved opens 1 pervasives
ContextPath Value[fineModuleVal]->
ContextPath Value[fineModuleVal]
Path fineModuleVal
CPPipe env:CompletionFunctionArguments
CPPipe type path:FineModule.t
CPPipe pathFromEnv:FineModule found:true
Path FineModule.
[{
312 changes: 153 additions & 159 deletions analysis/tests/src/expected/CompletionInferValues.res.txt

Large diffs are not rendered by default.

32 changes: 0 additions & 32 deletions analysis/tests/src/expected/CompletionJsx.res.txt
Original file line number Diff line number Diff line change
@@ -6,7 +6,6 @@ Resolved opens 1 pervasives
ContextPath Value[someString]->st
ContextPath Value[someString]
Path someString
CPPipe env:CompletionJsx
Path Js.String2.st
[{
"label": "Js.String2.startsWith",
@@ -37,7 +36,6 @@ Resolved opens 1 pervasives
ContextPath Value[someString]->st <<jsx>>
ContextPath Value[someString]
Path someString
CPPipe env:CompletionJsx
Path Js.String2.st
[{
"label": "React.string",
@@ -83,7 +81,6 @@ Resolved opens 1 pervasives
ContextPath Value[someString]->st <<jsx>>
ContextPath Value[someString]
Path someString
CPPipe env:CompletionJsx
Path Js.String2.st
[{
"label": "React.string",
@@ -128,7 +125,6 @@ Package opens Pervasives.JsxModules.place holder
Resolved opens 1 pervasives
ContextPath string->st <<jsx>>
ContextPath string
CPPipe env:CompletionJsx
Path Js.String2.st
[{
"label": "React.string",
@@ -175,7 +171,6 @@ ContextPath Value[Js, String2, trim](Nolabel)->st <<jsx>>
ContextPath Value[Js, String2, trim](Nolabel)
ContextPath Value[Js, String2, trim]
Path Js.String2.trim
CPPipe env:CompletionJsx envFromCompletionItem:Js_string2
Path Js.String2.st
[{
"label": "React.string",
@@ -221,7 +216,6 @@ Resolved opens 1 pervasives
ContextPath Value[someInt]-> <<jsx>>
ContextPath Value[someInt]
Path someInt
CPPipe env:CompletionJsx
Path Belt.Int.
[{
"label": "React.int",
@@ -231,12 +225,6 @@ Path Belt.Int.
"documentation": {"kind": "markdown", "value": "Turns `int` into a JSX element so it can be used inside of JSX."},
"sortText": "A",
"insertTextFormat": 2
}, {
"label": "Belt.Int.fromString",
"kind": 12,
"tags": [],
"detail": "string => option<int>",
"documentation": {"kind": "markdown", "value": "\nConverts a given `string` to an `int`. Returns `Some(int)` when the input is a number, `None` otherwise.\n\n## Examples\n\n```rescript\nJs.log(Belt.Int.fromString(\"1\") === Some(1)) /* true */\n```\n"}
}, {
"label": "Belt.Int.*",
"kind": 12,
@@ -261,12 +249,6 @@ Path Belt.Int.
"tags": [],
"detail": "int => float",
"documentation": {"kind": "markdown", "value": "\nConverts a given `int` to a `float`.\n\n## Examples\n\n```rescript\nJs.log(Belt.Int.toFloat(1) === 1.0) /* true */\n```\n"}
}, {
"label": "Belt.Int.fromFloat",
"kind": 12,
"tags": [],
"detail": "float => int",
"documentation": {"kind": "markdown", "value": "\nConverts a given `float` to an `int`.\n\n## Examples\n\n```rescript\nJs.log(Belt.Int.fromFloat(1.0) === 1) /* true */\n```\n"}
}, {
"label": "Belt.Int.-",
"kind": 12,
@@ -302,7 +284,6 @@ Package opens Pervasives.JsxModules.place holder
Resolved opens 1 pervasives
ContextPath int-> <<jsx>>
ContextPath int
CPPipe env:CompletionJsx
Path Belt.Int.
[{
"label": "React.int",
@@ -312,12 +293,6 @@ Path Belt.Int.
"documentation": {"kind": "markdown", "value": "Turns `int` into a JSX element so it can be used inside of JSX."},
"sortText": "A",
"insertTextFormat": 2
}, {
"label": "Belt.Int.fromString",
"kind": 12,
"tags": [],
"detail": "string => option<int>",
"documentation": {"kind": "markdown", "value": "\nConverts a given `string` to an `int`. Returns `Some(int)` when the input is a number, `None` otherwise.\n\n## Examples\n\n```rescript\nJs.log(Belt.Int.fromString(\"1\") === Some(1)) /* true */\n```\n"}
}, {
"label": "Belt.Int.*",
"kind": 12,
@@ -342,12 +317,6 @@ Path Belt.Int.
"tags": [],
"detail": "int => float",
"documentation": {"kind": "markdown", "value": "\nConverts a given `int` to a `float`.\n\n## Examples\n\n```rescript\nJs.log(Belt.Int.toFloat(1) === 1.0) /* true */\n```\n"}
}, {
"label": "Belt.Int.fromFloat",
"kind": 12,
"tags": [],
"detail": "float => int",
"documentation": {"kind": "markdown", "value": "\nConverts a given `float` to an `int`.\n\n## Examples\n\n```rescript\nJs.log(Belt.Int.fromFloat(1.0) === 1) /* true */\n```\n"}
}, {
"label": "Belt.Int.-",
"kind": 12,
@@ -384,7 +353,6 @@ Resolved opens 1 pervasives
ContextPath Value[someArr]->a <<jsx>>
ContextPath Value[someArr]
Path someArr
CPPipe env:CompletionJsx
Path Js.Array2.a
[{
"label": "React.array",
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
Complete src/CompletionMultipleEditorCompleteFrom.res 19:5
posCursor:[19:5] posNoWhite:[19:4] Found expr:[19:3->19:5]
Pexp_field [19:3->19:4] _:[22:0->19:5]
Completable: Cpath Value[a].""
Package opens Pervasives.JsxModules.place holder
Resolved opens 1 pervasives
ContextPath Value[a].""
ContextPath Value[a]
Path a
ContextPath Value[a]->
ContextPath Value[a]
Path a
CPPipe pathFromEnv:A found:true
Path A.
Path B.
Path C.
[{
"label": "->B.b",
"kind": 12,
"tags": [],
"detail": "A.a => int",
"documentation": null,
"sortText": "b",
"insertText": "->B.b",
"additionalTextEdits": [{
"range": {"start": {"line": 19, "character": 4}, "end": {"line": 19, "character": 5}},
"newText": ""
}]
}, {
"label": "->C.c",
"kind": 12,
"tags": [],
"detail": "A.a => char",
"documentation": null,
"sortText": "c",
"insertText": "->C.c",
"additionalTextEdits": [{
"range": {"start": {"line": 19, "character": 4}, "end": {"line": 19, "character": 5}},
"newText": ""
}]
}]

144 changes: 36 additions & 108 deletions analysis/tests/src/expected/CompletionPipeChain.res.txt
Original file line number Diff line number Diff line change
@@ -6,8 +6,6 @@ Resolved opens 1 pervasives
ContextPath Value[int]->
ContextPath Value[int]
Path int
CPPipe env:CompletionPipeChain
CPPipe type path:Integer.t
CPPipe pathFromEnv:Integer found:true
Path Integer.
[{
@@ -28,12 +26,6 @@ Path Integer.
"tags": [],
"detail": "(t, int => int) => t",
"documentation": null
}, {
"label": "Integer.make",
"kind": 12,
"tags": [],
"detail": "int => t",
"documentation": null
}]

Complete src/CompletionPipeChain.res 30:23
@@ -45,17 +37,9 @@ ContextPath Value[toFlt](Nolabel)->
ContextPath Value[toFlt](Nolabel)
ContextPath Value[toFlt]
Path toFlt
CPPipe env:CompletionPipeChain
CPPipe type path:SuperFloat.t
CPPipe pathFromEnv:SuperFloat found:true
Path SuperFloat.
[{
"label": "SuperFloat.fromInteger",
"kind": 12,
"tags": [],
"detail": "Integer.t => t",
"documentation": null
}, {
"label": "SuperFloat.toInteger",
"kind": 12,
"tags": [],
@@ -72,8 +56,6 @@ ContextPath Value[Integer, increment](Nolabel, Nolabel)->
ContextPath Value[Integer, increment](Nolabel, Nolabel)
ContextPath Value[Integer, increment]
Path Integer.increment
CPPipe env:CompletionPipeChain envFromCompletionItem:CompletionPipeChain.Integer
CPPipe type path:t
CPPipe pathFromEnv:Integer found:true
Path Integer.
[{
@@ -94,12 +76,6 @@ Path Integer.
"tags": [],
"detail": "(t, int => int) => t",
"documentation": null
}, {
"label": "Integer.make",
"kind": 12,
"tags": [],
"detail": "int => t",
"documentation": null
}]

Complete src/CompletionPipeChain.res 36:38
@@ -111,8 +87,6 @@ ContextPath Value[Integer, increment](Nolabel, Nolabel)->
ContextPath Value[Integer, increment](Nolabel, Nolabel)
ContextPath Value[Integer, increment]
Path Integer.increment
CPPipe env:CompletionPipeChain envFromCompletionItem:CompletionPipeChain.Integer
CPPipe type path:t
CPPipe pathFromEnv:Integer found:true
Path Integer.
[{
@@ -133,12 +107,6 @@ Path Integer.
"tags": [],
"detail": "(t, int => int) => t",
"documentation": null
}, {
"label": "Integer.make",
"kind": 12,
"tags": [],
"detail": "int => t",
"documentation": null
}]

Complete src/CompletionPipeChain.res 39:47
@@ -150,8 +118,6 @@ ContextPath Value[Integer, decrement](Nolabel, Nolabel)->
ContextPath Value[Integer, decrement](Nolabel, Nolabel)
ContextPath Value[Integer, decrement]
Path Integer.decrement
CPPipe env:CompletionPipeChain envFromCompletionItem:CompletionPipeChain.Integer
CPPipe type path:t
CPPipe pathFromEnv:Integer found:true
Path Integer.
[{
@@ -172,12 +138,6 @@ Path Integer.
"tags": [],
"detail": "(t, int => int) => t",
"documentation": null
}, {
"label": "Integer.make",
"kind": 12,
"tags": [],
"detail": "int => t",
"documentation": null
}]

Complete src/CompletionPipeChain.res 42:69
@@ -189,8 +149,6 @@ ContextPath Value[Integer, decrement](Nolabel, Nolabel)->
ContextPath Value[Integer, decrement](Nolabel, Nolabel)
ContextPath Value[Integer, decrement]
Path Integer.decrement
CPPipe env:CompletionPipeChain envFromCompletionItem:CompletionPipeChain.Integer
CPPipe type path:t
CPPipe pathFromEnv:Integer found:true
Path Integer.
[{
@@ -211,12 +169,6 @@ Path Integer.
"tags": [],
"detail": "(t, int => int) => t",
"documentation": null
}, {
"label": "Integer.make",
"kind": 12,
"tags": [],
"detail": "int => t",
"documentation": null
}]

Complete src/CompletionPipeChain.res 45:62
@@ -228,17 +180,9 @@ ContextPath Value[SuperFloat, fromInteger](Nolabel)->
ContextPath Value[SuperFloat, fromInteger](Nolabel)
ContextPath Value[SuperFloat, fromInteger]
Path SuperFloat.fromInteger
CPPipe env:CompletionPipeChain envFromCompletionItem:CompletionPipeChain.SuperFloat
CPPipe type path:t
CPPipe pathFromEnv:SuperFloat found:true
Path SuperFloat.
[{
"label": "SuperFloat.fromInteger",
"kind": 12,
"tags": [],
"detail": "Integer.t => t",
"documentation": null
}, {
"label": "SuperFloat.toInteger",
"kind": 12,
"tags": [],
@@ -255,8 +199,6 @@ ContextPath Value[SuperFloat, fromInteger](Nolabel)->t
ContextPath Value[SuperFloat, fromInteger](Nolabel)
ContextPath Value[SuperFloat, fromInteger]
Path SuperFloat.fromInteger
CPPipe env:CompletionPipeChain envFromCompletionItem:CompletionPipeChain.SuperFloat
CPPipe type path:t
CPPipe pathFromEnv:SuperFloat found:true
Path SuperFloat.t
[{
@@ -276,8 +218,6 @@ ContextPath Value[CompletionSupport, Test, make](Nolabel)->
ContextPath Value[CompletionSupport, Test, make](Nolabel)
ContextPath Value[CompletionSupport, Test, make]
Path CompletionSupport.Test.make
CPPipe env:CompletionPipeChain envFromCompletionItem:CompletionSupport.Test
CPPipe type path:t
CPPipe pathFromEnv:Test found:true
Path CompletionSupport.Test.
[{
@@ -292,12 +232,6 @@ Path CompletionSupport.Test.
"tags": [],
"detail": "t => t",
"documentation": null
}, {
"label": "CompletionSupport.Test.make",
"kind": 12,
"tags": [],
"detail": "int => t",
"documentation": null
}]

Complete src/CompletionPipeChain.res 54:78
@@ -309,8 +243,6 @@ ContextPath Value[CompletionSupport, Test, addSelf](Nolabel, Nolabel)->
ContextPath Value[CompletionSupport, Test, addSelf](Nolabel, Nolabel)
ContextPath Value[CompletionSupport, Test, addSelf]
Path CompletionSupport.Test.addSelf
CPPipe env:CompletionPipeChain envFromCompletionItem:CompletionSupport.Test
CPPipe type path:t
CPPipe pathFromEnv:Test found:true
Path CompletionSupport.Test.
[{
@@ -325,12 +257,6 @@ Path CompletionSupport.Test.
"tags": [],
"detail": "t => t",
"documentation": null
}, {
"label": "CompletionSupport.Test.make",
"kind": 12,
"tags": [],
"detail": "int => t",
"documentation": null
}]

Complete src/CompletionPipeChain.res 58:5
@@ -342,9 +268,8 @@ ContextPath Value[Js, Array2, forEach](Nolabel, Nolabel)->
ContextPath Value[Js, Array2, forEach](Nolabel, Nolabel)
ContextPath Value[Js, Array2, forEach]
Path Js.Array2.forEach
CPPipe env:CompletionPipeChain envFromCompletionItem:Js_array2
CPPipe type path:unit
CPPipe pathFromEnv: found:true
Path Js_array2.
[]

Complete src/CompletionPipeChain.res 62:6
@@ -356,7 +281,6 @@ ContextPath Value[Belt, Array, reduce](Nolabel, Nolabel, Nolabel)->t
ContextPath Value[Belt, Array, reduce](Nolabel, Nolabel, Nolabel)
ContextPath Value[Belt, Array, reduce]
Path Belt.Array.reduce
CPPipe env:CompletionPipeChain envFromCompletionItem:Belt_Array
Path Belt.Int.t
[{
"label": "Belt.Int.toString",
@@ -380,8 +304,6 @@ Resolved opens 1 pervasives
ContextPath Value[aliased]->
ContextPath Value[aliased]
Path aliased
CPPipe env:CompletionPipeChain
CPPipe type path:CompletionSupport.Test.t
CPPipe pathFromEnv:CompletionSupport.Test found:false
Path CompletionSupport.Test.
[{
@@ -396,12 +318,6 @@ Path CompletionSupport.Test.
"tags": [],
"detail": "t => t",
"documentation": null
}, {
"label": "CompletionSupport.Test.make",
"kind": 12,
"tags": [],
"detail": "int => t",
"documentation": null
}]

Complete src/CompletionPipeChain.res 73:15
@@ -412,8 +328,6 @@ Resolved opens 1 pervasives
ContextPath Value[notAliased]->
ContextPath Value[notAliased]
Path notAliased
CPPipe env:CompletionPipeChain
CPPipe type path:CompletionSupport.Test.t
CPPipe pathFromEnv:CompletionSupport.Test found:false
Path CompletionSupport.Test.
[{
@@ -428,12 +342,6 @@ Path CompletionSupport.Test.
"tags": [],
"detail": "t => t",
"documentation": null
}, {
"label": "CompletionSupport.Test.make",
"kind": 12,
"tags": [],
"detail": "int => t",
"documentation": null
}]

Complete src/CompletionPipeChain.res 82:30
@@ -452,8 +360,22 @@ ContextPath Value[props].support.root
ContextPath Value[props].support
ContextPath Value[props]
Path props
CPPipe env:CompletionPipeChain envFromCompletionItem:CompletionSupport.Nested
CPPipe type path:ReactDOM.Client.Root.t
ContextPath Value[props]->support
ContextPath Value[props]
Path props
CPPipe pathFromEnv:CompletionSupport2.Internal found:false
Path CompletionSupport2.Internal.support
ContextPath Value[props].support->root
ContextPath Value[props].support
ContextPath Value[props]
Path props
ContextPath Value[props]->support
ContextPath Value[props]
Path props
CPPipe pathFromEnv:CompletionSupport2.Internal found:false
Path CompletionSupport2.Internal.support
CPPipe pathFromEnv:CompletionSupport.Nested found:false
Path CompletionSupport.Nested.root
CPPipe pathFromEnv:ReactDOM.Client.Root found:false
Path ReactDOM.Client.Root.ren
[{
@@ -480,8 +402,6 @@ Resolved opens 1 pervasives
ContextPath Value[root]->ren
ContextPath Value[root]
Path root
CPPipe env:CompletionPipeChain
CPPipe type path:ReactDOM.Client.Root.t
CPPipe pathFromEnv:ReactDOM.Client.Root found:false
Path ReactDOM.Client.Root.ren
[{
@@ -503,8 +423,6 @@ Resolved opens 1 pervasives
ContextPath Value[int]->
ContextPath Value[int]
Path int
CPPipe env:CompletionPipeChain
CPPipe type path:Integer.t
CPPipe pathFromEnv:Integer found:true
Path Integer.
[{
@@ -525,12 +443,6 @@ Path Integer.
"tags": [],
"detail": "(t, int => int) => t",
"documentation": null
}, {
"label": "Integer.make",
"kind": 12,
"tags": [],
"detail": "int => t",
"documentation": null
}]

Complete src/CompletionPipeChain.res 98:21
@@ -543,8 +455,6 @@ Resolved opens 1 pervasives
ContextPath Value[int]->t
ContextPath Value[int]
Path int
CPPipe env:CompletionPipeChain
CPPipe type path:Integer.t
CPPipe pathFromEnv:Integer found:true
Path Integer.t
[{
@@ -563,7 +473,7 @@ Resolved opens 1 pervasives
ContextPath Value[r]->la
ContextPath Value[r]
Path r
CPPipe env:CompletionPipeChain
CPPipe pathFromEnv:Js.Re found:false
Path Js.Re.la
[{
"label": "Js.Re.lastIndex",
@@ -573,3 +483,21 @@ Path Js.Re.la
"documentation": {"kind": "markdown", "value": "\nReturns the index where the next match will start its search. This property\nwill be modified when the RegExp object is used, if the global (\"g\") flag is\nset.\n\n## Examples\n\n```rescript\nlet re = %re(\"/ab*TODO/g\")\nlet str = \"abbcdefabh\"\n\nlet break = ref(false)\nwhile !break.contents {\n switch Js.Re.exec_(re, str) {\n | Some(result) => Js.Nullable.iter(Js.Re.captures(result)[0], (. match_) => {\n let next = Belt.Int.toString(Js.Re.lastIndex(re))\n Js.log(\"Found \" ++ (match_ ++ (\". Next match starts at \" ++ next)))\n })\n | None => break := true\n }\n}\n```\n\nSee\n[`RegExp: lastIndex`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex)\non MDN.\n"}
}]

Complete src/CompletionPipeChain.res 112:7
posCursor:[112:7] posNoWhite:[112:6] Found expr:[112:3->0:-1]
Completable: Cpath Value[xx]->
Package opens Pervasives.JsxModules.place holder
Resolved opens 1 pervasives
ContextPath Value[xx]->
ContextPath Value[xx]
Path xx
CPPipe pathFromEnv:Xyz found:true
Path Xyz.
[{
"label": "Xyz.do",
"kind": 12,
"tags": [],
"detail": "xx => string",
"documentation": null
}]

64 changes: 64 additions & 0 deletions analysis/tests/src/expected/CompletionPipeProperty.res.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
Complete src/CompletionPipeProperty.res 21:17
posCursor:[21:17] posNoWhite:[21:16] Found expr:[21:3->21:17]
Pexp_field [21:3->21:16] _:[23:0->21:17]
Completable: Cpath Value[sprite].anchor.""
Package opens Pervasives.JsxModules.place holder
Resolved opens 1 pervasives
ContextPath Value[sprite].anchor.""
ContextPath Value[sprite].anchor
ContextPath Value[sprite]
Path sprite
ContextPath Value[sprite]->anchor
ContextPath Value[sprite]
Path sprite
CPPipe pathFromEnv:Sprite found:true
Path Sprite.anchor
ContextPath Value[sprite].anchor->
ContextPath Value[sprite].anchor
ContextPath Value[sprite]
Path sprite
ContextPath Value[sprite]->anchor
ContextPath Value[sprite]
Path sprite
CPPipe pathFromEnv:Sprite found:true
Path Sprite.anchor
CPPipe pathFromEnv:ObservablePoint found:true
Path ObservablePoint.
[{
"label": "x",
"kind": 5,
"tags": [],
"detail": "int",
"documentation": {"kind": "markdown", "value": "```rescript\nx: int\n```\n\n```rescript\ntype op = {mutable x: int, mutable y: int}\n```"}
}, {
"label": "y",
"kind": 5,
"tags": [],
"detail": "int",
"documentation": {"kind": "markdown", "value": "```rescript\ny: int\n```\n\n```rescript\ntype op = {mutable x: int, mutable y: int}\n```"}
}, {
"label": "->ObservablePoint.setBoth",
"kind": 12,
"tags": [],
"detail": "(op, float) => unit",
"documentation": null,
"sortText": "setBoth",
"insertText": "->ObservablePoint.setBoth",
"additionalTextEdits": [{
"range": {"start": {"line": 21, "character": 16}, "end": {"line": 21, "character": 17}},
"newText": ""
}]
}, {
"label": "->ObservablePoint.set",
"kind": 12,
"tags": [],
"detail": "(op, float, float) => unit",
"documentation": null,
"sortText": "set",
"insertText": "->ObservablePoint.set",
"additionalTextEdits": [{
"range": {"start": {"line": 21, "character": 16}, "end": {"line": 21, "character": 17}},
"newText": ""
}]
}]

101 changes: 59 additions & 42 deletions analysis/tests/src/expected/CompletionPipeSubmodules.res.txt
Original file line number Diff line number Diff line change
@@ -1,58 +1,47 @@
Complete src/CompletionPipeSubmodules.res 12:20
posCursor:[12:20] posNoWhite:[12:19] Found expr:[12:11->20:8]
Complete src/CompletionPipeSubmodules.res 13:20
posCursor:[13:20] posNoWhite:[13:19] Found expr:[13:11->21:8]
Completable: Cpath Value[A, B1, xx]->
Package opens Pervasives.JsxModules.place holder
Resolved opens 1 pervasives
ContextPath Value[A, B1, xx]->
ContextPath Value[A, B1, xx]
Path A.B1.xx
CPPipe env:CompletionPipeSubmodules envFromCompletionItem:CompletionPipeSubmodules.A.B1
CPPipe type path:b1
CPPipe pathFromEnv:A.B1 found:true
Path A.B1.
[{
"label": "A.B1.xx",
"label": "A.B1.d",
"kind": 12,
"tags": [],
"detail": "b1",
"documentation": {"kind": "markdown", "value": "```rescript\ntype b1 = B1\n```"}
}, {
"label": "A.B1.B1",
"kind": 4,
"tags": [],
"detail": "B1",
"documentation": {"kind": "markdown", "value": "```rescript\nB1\n```\n\n```rescript\ntype b1 = B1\n```"}
"detail": "b1 => string",
"documentation": null
}]

Complete src/CompletionPipeSubmodules.res 16:18
posCursor:[16:18] posNoWhite:[16:17] Found expr:[16:11->20:8]
Complete src/CompletionPipeSubmodules.res 17:18
posCursor:[17:18] posNoWhite:[17:17] Found expr:[17:11->21:8]
Completable: Cpath Value[A, x].v->
Package opens Pervasives.JsxModules.place holder
Resolved opens 1 pervasives
ContextPath Value[A, x].v->
ContextPath Value[A, x].v
ContextPath Value[A, x]
Path A.x
CPPipe env:CompletionPipeSubmodules envFromCompletionItem:CompletionPipeSubmodules.A
CPPipe type path:B1.b1
ContextPath Value[A, x]->v
ContextPath Value[A, x]
Path A.x
CPPipe pathFromEnv:A found:true
Path A.v
CPPipe pathFromEnv:A.B1 found:true
Path A.B1.
[{
"label": "A.B1.xx",
"label": "A.B1.d",
"kind": 12,
"tags": [],
"detail": "b1",
"documentation": {"kind": "markdown", "value": "```rescript\ntype b1 = B1\n```"}
}, {
"label": "A.B1.B1",
"kind": 4,
"tags": [],
"detail": "B1",
"documentation": {"kind": "markdown", "value": "```rescript\nB1\n```\n\n```rescript\ntype b1 = B1\n```"}
"detail": "b1 => string",
"documentation": null
}]

Complete src/CompletionPipeSubmodules.res 38:20
posCursor:[38:20] posNoWhite:[38:19] Found expr:[38:11->0:-1]
Complete src/CompletionPipeSubmodules.res 41:20
posCursor:[41:20] posNoWhite:[41:19] Found expr:[41:11->0:-1]
Completable: Cpath Value[E, e].v.v->
Package opens Pervasives.JsxModules.place holder
Resolved opens 1 pervasives
@@ -61,20 +50,34 @@ ContextPath Value[E, e].v.v
ContextPath Value[E, e].v
ContextPath Value[E, e]
Path E.e
CPPipe env:CompletionPipeSubmodules envFromCompletionItem:CompletionPipeSubmodules.D
CPPipe type path:C.t
ContextPath Value[E, e]->v
ContextPath Value[E, e]
Path E.e
CPPipe pathFromEnv:E found:true
Path E.v
ContextPath Value[E, e].v->v
ContextPath Value[E, e].v
ContextPath Value[E, e]
Path E.e
ContextPath Value[E, e]->v
ContextPath Value[E, e]
Path E.e
CPPipe pathFromEnv:E found:true
Path E.v
CPPipe pathFromEnv:D found:true
Path D.v
CPPipe pathFromEnv:C found:false
Path C.
[{
"label": "C.C",
"kind": 4,
"label": "C.do",
"kind": 12,
"tags": [],
"detail": "C",
"documentation": {"kind": "markdown", "value": "```rescript\nC\n```\n\n```rescript\ntype t = C\n```"}
"detail": "t => string",
"documentation": null
}]

Complete src/CompletionPipeSubmodules.res 42:21
posCursor:[42:21] posNoWhite:[42:20] Found expr:[42:11->0:-1]
Complete src/CompletionPipeSubmodules.res 45:21
posCursor:[45:21] posNoWhite:[45:20] Found expr:[45:11->0:-1]
Completable: Cpath Value[E, e].v.v2->
Package opens Pervasives.JsxModules.place holder
Resolved opens 1 pervasives
@@ -83,15 +86,29 @@ ContextPath Value[E, e].v.v2
ContextPath Value[E, e].v
ContextPath Value[E, e]
Path E.e
CPPipe env:CompletionPipeSubmodules envFromCompletionItem:CompletionPipeSubmodules.D
CPPipe type path:C2.t2
ContextPath Value[E, e]->v
ContextPath Value[E, e]
Path E.e
CPPipe pathFromEnv:E found:true
Path E.v
ContextPath Value[E, e].v->v2
ContextPath Value[E, e].v
ContextPath Value[E, e]
Path E.e
ContextPath Value[E, e]->v
ContextPath Value[E, e]
Path E.e
CPPipe pathFromEnv:E found:true
Path E.v
CPPipe pathFromEnv:D found:true
Path D.v2
CPPipe pathFromEnv:D.C2 found:true
Path D.C2.
[{
"label": "D.C2.C2",
"kind": 4,
"label": "D.C2.do",
"kind": 12,
"tags": [],
"detail": "C2",
"documentation": {"kind": "markdown", "value": "```rescript\nC2\n```\n\n```rescript\ntype t2 = C2\n```"}
"detail": "t2 => string",
"documentation": null
}]

36 changes: 18 additions & 18 deletions analysis/tests/src/expected/Cross.res.txt
Original file line number Diff line number Diff line change
@@ -35,12 +35,12 @@ Rename src/Cross.res 18:13 RenameWithInterfacePrime
"uri": "Cross.res"
},
"edits": [{
"range": {"start": {"line": 18, "character": 8}, "end": {"line": 18, "character": 27}},
"newText": "RenameWithInterfacePrime"
}, {
"range": {"start": {"line": 21, "character": 8}, "end": {"line": 21, "character": 27}},
"newText": "RenameWithInterfacePrime"
}]
"range": {"start": {"line": 18, "character": 8}, "end": {"line": 18, "character": 27}},
"newText": "RenameWithInterfacePrime"
}, {
"range": {"start": {"line": 21, "character": 8}, "end": {"line": 21, "character": 27}},
"newText": "RenameWithInterfacePrime"
}]
}
]

@@ -52,32 +52,32 @@ Rename src/Cross.res 21:28 xPrime
"uri": "RenameWithInterface.resi"
},
"edits": [{
"range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}},
"newText": "xPrime"
}]
"range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}},
"newText": "xPrime"
}]
},
{
"textDocument": {
"version": null,
"uri": "RenameWithInterface.res"
},
"edits": [{
"range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}},
"newText": "xPrime"
}]
"range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}},
"newText": "xPrime"
}]
},
{
"textDocument": {
"version": null,
"uri": "Cross.res"
},
"edits": [{
"range": {"start": {"line": 18, "character": 28}, "end": {"line": 18, "character": 29}},
"newText": "xPrime"
}, {
"range": {"start": {"line": 21, "character": 28}, "end": {"line": 21, "character": 29}},
"newText": "xPrime"
}]
"range": {"start": {"line": 18, "character": 28}, "end": {"line": 18, "character": 29}},
"newText": "xPrime"
}, {
"range": {"start": {"line": 21, "character": 28}, "end": {"line": 21, "character": 29}},
"newText": "xPrime"
}]
}
]

Loading