diff --git a/modules/jsdoc/doclet.js b/modules/jsdoc/doclet.js index 66a1b3ac..7947f261 100644 --- a/modules/jsdoc/doclet.js +++ b/modules/jsdoc/doclet.js @@ -114,6 +114,22 @@ this.tags[this.tags.length] = parse_tag.fromText(tagName + ' ' + tagValue); } + /** + Return the first tag with the given name. + @method Doclet#getTag + @param {String} tagName + @returns {Tag} The irst found tag with that name. + */ + Doclet.prototype.getTag = function(tagName) { + for (var i = 0, leni = this.tags.length; i < leni; i++) { + if (this.tags[i].name === tagName) { + return this.tags[i]; + } + } + + return null; + } + /** Does a tag with the given name exist in this doclet? @method Doclet#hasTag @@ -249,7 +265,8 @@ if (tagAbout.setsDocletAccess) { tags[tags.length] = parse_tag.fromText('access '+tags[i].name); } - else if (tags[i].name === 'const') { + + if (tags[i].name === 'const') { tags[tags.length] = parse_tag.fromText('attribute constant'); } else if (tags[i].name === 'readonly') { @@ -319,7 +336,11 @@ tags[tags.length] = parse_tag.fromText('memberof ' + memberof); } } + +// TODO should iterate over the tags here rather than letting doclet decide +// which ones to pick and choose from. the tag dictionary should tell us what to do + // now that we have a doclet object we can do some final adjustments function postprocess(doclet) { if ( doclet.hasTag('class') && !doclet.hasTag('constructor') ) { doclet.tags[doclet.tags.length] = parse_tag.fromText('isa constructor'); @@ -344,6 +365,21 @@ doclet.tags[doclet.tags.length] = parse_tag.fromText('attribute constant'); } } + + if ( doclet.hasTag('property') ) { + if ( !doclet.hasTag('type') ) { + var propertyTag = doclet.getTag('property'), + types = []; + if (propertyTag.type) { + if (typeof propertyTag.type === 'string') types = [propertyTag.type]; + else types = propertyTag.type; + + for (var i = 0, leni = types.length; i < leni; i++) { + doclet.tags[doclet.tags.length] = parse_tag.fromText('type '+types[i]); + } + } + } + } } function DocTagConflictError(message) { diff --git a/modules/jsdoc/name.js b/modules/jsdoc/name.js index fc6f8c36..58057f79 100644 --- a/modules/jsdoc/name.js +++ b/modules/jsdoc/name.js @@ -59,7 +59,7 @@ // if name doesn't already have a docspace and needs one // the namespace should appear in the path but not the name - if (tagDictionary.lookUp(isa).isDocspace) { + if (tagDictionary.lookUp(isa).setsDocletDocspace) { if ( /^[a-z_$-]+:(\S+)/i.test(name) ) { name = RegExp.$1; } @@ -86,6 +86,8 @@ var atoms = [], cursor = 0; path = path.replace(/(".+?")/g, function($) { + $ = $.slice(1, -1); + var token = '@' + atoms.length + '@'; atoms.push($); return token; @@ -102,6 +104,10 @@ for (var i = 0, leni = atoms.length; i < leni; i++) { prefix = prefix.replace('@'+i+'@', atoms[i]); shortname = shortname.replace('@'+i+'@', atoms[i]); + + // remove quotes from shortnames + ///^"(.+)"$/.test(shortname); + //if (RegExp.$1) { shortname = RegExp.$1; } } return [prefix, shortname]; diff --git a/modules/jsdoc/tag.js b/modules/jsdoc/tag.js index 13a1b8d9..3cf0a500 100644 --- a/modules/jsdoc/tag.js +++ b/modules/jsdoc/tag.js @@ -42,7 +42,7 @@ this.pdesc = ''; // raw is like: "tagname andsometagtext" - var parts = this.raw.match(/^\s*(\S+)(?:\s+([\s\S]*))?$/); + var parts = this.raw.match(/^\s*(\S+)(?:\s([\s\S]*))?$/); if (parts) { this.name = (parts[1] || '').toLowerCase(); // like @name @@ -54,11 +54,9 @@ var tagAbout = tagDictionary.lookUp(this.name); if (!tagAbout.keepsWhitespace) { - this.value = trim(tagText); - } - else { - this.value = tagText; + tagText = trim(tagText); } + this.value = tagText; if (tagAbout.canHaveType) { var [ diff --git a/modules/jsdoc/tagdictionary.js b/modules/jsdoc/tagdictionary.js index 5b158f26..9df1bea2 100644 --- a/modules/jsdoc/tagdictionary.js +++ b/modules/jsdoc/tagdictionary.js @@ -48,8 +48,9 @@ isExported : false, // this tag should appear as a top level property in the doclet? setsDocletIsa : false, // the name of this tag is used to define the doclet's isa property setsDocletName : false, // this tag can be used to name the doclet - setsDocletAccess: false, - isDocspace : false, // the name of this tag becomes the docspace for the doclet name, like "event:" + setsDocletAccess: false, // the name of this tag becomes the access of the doclet + setsDocletType : false, // the type of this tag becomes th type of the doclet + setsDocletDocspace : false, // the name of this tag becomes the docspace for the doclet name, like "event:" canHaveType : false, // this tag can have a {type} canHavePname : false, // this tag can have a parameter-type name canHavePdesc : false, // this tag can have a parameter-type desc @@ -74,7 +75,7 @@ // } // @desc - new TagDefinition('desc', { + new TagDefinition('desc', { // t isExported: true }); @@ -94,18 +95,18 @@ }); // @memberof - new TagDefinition('memberof', { + new TagDefinition('memberof', { //t isExported: true }); // @namespace - new TagDefinition('namespace', { + new TagDefinition('namespace', { //t setsDocletIsa: true, setsDocletName: true }); // @constructor - new TagDefinition('constructor', { + new TagDefinition('constructor', { //t setsDocletIsa: true, setsDocletName: true }); @@ -123,14 +124,14 @@ }); // @file|overview|fileoverview - new TagDefinition('file', { + new TagDefinition('file', { //t setsDocletIsa: true, setsDocletName: true, - isDocspace: true + setsDocletDocspace: true }); // @method - new TagDefinition('method', { + new TagDefinition('method', { //t canHaveType: true, canHavePname: true, canHavePdesc: true, @@ -138,25 +139,26 @@ }); // @property - new TagDefinition('property', { + new TagDefinition('property', { //t canHaveType: true, canHavePname: true, canHavePdesc: true, - setsDocletName: true + setsDocletName: true, + setsDocletType: true }); // @event new TagDefinition('event', { setsDocletIsa: true, setsDocletName: true, - isDocspace: true + setsDocletDocspace: true }); // @module new TagDefinition('module', { setsDocletIsa: true, setsDocletName: true, - isDocspace: true + setsDocletDocspace: true }); // @example diff --git a/test/runall.js b/test/runall.js index 9817e896..85f85954 100644 --- a/test/runall.js +++ b/test/runall.js @@ -5,6 +5,7 @@ load(BASEDIR + '/test/tests/04_jsdoc_docset.js'); load(BASEDIR + '/test/tests/05_jsdoc_doclet.js'); load(BASEDIR + '/test/tests/06_jsdoc_tag.js'); +load(BASEDIR + '/test/tests/08_tag_name.js'); load(BASEDIR + '/test/tests/09_tag_desc.js'); load(BASEDIR + '/test/tests/10_tag_constructor.js'); load(BASEDIR + '/test/tests/11_tag_namespace.js'); @@ -13,6 +14,7 @@ load(BASEDIR + '/test/tests/13_tag_method.js'); load(BASEDIR + '/test/tests/14_tag_member.js'); load(BASEDIR + '/test/tests/15_tag_type.js'); load(BASEDIR + '/test/tests/16_tag_return.js'); +load(BASEDIR + '/test/tests/17_tag_example.js'); load(BASEDIR + '/test/tests/20_tag_file.js'); diff --git a/test/tests/08_tag_name.js b/test/tests/08_tag_name.js new file mode 100644 index 00000000..540b8d4b --- /dev/null +++ b/test/tests/08_tag_name.js @@ -0,0 +1,73 @@ +(function() { + var jsdoc, + doclets; + + JSpec.describe('@name', function() { + + before(function() { + // docsets can only be created by parsers + jsdoc = { + tag: require('jsdoc/tag'), + parser: require('jsdoc/parser') + }; + jsdoc.parser.parseFiles(BASEDIR + 'test/tests/08_tag_name.js'); + doclets = jsdoc.parser.result; + }); + + describe('A doclet that has a @name tag followed by a simple string', function() { + it('should have an `name` property set to that string', function() { + var doclet = doclets[0].toObject(); + expect(doclet).to(have_property, 'name'); + expect(doclet.name).to(eql, 'Tipsy'); + }); + }); + + describe('A doclet that has a @name tag followed by a dotted string', function() { + it('should have an `name` property set to the last segment of that string', function() { + var doclet = doclets[1].toObject(); + expect(doclet).to(have_property, 'name'); + expect(doclet.name).to(eql, 'LaLa'); + }); + }); + + describe('A doclet that has a @name tag followed by a dotted string with quotes', function() { + it('should have an `name` property set to the last entire quoted segment of that string', function() { + var doclet = doclets[2].toObject(); + expect(doclet).to(have_property, 'name'); + expect(doclet.name).to(eql, 'and.don\'t.forget#Po!'); + }); + }); + + describe('A doclet that has a @name tag followed by a number', function() { + it('should have an `name` property set to the number', function() { + var doclet = doclets[3].toObject(); + expect(doclet).to(have_property, 'name'); + expect(doclet.name).to(eql, '0'); + }); + }); + }); +})(); + +(function testarea() { + + /** + @name Tipsy + @isa property + */ + + /** + @name Tubbie.LaLa + @isa property + */ + + /** + @name Tubbie."and.don't.forget#Po!" + @isa property + */ + + /** + @name Custards.0 + @isa property + */ + +})(); \ No newline at end of file diff --git a/test/tests/12_tag_property.js b/test/tests/12_tag_property.js index 88491e43..0e6296c0 100644 --- a/test/tests/12_tag_property.js +++ b/test/tests/12_tag_property.js @@ -14,7 +14,7 @@ doclets = jsdoc.parser.result; }); - describe('A doclet with a named property tag attached to a namespace', function() { + describe('A doclet with a named @property attached to a namespace', function() { it('should have an `isa` property set to "property"', function() { var doclet = doclets[2].toObject(); expect(doclet).to(have_property, 'isa'); @@ -34,7 +34,7 @@ }); }); - describe('A doclet with a named property tag attached to a constructor', function() { + describe('A doclet with a named @property and a type and a description', function() { it('should have an `isa` property set to "property"', function() { var doclet = doclets[3].toObject(); expect(doclet).to(have_property, 'isa'); @@ -47,36 +47,89 @@ expect(doclet.name).to(eql, 'bah'); }); - it('should have a `memberof` property set to the parent object name', function() { + it('should have a `type` property set to the parent given type', function() { var doclet = doclets[3].toObject(); - expect(doclet).to(have_property, 'memberof'); - expect(doclet.memberof).to(eql, 'bar'); + expect(doclet).to(have_property, 'type'); + expect(doclet.type).to(eql, ['string', 'number']); + }); + + it('should have a `desc` property set to the given description', function() { + var doclet = doclets[3].toObject(); + expect(doclet).to(have_property, 'desc'); + expect(doclet.desc).to(eql, 'Here is a description.'); }); }); - describe('A doclet with a named property tag after to a constructor tag', function() { + describe('A doclet with a named @property after to a constructor tag', function() { it('should be a constructor', function() { var doclet = doclets[4].toObject(); expect(doclet).to(have_property, 'isa'); + expect(doclet.isa).to(eql, 'constructor'); }); }); + describe('A doclet with a named @variable tag and a description', function() { + it('should have an `isa` property set to "property"', function() { + var doclet = doclets[5].toObject(); + expect(doclet).to(have_property, 'isa'); + expect(doclet.isa).to(eql, 'property'); + }); + + it('should have a `name` property set to the given name', function() { + var doclet = doclets[5].toObject(); + expect(doclet).to(have_property, 'name'); + expect(doclet.name).to(eql, 'zub'); + }); + + it('should have a `desc` property set to the given description', function() { + var doclet = doclets[5].toObject(); + expect(doclet).to(have_property, 'desc'); + expect(doclet.desc).to(eql, 'The description here.'); + }); + }); + + describe('A doclet with no name and a typed @property tag', function() { + it('should have an `isa` property set to "property"', function() { + var doclet = doclets[6].toObject(); + expect(doclet).to(have_property, 'isa'); + expect(doclet.isa).to(eql, 'property'); + }); + + it('should have a `type` property set to the given type', function() { + var doclet = doclets[6].toObject(); + expect(doclet).to(have_property, 'type'); + expect(doclet.type).to(eql, 'Function'); + }); + + it('should have a `name` property set to the name in the code', function() { + var doclet = doclets[6].toObject(); + expect(doclet).to(have_property, 'name'); + expect(doclet.name).to(eql, 'onShow'); + }); + }); + }); })(); (function testarea() { - - /** @namespace foo */ - /** @constructor bar */ + /** @namespace foo */ + var foo = {}, onShow, callbacks = [function(){}]; + /** @constructor bar */ + + + /** @property foo.fah */ - /** @property foo.fah */ + /** @property {string|number} bar.bah Here is a description. */ - /** @property bar.bah */ - - /** @constructor Zub - @property {string} zip - */ + /** @constructor Zub + @property {string} zip + */ + + /** @variable zub - The description here. */ + + /** @property {Function} */ + onShow = callbacks[0]; })(); \ No newline at end of file diff --git a/test/tests/13_tag_method.js b/test/tests/13_tag_method.js index 7fb83c16..99946060 100644 --- a/test/tests/13_tag_method.js +++ b/test/tests/13_tag_method.js @@ -14,7 +14,7 @@ doclets = jsdoc.parser.result; }); - describe('A doclet with a named method tag attached to a namespace', function() { + describe('A doclet with a named @method attached to a namespace', function() { it('should have an `isa` property set to "method"', function() { var doclet = doclets[2].toObject(); expect(doclet).to(have_property, 'isa'); @@ -34,7 +34,7 @@ }); }); - describe('A doclet with a named method tag attached to a constructor', function() { + describe('A doclet with a named @method attached to a constructor', function() { it('should have an `isa` property set to "method"', function() { var doclet = doclets[3].toObject(); expect(doclet).to(have_property, 'isa'); @@ -54,6 +54,20 @@ }); }); + describe('A doclet with a named @function tag', function() { + it('should have an `isa` property set to "method"', function() { + var doclet = doclets[4].toObject(); + expect(doclet).to(have_property, 'isa'); + expect(doclet.isa).to(eql, 'method'); + }); + + it('should have a `name` property set to the given name"', function() { + var doclet = doclets[4].toObject(); + expect(doclet).to(have_property, 'name'); + expect(doclet.name).to(eql, 'zub'); + }); + }); + }); })(); @@ -67,4 +81,6 @@ /** @method bar.bah */ + /** @function zub */ + })(); \ No newline at end of file diff --git a/test/tests/17_tag_example.js b/test/tests/17_tag_example.js new file mode 100644 index 00000000..bf073faa --- /dev/null +++ b/test/tests/17_tag_example.js @@ -0,0 +1,56 @@ +(function() { + var jsdoc, + doclets; + + JSpec.describe('@example', function() { + + before(function() { + // docsets can only be created by parsers + jsdoc = { + tag: require('jsdoc/tag'), + parser: require('jsdoc/parser') + }; + jsdoc.parser.parseFiles(BASEDIR + 'test/tests/17_tag_example.js'); + + doclets = jsdoc.parser.result.map(function($){ return $.toObject(); }); + }); + + describe('A doclet with a @example tag', function() { + it('should have an `example` property', function() { + var doclet = doclets[0]; + expect(doclet).to(have_property, 'example'); + }); + }); + + describe('the value of the `example` property', function() { + it('should preserve all whitespace', function() { + var doclet = doclets[0]; + expect(doclet.example).to(eql, ' var myresult;\n myresult = foo(a, b);\n'); + }); + }); + + describe('A doclet with a2 @example tags', function() { + it('should have an `example` property with length of 2', function() { + var doclet = doclets[1]; + expect(doclet).to(have_property, 'example'); + expect(doclet.example.length).to(eql, 2); + }); + }); + }); +})(); + +(function testarea() { + + /** + * @function foo + * @example + * var myresult; + * myresult = foo(a, b); + */ + + /** + * @function bar + * @example one fish + * @example two fish + */ +})(); \ No newline at end of file