Skip to content

Commit f8d5e36

Browse files
committed
Add jsx_tag_name
1 parent 697ccf8 commit f8d5e36

File tree

19 files changed

+510
-246
lines changed

19 files changed

+510
-246
lines changed

analysis/src/CompletionFrontEnd.ml

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,14 +1351,27 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
13511351
children
13521352
| _ -> JSXChildrenItems []
13531353
in
1354+
let compName_loc =
1355+
match compName with
1356+
| Parsetree.Lower {loc; _}
1357+
| Parsetree.QualifiedLower {loc; _}
1358+
| Parsetree.Upper {loc; _} ->
1359+
loc
1360+
in
1361+
let compName_lid = Ast_helper.longident_of_jsx_tag_name compName in
13541362
let jsxProps =
1355-
CompletionJsx.extractJsxProps ~compName ~props ~children
1363+
CompletionJsx.extractJsxProps
1364+
~compName:(Location.mkloc compName_lid compName_loc)
1365+
~props ~children
1366+
in
1367+
let compNamePath =
1368+
flattenLidCheckDot ~jsx:true
1369+
{txt = compName_lid; loc = compName_loc}
13561370
in
1357-
let compNamePath = flattenLidCheckDot ~jsx:true compName in
13581371
if debug then
13591372
Printf.printf "JSX <%s:%s %s> _children:%s\n"
13601373
(compNamePath |> String.concat ".")
1361-
(Loc.toString compName.loc)
1374+
(Loc.toString compName_loc)
13621375
(jsxProps.props
13631376
|> List.map
13641377
(fun ({name; posStart; posEnd; exp} : CompletionJsx.prop) ->
@@ -1369,6 +1382,19 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
13691382
(match jsxProps.childrenStart with
13701383
| None -> "None"
13711384
| Some childrenPosStart -> Pos.toString childrenPosStart);
1385+
(* If the tag name is an uppercase path and the cursor is right after a dot (e.g., <O.|),
1386+
prefer module member completion over JSX prop suggestions. *)
1387+
(match compName with
1388+
| Parsetree.Upper _ when blankAfterCursor = Some '.' ->
1389+
setResult
1390+
(Cpath
1391+
(CPId
1392+
{
1393+
loc = compName_loc;
1394+
path = compNamePath;
1395+
completionContext = Module;
1396+
}))
1397+
| _ -> ());
13721398
let jsxCompletable =
13731399
match expr.pexp_desc with
13741400
| Pexp_jsx_element
@@ -1383,11 +1409,11 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
13831409
| _ ->
13841410
CompletionJsx.findJsxPropsCompletable ~jsxProps
13851411
~endPos:(Loc.end_ expr.pexp_loc) ~posBeforeCursor
1386-
~posAfterCompName:(Loc.end_ compName.loc)
1412+
~posAfterCompName:(Loc.end_ compName_loc)
13871413
~firstCharBeforeCursorNoWhite ~charAtCursor
13881414
in
13891415
if jsxCompletable <> None then setResultOpt jsxCompletable
1390-
else if compName.loc |> Loc.hasPos ~pos:posBeforeCursor then
1416+
else if compName_loc |> Loc.hasPos ~pos:posBeforeCursor then
13911417
setResult
13921418
(match compNamePath with
13931419
| [prefix] when Char.lowercase_ascii prefix.[0] = prefix.[0] ->
@@ -1396,7 +1422,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
13961422
Cpath
13971423
(CPId
13981424
{
1399-
loc = compName.loc;
1425+
loc = compName_loc;
14001426
path = compNamePath;
14011427
completionContext = Module;
14021428
}))

analysis/src/SemanticTokens.ml

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,15 @@ 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.longident_of_jsx_tag_name lident in
269+
let loc =
270+
match lident with
271+
| Parsetree.Lower {loc; _}
272+
| Parsetree.QualifiedLower {loc; _}
273+
| Parsetree.Upper {loc; _} ->
274+
loc
275+
in
276+
emitter |> emitJsxOpen ~lid ~debug ~loc;
269277
let closing_line, closing_column = Loc.end_ e.pexp_loc in
270278
emitter (* <foo ...props /> <-- *)
271279
|> emitJsxTag ~debug ~name:"/>" ~pos:(closing_line, closing_column - 2)
@@ -281,7 +289,15 @@ let command ~debug ~emitter ~path =
281289
(* opening tag *)
282290
emitter (* --> <div... *)
283291
|> emitJsxTag ~debug ~name:"<" ~pos:(Loc.start e.pexp_loc);
284-
emitter |> emitJsxOpen ~lid:lident.txt ~debug ~loc:lident.loc;
292+
let lid = Ast_helper.longident_of_jsx_tag_name lident in
293+
let loc =
294+
match lident with
295+
| Parsetree.Lower {loc; _}
296+
| Parsetree.QualifiedLower {loc; _}
297+
| Parsetree.Upper {loc; _} ->
298+
loc
299+
in
300+
emitter |> emitJsxOpen ~lid ~debug ~loc;
285301
emitter (* <foo ...props > <-- *)
286302
|> emitJsxTag ~debug ~name:">"
287303
~pos:(Pos.ofLexing posOfGreatherthanAfterProps);
@@ -308,9 +324,15 @@ let command ~debug ~emitter ~path =
308324
emitter
309325
|> emitJsxTag ~debug ~name:"</"
310326
~pos:(Pos.ofLexing closing_less_than);
311-
emitter
312-
|> emitJsxClose ~debug ~lid:lident.txt
313-
~pos:(Loc.start tag_name_end.loc);
327+
let lid = Ast_helper.longident_of_jsx_tag_name tag_name_end in
328+
let loc =
329+
match tag_name_end with
330+
| Parsetree.Lower {loc; _}
331+
| Parsetree.QualifiedLower {loc; _}
332+
| Parsetree.Upper {loc; _} ->
333+
loc
334+
in
335+
emitter |> emitJsxClose ~debug ~lid ~pos:(Loc.end_ loc);
314336
emitter (* <foo> ... </foo> <-- *)
315337
|> emitJsxTag ~debug ~name:">"
316338
~pos:(Pos.ofLexing final_greather_than))

compiler/ml/ast_helper.ml

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

compiler/ml/ast_helper.mli

Lines changed: 5 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 ->
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 ->
222222
Parsetree.jsx_props ->
223223
Lexing.position ->
224224
Parsetree.jsx_children ->
@@ -301,6 +301,9 @@ module Te : sig
301301
val rebind : ?loc:loc -> ?attrs:attrs -> str -> lid -> extension_constructor
302302
end
303303

304+
val string_of_jsx_tag_name : Parsetree.jsx_tag_name -> string
305+
val longident_of_jsx_tag_name : Parsetree.jsx_tag_name -> Longident.t
306+
304307
(** {1 Module language} *)
305308

306309
(** Module type expressions *)

compiler/ml/ast_iterator.ml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -365,18 +365,18 @@ module E = struct
365365
iter_jsx_children sub children
366366
| Pexp_jsx_element
367367
(Jsx_unary_element
368-
{jsx_unary_element_tag_name = name; jsx_unary_element_props = props})
368+
{jsx_unary_element_tag_name = _name; jsx_unary_element_props = props})
369369
->
370-
iter_loc sub name;
370+
(* jsx_tag_name contains a Location.t inside; attributes and visitors can
371+
visit props and children as before *)
371372
iter_jsx_props sub props
372373
| Pexp_jsx_element
373374
(Jsx_container_element
374375
{
375-
jsx_container_element_tag_name_start = name;
376+
jsx_container_element_tag_name_start = _name;
376377
jsx_container_element_props = props;
377378
jsx_container_element_children = children;
378379
}) ->
379-
iter_loc sub name;
380380
iter_jsx_props sub props;
381381
iter_jsx_children sub children
382382
end

