Merge pull request #35 from tschaub/augment

Add inherited members based on @augments or @extends
This commit is contained in:
Michael Mathews 2011-09-23 13:42:46 -07:00
commit d8503ed9ab
5 changed files with 198 additions and 5 deletions

View File

@ -222,6 +222,7 @@ function main() {
indexAll(docs);
require('jsdoc/augment').addInherited(docs);
require('jsdoc/borrow').resolveBorrows(docs);
if (env.opts.explain) {

View File

@ -0,0 +1,121 @@
(function() {
exports.addInherited = function(docs) {
var dependencies = mapDependencies(docs.index);
var sorted = sort(dependencies);
var additions = [];
sorted.forEach(function(name) {
var doclets = docs.index[name];
Array.prototype.push.apply(additions, getAdditions(doclets, docs));
});
additions.forEach(function(doc) {
var name = doc.longname;
if (!(docs.index.hasOwnProperty(name))) {
docs.index[name] = [];
}
docs.index[name].push(doc);
docs.push(doc);
});
}
function mapDependencies(index) {
var doclets, doc, len, dependencies = {};
for (var name in index) {
doclets = index[name];
for (var i=0, ii=doclets.length; i<ii; ++i) {
doc = doclets[i];
if (doc.kind === "class") {
dependencies[name] = {};
len = doc.augments && doc.augments.length || 0;
for (var j=0; j<len; ++j) {
dependencies[name][doc.augments[j]] = true;
}
}
}
}
return dependencies;
}
function getAdditions(doclets, docs) {
var additions = [];
var doc, parents, members, member;
for (var i=0, ii=doclets.length; i<ii; ++i) {
doc = doclets[i];
parents = doc.augments;
if (parents && doc.kind === "class") {
for (var j=0, jj=parents.length; j<jj; ++j) {
members = getMembers(parents[j], docs);
for (var k=0, kk=members.length; k<kk; ++k) {
member = doop(members[k]);
member.memberof = doc.longname;
parts = member.longname.split("#");
parts[0] = doc.longname;
member.longname = parts.join("#");
additions.push(member);
}
}
}
}
return additions;
}
function getMembers(longname, docs) {
var candidate, members = [];
for (var i=0, ii=docs.length; i<ii; ++i) {
candidate = docs[i];
if (candidate.memberof === longname && candidate.scope === "instance") {
members.push(candidate);
}
}
return members;
}
function doop(o) {
if (o instanceof Object && o.constructor != Function) {
var clone = o instanceof Array ? [] : {}, prop;
for (prop in o){
if ( o.hasOwnProperty(prop) ) {
clone[prop] = (o[prop] instanceof Object)? doop(o[prop]) : o[prop];
}
}
return clone;
}
return o;
};
var Sorter = function(dependencies) {
this.dependencies = dependencies;
this.visited = {};
this.sorted = [];
};
Sorter.prototype = {
sort: function() {
for (var key in this.dependencies) {
this.visit(key);
}
return this.sorted;
},
visit: function(key) {
if (!(key in this.visited)) {
this.visited[key] = true;
if (!(key in this.dependencies)) {
throw new Error("Missing dependency: " + key);
}
for (var path in this.dependencies[key]) {
this.visit(path);
}
this.sorted.push(key);
}
}
};
function sort(dependencies) {
var sorter = new Sorter(dependencies);
return sorter.sort();
};
})();

View File

@ -1,14 +1,39 @@
/**
* @constructor
*/
* @constructor
*/
function Foo() {
/** First property */
this.prop1 = true;
}
/**
* Second property
* @type {String}
*/
Foo.prototype.prop2 = "parent prop2";
/**
* First parent method.
*/
Foo.prototype.method1 = function() {};
/**
* Second parent method.
*/
Foo.prototype.method2 = function() {};
/**
* @extends Foo
*/
* @constructor
* @extends Foo
*/
function Bar() {
/** Thrid prop **/
this.prop3 = true;
}
/**
* Second child method.
*/
Bar.prototype.method2 = function() {};

View File

@ -44,6 +44,11 @@ var testhelpers = {
doclets = testParser.parse('javascript:' + sourceCode);
testhelpers.indexAll(doclets);
require('jsdoc/augment').addInherited(doclets);
// test assume borrows have not yet been resolved
// require('jsdoc/borrow').resolveBorrows(doclets);
return {
doclets: doclets,

View File

@ -1,10 +1,51 @@
(function() {
var docSet = testhelpers.getDocSetFromFile('test/cases/augmentstag.js'),
foo = docSet.getByLongname('Foo')[0],
bar = docSet.getByLongname('Bar')[0];
fooProp1 = docSet.getByLongname('Foo#prop1')[0],
fooProp2 = docSet.getByLongname('Foo#prop2')[0],
fooProp3 = docSet.getByLongname('Foo#prop3')[0],
fooMethod1 = docSet.getByLongname('Foo#method1')[0],
fooMethod2 = docSet.getByLongname('Foo#method2')[0],
bar = docSet.getByLongname('Bar')[0],
barProp1 = docSet.getByLongname('Bar#prop1')[0],
barProp2 = docSet.getByLongname('Bar#prop2')[0],
barProp3 = docSet.getByLongname('Bar#prop3')[0],
barMethod1 = docSet.getByLongname('Bar#method1')[0],
barMethod2 = docSet.getByLongname('Bar#method2')[0];
test('When a symbol has an @augments tag, the doclet has a augments property that includes that value.', function() {
assert.equal(typeof bar.augments, 'object');
assert.equal(bar.augments[0], 'Foo');
});
test('When an object is extended, the original is not modified', function() {
assert.equal(fooProp3, undefined);
});
test('When an object is extended, it inherits properties set in parent constructor', function() {
assert.equal(fooProp1.memberof, "Foo");
assert.equal(barProp1.memberof, "Bar");
assert.equal(barProp1.description, fooProp1.description);
});
test('When an object is extended, it inherits properties set on parent prototype', function() {
assert.equal(fooProp2.memberof, "Foo");
assert.equal(barProp2.memberof, "Bar");
assert.equal(barProp2.description, fooProp2.description);
});
test('When an object is extended, it inherits methods set on parent prototype', function() {
assert.equal(fooMethod1.memberof, "Foo");
assert.equal(barMethod1.memberof, "Bar");
assert.equal(barMethod1.description, fooMethod1.description);
});
test('When an object is extended, it may override methods set on parent prototype', function() {
assert.equal(fooMethod2.memberof, "Foo");
assert.equal(fooMethod2.description, "Second parent method.");
assert.equal(barMethod2.memberof, "Bar");
assert.equal(barMethod2.description, "Second child method.");
});
})();