diff --git a/lib/jsdoc/name.js b/lib/jsdoc/name.js index 5cadacfe..b305fb5e 100644 --- a/lib/jsdoc/name.js +++ b/lib/jsdoc/name.js @@ -65,9 +65,10 @@ var scopeToPunc = exports.scopeToPunc = { var puncToScope = exports.puncToScope = _.invert(scopeToPunc); var DEFAULT_SCOPE = SCOPE.NAMES.STATIC; -var REGEXP_SCOPE_PUNC = '([' + _.values(SCOPE.PUNC) + '])'; -var REGEXP_LEADING_SCOPE = new RegExp('^' + REGEXP_SCOPE_PUNC); -var REGEXP_TRAILING_SCOPE = new RegExp(REGEXP_SCOPE_PUNC + '$'); +var SCOPE_PUNC = _.values(SCOPE.PUNC); +var REGEXP_SCOPE_PUNC = '[' + SCOPE_PUNC.join() + ']'; +var REGEXP_LEADING_SCOPE = new RegExp('^(' + REGEXP_SCOPE_PUNC + ')'); +var REGEXP_TRAILING_SCOPE = new RegExp('(' + REGEXP_SCOPE_PUNC + ')$'); function nameIsLongname(name, memberof) { var regexp = new RegExp('^' + memberof + REGEXP_SCOPE_PUNC); @@ -206,18 +207,19 @@ exports.applyNamespace = function(longname, ns) { return longname; }; -/** - Given a longname like "a.b#c(2)", slice it up into an object - containing the memberof, the scope, the name, and variation. - @param {string} longname - @param {string} forcedMemberof - @returns {object} Representing the properties of the given name. - */ -exports.shorten = function(longname, forcedMemberof) { - // quoted strings in a longname are atomic, convert to tokens - var atoms = [], token; +// TODO: docs +function shorten(longname, sliceChars, forcedMemberof) { + var i; + var memberof = ''; + var name = ''; + var parts; + var partsRegExp; + var scope = ''; + var token; + var tokens = []; + var variation; - // handle quoted names like foo["bar"] or foo['bar'] + // quoted strings in a longname are atomic, so we convert them to tokens longname = longname.replace(/(\[?["'].+?["']\]?)/g, function($) { var dot = ''; if ( /^\[/.test($) ) { @@ -225,34 +227,31 @@ exports.shorten = function(longname, forcedMemberof) { $ = $.replace( /^\[/g, '' ).replace( /\]$/g, '' ); } - token = '@{' + atoms.length + '}@'; - atoms.push($); + token = '@{' + tokens.length + '}@'; + tokens.push($); return dot + token; // foo["bar"] => foo.@{1}@ }); - var name = '', - scope = '', // ., ~, or # - memberof = '', - parts, - variation; - longname = prototypeToPunc(longname); - if (typeof forcedMemberof !== 'undefined') { + if (forcedMemberof !== undefined) { + partsRegExp = new RegExp('^(.*?)([' + sliceChars.join() + ']?)$'); name = longname.substr(forcedMemberof.length); - parts = forcedMemberof.match(/^(.*?)([#.~]?)$/); + parts = forcedMemberof.match(partsRegExp); - if (parts[1]) { memberof = parts[1] || forcedMemberof; } - if (parts[2]) { scope = parts[2]; } + if (parts[1]) { + memberof = parts[1] || forcedMemberof; + } + if (parts[2]) { + scope = parts[2]; + } } - else { - parts = longname ? - (longname.match( /^(:?(.+)([#.~]))?(.+?)$/ ) || []).reverse() : - ['']; - - name = parts[0] || ''; // ensure name is always initialised to avoid error being thrown when calling replace on undefined [gh-24] - scope = parts[1] || ''; // ., ~, or # + else if (longname) { + parts = (longname.match(new RegExp('^(:?(.+)([' + sliceChars.join() + ']))?(.+?)$')) || []) + .reverse(); + name = parts[0] || ''; + scope = parts[1] || ''; memberof = parts[2] || ''; } @@ -262,17 +261,71 @@ exports.shorten = function(longname, forcedMemberof) { variation = RegExp.$2; } - //// restore quoted strings back again - var i = atoms.length; + // restore quoted strings + i = tokens.length; while (i--) { - longname = longname.replace('@{' + i + '}@', atoms[i]); - memberof = memberof.replace('@{' + i + '}@', atoms[i]); - scope = scope.replace('@{' + i + '}@', atoms[i]); - name = name.replace('@{' + i + '}@', atoms[i]); + longname = longname.replace('@{' + i + '}@', tokens[i]); + memberof = memberof.replace('@{' + i + '}@', tokens[i]); + scope = scope.replace('@{' + i + '}@', tokens[i]); + name = name.replace('@{' + i + '}@', tokens[i]); } - //// return {longname: longname, memberof: memberof, scope: scope, name: name, variation: variation}; +} + +/** + Given a longname like "a.b#c(2)", slice it up into an object + containing the memberof, the scope, the name, and variation. + @param {string} longname + @param {string} forcedMemberof + @returns {object} Representing the properties of the given name. + */ +exports.shorten = function(longname, forcedMemberof) { + return shorten(longname, SCOPE_PUNC, forcedMemberof); +}; + +function splitLongname(longname) { + var chunks = []; + var currentNameInfo; + var nameInfo = {}; + var previousName = longname; + var splitters = SCOPE_PUNC.concat('/'); + + do { + currentNameInfo = nameInfo[previousName] = shorten(previousName, splitters); + previousName = currentNameInfo.memberof; + chunks.push(currentNameInfo.scope + currentNameInfo.name); + } while (previousName); + + return { + chunks: chunks.reverse(), + nameInfo: nameInfo + }; +} + +// TODO: docs +exports.longnamesToTree = function longnamesToTree(longnames, doclets) { + var tree = {}; + + longnames.forEach(function(longname) { + var processed = splitLongname(longname); + var nameInfo = processed.nameInfo; + var chunk; + var currentLongname = ''; + var currentNavItem = tree; + + processed.chunks.forEach(function(chunk) { + currentLongname += chunk; + + currentNavItem[chunk] = currentNavItem[chunk] || nameInfo[currentLongname]; + currentNavItem[chunk].doclet = doclets ? doclets[currentLongname] : null; + currentNavItem[chunk].children = currentNavItem[chunk].children || {}; + + currentNavItem = currentNavItem[chunk].children; + }); + }); + + return tree; }; /** diff --git a/lib/jsdoc/util/templateHelper.js b/lib/jsdoc/util/templateHelper.js index 8c0f3d4e..61b1e25b 100644 --- a/lib/jsdoc/util/templateHelper.js +++ b/lib/jsdoc/util/templateHelper.js @@ -6,12 +6,14 @@ var catharsis = require('catharsis'); var dictionary = require('jsdoc/tag/dictionary'); +var name = require('jsdoc/name'); var util = require('util'); var hasOwnProp = Object.prototype.hasOwnProperty; var NAMESPACES = require('jsdoc/name').NAMESPACES; var files = {}; +var ids = {}; // each container gets its own html file var containers = ['class', 'module', 'external', 'namespace', 'mixin']; @@ -760,3 +762,6 @@ exports.createLink = function(doclet) { return url; }; + +// TODO: docs +exports.longnamesToTree = name.longnamesToTree; diff --git a/test/specs/jsdoc/util/templateHelper.js b/test/specs/jsdoc/util/templateHelper.js index 28acd015..1db54291 100644 --- a/test/specs/jsdoc/util/templateHelper.js +++ b/test/specs/jsdoc/util/templateHelper.js @@ -1,4 +1,4 @@ -/*global afterEach, beforeEach, describe, expect, env, it, jasmine, spyOn */ +/*global afterEach, beforeEach, describe, expect, env, it, jasmine, spyOn, xdescribe */ /*eslint quotes:0 */ 'use strict'; @@ -133,6 +133,11 @@ describe("jsdoc/util/templateHelper", function() { expect(typeof helper.createLink).toBe("function"); }); + it('should export a "longnamesToTree" function', function() { + expect(helper.longnamesToTree).toBeDefined(); + expect(typeof helper.longnamesToTree).toBe('function'); + }); + describe("setTutorials", function() { // used in tutorialToUrl, toTutorial. it("setting tutorials to null causes all tutorial lookups to fail", function() { @@ -412,7 +417,7 @@ describe("jsdoc/util/templateHelper", function() { {kind: 'class', memberof: 'SomeNamespace'} // not global ]; var externals = [ - {kind: 'external'} + {kind: 'external', name: 'foo'} ]; var events = [ {kind: 'event'} @@ -1425,4 +1430,8 @@ describe("jsdoc/util/templateHelper", function() { expect(out).toBe(helper.htmlsafe(str)); }); }); + + xdescribe('longnamesToTree', function() { + // TODO + }); });