compiler/ml/ast_mapper.ml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,8 @@ module E = struct
358358
(Jsx_unary_element
359359
{jsx_unary_element_tag_name = name; jsx_unary_element_props = props})
360360
->
361-
jsx_unary_element ~loc ~attrs (map_loc sub name) (map_jsx_props sub props)
361+
(* pass through jsx_tag_name unchanged; it is not a loc *)
362+
jsx_unary_element ~loc ~attrs name (map_jsx_props sub props)
362363
| Pexp_jsx_element
363364
(Jsx_container_element
364365
{
@@ -368,8 +369,7 @@ module E = struct
368369
jsx_container_element_children = children;
369370
jsx_container_element_closing_tag = closing_tag;
370371
}) ->
371-
jsx_container_element ~loc ~attrs (map_loc sub name)
372-
(map_jsx_props sub props) ote
372+
jsx_container_element ~loc ~attrs name (map_jsx_props sub props) ote
373373
(map_jsx_children sub children)
374374
closing_tag
375375
end

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.Lower {name = s; loc = tag_name.loc}
404+
| Longident.Lident _ ->
405+
Pt.Upper {path = tag_name.txt; loc = tag_name.loc}
406+
| Longident.Ldot (path, last)
407+
when String.length last > 0
408+
&& Char.lowercase_ascii last.[0] = last.[0] ->
409+
Pt.QualifiedLower {path; name = last; loc = tag_name.loc}
410+
| _ -> Pt.Upper {path = tag_name.txt; loc = tag_name.loc}
411+
in
399412
match children with
400-
| None -> jsx_unary_element ~loc ~attrs tag_name props
413+
| None -> jsx_unary_element ~loc ~attrs jsx_tag props
401414
| Some children ->
402-
jsx_container_element ~loc ~attrs tag_name props Lexing.dummy_pos
415+
jsx_container_element ~loc ~attrs jsx_tag props Lexing.dummy_pos
403416
children None)
404417
| Pexp_apply (e, l) ->
405418
let e =

