completion.js 3.8 KB

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