diff --git a/lib/jsdoc/src/visitor.js b/lib/jsdoc/src/visitor.js index 20ecdcc8..c665cb22 100644 --- a/lib/jsdoc/src/visitor.js +++ b/lib/jsdoc/src/visitor.js @@ -229,6 +229,7 @@ Visitor.prototype.visitNode = function(node, parser, filename) { }; // TODO: docs +// TODO: note that it's essential to call this function before you try to resolve names! // TODO: may be able to get rid of this using knownAliases function trackVars(parser, node, e) { var enclosingScopeId = node.enclosingScope ? node.enclosingScope.nodeId : @@ -256,20 +257,17 @@ Visitor.prototype.makeSymbolFoundEvent = function(node, parser, filename) { switch (node.type) { // like: i = 0; case Syntax.AssignmentExpression: - // falls through - - // like: var i = 0; - case Syntax.VariableDeclarator: e = new SymbolFound(node, filename, extras); + trackVars(parser, node, e); + basename = parser.getBasename(e.code.name); - // TODO: handle code that does things like 'var self = this'; - if (basename !== 'this') { + // in addition to `this`, we need to special-case the string `____`, or else our + // `@lends` tag hackery causes things to appear in the wrong scope + if (basename !== 'this' && basename !== '____') { e.code.funcscope = parser.resolveVar(node, basename); } - trackVars(parser, node, e); - break; // like: function foo() {} @@ -308,6 +306,16 @@ Visitor.prototype.makeSymbolFoundEvent = function(node, parser, filename) { break; + // like: var i = 0; + case Syntax.VariableDeclarator: + e = new SymbolFound(node, filename, extras); + + trackVars(parser, node, e); + + basename = parser.getBasename(e.code.name); + + break; + default: // ignore } diff --git a/test/fixtures/lends6.js b/test/fixtures/lends6.js new file mode 100644 index 00000000..db6bb069 --- /dev/null +++ b/test/fixtures/lends6.js @@ -0,0 +1,29 @@ +define([], function() { + var Person = makeClass( + /** @lends Person.prototype */ + { + /** @constructs */ + initialize: function(name) { + this.name = name; + }, + /** Speak a message. */ + say: function(message) { + return this.name + " says: " + message; + } + } + ); + + var Robot = makeClass( + /** @lends Robot.prototype */ + { + /** @constructs */ + initialize: function(name) { + this.name = name; + }, + /** Feign emotion. */ + emote: function() { + return this.name + " loves you!"; + } + } + ); +}); diff --git a/test/specs/documentation/lends.js b/test/specs/documentation/lends.js index 9f0c4293..85253c80 100644 --- a/test/specs/documentation/lends.js +++ b/test/specs/documentation/lends.js @@ -1,6 +1,10 @@ /*global describe, expect, it, jasmine */ describe("lends", function() { describe("when a documented member is inside an object literal associated with a @lends tag", function() { + function removeUndocumented($) { + return !$.undocumented; + } + describe("standard case", function() { var docSet = jasmine.getDocSetFromFile('test/fixtures/lends.js'), init = docSet.getByLongname('Person#initialize'), @@ -67,12 +71,8 @@ describe("lends", function() { describe("case that uses @lends within nested function calls", function() { var docSet = jasmine.getDocSetFromFile('test/fixtures/lends5.js'); - var person = docSet.getByLongname('Person').filter(function(d) { - return !d.undocumented; - })[0]; - var say = docSet.getByLongname('Person#say').filter(function(d) { - return !d.undocumented; - })[0]; + var person = docSet.getByLongname('Person').filter(removeUndocumented)[0]; + var say = docSet.getByLongname('Person#say').filter(removeUndocumented)[0]; it("The class constructor should be documented with the name of the lendee", function() { expect(person).toBeDefined(); @@ -84,6 +84,36 @@ describe("lends", function() { expect(say).toBeDefined(); }); }); + + describe('case that uses @lends twice within a closure', function() { + var docSet = jasmine.getDocSetFromFile('test/fixtures/lends6.js'); + + it('The first class with a @lends tag should appear in the parse results', function() { + var person = docSet.getByLongname('Person').filter(removeUndocumented)[0]; + var say = docSet.getByLongname('Person#say').filter(removeUndocumented)[0]; + + expect(person).toBeDefined(); + expect(person.name).toBe('Person'); + expect(person.kind).toBe('class'); + + expect(say).toBeDefined(); + expect(say.name).toBe('say'); + expect(say.kind).toBe('function'); + }); + + it('The second class with a @lends tag should appear in the parse results', function() { + var robot = docSet.getByLongname('Robot').filter(removeUndocumented)[0]; + var emote = docSet.getByLongname('Robot#emote').filter(removeUndocumented)[0]; + + expect(robot).toBeDefined(); + expect(robot.name).toBe('Robot'); + expect(robot.kind).toBe('class'); + + expect(emote).toBeDefined(); + expect(emote.name).toBe('emote'); + expect(emote.kind).toBe('function'); + }); + }); }); describe("when a documented member is inside an objlit associated with a @lends tag that has no value.", function() {