travel/admin/node_modules/omelette/coffee/omelette.coffee

217 lines
6.4 KiB
CoffeeScript

###
# 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)
level
class 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 > -1
module.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