1 | 'use strict'
|
---|
2 | const path = require('path')
|
---|
3 |
|
---|
4 | // add bash completions to your
|
---|
5 | // yargs-powered applications.
|
---|
6 | module.exports = function completion (yargs, usage, command) {
|
---|
7 | const self = {
|
---|
8 | completionKey: 'get-yargs-completions'
|
---|
9 | }
|
---|
10 |
|
---|
11 | const zshShell = process.env.SHELL && process.env.SHELL.indexOf('zsh') !== -1
|
---|
12 | // get a list of completion commands.
|
---|
13 | // 'args' is the array of strings from the line to be completed
|
---|
14 | self.getCompletion = function getCompletion (args, done) {
|
---|
15 | const completions = []
|
---|
16 | const current = args.length ? args[args.length - 1] : ''
|
---|
17 | const argv = yargs.parse(args, true)
|
---|
18 | const aliases = yargs.parsed.aliases
|
---|
19 | const parentCommands = yargs.getContext().commands
|
---|
20 |
|
---|
21 | // a custom completion function can be provided
|
---|
22 | // to completion().
|
---|
23 | if (completionFunction) {
|
---|
24 | if (completionFunction.length < 3) {
|
---|
25 | const result = completionFunction(current, argv)
|
---|
26 |
|
---|
27 | // promise based completion function.
|
---|
28 | if (typeof result.then === 'function') {
|
---|
29 | return result.then((list) => {
|
---|
30 | process.nextTick(() => { done(list) })
|
---|
31 | }).catch((err) => {
|
---|
32 | process.nextTick(() => { throw err })
|
---|
33 | })
|
---|
34 | }
|
---|
35 |
|
---|
36 | // synchronous completion function.
|
---|
37 | return done(result)
|
---|
38 | } else {
|
---|
39 | // asynchronous completion function
|
---|
40 | return completionFunction(current, argv, (completions) => {
|
---|
41 | done(completions)
|
---|
42 | })
|
---|
43 | }
|
---|
44 | }
|
---|
45 |
|
---|
46 | const handlers = command.getCommandHandlers()
|
---|
47 | for (let i = 0, ii = args.length; i < ii; ++i) {
|
---|
48 | if (handlers[args[i]] && handlers[args[i]].builder) {
|
---|
49 | const builder = handlers[args[i]].builder
|
---|
50 | if (typeof builder === 'function') {
|
---|
51 | const y = yargs.reset()
|
---|
52 | builder(y)
|
---|
53 | return y.argv
|
---|
54 | }
|
---|
55 | }
|
---|
56 | }
|
---|
57 |
|
---|
58 | if (!current.match(/^-/) && parentCommands[parentCommands.length - 1] !== current) {
|
---|
59 | usage.getCommands().forEach((usageCommand) => {
|
---|
60 | const commandName = command.parseCommand(usageCommand[0]).cmd
|
---|
61 | if (args.indexOf(commandName) === -1) {
|
---|
62 | if (!zshShell) {
|
---|
63 | completions.push(commandName)
|
---|
64 | } else {
|
---|
65 | const desc = usageCommand[1] || ''
|
---|
66 | completions.push(commandName.replace(/:/g, '\\:') + ':' + desc)
|
---|
67 | }
|
---|
68 | }
|
---|
69 | })
|
---|
70 | }
|
---|
71 |
|
---|
72 | if (current.match(/^-/) || (current === '' && completions.length === 0)) {
|
---|
73 | const descs = usage.getDescriptions()
|
---|
74 | Object.keys(yargs.getOptions().key).forEach((key) => {
|
---|
75 | // If the key and its aliases aren't in 'args', add the key to 'completions'
|
---|
76 | const keyAndAliases = [key].concat(aliases[key] || [])
|
---|
77 | const notInArgs = keyAndAliases.every(val => args.indexOf(`--${val}`) === -1)
|
---|
78 | if (notInArgs) {
|
---|
79 | if (!zshShell) {
|
---|
80 | completions.push(`--${key}`)
|
---|
81 | } else {
|
---|
82 | const desc = descs[key] || ''
|
---|
83 | completions.push(`--${key.replace(/:/g, '\\:')}:${desc.replace('__yargsString__:', '')}`)
|
---|
84 | }
|
---|
85 | }
|
---|
86 | })
|
---|
87 | }
|
---|
88 |
|
---|
89 | done(completions)
|
---|
90 | }
|
---|
91 |
|
---|
92 | // generate the completion script to add to your .bashrc.
|
---|
93 | self.generateCompletionScript = function generateCompletionScript ($0, cmd) {
|
---|
94 | const templates = require('./completion-templates')
|
---|
95 | let script = zshShell ? templates.completionZshTemplate : templates.completionShTemplate
|
---|
96 | const name = path.basename($0)
|
---|
97 |
|
---|
98 | // add ./to applications not yet installed as bin.
|
---|
99 | if ($0.match(/\.js$/)) $0 = `./${$0}`
|
---|
100 |
|
---|
101 | script = script.replace(/{{app_name}}/g, name)
|
---|
102 | script = script.replace(/{{completion_command}}/g, cmd)
|
---|
103 | return script.replace(/{{app_path}}/g, $0)
|
---|
104 | }
|
---|
105 |
|
---|
106 | // register a function to perform your own custom
|
---|
107 | // completions., this function can be either
|
---|
108 | // synchrnous or asynchronous.
|
---|
109 | let completionFunction = null
|
---|
110 | self.registerFunction = (fn) => {
|
---|
111 | completionFunction = fn
|
---|
112 | }
|
---|
113 |
|
---|
114 | return self
|
---|
115 | }
|
---|