Merge branch 'master' of git://github.com/hemanth/bower into completion

This commit is contained in:
André Cruz
2012-12-08 10:10:08 +00:00
17 changed files with 476 additions and 13 deletions

View File

@@ -70,3 +70,13 @@ module.exports.line = function (argv) {
if (options.help) return help('cache-clean');
return module.exports(pkgs, options);
};
module.exports.completion = function (opts, cb) {
glob('./*', { cwd: config.cache }, function (err, dirs) {
if (err) return cb(err);
dirs = dirs.map(function (dir) {
return dir.replace(/^\.\//, '');
});
cb(null, dirs);
});
};

102
lib/commands/completion.js Normal file
View File

@@ -0,0 +1,102 @@
// ==========================================
// BOWER: Completion API
// ==========================================
// Copyright 2012 Twitter, Inc
// Licensed under The MIT License
// http://opensource.org/licenses/MIT
// ==========================================
var Emitter = require('events').EventEmitter;
var path = require('path');
var nopt = require('nopt');
var mkdirp = require('mkdirp');
var template = require('../util/template');
var complete = require('../util/completion');
var config = require('../core/config');
var help = require('./help');
var optionTypes = { help: Boolean };
var shorthand = { 'h': ['--help'] };
module.exports = function (argv, env) {
env = env || process.env;
var emitter = new Emitter;
var commands = require('../commands');
// top level flags
var flags = ['--no-color', '--help', '--version'];
var done = function done() {
process.nextTick(function () {
emitter.emit('end');
});
return emitter;
};
// if the COMP_* isn't in the env, then just dump the script.
if (!env.COMP_CWORD) {
template('completion').on('data', emitter.emit.bind(emitter, 'end'));
return emitter;
}
// parse environment and arguments, returns a hash of completion config.
var opts = complete(argv, env);
// if only one word, complete the list of command or top level flags
if (opts.w === 1) {
if (opts.word.charAt(0) === '-') complete.log(flags, opts);
else complete.log(Object.keys(commands), opts);
return done();
}
// try to find the bower command. first thing after all the configs.
var parsed = opts.conf = nopt({}, {}, opts.partialWords, 0);
var cmd = parsed.argv.remain[0];
// unable to find a command, complete the lisf of commands
if (!cmd) {
complete.log(Object.keys(commands), opts);
return done();
}
// if words[0] is a bower command, then complete on it.
cmd = commands[cmd];
if (cmd && cmd.completion) {
// prior to that, ensure the completion cache directory is created first
mkdirp(path.join(config.completion), function (err) {
if (err) return emitter.emit('error', err);
cmd.completion(opts, function (err, data) {
if (err) return emitter.emit('error', err);
// completing options, then append top level flags
if (opts.word.charAt(0) === '-') data = data.concat(flags);
complete.log(data, opts);
done();
});
});
return emitter;
}
// otherwise, do nothing
return emitter;
};
module.exports.line = function (argv) {
var emitter = new Emitter;
var options = nopt(optionTypes, shorthand, argv);
if (options.help) return help('completion');
module.exports(options.argv.remain.slice(2), process.env)
.on('data', emitter.emit.bind(emitter, 'data'))
.on('error', emitter.emit.bind(emitter, 'error'))
.on('end', emitter.emit.bind(emitter, 'end'));
return emitter;
};

View File

@@ -29,4 +29,8 @@ module.exports.line = function (argv) {
var options = nopt({}, {}, argv);
var paths = options.argv.remain.slice(1);
return module.exports(paths[0]);
};
};
module.exports.completion = function (opts, cb) {
return cb(null, Object.keys(require('../commands')));
};

View File

@@ -18,5 +18,6 @@ module.exports = {
'info': require('./info'),
'register': require('./register'),
'search': require('./search'),
'cache-clean': require('./cache-clean')
};
'cache-clean': require('./cache-clean'),
'completion': require('./completion')
};

View File

@@ -10,6 +10,7 @@ var nopt = require('nopt');
var template = require('../util/template');
var source = require('../core/source');
var install = require('./install');
var help = require('./help');
var optionTypes = { help: Boolean };
@@ -42,4 +43,6 @@ module.exports.line = function (argv) {
});
return emitter;
};
};
module.exports.completion = install.completion;

View File

@@ -13,8 +13,12 @@
var Emitter = require('events').EventEmitter;
var nopt = require('nopt');
var fs = require('fs');
var path = require('path');
var Manager = require('../core/manager');
var config = require('../core/config');
var source = require('../core/source');
var save = require('../util/save');
var help = require('./help');
@@ -43,3 +47,36 @@ module.exports.line = function (argv) {
if (options.help) return help('install');
return module.exports(paths, options);
};
module.exports.completion = function (opts, cb) {
var word = opts.word;
// completing options?
if (opts.words[0] === 'install' && word.charAt(0) === '-') {
return cb(null, Object.keys(optionTypes).map(function (option) {
return '--' + option;
}));
}
var cache = path.join(config.completion, 'install.json');
var done = function done(err, results) {
if (err) return cb(err);
var names = results.map(function (pkg) {
return pkg.name;
});
return cb(null, names);
};
fs.readFile(cache, function (err, body) {
if (!err) return done(null, JSON.parse(body));
// expected error, do the first request and cache the results
source.all(function (err, results) {
if (err) return cb(err);
fs.writeFile(cache, JSON.stringify(results, null, 2), function (err) {
done(err, results);
});
});
});
};

View File

