Skip to content

Commit

Permalink
Merge pull request #3357 from jafl/javascript-capture-methods-inside-…
Browse files Browse the repository at this point in the history
…objects

JavaScript: capture methods inside objects
  • Loading branch information
masatake committed Apr 23, 2022
2 parents f6528d8 + 50eb520 commit e325816
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 14 deletions.
4 changes: 4 additions & 0 deletions Units/parser-javascript.r/github-issue-1389.d/expected.tags
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
AnonymousClass80ca49fb0101 input.js /^var Namespace = Ember.Namespace = Ember.Object.extend=({$/;" c
Namespace input.js /^var Namespace = Ember.Namespace = Ember.Object.extend=({$/;" v
init input.js /^ init: function() {$/;" m class:AnonymousClass80ca49fb0101
isNamespace input.js /^ isNamespace: true,$/;" p class:AnonymousClass80ca49fb0101
10 changes: 10 additions & 0 deletions Units/parser-javascript.r/github-issue-1389.d/input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
var Namespace = Ember.Namespace = Ember.Object.extend=({
isNamespace: true,

init: function() {
Ember.Namespace.NAMESPACES.push(this);
Ember.Namespace.PROCESSED = false;
}
});

// https://github.com/universal-ctags/ctags/issues/1389
1 change: 1 addition & 0 deletions Units/parser-javascript.r/github-issue-1933.d/args.ctags
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--fields=+S
4 changes: 4 additions & 0 deletions Units/parser-javascript.r/github-issue-1933.d/expected.tags
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
func1 input.js /^ func1: () => {$/;" m class:testobj signature:()
func2 input.js /^ func2: () => {$/;" m class:testobj signature:()
func3 input.js /^ func3: x => {$/;" m class:testobj signature:(x)
testobj input.js /^testobj = {$/;" c
13 changes: 13 additions & 0 deletions Units/parser-javascript.r/github-issue-1933.d/input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
testobj = {
func1: () => {
console.log('test1');
},
func2: () => {
console.log('test2');
},
func3: x => {
console.log(x);
}
};

// https://github.com/universal-ctags/ctags/issues/1933
3 changes: 3 additions & 0 deletions Units/parser-javascript.r/github-issue-780.d/expected.tags
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
AnonymousClass6b70cce50101 input.js /^const Bar = Foo.create({$/;" c
Bar input.js /^const Bar = Foo.create({$/;" C
jump input.js /^ jump() {$/;" m class:AnonymousClass6b70cce50101
11 changes: 11 additions & 0 deletions Units/parser-javascript.r/github-issue-780.d/input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Foo from './foo';

const Bar = Foo.create({
jump() {
return 123;
}
})

export default Bar;

// https://github.com/universal-ctags/ctags/issues/780
12 changes: 12 additions & 0 deletions Units/parser-javascript.r/js-arraylist.d/expected.tags
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
AnonymousClassecad86450101 input.js /^ { a: "hello", b: 42 },$/;" c
AnonymousClassecad86450201 input.js /^ { a: "hi", b: 41 }$/;" c
AnonymousClassecad86450301 input.js /^ { a: {}, b: {} },$/;" c class:class
AnonymousClassecad86450401 input.js /^ { a: {}, b: {} }$/;" c class:class
a input.js /^ { a: {}, b: {} }$/;" p class:class.AnonymousClassecad86450401
a input.js /^ { a: {}, b: {} },$/;" p class:class.AnonymousClassecad86450301
a input.js /^ { a: "hello", b: 42 },$/;" p class:AnonymousClassecad86450101
a input.js /^ { a: "hi", b: 41 }$/;" p class:AnonymousClassecad86450201
a input.js /^var a = [];$/;" v
b input.js /^ { a: {}, b: {} }$/;" p class:class.AnonymousClassecad86450401
b input.js /^ { a: {}, b: {} },$/;" p class:class.AnonymousClassecad86450301
b input.js /^ { a: "hello", b: 42 },$/;" p class:AnonymousClassecad86450101
b input.js /^ { a: "hi", b: 41 }$/;" p class:AnonymousClassecad86450201
b input.js /^var b = [1, 2, 3];$/;" v
bar input.js /^ bar: [ 4, 5, 9]$/;" p class:class.test1
c input.js /^var c = [$/;" v
Expand Down
97 changes: 83 additions & 14 deletions parsers/jscript.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,8 @@ typedef enum eTokenType {
/* To handle Babel's decorators.
* Used only in readTokenFull or lower functions. */
TOKEN_ATMARK,
TOKEN_BINARY_OPERATOR
TOKEN_BINARY_OPERATOR,
TOKEN_ARROW
} tokenType;

typedef struct sTokenInfo {
Expand Down Expand Up @@ -226,6 +227,7 @@ static void readTokenFull (tokenInfo *const token, bool include_newlines, vStrin
static void skipArgumentList (tokenInfo *const token, bool include_newlines, vString *const repr);
static void parseFunction (tokenInfo *const token);
static bool parseBlock (tokenInfo *const token, const vString *const parentScope);
static bool parseMethods (tokenInfo *const token, const tokenInfo *const class, const bool is_es6_class);
static bool parseLine (tokenInfo *const token, bool is_inside_class);
static void parseUI5 (tokenInfo *const token);

Expand Down Expand Up @@ -851,10 +853,22 @@ static void readTokenFullRaw (tokenInfo *const token, bool include_newlines, vSt
case ':': token->type = TOKEN_COLON; break;
case '{': token->type = TOKEN_OPEN_CURLY; break;
case '}': token->type = TOKEN_CLOSE_CURLY; break;
case '=': token->type = TOKEN_EQUAL_SIGN; break;
case '[': token->type = TOKEN_OPEN_SQUARE; break;
case ']': token->type = TOKEN_CLOSE_SQUARE; break;

case '=':
{
int d = getcFromInputFile ();
if (d == '>')
token->type = TOKEN_ARROW;
else
{
ungetcToInputFile (d);
token->type = TOKEN_EQUAL_SIGN;
}
break;
}

case '+':
case '-':
{
Expand Down Expand Up @@ -1018,6 +1032,7 @@ static void readTokenFullRaw (tokenInfo *const token, bool include_newlines, vSt
(t) == TOKEN_OPEN_CURLY)
/* these cannot be the start or end of a statement */
#define IS_BINARY_OPERATOR(t) ((t) == TOKEN_EQUAL_SIGN || \
(t) == TOKEN_ARROW || \
(t) == TOKEN_COLON || \
(t) == TOKEN_PERIOD || \
(t) == TOKEN_STAR || \
Expand Down Expand Up @@ -1129,33 +1144,55 @@ static void readToken (tokenInfo *const token)
* Token parsing functions
*/

static void skipArgumentList (tokenInfo *const token, bool include_newlines, vString *const repr)
static void parseMethodsInAnonymousClass (tokenInfo *const token)
{
int nest_level = 0;
tokenInfo *const anon_class = newToken ();
copyToken (anon_class, token, true);
anonGenerate (anon_class->string, "AnonymousClass", JSTAG_CLASS);
anon_class->type = TOKEN_IDENTIFIER;

bool has_methods = parseMethods (token, anon_class, false);

if (has_methods)
makeJsTagCommon (anon_class, JSTAG_CLASS, NULL, NULL, true);

deleteToken (anon_class);
}

static void skipArgumentList (tokenInfo *const token, bool include_newlines, vString *const repr)
{
if (isType (token, TOKEN_OPEN_PAREN)) /* arguments? */
{
nest_level++;
int nest_level = 1;
if (repr)
vStringPut (repr, '(');

tokenType prev_token_type = token->type;
while (nest_level > 0 && ! isType (token, TOKEN_EOF))
{
readTokenFull (token, false, repr);
if (isType (token, TOKEN_OPEN_PAREN))
nest_level++;
else if (isType (token, TOKEN_CLOSE_PAREN))
nest_level--;
else if (isType (token, TOKEN_OPEN_CURLY))
{
if (prev_token_type == TOKEN_ARROW)
parseBlock (token, NULL);
else
parseMethodsInAnonymousClass (token);
}
else if (isKeyword (token, KEYWORD_function))
parseFunction (token);

prev_token_type = token->type;
}
readTokenFull (token, include_newlines, NULL);
}
}

static void skipArrayList (tokenInfo *const token, bool include_newlines)
{
int nest_level = 0;

/*
* Handle square brackets
* var name[1]
Expand All @@ -1164,14 +1201,24 @@ static void skipArrayList (tokenInfo *const token, bool include_newlines)

if (isType (token, TOKEN_OPEN_SQUARE)) /* arguments? */
{
nest_level++;
int nest_level = 1;
tokenType prev_token_type = token->type;
while (nest_level > 0 && ! isType (token, TOKEN_EOF))
{
readToken (token);
if (isType (token, TOKEN_OPEN_SQUARE))
nest_level++;
else if (isType (token, TOKEN_CLOSE_SQUARE))
nest_level--;
else if (isType (token, TOKEN_OPEN_CURLY))
{
if (prev_token_type == TOKEN_ARROW)
parseBlock (token, NULL);
else
parseMethodsInAnonymousClass (token);
}

prev_token_type = token->type;
}
readTokenFull (token, include_newlines, NULL);
}
Expand Down Expand Up @@ -1594,15 +1641,17 @@ static bool parseMethods (tokenInfo *const token, const tokenInfo *const class,
{
TRACE_ENTER_TEXT("token is '%s' of type %s in classToken '%s' of type %s (es6: %s)",
vStringValue(token->string), tokenTypeName (token->type),
vStringValue(class->string), tokenTypeName (class->type),
class == NULL ? "none" : vStringValue(class->string),
class == NULL ? "none" : tokenTypeName (class->type),
is_es6_class? "yes": "no");

tokenInfo *const name = newToken ();
bool has_methods = false;
vString *saveScope = vStringNew ();

vStringCopy (saveScope, token->scope);
addToScope (token, class->string);
if (class != NULL)
addToScope (token, class->string);

/*
* This deals with these formats
Expand Down Expand Up @@ -1741,10 +1790,11 @@ static bool parseMethods (tokenInfo *const token, const tokenInfo *const class,
if (isKeyword (token, KEYWORD_async))
readToken (token);
}

vString * signature = vStringNew ();
if ( is_shorthand || isKeyword (token, KEYWORD_function) )
{
TRACE_PRINT("Seems to be a function or shorthand");
vString *const signature = vStringNew ();

if (! is_shorthand)
{
Expand All @@ -1761,6 +1811,7 @@ static bool parseMethods (tokenInfo *const token, const tokenInfo *const class,
skipArgumentList(token, false, signature);
}

function:
if (isType (token, TOKEN_OPEN_CURLY))
{
has_methods = true;
Expand All @@ -1785,12 +1836,11 @@ static bool parseMethods (tokenInfo *const token, const tokenInfo *const class,
if (! is_es6_class)
readToken (token);
}

vStringDelete (signature);
}
else if (! is_es6_class)
{
bool has_child_methods = false;
tokenInfo *saved_token = newToken ();

/* skip whatever is the value */
while (! isType (token, TOKEN_COMMA) &&
Expand All @@ -1805,17 +1855,34 @@ static bool parseMethods (tokenInfo *const token, const tokenInfo *const class,
}
else if (isType (token, TOKEN_OPEN_PAREN))
{
skipArgumentList (token, false, NULL);
vStringClear (signature);
skipArgumentList (token, false, signature);
}
else if (isType (token, TOKEN_OPEN_SQUARE))
{
skipArrayList (token, false);
}
else if (isType (token, TOKEN_ARROW))
{
TRACE_PRINT("Seems to be an anonymous function");
if (vStringIsEmpty (signature) &&
isType (saved_token, TOKEN_IDENTIFIER))
{
vStringPut (signature, '(');
vStringCat (signature, saved_token->string);
vStringPut (signature, ')');
}
readToken (token);
deleteToken (saved_token);
goto function;
}
else
{
copyToken (saved_token, token, true);
readToken (token);
}
}
deleteToken (saved_token);

has_methods = true;
if (has_child_methods)
Expand All @@ -1828,6 +1895,8 @@ static bool parseMethods (tokenInfo *const token, const tokenInfo *const class,
makeJsTag (name, JSTAG_FIELD, NULL, NULL);
parseLine (token, true);
}

vStringDelete (signature);
}
else
{
Expand Down

0 comments on commit e325816

Please sign in to comment.