Skip to content

Commit 6d4e83e

Browse files
authored
Add jsx_tag_name (#7760)
* Add jsx_tag_name * Clean up parser * Some better union case names * jsx_tag_name loc * Use info from jsx_tag_name to determine upper or lower. * Clean up * Parser clean up * Refactor read_jsx_tag_name * Add value to JsxInvalid * Update snapshot * Move helper out of loop * Add changelog entry
1 parent 697ccf8 commit 6d4e83e

File tree

21 files changed

+546
-296
lines changed

21 files changed

+546
-296
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727
#### :house: Internal
2828

29+
- AST: Use jsx_tag_name instead of Longindent.t to store jsx tag name. https://github.com/rescript-lang/rescript/pull/7760
30+
2931
# 12.0.0-beta.5
3032

3133
#### :bug: Bug fix

analysis/src/CompletionFrontEnd.ml

Lines changed: 79 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1341,8 +1341,13 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
13411341
{
13421342
jsx_container_element_tag_name_start = compName;
13431343
jsx_container_element_props = props;
1344-
} ) ->
1344+
} ) -> (
13451345
inJsxContext := true;
1346+
let is_valid_tag_for_props =
1347+
match compName.txt with
1348+
| Parsetree.JsxTagInvalid _ -> false
1349+
| _ -> true
1350+
in
13461351
let children =
13471352
match expr.pexp_desc with
13481353
| Pexp_jsx_element
@@ -1351,43 +1356,81 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
13511356
children
13521357
| _ -> JSXChildrenItems []
13531358
in
1354-
let jsxProps =
1355-
CompletionJsx.extractJsxProps ~compName ~props ~children
1359+
let compName_loc = compName.loc in
1360+
let compName_lid =
1361+
Ast_helper.Jsx.longident_of_jsx_tag_name compName.txt
13561362
in
1357-
let compNamePath = flattenLidCheckDot ~jsx:true compName in
1358-
if debug then
1359-
Printf.printf "JSX <%s:%s %s> _children:%s\n"
1360-
(compNamePath |> String.concat ".")
1361-
(Loc.toString compName.loc)
1362-
(jsxProps.props
1363-
|> List.map
1364-
(fun ({name; posStart; posEnd; exp} : CompletionJsx.prop) ->
1365-
Printf.sprintf "%s[%s->%s]=...%s" name
1366-
(Pos.toString posStart) (Pos.toString posEnd)
1367-
(Loc.toString exp.pexp_loc))
1368-
|> String.concat " ")
1369-
(match jsxProps.childrenStart with
1370-
| None -> "None"
1371-
| Some childrenPosStart -> Pos.toString childrenPosStart);
1363+
let jsxPropsOpt =
1364+
if is_valid_tag_for_props then
1365+
Some
1366+
(CompletionJsx.extractJsxProps
1367+
~compName:(Location.mkloc compName_lid compName_loc)
1368+
~props ~children)
1369+
else None
1370+
in
1371+
let compNamePath =
1372+
flattenLidCheckDot ~jsx:true
1373+
{txt = compName_lid; loc = compName_loc}
1374+
in
1375+
(if debug then
1376+
match jsxPropsOpt with
1377+
| Some jsxProps ->
1378+
Printf.printf "JSX <%s:%s %s> _children:%s\n"
1379+
(compNamePath |> String.concat ".")
1380+
(Loc.toString compName_loc)
1381+
(jsxProps.props
1382+
|> List.map
1383+
(fun
1384+
({name; posStart; posEnd; exp} : CompletionJsx.prop) ->
1385+
Printf.sprintf "%s[%s->%s]=...%s" name
1386+
(Pos.toString posStart) (Pos.toString posEnd)
1387+
(Loc.toString exp.pexp_loc))
1388+
|> String.concat " ")
1389+
(match jsxProps.childrenStart with
1390+
| None -> "None"
1391+
| Some childrenPosStart -> Pos.toString childrenPosStart)
1392+
| None ->
1393+
Printf.printf "JSX <%s:%s > _children:None\n"
1394+
(compNamePath |> String.concat ".")
1395+
(Loc.toString compName_loc));
1396+
(* If the tag name is an uppercase path and the cursor is right after a dot (e.g., <O.|),
1397+
prefer module member completion over JSX prop suggestions. *)
1398+
(match compName.txt with
1399+
| Parsetree.JsxUpperTag _ when blankAfterCursor = Some '.' ->
1400+
setResult
1401+
(Cpath
1402+
(CPId
1403+
{
1404+
loc = compName_loc;
1405+
path = compNamePath;
1406+
completionContext = Module;
1407+
}))
1408+
| _ -> ());
13721409
let jsxCompletable =
1373-
match expr.pexp_desc with
1374-
| Pexp_jsx_element
1375-
(Jsx_container_element
1376-
{
1377-
jsx_container_element_closing_tag = None;
1378-
jsx_container_element_children =
1379-
JSXChildrenSpreading _ | JSXChildrenItems (_ :: _);
1380-
}) ->
1381-
(* This is a weird edge case where there is no closing tag but there are children *)
1410+
match (jsxPropsOpt, expr.pexp_desc) with
1411+
| ( Some _,
1412+
Pexp_jsx_element
1413+
(Jsx_container_element
1414+
{
1415+
jsx_container_element_closing_tag = None;
1416+
jsx_container_element_children =
1417+
JSXChildrenSpreading _ | JSXChildrenItems (_ :: _);
1418+
}) ) ->
13821419
None
1383-
| _ ->
1420+
| Some jsxProps, _ ->
13841421
CompletionJsx.findJsxPropsCompletable ~jsxProps
13851422
~endPos:(Loc.end_ expr.pexp_loc) ~posBeforeCursor
1386-
~posAfterCompName:(Loc.end_ compName.loc)
1423+
~posAfterCompName:(Loc.end_ compName_loc)
13871424
~firstCharBeforeCursorNoWhite ~charAtCursor
1425+
| None, _ -> None
13881426
in
1389-
if jsxCompletable <> None then setResultOpt jsxCompletable
1390-
else if compName.loc |> Loc.hasPos ~pos:posBeforeCursor then
1427+
(match jsxCompletable with
1428+
| Some _ as res -> setResultOpt res
1429+
| None -> ());
1430+
if
1431+
jsxCompletable = None
1432+
&& compName_loc |> Loc.hasPos ~pos:posBeforeCursor
1433+
then
13911434
setResult
13921435
(match compNamePath with
13931436
| [prefix] when Char.lowercase_ascii prefix.[0] = prefix.[0] ->
@@ -1396,11 +1439,14 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
13961439
Cpath
13971440
(CPId
13981441
{
1399-
loc = compName.loc;
1442+
loc = compName_loc;
14001443
path = compNamePath;
14011444
completionContext = Module;
14021445
}))
1403-
else iterateJsxProps ~iterator jsxProps
1446+
else
1447+
match jsxPropsOpt with
1448+
| Some jsxProps -> iterateJsxProps ~iterator jsxProps
1449+
| None -> ())
14041450
| Pexp_apply
14051451
{
14061452
funct = {pexp_desc = Pexp_ident {txt = Lident "->"}};

analysis/src/SemanticTokens.ml

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,9 @@ let command ~debug ~emitter ~path =
265265
*)
266266
emitter (* --> <div... *)
267267
|> emitJsxTag ~debug ~name:"<" ~pos:(Loc.start e.pexp_loc);
268-
emitter |> emitJsxOpen ~lid:lident.txt ~debug ~loc:lident.loc;
268+
let lid = Ast_helper.Jsx.longident_of_jsx_tag_name lident.txt in
269+
let loc = lident.loc in
270+
emitter |> emitJsxOpen ~lid ~debug ~loc;
269271
let closing_line, closing_column = Loc.end_ e.pexp_loc in
270272
emitter (* <foo ...props /> <-- *)
271273
|> emitJsxTag ~debug ~name:"/>" ~pos:(closing_line, closing_column - 2)
@@ -281,7 +283,9 @@ let command ~debug ~emitter ~path =
281283
(* opening tag *)
282284
emitter (* --> <div... *)
283285
|> emitJsxTag ~debug ~name:"<" ~pos:(Loc.start e.pexp_loc);
284-
emitter |> emitJsxOpen ~lid:lident.txt ~debug ~loc:lident.loc;
286+
let lid = Ast_helper.Jsx.longident_of_jsx_tag_name lident.txt in
287+
let loc = lident.loc in
288+
emitter |> emitJsxOpen ~lid ~debug ~loc;
285289
emitter (* <foo ...props > <-- *)
286290
|> emitJsxTag ~debug ~name:">"
287291
~pos:(Pos.ofLexing posOfGreatherthanAfterProps);
@@ -308,9 +312,11 @@ let command ~debug ~emitter ~path =
308312
emitter
309313
|> emitJsxTag ~debug ~name:"</"
310314
~pos:(Pos.ofLexing closing_less_than);
311-
emitter
312-
|> emitJsxClose ~debug ~lid:lident.txt
313-
~pos:(Loc.start tag_name_end.loc);
315+
let lid =
316+
Ast_helper.Jsx.longident_of_jsx_tag_name tag_name_end.txt
317+
in
318+
let loc = tag_name_end.loc in
319+
emitter |> emitJsxClose ~debug ~lid ~pos:(Loc.end_ loc);
314320
emitter (* <foo> ... </foo> <-- *)
315321
|> emitJsxTag ~debug ~name:">"
316322
~pos:(Pos.ofLexing final_greather_than))

