ZSH completion for ebook-convert

This commit is contained in:
Kovid Goyal 2013-02-27 08:58:33 +05:30
parent 866daf0519
commit 5bdf993ffa
3 changed files with 117 additions and 11 deletions

View File

@ -100,6 +100,9 @@ def option_recommendation_to_cli_option(add_option, rec):
switches = ['--disable-'+opt.long_switch] switches = ['--disable-'+opt.long_switch]
add_option(Option(*switches, **attrs)) add_option(Option(*switches, **attrs))
def group_titles():
return _('INPUT OPTIONS'), _('OUTPUT OPTIONS')
def add_input_output_options(parser, plumber): def add_input_output_options(parser, plumber):
input_options, output_options = \ input_options, output_options = \
plumber.input_options, plumber.output_options plumber.input_options, plumber.output_options
@ -109,14 +112,14 @@ def add_input_output_options(parser, plumber):
option_recommendation_to_cli_option(group, opt) option_recommendation_to_cli_option(group, opt)
if input_options: if input_options:
title = _('INPUT OPTIONS') title = group_titles()[0]
io = OptionGroup(parser, title, _('Options to control the processing' io = OptionGroup(parser, title, _('Options to control the processing'
' of the input %s file')%plumber.input_fmt) ' of the input %s file')%plumber.input_fmt)
add_options(io.add_option, input_options) add_options(io.add_option, input_options)
parser.add_option_group(io) parser.add_option_group(io)
if output_options: if output_options:
title = _('OUTPUT OPTIONS') title = group_titles()[1]
oo = OptionGroup(parser, title, _('Options to control the processing' oo = OptionGroup(parser, title, _('Options to control the processing'
' of the output %s')%plumber.output_fmt) ' of the output %s')%plumber.output_fmt)
add_options(oo.add_option, output_options) add_options(oo.add_option, output_options)

View File

