#!/usr/bin/env node /** * @author: Lin Zhang ( myhere.2009 AT gmail DOT com ) * @fileoverview: This script for auto-generate nodejs-doc.vim */ var util = require('util'), fs = require('fs'), path = require('path'), os = require('os'), emitter = new (require('events')).EventEmitter(); init(); function init() { initEvents(); initLoading(); getNodejsDoc(); } function initEvents() { // uncatched exception process.on('uncaughtException', function(err) { clearLoading(); console.error('Error: ' + err.stack); }); emitter.on('vimscript/done', function(message) { clearLoading(); console.log(message); console.log('Done!'); }); } function initLoading() { var chars = [ '-', '\\', '|', '/' ]; var index = 0, total = chars.length; initLoading.timer = setInterval(function() { index = ++index % total; var c = chars[index]; // clear console // @see: https://groups.google.com/forum/?fromgroups#!topic/nodejs/i-oqYFVty5I process.stdout.write('\033[2J\033[0;0H'); console.log('please wait:'); console.log(c); }, 200); } function clearLoading() { clearInterval(initLoading.timer); } function getNodejsDoc() { var http = require('http'); var req = http.get('http://nodejs.org/api/all.json', function(res){ var chunks = []; res.on('data', function(chunk) { chunks.push(chunk); }); res.on('end', function() { var buf = Buffer.concat(chunks), body = buf.toString('utf-8'); extract2VimScript(body); }); }).on('error', function(e) { console.error('problem with request: ' + e.message); }); } function extract2VimScript(body) { // for debug fs.writeFile('./nodejs-doc-all.json', body); var json = JSON.parse(body), vimObject; var _globals = sortModuleByName(mergeObject(getModsInfo(json.globals), getModsInfo(json.vars))), _modules = sortModuleByName(getModsInfo(json.modules)), _vars = (getVarInfo(json.vars)) .concat(getVarInfo(json.globals)) .sort(sortCompleteWord); _globals = copyGlobals(_globals, _modules); vimObject = { 'globals': _globals, 'modules': _modules, 'vars': _vars }; var filename = path.join(__dirname, 'nodejs-doc.vim'), comment = '" this file is auto created by "' + __filename + '", please do not edit it yourself!', content = 'let g:nodejs_complete_data = ' + JSON.stringify(vimObject), content = comment + os.EOL + content; fs.writeFile(filename, content, function(err) { emitter.emit('vimscript/done', 'write file to "' + filename + '" complete.'); }); // for debug fs.writeFileSync(filename + '.js', JSON.stringify(vimObject, null, 2)); } function getModsInfo(mods) { var ret = {}; if (!util.isArray(mods)) { return ret; } mods.forEach(function(mod) { var mod_name = getModName(mod), mod_props = getModProps(mod); // class var mod_classes = {}; var classes = mod.classes || []; classes.forEach(function(cls) { var names = getClassName(cls, mod_name); var cls_name = names.cls_name, _mod_name = names.mod_name; if (_mod_name && mod_name != _mod_name) { mod_name = names.mod_name; } mod_classes[cls_name] = getModProps(cls); }); if (mod_props.length == 0 && classes.length == 0) { } else { ret[mod_name] = { props: mod_props, classes: mod_classes } } }); return ret; } function getModProps(mod) { var is_legal_property_name = function(name) { name += ''; name = name.trim(); return (/^[$a-zA-Z_][$a-zA-Z0-9_]*$/i).test(name); }; var props = []; // properties var properties = mod.properties || []; properties.forEach(function(property) { var name = property.name; if (is_legal_property_name(name)) { var item = {}; item.word = name; item.kind = 'm'; item.info = ' '; props.push(item); } else { console.log('illegal name: ' + name); } }); // methods var methods = mod.methods || []; methods.forEach(function(method) { var name = method.name; if (is_legal_property_name(name)) { var item = {}; if (method.type == 'method') { item.word = name; item.info = method.textRaw; item.kind = 'f'; props.push(item); } } else { console.log('illegal name: ' + name); } }); // classes var classes = mod.classes || []; classes.forEach(function(cls) { var mod_name = getModName(mod); var names = getClassName(cls, mod_name); var name = names.cls_name; if (is_legal_property_name(name)) { var item = {}; item.word = names.cls_name; item.kind = 'f'; item.info = ' '; props.push(item); } else { console.log('illegal name: ' + name); } }); props = props.sort(sortCompleteWord); return props; } function getModName(mod) { // module name var mod_name = mod.name; // invalid module name like 'tls_(ssl)' // then guess the module name from textRaw 'TLS (SSL)' if ((/[^_a-z\d\$]/i).test(mod_name)) { var textRaw = mod.textRaw; var matched = textRaw.match(/^[_a-z\d\$]+/i); if (matched) { var mod_name_len = matched[0].length; mod_name = mod_name.substr(0, mod_name_len); } } return mod_name; } function getClassName(cls, mod_name) { var str = cls.name; var names = str.split('.'); var _mod_name = names[0], cls_name; if (names.length == 1) { cls_name = _mod_name; } else { // 修正 mod_name; events.EventEmitter if (_mod_name.toLowerCase() == mod_name.toLowerCase()) { mod_name = _mod_name; } cls_name = names.slice(1).join('.'); } return { mod_name: mod_name, cls_name: cls_name }; } function getVarInfo(vars) { var ret = []; if (!util.isArray(vars)) { return ret; } vars.forEach(function(_var) { // if var is a function if ((/\([^\(\)]*\)\s*$/).test(_var.textRaw)) { ret.push({ word: _var.name, info: _var.textRaw, kind: 'f' }); } else { ret.push({ word: _var.name, kind: 'v', info: ' ' }); } }); // sort ret = ret.sort(sortCompleteWord); return ret; } function copyGlobals(globals, modules) { var _Buffer = modules.buffer.classes.Buffer; globals.Buffer = { props: [], classes: { '.self': _Buffer } }; return globals; } // helpers /** * @param {Object} */ function sortModuleByName(mods) { var keys = Object.keys(mods); // sort keys.sort(); var ret = {}; keys.forEach(function(k) { ret[k] = mods[k]; }); return ret; } /** * @param {Object} * @param {Object} */ function sortCompleteWord(a, b) { var a_w = a.word.toLowerCase(), b_w = b.word.toLowerCase(); return a_w < b_w ? -1 : (a_w > b_w ? 1 : 0); } /** * @desc merge Object * @arguemnts: {Object} * * @return: return the new merged Object */ function mergeObject() { var ret = {}, args = Array.prototype.slice.call(arguments); args.forEach(function(obj) { for (var p in obj) { if (obj.hasOwnProperty(p)) { ret[p] = obj[p]; } } }); return ret; }