Skip to content

Commit 42bddab

Browse files
committed
completion for lowercase JSX as HTML elements
1 parent 8a857e6 commit 42bddab

File tree

6 files changed

+275
-25
lines changed

6 files changed

+275
-25
lines changed

analysis/src/CompletionBackEnd.ml

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ let getEnvWithOpens ~scope ~(env : QueryEnv.t) ~package
211211
| None -> None
212212
| Some env -> ResolvePath.resolvePath ~env ~package ~path))
213213

214-
let detail name (kind : Completion.kind) =
214+
let kindToDetail name (kind : Completion.kind) =
215215
match kind with
216216
| Type {decl} -> decl |> Shared.declToString name
217217
| Value typ -> typ |> Shared.typeToString
@@ -568,11 +568,17 @@ let completionToItem
568568
insertText;
569569
insertTextFormat;
570570
filterText;
571+
detail;
571572
} =
572573
let item =
573574
mkItem ~name
574575
~kind:(Completion.kindToInt kind)
575-
~deprecated ~detail:(detail name kind) ~docstring
576+
~deprecated
577+
~detail:
578+
(match detail with
579+
| None -> kindToDetail name kind
580+
| Some detail -> detail)
581+
~docstring
576582
in
577583
if !Cfg.supportsSnippets then
578584
{item with sortText; insertText; insertTextFormat; filterText}
@@ -1526,3 +1532,16 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover
15261532
| _ -> [c])
15271533
| _ -> [c])
15281534
|> List.flatten
1535+
| ChtmlElement {prefix} ->
1536+
CompletionJsx.htmlElements
1537+
|> List.filter_map (fun (elementName, description, deprecated) ->
1538+
if Utils.startsWith elementName prefix then
1539+
let name = "<" ^ elementName ^ ">" in
1540+
Some
1541+
(Completion.create name ~kind:(Label name) ~detail:description ~env
1542+
~docstring:[description] ~insertText:elementName
1543+
?deprecated:
1544+
(match deprecated with
1545+
| true -> Some "true"
1546+
| false -> None))
1547+
else None)