compiler/ml/ast_helper.ml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,3 +428,21 @@ module Te = struct
428428
pext_attributes = attrs;
429429
}
430430
end
431+
432+
module Jsx = struct
433+
let string_of_jsx_tag_name (tag_name : Parsetree.jsx_tag_name) : string =
434+
match tag_name with
435+
| Parsetree.JsxLowerTag name -> name
436+
| Parsetree.JsxQualifiedLowerTag {path; name} ->
437+
String.concat "." (Longident.flatten path) ^ "." ^ name
438+
| Parsetree.JsxUpperTag path -> String.concat "." (Longident.flatten path)
439+
| Parsetree.JsxTagInvalid name -> name
440+
441+
let longident_of_jsx_tag_name (tag_name : Parsetree.jsx_tag_name) :
442+
Longident.t =
443+
match tag_name with
444+
| Parsetree.JsxLowerTag name -> Longident.Lident name
445+
| Parsetree.JsxQualifiedLowerTag {path; name} -> Longident.Ldot (path, name)
446+
| Parsetree.JsxUpperTag path -> path
447+
| Parsetree.JsxTagInvalid name -> Longident.Lident name
448+
end

compiler/ml/ast_helper.mli

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,13 +212,13 @@ module Exp : sig
212212
val jsx_unary_element :
213213
?loc:loc ->
214214
?attrs:attrs ->
215-
Longident.t Location.loc ->
215+
Parsetree.jsx_tag_name Location.loc ->
216216
Parsetree.jsx_props ->
217217
expression
218218
val jsx_container_element :
219219
?loc:loc ->
220220
?attrs:attrs ->
221-
Longident.t Location.loc ->
221+
Parsetree.jsx_tag_name Location.loc ->
222222
Parsetree.jsx_props ->
223223
Lexing.position ->
224224
Parsetree.jsx_children ->
@@ -301,6 +301,11 @@ module Te : sig
301301
val rebind : ?loc:loc -> ?attrs:attrs -> str -> lid -> extension_constructor
302302
end
303303