@ -137,9 +137,12 @@ class ZshCompleter(object): # {{{
def get_options(self, parser, cover_opts=('--cover',), opf_opts=('--opf',), def get_options(self, parser, cover_opts=('--cover',), opf_opts=('--opf',),
file_map={}): file_map={}):
options = parser.option_list if hasattr(parser, 'option_list'):
for group in parser.option_groups: options = parser.option_list
options += group.option_list for group in parser.option_groups:
options += group.option_list
else:
options = parser
for opt in options: for opt in options:
lo, so = opt._long_opts, opt._short_opts lo, so = opt._long_opts, opt._short_opts
if opt.takes_value(): if opt.takes_value():
@ -160,7 +163,7 @@ class ZshCompleter(object): # {{{
arg = '' arg = ''
if opt.takes_value(): if opt.takes_value():
arg = ':"%s":'%h arg = ':"%s":'%h
if opt.dest in {'to_dir', 'outbox', 'with_library', 'library_path'}: if opt.dest in {'debug_pipeline', 'to_dir', 'outbox', 'with_library', 'library_path'}:
arg += "'_path_files -/'" arg += "'_path_files -/'"
elif opt.choices: elif opt.choices:
arg += "(%s)"%'|'.join(opt.choices) arg += "(%s)"%'|'.join(opt.choices)
@ -201,6 +204,98 @@ class ZshCompleter(object): # {{{
txt = '_arguments -s \\\n ' + opts txt = '_arguments -s \\\n ' + opts
self.commands[name] = txt self.commands[name] = txt
def do_ebook_convert(self, f):
from calibre.ebooks.conversion.plumber import supported_input_formats
from calibre.web.feeds.recipes.collection import get_builtin_recipe_titles
from calibre.customize.ui import available_output_formats
from calibre.ebooks.conversion.cli import create_option_parser, group_titles
from calibre.utils.logging import DevNull
input_fmts = set(supported_input_formats())
output_fmts = set(available_output_formats())
iexts = {x.upper() for x in input_fmts}.union(input_fmts)
oexts = {x.upper() for x in output_fmts}.union(output_fmts)
w = lambda x: f.write(x if isinstance(x, bytes) else x.encode('utf-8'))
# Arg 1
w('\n_ebc_input_args() {')
w('\n local extras; extras=(')
w('\n {-h,--help}":Show Help"')
w('\n "--version:Show program version"')
w('\n "--list-recipes:List builtin recipe names"')
for recipe in sorted(set(get_builtin_recipe_titles())):
recipe = recipe.replace(':', '\\:').replace('"', '\\"')
w(u'\n "%s.recipe"'%(recipe))
w('\n ); _describe -t recipes "ebook-convert builtin recipes" extras')
w('\n _files -g "%s"'%' '.join(('*.%s'%x for x in iexts)))
w('\n}\n')
# Arg 2
w('\n_ebc_output_args() {')
w('\n local extras; extras=(')
for x in output_fmts:
w('\n ".{0}:Convert to a .{0} file with the same name as the input file"'.format(x))
w('\n ); _describe -t output "ebook-convert output" extras')
w('\n _files -g "%s"'%' '.join(('*.%s'%x for x in oexts)))
w('\n _path_files -/')
w('\n}\n')
log = DevNull()
def get_parser(input_fmt='epub', output_fmt=None):
of = ('dummy2.'+output_fmt) if output_fmt else 'dummy'
return create_option_parser(('ec', 'dummy1.'+input_fmt, of, '-h'), log)[0]
# Common options
input_group, output_group = group_titles()
p = get_parser()
opts = p.option_list
for group in p.option_groups:
if group.title not in {input_group, output_group}:
opts += group.option_list
opts.append(p.get_option('--pretty-print'))
opts.append(p.get_option('--input-encoding'))
opts = '\\\n '.join(tuple(
self.get_options(opts, file_map={'--search-replace':()})))
w('\n_ebc_common_opts() {')
w('\n _arguments -s \\\n ' + opts)
w('\n}\n')
# Input/Output format options
for fmts, group_title, func in (
(input_fmts, input_group, '_ebc_input_opts_%s'),
(output_fmts, output_group, '_ebc_output_opts_%s'),
):
for fmt in fmts:
is_input = group_title == input_group
if is_input and fmt in {'rar', 'zip', 'oebzip'}: continue
p = (get_parser(input_fmt=fmt) if is_input
else get_parser(output_fmt=fmt))
opts = None
for group in p.option_groups:
if group.title == group_title:
opts = [o for o in group.option_list if
'--pretty-print' not in o._long_opts and
'--input-encoding' not in o._long_opts]
if not opts: continue
opts = '\\\n '.join(tuple(self.get_options(opts)))
w('\n%s() {'%(func%fmt))
w('\n _arguments -s \\\n ' + opts)
w('\n}\n')
w('\n_ebook_convert() {')
w('\n local iarg oarg context state_descr state line\n typeset -A opt_args\n local ret=1')
w("\n _arguments '1: :_ebc_input_args' '*::ebook-convert output:->args' && ret=0")
w("\n case $state in \n (args)")
w('\n iarg=${line[1]##*.}; ')
w("\n _arguments '1: :_ebc_output_args' '*::ebook-convert options:->args' && ret=0")
w("\n case $state in \n (args)")
w('\n oarg=${line[1]##*.}')
w('\n iarg="_ebc_input_opts_${(L)iarg}"; oarg="_ebc_output_opts_${(L)oarg}"')
w('\n _call_function - $iarg; _call_function - $oarg; _ebc_common_opts; ret=0')
w('\n ;;\n esac')
w("\n ;;\n esac\n return ret")
w('\n}\n')
def do_calibredb(self, f): def do_calibredb(self, f):
import calibre.library.cli as cli import calibre.library.cli as cli
from calibre.customize.ui import available_catalog_formats from calibre.customize.ui import available_catalog_formats
@ -246,13 +341,13 @@ class ZshCompleter(object): # {{{
f.write('\n_calibredb() {') f.write('\n_calibredb() {')
f.write( f.write(
r''' r'''
local context curcontext="$curcontext" state line local state line state_descr context
typeset -A opt_args typeset -A opt_args
local ret=1 local ret=1
_arguments -C \ _arguments \
'1: :_calibredb_cmds' \ '1: :_calibredb_cmds' \
'*::arg:->args' \ '*::calibredb subcommand options:->args' \
&& ret=0 && ret=0
case $state in case $state in
@ -273,8 +368,10 @@ class ZshCompleter(object): # {{{
def write(self): def write(self):
if self.dest: if self.dest:
self.commands['calibredb'] = ' _calibredb "$@"' self.commands['calibredb'] = ' _calibredb "$@"'
self.commands['ebook-convert'] = ' _ebook_convert "$@"'
with open(self.dest, 'wb') as f: with open(self.dest, 'wb') as f:
f.write('#compdef ' + ' '.join(self.commands)+'\n') f.write('#compdef ' + ' '.join(self.commands)+'\n')
self.do_ebook_convert(f)
self.do_calibredb(f) self.do_calibredb(f)
f.write('case $service in\n') f.write('case $service in\n')
for c, txt in self.commands.iteritems(): for c, txt in self.commands.iteritems():
@ -380,7 +477,7 @@ class PostInstall:
def setup_completion(self): # {{{ def setup_completion(self): # {{{
try: try:
self.info('Setting up bash/zsh completion...') self.info('Setting up command-line completion...')
from calibre.ebooks.metadata.cli import option_parser as metaop, filetypes as meta_filetypes from calibre.ebooks.metadata.cli import option_parser as metaop, filetypes as meta_filetypes
from calibre.ebooks.lrf.lrfparser import option_parser as lrf2lrsop from calibre.ebooks.lrf.lrfparser import option_parser as lrf2lrsop
from calibre.gui2.lrf_renderer.main import option_parser as lrfviewerop from calibre.gui2.lrf_renderer.main import option_parser as lrfviewerop
@ -808,3 +905,4 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) sys.exit(main())
sys.exit(main())

View File

@ -28,7 +28,6 @@ class Stream(object):
def flush(self): def flush(self):
self.stream.flush() self.stream.flush()
class ANSIStream(Stream): class ANSIStream(Stream):
def __init__(self, stream=sys.stdout): def __init__(self, stream=sys.stdout):
@ -164,6 +163,12 @@ class Log(object):
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs):
self.prints(INFO, *args, **kwargs) self.prints(INFO, *args, **kwargs)
class DevNull(Log):
def __init__(self):
Log.__init__(self, level=Log.ERROR)
self.outputs = []
class ThreadSafeLog(Log): class ThreadSafeLog(Log):
def __init__(self, level=Log.INFO): def __init__(self, level=Log.INFO):