analysis/src/CompletionFrontEnd.ml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -735,7 +735,11 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
735735
in
736736
if jsxCompletable <> None then setResultOpt jsxCompletable
737737
else if compName.loc |> Loc.hasPos ~pos:posBeforeCursor then
738-
setResult (Cpath (CPId (compNamePath, Module)))
738+
setResult
739+
(match compNamePath with
740+
| [prefix] when Char.lowercase_ascii prefix.[0] = prefix.[0] ->
741+
ChtmlElement {prefix}
742+
| _ -> Cpath (CPId (compNamePath, Module)))
739743
| Pexp_apply
740744
( {pexp_desc = Pexp_ident {txt = Lident "|."}},
741745
[

analysis/src/CompletionJsx.ml

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,206 @@ let domLabels =
487487
("suppressContentEditableWarning", bool);
488488
]
489489

490+
let htmlElements =
491+
[
492+
("a", "Defines a hyperlink.", false);
493+
("abbr", "Defines an abbreviated form of a longer word or phrase.", false);
494+
("acronym", "Defines an acronym. Use <abbr> instead.", true);
495+
("address", "Specifies the author's contact information.", false);
496+
( "applet",
497+
"Embeds a Java applet (mini Java applications) on the page. Use <object> \
498+
instead.",
499+
true );
500+
("area", "Defines a specific area within an image map.", false);
501+
("article", "Defines an article.", false);
502+
("aside", "Defines some content loosely related to the page content.", false);
503+
("audio", "Embeds a sound, or an audio stream in an HTML document.", false);
504+
("b", "Displays text in a bold style.", false);
505+
("base", "Defines the base URL for all relative URLs in a document.", false);
506+
("basefont", "Specifies the base font for a page. Use CSS instead.", true);
507+
( "bdi",
508+
"Represents text that is isolated from its surrounding for the purposes \
509+
of bidirectional text formatting.",
510+
false );
511+
("bdo", "Overrides the current text direction.", false);
512+
("big", "Displays text in a large size. Use CSS instead.", true);
513+
( "blockquote",
514+
"Represents a section that is quoted from another source.",
515+
false );
516+
("body", "Defines the document's body.", false);
517+
("br", "Produces a single line break.", false);
518+
("button", "Creates a clickable button.", false);
519+
( "canvas",
520+
"Defines a region in the document, which can be used to draw graphics on \
521+
the fly via scripting (usually JavaScript).",
522+
false );
523+
("caption", "Defines the caption or title of the table.", false);
524+
("center", "Align contents in the center. Use CSS instead.", true);
525+
("cite", "Indicates a citation or reference to another source.", false);
526+
("code", "Specifies text as computer code.", false);
527+
( "col",
528+
"Defines attribute values for one or more columns in a table.",
529+
false );
530+
("colgroup", "Specifies attributes for multiple columns in a table.", false);
531+
( "data",
532+
"Links a piece of content with a machine-readable translation.",
533+
false );
534+
( "datalist",
535+
"Represents a set of pre-defined options for an <input> element.",
536+
false );
537+
( "dd",
538+
"Specifies a description, or value for the term (<dt>) in a description \
539+
list (<dl>).",
540+
false );
541+
("del", "Represents text that has been deleted from the document.", false);
542+
( "details",
543+
"Represents a widget from which the user can obtain additional \
544+
information or controls on-demand.",
545+
false );
546+
("dfn", "Specifies a definition.", false);
547+
("dialog", "Defines a dialog box or subwindow.", false);
548+
("dir", "Defines a directory list. Use <ul> instead.", true);
549+
("div", "Specifies a division or a section in a document.", false);
550+
("dl", "Defines a description list.", false);
551+
("dt", "Defines a term (an item) in a description list.", false);
552+
("em", "Defines emphasized text.", false);
553+
( "embed",
554+
"Embeds external application, typically multimedia content like audio or \
555+
video into an HTML document.",
556+
false );
557+
("fieldset", "Specifies a set of related form fields.", false);
558+
("figcaption", "Defines a caption or legend for a figure.", false);
559+
("figure", "Represents a figure illustrated as part of the document.", false);
560+
("font", "Defines font, color, and size for text. Use CSS instead.", true);
561+
("footer", "Represents the footer of a document or a section.", false);
562+
("form", "Defines an HTML form for user input.", false);
563+
("frame", "Defines a single frame within a frameset.", true);
564+
("frameset", "Defines a collection of frames or other frameset.", true);
565+
( "head",
566+
"Defines the head portion of the document that contains information \
567+
about the document such as title.",
568+
false );
569+
("header", "Represents the header of a document or a section.", false);
570+
("hgroup", "Defines a group of headings.", false);
571+
("h1", "to <h6>\tDefines HTML headings.", false);
572+
("hr", "Produce a horizontal line.", false);
573+
("html", "Defines the root of an HTML document.", false);
574+
("i", "Displays text in an italic style.", false);
575+
("iframe", "Displays a URL in an inline frame.", false);
576+
("img", "Represents an image.", false);
577+
("input", "Defines an input control.", false);
578+
( "ins",
579+
"Defines a block of text that has been inserted into a document.",
580+
false );
581+
("kbd", "Specifies text as keyboard input.", false);
582+
( "keygen",
583+
"Represents a control for generating a public-private key pair.",
584+
false );
585+
("label", "Defines a label for an <input> control.", false);
586+
("legend", "Defines a caption for a <fieldset> element.", false);
587+
("li", "Defines a list item.", false);
588+
( "link",
589+
"Defines the relationship between the current document and an external \
590+
resource.",
591+
false );
592+
("main", "Represents the main or dominant content of the document.", false);
593+
("map", "Defines a client-side image-map.", false);
594+
("mark", "Represents text highlighted for reference purposes.", false);
595+
("menu", "Represents a list of commands.", false);
596+
( "menuitem",
597+
"Defines a list (or menuitem) of commands that a user can perform.",
598+
false );
599+
("meta", "Provides structured metadata about the document content.", false);
600+
("meter", "Represents a scalar measurement within a known range.", false);
601+
("nav", "Defines a section of navigation links.", false);
602+
( "noframes",
603+
"Defines an alternate content that displays in browsers that do not \
604+
support frames.",
605+
true );
606+
( "noscript",
607+
"Defines alternative content to display when the browser doesn't support \
608+
scripting.",
609+
false );
610+
("object", "Defines an embedded object.", false);
611+
("ol", "Defines an ordered list.", false);
612+
( "optgroup",
613+
"Defines a group of related options in a selection list.",
614+
false );
615+
("option", "Defines an option in a selection list.", false);
616+
("output", "Represents the result of a calculation.", false);
617+
("p", "Defines a paragraph.", false);
618+
("param", "Defines a parameter for an object or applet element.", false);
619+
("picture", "Defines a container for multiple image sources.", false);
620+
("pre", "Defines a block of preformatted text.", false);
621+
("progress", "Represents the completion progress of a task.", false);
622+
("q", "Defines a short inline quotation.", false);
623+
( "rp",
624+
"Provides fall-back parenthesis for browsers that that don't support \
625+
ruby annotations.",
626+
false );
627+
( "rt",
628+
"Defines the pronunciation of character presented in a ruby annotations.",
629+
false );
630+
("ruby", "Represents a ruby annotation.", false);
631+
( "s",
632+
"Represents contents that are no longer accurate or no longer relevant.",
633+
false );
634+
("samp", "Specifies text as sample output from a computer program.", false);
635+
( "script",
636+
"Places script in the document for client-side processing.",
637+
false );
638+
( "section",
639+
"Defines a section of a document, such as header, footer etc.",
640+
false );
641+
("select", "Defines a selection list within a form.", false);
642+
("small", "Displays text in a smaller size.", false);
643+
( "source",
644+
"Defines alternative media resources for the media elements like <audio> \
645+
or <video>.",
646+
false );
647+
("span", "Defines an inline styleless section in a document.", false);
648+
("strike", "Displays text in strikethrough style.", true);
649+
("strong", "Indicate strongly emphasized text.", false);
650+
( "style",
651+
"Inserts style information (commonly CSS) into the head of a document.",
652+
false );
653+
("sub", "Defines subscripted text.", false);
654+
("summary", "Defines a summary for the <details> element.", false);
655+
("sup", "Defines superscripted text.", false);
656+
( "svg",
657+
"Embed SVG (Scalable Vector Graphics) content in an HTML document.",
658+
false );
659+
("table", "Defines a data table.", false);
660+
( "tbody",
661+
"Groups a set of rows defining the main body of the table data.",
662+
false );
663+
("td", "Defines a cell in a table.", false);
664+
( "template",
665+
"Defines the fragments of HTML that should be hidden when the page is \
666+
loaded, but can be cloned and inserted in the document by JavaScript.",
667+
false );
668+
("textarea", "Defines a multi-line text input control (text area).", false);
669+
( "tfoot",
670+
"Groups a set of rows summarizing the columns of the table.",
671+
false );
672+
("th", "Defines a header cell in a table.", false);
673+
( "thead",
674+
"Groups a set of rows that describes the column labels of a table.",
675+
false );
676+
("time", "Represents a time and/or date.", false);
677+
("title", "Defines a title for the document.", false);
678+
("tr", "Defines a row of cells in a table.", false);
679+
( "track",
680+
"Defines text tracks for the media elements like <audio> or <video>.",
681+
false );
682+
("tt", "Obsolete Displays text in a teletype style.", false);
683+
("u", "Displays text with an underline.", false);
684+
("ul", "Defines an unordered list.", false);
685+
("var", "Defines a variable.", false);
686+
("video", "Embeds video content in an HTML document.", false);
687+
("wbr", "Represents a line break opportunity.", false);
688+
]
689+
490690
let getJsxLabels ~componentPath ~findTypeOfValue ~package =
491691
match componentPath @ ["make"] |> findTypeOfValue with
492692
| Some (typ, make_env) ->

analysis/src/SharedTypes.ml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -348,19 +348,22 @@ module Completion = struct
348348
deprecated: string option;
349349
docstring: string list;
350350
kind: kind;
351+
detail: string option;
351352
}
352353

