Skip to content
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

typed expression completion for lowercase JSX tags #702

Merged
merged 3 commits into from
Jan 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
- Let `_` trigger completion in patterns. https://github.com/rescript-lang/rescript-vscode/pull/692
- Support inline records in completion. https://github.com/rescript-lang/rescript-vscode/pull/695
- Add way to autocomplete an exhaustive switch statement for identifiers. Example: an identifier that's a variant can have a switch autoinserted matching all variant cases. https://github.com/rescript-lang/rescript-vscode/pull/699
- Support typed expression completion for lowercase (builtin) JSX tags. https://github.com/rescript-lang/rescript-vscode/pull/702

#### :nail_care: Polish

Expand Down
57 changes: 46 additions & 11 deletions analysis/src/CompletionBackEnd.ml
Original file line number Diff line number Diff line change
Expand Up @@ -756,18 +756,12 @@ let rec getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env
| Lazy -> ["Lazy"]
| Char -> ["Char"])
| TypExpr t -> (
let rec expandPath (path : Path.t) =
match path with
| Pident id -> [Ident.name id]
| Pdot (p, s, _) -> s :: expandPath p
| Papply _ -> []
in
match t.Types.desc with
| Tconstr (path, _typeArgs, _)
| Tlink {desc = Tconstr (path, _typeArgs, _)}
| Tsubst {desc = Tconstr (path, _typeArgs, _)}
| Tpoly ({desc = Tconstr (path, _typeArgs, _)}, []) -> (
match expandPath path with
match Utils.expandPath path with
| _ :: pathRev ->
(* type path is relative to the completion environment
express it from the root of the file *)
Expand Down Expand Up @@ -882,10 +876,36 @@ let rec getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env
~opens ~allFiles ~pos ~env ~scope
|> completionsGetTypeEnv
in
let lowercaseComponent =
match pathToComponent with
| [elName] when Char.lowercase_ascii elName.[0] = elName.[0] -> true
| _ -> false
in
let targetLabel =
CompletionJsx.getJsxLabels ~componentPath:pathToComponent ~findTypeOfValue
~package
|> List.find_opt (fun (label, _, _) -> label = propName)
if lowercaseComponent then
let rec digToTypeForCompletion path ~env =
match
path
|> getCompletionsForPath ~completionContext:Type ~exact:true
~package ~opens ~allFiles ~pos ~env ~scope
with
| {kind = Type {kind = Abstract (Some (p, _))}; env} :: _ ->
(* This case happens when what we're looking for is a type alias.
This is the case in newer rescript-react versions where
ReactDOM.domProps is an alias for JsxEvent.t. *)
let pathRev = p |> Utils.expandPath in
pathRev |> List.rev |> digToTypeForCompletion ~env
| {kind = Type {kind = Record fields}; env} :: _ -> (
match fields |> List.find_opt (fun f -> f.fname.txt = propName) with
| None -> None
| Some f -> Some (f.fname.txt, f.typ, env))
| _ -> None
in
["ReactDOM"; "domProps"] |> digToTypeForCompletion ~env
else
CompletionJsx.getJsxLabels ~componentPath:pathToComponent
~findTypeOfValue ~package
|> List.find_opt (fun (label, _, _) -> label = propName)
in
match targetLabel with
| None -> []
Expand Down Expand Up @@ -1132,7 +1152,7 @@ let rec completeTypedValue (t : SharedTypes.completionType) ~env ~full ~prefix
(* Pretty print a few common patterns. *)
match Path.head p |> Ident.name with
| "unit" -> "()"
| "ReactEvent" -> "event"
| "ReactEvent" | "JsxEvent" -> "event"
| _ -> "v" ^ indexText)
in
let mkFnArgs ~asSnippet =
Expand Down Expand Up @@ -1316,10 +1336,25 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover
with
| None -> []
| Some (typ, env, completionContext) -> (
let isJsx =
match contextPath with
| CJsxPropValue _ | CPPipe {inJsx = true} -> true
| _ -> false
in
let items =
typ
|> completeTypedValue ~mode:Expression ~env ~full ~prefix
~completionContext
|> List.map (fun (c : Completion.t) ->
if isJsx then
{
c with
insertText =
(match c.insertText with
| None -> None
| Some text -> Some ("{" ^ text ^ "}"));
}
else c)
in
match (prefix, completionContext) with
| "", _ -> items
Expand Down
6 changes: 6 additions & 0 deletions analysis/src/Utils.ml
Original file line number Diff line number Diff line change
Expand Up @@ -206,3 +206,9 @@ let rangeOfLoc (loc : Location.t) =
let start = loc |> Loc.start |> mkPosition in
let end_ = loc |> Loc.end_ |> mkPosition in
{Protocol.start; end_}

let rec expandPath (path : Path.t) =
match path with
| Pident id -> [Ident.name id]
| Pdot (p, s, _) -> s :: expandPath p
| Papply _ -> []
6 changes: 6 additions & 0 deletions analysis/tests/src/CompletionJsxProps.res
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@
// let _ = <CompletionSupport.TestComponent polyArg=#t
// ^com

// let _ = <div muted= />
// ^com

// let _ = <div onMouseEnter= />
// ^com

51 changes: 42 additions & 9 deletions analysis/tests/src/expected/CompletionJsxProps.res.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Completable: Cexpression CJsxPropValue [CompletionSupport, TestComponent] test=T
"detail": "Two\n\ntype testVariant = One | Two | Three(int)",
"documentation": null,
"sortText": "A Two",
"insertText": "Two",
"insertText": "{Two}",
"insertTextFormat": 2
}, {
"label": "Three(_)",
Expand All @@ -48,7 +48,7 @@ Completable: Cexpression CJsxPropValue [CompletionSupport, TestComponent] test=T
"detail": "Three(int)\n\ntype testVariant = One | Two | Three(int)",
"documentation": null,
"sortText": "A Three(_)",
"insertText": "Three(${1:_})",
"insertText": "{Three(${1:_})}",
"insertTextFormat": 2
}, {
"label": "TableclothMap",
Expand All @@ -74,31 +74,31 @@ Completable: Cexpression CJsxPropValue [CompletionSupport, TestComponent] polyAr
"tags": [],
"detail": "#one\n\n[#one | #three(int, bool) | #two | #two2]",
"documentation": null,
"insertText": "#one",
"insertText": "{#one}",
"insertTextFormat": 2
}, {
"label": "#three(_, _)",
"kind": 4,
"tags": [],
"detail": "#three(int, bool)\n\n[#one | #three(int, bool) | #two | #two2]",
"documentation": null,
"insertText": "#three(${1:_}, ${2:_})",
"insertText": "{#three(${1:_}, ${2:_})}",
"insertTextFormat": 2
}, {
"label": "#two",
"kind": 4,
"tags": [],
"detail": "#two\n\n[#one | #three(int, bool) | #two | #two2]",
"documentation": null,
"insertText": "#two",
"insertText": "{#two}",
"insertTextFormat": 2
}, {
"label": "#two2",
"kind": 4,
"tags": [],
"detail": "#two2\n\n[#one | #three(int, bool) | #two | #two2]",
"documentation": null,
"insertText": "#two2",
"insertText": "{#two2}",
"insertTextFormat": 2
}]

