From e95aea7a6b664998addb3c96e72dd5416af1d52c Mon Sep 17 00:00:00 2001 From: Swagatam Mitra Date: Mon, 28 Aug 2017 10:31:42 +0530 Subject: [PATCH] JSUtils modification to handle es6 constructs (#13635) * JSUtils modification to handle es6 constructs * Addressed review comments and added es6 test cases * Address Code review comments and add support for ArrowFunctionExpressions with new test case * Update comments * Refactor to remove extra variables --- Gruntfile.js | 8 + .../default/QuickOpenJavaScript/main.js | 4 +- src/language/JSUtils.js | 130 ++++- src/npm-shrinkwrap.json | 457 ++++++++++++++++++ src/package.json | 1 + .../JSUtils-test-files/es6-async-arrow.js | 4 + test/spec/JSUtils-test-files/es6-classes.js | 10 + .../JSUtils-test-files/es6-getter-setter.js | 11 + .../JSUtils-test-files/es6-inheritance.js | 13 + .../JSUtils-test-files/es6-static-methods.js | 5 + test/spec/JSUtils-test.js | 108 ++++- 11 files changed, 700 insertions(+), 51 deletions(-) create mode 100644 src/npm-shrinkwrap.json create mode 100644 test/spec/JSUtils-test-files/es6-async-arrow.js create mode 100644 test/spec/JSUtils-test-files/es6-classes.js create mode 100644 test/spec/JSUtils-test-files/es6-getter-setter.js create mode 100644 test/spec/JSUtils-test-files/es6-inheritance.js create mode 100644 test/spec/JSUtils-test-files/es6-static-methods.js diff --git a/Gruntfile.js b/Gruntfile.js index d6f39144b68..c434d1fdc31 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -138,6 +138,14 @@ module.exports = function (grunt) { src: [ 'less/dist/less.min.js' ] + }, + { + expand: true, + dest: 'src/thirdparty/acorn', + cwd: 'src/node_modules/acorn', + src: [ + 'dist/{,*/}*' + ] } ] } diff --git a/src/extensions/default/QuickOpenJavaScript/main.js b/src/extensions/default/QuickOpenJavaScript/main.js index bcd0909a3c8..c14801b096a 100644 --- a/src/extensions/default/QuickOpenJavaScript/main.js +++ b/src/extensions/default/QuickOpenJavaScript/main.js @@ -65,9 +65,7 @@ define(function (require, exports, module) { var lines = docText.split("\n"); var functions = JSUtils.findAllMatchingFunctionsInText(docText, "*"); functions.forEach(function (funcEntry) { - var chFrom = lines[funcEntry.lineStart].indexOf(funcEntry.name); - var chTo = chFrom + funcEntry.name.length; - functionList.push(new FileLocation(null, funcEntry.lineStart, chFrom, chTo, funcEntry.name)); + functionList.push(new FileLocation(null, funcEntry.nameLineStart, funcEntry.columnStart, funcEntry.columnEnd, funcEntry.label || funcEntry.name)); }); return functionList; } diff --git a/src/language/JSUtils.js b/src/language/JSUtils.js index e5c091b6150..8f6bc9eac44 100644 --- a/src/language/JSUtils.js +++ b/src/language/JSUtils.js @@ -21,15 +21,16 @@ * */ -/*jslint regexp: true */ - /** * Set of utilities for simple parsing of JS text. */ define(function (require, exports, module) { "use strict"; - var _ = require("thirdparty/lodash"); + var _ = require("thirdparty/lodash"), + Acorn = require("thirdparty/acorn/dist/acorn"), + AcornLoose = require("thirdparty/acorn/dist/acorn_loose"), + ASTWalker = require("thirdparty/acorn/dist/walk"); // Load brackets modules var CodeMirror = require("thirdparty/CodeMirror/lib/codemirror"), @@ -47,17 +48,6 @@ define(function (require, exports, module) { */ var _changedDocumentTracker = new ChangedDocumentTracker(); - /** - * Function matching regular expression. Recognizes the forms: - * "function functionName()", "functionName = function()", and - * "functionName: function()". - * - * Note: JavaScript identifier matching is not strictly to spec. This - * RegExp matches any sequence of characters that is not whitespace. - * @type {RegExp} - */ - var _functionRegExp = /(function\s+([$_A-Za-z\u007F-\uFFFF][$_A-Za-z0-9\u007F-\uFFFF]*)\s*(\([^)]*\)))|(([$_A-Za-z\u007F-\uFFFF][$_A-Za-z0-9\u007F-\uFFFF]*)\s*[:=]\s*function\s*(\([^)]*\)))/g; - /** * @private * 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) { * @return {Object.} */ function _findAllFunctionsInText(text) { - var results = {}, + var AST, + results = {}, functionName, + resultNode, + memberPrefix, match; - + PerfUtils.markStart(PerfUtils.JSUTILS_REGEXP); - - while ((match = _functionRegExp.exec(text)) !== null) { - functionName = (match[2] || match[5]).trim(); - + + try { + AST = Acorn.parse(text, {locations: true}); + } catch (e) { + AST = AcornLoose.parse_dammit(text, {locations: true}); + } + + function _addResult(node, offset, prefix) { + memberPrefix = prefix ? prefix + " - " : ""; + resultNode = node.id || node.key || node; + functionName = resultNode.name; if (!Array.isArray(results[functionName])) { results[functionName] = []; } - results[functionName].push({offsetStart: match.index}); + results[functionName].push( + { + offsetStart: offset || node.start, + label: memberPrefix ? memberPrefix + functionName : null, + location: resultNode.loc + } + ); } + + ASTWalker.simple(AST, { + /* + function () {} + */ + FunctionDeclaration: function (node) { + // As acorn_loose marks identifier names with '✖' under erroneous declarations + // we should have a check to discard such 'FunctionDeclaration' nodes + if (node.id.name !== '✖') { + _addResult(node); + } + }, + /* + class () {} + */ + ClassDeclaration: function (node) { + _addResult(node); + ASTWalker.simple(node, { + /* + class () { + () { + + } + } + */ + MethodDefinition: function (methodNode) { + _addResult(methodNode, methodNode.key.start, node.id.name); + } + }); + }, + /* + var = function () {} + + or + + var = () => {} + */ + VariableDeclarator: function (node) { + if (node.init && (node.init.type === "FunctionExpression" || node.init.type === "ArrowFunctionExpression")) { + _addResult(node); + } + }, + /* + SomeFunction.prototype. = function () {} + */ + AssignmentExpression: function (node) { + if (node.right && node.right.type === "FunctionExpression") { + if (node.left && node.left.type === "MemberExpression" && node.left.property) { + _addResult(node.left.property); + } + } + }, + /* + { + : function() {} + } + */ + Property: function (node) { + if (node.value && node.value.type === "FunctionExpression") { + if (node.key && node.key.type === "Identifier") { + _addResult(node.key); + } + } + }, + /* + : function() {} + */ + LabeledStatement: function (node) { + if (node.body && node.body.type === "FunctionDeclaration") { + if (node.label) { + _addResult(node.label); + } + } + } + }); PerfUtils.addMeasurement(PerfUtils.JSUTILS_REGEXP); @@ -415,8 +496,13 @@ define(function (require, exports, module) { var endOffset = _getFunctionEndOffset(text, funcEntry.offsetStart); result.push({ name: functionName, + label: funcEntry.label, lineStart: StringUtils.offsetToLineNum(lines, funcEntry.offsetStart), - lineEnd: StringUtils.offsetToLineNum(lines, endOffset) + lineEnd: StringUtils.offsetToLineNum(lines, endOffset), + nameLineStart: funcEntry.location.start.line - 1, + nameLineEnd: funcEntry.location.end.line - 1, + columnStart: funcEntry.location.start.column, + columnEnd: funcEntry.location.end.column }); }); } diff --git a/src/npm-shrinkwrap.json b/src/npm-shrinkwrap.json new file mode 100644 index 00000000000..329914edd46 --- /dev/null +++ b/src/npm-shrinkwrap.json @@ -0,0 +1,457 @@ +{ + "name": "brackets-src", + "dependencies": { + "acorn": { + "version": "5.1.1", + "from": "acorn@>=5.0.1 <6.0.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.1.tgz", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "from": "ansi-regex@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" + }, + "ansi-styles": { + "version": "2.2.1", + "from": "ansi-styles@>=2.2.1 <3.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "optional": true + }, + "asap": { + "version": "2.0.5", + "from": "asap@>=2.0.3 <2.1.0", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.5.tgz", + "optional": true + }, + "asn1": { + "version": "0.2.3", + "from": "asn1@>=0.2.3 <0.3.0", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "optional": true + }, + "assert-plus": { + "version": "0.2.0", + "from": "assert-plus@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "optional": true + }, + "asynckit": { + "version": "0.4.0", + "from": "asynckit@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "optional": true + }, + "aws-sign2": { + "version": "0.6.0", + "from": "aws-sign2@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "optional": true + }, + "aws4": { + "version": "1.6.0", + "from": "aws4@>=1.2.1 <2.0.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "optional": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "from": "bcrypt-pbkdf@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "optional": true + }, + "boom": { + "version": "2.10.1", + "from": "boom@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" + }, + "caseless": { + "version": "0.11.0", + "from": "caseless@>=0.11.0 <0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", + "optional": true + }, + "chalk": { + "version": "1.1.3", + "from": "chalk@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "optional": true + }, + "codemirror": { + "version": "5.28.0", + "from": "codemirror@5.28.0", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.28.0.tgz" + }, + "combined-stream": { + "version": "1.0.5", + "from": "combined-stream@>=1.0.5 <1.1.0", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz" + }, + "commander": { + "version": "2.9.0", + "from": "commander@>=2.9.0 <3.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "optional": true + }, + "cryptiles": { + "version": "2.0.5", + "from": "cryptiles@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "optional": true + }, + "dashdash": { + "version": "1.14.1", + "from": "dashdash@>=1.12.0 <2.0.0", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "optional": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "optional": true + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "from": "delayed-stream@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + }, + "ecc-jsbn": { + "version": "0.1.1", + "from": "ecc-jsbn@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "optional": true + }, + "errno": { + "version": "0.1.4", + "from": "errno@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", + "optional": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "from": "escape-string-regexp@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "optional": true + }, + "extend": { + "version": "3.0.0", + "from": "extend@>=3.0.0 <3.1.0", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz", + "optional": true + }, + "extsprintf": { + "version": "1.0.2", + "from": "extsprintf@1.0.2", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz" + }, + "forever-agent": { + "version": "0.6.1", + "from": "forever-agent@>=0.6.1 <0.7.0", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "optional": true + }, + "form-data": { + "version": "2.1.2", + "from": "form-data@>=2.1.1 <2.2.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.2.tgz", + "optional": true + }, + "generate-function": { + "version": "2.0.0", + "from": "generate-function@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", + "optional": true + }, + "generate-object-property": { + "version": "1.2.0", + "from": "generate-object-property@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "optional": true + }, + "getpass": { + "version": "0.1.6", + "from": "getpass@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz", + "optional": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "optional": true + } + } + }, + "graceful-fs": { + "version": "4.1.11", + "from": "graceful-fs@>=4.1.2 <5.0.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "optional": true + }, + "graceful-readlink": { + "version": "1.0.1", + "from": "graceful-readlink@>=1.0.0", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "optional": true + }, + "har-validator": { + "version": "2.0.6", + "from": "har-validator@>=2.0.6 <2.1.0", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", + "optional": true + }, + "has-ansi": { + "version": "2.0.0", + "from": "has-ansi@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "optional": true + }, + "hawk": { + "version": "3.1.3", + "from": "hawk@>=3.1.3 <3.2.0", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "optional": true + }, + "hoek": { + "version": "2.16.3", + "from": "hoek@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" + }, + "http-signature": { + "version": "1.1.1", + "from": "http-signature@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "optional": true + }, + "image-size": { + "version": "0.5.1", + "from": "image-size@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.1.tgz", + "optional": true + }, + "is-my-json-valid": { + "version": "2.15.0", + "from": "is-my-json-valid@>=2.12.4 <3.0.0", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.15.0.tgz", + "optional": true + }, + "is-property": { + "version": "1.0.2", + "from": "is-property@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "optional": true + }, + "is-typedarray": { + "version": "1.0.0", + "from": "is-typedarray@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "optional": true + }, + "isstream": { + "version": "0.1.2", + "from": "isstream@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "optional": true + }, + "jodid25519": { + "version": "1.0.2", + "from": "jodid25519@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz", + "optional": true + }, + "jsbn": { + "version": "0.1.1", + "from": "jsbn@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "from": "json-schema@0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "optional": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "from": "json-stringify-safe@>=5.0.1 <5.1.0", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "optional": true + }, + "jsonpointer": { + "version": "4.0.1", + "from": "jsonpointer@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", + "optional": true + }, + "jsprim": { + "version": "1.3.1", + "from": "jsprim@>=1.2.2 <2.0.0", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.3.1.tgz", + "optional": true + }, + "less": { + "version": "2.7.2", + "from": "less@2.7.2", + "resolved": "https://registry.npmjs.org/less/-/less-2.7.2.tgz" + }, + "mime": { + "version": "1.3.4", + "from": "mime@>=1.2.11 <2.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", + "optional": true + }, + "mime-db": { + "version": "1.26.0", + "from": "mime-db@>=1.26.0 <1.27.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.26.0.tgz" + }, + "mime-types": { + "version": "2.1.14", + "from": "mime-types@>=2.1.7 <2.2.0", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.14.tgz" + }, + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "optional": true + }, + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "optional": true + }, + "oauth-sign": { + "version": "0.8.2", + "from": "oauth-sign@>=0.8.1 <0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "optional": true + }, + "pinkie": { + "version": "2.0.4", + "from": "pinkie@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "optional": true + }, + "pinkie-promise": { + "version": "2.0.1", + "from": "pinkie-promise@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "optional": true + }, + "promise": { + "version": "7.1.1", + "from": "promise@>=7.1.1 <8.0.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.1.1.tgz", + "optional": true + }, + "prr": { + "version": "0.0.0", + "from": "prr@>=0.0.0 <0.1.0", + "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz", + "optional": true + }, + "punycode": { + "version": "1.4.1", + "from": "punycode@>=1.4.1 <2.0.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "optional": true + }, + "qs": { + "version": "6.3.1", + "from": "qs@>=6.3.0 <6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.1.tgz", + "optional": true + }, + "request": { + "version": "2.79.0", + "from": "request@>=2.72.0 <3.0.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.79.0.tgz", + "optional": true + }, + "sntp": { + "version": "1.0.9", + "from": "sntp@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "optional": true + }, + "source-map": { + "version": "0.5.6", + "from": "source-map@>=0.5.3 <0.6.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "optional": true + }, + "sshpk": { + "version": "1.10.2", + "from": "sshpk@>=1.7.0 <2.0.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.10.2.tgz", + "optional": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "from": "assert-plus@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "optional": true + } + } + }, + "stringstream": { + "version": "0.0.5", + "from": "stringstream@>=0.0.4 <0.1.0", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "optional": true + }, + "strip-ansi": { + "version": "3.0.1", + "from": "strip-ansi@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "optional": true + }, + "supports-color": { + "version": "2.0.0", + "from": "supports-color@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "optional": true + }, + "tough-cookie": { + "version": "2.3.2", + "from": "tough-cookie@>=2.3.0 <2.4.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "optional": true + }, + "tunnel-agent": { + "version": "0.4.3", + "from": "tunnel-agent@>=0.4.1 <0.5.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "optional": true + }, + "tweetnacl": { + "version": "0.14.5", + "from": "tweetnacl@>=0.14.0 <0.15.0", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "optional": true + }, + "uuid": { + "version": "3.0.1", + "from": "uuid@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", + "optional": true + }, + "verror": { + "version": "1.3.6", + "from": "verror@1.3.6", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", + "optional": true + }, + "xtend": { + "version": "4.0.1", + "from": "xtend@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "optional": true + } + } +} diff --git a/src/package.json b/src/package.json index 042e24d1724..62df5849243 100644 --- a/src/package.json +++ b/src/package.json @@ -1,6 +1,7 @@ { "name": "brackets-src", "dependencies": { + "acorn": "5.1.1", "codemirror": "5.28.0", "less": "2.7.2" } diff --git a/test/spec/JSUtils-test-files/es6-async-arrow.js b/test/spec/JSUtils-test-files/es6-async-arrow.js new file mode 100644 index 00000000000..3f207a4e6a3 --- /dev/null +++ b/test/spec/JSUtils-test-files/es6-async-arrow.js @@ -0,0 +1,4 @@ +// Async fn def +var foo = { async bar() {} }; +// ArrowExpression usage to define function +var fooAgain = () => {}; \ No newline at end of file diff --git a/test/spec/JSUtils-test-files/es6-classes.js b/test/spec/JSUtils-test-files/es6-classes.js new file mode 100644 index 00000000000..7d53c205276 --- /dev/null +++ b/test/spec/JSUtils-test-files/es6-classes.js @@ -0,0 +1,10 @@ +class Shape { + constructor (id, x, y) { + this.id = id + this.move(x, y) + } + move (x, y) { + this.x = x + this.y = y + } +} \ No newline at end of file diff --git a/test/spec/JSUtils-test-files/es6-getter-setter.js b/test/spec/JSUtils-test-files/es6-getter-setter.js new file mode 100644 index 00000000000..0a26abb6d2a --- /dev/null +++ b/test/spec/JSUtils-test-files/es6-getter-setter.js @@ -0,0 +1,11 @@ +class Rectangle { + constructor (width, height) { + this._width = width + this._height = height + } + set width (width) { this._width = width } + get width () { return this._width } + set height (height) { this._height = height } + get height () { return this._height } + get area () { return this._width * this._height } +} \ No newline at end of file diff --git a/test/spec/JSUtils-test-files/es6-inheritance.js b/test/spec/JSUtils-test-files/es6-inheritance.js new file mode 100644 index 00000000000..7fd42d7ff89 --- /dev/null +++ b/test/spec/JSUtils-test-files/es6-inheritance.js @@ -0,0 +1,13 @@ +class Rectangle extends Shape { + constructor (id, x, y, width, height) { + super(id, x, y) + this.width = width + this.height = height + } +} +class Circle extends Shape { + constructor (id, x, y, radius) { + super(id, x, y) + this.radius = radius + } +} \ No newline at end of file diff --git a/test/spec/JSUtils-test-files/es6-static-methods.js b/test/spec/JSUtils-test-files/es6-static-methods.js new file mode 100644 index 00000000000..7660286e2b2 --- /dev/null +++ b/test/spec/JSUtils-test-files/es6-static-methods.js @@ -0,0 +1,5 @@ +class Rectangle extends Shape { + static defaultRectangle () { + return new Rectangle("default", 0, 0, 100, 100) + } +} \ No newline at end of file diff --git a/test/spec/JSUtils-test.js b/test/spec/JSUtils-test.js index 55bc36bac01..b09f3eaf864 100644 --- a/test/spec/JSUtils-test.js +++ b/test/spec/JSUtils-test.js @@ -45,13 +45,18 @@ define(function (require, exports, module) { return this.actual.functionName.trim() === expected; }; - var simpleJsFileEntry = FileSystem.getFileForPath(testPath + "/simple.js"); - var trickyJsFileEntry = FileSystem.getFileForPath(testPath + "/tricky.js"); - var invalidJsFileEntry = FileSystem.getFileForPath(testPath + "/invalid.js"); - var jQueryJsFileEntry = FileSystem.getFileForPath(testPath + "/jquery-1.7.js"); - var braceEndJsFileEntry = FileSystem.getFileForPath(testPath + "/braceEnd.js"); - var eofJsFileEntry = FileSystem.getFileForPath(testPath + "/eof.js"); - var eof2JsFileEntry = FileSystem.getFileForPath(testPath + "/eof2.js"); + var simpleJsFileEntry = FileSystem.getFileForPath(testPath + "/simple.js"); + var trickyJsFileEntry = FileSystem.getFileForPath(testPath + "/tricky.js"); + var invalidJsFileEntry = FileSystem.getFileForPath(testPath + "/invalid.js"); + var jQueryJsFileEntry = FileSystem.getFileForPath(testPath + "/jquery-1.7.js"); + var braceEndJsFileEntry = FileSystem.getFileForPath(testPath + "/braceEnd.js"); + var eofJsFileEntry = FileSystem.getFileForPath(testPath + "/eof.js"); + var eof2JsFileEntry = FileSystem.getFileForPath(testPath + "/eof2.js"); + var es6ClassesFileEntry = FileSystem.getFileForPath(testPath + "/es6-classes.js"); + var es6StaticsFileEntry = FileSystem.getFileForPath(testPath + "/es6-static-methods.js"); + var es6InheritanceFileEntry = FileSystem.getFileForPath(testPath + "/es6-inheritance.js"); + var es6GetterSetterFileEntry = FileSystem.getFileForPath(testPath + "/es6-getter-setter.js"); + var es6AsyncAndArrowFileEntry = FileSystem.getFileForPath(testPath + "/es6-async-arrow.js"); function init(spec, fileEntry) { if (fileEntry) { @@ -110,6 +115,76 @@ define(function (require, exports, module) { var result = JSUtils.findAllMatchingFunctionsInText(jsCode, functionName); expect(result.length).toBe(0); } + + it("should return correct start and end line numbers for es6 class definitions and methods", function () { + runs(function () { + doneLoading = false; + init(this, es6ClassesFileEntry); + }); + waitsFor(function () { return doneLoading; }, 1000); + + runs(function () { + expectFunctionRanges(this, this.fileJsContent, "Shape", [ {start: 0, end: 9} ]); + expectFunctionRanges(this, this.fileJsContent, "constructor", [ {start: 1, end: 4} ]); + expectFunctionRanges(this, this.fileJsContent, "move", [ {start: 5, end: 8} ]); + }); + }); + + it("should return correct start and end line numbers for es6 static class methods", function () { + runs(function () { + doneLoading = false; + init(this, es6StaticsFileEntry); + }); + waitsFor(function () { return doneLoading; }, 1000); + + runs(function () { + expectFunctionRanges(this, this.fileJsContent, "Rectangle", [ {start: 0, end: 4} ]); + expectFunctionRanges(this, this.fileJsContent, "defaultRectangle", [ {start: 1, end: 3} ]); + }); + }); + + it("should return correct start and end line numbers for es6 class inheritance", function () { + runs(function () { + doneLoading = false; + init(this, es6InheritanceFileEntry); + }); + waitsFor(function () { return doneLoading; }, 1000); + + runs(function () { + expectFunctionRanges(this, this.fileJsContent, "Rectangle", [ {start: 0, end: 6} ]); + expectFunctionRanges(this, this.fileJsContent, "Circle", [ {start: 7, end: 12} ]); + expectFunctionRanges(this, this.fileJsContent, "constructor", [ {start: 1, end: 5}, {start: 8, end: 11} ]); + }); + }); + + it("should return correct start and end line numbers for es6 class members getters/setters", function () { + runs(function () { + doneLoading = false; + init(this, es6GetterSetterFileEntry); + }); + waitsFor(function () { return doneLoading; }, 1000); + + runs(function () { + expectFunctionRanges(this, this.fileJsContent, "Rectangle", [ {start: 0, end: 10} ]); + expectFunctionRanges(this, this.fileJsContent, "constructor", [ {start: 1, end: 4} ]); + expectFunctionRanges(this, this.fileJsContent, "width", [ {start: 5, end: 5}, {start: 6, end: 6} ]); + expectFunctionRanges(this, this.fileJsContent, "height", [ {start: 7, end: 7}, {start: 8, end: 8} ]); + expectFunctionRanges(this, this.fileJsContent, "area", [ {start: 9, end: 9} ]); + }); + }); + + it("should return correct start and end line numbers for es6 async and arrow function expressions", function () { + runs(function () { + doneLoading = false; + init(this, es6AsyncAndArrowFileEntry); + }); + waitsFor(function () { return doneLoading; }, 1000); + + runs(function () { + expectFunctionRanges(this, this.fileJsContent, "bar", [ {start: 1, end: 1} ]); + expectFunctionRanges(this, this.fileJsContent, "fooAgain", [ {start: 3, end: 3} ]); + }); + }); it("should return correct start and end line numbers for simple functions", function () { runs(function () { @@ -181,25 +256,6 @@ define(function (require, exports, module) { }); }); - it("should ignore identifiers with whitespace", function () { - runs(function () { - doneLoading = false; - init(this, simpleJsFileEntry); - }); - waitsFor(function () { return doneLoading; }, 1000); - - runs(function () { - var negativeTests = ["invalid", "identifier", "invalid identifier"], - result, - content = this.fileJsContent; - - negativeTests.forEach(function (name) { - result = JSUtils.findAllMatchingFunctionsInText(content, name); - expect(result.length).toBe(0); - }); - }); - }); - it("should return correct start and end line numbers for prototype method declarations", function () { runs(function () { doneLoading = false;