353-
let create ~kind ~env ?(docstring = []) ?filterText name =
354+
let create ~kind ~env ?(docstring = []) ?filterText ?insertText ?deprecated
355+
?detail name =
354356
{
355357
name;
356358
env;
357-
deprecated = None;
359+
deprecated;
358360
docstring;
359361
kind;
360362
sortText = None;
361-
insertText = None;
363+
insertText;
362364
insertTextFormat = None;
363365
filterText;
366+
detail;
364367
}
365368

366369
let createWithSnippet ~name ?insertText ~kind ~env ?sortText ?filterText
@@ -375,6 +378,7 @@ module Completion = struct
375378
insertText;
376379
insertTextFormat = Some Protocol.Snippet;
377380
filterText;
381+
detail = None;
378382
}
379383

380384
(* https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion *)
@@ -654,6 +658,7 @@ module Completable = struct
654658
fallback: t option;
655659
}
656660
| CexhaustiveSwitch of {contextPath: contextPath; exprLoc: Location.t}
661+
| ChtmlElement of {prefix: string}
657662

658663
let toString =
659664
let completionContextToString = function
@@ -740,6 +745,7 @@ module Completable = struct
740745
|> String.concat ", "))
741746
| CexhaustiveSwitch {contextPath} ->
742747
"CexhaustiveSwitch " ^ contextPathToString contextPath
748+
| ChtmlElement {prefix} -> "ChtmlElement <" ^ prefix
743749
end
744750

745751
module CursorPosition = struct

analysis/tests/src/CompletionJsx.res

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ module SomeComponent = {
2828
// ^com
2929
// {someArr->a}
3030
// ^com
31-
// <d
32-
// ^com
31+
// <di
32+
// ^com
3333
</div>
3434
}
3535
}

