diff --git a/CHANGELOG.md b/CHANGELOG.md index f2f4c4ad5..ab7462c74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ ## master +#### :nail_care: Polish + +Enhance decorator completion. https://github.com/rescript-lang/rescript-vscode/pull/908 + ## 1.38.0 #### :nail_care: Polish diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 7df6d8a99..5e5de855e 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1746,6 +1746,36 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable = && (forHover || not (List.mem name identsSeen))) |> List.map mkLabel) @ keyLabels + | CdecoratorPayload (JsxConfig {prefix; nested}) -> ( + let mkField ~name ~primitive = + { + stamp = -1; + fname = {loc = Location.none; txt = name}; + optional = true; + typ = Ctype.newconstr (Path.Pident (Ident.create primitive)) []; + docstring = []; + deprecated = None; + } + in + let typ : completionType = + Trecord + { + env; + definition = `NameOnly "jsxConfig"; + fields = + [ + mkField ~name:"version" ~primitive:"int"; + mkField ~name:"module_" ~primitive:"string"; + mkField ~name:"mode" ~primitive:"string"; + ]; + } + in + match typ |> TypeUtils.resolveNested ~env ~full ~nested with + | None -> [] + | Some (typ, _env, completionContext, typeArgContext) -> + typ + |> completeTypedValue ?typeArgContext ~rawOpens ~mode:Expression ~full + ~prefix ~completionContext) | CdecoratorPayload (Module prefix) -> let packageJsonPath = Utils.findPackageJson (full.package.rootPath |> Uri.fromPath) @@ -1824,8 +1854,13 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable = ~kind:(Label (if isLocal then "Local file" else "Package")) ~env) | Cdecorator prefix -> - let mkDecorator (name, docstring) = - {(Completion.create name ~kind:(Label "") ~env) with docstring} + let mkDecorator (name, docstring, maybeInsertText) = + { + (Completion.createWithSnippet ~name ~kind:(Label "") ~env + ?insertText:maybeInsertText ()) + with + docstring; + } in let isTopLevel = String.starts_with ~prefix:"@" prefix in let prefix = @@ -1837,8 +1872,8 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable = else CompletionDecorators.local in decorators - |> List.filter (fun (decorator, _) -> Utils.startsWith decorator prefix) - |> List.map (fun (decorator, doc) -> + |> List.filter (fun (decorator, _, _) -> Utils.startsWith decorator prefix) + |> List.map (fun (decorator, maybeInsertText, doc) -> let parts = String.split_on_char '.' prefix in let len = String.length prefix in let dec2 = @@ -1846,7 +1881,7 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable = String.sub decorator len (String.length decorator - len) else decorator in - (dec2, doc)) + (dec2, doc, maybeInsertText)) |> List.map mkDecorator | CnamedArg (cp, prefix, identsSeen) -> let labels = diff --git a/analysis/src/CompletionDecorators.ml b/analysis/src/CompletionDecorators.ml index f8beb7910..d529cec78 100644 --- a/analysis/src/CompletionDecorators.ml +++ b/analysis/src/CompletionDecorators.ml @@ -1,6 +1,7 @@ let local = [ ( "as", + Some "as(\"$0\")", [ {|The `@as` decorator is commonly used on record types to alias record field names to a different JavaScript attribute name. @@ -11,6 +12,7 @@ It is also possible to map a ReScript record to a JavaScript array by passing in [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#as-decorator).|}; ] ); ( "dead", + None, [ {|The `@dead` decorator is for reanalyze, a static analysis tool for ReScript that can do dead code analysis. @@ -21,12 +23,14 @@ It is also possible to map a ReScript record to a JavaScript array by passing in > Hint: Did you know you can run an interactive code analysis in your project by running the command `> ReScript: Start Code Analyzer`? Try it!|}; ] ); ( "deriving", + Some "deriving($0)", [ {|When the `@deriving` decorator is applied to a record type, it expands the type into a factory function plus a set of getter/setter functions for its fields. [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#deriving-decorator).|}; ] ); ( "deprecated", + Some "deprecated(\"$0\")", [ {|The `@deprecated` decorator is used to add deprecation notes to types, values and submodules. The compiler and editor tooling will yield a warning whenever a deprecated entity is being used. @@ -35,6 +39,7 @@ Alternatively, use the `@@deprecated` decorator to add a deprecation warning to [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#expression-deprecated-decorator).|}; ] ); ( "doesNotRaise", + None, [ {|The `@doesNotRaise` decorator is for reanalyze, a static analysis tool for ReScript that can perform exception analysis. @@ -46,54 +51,63 @@ could potentially raise. > Hint: Did you know you can run an interactive code analysis in your project by running the command `> ReScript: Start Code Analyzer`? Try it!|}; ] ); ( "genType", + None, [ {|The @genType decorator may be used to export ReScript values and types to JavaScript, and import JavaScript values and types into ReScript. It allows seamless integration of compiled ReScript modules in existing TypeScript, Flow, or plain JavaScript codebases, without loosing type information across different type systems. [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#gentype-decorator).|}; ] ); ( "genType.as", + Some "genType.as(\"$0\")", [ {|The @genType decorator may be used to export ReScript values and types to JavaScript, and import JavaScript values and types into ReScript. It allows seamless integration of compiled ReScript modules in existing TypeScript, Flow, or plain JavaScript codebases, without loosing type information across different type systems. [Read more and see examples in the documentation](https://rescript-lang.org/docs/gentype/latest/usage).|}; ] ); ( "genType.import", + None, [ {|The @genType decorator may be used to export ReScript values and types to JavaScript, and import JavaScript values and types into ReScript. It allows seamless integration of compiled ReScript modules in existing TypeScript, Flow, or plain JavaScript codebases, without loosing type information across different type systems. [Read more and see examples in the documentation](https://rescript-lang.org/docs/gentype/latest/usage).|}; ] ); ( "genType.opaque", + None, [ {|The @genType decorator may be used to export ReScript values and types to JavaScript, and import JavaScript values and types into ReScript. It allows seamless integration of compiled ReScript modules in existing TypeScript, Flow, or plain JavaScript codebases, without loosing type information across different type systems. [Read more and see examples in the documentation](https://rescript-lang.org/docs/gentype/latest/usage).|}; ] ); ( "get", + None, [ {|The `@get` decorator is used to bind to a property of an object. [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#get-decorator).|}; ] ); ( "get_index", + None, [ {|The `@get_index` decorator is used to access a dynamic property on an object, or an index of an array. [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#get-index-decorator).|}; ] ); ( "inline", + None, [ {|The `@inline` decorator tells the compiler to inline its value in every place the binding is being used, rather than use a variable. [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#inline-decorator).|}; ] ); ( "int", + None, [ {|The `@int` decorator can be used with polymorphic variants and the @as decorator on externals to modify the compiled JavaScript to use integers for the values instead of strings. [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#int-decorator).|}; ] ); ( "live", + None, [ {|The `@live` decorator is for reanalyze, a static analysis tool for ReScript that can do dead code analysis. @@ -104,18 +118,21 @@ could potentially raise. Hint: Did you know you can run an interactive code analysis in your project by running the command `> ReScript: Start Code Analyzer`? Try it!|}; ] ); ( "meth", + None, [ {|The `@meth` decorator is used to call a function on a JavaScript object, and avoid issues with currying. [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#meth-decorator).|}; ] ); ( "module", + Some "module(\"$0\")", [ {|The `@module` decorator is used to bind to a JavaScript module. [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#module-decorator).|}; ] ); ( "new", + None, [ {| The `@new` decorator is used whenever you need to bind to a JavaScript class constructor that requires the new keword for instantiation.| @@ -123,12 +140,14 @@ The `@new` decorator is used whenever you need to bind to a JavaScript class con [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#new-decorator).|}; ] ); ( "obj", + None, [ {|The `@obj` decorator is used to create functions that return JavaScript objects with properties that match the function's parameter labels. [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#obj-decorator).|}; ] ); ( "raises", + Some "raises(\"$0\")", [ {|The `@raises` decorator is for reanalyze, a static analysis tool for ReScript that can perform exception analysis. @@ -139,6 +158,7 @@ Example `@raises(Exn)` or `@raises([E1, E2, E3])` for multiple exceptions. > Hint: Did you know you can run an interactive code analysis in your project by running the command `> ReScript: Start Code Analyzer`? Try it!|}; ] ); ( "react.component", + None, [ {|The `@react.component` decorator is used to annotate functions that are RescriptReact components. @@ -149,72 +169,84 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#react-component-decorator).|}; ] ); ( "return", + Some "return(${1:nullable})", [ {|The `@return` decorator is used to control how `null` and `undefined` values are converted to option types in ReScript. [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#return-decorator).|}; ] ); ( "scope", + Some "scope(\"$0\")", [ {|The `@scope` decorator is used with other decorators such as `@val` and `@module` to declare a parent scope for the binding. [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#scope-decorator).|}; ] ); ( "send", + None, [ {|The `@send` decorator is used to bind to a method on an object or array. [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#send-decorator).|}; ] ); ( "set", + None, [ {|The `@set` decorator is used to set a property of an object. [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#set-decorator).|}; ] ); ( "set_index", + None, [ {|The `@set_index` decorator is used to set a dynamic property on an object, or an index of an array. [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#set-index-decorator).|}; ] ); ( "string", + None, [ {|The `@string` decorator can be used with polymorphic variants and the `@as` decorator on externals to modify the string values used for the variants in the compiled JavaScript. [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#string-decorator).|}; ] ); ( "this", + None, [ {|The `@this` decorator may be used to bind to an external callback function that require access to a this context. [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#this-decorator).|}; ] ); ( "unboxed", + None, [ {|The `@unboxed` decorator provides a way to unwrap variant constructors that have a single argument, or record objects that have a single field. [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#unboxed-decorator).|}; ] ); ( "uncurry", + None, [ {|The `@uncurry` decorator can be used to mark any callback argument within an external function as an uncurried function without the need for any explicit uncurried function syntax (`(.) => { ... }`). [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#uncurry-decorator).|}; ] ); ( "unwrap", + None, [ {|The `@unwrap` decorator may be used when binding to external functions that accept multiple types for an argument. [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#unwrap-decorator).|}; ] ); ( "val", + None, [ {|The `@val` decorator allows you to bind to JavaScript values that are on the global scope. [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#val-decorator).|}; ] ); ( "variadic", + None, [ {|The `@variadic` decorator is used to model JavaScript functions that take a variable number of arguments, where all arguments are of the same type. @@ -225,22 +257,32 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i let toplevel = [ ( "deprecated", + Some "deprecated(\"$0\")", [ {|The `@@deprecated` decorator is used to add a deprecation note to the file-level of a module. The compiler and editor tooling will yield a warning whenever a deprecated file module is being used. [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#module-deprecated-decorator).|}; ] ); ( "directive", + Some "directive(\"$0\")", [ {|The `@@directive` decorator will output that string verbatim at the very top of the generated JavaScript file, before any imports. [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#directive-decorator).|}; ] ); ( "warning", + Some "warning(\"$0\")", [ {|The `@@warning` decorator is used to modify the enabled compiler warnings for the current module. See here for all available warning numbers. [Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#module-warning-decorator). |}; ] ); + ( "jsxConfig", + Some "jsxConfig({$0})", + [ + {|The `@@jsxConfig` decorator is used to change the config for JSX on the fly. + +[Read more and see examples in the documentation](https://rescript-lang.org/docs/manual/latest/jsx#file-level-configuration).|}; + ] ); ] diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 053787576..762e6d5a6 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -770,60 +770,79 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor let attribute (iterator : Ast_iterator.iterator) ((id, payload) : Parsetree.attribute) = (if String.length id.txt >= 4 && String.sub id.txt 0 4 = "res." then - (* skip: internal parser attribute *) () - else if id.loc.loc_ghost then () - else if id.loc |> Loc.hasPos ~pos:posBeforeCursor then - let posStart, posEnd = Loc.range id.loc in - match - (Pos.positionToOffset text posStart, Pos.positionToOffset text posEnd) - with - | Some offsetStart, Some offsetEnd -> - (* Can't trust the parser's location - E.g. @foo. let x... gives as label @foo.let *) - let label = - let rawLabel = - String.sub text offsetStart (offsetEnd - offsetStart) - in - let ( ++ ) x y = - match (x, y) with - | Some i1, Some i2 -> Some (min i1 i2) - | Some _, None -> x - | None, _ -> y - in - let label = - match - String.index_opt rawLabel ' ' - ++ String.index_opt rawLabel '\t' - ++ String.index_opt rawLabel '\r' - ++ String.index_opt rawLabel '\n' - with - | None -> rawLabel - | Some i -> String.sub rawLabel 0 i - in - if label <> "" && label.[0] = '@' then - String.sub label 1 (String.length label - 1) - else label - in - found := true; - if debug then - Printf.printf "Attribute id:%s:%s label:%s\n" id.txt - (Loc.toString id.loc) label; - setResult (Completable.Cdecorator label) - | _ -> () - else if id.txt = "module" then - (match payload with - | PStr - [ - { - pstr_desc = - Pstr_eval - ( {pexp_loc; pexp_desc = Pexp_constant (Pconst_string (s, _))}, - _ ); - }; - ] - when locHasCursor pexp_loc -> - setResult (Completable.CdecoratorPayload (Module s)) - | _ -> ())); + (* skip: internal parser attribute *) () + else if id.loc.loc_ghost then () + else if id.loc |> Loc.hasPos ~pos:posBeforeCursor then + let posStart, posEnd = Loc.range id.loc in + match + (Pos.positionToOffset text posStart, Pos.positionToOffset text posEnd) + with + | Some offsetStart, Some offsetEnd -> + (* Can't trust the parser's location + E.g. @foo. let x... gives as label @foo.let *) + let label = + let rawLabel = + String.sub text offsetStart (offsetEnd - offsetStart) + in + let ( ++ ) x y = + match (x, y) with + | Some i1, Some i2 -> Some (min i1 i2) + | Some _, None -> x + | None, _ -> y + in + let label = + match + String.index_opt rawLabel ' ' + ++ String.index_opt rawLabel '\t' + ++ String.index_opt rawLabel '\r' + ++ String.index_opt rawLabel '\n' + with + | None -> rawLabel + | Some i -> String.sub rawLabel 0 i + in + if label <> "" && label.[0] = '@' then + String.sub label 1 (String.length label - 1) + else label + in + found := true; + if debug then + Printf.printf "Attribute id:%s:%s label:%s\n" id.txt + (Loc.toString id.loc) label; + setResult (Completable.Cdecorator label) + | _ -> () + else if id.txt = "module" then + match payload with + | PStr + [ + { + pstr_desc = + Pstr_eval + ( {pexp_loc; pexp_desc = Pexp_constant (Pconst_string (s, _))}, + _ ); + }; + ] + when locHasCursor pexp_loc -> + if Debug.verbose () then + print_endline "[decoratorCompletion] Found @module"; + setResult (Completable.CdecoratorPayload (Module s)) + | _ -> () + else if id.txt = "jsxConfig" then + match payload with + | PStr [{pstr_desc = Pstr_eval (expr, _)}] -> ( + if Debug.verbose () then + print_endline "[decoratorCompletion] Found @jsxConfig"; + match + CompletionExpressions.traverseExpr expr ~exprPath:[] + ~pos:posBeforeCursor ~firstCharBeforeCursorNoWhite + with + | None -> () + | Some (prefix, nested) -> + if Debug.verbose () then + print_endline "[decoratorCompletion] Found @jsxConfig path!"; + setResult + (Completable.CdecoratorPayload + (JsxConfig {nested = List.rev nested; prefix}))) + | _ -> ()); Ast_iterator.default_iterator.attribute iterator (id, payload) in let rec iterateFnArguments ~args ~iterator ~isPipe diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index be8ac0a6c..def2d02ab 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -628,7 +628,9 @@ module Completable = struct type patternMode = Default | Destructuring - type decoratorPayload = Module of string + type decoratorPayload = + | Module of string + | JsxConfig of {nested: nestedPath list; prefix: string} type t = | Cdecorator of string (** e.g. @module *) @@ -715,6 +717,7 @@ module Completable = struct | Cpath cp -> "Cpath " ^ contextPathToString cp | Cdecorator s -> "Cdecorator(" ^ str s ^ ")" | CdecoratorPayload (Module s) -> "CdecoratorPayload(module=" ^ s ^ ")" + | CdecoratorPayload (JsxConfig _) -> "JsxConfig" | CnamedArg (cp, s, sl2) -> "CnamedArg(" ^ (cp |> contextPathToString) diff --git a/analysis/tests/src/CompletionAttributes.res b/analysis/tests/src/CompletionAttributes.res index caba8c300..693d714ea 100644 --- a/analysis/tests/src/CompletionAttributes.res +++ b/analysis/tests/src/CompletionAttributes.res @@ -1,3 +1,21 @@ +// @modu +// ^com + // @module("") external doStuff: t = "test" // ^com +// @@js +// ^com + +// @@jsxConfig({}) +// ^com + +// @@jsxConfig({m}) +// ^com + +// @@jsxConfig({module_: }) +// ^com + +// @@jsxConfig({module_: "", }) +// ^com + diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 5321663b8..3add27f62 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -553,7 +553,8 @@ Resolved opens 1 pervasives "kind": 4, "tags": [], "detail": "", - "documentation": {"kind": "markdown", "value": "The `@react.component` decorator is used to annotate functions that are RescriptReact components.\n\nYou will need this decorator whenever you want to use a ReScript / React component in ReScript JSX expressions.\n\nNote: The `@react.component` decorator requires the react-jsx config to be set in your `bsconfig.json` to enable the required React transformations.\n\n[Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#react-component-decorator)."} + "documentation": {"kind": "markdown", "value": "The `@react.component` decorator is used to annotate functions that are RescriptReact components.\n\nYou will need this decorator whenever you want to use a ReScript / React component in ReScript JSX expressions.\n\nNote: The `@react.component` decorator requires the react-jsx config to be set in your `bsconfig.json` to enable the required React transformations.\n\n[Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#react-component-decorator)."}, + "insertTextFormat": 2 }] Complete src/Completion.res 68:10 @@ -568,7 +569,8 @@ Resolved opens 1 pervasives "kind": 4, "tags": [], "detail": "", - "documentation": {"kind": "markdown", "value": "The `@react.component` decorator is used to annotate functions that are RescriptReact components.\n\nYou will need this decorator whenever you want to use a ReScript / React component in ReScript JSX expressions.\n\nNote: The `@react.component` decorator requires the react-jsx config to be set in your `bsconfig.json` to enable the required React transformations.\n\n[Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#react-component-decorator)."} + "documentation": {"kind": "markdown", "value": "The `@react.component` decorator is used to annotate functions that are RescriptReact components.\n\nYou will need this decorator whenever you want to use a ReScript / React component in ReScript JSX expressions.\n\nNote: The `@react.component` decorator requires the react-jsx config to be set in your `bsconfig.json` to enable the required React transformations.\n\n[Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#react-component-decorator)."}, + "insertTextFormat": 2 }] Complete src/Completion.res 71:27 diff --git a/analysis/tests/src/expected/CompletionAttributes.res.txt b/analysis/tests/src/expected/CompletionAttributes.res.txt index 598f77f84..cc946710c 100644 --- a/analysis/tests/src/expected/CompletionAttributes.res.txt +++ b/analysis/tests/src/expected/CompletionAttributes.res.txt @@ -1,4 +1,19 @@ -Complete src/CompletionAttributes.res 0:12 +Complete src/CompletionAttributes.res 0:8 +Attribute id:modu:[0:3->0:8] label:modu +Completable: Cdecorator(modu) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +[{ + "label": "module", + "kind": 4, + "tags": [], + "detail": "", + "documentation": {"kind": "markdown", "value": "The `@module` decorator is used to bind to a JavaScript module.\n\n[Read more and see examples in the documentation](https://rescript-lang.org/syntax-lookup#module-decorator)."}, + "insertText": "module(\"$0\")", + "insertTextFormat": 2 + }] + +Complete src/CompletionAttributes.res 3:12 XXX Not found! Completable: CdecoratorPayload(module=) Package opens Pervasives.JsxModules.place holder @@ -17,3 +32,97 @@ Resolved opens 1 pervasives "documentation": null }] +Complete src/CompletionAttributes.res 6:7 +Attribute id:js:[6:3->6:7] label:@js +Completable: Cdecorator(@js) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +[{ + "label": "jsxConfig", + "kind": 4, + "tags": [], + "detail": "", + "documentation": {"kind": "markdown", "value": "The `@@jsxConfig` decorator is used to change the config for JSX on the fly.\n\n[Read more and see examples in the documentation](https://rescript-lang.org/docs/manual/latest/jsx#file-level-configuration)."}, + "insertText": "jsxConfig({$0})", + "insertTextFormat": 2 + }] + +Complete src/CompletionAttributes.res 9:16 +XXX Not found! +Completable: JsxConfig +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +[{ + "label": "version", + "kind": 5, + "tags": [], + "detail": "version?: int\n\ntype jsxConfig = {version: int, module_: string, mode: string}", + "documentation": null + }, { + "label": "module_", + "kind": 5, + "tags": [], + "detail": "module_?: string\n\ntype jsxConfig = {version: int, module_: string, mode: string}", + "documentation": null + }, { + "label": "mode", + "kind": 5, + "tags": [], + "detail": "mode?: string\n\ntype jsxConfig = {version: int, module_: string, mode: string}", + "documentation": null + }] + +Complete src/CompletionAttributes.res 12:17 +XXX Not found! +Completable: JsxConfig +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +[{ + "label": "module_", + "kind": 5, + "tags": [], + "detail": "module_?: string\n\ntype jsxConfig = {version: int, module_: string, mode: string}", + "documentation": null + }, { + "label": "mode", + "kind": 5, + "tags": [], + "detail": "mode?: string\n\ntype jsxConfig = {version: int, module_: string, mode: string}", + "documentation": null + }] + +Complete src/CompletionAttributes.res 15:25 +XXX Not found! +Completable: JsxConfig +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +[{ + "label": "\"\"", + "kind": 12, + "tags": [], + "detail": "string", + "documentation": null, + "sortText": "A", + "insertText": "\"$0\"", + "insertTextFormat": 2 + }] + +Complete src/CompletionAttributes.res 18:29 +XXX Not found! +Completable: JsxConfig +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +[{ + "label": "version", + "kind": 5, + "tags": [], + "detail": "version?: int\n\ntype jsxConfig = {version: int, module_: string, mode: string}", + "documentation": null + }, { + "label": "mode", + "kind": 5, + "tags": [], + "detail": "mode?: string\n\ntype jsxConfig = {version: int, module_: string, mode: string}", + "documentation": null + }] +