completion.js 6.0 KB

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