analysis/tests/src/expected/CompletionJsx.res.txt

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -358,27 +358,48 @@ Completable: Cpath Value[someArr]->a <<jsx>>
358358
"documentation": {"kind": "markdown", "value": "Deprecated: `append` is not type-safe. Use `concat` instead.\n\n"}
359359
}]
360360

361-
Complete src/CompletionJsx.res 30:11
362-
posCursor:[30:11] posNoWhite:[30:10] Found expr:[8:13->33:3]
363-
posCursor:[30:11] posNoWhite:[30:10] Found expr:[9:4->32:10]
364-
posCursor:[30:11] posNoWhite:[30:10] Found expr:[10:4->32:10]
365-
posCursor:[30:11] posNoWhite:[30:10] Found expr:[11:4->32:10]
366-
posCursor:[30:11] posNoWhite:[30:10] Found expr:[12:4->32:10]
367-
posCursor:[30:11] posNoWhite:[30:10] Found expr:[15:5->32:10]
361+
Complete src/CompletionJsx.res 30:12
362+
posCursor:[30:12] posNoWhite:[30:11] Found expr:[8:13->33:3]
363+
posCursor:[30:12] posNoWhite:[30:11] Found expr:[9:4->32:10]
364+
posCursor:[30:12] posNoWhite:[30:11] Found expr:[10:4->32:10]
365+
posCursor:[30:12] posNoWhite:[30:11] Found expr:[11:4->32:10]
366+
posCursor:[30:12] posNoWhite:[30:11] Found expr:[12:4->32:10]
367+
posCursor:[30:12] posNoWhite:[30:11] Found expr:[15:5->32:10]
368368
JSX <div:[15:5->15:8] > _children:15:8
369-
posCursor:[30:11] posNoWhite:[30:10] Found expr:[15:8->32:4]
369+
posCursor:[30:12] posNoWhite:[30:11] Found expr:[15:8->32:4]
370370
Pexp_construct :::[16:7->32:4] [16:7->32:4]
371-
posCursor:[30:11] posNoWhite:[30:10] Found expr:[16:7->32:4]
372-
posCursor:[30:11] posNoWhite:[30:10] Found expr:[17:7->32:4]
371+
posCursor:[30:12] posNoWhite:[30:11] Found expr:[16:7->32:4]
372+
posCursor:[30:12] posNoWhite:[30:11] Found expr:[17:7->32:4]
373373
Pexp_construct :::[17:7->32:4] [17:7->32:4]
374-
posCursor:[30:11] posNoWhite:[30:10] Found expr:[17:7->32:4]
375-
posCursor:[30:11] posNoWhite:[30:10] Found expr:[30:10->32:4]
374+
posCursor:[30:12] posNoWhite:[30:11] Found expr:[17:7->32:4]
375+
posCursor:[30:12] posNoWhite:[30:11] Found expr:[30:10->32:4]
376376
Pexp_construct :::[30:10->32:4] [30:10->32:4]
377-
posCursor:[30:11] posNoWhite:[30:10] Found expr:[30:10->32:4]
378-
posCursor:[30:11] posNoWhite:[30:10] Found expr:[30:10->30:11]
379-
JSX <d:[30:10->30:11] > _children:None
380-
Completable: Cpath Module[d]
381-
[]
377+
posCursor:[30:12] posNoWhite:[30:11] Found expr:[30:10->32:4]
378+
posCursor:[30:12] posNoWhite:[30:11] Found expr:[30:10->30:12]
379+
JSX <di:[30:10->30:12] > _children:None
380+
Completable: ChtmlElement <di
381+
[{
382+
"label": "<dialog>",
383+
"kind": 4,
384+
"tags": [],
385+
"detail": "Defines a dialog box or subwindow.",
386+
"documentation": {"kind": "markdown", "value": "Defines a dialog box or subwindow."},
387+
"insertText": "dialog"
388+
}, {
389+
"label": "<dir>",
390+
"kind": 4,
391+
"tags": [1],
392+
"detail": "Defines a directory list. Use <ul> instead.",
393+
"documentation": {"kind": "markdown", "value": "Deprecated: true\n\nDefines a directory list. Use <ul> instead."},
394+
"insertText": "dir"
395+
}, {
396+
"label": "<div>",
397+
"kind": 4,
398+
"tags": [],
399+
"detail": "Specifies a division or a section in a document.",
400+
"documentation": {"kind": "markdown", "value": "Specifies a division or a section in a document."},
401+
"insertText": "div"
402+
}]
382403

383404
Complete src/CompletionJsx.res 45:23
384405
posCursor:[45:23] posNoWhite:[45:22] Found expr:[45:4->45:23]

0 commit comments

Comments
 (0)