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