Added support for @mixin tag.

This commit is contained in:
Michael Mathews 2010-07-30 21:38:31 +01:00
parent 6d3143143e
commit cad6e9c01a
8 changed files with 98 additions and 81 deletions

View File

@ -261,7 +261,7 @@
var name = '', var name = '',
taggedName = '', taggedName = '',
kind = '', kind = '',
taggedIsa = '', taggedKind = '',
memberof = '', memberof = '',
taggedMemberof = '', taggedMemberof = '',
isFile = false, // TODO this should be handled by an event handler in tag dictionary isFile = false, // TODO this should be handled by an event handler in tag dictionary
@ -300,7 +300,7 @@
if (kind && kind !== tags[i].value) { if (kind && kind !== tags[i].value) {
throw new DocTagConflictError('Symbol has too many isas, cannot be both: ' + kind + ' and ' + tags[i].value); throw new DocTagConflictError('Symbol has too many isas, cannot be both: ' + kind + ' and ' + tags[i].value);
} }
taggedIsa = kind = tags[i].value; taggedKind = kind = tags[i].value;
} }
else if (tags[i].name === 'memberof') { else if (tags[i].name === 'memberof') {
if (memberof) { if (memberof) {
@ -337,6 +337,19 @@
} }
} }
if ( /^\s*(\S+)\s*=>\s*(\S+)/.test(taggedName) ) {
taggedName = RegExp.$1;
var refersto = RegExp.$2;
tags.setTag('name', taggedName);
taggedKind = 'mixin';
tags.setTag('kind', taggedKind);
tags.addTag('refersto', refersto);
}
if (name && !taggedName) { if (name && !taggedName) {
tags.addTag('name', name); tags.addTag('name', name);
} }
@ -345,7 +358,7 @@
tags.addTag('name', 'file:'+meta.file); tags.addTag('name', 'file:'+meta.file);
} }
if (kind && !taggedIsa) { if (kind && !taggedKind) {
tags.addTag('kind', kind); tags.addTag('kind', kind);
} }

View File

