mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
linux: install bash completion as individual components
In order to prevent bloating the shell with thousands of bash completion specs, bash completions are lazy-loaded as soon as the command is first tab-completed. This only works when the completionsdir contains a filename with the same name as the command being completed; as a result, calibre commands were able to be tab-completed only after 'calibre' was first completed. (This is unlike the zsh completions, which work when installed as a single unified file because zsh builds a cache of all known compdefs, and can load a completion on demand by reading from the file which contains it.) One common solution for programs which install several completions that share common helper functions is to install the completions in one file, and symlink all other command names to ensure the file is loaded by any name. I've opted for the other solution, which is to install each completion separately, since there is no common helper function to load once and use everywhere. As a result, there are some small speedups to be gained from only loading the completions being used. The main change is reindenting a lot of code to no longer be in a global context manager, but instead get written inside o_and_e/o_and_w. It's also necessary to write each individual completion file to the uninstaller, so do that.
This commit is contained in:
parent
05d42e0fe8
commit
19c4636677
@ -516,13 +516,13 @@ def get_bash_completion_path(root, share, info):
|
||||
info('Failed to find directory to install bash completions, using default.')
|
||||
path = '/usr/share/bash-completion/completions'
|
||||
if path and os.path.exists(path) and os.path.isdir(path):
|
||||
return os.path.join(path, 'calibre')
|
||||
return path
|
||||
else:
|
||||
# Use the default bash-completion dir under staging_share
|
||||
return os.path.join(share, 'bash-completion', 'completions', 'calibre')
|
||||
return os.path.join(share, 'bash-completion', 'completions')
|
||||
|
||||
|
||||
def write_completion(bash_comp_dest, zsh):
|
||||
def write_completion(self, bash_comp_dest, zsh):
|
||||
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.gui2.lrf_renderer.main import option_parser as lrfviewerop
|
||||
@ -540,47 +540,51 @@ def write_completion(bash_comp_dest, zsh):
|
||||
input_formats = sorted(all_input_formats())
|
||||
tweak_formats = sorted(x.lower() for x in SUPPORTED|IMPORTABLE)
|
||||
|
||||
if bash_comp_dest and not os.path.exists(os.path.dirname(bash_comp_dest)):
|
||||
os.makedirs(os.path.dirname(bash_comp_dest))
|
||||
if bash_comp_dest and not os.path.exists(bash_comp_dest):
|
||||
os.makedirs(bash_comp_dest)
|
||||
|
||||
complete = 'calibre-complete'
|
||||
if getattr(sys, 'frozen_path', None):
|
||||
complete = os.path.join(getattr(sys, 'frozen_path'), complete)
|
||||
|
||||
with open(bash_comp_dest or os.devnull, 'wb') as f:
|
||||
def o_and_e(name, *args, **kwargs):
|
||||
bash_compfile = os.path.join(bash_comp_dest, name)
|
||||
with open(bash_compfile, 'wb') as f:
|
||||
f.write(opts_and_exts(name, *args, **kwargs))
|
||||
self.manifest.append(bash_compfile)
|
||||
zsh.opts_and_exts(name, *args, **kwargs)
|
||||
|
||||
def o_and_e(*args, **kwargs):
|
||||
f.write(opts_and_exts(*args, **kwargs))
|
||||
zsh.opts_and_exts(*args, **kwargs)
|
||||
def o_and_w(name, *args, **kwargs):
|
||||
bash_compfile = os.path.join(bash_comp_dest, name)
|
||||
with open(bash_compfile, 'wb') as f:
|
||||
f.write(opts_and_words(name, *args, **kwargs))
|
||||
self.manifest.append(bash_compfile)
|
||||
zsh.opts_and_words(name, *args, **kwargs)
|
||||
|
||||
def o_and_w(*args, **kwargs):
|
||||
f.write(opts_and_words(*args, **kwargs))
|
||||
zsh.opts_and_words(*args, **kwargs)
|
||||
|
||||
f.write(b'# calibre Bash Shell Completion\n')
|
||||
o_and_e('calibre', guiop, BOOK_EXTENSIONS)
|
||||
o_and_e('lrf2lrs', lrf2lrsop, ['lrf'], file_map={'--output':['lrs']})
|
||||
o_and_e('ebook-meta', metaop,
|
||||
list(meta_filetypes()), cover_opts=['--cover', '-c'],
|
||||
opf_opts=['--to-opf', '--from-opf'])
|
||||
o_and_e('ebook-polish', polish_op,
|
||||
[x.lower() for x in SUPPORTED], cover_opts=['--cover', '-c'],
|
||||
opf_opts=['--opf', '-o'])
|
||||
o_and_e('lrfviewer', lrfviewerop, ['lrf'])
|
||||
o_and_e('ebook-viewer', viewer_op, input_formats)
|
||||
o_and_e('ebook-edit', tweak_op, tweak_formats)
|
||||
o_and_w('fetch-ebook-metadata', fem_op, [])
|
||||
o_and_w('calibre-smtp', smtp_op, [])
|
||||
o_and_w('calibre-server', serv_op, [])
|
||||
o_and_e('calibre-debug', debug_op, ['py', 'recipe', 'epub', 'mobi', 'azw', 'azw3', 'docx'], file_map={
|
||||
'--tweak-book':['epub', 'azw3', 'mobi'],
|
||||
'--subset-font':['ttf', 'otf'],
|
||||
'--exec-file':['py', 'recipe'],
|
||||
'--add-simple-plugin':['py'],
|
||||
'--inspect-mobi':['mobi', 'azw', 'azw3'],
|
||||
'--viewer':sorted(available_input_formats()),
|
||||
})
|
||||
f.write((textwrap.dedent('''
|
||||
o_and_e('calibre', guiop, BOOK_EXTENSIONS)
|
||||
o_and_e('lrf2lrs', lrf2lrsop, ['lrf'], file_map={'--output':['lrs']})
|
||||
o_and_e('ebook-meta', metaop,
|
||||
list(meta_filetypes()), cover_opts=['--cover', '-c'],
|
||||
opf_opts=['--to-opf', '--from-opf'])
|
||||
o_and_e('ebook-polish', polish_op,
|
||||
[x.lower() for x in SUPPORTED], cover_opts=['--cover', '-c'],
|
||||
opf_opts=['--opf', '-o'])
|
||||
o_and_e('lrfviewer', lrfviewerop, ['lrf'])
|
||||
o_and_e('ebook-viewer', viewer_op, input_formats)
|
||||
o_and_e('ebook-edit', tweak_op, tweak_formats)
|
||||
o_and_w('fetch-ebook-metadata', fem_op, [])
|
||||
o_and_w('calibre-smtp', smtp_op, [])
|
||||
o_and_w('calibre-server', serv_op, [])
|
||||
o_and_e('calibre-debug', debug_op, ['py', 'recipe', 'epub', 'mobi', 'azw', 'azw3', 'docx'], file_map={
|
||||
'--tweak-book':['epub', 'azw3', 'mobi'],
|
||||
'--subset-font':['ttf', 'otf'],
|
||||
'--exec-file':['py', 'recipe'],
|
||||
'--add-simple-plugin':['py'],
|
||||
'--inspect-mobi':['mobi', 'azw', 'azw3'],
|
||||
'--viewer':sorted(available_input_formats()),
|
||||
})
|
||||
with open(os.path.join(bash_comp_dest, 'ebook-device'), 'wb') as f:
|
||||
f.write(textwrap.dedent('''\
|
||||
_ebook_device_ls()
|
||||
{
|
||||
local pattern search listing prefix
|
||||
@ -650,10 +654,11 @@ def write_completion(bash_comp_dest, zsh):
|
||||
;;
|
||||
esac
|
||||
}
|
||||
complete -o nospace -F _ebook_device ebook-device
|
||||
|
||||
complete -o nospace -C %s ebook-convert
|
||||
''')%complete).encode('utf-8'))
|
||||
complete -o nospace -F _ebook_device ebook-device''').encode('utf-8'))
|
||||
self.manifest.append(os.path.join(bash_comp_dest, 'ebook-device'))
|
||||
with open(os.path.join(bash_comp_dest, 'ebook-convert'), 'wb') as f:
|
||||
f.write(('complete -o nospace -C %s ebook-convert'%complete).encode('utf-8'))
|
||||
self.manifest.append(os.path.join(bash_comp_dest, 'ebook-convert'))
|
||||
zsh.write()
|
||||
# }}}
|
||||
|
||||
@ -777,9 +782,8 @@ class PostInstall:
|
||||
self.manifest.append(zsh.dest)
|
||||
bash_comp_dest = get_bash_completion_path(self.opts.staging_root, os.path.dirname(self.opts.staging_sharedir), self.info)
|
||||
if bash_comp_dest is not None:
|
||||
self.info('Installing bash completion to:', bash_comp_dest)
|
||||
self.manifest.append(bash_comp_dest)
|
||||
write_completion(bash_comp_dest, zsh)
|
||||
self.info('Installing bash completion to:', bash_comp_dest+os.sep)
|
||||
write_completion(self, bash_comp_dest, zsh)
|
||||
except TypeError as err:
|
||||
if 'resolve_entities' in unicode_type(err):
|
||||
print('You need python-lxml >= 2.0.5 for calibre')
|
||||
|
Loading…
x
Reference in New Issue
Block a user