21
21
*
22
22
*/
23
23
24
- /*jslint regexp: true */
25
-
26
24
/**
27
25
* Set of utilities for simple parsing of JS text.
28
26
*/
29
27
define ( function ( require , exports , module ) {
30
28
"use strict" ;
31
29
32
- var _ = require ( "thirdparty/lodash" ) ;
30
+ var _ = require ( "thirdparty/lodash" ) ,
31
+ Acorn = require ( "thirdparty/acorn/dist/acorn" ) ,
32
+ AcornLoose = require ( "thirdparty/acorn/dist/acorn_loose" ) ,
33
+ ASTWalker = require ( "thirdparty/acorn/dist/walk" ) ;
33
34
34
35
// Load brackets modules
35
36
var CodeMirror = require ( "thirdparty/CodeMirror/lib/codemirror" ) ,
@@ -47,17 +48,6 @@ define(function (require, exports, module) {
47
48
*/
48
49
var _changedDocumentTracker = new ChangedDocumentTracker ( ) ;
49
50
50
- /**
51
- * Function matching regular expression. Recognizes the forms:
52
- * "function functionName()", "functionName = function()", and
53
- * "functionName: function()".
54
- *
55
- * Note: JavaScript identifier matching is not strictly to spec. This
56
- * RegExp matches any sequence of characters that is not whitespace.
57
- * @type {RegExp }
58
- */
59
- var _functionRegExp = / ( f u n c t i o n \s + ( [ $ _ A - Z a - z \u007F - \uFFFF ] [ $ _ A - Z a - z 0 - 9 \u007F - \uFFFF ] * ) \s * ( \( [ ^ ) ] * \) ) ) | ( ( [ $ _ A - Z a - z \u007F - \uFFFF ] [ $ _ A - Z a - z 0 - 9 \u007F - \uFFFF ] * ) \s * [: = ] \s * f u n c t i o n \s * ( \( [ ^ ) ] * \) ) ) / g;
60
-
61
51
/**
62
52
* @private
63
53
* Return an object mapping function name to offset info for all functions in the specified text.
@@ -66,21 +56,112 @@ define(function (require, exports, module) {
66
56
* @return {Object.<string, Array.<{offsetStart: number, offsetEnd: number}> }
67
57
*/
68
58
function _findAllFunctionsInText ( text ) {
69
- var results = { } ,
59
+ var AST ,
60
+ results = { } ,
70
61
functionName ,
62
+ resultNode ,
63
+ memberPrefix ,
71
64
match ;
72
-
65
+
73
66
PerfUtils . markStart ( PerfUtils . JSUTILS_REGEXP ) ;
74
-
75
- while ( ( match = _functionRegExp . exec ( text ) ) !== null ) {
76
- functionName = ( match [ 2 ] || match [ 5 ] ) . trim ( ) ;
77
-
67
+
68
+ try {
69
+ AST = Acorn . parse ( text , { locations : true } ) ;
70
+ } catch ( e ) {
71
+ AST = AcornLoose . parse_dammit ( text , { locations : true } ) ;
72
+ }
73
+
74
+ function _addResult ( node , offset , prefix ) {
75
+ memberPrefix = prefix ? prefix + " - " : "" ;
76
+ resultNode = node . id || node . key || node ;
77
+ functionName = resultNode . name ;
78
78
if ( ! Array . isArray ( results [ functionName ] ) ) {
79
79
results [ functionName ] = [ ] ;
80
80
}
81
81
82
- results [ functionName ] . push ( { offsetStart : match . index } ) ;
82
+ results [ functionName ] . push (
83
+ {
84
+ offsetStart : offset || node . start ,
85
+ label : memberPrefix ? memberPrefix + functionName : null ,
86
+ location : resultNode . loc
87
+ }
88
+ ) ;
83
89
}
90
+
91
+ ASTWalker . simple ( AST , {
92
+ /*
93
+ function <functionName> () {}
94
+ */
95
+ FunctionDeclaration : function ( node ) {
96
+ // As acorn_loose marks identifier names with '✖' under erroneous declarations
97
+ // we should have a check to discard such 'FunctionDeclaration' nodes
98
+ if ( node . id . name !== '✖' ) {
99
+ _addResult ( node ) ;
100
+ }
101
+ } ,
102
+ /*
103
+ class <className> () {}
104
+ */
105
+ ClassDeclaration : function ( node ) {
106
+ _addResult ( node ) ;
107
+ ASTWalker . simple ( node , {
108
+ /*
109
+ class <className> () {
110
+ <methodName> () {
111
+
112
+ }
113
+ }
114
+ */
115
+ MethodDefinition : function ( methodNode ) {
116
+ _addResult ( methodNode , methodNode . key . start , node . id . name ) ;
117
+ }
118
+ } ) ;
119
+ } ,
120
+ /*
121
+ var <functionName> = function () {}
122
+
123
+ or
124
+
125
+ var <functionName> = () => {}
126
+ */
127
+ VariableDeclarator : function ( node ) {
128
+ if ( node . init && ( node . init . type === "FunctionExpression" || node . init . type === "ArrowFunctionExpression" ) ) {
129
+ _addResult ( node ) ;
130
+ }
131
+ } ,
132
+ /*
133
+ SomeFunction.prototype.<functionName> = function () {}
134
+ */
135
+ AssignmentExpression : function ( node ) {
136
+ if ( node . right && node . right . type === "FunctionExpression" ) {
137
+ if ( node . left && node . left . type === "MemberExpression" && node . left . property ) {
138
+ _addResult ( node . left . property ) ;
139
+ }
140
+ }
141
+ } ,
142
+ /*
143
+ {
144
+ <functionName>: function() {}
145
+ }
146
+ */
147
+ Property : function ( node ) {
148
+ if ( node . value && node . value . type === "FunctionExpression" ) {
149
+ if ( node . key && node . key . type === "Identifier" ) {
150
+ _addResult ( node . key ) ;
151
+ }
152
+ }
153
+ } ,
154
+ /*
155
+ <functionName>: function() {}
156
+ */
157
+ LabeledStatement : function ( node ) {
158
+ if ( node . body && node . body . type === "FunctionDeclaration" ) {
159
+ if ( node . label ) {
160
+ _addResult ( node . label ) ;
161
+ }
162
+ }
163
+ }
164
+ } ) ;
84
165
85
166
PerfUtils . addMeasurement ( PerfUtils . JSUTILS_REGEXP ) ;
86
167
@@ -415,8 +496,13 @@ define(function (require, exports, module) {
415
496
var endOffset = _getFunctionEndOffset ( text , funcEntry . offsetStart ) ;
416
497
result . push ( {
417
498
name : functionName ,
499
+ label : funcEntry . label ,
418
500
lineStart : StringUtils . offsetToLineNum ( lines , funcEntry . offsetStart ) ,
419
- lineEnd : StringUtils . offsetToLineNum ( lines , endOffset )
501
+ lineEnd : StringUtils . offsetToLineNum ( lines , endOffset ) ,
502
+ nameLineStart : funcEntry . location . start . line - 1 ,
503
+ nameLineEnd : funcEntry . location . end . line - 1 ,
504
+ columnStart : funcEntry . location . start . column ,
505
+ columnEnd : funcEntry . location . end . column
420
506
} ) ;
421
507
} ) ;
422
508
}
0 commit comments