@ -11,9 +11,8 @@
(function() { (function() {
var Token = Packages.org.mozilla.javascript.Token, var Token = Packages.org.mozilla.javascript.Token,
currentModule = ''; currentModule = '',
jsdoc = {
var jsdoc = {
tagDictionary: require('jsdoc/tagdictionary') tagDictionary: require('jsdoc/tagdictionary')
}; };
@ -54,7 +53,7 @@
[prefix, scope, name] = exports.shorten(name); [prefix, scope, name] = exports.shorten(name);
} }
else { // like @name bar, @memberof foo else { // like @name bar, @memberof foo
if ( /([.~#])$/.test(memberof) ) { // like @memberof foo# or @memberof foo~ if ( /([#.~])$/.test(memberof) ) { // like @memberof foo# or @memberof foo~
path = memberof + name; path = memberof + name;
scope = RegExp.$1; scope = RegExp.$1;
doclet.setTag('scope', puncToScope[scope]); doclet.setTag('scope', puncToScope[scope]);
@ -153,77 +152,65 @@
return [prefix, scope, name]; return [prefix, scope, name];
} }
/** Given an AST node, return the path to the enclosing node. */ function docToPath(doclet, tagName) {
function getEnclosingPath(node) { // TODO protect quoted parts of the path that may contain the string "prototype"
var enclosingNode, return (doclet.tagValue(tagName) || '').replace(/\.prototype\.?/g, '#');
enclosingDoc;
if (node.parent && node.parent.type === Token.OBJECTLIT) {
if (enclosingNode = node.parent) {
enclosingDoc = exports.docFromNode(enclosingNode);
}
}
else {
if ( enclosingNode = node.getEnclosingFunction() ) {
enclosingDoc = exports.docFromNode(enclosingNode);
}
}
if (enclosingDoc) {
return (enclosingDoc.tagValue('path') || '').replace(/\.prototype\.?/g, '#');
}
} }
/** /**
Resolve how to document the `this.` portion of a symbol name. Apply information about how nested this AST node is to what we know about
the name.
*/ */
exports.resolveThis = function(name, node, doclet) { exports.resolvePath = function(name, node, doclet) {
var enclosing, var enclosing,
enclosingDoc, enclosingDoc,
enclosingPath, enclosingPath,
memberof = (doclet.tagValue('memberof') || '').replace(/\.prototype\.?/g, '#'); memberof;
// documented member of an undocumented object literal?
// like foo = { /** a bar. */ bar: 1};
if (node.parent && node.parent.type === Token.OBJECTLIT) { if (node.parent && node.parent.type === Token.OBJECTLIT) {
if ( enclosingPath = getEnclosingPath(node) ) { if ( enclosingDoc = exports.docFromNode(node.parent) ) {
if ( enclosingPath = docToPath(enclosingDoc, 'path') ) {
name = enclosingPath + (/([#.~])$/.test(enclosingPath) ? '' : '.') + name; name = enclosingPath + (/([#.~])$/.test(enclosingPath) ? '' : '.') + name;
} }
} }
}
// what's all this then?
else if ( name.indexOf('this.') === 0 ) { else if ( name.indexOf('this.') === 0 ) {
memberof = docToPath(doclet, 'memberof');
// need to examine the source code to determine the full path :(
if (!memberof || memberof === 'this') { if (!memberof || memberof === 'this') {
enclosing = node.getEnclosingFunction() enclosing = node.getEnclosingFunction()
enclosingDoc = exports.docFromNode(enclosing); enclosingDoc = exports.docFromNode(enclosing);
if (enclosingDoc) { if (enclosingDoc) { // documented enclosing symbol
if (enclosingDoc.tagValue('scope') === 'inner') { if (enclosingDoc.tagValue('scope') === 'inner') {
memberof = ''; // inner functions have `this` scope of global memberof = ''; // inner functions always have `this` resolve to the global object
} }
else { else {
memberof = enclosingDoc.tagValue('path'); memberof = docToPath(enclosingDoc, 'path');
} }
} }
else {
memberof = '';
}
if (enclosing && !memberof) { if (enclosing && !memberof) { // inside an anonymous function, this resolves to the global object
memberof = ''; // [[anonymousFunction]] memberof = '';
name = name.slice(5); // remove `this.` name = name.slice(5); // remove `this.`
} }
else if (!enclosing) { else if (!enclosing) {
memberof = ''; // [[globalObject]] memberof = ''; // no enclosing function, this resolves to the global object
} }
if (memberof || !enclosing) { if (memberof || !enclosing) {
// `this` refers to nearest non-inner member in the name path // `this` refers to nearest non-inner member in the name path
if (enclosingDoc && enclosingDoc.tagValue('kind') !== 'constructor') { if (enclosingDoc && enclosingDoc.tagValue('kind') !== 'constructor') {
var parts = memberof.split(/[#~.]/); var parts = memberof.split(/[#.~]/);
var suffix = parts.pop(); var suffix = parts.pop();
memberof = memberof.slice(0, -suffix.length); // remove suffix from memberof memberof = memberof.slice(0, -suffix.length); // remove suffix from memberof
} }
var joiner = (memberof === '')? '' : (/[#~.]$/.test(memberof))? '' : '#'; var joiner = (memberof === '')? '' : (/[#.~]$/.test(memberof))? '' : '#';
name = memberof + joiner + name.slice(5); // replace `this.` with memberof name = memberof + joiner + name.slice(5); // replace `this.` with memberof
} }
} }
@ -276,7 +263,7 @@
return null; return null;
} }
// tuples, like [ [noderef, doclet], [noderef, doclet] ] // a linking map, like [ [noderef, doclet], [noderef, doclet] ]
exports.refs = []; exports.refs = [];
function getTypeName(node) { function getTypeName(node) {

View File

@ -62,7 +62,7 @@
// this thing may have commented members, so keep a ref to the thing but don't add it to the doclets list // this thing may have commented members, so keep a ref to the thing but don't add it to the doclets list
thisDoclet = jsdoc.doclet.makeDoclet('[[undocumented]]', node, currentSourceName); thisDoclet = jsdoc.doclet.makeDoclet('[[undocumented]]', node, currentSourceName);
nodeName = jsdoc.name.resolveThis(node.name, node, thisDoclet); nodeName = jsdoc.name.resolvePath(node.name, node, thisDoclet);
thisDoclet.setName(nodeName); thisDoclet.setName(nodeName);
jsdoc.name.refs.push([ jsdoc.name.refs.push([
node, node,
@ -97,7 +97,7 @@
} }
if (!thisDocletName) { // guess name from the source code if (!thisDocletName) { // guess name from the source code
nodeName = jsdoc.name.resolveThis(nodeName, node, thisDoclet); nodeName = jsdoc.name.resolvePath(nodeName, node, thisDoclet);
thisDoclet.setName(nodeName); thisDoclet.setName(nodeName);
jsdoc.doclets.addDoclet(thisDoclet); jsdoc.doclets.addDoclet(thisDoclet);
@ -109,7 +109,7 @@
// this thing may have commented members, so keep a ref to the thing but don't add it to the doclets list // this thing may have commented members, so keep a ref to the thing but don't add it to the doclets list
thisDoclet = jsdoc.doclet.makeDoclet('[[undocumented]]', node, currentSourceName); thisDoclet = jsdoc.doclet.makeDoclet('[[undocumented]]', node, currentSourceName);
nodeName = jsdoc.name.resolveThis(nodeName, node, thisDoclet); nodeName = jsdoc.name.resolvePath(nodeName, node, thisDoclet);
thisDoclet.setName(nodeName); thisDoclet.setName(nodeName);
jsdoc.name.refs.push([ jsdoc.name.refs.push([

View File

@ -33,7 +33,7 @@ exports.jsdocSchema = {
"kind": { "kind": {
"type": "string", "type": "string",
"maxItems": 1, "maxItems": 1,
"enum": ["constructor", "module", "event", "namespace", "method", "property", "enum", "class", "interface", "constant", "file", "version"] "enum": ["constructor", "module", "event", "namespace", "method", "property", "enum", "class", "interface", "constant", "mixin", "file", "version"]
}, },
"access": { "access": {
"type": "string", "type": "string",

View File

@ -135,6 +135,19 @@
return false; return false;
} }
function setTag(tagName, tagValue) {
var i = this.length;
while(i--) {
if (this[i].name === tagName) {
this[i].value = tagValue;
return true;
}
}
this.addTag(tagName, tagValue);
return false;
}
/** /**
Given the source of a jsdoc comment, finds the tags. Given the source of a jsdoc comment, finds the tags.
@private @private
@ -146,6 +159,7 @@
var tags = []; var tags = [];
tags.addTag = addTag; tags.addTag = addTag;
tags.hasTag = hasTag; tags.hasTag = hasTag;
tags.setTag = setTag;
// split out the basic tags, keep surrounding whitespace // split out the basic tags, keep surrounding whitespace
commentSrc commentSrc

View File

@ -466,4 +466,12 @@
new TagDefinition('see', { new TagDefinition('see', {
isExported: true isExported: true
}); });
/** Syntax: @refersto <text>
@property {module:jsdoc/tagdictionary~TagDefinition} refersto
@memberOf module:jsdoc/tagdictionary~tagDefinitions
*/
new TagDefinition('refersto', {
isExported: true
});
})(); })();

19
test/samples/tag_name.js Normal file
View File

@ -0,0 +1,19 @@
/**
@name Tipsy
@kind property
*/
/**
@name Tubbie.LaLa
@kind property
*/
/**
@name Tubbie."and.don't.forget#Po!"
@kind property
*/
/**
@name Custards.0
@kind property
*/

View File

@ -10,7 +10,7 @@
tag: require('jsdoc/tag'), tag: require('jsdoc/tag'),
parser: require('jsdoc/parser') parser: require('jsdoc/parser')
}; };
jsdoc.parser.parseFiles(BASEDIR + 'test/tests/08_tag_name.js'); jsdoc.parser.parseFiles(BASEDIR + 'test/samples/tag_name.js');
doclets = jsdoc.parser.result; doclets = jsdoc.parser.result;
}); });
@ -47,27 +47,3 @@
}); });
}); });
})(); })();
(function testarea() {
/**
@name Tipsy
@kind property
*/
/**
@name Tubbie.LaLa
@kind property
*/
/**
@name Tubbie."and.don't.forget#Po!"
@kind property
*/
/**
@name Custards.0
@kind property
*/
})();