Added support for @enum. Rename old @property to @member.

This commit is contained in:
Michael Mathews 2011-09-26 14:46:31 +01:00
parent 31161fc9b6
commit 2e857fcecc
17 changed files with 345 additions and 35 deletions

View File

@ -51,8 +51,9 @@ exports.Doclet.prototype.postProcess = function() {
this.setLongname(this.name);
}
if (this.memberof === '') {
delete(this.memberof);
delete(this.memberof);
}
if (!this.kind && this.meta && this.meta.code) {
this.addTag( 'kind', codetypeToKind(this.meta.code.type) );
}

View File

@ -31,18 +31,18 @@ exports.resolve = function(doclet) {
name = doclet.longname = doclet.meta.code.funcscope + '~' + name;
}
if (memberof) { // @memberof tag given
memberof = memberof.replace(/\.prototype\.?/g, '#');
if (memberof || doclet.forceMemberof) { // @memberof tag given
memberof = ('' || memberof).replace(/\.prototype\.?/g, '#');
// the name is a fullname, like @name foo.bar, @memberof foo
if (name && name.indexOf(memberof) === 0) {
about = exports.shorten(name);
about = exports.shorten(name, (doclet.forceMemberof? memberof : undefined));
}
else if (name && /([#.~])$/.test(memberof) ) { // like @memberof foo# or @memberof foo~
about = exports.shorten(memberof + name);
about = exports.shorten(memberof + name, (doclet.forceMemberof? memberof : undefined));
}
else if (name && doclet.scope ) { // like @memberof foo# or @memberof foo~
about = exports.shorten(memberof + scopeToPunc[doclet.scope] + name);
about = exports.shorten(memberof + (scopeToPunc[doclet.scope]||'') + name, (doclet.forceMemberof? memberof : undefined));
}
}
else { // no @memberof
@ -67,7 +67,7 @@ exports.resolve = function(doclet) {
}
else if (about.scope) {
if (about.memberof === '<global>') { // via @memberof <global> ?
delete doclet.scope;
doclet.scope = 'global';
}
else {
doclet.scope = puncToScope[about.scope];
@ -130,9 +130,10 @@ exports.applyNamespace = function(longname, ns) {
Given a longname like "a.b#c(2)", slice it up into ["a.b", "#", 'c', '2'],
representing the memberof, the scope, the name, and variation.
@param {string} longname
@param {string} forcedMemberof
@returns {object} Representing the properties of the given name.
*/
exports.shorten = function(longname) {
exports.shorten = function(longname, forcedMemberof) {
// quoted strings in a longname are atomic, convert to tokens
var atoms = [], token;
@ -149,18 +150,32 @@ exports.shorten = function(longname) {
return dot + token; // foo["bar"] => foo.@{1}@
});
longname = longname.replace( /\.prototype\.?/g, '#' );
var parts = longname?
(longname.match( /^(:?(.+)([#.~]))?(.+?)$/ ) || []).reverse()
: [''];
var name = parts[0] || '', // ensure name is always initialised to avoid error being thrown when calling replace on undefined [gh-24]
scope = parts[1] || '', // ., ~, or #
memberof = parts[2] || '',
var name = '',
scope = '', // ., ~, or #
memberof = '',
variation;
longname = longname.replace( /\.prototype\.?/g, '#' );
//console.log(forcedMemberof);
if (typeof forcedMemberof !== 'undefined') {
//console.log('forcedMemberof');
name = longname.substr(forcedMemberof.length);
var parts = forcedMemberof.match(/^(.*?)([#.~]?)$/);
//console.log(parts);
if (parts[1]) memberof = parts[1] || forcedMemberof;
if (parts[2]) scope = parts[2];
}
else {
var parts = longname?
(longname.match( /^(:?(.+)([#.~]))?(.+?)$/ ) || []).reverse()
: [''];
name = parts[0] || ''; // ensure name is always initialised to avoid error being thrown when calling replace on undefined [gh-24]
scope = parts[1] || ''; // ., ~, or #
memberof = parts[2] || '';
}
// like /** @name foo.bar(2) */
if ( /(.+)\(([^)]+)\)$/.test(name) ) {
name = RegExp.$1, variation = RegExp.$2;

View File

@ -128,6 +128,10 @@ exports.attachTo = function(parser) {
}
}
if (!newDoclet.memberof) {
newDoclet.scope = 'global';
}
addDoclet.call(this, newDoclet);
e.doclet = newDoclet;
}

View File

@ -201,6 +201,23 @@ exports.Parser.prototype.resolveThis = function(node) {
}
}
/**
Given: foo = { x:1 }, find foo from x.
*/
exports.Parser.prototype.resolvePropertyParent = function(node) {
var memberof = {};
if (node.parent) {
var parent = node.parent;
if (parent.type === Token.COLON) parent = parent.parent; // go up one more
memberof.id = 'astnode'+parent.hashCode();
memberof.doclet = this.refs[memberof.id];
if (memberof.doclet) { return memberof; }
}
}
/**
* Resolve what function a var is limited to.
* @param {astnode} node
@ -286,6 +303,14 @@ function visitNode(node) {
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; }
parent.doclet.properties.push(e.doclet);
}
}
else if (node.type == Token.VAR || node.type == Token.LET || node.type == Token.CONST) {

View File

@ -161,6 +161,15 @@ exports.defineTags = function(dictionary) {
})
.synonym('desc');
dictionary.defineTag('enum', {
canHaveType: true,
onTagged: function(doclet, tag) {
doclet.kind = 'member';
doclet.isEnum = true;
doclet.type = tag.value.type;
}
});
dictionary.defineTag('event', {
onTagged: function(doclet, tag) {
setDocletKindToTitle(doclet, tag);
@ -290,9 +299,17 @@ exports.defineTags = function(dictionary) {
dictionary.defineTag('memberof', {
mustHaveValue: true,
onTagged: function(doclet, tag) {
if (tag.originalTitle === 'memberof!') {
doclet.forceMemberof = true;
if (tag.value === '<global>') {
doclet.addTag('global');
delete doclet.memberof;
}
}
setDocletMemberof(doclet, tag);
}
});
})
.synonym('memberof!');
dictionary.defineTag('mixin', {
onTagged: function(doclet, tag) {
@ -544,7 +561,9 @@ function setNameToFile(doclet, tag) {
}
function setDocletMemberof(doclet, tag) {
doclet.setMemberof(tag.value);
if (tag.value && tag.value !== '<global>') {
doclet.setMemberof(tag.value);
}
}
function applyNamespace(doclet, tag) {

View File

@ -107,5 +107,5 @@ function parseTypes(type) {
/** @private */
function trim(text) {
return text.replace(/^\s+|\s+$/g, '');
return text.trim();
}

View File

@ -134,7 +134,7 @@ h4
color: #A35A00;
}
h5
h5, .container-overview .subsection-title
{
font-size: 16px;
font-weight: bold;
@ -203,16 +203,16 @@ h6
border-left: 3px #ddd solid;
}
.params
.params, .props
{
border-spacing: 0;
border: 0;
border-collapse: collapse;
}
.params .name { color: #1C02A3; }
.params .name, .props .name { color: #1C02A3; }
.params td, .params th
.params td, .params th, .props td, .props th
{
border: 1px solid #ddd;
margin: 0px;
@ -222,17 +222,17 @@ h6
display: table-cell;
}
.params thead tr
.params thead tr, .props thead tr
{
background-color: #ddd;
font-weight: bold;
}
.params .params thead tr
.params .params thead tr, .props .props thead tr
{
background-color: #fff;
font-weight: bold;
}
.params th { border-right: 1px solid #aaa; }
.params thead .last { border-right: 1px solid #ddd; }
.params th, .props th { border-right: 1px solid #aaa; }
.params thead .last, .props thead .last { border-right: 1px solid #ddd; }

View File

@ -46,7 +46,8 @@
?>
</header>
<article>
<article>
<div class="container-overview">
<?js
if (doc.kind === 'module' && doc.module) {
print(render('method.tmpl', doc.module));
@ -68,6 +69,7 @@
}
}
?>
</div>
<?js
if (doc.augments && doc.augments.length) {

View File

@ -1,4 +1,17 @@
<dl class="details">
<?js
var properties = this.properties;
if (properties && properties.length && properties.forEach) {
?>
<h5 class="subsection-title">Properties:</h5>
<dl><?js
print( render('properties.tmpl', properties) );
?></dl>
<?js } ?>
<?js if (this.version) {?>
<dt class="tag-version">Version:</dt>
<dd class="tag-version"><?js= version ?></dd>

View File

@ -0,0 +1,24 @@
<dt id="member:<?js= longname ?>">
<h4 class="name"><?js= this.attribs + name + this.signature ?></h4>
<?js if (this.summary) { ?>
<p class="summary"><?js= summary ?></p>
<?js } ?>
</dt>
<dd>
<?js if (this.description) { ?>
<p class="description">
<?js= this.description ?>
</p>
<?js } ?>
<?js print(render('details.tmpl', this)); ?>
<?js
if (this.examples && examples.length) {
print('<h5>Example' + (examples.length > 1? 's':'') + '</h5>');
print( render('examples.tmpl', examples) );
}
?>
</dd>

View File

@ -13,8 +13,6 @@
</p>
<?js } ?>
<?js print(render('details.tmpl', this)); ?>
<?js
if (this['this']) {
print('<h5>This:</h5>');
@ -29,6 +27,8 @@
}
?>
<?js print(render('details.tmpl', this)); ?>
<?js if (this.fires && fires.length) { ?>
<h5>Fires:</h5>
<ul><?js

View File

@ -0,0 +1,115 @@
<?js
var props = this;
/* sort subprops under their parent props (like opts.classname) */
var parentProp = null;
props.forEach(function(prop, i) {
if (!prop) { return; }
if ( parentProp && prop.name.indexOf(parentProp.name + '.') === 0 ) {
prop.name = prop.name.substr(parentProp.name.length+1);
parentProp.subprops = parentProp.subprops || [];
parentProp.subprops.push(prop);
props[i] = null;
}
else {
parentProp = prop;
}
});
/* determine if we need extra columns, "attributes" and "default" */
props.hasAttributes = false;
props.hasDefault = false;
props.hasName = false;
props.forEach(function(prop) {
if (!prop) { return; }
if (prop.optional || prop.nullable) {
props.hasAttributes = true;
}
if (prop.name) {
props.hasName = true;
}
if (typeof prop.defaultvalue !== 'undefined') {
props.hasDefault = true;
}
});
?>
<table class="props">
<thead>
<tr>
<?js if (props.hasName) {?>
<th>Name</th>
<?js } ?>
<th>Type</th>
<?js if (props.hasAttributes) {?>
<th>Argument</th>
<?js } ?>
<?js if (props.hasDefault) {?>
<th>Default</th>
<?js } ?>
<th class="last">Description</th>
</tr>
</thead>
<tbody>
<?js
props.forEach(function(prop) {
if (!prop) { return; }
?>
<tr>
<?js if (props.hasName) {?>
<td class="name"><code><?js= prop.name ?></code></td>
<?js } ?>
<td class="type">
<?js
if (prop.type && prop.type.names) {
prop.type.names.forEach(function(name, i) {
print( linkto(name, htmlsafe(name)) );
if (i < prop.type.names.length-1) { print(' | '); }
});
}
?>
</td>
<?js if (props.hasAttributes) {?>
<td class="attributes">
<?js
if (prop.optional) {
print( '&lt;optional><br>' );
}
if (prop.nullable) {
print( '&lt;nullable><br>' );
}
?>
</td>
<?js } ?>
<?js if (props.hasDefault) {?>
<td class="default">
<?js
if (typeof prop.defaultvalue !== 'undefined') {
print( htmlsafe(prop.defaultvalue) );
}
?>
</td>
<?js } ?>
<td class="description last"><?js= prop.description ?><?js if (prop.subprops) {
print( '<h6>Properties</h6>' + render('properties.tmpl', prop.subprops) );
}?></td>
</tr>
<?js }); ?>
</tbody>
</table>

29
test/cases/enum.js Normal file
View File

@ -0,0 +1,29 @@
/** @constructor */
function Data() {
/**
The current position.
@enum {number}
*/
this.point = {
/** The x coordinate of the point. */
x: 0,
/** The y coordinate of the point. */
y: 0
};
}
/**
* Enum for tri-state values.
* @enum {number}
*/
TriState = {
/** true */
TRUE: 1,
/** false */
FALSE: -1,
/** @type {boolean} */
MAYBE: true
};

View File

@ -0,0 +1,44 @@
/** @constructor
*/
function Data() {
/**
The current position.
@type {object}
@property {boolean} needsRevalidate Does this point need to be revalidated?
*/
this.point = {
/**
The x coordinate of the point.
@type {number}
@name point.x
@memberof! Data#
*/
x: 0,
/**
The y coordinate of the point.
@type {number}
@name point.y
@memberof! Data#
@see {@link Data#point.x}
*/
y: 0,
needsRevalidate: false
};
}
var map = {
/**
@type {Array}
@name map.routes
@memberof! <global>
@property {Data#point} point
*/
routes: []
}
/** The current cursor. */
var cursor = {};

19
test/cases/propertytag.js Normal file
View File

@ -0,0 +1,19 @@
/**
* @namespace
* @property {Object} defaults The default values.
* @property {Number} defaults.a The a property of the defaults.
* @property {String} defaults.b The b property of the defaults.
*/
myobject = {
defaults: {
a: 1,
b: "Hit the light",
/**
* The c property of the defaults.
* @member
* @type {Boolean}
* @property {String} prop The property of c.
*/
c: true
}
};

View File

@ -2,8 +2,8 @@
var docSet = testhelpers.getDocSetFromFile('test/cases/aliasglobal.js'),
log = docSet.getByLongname('log')[0];
test('When a symbol is documented as a static member of <global> it is not static.', function() {
assert.equal(typeof log.scope, 'undefined');
test('When a symbol is documented as a static member of <global> it\'s scope is "global" and not "static".', function() {
assert.equal(log.scope, 'global');
});
})();

View File

@ -13,7 +13,7 @@
assert.equal(found[0][0].comment, '/** document me */', 'The first constant should get the docs.');
assert.equal(found[0][0].name, 'GREEN', 'The short name should be correct.');
assert.equal(found[0][0].memberof, undefined, 'The memberof should be undefined.');
assert.equal(found[0][0].scope, undefined, 'The scope should be undefined.');
assert.equal(found[0][0].scope, 'global', 'The scope should be global.');
assert.equal(found[1].length, 1, 'The second constant should be found');
assert.equal(found[1][0].undocumented, true, 'The second constant should not get the docs.');
@ -23,7 +23,7 @@
assert.equal(found[4][0].comment, '/** document me */', 'The correct var should get the docs.');
assert.equal(found[4][0].name, 'results', 'The short name should be correct.');
assert.equal(found[4][0].memberof, undefined, 'The memberof should be undefined.');
assert.equal(found[4][0].scope, undefined, 'The scope should be undefined.');
assert.equal(found[4][0].scope, 'global', 'The scope should be global.');
});
})();