| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 | #### Omelette Simple Auto Completion for Node###{EventEmitter} = require "events"path           = require "path"fs             = require "fs"os             = require "os"depthOf = (object) ->  level = 1  for own key of object    if typeof object[key] is 'object'      depth = depthOf(object[key]) + 1      level = Math.max(depth, level)  levelclass Omelette extends EventEmitter  {log} = console  constructor: ->    @compgen  = process.argv.indexOf "--compgen"    @install  = process.argv.indexOf("--completion") > -1    @installFish  = process.argv.indexOf("--completion-fish") > -1    isZsh     = process.argv.indexOf("--compzsh") > -1    isFish    = process.argv.indexOf("--compfish") > -1    @isDebug  = process.argv.indexOf("--debug") > -1    @fragment = parseInt(process.argv[@compgen+1])-(if isZsh then 1 else 0)    @line     = process.argv[@compgen+3]    @word     = @line?.trim().split(/\s+/).pop()    {@HOME, @SHELL} = process.env  setProgram: (programs)->    programs = programs.split '|'    [@program] = programs    @programs = programs.map (program)-> program.replace ///      [       ^     # Do not allow except:        A-Z  # .. uppercase        a-z  # .. lowercase        0-9  # .. numbers        \.   # .. dots        \_   # .. underscores        \-   # .. dashes      ]    ///g, ''  setFragments: (@fragments...)->  generate: ->    data = {before: @word, @fragment, @line, @reply}    @emit "complete", @fragments[@fragment-1], data    @emit @fragments[@fragment-1], data    @emit "$#{@fragment}", data    process.exit()  reply: (words=[])->    console.log words.join? os.EOL    process.exit()  tree: (objectTree={})->    depth = depthOf objectTree    for level in [1..depth]      @on "$#{level}", ({ fragment, reply, line })->        accessor = new Function '_', """          return _['#{line.split(/\s+/).slice(1).filter(Boolean).join("']['")}']        """        replies = if fragment is 1 then Object.keys(objectTree) else accessor(objectTree)        reply do (replies = replies)->          return replies() if replies instanceof Function          return replies if replies instanceof Array          return Object.keys(replies) if replies instanceof Object    this  generateCompletionCode: ->    completions = @programs.map (program)=>      completion = "_#{program}_completion"      """      ### #{program} completion - begin. generated by omelette.js ###      if type compdef &>/dev/null; then        #{completion}() {          compadd -- `#{@program} --compzsh --compgen "${CURRENT}" "${words[CURRENT-1]}" "${BUFFER}"`        }        compdef #{completion} #{program}      elif type complete &>/dev/null; then        #{completion}() {          local cur prev nb_colon          _get_comp_words_by_ref -n : cur prev          nb_colon=$(grep -o ":" <<< "$COMP_LINE" | wc -l)          COMPREPLY=( $(compgen -W '$(#{@program} --compbash --compgen "$((COMP_CWORD - (nb_colon * 2)))" "$prev" "${COMP_LINE}")' -- "$cur") )          __ltrim_colon_completions "$cur"        }        complete -F #{completion} #{program}      fi      ### #{program} completion - end ###      """    # Adding aliases for testing purposes    completions.push @generateTestAliases() if @isDebug    completions.join os.EOL  generateCompletionCodeFish: ->    completions = @programs.map (program)=>      completion = "_#{program}_completion"      """      ### #{program} completion - begin. generated by omelette.js ###      function #{completion}        #{@program} --compfish --compgen (count (commandline -poc)) (commandline -pt) (commandline -pb)      end      complete -f -c #{program} -a '(#{completion})'      ### #{program} completion - end ###      """    # Adding aliases for testing purposes    completions.push @generateTestAliases() if @isDebug    completions.join os.EOL  generateTestAliases: ->    fullPath = path.join process.cwd(), @program    debugAliases   = @programs.map((program)-> "  alias #{program}=#{fullPath}").join os.EOL    debugUnaliases = @programs.map((program)-> "  unalias #{program}").join os.EOL    """    ### test method ###    omelette-debug-#{@program}() {    #{debugAliases}    }    omelette-nodebug-#{@program}() {    #{debugUnaliases}    }    ### tests ###    """  checkInstall: ->    if @install      log @generateCompletionCode()      process.exit()    if @installFish      log @generateCompletionCodeFish()      process.exit()  getActiveShell: ->    {SHELL} = process.env    if SHELL.match /bash/      then 'bash'    else if SHELL.match /zsh/  then 'zsh'    else if SHELL.match /fish/ then 'fish'  getDefaultShellInitFile: ->    fileAt = (root)->      (file)-> path.join root, file    fileAtHome = fileAt @HOME    switch @shell = @getActiveShell()      when 'bash'  then fileAtHome '.bash_profile'      when 'zsh'   then fileAtHome '.zshrc'      when 'fish'  then fileAtHome '.config/fish/config.fish'  setupShellInitFile: (initFile=@getDefaultShellInitFile())->    template = (command)=>      """      # begin #{@program} completion      #{command}      # end #{@program} completion      """    switch @shell      when 'bash'        programFolder = path.join @HOME, ".#{@program}"        completionPath = path.join programFolder, 'completion.sh'        fs.mkdirSync programFolder unless fs.existsSync programFolder        fs.writeFileSync completionPath, @generateCompletionCode()        fs.appendFileSync initFile, template "source #{completionPath}"      when 'zsh'        fs.appendFileSync initFile, template ". <(#{@program} --completion)"      when 'fish'        fs.appendFileSync initFile, template "#{@program} --completion-fish | source"    process.exit();  init: ->    do @generate if @compgen > -1module.exports = (template, args...)->  if template instanceof Array and args.length > 0    [program, callbacks] = [template[0].trim(), args]    fragments = callbacks.map (callback, index) -> "arg#{index}"  else    [program, fragments...] = template.split /\s+/    callbacks = []  fragments = fragments.map (fragment)-> fragment.replace /^\<+|\>+$/g, ''  _omelette = new Omelette  _omelette.setProgram program  _omelette.setFragments fragments...  _omelette.checkInstall()  for callback, index in callbacks    fragment = "arg#{index}"    do (callback = callback)->      _omelette.on fragment, (args...)->        @reply if callback instanceof Array          callback        else          callback args...  _omelette
 |