diff --git a/lib/jsdoc/schema.js b/lib/jsdoc/schema.js index 395e4481..65270c4f 100644 --- a/lib/jsdoc/schema.js +++ b/lib/jsdoc/schema.js @@ -337,6 +337,10 @@ var DOCLET_SCHEMA = exports.DOCLET_SCHEMA = { type: BOOLEAN_OPTIONAL, optional: true }, + generator: { + type: BOOLEAN, + optional: true + }, ignore: { type: BOOLEAN, optional: true diff --git a/lib/jsdoc/src/visitor.js b/lib/jsdoc/src/visitor.js index 68862dc7..e601b132 100644 --- a/lib/jsdoc/src/visitor.js +++ b/lib/jsdoc/src/visitor.js @@ -359,6 +359,28 @@ function makePrivatePropertyFinisher(parser) { }; } +/** + * Create a function that will mark a doclet as a generator function. + * + * @private + * @param {module:jsdoc/src/parser.Parser} parser - The JSDoc parser. + * @return {function} A function that marks a doclet as a generator function. + */ +function makeGeneratorFinisher(parser) { + return function(e) { + var doclet = e.doclet; + + if (!doclet) { + return; + } + + if (e.code.node.generator || e.code.node.init && e.code.node.init.generator || + e.code.node.value && e.code.node.value.generator) { + doclet.generator = true; + } + }; +} + // TODO: docs function SymbolFound(node, filename, extras) { var self = this; @@ -683,7 +705,9 @@ Visitor.prototype.makeSymbolFoundEvent = function(node, parser, filename) { // handle rest parameters makeRestParamFinisher(parser), // handle async functions - makeAsyncFunctionFinisher(parser) + makeAsyncFunctionFinisher(parser), + // handle generator functions + makeGeneratorFinisher(parser) ]; e = new SymbolFound(node, filename, extras); @@ -731,7 +755,9 @@ Visitor.prototype.makeSymbolFoundEvent = function(node, parser, filename) { // handle rest parameters makeRestParamFinisher(parser), // handle async functions - makeAsyncFunctionFinisher(parser) + makeAsyncFunctionFinisher(parser), + // handle generator functions + makeGeneratorFinisher(parser) ]; // for constructors, we attempt to merge the constructor's docs into the class's docs if (node.kind === 'constructor') { @@ -774,8 +800,13 @@ Visitor.prototype.makeSymbolFoundEvent = function(node, parser, filename) { // like: var i = 0; case Syntax.VariableDeclarator: - // handle async functions - extras.finishers = [makeAsyncFunctionFinisher(parser)]; + + extras.finishers = [ + // handle async functions + makeAsyncFunctionFinisher(parser), + // handle generator functions + makeGeneratorFinisher(parser) + ]; e = new SymbolFound(node, filename, extras); diff --git a/lib/jsdoc/tag/dictionary/definitions.js b/lib/jsdoc/tag/dictionary/definitions.js index e9af6838..6605c5dc 100644 --- a/lib/jsdoc/tag/dictionary/definitions.js +++ b/lib/jsdoc/tag/dictionary/definitions.js @@ -437,6 +437,12 @@ var baseTags = exports.baseTags = { }, synonyms: ['func', 'method'] }, + generator: { + mustNotHaveValue: true, + onTagged: function(doclet, tag) { + doclet.generator = true; + } + }, global: { mustNotHaveValue: true, onTagged: function(doclet, tag) { diff --git a/lib/jsdoc/util/templateHelper.js b/lib/jsdoc/util/templateHelper.js index e933a969..d07de8c0 100644 --- a/lib/jsdoc/util/templateHelper.js +++ b/lib/jsdoc/util/templateHelper.js @@ -637,6 +637,10 @@ exports.getAttribs = function(d) { attribs.push('async'); } + if (d.generator) { + attribs.push('generator'); + } + if (d.virtual) { attribs.push('abstract'); } diff --git a/test/fixtures/generators.js b/test/fixtures/generators.js new file mode 100644 index 00000000..55278042 --- /dev/null +++ b/test/fixtures/generators.js @@ -0,0 +1,28 @@ +'use strict'; + +/** Generate unique IDs starting at 0. */ +function* startsAt0() { + var index = 0; + while (true) { + yield index++; + } +} + +/** Generate unique IDs starting at 1. */ +var startsAt1 = function* startsAt1() { + var index = 1; + while (true) { + yield index++; + } +}; + +/** Generator class. */ +class Generator { + /** Generate unique IDs starting at 2. */ + * startsAt2() { + var index = 2; + while (true) { + yield index++; + } + } +} diff --git a/test/fixtures/generatortag.js b/test/fixtures/generatortag.js new file mode 100644 index 00000000..f1f6ce62 --- /dev/null +++ b/test/fixtures/generatortag.js @@ -0,0 +1,7 @@ +'use strict'; + +/** + * Sample generator function. + * @function idMaker + * @generator + */ diff --git a/test/specs/documentation/generators.js b/test/specs/documentation/generators.js new file mode 100644 index 00000000..7e45765c --- /dev/null +++ b/test/specs/documentation/generators.js @@ -0,0 +1,20 @@ +'use strict'; + +describe('generator functions', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/generators.js'); + var startsAt0 = docSet.getByLongname('startsAt0')[0]; + var startsAt1 = docSet.getByLongname('startsAt1')[0]; + var startsAt2 = docSet.getByLongname('Generator#startsAt2')[0]; + + it('should flag generator functions', function() { + expect(startsAt0.generator).toBe(true); + }); + + it('should flag generator functions assigned to variables', function() { + expect(startsAt1.generator).toBe(true); + }); + + it('should flag generator functions that are method definitions', function() { + expect(startsAt2.generator).toBe(true); + }); +}); diff --git a/test/specs/jsdoc/util/templateHelper.js b/test/specs/jsdoc/util/templateHelper.js index 20e56944..d1e9d349 100644 --- a/test/specs/jsdoc/util/templateHelper.js +++ b/test/specs/jsdoc/util/templateHelper.js @@ -728,6 +728,11 @@ describe("jsdoc/util/templateHelper", function() { doTests(tests, 'async'); }); + it('should detect if a doclet is a generator function', function() { + var tests = { '@generator': 'generator' }; + doTests(tests, 'generator'); + }); + it("should detect multiple attributes", function() { var fdsaFoo = new doclet.Doclet('/** @const module:fdsa~FOO\n@readonly\n@private */', {}); attribs = helper.getAttribs(fdsaFoo); diff --git a/test/specs/tags/generatortag.js b/test/specs/tags/generatortag.js new file mode 100644 index 00000000..fd8f26a8 --- /dev/null +++ b/test/specs/tags/generatortag.js @@ -0,0 +1,10 @@ +'use strict'; + +describe('@generator tag', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/generatortag.js'); + var idMaker = docSet.getByLongname('idMaker')[0]; + + it('should mark the symbol as a generator function', function() { + expect(idMaker.generator).toBe(true); + }); +});