@@ -108,4 +108,12 @@ module.exports.line = function (argv) {
if (options.help) return help('link');
return module.exports(name);
};
};
module.exports.completion = function (opts, cb) {
fs.readdir(config.links, function (err, dirs) {
// ignore ENOENT, ~/.bower/links not created yet
if (err && err.code === 'ENOENT') return cb(null, []);
cb(err, dirs);
});
};

View File

@@ -177,4 +177,14 @@ module.exports.line = function (argv) {
var options = nopt(optionTypes, shorthand, argv);
if (options.help) return help('list');
return module.exports(options);
};
};
module.exports.completion = function (opts, cb) {
if (!/^-/.test(opts.word)) return cb(null, []);
var results = Object.keys(optionTypes).map(function (option) {
return '--' + option;
});
cb(null, results);
};

View File

@@ -11,6 +11,7 @@ var nopt = require('nopt');
var template = require('../util/template');
var source = require('../core/source');
var install = require('./install');
var help = require('./help');
var optionTypes = { help: Boolean };
@@ -46,4 +47,6 @@ module.exports.line = function (argv) {
if (options.help || !names.length) return help('lookup');
return module.exports(names[0]);
};
};
module.exports.completion = install.completion;

View File

@@ -11,6 +11,7 @@ var nopt = require('nopt');
var template = require('../util/template');
var source = require('../core/source');
var install = require('./install');
var help = require('./help');
var optionTypes = { help: Boolean };
@@ -46,4 +47,6 @@ module.exports.line = function (argv) {
if (options.help) return help('search');
return module.exports(names[0]);
};
};
module.exports.completion = install.completion;

View File

@@ -149,4 +149,21 @@ module.exports.line = function (argv) {
var names = options.argv.remain.slice(1);
return module.exports(names, options);
};
};
module.exports.completion = function (opts, cb) {
var word = opts.word;
// completing options?
if (opts.words[0] === 'uninstall' && word.charAt(0) === '-') {
return cb(null, Object.keys(optionTypes).map(function (option) {
return '--' + option;
}));
}
fs.readdir(config.directory, function (err, dirs) {
// ignore ENOENT, ./components not created yet
if (err && err.code === 'ENOENT') return cb(null, []);
cb(err, dirs);
});
};

View File

@@ -11,8 +11,9 @@ var async = require('async');
var nopt = require('nopt');
var _ = require('lodash');
var Manager = require('../core/manager');
var help = require('./help');
var Manager = require('../core/manager');
var help = require('./help');
var uninstall = require('./uninstall');
var shorthand = { 'h': ['--help'], 'f': ['--force'] };
var optionTypes = { help: Boolean, force: Boolean };
@@ -75,4 +76,6 @@ module.exports.line = function (argv) {
var paths = options.argv.remain.slice(1);
return module.exports(paths, options);
};
};
module.exports.completion = uninstall.completion;

View File

@@ -26,6 +26,7 @@ var folder = process.platform === 'win32'
var config = require('rc') ('bower', {
cache : path.join(roaming, folder, 'cache'),
links : path.join(roaming, folder, 'links'),
completion : path.join(roaming, folder, 'completion'),
json : 'component.json',
endpoint : 'https://bower.herokuapp.com',
directory : 'components'
@@ -41,4 +42,4 @@ if (fileExists(localFile)) {
// If an uncaught exception occurs, the temporary directories will be deleted nevertheless
tmp.setGracefulCleanup();
module.exports = config;
module.exports = config;

65
lib/util/completion.js Normal file
View File

@@ -0,0 +1,65 @@
// ==========================================
// BOWER: completion
// ==========================================
// Copyright 2012 Twitter, Inc
// Licensed under The MIT License
// http://opensource.org/licenses/MIT
// ==========================================
// This module exposes a simple helper to parse the environment variables in
// case of a tab completion command. It parses the provided `argv` (nopt's
// remain arguments after `--`) and `env` (should be process.env)
//
// It is inspired and based off Isaac's work on npm.
module.exports = function (argv, env) {
var opts = {};
// w is the words number, based on the cursor position
opts.w = +env.COMP_CWORD;
// words is the escaped sequence of words following `bower`
opts.words = argv.map(function (word) {
return word.charAt(0) === "\"" ?
word.replace(/^"|"$/g, "") :
word.replace(/\\ /g, " ");
});
// word is a shortcut to the last word in the line
opts.word = opts.words[opts.w - 1];
// line is the sequence of tab completed words.
opts.line = env.COMP_LINE;
// point is the cursor position in the line
opts.point = +env.COMP_POINT;
// length is the whole line's length.
opts.length = opts.line.length;
// partialLine is the line ignoring the sequence of characters after
// cursor position, ie. tabbing at: bower install j|qu
// gives back a partialLine: bower install j
opts.partialLine = opts.line.slice(0, opts.point);
// partialWords is only returning the words based on cursor position,
// ie tabbing at: bower install ze|pto backbone
// gives back a partialWords array: ['install', 'zepto']
opts.partialWords = opts.words.slice(0, opts.w);
return opts;
};
module.exports.log = function (arr, opts, prefix) {
arr = Array.isArray(arr) ? arr : [arr];
arr.filter(module.exports.abbrev(opts)).forEach(function (word) {
console.log(word);
});
};
module.exports.abbrev = function abbrev(opts) {
var word = opts.word.replace(/\./g, '\\.');
return function (it) {
return new RegExp('^' + word).test(it);
};
};