diff --git a/CHANGELOG.md b/CHANGELOG.md index c3917f4045..1a690a0d3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - Fix async context checking for module await. https://github.com/rescript-lang/rescript/pull/7271 - Fix `%external` extension. https://github.com/rescript-lang/rescript/pull/7272 - Fix issue with type environment for unified ops. https://github.com/rescript-lang/rescript/pull/7277 +- Fix completion for application with tagged template. https://github.com/rescript-lang/rescript/pull/7278 # 12.0.0-alpha.8 diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 4ca35778a8..661af008d3 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -1127,6 +1127,65 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor (* Case foo-> when the parser adds a ghost expression to the rhs so the apply expression does not include the cursor *) if setPipeResult ~lhs ~id:"" then setFound () + (* + A dot completion for a tagged templated application with an expr hole. + Example: + sh`echo "meh"`. + *) + | Pexp_apply + { + funct = {pexp_desc = Pexp_ident {txt = Lident "."; loc = _}}; + args = + [ + (* sh`echo "meh"` *) + (_, ({pexp_desc = Pexp_apply _} as innerExpr)); + (* recovery inserted node *) + (_, {pexp_desc = Pexp_extension ({txt = "rescript.exprhole"}, _)}); + ]; + } + when Res_parsetree_viewer.is_tagged_template_literal innerExpr -> + exprToContextPath innerExpr + |> Option.iter (fun cpath -> + setResult + (Cpath + (CPField + { + contextPath = cpath; + fieldName = ""; + posOfDot; + exprLoc = expr.pexp_loc; + })); + setFound ()) + (* + A dot completion for a tagged templated application with an ident. + Example: + sh`echo "meh"`.foo + *) + | Pexp_apply + { + funct = {pexp_desc = Pexp_ident {txt = Lident "."; loc = _}}; + args = + [ + (* sh`echo "meh"` *) + (_, ({pexp_desc = Pexp_apply _} as innerExpr)); + (* foo *) + (_, {pexp_desc = Pexp_ident {txt = Lident fieldName}}); + ]; + } + when Res_parsetree_viewer.is_tagged_template_literal innerExpr + && expr.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor -> + exprToContextPath innerExpr + |> Option.iter (fun cpath -> + setResult + (Cpath + (CPField + { + contextPath = cpath; + fieldName; + posOfDot; + exprLoc = expr.pexp_loc; + })); + setFound ()) | _ -> ( if expr.pexp_loc |> Loc.hasPos ~pos:posNoWhite && !result = None then ( setFound (); diff --git a/tests/analysis_tests/tests/src/CompletionTaggedTemplate.res b/tests/analysis_tests/tests/src/CompletionTaggedTemplate.res new file mode 100644 index 0000000000..dce1a0cbcd --- /dev/null +++ b/tests/analysis_tests/tests/src/CompletionTaggedTemplate.res @@ -0,0 +1,16 @@ +module M = { + type t + + let a = (_t:t) => 4 + let b = (_:t) => "c" + let xyz = (_:t, p:int) => p + 1 +} + +@module("meh") @taggedTemplate +external meh: (array, array) => M.t = "default" + +// let x = meh`foo`. +// ^com + +// let y = meh`bar`.x +// ^com diff --git a/tests/analysis_tests/tests/src/expected/CompletionTaggedTemplate.res.txt b/tests/analysis_tests/tests/src/expected/CompletionTaggedTemplate.res.txt new file mode 100644 index 0000000000..73f0090148 --- /dev/null +++ b/tests/analysis_tests/tests/src/expected/CompletionTaggedTemplate.res.txt @@ -0,0 +1,80 @@ +Complete src/CompletionTaggedTemplate.res 11:20 +posCursor:[11:20] posNoWhite:[11:19] Found expr:[11:11->0:-1] +Completable: Cpath Value[meh](Nolabel, Nolabel)."" +Package opens Pervasives.JsxModules.place holder +ContextPath Value[meh](Nolabel, Nolabel)."" +ContextPath Value[meh](Nolabel, Nolabel) +ContextPath Value[meh] +Path meh +ContextPath Value[meh](Nolabel, Nolabel, Nolabel)-> +ContextPath Value[meh](Nolabel, Nolabel, Nolabel) +ContextPath Value[meh] +Path meh +CPPipe pathFromEnv:M found:true +Path M. +[{ + "label": "->M.xyz", + "kind": 12, + "tags": [], + "detail": "(t, int) => int", + "documentation": null, + "sortText": "xyz", + "insertText": "->M.xyz", + "additionalTextEdits": [{ + "range": {"start": {"line": 11, "character": 19}, "end": {"line": 11, "character": 20}}, + "newText": "" + }] + }, { + "label": "->M.b", + "kind": 12, + "tags": [], + "detail": "t => string", + "documentation": null, + "sortText": "b", + "insertText": "->M.b", + "additionalTextEdits": [{ + "range": {"start": {"line": 11, "character": 19}, "end": {"line": 11, "character": 20}}, + "newText": "" + }] + }, { + "label": "->M.a", + "kind": 12, + "tags": [], + "detail": "t => int", + "documentation": null, + "sortText": "a", + "insertText": "->M.a", + "additionalTextEdits": [{ + "range": {"start": {"line": 11, "character": 19}, "end": {"line": 11, "character": 20}}, + "newText": "" + }] + }] + +Complete src/CompletionTaggedTemplate.res 14:21 +posCursor:[14:21] posNoWhite:[14:20] Found expr:[14:11->14:21] +Completable: Cpath Value[meh](Nolabel, Nolabel).x +Package opens Pervasives.JsxModules.place holder +ContextPath Value[meh](Nolabel, Nolabel).x +ContextPath Value[meh](Nolabel, Nolabel) +ContextPath Value[meh] +Path meh +ContextPath Value[meh](Nolabel, Nolabel, Nolabel)->x +ContextPath Value[meh](Nolabel, Nolabel, Nolabel) +ContextPath Value[meh] +Path meh +CPPipe pathFromEnv:M found:true +Path M.x +[{ + "label": "->M.xyz", + "kind": 12, + "tags": [], + "detail": "(t, int) => int", + "documentation": null, + "sortText": "xyz", + "insertText": "->M.xyz", + "additionalTextEdits": [{ + "range": {"start": {"line": 14, "character": 19}, "end": {"line": 14, "character": 20}}, + "newText": "" + }] + }] +