mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-19 04:26:01 +08:00
It requires little tweaking in the case-block. Now the completion shell script is generated out of TypeScript code entirely. The options and aliases are generated dynamically. There are options to only produce bash- or zsh-specific code. Closes #3981.
183 lines
5.0 KiB
TypeScript
183 lines
5.0 KiB
TypeScript
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
|
|
import { oneLine, stripIndent } from 'common-tags';
|
|
|
|
const stringUtils = require('ember-cli-string-utils');
|
|
const Command = require('../ember-cli/lib/models/command');
|
|
const lookupCommand = require('../ember-cli/lib/cli/lookup-command');
|
|
|
|
function extractOptions(opts: any): String {
|
|
const output: String[] = [];
|
|
|
|
for (let index = 0; index < opts.length; index++) {
|
|
const element = opts[index];
|
|
output.push('--' + element.name);
|
|
if (element.aliases) {
|
|
output.push('-' + element.aliases[0]);
|
|
}
|
|
}
|
|
|
|
return output.sort().join(' ');
|
|
}
|
|
|
|
export interface CompletionCommandOptions {
|
|
all?: boolean;
|
|
bash?: boolean;
|
|
zsh?: boolean;
|
|
};
|
|
|
|
const commandsToIgnore = [
|
|
'easter-egg',
|
|
'destroy',
|
|
'github-pages-deploy' // errors because there is no base github-pages command
|
|
];
|
|
|
|
const optsNg: String[] = [];
|
|
|
|
const CompletionCommand = Command.extend({
|
|
name: 'completion',
|
|
description: 'Adds autocomplete functionality to `ng` commands and subcommands',
|
|
works: 'everywhere',
|
|
availableOptions: [
|
|
{ name: 'all', type: Boolean, default: true, aliases: ['a'] },
|
|
{ name: 'bash', type: Boolean, default: false, aliases: ['b'] },
|
|
{ name: 'zsh', type: Boolean, default: false, aliases: ['z'] }
|
|
],
|
|
|
|
run: function (commandOptions: CompletionCommandOptions) {
|
|
commandOptions.all = !commandOptions.bash && !commandOptions.zsh;
|
|
|
|
const commandFiles = fs.readdirSync(__dirname)
|
|
.filter(file => file.match(/\.ts$/) && !file.match(/\.run.ts$/))
|
|
.map(file => path.parse(file).name)
|
|
.filter(file => {
|
|
return commandsToIgnore.indexOf(file) < 0;
|
|
})
|
|
.map(file => file.toLowerCase());
|
|
|
|
const commandMap = commandFiles.reduce((acc: any, curr: string) => {
|
|
let classifiedName = stringUtils.classify(curr);
|
|
let defaultImport = require(`./${curr}`).default;
|
|
|
|
acc[classifiedName] = defaultImport;
|
|
|
|
return acc;
|
|
}, {});
|
|
|
|
let caseBlock = '';
|
|
|
|
commandFiles.forEach(cmd => {
|
|
const Command = lookupCommand(commandMap, cmd);
|
|
const com: String[] = [];
|
|
|
|
const command = new Command({
|
|
ui: this.ui,
|
|
project: this.project,
|
|
commands: this.commands,
|
|
tasks: this.tasks
|
|
});
|
|
|
|
optsNg.push(command.name);
|
|
com.push(command.name);
|
|
|
|
if (command.aliases) {
|
|
command.aliases.forEach((element: String) => {
|
|
optsNg.push(element);
|
|
com.push(element);
|
|
});
|
|
}
|
|
|
|
if (command.availableOptions && command.availableOptions[0]) {
|
|
let opts = extractOptions (command.availableOptions);
|
|
caseBlock = caseBlock + ' ' + com.sort().join('|') + ') opts="' + opts + '" ;;\n';
|
|
}
|
|
});
|
|
|
|
caseBlock = 'ng|help) opts="' + optsNg.sort().join(' ') + '" ;;\n' +
|
|
caseBlock +
|
|
' *) opts="" ;;';
|
|
|
|
console.log(stripIndent`
|
|
###-begin-ng-completion###
|
|
#
|
|
|
|
# ng command completion script
|
|
# This command supports 3 cases.
|
|
# 1. (Default case) It prints a common completion initialisation for both Bash and Zsh.
|
|
# It is the result of either calling "ng completion" or "ng completion -a".
|
|
# 2. Produce Bash-only completion: "ng completion -b" or "ng completion --bash".
|
|
# 3. Produce Zsh-only completion: "ng completion -z" or "ng completion --zsh".
|
|
#
|
|
# Installation: ng completion -b >> ~/.bashrc
|
|
# or ng completion -z >> ~/.zshrc
|
|
#`);
|
|
|
|
if (commandOptions.all && !commandOptions.bash) {
|
|
console.log('if test ".$(type -t complete 2>/dev/null || true)" = ".builtin"; then');
|
|
}
|
|
|
|
if (commandOptions.all || commandOptions.bash) {
|
|
console.log(stripIndent`
|
|
_ng_completion() {
|
|
local cword pword opts
|
|
|
|
COMPREPLY=()
|
|
cword=\${COMP_WORDS[COMP_CWORD]}
|
|
pword=\${COMP_WORDS[COMP_CWORD - 1]}
|
|
|
|
case \${pword} in
|
|
${caseBlock}
|
|
esac
|
|
|
|
COMPREPLY=( $(compgen -W '\${opts}' -- $cword) )
|
|
|
|
return 0
|
|
}
|
|
|
|
complete -o default -F _ng_completion ng
|
|
`);
|
|
}
|
|
|
|
if (commandOptions.all) {
|
|
console.log(stripIndent`
|
|
elif test ".$(type -w compctl 2>/dev/null || true)" = ".compctl: builtin" ; then
|
|
`);
|
|
}
|
|
|
|
if (commandOptions.all || commandOptions.zsh) {
|
|
console.log(stripIndent`
|
|
_ng_completion () {
|
|
local words cword opts
|
|
read -Ac words
|
|
read -cn cword
|
|
let cword-=1
|
|
|
|
case $words[cword] in
|
|
${caseBlock}
|
|
esac
|
|
|
|
setopt shwordsplit
|
|
reply=($opts)
|
|
unset shwordsplit
|
|
}
|
|
|
|
compctl -K _ng_completion ng
|
|
`);
|
|
}
|
|
|
|
if (commandOptions.all) {
|
|
console.log(stripIndent`
|
|
else
|
|
echo "Builtin command 'complete' or 'compctl' is redefined; cannot produce completion."
|
|
return 1
|
|
fi`);
|
|
}
|
|
|
|
console.log('###-end-ng-completion###');
|
|
|
|
}
|
|
});
|
|
|
|
export default CompletionCommand;
|