completion.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. 'use strict'
  2. const CUSTOM = ['']
  3. const BOOLEAN = false
  4. const options = {
  5. start: {
  6. '--port': CUSTOM,
  7. '--auto-watch': BOOLEAN,
  8. '--no-auto-watch': BOOLEAN,
  9. '--log-level': ['disable', 'debug', 'info', 'warn', 'error'],
  10. '--colors': BOOLEAN,
  11. '--no-colors': BOOLEAN,
  12. '--reporters': ['dots', 'progress'],
  13. '--no-reporters': BOOLEAN,
  14. '--browsers': ['Chrome', 'ChromeCanary', 'Firefox', 'PhantomJS', 'Safari', 'Opera'],
  15. '--no-browsers': BOOLEAN,
  16. '--single-run': BOOLEAN,
  17. '--no-single-run': BOOLEAN,
  18. '--help': BOOLEAN
  19. },
  20. init: {
  21. '--log-level': ['disable', 'debug', 'info', 'warn', 'error'],
  22. '--colors': BOOLEAN,
  23. '--no-colors': BOOLEAN,
  24. '--help': BOOLEAN
  25. },
  26. run: {
  27. '--no-refresh': BOOLEAN,
  28. '--port': CUSTOM,
  29. '--help': BOOLEAN
  30. }
  31. }
  32. function parseEnv (argv, env) {
  33. const words = argv.slice(5)
  34. return {
  35. words: words,
  36. count: parseInt(env.COMP_CWORD, 10),
  37. last: words[words.length - 1],
  38. prev: words[words.length - 2]
  39. }
  40. }
  41. function opositeWord (word) {
  42. if (word.charAt(0) !== '-') {
  43. return null
  44. }
  45. return word.substr(0, 5) === '--no-' ? '--' + word.substr(5) : '--no-' + word.substr(2)
  46. }
  47. function sendCompletionNoOptions () {}
  48. function sendCompletion (possibleWords, env) {
  49. const regexp = new RegExp('^' + env.last)
  50. const filteredWords = possibleWords.filter(function (word) {
  51. return regexp.test(word) && env.words.indexOf(word) === -1 &&
  52. env.words.indexOf(opositeWord(word)) === -1
  53. })
  54. if (!filteredWords.length) {
  55. return sendCompletionNoOptions(env)
  56. }
  57. filteredWords.forEach(function (word) {
  58. console.log(word)
  59. })
  60. }
  61. const glob = require('glob')
  62. const globOpts = {
  63. mark: true,
  64. nocase: true
  65. }
  66. function sendCompletionFiles (env) {
  67. glob(env.last + '*', globOpts, function (err, files) {
  68. if (err) return console.error(err)
  69. if (files.length === 1 && files[0].charAt(files[0].length - 1) === '/') {
  70. sendCompletionFiles({last: files[0]})
  71. } else {
  72. console.log(files.join('\n'))
  73. }
  74. })
  75. }
  76. function sendCompletionConfirmLast (env) {
  77. console.log(env.last)
  78. }
  79. function complete (env) {
  80. if (env.count === 1) {
  81. if (env.words[0].charAt(0) === '-') {
  82. return sendCompletion(['--help', '--version'], env)
  83. }
  84. return sendCompletion(Object.keys(options), env)
  85. }
  86. if (env.count === 2 && env.words[1].charAt(0) !== '-') {
  87. // complete files (probably karma.conf.js)
  88. return sendCompletionFiles(env)
  89. }
  90. const cmdOptions = options[env.words[0]]
  91. const previousOption = cmdOptions[env.prev]
  92. if (!cmdOptions) {
  93. // no completion, wrong command
  94. return sendCompletionNoOptions()
  95. }
  96. if (previousOption === CUSTOM && env.last) {
  97. // custom value with already filled something
  98. return sendCompletionConfirmLast(env)
  99. }
  100. if (previousOption) {
  101. // custom options
  102. return sendCompletion(previousOption, env)
  103. }
  104. return sendCompletion(Object.keys(cmdOptions), env)
  105. }
  106. function completion () {
  107. if (process.argv[3] === '--') {
  108. return complete(parseEnv(process.argv, process.env))
  109. }
  110. // just print out the karma-completion.sh
  111. const fs = require('graceful-fs')
  112. const path = require('path')
  113. fs.readFile(path.resolve(__dirname, '../scripts/karma-completion.sh'), 'utf8', function (err, data) {
  114. if (err) return console.error(err)
  115. process.stdout.write(data)
  116. process.stdout.on('error', function (error) {
  117. // Darwin is a real dick sometimes.
  118. //
  119. // This is necessary because the "source" or "." program in
  120. // bash on OS X closes its file argument before reading
  121. // from it, meaning that you get exactly 1 write, which will
  122. // work most of the time, and will always raise an EPIPE.
  123. //
  124. // Really, one should not be tossing away EPIPE errors, or any
  125. // errors, so casually. But, without this, `. <(karma completion)`
  126. // can never ever work on OS X.
  127. if (error.errno === 'EPIPE') {
  128. error = null
  129. }
  130. })
  131. })
  132. }
  133. // PUBLIC API
  134. exports.completion = completion
  135. // for testing
  136. exports.opositeWord = opositeWord
  137. exports.sendCompletion = sendCompletion
  138. exports.complete = complete