Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(814)

Unified Diff: third_party/WebKit/Source/devtools/front_end/formatter_worker/JavaScriptOutline.js

Issue 2770123002: DevTools: produce javascript outline for javascript classess (Closed)
Patch Set: missing build files Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/devtools/front_end/formatter_worker/JavaScriptOutline.js
diff --git a/third_party/WebKit/Source/devtools/front_end/formatter_worker/JavaScriptOutline.js b/third_party/WebKit/Source/devtools/front_end/formatter_worker/JavaScriptOutline.js
index f3a6c1bcccbd127d383f222171dc5da340e293e3..30713c4dd49a51d9594485cd213f2b2b17ee104c 100644
--- a/third_party/WebKit/Source/devtools/front_end/formatter_worker/JavaScriptOutline.js
+++ b/third_party/WebKit/Source/devtools/front_end/formatter_worker/JavaScriptOutline.js
@@ -5,78 +5,156 @@
* @param {string} content
*/
FormatterWorker.javaScriptOutline = function(content) {
- var chunkSize = 100000; // characters per data chunk
+ var chunkSize = 100000;
var outlineChunk = [];
- var previousIdentifier = null;
- var previousToken = null;
- var processedChunkCharacters = 0;
- var addedFunction = false;
- var isReadingArguments = false;
- var argumentsText = '';
- var currentFunction = null;
- var tokenizer = new FormatterWorker.AcornTokenizer(content);
- var AT = FormatterWorker.AcornTokenizer;
+ var lastReportedOffset = 0;
- while (tokenizer.peekToken()) {
- var token = /** @type {!Acorn.TokenOrComment} */ (tokenizer.nextToken());
- if (AT.lineComment(token) || AT.blockComment(token))
- continue;
+ var ast;
+ try {
+ ast = acorn.parse(content, {ranges: false, ecmaVersion: 8});
+ } catch (e) {
+ ast = acorn.parse_dammit(content, {ranges: false, ecmaVersion: 8});
+ }
- var tokenValue = content.substring(token.start, token.end);
+ var textCursor = new TextUtils.TextCursor(content.computeLineEndings());
+ var walker = new FormatterWorker.ESTreeWalker(beforeVisit);
+ walker.walk(ast);
+ postMessage({chunk: outlineChunk, isLastChunk: true});
- if (AT.identifier(token) && previousToken &&
- (AT.identifier(previousToken, 'get') || AT.identifier(previousToken, 'set'))) {
- currentFunction = {
- line: tokenizer.tokenLineStart(),
- column: tokenizer.tokenColumnStart(),
- name: previousToken.value + ' ' + tokenValue
- };
- addedFunction = true;
- previousIdentifier = null;
- } else if (AT.identifier(token)) {
- previousIdentifier = tokenValue;
- if (tokenValue && previousToken && AT.keyword(previousToken, 'function')) {
- // A named function: "function f...".
- currentFunction = {line: tokenizer.tokenLineStart(), column: tokenizer.tokenColumnStart(), name: tokenValue};
- addedFunction = true;
- previousIdentifier = null;
- }
+ /**
+ * @param {!ESTree.Node} node
+ */
+ function beforeVisit(node) {
+ if (node.type === 'ClassDeclaration') {
+ reportClass(/** @type {!ESTree.Node} */ (node.id));
+ } else if (node.type === 'VariableDeclarator' && isClassNode(node.init)) {
+ reportClass(/** @type {!ESTree.Node} */ (node.id));
+ } else if (node.type === 'AssignmentExpression' && isIdentifier(node.left) && isClassNode(node.right)) {
+ reportClass(/** @type {!ESTree.Node} */ (node.left));
+ } else if (node.type === 'FunctionDeclaration') {
+ reportFunction(/** @type {!ESTree.Node} */ (node.id), node);
+ } else if (node.type === 'VariableDeclarator' && isFunctionNode(node.init)) {
+ reportFunction(/** @type {!ESTree.Node} */ (node.id), /** @type {!ESTree.Node} */ (node.init));
+ } else if (node.type === 'AssignmentExpression' && isIdentifier(node.left) && isFunctionNode(node.right)) {
+ reportFunction(/** @type {!ESTree.Node} */ (node.left), /** @type {!ESTree.Node} */ (node.right));
} else if (
- AT.keyword(token, 'function') && previousIdentifier && previousToken && AT.punctuator(previousToken, ':=')) {
- // Anonymous function assigned to an identifier: "...f = function..."
- // or "funcName: function...".
- currentFunction = {
- line: tokenizer.tokenLineStart(),
- column: tokenizer.tokenColumnStart(),
- name: previousIdentifier
- };
- addedFunction = true;
- previousIdentifier = null;
- } else if (AT.punctuator(token, '.') && previousToken && AT.identifier(previousToken)) {
- previousIdentifier += '.';
- } else if (AT.punctuator(token, '(') && addedFunction) {
- isReadingArguments = true;
+ (node.type === 'MethodDefinition' || node.type === 'Property') && isIdentifier(node.key) &&
dgozman 2017/03/24 01:00:00 String literal as a computed property key.
lushnikov 2017/03/24 19:19:36 Done.
+ isFunctionNode(node.value)) {
dgozman 2017/03/24 01:00:00 Property could be a class.
lushnikov 2017/03/24 19:19:37 Done.
+ var namePrefix = [];
+ if (node.kind === 'get' || node.kind === 'set')
+ namePrefix.push(node.kind);
+ if (node.static)
+ namePrefix.push('static');
+ reportFunction(node.key, node.value, namePrefix.join(' '));
}
- if (isReadingArguments && tokenValue)
- argumentsText += tokenValue;
+ }
- if (AT.punctuator(token, ')') && isReadingArguments) {
- addedFunction = false;
- isReadingArguments = false;
- currentFunction.arguments = argumentsText.replace(/,[\r\n\s]*/g, ', ').replace(/([^,])[\r\n\s]+/g, '$1');
- argumentsText = '';
- outlineChunk.push(currentFunction);
- }
+ /**
+ * @param {!ESTree.Node} nameNode
+ */
+ function reportClass(nameNode) {
+ var name = 'class ' + stringifyIdentifier(nameNode);
+ textCursor.advance(nameNode.start);
+ addOutlineItem({
+ name: name,
+ line: textCursor.lineNumber(),
+ column: textCursor.columnNumber(),
+ });
+ }
+
+ /**
+ * @param {!ESTree.Node} nameNode
+ * @param {!ESTree.Node} functionNode
+ * @param {string=} namePrefix
+ */
+ function reportFunction(nameNode, functionNode, namePrefix) {
+ var name = stringifyIdentifier(nameNode);
+ if (functionNode.generator)
+ name = '*' + name;
+ if (namePrefix)
+ name = namePrefix + ' ' + name;
+ if (functionNode.async)
+ name = 'async ' + name;
+
+ textCursor.advance(nameNode.start);
+ addOutlineItem({
+ name: name,
+ line: textCursor.lineNumber(),
+ column: textCursor.columnNumber(),
+ arguments: stringifyArguments(/** @type {!Array<!ESTree.Node>} */ (functionNode.params))
+ });
+ }
+
+ /**
+ * @param {(!ESTree.Node|undefined)} node
+ * @return {boolean}
+ */
+ function isIdentifier(node) {
dgozman 2017/03/24 01:00:00 isName
lushnikov 2017/03/24 19:19:36 Done.
+ if (!node)
+ return false;
+ return node.type === 'Identifier' || node.type === 'MemberExpression';
dgozman 2017/03/24 01:00:00 && !node.computed
lushnikov 2017/03/24 19:19:36 Done.
+ }
- previousToken = token;
- processedChunkCharacters += token.end - token.start;
+ /**
+ * @param {(!ESTree.Node|undefined)} node
+ * @return {boolean}
+ */
+ function isFunctionNode(node) {
+ if (!node)
+ return false;
+ return node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression';
+ }
- if (processedChunkCharacters >= chunkSize) {
- postMessage({chunk: outlineChunk, isLastChunk: false});
- outlineChunk = [];
- processedChunkCharacters = 0;
+ /**
+ * @param {(!ESTree.Node|undefined)} node
+ * @return {boolean}
+ */
+ function isClassNode(node) {
+ return !!node && node.type === 'ClassExpression';
+ }
+
+ /**
+ * @param {!ESTree.Node} node
+ * @return {string}
+ */
+ function stringifyIdentifier(node) {
+ var path = [];
+ while (node.type === 'MemberExpression') {
+ path.push(node.property.name);
dgozman 2017/03/24 01:00:00 Cannot get property "name" of undefined
lushnikov 2017/03/24 19:19:37 Done.
+ node = /** @type {!ESTree.Node} */ (node.object);
}
+ console.assert(node.type === 'Identifier', 'Cannot extract identifier from unknown type: ' + node.type);
dgozman 2017/03/24 01:00:00 (a || b).c = 2;
lushnikov 2017/03/24 19:19:37 Done.
+ path.push(node.name);
+ path.reverse();
+ return path.join('.');
}
- postMessage({chunk: outlineChunk, isLastChunk: true});
+ /**
+ * @param {!Array<!ESTree.Node>} params
+ * @return {string}
+ */
+ function stringifyArguments(params) {
+ var result = [];
+ for (var param of params) {
+ if (param.type === 'Identifier')
+ result.push(param.name);
+ else if (param.type === 'RestElement' && param.argument.type === 'Identifier')
+ result.push('...' + param.argument.name);
+ else
+ console.error('Error: unexpected function parameter type: ' + param.type);
+ }
+ return '(' + result.join(', ') + ')';
+ }
+
+ /**
+ * @param {{name: string, line: number, column: number, arguments: (string|undefined)}} item
+ */
+ function addOutlineItem(item) {
+ outlineChunk.push(item);
+ if (textCursor.offset() - lastReportedOffset < chunkSize)
+ return;
+ postMessage({chunk: outlineChunk, isLastChunk: false});
+ outlineChunk = [];
+ lastReportedOffset = textCursor.offset();
+ }
};

Powered by Google App Engine
This is Rietveld 408576698