mirror of
https://github.com/jsdoc/jsdoc.git
synced 2025-12-08 19:46:11 +00:00
Added support for params, nullable, optional.
This commit is contained in:
parent
1da08819b7
commit
9a45e4a8a4
@ -131,27 +131,48 @@
|
||||
o = {};
|
||||
|
||||
for (var i = 0, leni = this.tags.length; i < leni; i++) {
|
||||
if (exportTags.indexOf(this.tags[i].name) === -1) { continue; }
|
||||
|
||||
tag = this.tags[i];
|
||||
|
||||
if ( exportTags.indexOf(tag.name) === -1 ) { continue; }
|
||||
|
||||
tagName = tag.name;
|
||||
tagValue = {};
|
||||
|
||||
if (tag.type) {
|
||||
tagValue.type = tag.type;
|
||||
// not a long tag
|
||||
if (!tag.pname && tag.text) { tagValue.text = tag.text; }
|
||||
}
|
||||
|
||||
// if (tag.type && tag.type.length) {
|
||||
// tagValue.type = tag.type;
|
||||
// // not a long tag
|
||||
// if (!tag.pname && tag.text) { tagValue.text = tag.text; }
|
||||
// }
|
||||
|
||||
// a long tag
|
||||
if (tag.pname) { tagValue.name = tag.pname; }
|
||||
if (tag.pname) {
|
||||
|
||||
if ( /^\[(.+)\]$/.test(tag.pname) ) {
|
||||
tagValue.name = RegExp.$1;
|
||||
tag.poptional = true;
|
||||
}
|
||||
else {
|
||||
tagValue.name = tag.pname;
|
||||
}
|
||||
tagValue.type = tag.type;
|
||||
// print('```` name is '+tagName+': '+tagValue);
|
||||
}
|
||||
if (tag.pdesc) { tagValue.desc = tag.pdesc; }
|
||||
if (typeof tag.poptional === 'boolean') { tagValue.optional = tag.poptional; }
|
||||
if (typeof tag.pnullable === 'boolean') { tagValue.nullable = tag.pnullable; }
|
||||
|
||||
// tag value is not an object, it's just a simple string
|
||||
if (!tag.pname && !tag.type) { tagValue = tag.text; }
|
||||
|
||||
if (!o[tagName]) { o[tagName] = tagValue; }
|
||||
else if (o[tagName].push) { o[tagName].push(tagValue); }
|
||||
else {
|
||||
if (!tag.pname) {
|
||||
tagValue = tag.text;
|
||||
}
|
||||
|
||||
if (typeof o[tagName] === 'undefined') { // not defined
|
||||
o[tagName] = tagValue;
|
||||
}
|
||||
else if (o[tagName].push) { // is an array
|
||||
o[tagName].push(tagValue);
|
||||
}
|
||||
else { // is a string, but needs to be an array
|
||||
o[tagName] = [ o[tagName] ];
|
||||
o[tagName].push(tagValue);
|
||||
}
|
||||
@ -266,7 +287,7 @@
|
||||
if (memberof) {
|
||||
throw new DocTagConflictError('doclet has too many tags of type: @memberof.');
|
||||
}
|
||||
taggedMemberof = memberof = tags[i].text;
|
||||
taggedMemberof = memberof = tags[i].text+'ZZZ_0';
|
||||
}
|
||||
|
||||
if ( nameables.indexOf(tags[i].name) > -1 ) {
|
||||
@ -282,7 +303,7 @@
|
||||
}
|
||||
|
||||
if (tags[i].type) {
|
||||
tags[tags.length] = tag.fromTagText('type ' + tags[i].type);
|
||||
tags[tags.length] = tag.fromTagText('type ' + tags[i].type.join('|'));
|
||||
}
|
||||
|
||||
if (denom && denom !== tags[i].name) {
|
||||
@ -297,7 +318,7 @@
|
||||
if (memberof) {
|
||||
throw new DocTagConflictError('doclet has too many tags of type: @memberof.');
|
||||
}
|
||||
memberof = tags[i].text;
|
||||
memberof = tags[i].text+'ZZZ_1';
|
||||
}
|
||||
|
||||
if (denom && denom !== memberofs[tags[i].name]) {
|
||||
@ -316,7 +337,7 @@
|
||||
}
|
||||
|
||||
if (memberof && !taggedMemberof) {
|
||||
tags[tags.length] = tag.fromTagText('memberof ' + memberof);
|
||||
tags[tags.length] = tag.fromTagText('memberof ' + memberof+'ZZZ_2');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -76,7 +76,7 @@
|
||||
var shortname = path.split(/([#.-])/).pop(),
|
||||
splitOn = RegExp.$1,
|
||||
splitAt = path.lastIndexOf(splitOn),
|
||||
prefix = (splitAt === -1)? '' : path.slice(0, splitAt);
|
||||
prefix = (splitOn && splitAt !== -1)? path.slice(0, splitAt) : '';
|
||||
|
||||
if (splitOn === '#') { prefix = prefix + splitOn; }
|
||||
return [prefix, shortname];
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
@module jsdoc/tag
|
||||
*/
|
||||
(function() {
|
||||
var jsdoc_type = require('jsdoc/type');
|
||||
|
||||
exports.fromCommentText = function(commentText) {
|
||||
var tag,
|
||||
@ -45,7 +46,7 @@
|
||||
function Tag(tagText) {
|
||||
this.raw = tagText;
|
||||
this.name = '';
|
||||
this.type = '';
|
||||
this.type = [];
|
||||
this.text = '';
|
||||
this.pname = '';
|
||||
this.pdesc = '';
|
||||
@ -54,24 +55,29 @@
|
||||
var bits = tagText.match(/^(\S+)(?:\s+([\s\S]*))?$/);
|
||||
|
||||
if (bits) {
|
||||
|
||||
this.name = (bits[1] || '').toLowerCase();
|
||||
this.text = bits[2] || '';
|
||||
this.name = (bits[1] || '').toLowerCase(); // like @name
|
||||
this.name = synonym(this.name);
|
||||
|
||||
var typeText = splitType(this.text);
|
||||
this.text = bits[2] || ''; // all the rest of the tag
|
||||
|
||||
var type, text, optional, nullable;
|
||||
[type, text, optional, nullable] = jsdoc_type.parse(this.text);
|
||||
|
||||
// @type tags are the only tag that is not allowed to have a {type}!
|
||||
if (this.name === 'type') {
|
||||
typeText.text = typeText.text || typeText.type;
|
||||
delete typeText.type;
|
||||
text = text || type.join('|');
|
||||
type = [];
|
||||
}
|
||||
|
||||
this.type = typeText.type;
|
||||
|
||||
this.text = trim(typeText.text);
|
||||
if (type && type.length) {
|
||||
this.type = type;
|
||||
}
|
||||
if (optional !== null) { this.poptional = optional; }
|
||||
if (nullable !== null) { this.pnullable = nullable; }
|
||||
|
||||
this.text = text;
|
||||
if (longTags.indexOf(this.name) > -1) { // is a tag that uses the long format
|
||||
var [pname, pdesc] = splitPname(this.text);
|
||||
var [pname, pdesc] = parsePname(this.text);
|
||||
this.pname = pname;
|
||||
this.pdesc = pdesc;
|
||||
}
|
||||
@ -85,55 +91,28 @@
|
||||
/**
|
||||
Split the parameter name and parameter desc from the tag text.
|
||||
@private
|
||||
@method splitPname
|
||||
@method parsePname
|
||||
@param {string} tagText
|
||||
@returns Array.<string> The pname and the pdesc.
|
||||
*/
|
||||
function splitPname(tagText) {
|
||||
function parsePname(tagText) {
|
||||
tagText.match(/^(\S+)(\s+(\S.*))?$/);
|
||||
|
||||
return [RegExp.$1, RegExp.$3];
|
||||
}
|
||||
|
||||
/**
|
||||
Split the tag type and remaining tag text from the tag text.
|
||||
@private
|
||||
@method splitType
|
||||
@param {string} tagText
|
||||
@returns Object Like {type: tagType, text: tagText}
|
||||
*/
|
||||
function splitType(tagText) {
|
||||
var type = '',
|
||||
text = tagText,
|
||||
count = 0;
|
||||
|
||||
// I reserve the right to use {@whatever ...} for something unrelated to type
|
||||
if (tagText[0] === '{' && tagText[1] !== '@') {
|
||||
count++;
|
||||
|
||||
for (var i = 1, leni = tagText.length; i < leni; i++) {
|
||||
if (tagText[i] === '{') { count++; }
|
||||
if (tagText[i] === '}') { count--; }
|
||||
if (count === 0) {
|
||||
type = trim(tagText.slice(1, i));
|
||||
text = trim(tagText.slice(i+1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
function synonym(name) {
|
||||
if ( synonym.map.hasOwnProperty(name) ) {
|
||||
return synonym.map[name];
|
||||
}
|
||||
else {
|
||||
return name;
|
||||
}
|
||||
|
||||
return { type: type, text: text };
|
||||
}
|
||||
|
||||
/**
|
||||
Remove leading and trailing whitespace.
|
||||
@private
|
||||
@method trim
|
||||
@param {string} text
|
||||
@returns {string}
|
||||
*/
|
||||
function trim(text) {
|
||||
return text.replace(/^\s+|\s+$/g, '');
|
||||
synonym.map = {
|
||||
'description': 'desc',
|
||||
'function': 'method',
|
||||
'variable': 'member'
|
||||
}
|
||||
|
||||
})();
|
||||
@ -14,12 +14,13 @@
|
||||
load(BASEDIR + 'lib/jsunity.js');
|
||||
|
||||
testSuites = [];
|
||||
load(BASEDIR + 'tests/opts.js');
|
||||
load(BASEDIR + 'tests/docset.js');
|
||||
load(BASEDIR + 'tests/tag_namespace.js');
|
||||
load(BASEDIR + 'tests/opts.js');
|
||||
load(BASEDIR + 'tests/docset.js');
|
||||
load(BASEDIR + 'tests/tag_namespace.js');
|
||||
load(BASEDIR + 'tests/tag_constructor.js');
|
||||
load(BASEDIR + 'tests/tag_const.js');
|
||||
load(BASEDIR + 'tests/tag_enum.js');
|
||||
load(BASEDIR + 'tests/tag_const.js');
|
||||
load(BASEDIR + 'tests/tag_enum.js');
|
||||
load(BASEDIR + 'tests/tag_param.js');
|
||||
|
||||
jsUnity.attachAssertions();
|
||||
jsUnity.log = function (s) { print(s); };
|
||||
|
||||
96
modules/jsdoc/type.js
Normal file
96
modules/jsdoc/type.js
Normal file
@ -0,0 +1,96 @@
|
||||
/**
|
||||
@overview
|
||||
@author Michael Mathews <micmath@gmail.com>
|
||||
@license Apache License 2.0 - See file 'LICENSE.md' in this project.
|
||||
*/
|
||||
|
||||
/**
|
||||
Parse type expressions.
|
||||
@module jsdoc/type
|
||||
*/
|
||||
(function() {
|
||||
|
||||
/**
|
||||
@param {string} tagText
|
||||
@returns {Array.<string>}
|
||||
*/
|
||||
exports.parse = function(tagText) {
|
||||
if (typeof tagText !== 'string') { tagText = ''; }
|
||||
var type = '',
|
||||
types = [],
|
||||
text = '',
|
||||
count = 0;
|
||||
|
||||
// type expressions start with '{'
|
||||
if (tagText[0] === '{') {
|
||||
count++;
|
||||
|
||||
// find matching closer '}'
|
||||
for (var i = 1, leni = tagText.length; i < leni; i++) {
|
||||
if (tagText[i] === '{') { count++; }
|
||||
else if (tagText[i] === '}') { count--; }
|
||||
|
||||
if (count === 0) {
|
||||
type = trim(tagText.slice(1, i));
|
||||
text = trim(tagText.slice(i+1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type === '') { text = tagText; }
|
||||
|
||||
[type, optional] = parseOptional(type);
|
||||
[type, nullable] = parseNullable(type);
|
||||
|
||||
types = parseTypes(type); // make it into an array
|
||||
|
||||
return [types, text, optional, nullable];
|
||||
}
|
||||
|
||||
function parseOptional(type) {
|
||||
var optional = null;
|
||||
|
||||
// {sometype=} means optional
|
||||
if ( /(.+)=$/.test(type) ) {
|
||||
type = RegExp.$1;
|
||||
optional = true;
|
||||
}
|
||||
|
||||
return [type, optional];
|
||||
}
|
||||
|
||||
function parseNullable(type) {
|
||||
var nullable = null;
|
||||
|
||||
// {?sometype} means nullable, {!sometype} means not-nullable
|
||||
if ( /^([\?\!])(.+)$/.test(type) ) {
|
||||
type = RegExp.$2;
|
||||
nullable = (RegExp.$1 === '?')? true : false;
|
||||
}
|
||||
|
||||
return [type, nullable];
|
||||
}
|
||||
|
||||
function parseTypes(type) {
|
||||
var types = [];
|
||||
|
||||
if (type.indexOf('|') > -1) {
|
||||
// remove optional parens
|
||||
if ( /^\s*\(\s*(.+)\s*\)\s*$/.test(type) ) {
|
||||
type = RegExp.$1;
|
||||
}
|
||||
types = type.split(/\s*\|\s*/g);
|
||||
}
|
||||
else {
|
||||
types = [type];
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
/** @private */
|
||||
function trim(text) {
|
||||
return text.replace(/^\s+|\s+$/g, '');
|
||||
}
|
||||
})();
|
||||
@ -18,15 +18,15 @@
|
||||
},
|
||||
|
||||
testConstCompactTag: function() {
|
||||
var doc = docset.getDocsByPath('pi');
|
||||
assertEqual(doc.length, 1, '1 doclet by that name is found.');
|
||||
|
||||
doc = doc[0];
|
||||
var docs = docset.getDocsByPath('pi');
|
||||
assertEqual(docs.length, 1, '1 doclet by that name is found.');
|
||||
|
||||
var doc = docs[0].toObject();
|
||||
|
||||
assertEqual(typeof doc, 'object', 'The found doclet is an object.');
|
||||
assertEqual(doc.tagText('path'), 'pi', 'The found doclet has the expected path.');
|
||||
assertEqual(doc.tagText('type'), 'number', 'The found doclet has the expected type.');
|
||||
assertEqual(doc.tagText('desc'), "The ratio of any circle's circumference to its diameter.", 'The found doclet has the expected desc.');
|
||||
assertEqual(doc.path, 'pi', 'The found doclet has the expected path.');
|
||||
assertEqual(doc.type, 'number', 'The found doclet has the expected type.');
|
||||
assertEqual(doc.desc, "The ratio of any circle's circumference to its diameter.", 'The found doclet has the expected desc.');
|
||||
},
|
||||
|
||||
testConstCompactVerbose: function() {
|
||||
@ -65,9 +65,7 @@ function sample() {
|
||||
|
||||
/**
|
||||
* Euler's number.
|
||||
* @const
|
||||
* @name e
|
||||
* @type number
|
||||
* @const {number} e
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
105
tests/tag_param.js
Normal file
105
tests/tag_param.js
Normal file
@ -0,0 +1,105 @@
|
||||
(function() {
|
||||
var jsdoc = { parser: require('jsdoc/parser') };
|
||||
|
||||
jsdoc.parser.parseFiles(BASEDIR + 'tests/tag_param.js');
|
||||
var docset = jsdoc.parser.result;
|
||||
|
||||
var testSuite = {
|
||||
suiteName: 'tag_param',
|
||||
|
||||
setUp: function() {
|
||||
},
|
||||
|
||||
tearDown: function() {
|
||||
},
|
||||
|
||||
testParamWithSimpleType: function() {
|
||||
var docs = docset.getDocsByPath('Shape');
|
||||
|
||||
assertEqual(docs.length, 1, 'All constructor doclets by that path name are found.');
|
||||
|
||||
var doc = docs[0].toObject(),
|
||||
params = doc.param;
|
||||
//print('>>> doc is '+doc.toSource());
|
||||
//print('>>> params is '+params.toSource());
|
||||
assertEqual(params[0].name, 'top', 'The found parameter has the correct name.');
|
||||
assertEqual(typeof params[0].type, 'object', 'The found parameter has types.');
|
||||
assertEqual(params[0].type.length, 1, 'The found parameter has the correct number of types.');
|
||||
assertEqual(params[0].type[0], 'number', 'The found parameter has the correct type value.');
|
||||
|
||||
},
|
||||
|
||||
testParamWithNullableType: function() {
|
||||
var docs = docset.getDocsByPath('Shape');
|
||||
|
||||
assertEqual(docs.length, 1, 'All constructor doclets by that path name are found.');
|
||||
|
||||
var doc = docs[0].toObject(),
|
||||
params = doc.param;
|
||||
|
||||
assertEqual(params[1].name, 'left', 'The found parameter has the correct name.');
|
||||
assertEqual(typeof params[1].type, 'object', 'The found parameter has types.');
|
||||
assertEqual(params[1].type.length, 1, 'The found parameter has the correct number of types.');
|
||||
assertEqual(params[1].type[0], 'number', 'The found parameter has the correct type value.');
|
||||
assertEqual(params[1].nullable, false, 'The found parameter has the correct !nullable value.');
|
||||
assertEqual(params[2].nullable, true, 'The found parameter has the correct ?nullable value.');
|
||||
},
|
||||
|
||||
testParamWithOptionalType: function() {
|
||||
var docs = docset.getDocsByPath('Shape');
|
||||
|
||||
assertEqual(docs.length, 1, 'All doclets by that path name are found.');
|
||||
|
||||
var doc = docs[0].toObject(),
|
||||
params = doc.param;
|
||||
|
||||
assertEqual(params[3].name, 'fixed', 'The found parameter has the correct name.');
|
||||
assertEqual(typeof params[1].type, 'object', 'The found parameter has types.');
|
||||
assertEqual(params[3].type.length, 1, 'The found parameter has the correct number of types.');
|
||||
assertEqual(params[3].type[0], 'boolean', 'The found parameter has the correct type value.');
|
||||
assertEqual(params[3].nullable, undefined, 'The found parameter has the default nullable value.');
|
||||
assertEqual(params[3].optional, true, 'The found parameter has the correct optional value.');
|
||||
},
|
||||
|
||||
testParamWithMultipleType: function() {
|
||||
var docs = docset.getDocsByPath('rotate');
|
||||
|
||||
assertEqual(docs.length, 1, 'All doclets by that path name are found.');
|
||||
|
||||
var doc = docs[0].toObject(),
|
||||
params = doc.param;
|
||||
|
||||
assertEqual(params[0].name, 'deg', 'The found parameter has the correct name.');
|
||||
assertEqual(typeof params[0].type, 'object', 'The found parameter has types.');
|
||||
assertEqual(params[0].type.length, 2, 'The found parameter has the correct number of types.');
|
||||
assertEqual(params[0].type[0], 'Degree', 'The found parameter has the correct type[0] value.');
|
||||
assertEqual(params[0].type[1], 'number', 'The found parameter has the correct type[1] value.');
|
||||
|
||||
assertEqual(params[1].name, 'axis', 'The found parameter has the correct name.');
|
||||
assertEqual(params[1].optional, true, 'The found parameter has the correct optional.');
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
testSuites.push(testSuite);
|
||||
})();
|
||||
|
||||
function sample() {
|
||||
|
||||
/** @constructor
|
||||
@param {number} top
|
||||
@param {!number} left
|
||||
@param {?number} sides
|
||||
@param {boolean=} fixed
|
||||
*/
|
||||
function Shape(top, left, sides, fixed) {
|
||||
}
|
||||
|
||||
/** @method
|
||||
@param {Degree|number} deg
|
||||
@param [axis]
|
||||
*/
|
||||
function rotate(deg, axis) {
|
||||
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user