Refactored parsing to allow for easily extending/overriding parseing behavior

This commit is contained in:
Jannon 2012-01-25 14:17:22 -08:00
parent 279554f1a3
commit f52302e71d
14 changed files with 567 additions and 300 deletions

3
.gitignore vendored
View File

@ -1,3 +1,6 @@
build-files/java/build
jsdoc.jar
test/tutorials/out
.externalToolBuilders
.settings
.project

View File

@ -9,7 +9,10 @@
},
"plugins": [
"plugins/jquery.ui.widget"
],
"nodeVisitor": "plugins/jquery.ui.widget.visitor",
"jsVersion": 180
}

View File

@ -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])) {

74
plugins/jquery.ui.widget.js vendored Normal file
View File

@ -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);
};
*/

View File

@ -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);
}

View File

@ -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: '<anonymous>',
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;
}

View File

@ -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.<string>} 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: '<anonymous>',
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.

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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) {