You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

completion.js 3.8KB

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