Skip to content

Commit

Permalink
Merge pull request #4191 from masatake/jsx
Browse files Browse the repository at this point in the history
JavaScript,HTML: skip JSX elements
  • Loading branch information
masatake authored Feb 24, 2025
2 parents 3e21c2f + 6c6799a commit f272e5c
Show file tree
Hide file tree
Showing 14 changed files with 353 additions and 32 deletions.
3 changes: 3 additions & 0 deletions Units/parser-javascript.r/simple-jsx-no-guest.d/args.ctags
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
--sort=no
--extras=+r
--fields=+lr
8 changes: 8 additions & 0 deletions Units/parser-javascript.r/simple-jsx-no-guest.d/expected.tags
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
foo input.jsx /^function foo() {$/;" f language:JavaScript roles:def
Comp1 input-0.jsx /^const Comp1 = () => {$/;" f language:JavaScript roles:def
x input-0.jsx /^ const x = (arg) => console.log(arg)$/;" f language:JavaScript function:Comp1 roles:def
Comp2 input-0.jsx /^const Comp2 = (props) => {$/;" f language:JavaScript roles:def
Comp3 input-0.jsx /^const Comp3 = (str) => {$/;" f language:JavaScript roles:def
Comp4 input-0.jsx /^const Comp4 = (str) => {$/;" f language:JavaScript roles:def
Comp5 input-0.jsx /^const Comp5 = (str) => {$/;" f language:JavaScript roles:def
z0 input-0.jsx /^var z0$/;" v language:JavaScript roles:def
36 changes: 36 additions & 0 deletions Units/parser-javascript.r/simple-jsx-no-guest.d/input-0.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const Comp1 = () => {
const x = (arg) => console.log(arg)
x(4)
return(<Comp2 text={<p>Some Text</p>}/>)
}

const Comp2 = (props) => {
return (
props.text
)
}

const Comp3 = (str) => {
return(<div>
<h1>hello</h1>
something: {str}
<h1>bye</h1>
</div>)
}

const Comp4 = (str) => {
return(<>
<h1>bonjour</h1>
something: {str}
{<h2>frag{subtitle}ment</h2>}
<h1>au revoir</h1>
</>)
}

const Comp5 = (str) => {
return <x/>
}

var z0

// Taken from https://stackoverflow.com/questions/79395369/why-does-universal-ctags-fail-to-record-some-functions-in-the-tags-file-for-a-pr
7 changes: 7 additions & 0 deletions Units/parser-javascript.r/simple-jsx-no-guest.d/input.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
function foo() {
var x = <div>
<Menu />
<div>;

return x;
}
3 changes: 3 additions & 0 deletions Units/parser-javascript.r/simple-jsx.d/args.ctags
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
--sort=no
--extras=+{guest}r
--fields=+lr
14 changes: 13 additions & 1 deletion Units/parser-javascript.r/simple-jsx.d/expected.tags
Original file line number Diff line number Diff line change
@@ -1 +1,13 @@
foo input.jsx /^function foo() {$/;" f
foo input.jsx /^function foo() {$/;" f language:JavaScript roles:def
Comp1 input-0.jsx /^const Comp1 = () => {$/;" f language:JavaScript roles:def
x input-0.jsx /^ const x = (arg) => console.log(arg)$/;" f language:JavaScript function:Comp1 roles:def
Comp2 input-0.jsx /^const Comp2 = (props) => {$/;" f language:JavaScript roles:def
Comp3 input-0.jsx /^const Comp3 = (str) => {$/;" f language:JavaScript roles:def
hello input-0.jsx /^ <h1>hello<\/h1>$/;" h language:HTML roles:def
bye input-0.jsx /^ <h1>bye<\/h1>$/;" h language:HTML roles:def
Comp4 input-0.jsx /^const Comp4 = (str) => {$/;" f language:JavaScript roles:def
bonjour input-0.jsx /^ <h1>bonjour<\/h1>$/;" h language:HTML roles:def
frag ment input-0.jsx /^ {<h2>frag{subtitle}ment<\/h2>}$/;" i language:HTML roles:def
au revoir input-0.jsx /^ <h1>au revoir<\/h1>$/;" h language:HTML roles:def
Comp5 input-0.jsx /^const Comp5 = (str) => {$/;" f language:JavaScript roles:def
z0 input-0.jsx /^var z0$/;" v language:JavaScript roles:def
36 changes: 36 additions & 0 deletions Units/parser-javascript.r/simple-jsx.d/input-0.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const Comp1 = () => {
const x = (arg) => console.log(arg)
x(4)
return(<Comp2 text={<p>Some Text</p>}/>)
}

const Comp2 = (props) => {
return (
props.text
)
}

const Comp3 = (str) => {
return(<div>
<h1>hello</h1>
something: {str}
<h1>bye</h1>
</div>)
}

const Comp4 = (str) => {
return(<>
<h1>bonjour</h1>
something: {str}
{<h2>frag{subtitle}ment</h2>}
<h1>au revoir</h1>
</>)
}

const Comp5 = (str) => {
return <x/>
}

var z0

// Taken from https://stackoverflow.com/questions/79395369/why-does-universal-ctags-fail-to-record-some-functions-in-the-tags-file-for-a-pr
124 changes: 94 additions & 30 deletions parsers/html.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include "promise.h"
#include "trace.h"

#include "x-jscript.h"

/* The max. number of nested elements - prevents further recursion if the limit
* is exceeded and avoids stack overflow for invalid input containing too many
* open tags */
Expand Down Expand Up @@ -194,11 +196,17 @@ typedef struct {
static int Lang_html;


static void readTag (tokenInfo *token, vString *text, int depth);
static void readTag (tokenInfo *token, vString *text, int depth, bool asJSX);

static void skipOtherScriptContent (const int delimiter);

static void readTokenText (tokenInfo *const token, bool collectText)
static void skipJavaScriptObjectExpression (void)
{
ungetcToInputFile ('{');
javaScriptSkipObjectExpression ();
}

static void readTokenText (tokenInfo *const token, bool collectText, bool asJSX)
{
int c;
int lastC = 'X'; /* whatever non-space character */
Expand All @@ -220,6 +228,15 @@ static void readTokenText (tokenInfo *const token, bool collectText)
token->type = TOKEN_TEXT;
break;

case '{':
if (asJSX)
{
/* If we find {...} in HTML in JSXElement,
* replace it with a whitespace ' '. */
skipJavaScriptObjectExpression ();
c = ' ';
}
/* FALLTHROUGH */
default:
if (collectText)
{
Expand Down Expand Up @@ -288,7 +305,7 @@ static void readTokenInScript (tokenInfo *const token)
TRACE_PRINT("token (in script): %s (%s)", tokenTypes[token->type], vStringValue (token->string));
}

static void readToken (tokenInfo *const token, bool skipComments)
static void readToken (tokenInfo *const token, bool skipComments, bool asJSX)
{
int c;

Expand Down Expand Up @@ -387,6 +404,15 @@ static void readToken (tokenInfo *const token, bool skipComments)
break;
}

case '{':
if (asJSX)
{
token->type = TOKEN_STRING;
skipJavaScriptObjectExpression ();
break;
}
/* FALLTHROUGH */

default:
{
do
Expand Down Expand Up @@ -419,26 +445,26 @@ static void appendText (vString *text, vString *appendedText)
}
}

static bool readTagContent (tokenInfo *token, vString *text, long *line, long *lineOffset, int depth)
static bool readTagContent (tokenInfo *token, vString *text, long *line, long *lineOffset, int depth, bool asJSX)
{
TRACE_ENTER();

tokenType type;

readTokenText (token, text != NULL);
readTokenText (token, text != NULL, asJSX);
appendText (text, token->string);

do
{
*line = getInputLineNumber ();
*lineOffset = getInputLineOffset ();
readToken (token, false);
readToken (token, false, asJSX);
type = token->type;
if (type == TOKEN_OPEN_TAG_START)
readTag (token, text, depth + 1);
readTag (token, text, depth + 1, asJSX);
if (type == TOKEN_COMMENT || type == TOKEN_OPEN_TAG_START)
{
readTokenText (token, text != NULL);
readTokenText (token, text != NULL, asJSX);
appendText (text, token->string);
}
}
Expand Down Expand Up @@ -564,13 +590,21 @@ static void makeClassRefTags (const char *classes)
vStringDelete (klass);
}

static void readTag (tokenInfo *token, vString *text, int depth)
static void readTag (tokenInfo *token, vString *text, int depth, bool asJSX)
{
TRACE_ENTER();

bool textCreated = false;

readToken (token, true);
readToken (token, true, asJSX);
if (asJSX && token->type == TOKEN_TAG_END)
{
/* Accept <> */
ungetcToInputFile ('>');
token->type = TOKEN_NAME;
vStringClear (token->string);
}

if (token->type == TOKEN_NAME)
{
keywordId startTag;
Expand All @@ -592,46 +626,47 @@ static void readTag (tokenInfo *token, vString *text, int depth)
{
keywordId attribute = KEYWORD_NONE;

readToken (token, true);
readToken (token, true, asJSX);
if (token->type == TOKEN_NAME)
attribute = lookupKeyword (vStringValue (token->string), Lang_html);

if (attribute == KEYWORD_class)
{
readToken (token, true);
readToken (token, true, asJSX);
if (token->type == TOKEN_EQUAL)
{
readToken (token, true);
if (token->type == TOKEN_STRING)
readToken (token, true, asJSX);
if (token->type == TOKEN_STRING
&& !vStringIsEmpty (token->string))
makeClassRefTags (vStringValue (token->string));
}
}
else if (attribute == KEYWORD_id)
{
readToken (token, true);
readToken (token, true, asJSX);
if (token->type == TOKEN_EQUAL)
{
readToken (token, true);
readToken (token, true, asJSX);
if (token->type == TOKEN_STRING)
makeSimpleTag (token->string, K_ID);
}
}
else if (startTag == KEYWORD_a && attribute == KEYWORD_name)
{
readToken (token, true);
readToken (token, true, asJSX);
if (token->type == TOKEN_EQUAL)
{
readToken (token, true);
readToken (token, true, asJSX);
if (token->type == TOKEN_STRING || token->type == TOKEN_NAME)
makeSimpleTag (token->string, K_ANCHOR);
}
}
else if (startTag == KEYWORD_script && attribute == KEYWORD_src)
{
readToken (token, true);
readToken (token, true, asJSX);
if (token->type == TOKEN_EQUAL)
{
readToken (token, true);
readToken (token, true, asJSX);
if (token->type == TOKEN_STRING)
makeSimpleRefTag (token->string, K_SCRIPT,
SCRIPT_KIND_EXTERNAL_FILE_ROLE);
Expand All @@ -641,10 +676,10 @@ static void readTag (tokenInfo *token, vString *text, int depth)
{
if (attribute == KEYWORD_rel)
{
readToken (token, true);
readToken (token, true, asJSX);
if (token->type == TOKEN_EQUAL)
{
readToken (token, true);
readToken (token, true, asJSX);
if (token->type == TOKEN_STRING &&
/* strcmp is not enough:
* e.g. <link href="fancy.css"
Expand All @@ -656,10 +691,10 @@ static void readTag (tokenInfo *token, vString *text, int depth)
}
else if (attribute == KEYWORD_href)
{
readToken (token, true);
readToken (token, true, asJSX);
if (token->type == TOKEN_EQUAL)
{
readToken (token, true);
readToken (token, true, asJSX);
if (token->type == TOKEN_STRING)
{
if (stylesheet == NULL)
Expand Down Expand Up @@ -700,14 +735,22 @@ static void readTag (tokenInfo *token, vString *text, int depth)
if (script)
makePromise ("JavaScript", startLineNumber, startLineOffset,
endLineNumber, endLineOffset, startSourceLineNumber);
readToken (token, true);
readToken (token, true, asJSX);
goto out;
}

tag_start2 = readTagContent (token, text, &endLineNumber, &endLineOffset, depth);
tag_start2 = readTagContent (token, text, &endLineNumber, &endLineOffset, depth, asJSX);
if (tag_start2)
{
readToken (token, true);
readToken (token, true, asJSX);
if (asJSX && token->type == TOKEN_TAG_END)
{
/* Accept </> */
ungetcToInputFile ('>');
token->type = TOKEN_NAME;
vStringClear (token->string);
}

if (isHeading && textCreated && vStringLength (text) > 0)
{
keywordId endTag = lookupKeyword (vStringValue (token->string), Lang_html);
Expand Down Expand Up @@ -737,7 +780,7 @@ static void readTag (tokenInfo *token, vString *text, int depth)
endLineNumber, endLineOffset, startSourceLineNumber);
}

readToken (token, true);
readToken (token, true, asJSX);
}
}
}
Expand All @@ -759,9 +802,9 @@ static void findHtmlTags (void)

do
{
readToken (&token, true);
readToken (&token, true, false);
if (token.type == TOKEN_OPEN_TAG_START)
readTag (&token, NULL, 0);
readTag (&token, NULL, 0, false);
}
while (token.type != TOKEN_EOF);

Expand Down Expand Up @@ -789,3 +832,24 @@ extern parserDefinition* HtmlParser (void)
def->keywordCount = ARRAY_SIZE (HtmlKeywordTable);
return def;
}

/* Just another entry point for handling JSX */
extern void htmlParseJSXElement (void)
{
pushLanguage (Lang_html);
{
TRACE_ENTER();

tokenInfo token;
token.string = vStringNew ();

readToken (&token, true, true);
if (token.type == TOKEN_OPEN_TAG_START)
readTag (&token, NULL, 0, true);

vStringDelete (token.string);

TRACE_LEAVE();
}
popLanguage ();
}
Loading

0 comments on commit f272e5c

Please sign in to comment.