Expand All @@ -112,23 +112,56 @@ Completable: Cexpression CJsxPropValue [CompletionSupport, TestComponent] polyAr
"tags": [],
"detail": "#three(int, bool)\n\n[#one | #three(int, bool) | #two | #two2]",
"documentation": null,
"insertText": "three(${1:_}, ${2:_})",
"insertText": "{three(${1:_}, ${2:_})}",
"insertTextFormat": 2
}, {
"label": "#two",
"kind": 4,
"tags": [],
"detail": "#two\n\n[#one | #three(int, bool) | #two | #two2]",
"documentation": null,
"insertText": "two",
"insertText": "{two}",
"insertTextFormat": 2
}, {
"label": "#two2",
"kind": 4,
"tags": [],
"detail": "#two2\n\n[#one | #three(int, bool) | #two | #two2]",
"documentation": null,
"insertText": "two2",
"insertText": "{two2}",
"insertTextFormat": 2
}]

Complete src/CompletionJsxProps.res 15:22
posCursor:[15:22] posNoWhite:[15:21] Found expr:[15:12->15:25]
JSX <div:[15:12->15:15] muted[15:16->15:21]=...__ghost__[0:-1->0:-1]> _children:15:23
Completable: Cexpression CJsxPropValue [div] muted
[{
"label": "true",
"kind": 4,
"tags": [],
"detail": "bool",
"documentation": null
}, {
"label": "false",
"kind": 4,
"tags": [],
"detail": "bool",
"documentation": null
}]

Complete src/CompletionJsxProps.res 18:29
posCursor:[18:29] posNoWhite:[18:28] Found expr:[18:12->18:32]
JSX <div:[18:12->18:15] onMouseEnter[18:16->18:28]=...__ghost__[0:-1->0:-1]> _children:18:30
Completable: Cexpression CJsxPropValue [div] onMouseEnter
[{
"label": "event => {}",
"kind": 12,
"tags": [],
"detail": "JsxEvent.Mouse.t => unit",
"documentation": null,
"sortText": "A",
"insertText": "{${1:event} => {$0}}",
"insertTextFormat": 2
}]