mirror of
https://github.com/jsdoc/jsdoc.git
synced 2025-12-08 19:46:11 +00:00
Refactored parsing to allow for easily extending/overriding parseing behavior
This commit is contained in:
parent
279554f1a3
commit
f52302e71d
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,6 @@
|
||||
build-files/java/build
|
||||
jsdoc.jar
|
||||
test/tutorials/out
|
||||
.externalToolBuilders
|
||||
.settings
|
||||
.project
|
||||
|
||||
@ -9,7 +9,10 @@
|
||||
},
|
||||
|
||||
"plugins": [
|
||||
"plugins/jquery.ui.widget"
|
||||
],
|
||||
|
||||
"nodeVisitor": "plugins/jquery.ui.widget.visitor",
|
||||
|
||||
"jsVersion": 180
|
||||
}
|
||||
5
jsdoc.js
5
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])) {
|
||||
|
||||
74
plugins/jquery.ui.widget.js
vendored
Normal file
74
plugins/jquery.ui.widget.js
vendored
Normal 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);
|
||||
};
|
||||
*/
|
||||
89
plugins/jquery.ui.widget.visitor.js
Normal file
89
plugins/jquery.ui.widget.visitor.js
Normal 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);
|
||||
}
|
||||
355
rhino_modules/jsdoc/src/defaultvisitor.js
Normal file
355
rhino_modules/jsdoc/src/defaultvisitor.js
Normal 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;
|
||||
}
|
||||
@ -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.
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user