compiler/ml/ast_mapper_to0.ml

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,16 @@ 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+
let txt, loc =
500+
match tag_name with
501+
| Lower {name; loc} -> (Longident.Lident name, loc)
502+
| QualifiedLower {path; name; loc} ->
503+
(Longident.Ldot (path, name), loc)
504+
| Upper {path; loc} -> (path, loc)
505+
in
506+
{txt; loc}
507+
in
499508
let props = map_jsx_props sub props in
500509
let children_expr =
501510
let loc =
@@ -525,7 +534,16 @@ module E = struct
525534
jsx_container_element_props = props;
526535
jsx_container_element_children = children;
527536
}) ->
528-
let tag_ident = map_loc sub tag_name in
537+
let tag_ident : Longident.t Location.loc =
538+
let txt, loc =
539+
match tag_name with
540+
| Lower {name; loc} -> (Longident.Lident name, loc)
541+
| QualifiedLower {path; name; loc} ->
542+
(Longident.Ldot (path, name), loc)
543+
| Upper {path; loc} -> (path, loc)
544+
in
545+
{txt; loc}
546+
in
529547
let props = map_jsx_props sub props in
530548
let children_expr = map_jsx_children sub loc children in
531549
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 with
299+
| Lower _ -> ()
300+
| QualifiedLower {path; _} | Upper {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 with
310+
| Lower _ -> ()
311+
| QualifiedLower {path; _} | Upper {path; _} -> add_path bv path);
307312
and_jsx_props bv props;
308313
add_jsx_children bv children
309314

compiler/ml/parsetree.ml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -325,20 +325,25 @@ and jsx_element =
325325
| Jsx_unary_element of jsx_unary_element
326326
| Jsx_container_element of jsx_container_element
327327

328+
and jsx_tag_name =
329+
| Lower of {name: string; loc: Location.t}
330+
| QualifiedLower of {path: Longident.t; name: string; loc: Location.t}
331+
| Upper of {path: Longident.t; loc: Location.t}
332+
328333
and jsx_fragment = {
329334
(* > *) jsx_fragment_opening: Lexing.position;
330335
(* children *) jsx_fragment_children: jsx_children;
331336
(* </ *) jsx_fragment_closing: Lexing.position;
332337
}
333338

334339
and jsx_unary_element = {
335-
jsx_unary_element_tag_name: Longident.t loc;
340+
jsx_unary_element_tag_name: jsx_tag_name;
336341
jsx_unary_element_props: jsx_props;
337342
}
338343

339344
and jsx_container_element = {
340345
(* jsx_container_element_opening_tag_start: Lexing.position; *)
341-
jsx_container_element_tag_name_start: Longident.t loc;
346+
jsx_container_element_tag_name_start: jsx_tag_name;
342347
(* > *)
343348
jsx_container_element_opening_tag_end: Lexing.position;
344349
jsx_container_element_props: jsx_props;
@@ -376,7 +381,7 @@ and jsx_closing_container_tag = {
376381
(* </ *)
377382
jsx_closing_container_tag_start: Lexing.position;
378383
(* name *)
379-
jsx_closing_container_tag_name: Longident.t loc;
384+
jsx_closing_container_tag_name: jsx_tag_name;
380385
(* > *)
381386
jsx_closing_container_tag_end: Lexing.position;
382387
}

0 commit comments

Comments
 (0)