Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
292 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
'use strict'; | ||
|
||
var path = require('path'); | ||
var execFile = require('child_process').execFile; | ||
var parseHelp = require('parse-help'); | ||
|
||
/** | ||
* The Completer is in charge of handling `yo-complete` behavior. | ||
* @constructor | ||
* @param {Environment} env A yeoman environment instance | ||
*/ | ||
var Completer = module.exports = function (env) { | ||
this.env = env; | ||
}; | ||
|
||
/** | ||
* Completion event done | ||
* | ||
* @param {String} data Environment object as parsed by tabtab | ||
* @param {Function} done Callback to invoke with completion results | ||
*/ | ||
Completer.prototype.complete = function (data, done) { | ||
if (data.last !== 'yo' && !/^-/.test(data.last)) { | ||
return this.generator(data, done); | ||
} | ||
|
||
this.env.lookup(function (err) { | ||
if (err) { | ||
return done(err); | ||
} | ||
|
||
var meta = this.env.getGeneratorsMeta(); | ||
var results = Object.keys(meta).map(this.item('yo'), this); | ||
done(null, results); | ||
}.bind(this)); | ||
}; | ||
|
||
|
||
/** | ||
* Generator completion event done | ||
* | ||
* @param {String} data Environment object as parsed by tabtab | ||
* @param {Function} done Callback to invoke with completion results | ||
*/ | ||
Completer.prototype.generator = function (data, done) { | ||
var last = data.last; | ||
var bin = path.join(__dirname, '../cli.js'); | ||
|
||
execFile('node', [bin, last, '--help'], function (err, out) { | ||
if (err) { | ||
return done(err); | ||
} | ||
|
||
var results = this.parseHelp(last, out); | ||
done(null, results); | ||
}.bind(this)); | ||
}; | ||
|
||
/** | ||
* Helper to format completion results into { name, description } objects | ||
* | ||
* @param {String} data Environment object as parsed by tabtab | ||
* @param {Function} done Callback to invoke with completion results | ||
*/ | ||
Completer.prototype.item = function (desc, prefix) { | ||
prefix = prefix || ''; | ||
return function (item) { | ||
var name = typeof item === 'string' ? item : item.name; | ||
desc = typeof item !== 'string' && item.description ? item.description : desc; | ||
desc = desc.replace(/^#?\s*/g, ''); | ||
desc = desc.replace(/:/g, '->'); | ||
desc = desc.replace(/'/g, ' '); | ||
|
||
return { | ||
name: prefix + name, | ||
description: desc | ||
}; | ||
}; | ||
}; | ||
|
||
/** | ||
* parse-help wrapper. Invokes parse-help with stdout result, returning the | ||
* list of completion items for flags / alias. | ||
* | ||
* @param {String} last Last word in COMP_LINE (completed line in command line) | ||
* @param {String} out Help output | ||
*/ | ||
Completer.prototype.parseHelp = function (last, out) { | ||
var help = parseHelp(out); | ||
var alias = []; | ||
var results = Object.keys(help.flags).map(function (key) { | ||
var flag = help.flags[key]; | ||
if (flag.alias) { | ||
alias.push(Object.assign({}, flag, { name: flag.alias })); | ||
} | ||
flag.name = key; | ||
return flag; | ||
}).map(this.item(last, '--'), this); | ||
|
||
results = results.concat(alias.map(this.item(last.replace(':', '_'), '-'), this)); | ||
results = results.filter(function (r) { | ||
return r.name !== '--help' && r.name !== '-h'; | ||
}); | ||
|
||
return results; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
#! /usr/bin/env node | ||
'use strict'; | ||
|
||
var env = require('yeoman-environment').createEnv(); | ||
var Completer = require('./completer'); | ||
|
||
var tabtab = require('tabtab')({ | ||
name: 'yo' | ||
}); | ||
|
||
var completer = new Completer(env); | ||
|
||
// Lookup installed generator in yeoman environment, respond completion results | ||
// with each generator. | ||
tabtab.on('yo', completer.complete.bind(completer)); | ||
|
||
// Register complete command | ||
tabtab.start(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
'use strict'; | ||
|
||
var path = require('path'); | ||
var assert = require('assert'); | ||
var Completer = require('../lib/completion/completer'); | ||
var execFile = require('child_process').execFile; | ||
|
||
var help = [ | ||
' Usage:', | ||
' yo backbone:app [options] [<app_name>]', | ||
'', | ||
' Options:', | ||
' -h, --help # Print the generator\'s options and usage', | ||
' --skip-cache # Do not remember prompt answers Default: false', | ||
' --skip-install # Do not automatically install dependencies Default: false', | ||
' --appPath # Name of application directory Default: app', | ||
' --requirejs # Support requirejs Default: false', | ||
' --template-framework # Choose template framework. lodash/handlebars/mustache Default: lodash', | ||
' --test-framework # Choose test framework. mocha/jasmine Default: mocha', | ||
'', | ||
' Arguments:', | ||
' app_name Type: String Required: false' | ||
].join('\n'); | ||
|
||
describe('Completion', function () { | ||
|
||
before(function () { | ||
this.env = require('yeoman-environment').createEnv(); | ||
}); | ||
|
||
describe('Test completion STDOUT output', function () { | ||
it('Returns the completion candidates for both options and installed generators', function (done) { | ||
var yocomplete = path.join(__dirname, '../lib/completion/index.js'); | ||
var yo = path.join(__dirname, '../lib/cli'); | ||
|
||
var cmd = 'export cmd=\"yo\" && DEBUG=\"tabtab*\" COMP_POINT=\"4\" COMP_LINE=\"$cmd\" COMP_CWORD=\"$cmd\"'; | ||
cmd += 'node ' + yocomplete + ' completion -- ' + yo + ' $cmd'; | ||
|
||
execFile('bash', ['-c', cmd], function (err, out) { | ||
if (err) { | ||
return done(err); | ||
} | ||
|
||
assert.ok(/-f/.test(out)); | ||
assert.ok(/--force/.test(out)); | ||
assert.ok(/--version/.test(out)); | ||
assert.ok(/--no-color/.test(out)); | ||
assert.ok(/--no-insight/.test(out)); | ||
assert.ok(/--insight/.test(out)); | ||
assert.ok(/--generators/.test(out)); | ||
|
||
done(); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('Completer', function () { | ||
beforeEach(function () { | ||
this.completer = new Completer(this.env); | ||
}); | ||
|
||
describe('#parseHelp', function () { | ||
it('Returns completion items based on help output', function () { | ||
var results = this.completer.parseHelp('backbone:app', help); | ||
var first = results[0]; | ||
|
||
assert.equal(results.length, 6); | ||
assert.deepEqual(first, { | ||
name: '--skip-cache', | ||
description: 'Do not remember prompt answers Default-> false' | ||
}); | ||
}); | ||
}); | ||
|
||
describe('#item', function () { | ||
it('Format results into { name, description }', function () { | ||
var list = ['foo', 'bar']; | ||
var results = list.map(this.completer.item('yo!', '--')); | ||
assert.deepEqual(results, [{ | ||
name: '--foo', | ||
description: 'yo!' | ||
}, { | ||
name: '--bar', | ||
description: 'yo!' | ||
}]); | ||
}); | ||
|
||
it('Escapes certain characters before consumption by shell scripts', function () { | ||
var list = ['foo']; | ||
|
||
var desc = '# yo I\'m a very subtle description, with chars that likely will break your Shell: yeah I\'m mean'; | ||
var expected = 'yo I m a very subtle description, with chars that likely will break your Shell-> yeah I m mean'; | ||
var results = list.map(this.completer.item(desc, '-')); | ||
|
||
assert.equal(results[0].description, expected); | ||
}); | ||
}); | ||
|
||
describe('#generator', function () { | ||
it('Returns completion candidates from generator help output', function (done) { | ||
// Here we test against yo --help (could use dummy:yo --help) | ||
this.completer.generator({ last: '' }, function (err, results) { | ||
if (err) { | ||
return done(err); | ||
} | ||
|
||
/* eslint no-multi-spaces: 0 */ | ||
assert.deepEqual(results, [ | ||
{ name: '--force', description: 'Overwrite files that already exist' }, | ||
{ name: '--version', description: 'Print version' }, | ||
{ name: '--no-color', description: 'Disable colors' }, | ||
{ name: '-f', description: 'Overwrite files that already exist' } | ||
]); | ||
|
||
done(); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('#complete', function () { | ||
// SKipping on CI right now, otherwise might introduce an `npm install | ||
// generator-dummy` as a pretest script | ||
|
||
it.skip('Returns the list of user installed generators as completion candidates', function (done) { | ||
this.completer.complete({ last: 'yo' }, function (err, results) { | ||
if (err) { | ||
return done(err); | ||
} | ||
|
||
console.log(results); | ||
var dummy = results.find(function (result) { | ||
return result.name === 'dummy:yo'; | ||
}); | ||
|
||
assert.equal(dummy.name, 'dummy:yo'); | ||
assert.equal(dummy.description, 'yo'); | ||
|
||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
|
||
}); |