From f52302e71d5d9754f5ce1d193b0ba4cda133d566 Mon Sep 17 00:00:00 2001 From: Jannon Date: Wed, 25 Jan 2012 14:17:22 -0800 Subject: [PATCH] Refactored parsing to allow for easily extending/overriding parseing behavior --- .gitignore | 3 + conf.json | 3 + jsdoc.js | 5 + plugins/jquery.ui.widget.js | 74 +++++ plugins/jquery.ui.widget.visitor.js | 89 ++++++ rhino_modules/jsdoc/src/defaultvisitor.js | 355 ++++++++++++++++++++++ rhino_modules/jsdoc/src/parser.js | 330 ++------------------ test/runner.js | 1 + test/t/cases/file.js | 1 + test/t/cases/modules/data/mod-1.js | 1 + test/t/cases/modules/data/mod-2.js | 1 + test/t/cases/modules/data/mod-3.js | 1 + test/t/jsdoc/src/handlers.js | 1 + test/t/jsdoc/src/parser.js | 2 + 14 files changed, 567 insertions(+), 300 deletions(-) create mode 100644 plugins/jquery.ui.widget.js create mode 100644 plugins/jquery.ui.widget.visitor.js create mode 100644 rhino_modules/jsdoc/src/defaultvisitor.js diff --git a/.gitignore b/.gitignore index 04ae28a8..a15ff22f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ build-files/java/build jsdoc.jar test/tutorials/out +.externalToolBuilders +.settings +.project diff --git a/conf.json b/conf.json index 2ff04b4c..f5fb9fb3 100644 --- a/conf.json +++ b/conf.json @@ -9,7 +9,10 @@ }, "plugins": [ + "plugins/jquery.ui.widget" ], + "nodeVisitor": "plugins/jquery.ui.widget.visitor", + "jsVersion": 180 } \ No newline at end of file diff --git a/jsdoc.js b/jsdoc.js index a0077d33..9de657ed 100644 --- a/jsdoc.js +++ b/jsdoc.js @@ -194,6 +194,11 @@ function main() { } } + // allow user-defined node visitor + if (!env.conf.nodeVisitor) { env.conf.nodeVisitor = "jsdoc/src/defaultvisitor"; } + var visitor = require(env.conf.nodeVisitor); + app.jsdoc.parser.setVisitor(visitor.NodeVisitor); + // any source file named package.json is treated special for (var i = 0, l = env.opts._.length; i < l; i++ ) { if (/\bpackage\.json$/i.test(env.opts._[i])) { diff --git a/plugins/jquery.ui.widget.js b/plugins/jquery.ui.widget.js new file mode 100644 index 00000000..228b5a40 --- /dev/null +++ b/plugins/jquery.ui.widget.js @@ -0,0 +1,74 @@ +/** + @overview This is just an test. + @module plugins/jquery.ui.widget + @author Jannon Frank jannonfrank.com + */ + +function printFields(e, depth) { + depth = depth || 0; + var prefix = ""; + for(var i = 0; i < depth; i++) { + prefix += "\t"; + } + + for (var f in e) { + if (e['hasOwnProperty'] && e.hasOwnProperty(f)) { + if (typeof e[f] != 'object') { + console.log(prefix + f + ": " + e[f] + " (" + typeof e[f] + "),"); + } else { + console.log(prefix + f + ": {"); + printFields(e[f], ++depth); + console.log(prefix + "}"); + } + } + } +} + + +/** + Make your descriptions more shoutier. + * +exports.newDoclet = function(e) { + console.log("----------"); + console.log("New Doclet"); + console.log("----------"); + printFields(e); +}; +exports.jsdocCommentFound = function(e) { + console.log("--------------------"); + console.log("JS Doc Comment Found"); + console.log("--------------------"); + printFields(e); +}; +exports.symbolFound = function(e) { + console.log("------------"); + console.log("Symbol Found"); + console.log("------------"); + printFields(e); +};*/ +/* +exports.fileBegin = function(e) { + console.log("----------"); + console.log("File Begin"); + console.log("----------"); + printFields(e); +}; +exports.fileComplete = function(e) { + console.log("-------------"); + console.log("File Complete"); + console.log("-------------"); + printFields(e); +}; +exports.beforeParse = function(e) { + console.log("------------"); + console.log("Before Parse"); + console.log("------------"); + printFields(e); +}; +exports.sourceFileFound = function(e) { + console.log("-----------------"); + console.log("Source File Found"); + console.log("-----------------"); + printFields(e); +}; +*/ \ No newline at end of file diff --git a/plugins/jquery.ui.widget.visitor.js b/plugins/jquery.ui.widget.visitor.js new file mode 100644 index 00000000..3c7faf6a --- /dev/null +++ b/plugins/jquery.ui.widget.visitor.js @@ -0,0 +1,89 @@ +/** + * @module jsdoc/src/jquery.ui.widget.visitor + */ + +function extend(obj, extObj) { + if (arguments.length > 2) { + for (var a = 1; a < arguments.length; a++) { + extend(obj, arguments[a]); + } + } else { + for (var field in extObj) { + obj[field] = extObj[field]; + } + } + return obj; +}; + +var base = require("jsdoc/src/defaultvisitor").NodeVisitor, + Token = Packages.org.mozilla.javascript.Token, + parser = require("jsdoc/src/parser").Parser; + +exports.NodeVisitor = extend({}, base, { + visitOtherNode: function(node) { + var e, + commentSrc, + currentParser = parser.currentParser, + currentSourceName = parser.currentSourceName, + visitor = currentParser.getVisitor(); + if (node.type == Token.GETPROP) { + var src = node.toSource(), + parent = node.getParent(); + if (src == "$.widget" && parent && parent.type == Token.CALL) { + e = { + id: 'astnode'+parent.hashCode(), // the id of the call node + comment: String(parent.jsDoc||'@undocumented'), + lineno: parent.getLineno(), + filename: currentSourceName, + astnode: parent, + code: aboutWidgetNode(parent) + }; + //debugPrintNode(node); + //debugPrintNodeEnclosure(node); + if ( visitor.isValidJsdoc(e.comment) ) { + currentParser.fire('symbolFound', e, currentParser); + } + + if (e.doclet) { + currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet + } + } + } + } +}); + +/** + * Gets the name and type of the widget. + * @private + * @memberof module:plugins/jquery.ui.widget.visitor.NodeVisitor + */ +function aboutWidgetNode(node) { + var name = node.getArguments().get(0).toSource(), + about = { + "name": "" + name.substring(1, name.length() - 1), + "type": "widget", + "node": node + }; + return about; +} + +function debugPrintNode(node, printSource) { + try { + if (printSource) { + console.log(node.getLineno() + ": " + Token.typeToName(node.type) + "- " + node.toSource()); + } else { + console.log(node.getLineno() + ": " + Token.typeToName(node.type)); + } + } catch(e) { + console.log("ERROR on line " + node.getLineno() + " - type: " + node.type); + } +} + +function debugPrintNodeEnclosure(node) { + console.log("Function: " + (node.getEnclosingFunction() && node.getEnclosingFunction().getName())); + console.log("Scope: " + node.getEnclosingScope()); + console.log("Parent: " + (node.getParent() && Token.typeToName(node.getParent().type))); + console.log("Left: " + Token.typeToName(node.left.type) + " - " + node.left.toSource()); + console.log("Right: " + Token.typeToName(node.right.type) + " - " + node.right.toSource()); + console.log("jsdoc: " + node.jsdoc); +} diff --git a/rhino_modules/jsdoc/src/defaultvisitor.js b/rhino_modules/jsdoc/src/defaultvisitor.js new file mode 100644 index 00000000..43cc69f4 --- /dev/null +++ b/rhino_modules/jsdoc/src/defaultvisitor.js @@ -0,0 +1,355 @@ +/** + * @module jsdoc/src/defaultvisitor + */ + +var Token = Packages.org.mozilla.javascript.Token, + parser = require("jsdoc/src/parser").Parser, + tkn = parser.tkn; + +/** + * Contains functions that do the bulk of the parsing work, visiting each of the + * nodes in the ast and finding those nodes relevant to documentation. It can + * be extended or overridden to support additional documentation requirements. + * For instance, to handle the "$.widget()" calls of the jQuery UI widget paradigm. + * + */ +exports.NodeVisitor = { + + visitScriptNode: function(node) { + var e, + commentSrc, + currentParser = parser.currentParser, + currentSourceName = parser.currentSourceName, + visitor = currentParser.getVisitor(); + + // note: ALL comments are seen in this block... + for each(var comment in node.comments.toArray()) { + if (comment.commentType !== Token.CommentType.JSDOC) { + continue; + } + + if (commentSrc = ''+comment.toSource()) { + + e = { + comment: commentSrc, + lineno: comment.getLineno(), + filename: currentSourceName + }; + + if ( visitor.isValidJsdoc(commentSrc) ) { + currentParser.fire('jsdocCommentFound', e, currentParser); + } + } + } + }, + + visitAssignNode: function(node) { + var e, + commentSrc, + currentParser = parser.currentParser, + currentSourceName = parser.currentSourceName, + visitor = currentParser.getVisitor(); + e = { + id: 'astnode'+node.hashCode(), // the id of the ASSIGN node + comment: String(node.jsDoc||'@undocumented'), + lineno: node.left.getLineno(), + filename: currentSourceName, + astnode: node, + code: visitor.aboutNode(node) + }; + + var basename = e.code.name.replace(/^([$a-z_][$a-z_0-9]*).*?$/i, '$1'); + + if (basename !== 'this') e.code.funcscope = currentParser.resolveVar(node, basename); + + if ( visitor.isValidJsdoc(e.comment) ) { + currentParser.fire('symbolFound', e, currentParser); + } + + if (e.doclet) { + currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet + } + }, + + visitColonNode: function(node) { + var e, + commentSrc, + currentParser = parser.currentParser, + currentSourceName = parser.currentSourceName, + visitor = currentParser.getVisitor(); + e = { + id: 'astnode'+node.hashCode(), // the id of the COLON node + comment: String(node.left.jsDoc||'@undocumented'), + lineno: node.left.getLineno(), + filename: currentSourceName, + astnode: node, + code: visitor.aboutNode(node) + }; + + if ( visitor.isValidJsdoc(e.comment) ) { + currentParser.fire('symbolFound', e, currentParser); + } + + if (e.doclet) { + currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet + } + + var parent = currentParser.resolvePropertyParent(node); + if (parent && parent.doclet.isEnum) { + if (!parent.doclet.properties) { parent.doclet.properties = []; } + // members of an enum inherit the enum's type + if (parent.doclet.type && !e.doclet.type) { e.doclet.type = parent.doclet.type; } + delete e.doclet.undocumented; + e.doclet.defaultvalue = e.doclet.meta.code.value; + parent.doclet.properties.push(e.doclet); + } + }, + + visitVarNode: function(node) { + var e, + commentSrc, + currentParser = parser.currentParser, + currentSourceName = parser.currentSourceName, + visitor = currentParser.getVisitor(); + if (node.variables) { + return true; // we'll get each var separately on future visits + } + + if (node.parent.variables.toArray()[0] === node) { // like /** blah */ var a=1, b=2, c=3; + // the first var assignment gets any jsDoc before the whole var series + node.jsDoc = node.parent.jsDoc; + } + + e = { + id: 'astnode'+node.hashCode(), // the id of the VARIABLE node + comment: String(node.jsDoc||'@undocumented'), + lineno: node.getLineno(), + filename: currentSourceName, + astnode: node, + code: visitor.aboutNode(node) + }; + + // keep track of vars in a function scope + if (node.enclosingFunction) { + var func = 'astnode'+node.enclosingFunction.hashCode(), + funcDoc = currentParser.refs[func]; + + if (funcDoc) { + funcDoc.meta.vars = funcDoc.meta.vars || []; + funcDoc.meta.vars.push(e.code.name); + } + } + + if ( visitor.isValidJsdoc(e.comment) ) { + currentParser.fire('symbolFound', e, currentParser); + } + + if (e.doclet) { + currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet + } + }, + + visitFunctionNode: function(node) { + var e, + commentSrc, + currentParser = parser.currentParser, + currentSourceName = parser.currentSourceName, + visitor = currentParser.getVisitor(); + e = { + id: 'astnode'+node.hashCode(), // the id of the FUNCTION node + comment: String(node.jsDoc||'@undocumented'), + lineno: node.getLineno(), + filename: currentSourceName, + astnode: node, + code: visitor.aboutNode(node) + }; + + e.code.name = (node.type == tkn.NAMEDFUNCTIONTATEMENT)? '' : String(node.name) || ''; + //console.log(':: e.code.name is '+e.code.name); + // keep track of vars in a function scope + if (node.enclosingFunction) { + var func = 'astnode'+node.enclosingFunction.hashCode(), + funcDoc = currentParser.refs[func]; + + if (funcDoc) { + funcDoc.meta.vars = funcDoc.meta.vars || []; + funcDoc.meta.vars.push(e.code.name); + } + } + + var basename = e.code.name.replace(/^([$a-z_][$a-z_0-9]*).*?$/i, '$1'); + e.code.funcscope = currentParser.resolveVar(node, basename); + + if ( visitor.isValidJsdoc(e.comment) ) { + currentParser.fire('symbolFound', e, currentParser); + } + + if (e.doclet) { + currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet + } + else if (!currentParser.refs['astnode'+e.code.node.hashCode()]) { // keep references to undocumented anonymous functions too as they might have scoped vars + currentParser.refs['astnode'+e.code.node.hashCode()] = { + longname: '', + meta: { code: e.code } + }; + } + }, + + visitOtherNode: function(node) { + //no-op + }, + + visitNode: function(node) { + var e, + commentSrc, + currentParser = parser.currentParser, + currentSourceName = parser.currentSourceName, + visitor = currentParser.getVisitor(); + + // look for stand-alone doc comments + if (node.type === Token.SCRIPT && node.comments) { + visitor.visitScriptNode(node); + } + else if (node.type === Token.ASSIGN) { + visitor.visitAssignNode(node); + } + else if (node.type === Token.COLON) { // assignment within an object literal + visitor.visitColonNode(node); + } + else if (node.type == Token.VAR || node.type == Token.LET || node.type == Token.CONST) { + visitor.visitVarNode(node); + } + else if (node.type == Token.FUNCTION || node.type == tkn.NAMEDFUNCTIONTATEMENT) { + visitor.visitFunctionNode(node); + } + else { + visitor.visitOtherNode(node); + } + + return true; + }, + isValidJsdoc: function(commentSrc) { + return commentSrc.indexOf('/***') !== 0; /*** ignore comments that start with many stars ***/ + }, + /** + * Attempts to find the name and type of the given node. + */ + aboutNode: function(node) { + about = {}; + + if (node.type == Token.FUNCTION || node.type == tkn.NAMEDFUNCTIONTATEMENT) { + about.name = node.type == tkn.NAMEDFUNCTIONTATEMENT? '' : '' + node.name; + about.type = 'function'; + about.node = node; + } + else if (node.type == Token.VAR || node.type == Token.LET || node.type == Token.CONST) { + about.name = nodeToString(node.target); + if (node.initializer) { // like var i = 0; + about.node = node.initializer; + about.value = nodeToString(about.node); + about.type = getTypeName(node.initializer); + if (about.type === 'FUNCTION' && about.node.name) { + about.node.type = tkn.NAMEDFUNCTIONTATEMENT; + } + } + else { // like var i; + about.node = node.target; + about.value = nodeToString(about.node); + about.type = 'undefined'; + } + } + else if (node.type === Token.ASSIGN || node.type === Token.COLON) { + about.name = nodeToString(node.left); + if (node.type === Token.COLON) { + + // objlit keys with unsafe variable-name characters must be quoted + if (!/^[$_a-z][$_a-z0-9]*$/i.test(about.name) ) { + about.name = '"'+about.name.replace(/"/g, '\\"')+'"'; + } + } + about.node = node.right; + about.value = nodeToString(about.node); + about.type = getTypeName(node.right); + + if (about.type === 'FUNCTION' && about.node.name) { + about.node.type = tkn.NAMEDFUNCTIONTATEMENT; + } + } + else { + // type 39 (NAME) + var string = nodeToString(node); + if (string) { + about.name = string; + } + } + + // get names of the formal parameters declared for this function + if (about.node && about.node.getParamCount) { + var paramCount = about.node.getParamCount(); + if (typeof paramCount === 'number') { + about.node.flattenSymbolTable(true); + var paramNames = []; + for (var i = 0, len = paramCount; i < len; i++) { + paramNames.push(''+about.node.getParamOrVarName(i)); + } + about.paramnames = paramNames; + } + } + + return about; + } +} + + + +/** @private + * @memberof module:src/defaultvisitor.NodeVisitor + */ +function nodeToString(node) { + var str; + + if (!node) return; + + if (node.type === Token.GETPROP) { + str = [nodeToString(node.target), node.property.string].join('.'); + } + else if (node.type === Token.VAR) { + str = nodeToString(node.target) + } + else if (node.type === Token.NAME) { + str = node.string; + } + else if (node.type === Token.STRING) { + str = node.value; + } + else if (node.type === Token.NUMBER) { + str = node.value; + } + else if (node.type === Token.THIS) { + str = 'this'; + } + else if (node.type === Token.GETELEM) { + str = node.toSource(); // like: Foo['Bar'] + } + else if (node.type === Token.NEG || node.type === Token.TRUE || node.type === Token.FALSE) { + str = node.toSource(); // like -1 + } + else { + str = getTypeName(node); + } + + return '' + str; +}; + +/** @private + * @memberof module:src/defaultvisitor.NodeVisitor + */ +function getTypeName(node) { + var type = ''; + + if (node) { + type = ''+ Packages.org.mozilla.javascript.Token.typeToName(node.getType()); + } + + return type; +} \ No newline at end of file diff --git a/rhino_modules/jsdoc/src/parser.js b/rhino_modules/jsdoc/src/parser.js index fc78bb52..6fa08674 100644 --- a/rhino_modules/jsdoc/src/parser.js +++ b/rhino_modules/jsdoc/src/parser.js @@ -5,9 +5,7 @@ * @requires common/events */ -var Token = Packages.org.mozilla.javascript.Token, - currentParser = null, - currentSourceName = ''; +var Token = Packages.org.mozilla.javascript.Token; /** * @class @@ -22,6 +20,9 @@ exports.Parser = function() { } require('common/util').mixin(exports.Parser.prototype, require('common/events')); +exports.Parser.currentParser = null, +exports.Parser.currentSourceName = ''; + /** * Parse the given source files for JSDoc comments. * @param {Array.} sourceFiles An array of filepaths to the JavaScript sources. @@ -60,9 +61,9 @@ exports.Parser.prototype.parse = function(sourceFiles, encoding) { } } - currentParser = this; + exports.Parser.currentParser = this; this._parseSourceCode(sourceCode, filename); - currentParser = null; + exports.Parser.currentParser = null; } return this._resultBuffer; @@ -86,11 +87,25 @@ exports.Parser.prototype.addResult = function(o) { * Empty any accumulated results of calls to parse. */ exports.Parser.prototype.clear = function() { - currentParser = null; - currentSourceName = ''; + exports.Parser.currentParser = null; + exports.Parser.currentSourceName = ''; this._resultBuffer = []; } +/** + * Set the node visitor to use in parsing + */ +exports.Parser.prototype.setVisitor = function(visitor) { + this._visitor = visitor; +} + +/** + * Get the node visitor to use in parsing + */ +exports.Parser.prototype.getVisitor = function() { + return this._visitor; +} + /** @private */ exports.Parser.prototype._parseSourceCode = function(sourceCode, sourceName) { var e = {filename: sourceName}; @@ -100,21 +115,22 @@ exports.Parser.prototype._parseSourceCode = function(sourceCode, sourceName) { e = {filename: sourceName, source: sourceCode}; this.fire('beforeParse', e); sourceCode = e.source; - currentSourceName = sourceName = e.filename; + exports.Parser.currentSourceName = sourceName = e.filename; sourceCode = pretreat(e.source); - var ast = parserFactory().parse(sourceCode, sourceName, 1); + var ast = parserFactory().parse(sourceCode, sourceName, 1) + ast.visit( new Packages.org.mozilla.javascript.ast.NodeVisitor({ - visit: visitNode + visit: this._visitor.visitNode }) ); } this.fire('fileComplete', e); - currentSourceName = ''; + exports.Parser.currentSourceName = ''; } function pretreat(code) { @@ -135,6 +151,9 @@ function pretreat(code) { .replace(/ยป/g, '*/'); } +var tkn = { NAMEDFUNCTIONTATEMENT: -1001 }; +exports.Parser.tkn = tkn; + /** * Given a node, determine what the node is a member of. * @param {astnode} node @@ -247,166 +266,6 @@ exports.Parser.prototype.resolveVar = function(node, basename) { return this.resolveVar(enclosingFunction, basename); } -/** @private */ -function visitNode(node) { - var e, - commentSrc; - - // look for stand-alone doc comments - if (node.type === Token.SCRIPT && node.comments) { - // note: ALL comments are seen in this block... - for each(var comment in node.comments.toArray()) { - if (comment.commentType !== Token.CommentType.JSDOC) { - continue; - } - - if (commentSrc = ''+comment.toSource()) { - - e = { - comment: commentSrc, - lineno: comment.getLineno(), - filename: currentSourceName - }; - - if ( isValidJsdoc(commentSrc) ) { - currentParser.fire('jsdocCommentFound', e, currentParser); - } - } - } - } - else if (node.type === Token.ASSIGN) { - e = { - id: 'astnode'+node.hashCode(), // the id of the ASSIGN node - comment: String(node.jsDoc||'@undocumented'), - lineno: node.left.getLineno(), - filename: currentSourceName, - astnode: node, - code: aboutNode(node) - }; - - var basename = e.code.name.replace(/^([$a-z_][$a-z_0-9]*).*?$/i, '$1'); - - if (basename !== 'this') e.code.funcscope = currentParser.resolveVar(node, basename); - - if ( isValidJsdoc(e.comment) ) { - currentParser.fire('symbolFound', e, currentParser); - } - - if (e.doclet) { - currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet - } - } - else if (node.type === Token.COLON) { // assignment within an object literal - e = { - id: 'astnode'+node.hashCode(), // the id of the COLON node - comment: String(node.left.jsDoc||'@undocumented'), - lineno: node.left.getLineno(), - filename: currentSourceName, - astnode: node, - code: aboutNode(node) - }; - - if ( isValidJsdoc(e.comment) ) { - currentParser.fire('symbolFound', e, currentParser); - } - - if (e.doclet) { - currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet - } - - var parent = currentParser.resolvePropertyParent(node); - if (parent && parent.doclet.isEnum) { - if (!parent.doclet.properties) { parent.doclet.properties = []; } - // members of an enum inherit the enum's type - if (parent.doclet.type && !e.doclet.type) { e.doclet.type = parent.doclet.type; } - delete e.doclet.undocumented; - e.doclet.defaultvalue = e.doclet.meta.code.value; - parent.doclet.properties.push(e.doclet); - } - } - else if (node.type == Token.VAR || node.type == Token.LET || node.type == Token.CONST) { - - if (node.variables) { - return true; // we'll get each var separately on future visits - } - - if (node.parent.variables.toArray()[0] === node) { // like /** blah */ var a=1, b=2, c=3; - // the first var assignment gets any jsDoc before the whole var series - node.jsDoc = node.parent.jsDoc; - } - - e = { - id: 'astnode'+node.hashCode(), // the id of the VARIABLE node - comment: String(node.jsDoc||'@undocumented'), - lineno: node.getLineno(), - filename: currentSourceName, - astnode: node, - code: aboutNode(node) - }; - - // keep track of vars in a function scope - if (node.enclosingFunction) { - var func = 'astnode'+node.enclosingFunction.hashCode(), - funcDoc = currentParser.refs[func]; - - if (funcDoc) { - funcDoc.meta.vars = funcDoc.meta.vars || []; - funcDoc.meta.vars.push(e.code.name); - } - } - - if ( isValidJsdoc(e.comment) ) { - currentParser.fire('symbolFound', e, currentParser); - } - - if (e.doclet) { - currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet - } - } - else if (node.type == Token.FUNCTION || node.type == tkn.NAMEDFUNCTIONTATEMENT) { - e = { - id: 'astnode'+node.hashCode(), // the id of the COLON node - comment: String(node.jsDoc||'@undocumented'), - lineno: node.getLineno(), - filename: currentSourceName, - astnode: node, - code: aboutNode(node) - }; - - e.code.name = (node.type == tkn.NAMEDFUNCTIONTATEMENT)? '' : String(node.name) || ''; -//console.log(':: e.code.name is '+e.code.name); - // keep track of vars in a function scope - if (node.enclosingFunction) { - var func = 'astnode'+node.enclosingFunction.hashCode(), - funcDoc = currentParser.refs[func]; - - if (funcDoc) { - funcDoc.meta.vars = funcDoc.meta.vars || []; - funcDoc.meta.vars.push(e.code.name); - } - } - - var basename = e.code.name.replace(/^([$a-z_][$a-z_0-9]*).*?$/i, '$1'); - e.code.funcscope = currentParser.resolveVar(node, basename); - - if ( isValidJsdoc(e.comment) ) { - currentParser.fire('symbolFound', e, currentParser); - } - - if (e.doclet) { - currentParser.refs['astnode'+e.code.node.hashCode()] = e.doclet; // allow lookup from value => doclet - } - else if (!currentParser.refs['astnode'+e.code.node.hashCode()]) { // keep references to undocumented anonymous functions too as they might have scoped vars - currentParser.refs['astnode'+e.code.node.hashCode()] = { - longname: '', - meta: { code: e.code } - }; - } - } - - return true; -} - /** @private */ function parserFactory() { var cx = Packages.org.mozilla.javascript.Context.getCurrentContext(); @@ -419,135 +278,6 @@ function parserFactory() { ce.initFromContext(cx); return new Packages.org.mozilla.javascript.Parser(ce, ce.getErrorReporter()); } -var tkn = { NAMEDFUNCTIONTATEMENT: -1001 }; -/** - * Attempts to find the name and type of the given node. - * @private - * @memberof module:src/parser.Parser - */ -function aboutNode(node) { - about = {}; - - if (node.type == Token.FUNCTION || node.type == tkn.NAMEDFUNCTIONTATEMENT) { - about.name = node.type == tkn.NAMEDFUNCTIONTATEMENT? '' : '' + node.name; - about.type = 'function'; - about.node = node; - } - else if (node.type == Token.VAR || node.type == Token.LET || node.type == Token.CONST) { - about.name = nodeToString(node.target); - if (node.initializer) { // like var i = 0; - about.node = node.initializer; - about.value = nodeToString(about.node); - about.type = getTypeName(node.initializer); - if (about.type === 'FUNCTION' && about.node.name) { - about.node.type = tkn.NAMEDFUNCTIONTATEMENT; - } - } - else { // like var i; - about.node = node.target; - about.value = nodeToString(about.node); - about.type = 'undefined'; - } - } - else if (node.type === Token.ASSIGN || node.type === Token.COLON) { - about.name = nodeToString(node.left); - if (node.type === Token.COLON) { - - // objlit keys with unsafe variable-name characters must be quoted - if (!/^[$_a-z][$_a-z0-9]*$/i.test(about.name) ) { - about.name = '"'+about.name.replace(/"/g, '\\"')+'"'; - } - } - about.node = node.right; - about.value = nodeToString(about.node); - about.type = getTypeName(node.right); - - if (about.type === 'FUNCTION' && about.node.name) { - about.node.type = tkn.NAMEDFUNCTIONTATEMENT; - } - } - else { - // type 39 (NAME) - var string = nodeToString(node); - if (string) { - about.name = string; - } - } - - // get names of the formal parameters declared for this function - if (about.node && about.node.getParamCount) { - var paramCount = about.node.getParamCount(); - if (typeof paramCount === 'number') { - about.node.flattenSymbolTable(true); - var paramNames = []; - for (var i = 0, len = paramCount; i < len; i++) { - paramNames.push(''+about.node.getParamOrVarName(i)); - } - about.paramnames = paramNames; - } - } - - return about; -} - -/** @private - @memberof module:src/parser.Parser -*/ -function nodeToString(node) { - var str; - - if (!node) return; - - if (node.type === Token.GETPROP) { - str = [nodeToString(node.target), node.property.string].join('.'); - } - else if (node.type === Token.VAR) { - str = nodeToString(node.target) - } - else if (node.type === Token.NAME) { - str = node.string; - } - else if (node.type === Token.STRING) { - str = node.value; - } - else if (node.type === Token.NUMBER) { - str = node.value; - } - else if (node.type === Token.THIS) { - str = 'this'; - } - else if (node.type === Token.GETELEM) { - str = node.toSource(); // like: Foo['Bar'] - } - else if (node.type === Token.NEG || node.type === Token.TRUE || node.type === Token.FALSE) { - str = node.toSource(); // like -1 - } - else { - str = getTypeName(node); - } - - return '' + str; -}; - -/** @private - @memberof module:src/parser.Parser -*/ -function getTypeName(node) { - var type = ''; - - if (node) { - type = ''+ Packages.org.mozilla.javascript.Token.typeToName(node.getType()); - } - - return type; -} - -/** @private - @memberof module:src/parser.Parser -*/ -function isValidJsdoc(commentSrc) { - return commentSrc.indexOf('/***') !== 0; /*** ignore comments that start with many stars ***/ -} /** Fired whenever the parser encounters a JSDoc comment in the current source code. diff --git a/test/runner.js b/test/runner.js index b37c9cd0..e0403d98 100644 --- a/test/runner.js +++ b/test/runner.js @@ -40,6 +40,7 @@ var testhelpers = { doclets; testParser = new (require('jsdoc/src/parser')).Parser(); + testParser.setVisitor(require('jsdoc/src/defaultvisitor').NodeVisitor); require('jsdoc/src/handlers').attachTo(testParser); doclets = testParser.parse('javascript:' + sourceCode); diff --git a/test/t/cases/file.js b/test/t/cases/file.js index bed303fc..285b4d1f 100644 --- a/test/t/cases/file.js +++ b/test/t/cases/file.js @@ -3,6 +3,7 @@ doclets; app.jsdoc.parser = new srcParser.Parser(); + app.jsdoc.parser.setVisitor(require('jsdoc/src/defaultvisitor').NodeVisitor); require('jsdoc/src/handlers').attachTo(app.jsdoc.parser); diff --git a/test/t/cases/modules/data/mod-1.js b/test/t/cases/modules/data/mod-1.js index 871f114f..a6aacf65 100644 --- a/test/t/cases/modules/data/mod-1.js +++ b/test/t/cases/modules/data/mod-1.js @@ -5,6 +5,7 @@ env.opts._ = [__dirname + '/test/cases/modules/']; app.jsdoc.parser = new srcParser.Parser(); + app.jsdoc.parser.setVisitor(require('jsdoc/src/defaultvisitor').NodeVisitor); require('jsdoc/src/handlers').attachTo(app.jsdoc.parser); diff --git a/test/t/cases/modules/data/mod-2.js b/test/t/cases/modules/data/mod-2.js index a7f51e0f..51a1f779 100644 --- a/test/t/cases/modules/data/mod-2.js +++ b/test/t/cases/modules/data/mod-2.js @@ -5,6 +5,7 @@ env.opts._ = [__dirname + '/test/cases/modules/']; app.jsdoc.parser = new srcParser.Parser(); + app.jsdoc.parser.setVisitor(require('jsdoc/src/defaultvisitor').NodeVisitor); require('jsdoc/src/handlers').attachTo(app.jsdoc.parser); diff --git a/test/t/cases/modules/data/mod-3.js b/test/t/cases/modules/data/mod-3.js index a2f42bd7..af8f7ec8 100644 --- a/test/t/cases/modules/data/mod-3.js +++ b/test/t/cases/modules/data/mod-3.js @@ -5,6 +5,7 @@ env.opts._ = [__dirname + '/test/cases/modules/']; app.jsdoc.parser = new srcParser.Parser(); + app.jsdoc.parser.setVisitor(require('jsdoc/src/defaultvisitor').NodeVisitor); require('jsdoc/src/handlers').attachTo(app.jsdoc.parser); diff --git a/test/t/jsdoc/src/handlers.js b/test/t/jsdoc/src/handlers.js index 7121d605..d47c7bab 100644 --- a/test/t/jsdoc/src/handlers.js +++ b/test/t/jsdoc/src/handlers.js @@ -2,6 +2,7 @@ var jsdoc = {src: { parser: require('jsdoc/src/parser')}}, testParser = new jsdoc.src.parser.Parser(); + testParser.setVisitor(require('jsdoc/src/defaultvisitor').NodeVisitor); require('jsdoc/src/handlers').attachTo(testParser); diff --git a/test/t/jsdoc/src/parser.js b/test/t/jsdoc/src/parser.js index 7efa240c..3e3e9600 100644 --- a/test/t/jsdoc/src/parser.js +++ b/test/t/jsdoc/src/parser.js @@ -23,6 +23,7 @@ jsdocCounter = 0, event = null, parser = new jsdoc.src.parser.Parser(); + parser.setVisitor(require('jsdoc/src/defaultvisitor').NodeVisitor); parser .on('jsdocCommentFound', function(e) { @@ -40,6 +41,7 @@ var sourceCode = 'javascript:var foo = 1;', symbolCounter = 0, parser = new jsdoc.src.parser.Parser(); + parser.setVisitor(require('jsdoc/src/defaultvisitor').NodeVisitor); parser .on('symbolFound', function(e) {