304+
module Jsx : sig
305+
val string_of_jsx_tag_name : Parsetree.jsx_tag_name -> string
306+
val longident_of_jsx_tag_name : Parsetree.jsx_tag_name -> Longident.t
307+
end
308+
304309
(** {1 Module language} *)
305310

306311
(** Module type expressions *)

compiler/ml/ast_mapper_from0.ml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -396,10 +396,23 @@ module E = struct
396396
when has_jsx_attribute () -> (
397397
let attrs = attrs |> List.filter (fun ({txt}, _) -> txt <> "JSX") in
398398
let props, children = extract_props_and_children sub args in
399+
let jsx_tag : Pt.jsx_tag_name =
400+
match tag_name.txt with
401+
| Longident.Lident s
402+
when String.length s > 0 && Char.lowercase_ascii s.[0] = s.[0] ->
403+
Pt.JsxLowerTag s
404+
| Longident.Lident _ -> Pt.JsxUpperTag tag_name.txt
405+
| Longident.Ldot (path, last)
406+
when String.length last > 0
407+
&& Char.lowercase_ascii last.[0] = last.[0] ->
408+
Pt.JsxQualifiedLowerTag {path; name = last}
409+
| _ -> Pt.JsxUpperTag tag_name.txt
410+
in
411+
let jsx_tag_name = {txt = jsx_tag; loc = tag_name.loc} in
399412
match children with
400-
| None -> jsx_unary_element ~loc ~attrs tag_name props
413+
| None -> jsx_unary_element ~loc ~attrs jsx_tag_name props
401414
| Some children ->
402-
jsx_container_element ~loc ~attrs tag_name props Lexing.dummy_pos
415+
jsx_container_element ~loc ~attrs jsx_tag_name props Lexing.dummy_pos
403416
children None)
404417
| Pexp_apply (e, l) ->
405418
let e =

