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