diff --git a/CHANGELOG.md b/CHANGELOG.md index ab7462c74..a69eef61c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,8 @@ #### :nail_care: Polish -Enhance decorator completion. https://github.com/rescript-lang/rescript-vscode/pull/908 +- Enhance decorator completion. https://github.com/rescript-lang/rescript-vscode/pull/908 +- Completion for import attributes in `@module`. https://github.com/rescript-lang/rescript-vscode/pull/913 ## 1.38.0 diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 5e5de855e..434589448 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1776,6 +1776,49 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable = typ |> completeTypedValue ?typeArgContext ~rawOpens ~mode:Expression ~full ~prefix ~completionContext) + | CdecoratorPayload (ModuleWithImportAttributes {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 importAttributesConfig : completionType = + Trecord + { + env; + definition = `NameOnly "importAttributesConfig"; + fields = [mkField ~name:"type_" ~primitive:"string"]; + } + in + let rootConfig : completionType = + Trecord + { + env; + definition = `NameOnly "moduleConfig"; + fields = + [ + mkField ~name:"from" ~primitive:"string"; + mkField ~name:"with" ~primitive:"string"; + ]; + } + in + let nested, typ = + match nested with + | NFollowRecordField {fieldName = "with"} :: rest -> + (rest, importAttributesConfig) + | _ -> (nested, rootConfig) + 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) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 762e6d5a6..042aa5006 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -825,6 +825,48 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor if Debug.verbose () then print_endline "[decoratorCompletion] Found @module"; setResult (Completable.CdecoratorPayload (Module s)) + | PStr + [ + { + pstr_desc = + Pstr_eval + ( { + pexp_desc = + Pexp_record + ( ( {txt = Lident "from"}, + { + pexp_loc; + pexp_desc = + Pexp_constant (Pconst_string (s, _)); + } ) + :: _, + _ ); + }, + _ ); + }; + ] + when locHasCursor pexp_loc -> + if Debug.verbose () then + print_endline + "[decoratorCompletion] Found @module with import attributes and \ + cursor on \"from\""; + setResult (Completable.CdecoratorPayload (Module s)) + | PStr [{pstr_desc = Pstr_eval (expr, _)}] -> ( + if Debug.verbose () then + print_endline + "[decoratorCompletion] Found @module with non-string payload"; + match + CompletionExpressions.traverseExpr expr ~exprPath:[] + ~pos:posBeforeCursor ~firstCharBeforeCursorNoWhite + with + | None -> () + | Some (prefix, nested) -> + if Debug.verbose () then + print_endline "[decoratorCompletion] Found @module record path"; + setResult + (Completable.CdecoratorPayload + (ModuleWithImportAttributes {nested = List.rev nested; prefix})) + ) | _ -> () else if id.txt = "jsxConfig" then match payload with diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index def2d02ab..3e4bd63d3 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -630,6 +630,7 @@ module Completable = struct type decoratorPayload = | Module of string + | ModuleWithImportAttributes of {nested: nestedPath list; prefix: string} | JsxConfig of {nested: nestedPath list; prefix: string} type t = @@ -717,6 +718,7 @@ module Completable = struct | Cpath cp -> "Cpath " ^ contextPathToString cp | Cdecorator s -> "Cdecorator(" ^ str s ^ ")" | CdecoratorPayload (Module s) -> "CdecoratorPayload(module=" ^ s ^ ")" + | CdecoratorPayload (ModuleWithImportAttributes _) -> "CdecoratorPayload(moduleWithImportAttributes)" | CdecoratorPayload (JsxConfig _) -> "JsxConfig" | CnamedArg (cp, s, sl2) -> "CnamedArg(" diff --git a/analysis/tests/src/CompletionAttributes.res b/analysis/tests/src/CompletionAttributes.res index 693d714ea..cf9e011f0 100644 --- a/analysis/tests/src/CompletionAttributes.res +++ b/analysis/tests/src/CompletionAttributes.res @@ -19,3 +19,15 @@ // @@jsxConfig({module_: "", }) // ^com +// @module({}) external doStuff: t = "default" +// ^com + +// @module({with: }) external doStuff: t = "default" +// ^com + +// @module({with: {}}) external doStuff: t = "default" +// ^com + +// @module({from: "" }) external doStuff: t = "default" +// ^com + diff --git a/analysis/tests/src/expected/CompletionAttributes.res.txt b/analysis/tests/src/expected/CompletionAttributes.res.txt index cc946710c..3c8da2d2e 100644 --- a/analysis/tests/src/expected/CompletionAttributes.res.txt +++ b/analysis/tests/src/expected/CompletionAttributes.res.txt @@ -126,3 +126,70 @@ Resolved opens 1 pervasives "documentation": null }] +Complete src/CompletionAttributes.res 21:12 +XXX Not found! +Completable: CdecoratorPayload(moduleWithImportAttributes) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +[{ + "label": "from", + "kind": 5, + "tags": [], + "detail": "from?: string\n\ntype moduleConfig = {from: string, with: string}", + "documentation": null + }, { + "label": "with", + "kind": 5, + "tags": [], + "detail": "with?: string\n\ntype moduleConfig = {from: string, with: string}", + "documentation": null + }] + +Complete src/CompletionAttributes.res 24:17 +XXX Not found! +Completable: CdecoratorPayload(moduleWithImportAttributes) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +[{ + "label": "{}", + "kind": 12, + "tags": [], + "detail": "type importAttributesConfig = {type_: string}", + "documentation": null, + "sortText": "A", + "insertText": "{$0}", + "insertTextFormat": 2 + }] + +Complete src/CompletionAttributes.res 27:19 +XXX Not found! +Completable: CdecoratorPayload(moduleWithImportAttributes) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +[{ + "label": "type_", + "kind": 5, + "tags": [], + "detail": "type_?: string\n\ntype importAttributesConfig = {type_: string}", + "documentation": null + }] + +Complete src/CompletionAttributes.res 30:19 +XXX Not found! +Completable: CdecoratorPayload(module=) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +[{ + "label": "@rescript/react", + "kind": 4, + "tags": [], + "detail": "Package", + "documentation": null + }, { + "label": "./tst.js", + "kind": 4, + "tags": [], + "detail": "Local file", + "documentation": null + }] +