compiler/ml/ast_mapper_to0.ml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,9 @@ module E = struct
495495
jsx_unary_element_tag_name = tag_name;
496496
jsx_unary_element_props = props;
497497
}) ->
498-
let tag_ident = map_loc sub tag_name in
498+
let tag_ident : Longident.t Location.loc =
499+
tag_name |> Location.map_loc Ast_helper.Jsx.longident_of_jsx_tag_name
500+
in
499501
let props = map_jsx_props sub props in
500502
let children_expr =
501503
let loc =
@@ -525,7 +527,9 @@ module E = struct
525527
jsx_container_element_props = props;
526528
jsx_container_element_children = children;
527529
}) ->
528-
let tag_ident = map_loc sub tag_name in
530+
let tag_ident : Longident.t Location.loc =
531+
tag_name |> Location.map_loc Ast_helper.Jsx.longident_of_jsx_tag_name
532+
in
529533
let props = map_jsx_props sub props in
530534
let children_expr = map_jsx_children sub loc children in
531535
apply ~loc ~attrs:(jsx_attr sub :: attrs) (ident tag_ident)

compiler/ml/depend.ml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,10 @@ let rec add_expr bv exp =
294294
(Jsx_unary_element
295295
{jsx_unary_element_tag_name = name; jsx_unary_element_props = props})
296296
->
297-
add bv name;
297+
(* Conservatively add all module path segments referenced by the tag name *)
298+
(match name.txt with
299+
| JsxLowerTag _ | JsxTagInvalid _ -> ()
300+
| JsxQualifiedLowerTag {path; _} | JsxUpperTag path -> add_path bv path);
298301
and_jsx_props bv props
299302
| Pexp_jsx_element
300303
(Jsx_container_element
@@ -303,7 +306,9 @@ let rec add_expr bv exp =
303306
jsx_container_element_props = props;
304307
jsx_container_element_children = children;
305308
}) ->
306-
add bv name;
309+
(match name.txt with
310+
| JsxLowerTag _ | JsxTagInvalid _ -> ()
311+
| JsxQualifiedLowerTag {path; _} | JsxUpperTag path -> add_path bv path);
307312
and_jsx_props bv props;
308313
add_jsx_children bv children
309314

compiler/ml/location.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,3 +300,5 @@ let raise_errorf ?(loc = none) ?(sub = []) ?(if_highlight = "") =
300300

301301
let deprecated ?(def = none) ?(use = none) loc msg =
302302
prerr_warning loc (Warnings.Deprecated (msg, def, use))
303+
304+
let map_loc f {txt; loc} = {txt = f txt; loc}

compiler/ml/location.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,5 @@ val report_exception : formatter -> exn -> unit
131131
(** Reraise the exception if it is unknown. *)
132132

133133
val deprecated : ?def:t -> ?use:t -> t -> string -> unit
134+
135+
val map_loc : ('a -> 'b) -> 'a loc -> 'b loc

0 commit comments

Comments
 (0)