diff --git a/lib/jsdoc/tag/type.js b/lib/jsdoc/tag/type.js index 4f6ac507..9eab4fbe 100644 --- a/lib/jsdoc/tag/type.js +++ b/lib/jsdoc/tag/type.js @@ -168,7 +168,10 @@ function parseName(tagInfo) { } /** @private */ -function getTypeStrings(parsedType) { +function getTypeStrings(parsedType, isOutermostType) { + var applications; + var typeString; + var types = []; var catharsis = require('catharsis'); @@ -192,7 +195,19 @@ function getTypeStrings(parsedType) { types.push('Object'); break; case TYPES.TypeApplication: - types.push( catharsis.stringify(parsedType) ); + // if this is the outermost type, we strip the modifiers; otherwise, we keep them + if (isOutermostType) { + applications = parsedType.applications.map(function(application) { + return getTypeStrings(application); + }).join(', '); + typeString = util.format( '%s.<%s>', getTypeStrings(parsedType.expression), + applications ); + + types.push(typeString); + } + else { + types.push( catharsis.stringify(parsedType) ); + } break; case TYPES.TypeUnion: parsedType.elements.forEach(function(element) { @@ -244,7 +259,7 @@ function parseTypeExpression(tagInfo) { } if (parsedType) { - tagInfo.type = tagInfo.type.concat( getTypeStrings(parsedType) ); + tagInfo.type = tagInfo.type.concat( getTypeStrings(parsedType, true) ); // Catharsis and JSDoc use the same names for 'optional' and 'nullable'... ['optional', 'nullable'].forEach(function(key) { diff --git a/lib/jsdoc/util/templateHelper.js b/lib/jsdoc/util/templateHelper.js index bdf246cb..876ae8b8 100644 --- a/lib/jsdoc/util/templateHelper.js +++ b/lib/jsdoc/util/templateHelper.js @@ -519,6 +519,13 @@ exports.getAttribs = function(d) { attribs.push('constant'); } + if (d.nullable === true) { + attribs.push('nullable'); + } + else if (d.nullable === false) { + attribs.push('non-null'); + } + return attribs; }; diff --git a/templates/default/publish.js b/templates/default/publish.js index 43266d46..f7aa3374 100644 --- a/templates/default/publish.js +++ b/templates/default/publish.js @@ -7,6 +7,7 @@ var template = require('jsdoc/template'), taffy = require('taffydb').taffy, logger = require('jsdoc/util/logger'), helper = require('jsdoc/util/templateHelper'), + util = require('util'), htmlsafe = helper.htmlsafe, linkto = helper.linkto, resolveAuthorLinks = helper.resolveAuthorLinks, @@ -58,33 +59,125 @@ function needsSignature(doclet) { return needsSig; } -function addSignatureParams(f) { - var params = helper.getSignatureParams(f, 'optional'); +function getSignatureAttributes(item) { + var attributes = []; - f.signature = (f.signature || '') + '('+params.join(', ')+')'; + if (item.optional) { + attributes.push('opt'); + } + + if (item.nullable === true) { + attributes.push('nullable'); + } + else if (item.nullable === false) { + attributes.push('non-null'); + } + + return attributes; +} + +function updateItemName(item) { + var attributes = getSignatureAttributes(item); + var itemName = item.name || ''; + + if (item.variable) { + itemName = '…' + itemName; + } + + if (attributes && attributes.length) { + itemName = util.format( '%s%s', itemName, + attributes.join(', ') ); + } + + return itemName; +} + +function addParamAttributes(params) { + return params.map(updateItemName); +} + +function buildItemTypeStrings(item) { + var types = []; + + if (item.type && item.type.names) { + item.type.names.forEach(function(name) { + types.push( linkto(name, htmlsafe(name)) ); + }); + } + + return types; +} + +function buildAttribsString(attribs) { + var attribsString = ''; + + if (attribs && attribs.length) { + attribsString = htmlsafe( util.format('(%s) ', attribs.join(', ')) ); + } + + return attribsString; +} + +function addNonParamAttributes(items) { + var types = []; + + items.forEach(function(item) { + types = types.concat( buildItemTypeStrings(item) ); + }); + + return types; +} + +function addSignatureParams(f) { + var params = f.params ? addParamAttributes(f.params) : []; + + f.signature = util.format( '%s(%s)', (f.signature || ''), params.join(', ') ); } function addSignatureReturns(f) { - var returnTypes = helper.getSignatureReturns(f); + var attribs = []; + var attribsString = ''; + var returnTypes = []; + var returnTypesString = ''; + + // jam all the return-type attributes into an array. this could create odd results (for example, + // if there are both nullable and non-nullable return types), but let's assume that most people + // who use multiple @return tags aren't using Closure Compiler type annotations, and vice-versa. + if (f.returns) { + f.returns.forEach(function(item) { + helper.getAttribs(item).forEach(function(attrib) { + if (attribs.indexOf(attrib) === -1) { + attribs.push(attrib); + } + }); + }); + + attribsString = buildAttribsString(attribs); + } + + if (f.returns) { + returnTypes = addNonParamAttributes(f.returns); + } + if (returnTypes.length) { + returnTypesString = util.format( ' → %s{%s}', attribsString, returnTypes.join('|') ); + } f.signature = '' + (f.signature || '') + '' + - '' + - (returnTypes && returnTypes.length ? ' → {' + returnTypes.join('|') + '}' : '') + - ''; + '' + returnTypesString + ''; } function addSignatureTypes(f) { - var types = helper.getSignatureTypes(f); + var types = f.type ? buildItemTypeStrings(f) : []; - f.signature = (f.signature || '') + ''+(types.length? ' :'+types.join('|') : '')+''; + f.signature = (f.signature || '') + '' + + (types.length ? ' :' + types.join('|') : '') + ''; } function addAttribs(f) { var attribs = helper.getAttribs(f); + var attribsString = buildAttribsString(attribs); - f.attribs = '' + htmlsafe(attribs.length ? - // we want the template output to say 'abstract', not 'virtual' - '<' + attribs.join(', ').replace('virtual', 'abstract') + '> ' : '') + ''; + f.attribs = util.format('%s', attribsString); } function shortenPaths(files, commonPrefix) { diff --git a/templates/default/static/styles/jsdoc-default.css b/templates/default/static/styles/jsdoc-default.css index bf6b51cf..124ef6c7 100644 --- a/templates/default/static/styles/jsdoc-default.css +++ b/templates/default/static/styles/jsdoc-default.css @@ -58,8 +58,7 @@ section display: none; } -.optional:after { - content: "opt"; +.signature-attributes { font-size: 60%; color: #aaa; font-style: italic;