diff --git a/CMakeLists.txt b/CMakeLists.txt index ae9ba164..8509f7ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,8 +111,8 @@ if (BUILDDOC) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doxy/node/${JSDOC_FILE} ${CMAKE_CURRENT_BINARY_DIR}/${JSDOC_FILE} COPYONLY) endforeach() add_custom_target(jsdoc - NODE_PATH=${NODE_PATH} ${NODE_EXECUTABLE} docgen -m upm -i xml - COMMAND ${YUIDOC_EXECUTABLE} -C --no-sort --config generators/yuidoc/conf.json -o html/node jsdoc/yuidoc/upm + NODE_PATH=${NODE_PATH} ${NODE_EXECUTABLE} docgen -m upm -i xml -t ${CMAKE_CURRENT_SOURCE_DIR}/src -g ../../../../docs/images/ + COMMAND ${YUIDOC_EXECUTABLE} -C --no-sort --helpers generators/yuidoc/helper.js --themedir generators/yuidoc/tmpl -o html/node jsdoc/yuidoc/upm COMMAND NODE_PATH=${NODE_PATH} ${NODE_EXECUTABLE} tolower -i html/node DEPENDS doc WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} diff --git a/doxy/node/docgen.js b/doxy/node/docgen.js index 4a358914..ecf190c4 100644 --- a/doxy/node/docgen.js +++ b/doxy/node/docgen.js @@ -35,7 +35,7 @@ var xml2js = require('./xml2js') _.extend(opts, { addOptions: function(module) { return module.addOptions(opts); } }); opts .option('-m, --module [module]', 'module name for which to build documentation', 'mraa') - .option('-f, --formats [formats]', 'format for js comments', 'jsdoc,yuidoc,ternjs') + .option('-f, --formats [formats]', 'format for js comments', 'yuidoc,ternjs') .option('-o, --outdir [directory]', 'top directory to build documentation', __dirname + '/jsdoc') .addOptions(xml2js) .parse(process.argv); @@ -44,12 +44,14 @@ opts // use promise-style programming rather than spaghetti callbacks Promise.promisifyAll(fs); + // TODO: create directory structure if doesn't exist var formats = opts.formats.split(','); formats.forEach(function(format){ mkdirp('jsdoc/' + format + '/' + opts.module); }); + // main xml2js.parse().then(function(specjs) { Promise.all(_.map(formats, function(format) { diff --git a/doxy/node/generators/yuidoc/generator.js b/doxy/node/generators/yuidoc/generator.js index 7217c050..94c45ea6 100644 --- a/doxy/node/generators/yuidoc/generator.js +++ b/doxy/node/generators/yuidoc/generator.js @@ -28,10 +28,10 @@ var _ = require('lodash'); // generate YuiDocs-style documentation function generateDocs(specjs) { - var docs = GENERATE_MODULE(specjs.MODULE); - GENERATE_TYPE = (function(enums) { + var docs = GENERATE_MODULE(specjs.MODULE, ''); + GENERATE_TYPE = (function(enums) { return function(type) { - return (_.contains(enums, type) ? ('Enum ' + type) : type); + return (_.contains(enums, type) ? ('Enum ' + type) : type); } })(_.keys(specjs.ENUMS_BY_GROUP)); docs = _.reduce(specjs.METHODS, function(memo, methodSpec, methodName) { @@ -40,24 +40,25 @@ function generateDocs(specjs) { docs = _.reduce(specjs.ENUMS, function(memo, enumSpec, enumName) { return memo += GENERATE_ENUM(enumName, enumSpec); }, docs); - docs = _.reduce(specjs.CLASSES, function(memo, classSpec, parentClass) { - return memo - + GENERATE_CLASS(parentClass, classSpec.description) - + _.reduce(classSpec.methods, function(memo, methodSpec, methodName) { - return memo += GENERATE_METHOD(methodName, methodSpec, parentClass); - }, '') - + _.reduce(classSpec.variables, function(memo, variableSpec, variableName) { - return memo += GENERATE_VAR(variableName, variableSpec, parentClass); - }, '') - + _.reduce(classSpec.enums, function(memo, enumSpec, enumName) { - return memo += GENERATE_ENUM(enumName, enumSpec, parentClass); - }, ''); - }, docs); + if (_.isEmpty(specjs.CLASSGROUPS)) { + docs += GENERATE_CLASSES(specjs.CLASSES); + } else { + docs += GENERATE_MODULE('common', ''); + var grouped = _.flatten(_.pluck(_.values(specjs.CLASSGROUPS), 'classes')); + var ungrouped = _.difference(_.keys(specjs.CLASSES), grouped); + docs += GENERATE_CLASSES(_.pick(specjs.CLASSES, ungrouped), 'common'); + _.each(specjs.CLASSGROUPS, function(groupSpec, groupName) { + docs += GENERATE_CLASSES(_.pick(specjs.CLASSES, groupSpec.classes), groupName); + }); + // TODO: figure out why yuidoc won't associate the class with the right module if module definitions are interspersed + _.each(specjs.CLASSGROUPS, function(groupSpec, groupName) { + docs += GENERATE_MODULE(groupName, groupSpec.description); + }); + } return docs; } - // comment wrapper around entire spec function GENERATE_DOC(text) { return '/**\n' + text + ' */\n'; @@ -65,15 +66,35 @@ function GENERATE_DOC(text) { // generate module spec -function GENERATE_MODULE(module) { - return GENERATE_DOC('@module ' + module + '\n'); +function GENERATE_MODULE(name, description) { + return GENERATE_DOC(description + '\n' + + '@module ' + name + '\n'); +} + + +// generate spec for the given list of classes +function GENERATE_CLASSES(classes, parent) { + return _.reduce(classes, function(memo, classSpec, className) { + return memo + + GENERATE_CLASS(className, classSpec.description, parent) + + _.reduce(classSpec.methods, function(memo, methodSpec, methodName) { + return memo += GENERATE_METHOD(methodName, methodSpec, className); + }, '') + + _.reduce(classSpec.variables, function(memo, variableSpec, variableName) { + return memo += GENERATE_VAR(variableName, variableSpec, className); + }, '') + + _.reduce(classSpec.enums, function(memo, enumSpec, enumName) { + return memo += GENERATE_ENUM(enumName, enumSpec, className); + }, ''); + }, ''); } // generate class spec -function GENERATE_CLASS(name, description) { - return GENERATE_DOC(description + '\n' - + '@class ' + name + '\n'); +function GENERATE_CLASS(name, description, parent) { + return GENERATE_DOC(description + '\n' + + '@class ' + name + '\n' + + (parent ? ('@module ' + parent + '\n') : '')); } @@ -83,7 +104,7 @@ function GENERATE_METHOD(name, spec, parent) { + '@method ' + name + '\n' + (parent ? ('@for ' + parent + '\n') : '@for common\n') + _.reduce(spec.params, function(memo, paramSpec, paramName) { - return memo + '@param {' + GENERATE_TYPE(paramSpec.type) + '} ' + paramName + ' ' + paramSpec.description + '\n'; + return memo + '@param {' + GENERATE_TYPE(paramSpec.type) + '} ' + paramName + ' ' + paramSpec.description + '\n'; }, '') + ( !_.isEmpty(spec.return) ? ('@return {' + GENERATE_TYPE(spec.return.type) + '} ' + spec.return.description + '\n') : '')); } @@ -105,13 +126,13 @@ function GENERATE_VAR(name, spec, parent) { + '@type ' + spec.type + '\n' + '@for ' + parent + '\n'); } - + // TODO -// generate link spec +// generate link spec function GENERATE_LINK(text) { return '{{#crossLink "' + text + '"}}{{/crossLink}}'; } -module.exports = generateDocs; +module.exports = generateDocs; \ No newline at end of file diff --git a/doxy/node/generators/yuidoc/helper.js b/doxy/node/generators/yuidoc/helper.js new file mode 100644 index 00000000..504e3767 --- /dev/null +++ b/doxy/node/generators/yuidoc/helper.js @@ -0,0 +1,196 @@ +/* + * Author: Heidi Pan + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +// extract the field from the class description text +function getField(classDescription, field) { + var pattern = new RegExp('\\+ __' + field + ':__ [A-Za-z0-9]*'); + var label = new RegExp('\\+ __' + field + ':__'); + if (classDescription) { + var matched = classDescription.match(pattern); + if (matched) { + return (matched[0].replace(label, '')).trim(); + } + } + return 'other'; +} + + +// generate html to group modules by the given field (e.g. category/connection type) of its children classes +function listByGroup(modules, classes, field, projectRoot) { + + var moduleClasses = {}; + var modulesByGroup = {}; + var i, j; + for (i = 0; i < modules.length; i++) { + moduleClasses[modules[i].name] = []; + } + for (i = 0; i < classes.length; i++) { + moduleClasses[classes[i].module].push(i); + } + for (var module in moduleClasses) { + var classIndices = moduleClasses[module]; + var groups = []; + for (i = 0; i < classIndices.length; i++) { + groups.push(getField(classes[classIndices[i]].description, field)); + } + if (groups.length != 0) { + var group = groups[0]; + var sanitychecked = true; + for (i = 1; i < groups.length; i++) { + if (groups[i] != group) { + sanitychecked = false; + break; + } + } + if (!sanitychecked) { + // TODO + } + if (group in modulesByGroup) { + modulesByGroup[group].push(module); + } else { + modulesByGroup[group] = [module]; + } + } + } + var groups = Object.keys(modulesByGroup).sort(); + var html = ''; + var pfx = projectRoot + 'modules/'; + var first = true; + for (i = 0; i < groups.length; i++) { + var group = groups[i]; + html += (first ? '' : ''); + html += '
' + group + '