diff --git a/kitty/cli.py b/kitty/cli.py index ac2651fe3..26aa1a711 100644 --- a/kitty/cli.py +++ b/kitty/cli.py @@ -157,6 +157,7 @@ def parse_option_spec(spec=OPTIONS): seq = [] disabled = [] mpat = re.compile('([a-z]+)=(.+)') + current_cmd = None for line in lines: line = line.strip() @@ -250,7 +251,7 @@ def wrap(text, limit=80): return reversed(lines) -def print_help_for_seq(seq): +def print_help_for_seq(seq, usage, message, appname): from kitty.icat import screen_size try: linesz = min(screen_size().cols, 76) @@ -265,11 +266,14 @@ def print_help_for_seq(seq): j = '\n' + (' ' * indent) a((' ' * leading_indent) + j.join(wrap(text, limit=linesz - indent))) - a('{}: {} [options] [program-to-run ...]'.format(title('Usage'), bold(yellow(appname)))) + usage = usage or '[program-to-run ...]' + a('{}: {} [options] {}'.format(title('Usage'), bold(yellow(appname)), usage)) a('') - wa('Run the {appname} terminal emulator. You can also specify the {program} to run inside {appname} as normal' - ' arguments following the {options}. For example: {appname} /bin/sh'.format( - appname=italic(appname), options=italic('options'), program=italic('program'))) + message = message or ( + 'Run the |_ {appname}| terminal emulator. You can also specify the |_ program| to run inside |_ {appname}| as normal' + ' arguments following the |_ options|. For example: {appname} /bin/sh' + ).format(appname=appname) + wa(prettify(message)) a('') a('{}:'.format(title('Options'))) for opt in seq: @@ -283,7 +287,7 @@ def print_help_for_seq(seq): text = '\n'.join(blocks) + '\n\n' + version() if sys.stdout.isatty(): - p = subprocess.Popen(['less', '-isR'], stdin=subprocess.PIPE) + p = subprocess.Popen(['less', '-isRXF'], stdin=subprocess.PIPE) p.communicate(text.encode('utf-8')) raise SystemExit(p.wait()) else: @@ -305,11 +309,12 @@ def defval_for_opt(opt): class Options: - def __init__(self, seq): + def __init__(self, seq, usage, message, appname): self.alias_map = {} self.seq = seq self.names_map = {} self.values_map = {} + self.usage, self.message, self.appname = usage, message, appname for opt in seq: if isinstance(opt, str): continue @@ -327,7 +332,7 @@ class Options: def needs_arg(self, alias): if alias in ('-h', '--help'): - print_help_for_seq(self.seq) + print_help_for_seq(self.seq, self.usage, self.message, self.appname or appname) raise SystemExit(0) opt = self.opt_for_alias(alias) if opt['dest'] == 'version': @@ -364,14 +369,12 @@ class Namespace: setattr(self, name, kwargs[name]) -def parse_cmdline(options, args=None): +def parse_cmdline(oc, disabled, args=None): NORMAL, EXPECTING_ARG = 'NORMAL', 'EXPECTING_ARG' state = NORMAL if args is None: args = sys.argv[1:] args = deque(args) - seq, disabled = options - oc = Options(seq) current_option = None while args: @@ -416,9 +419,11 @@ def options_spec(): return options_spec.ans -def parse_args(args=None): - options = parse_option_spec(options_spec()) - return parse_cmdline(options, args=args) +def parse_args(args=None, ospec=options_spec, usage=None, message=None, appname=None): + options = parse_option_spec(ospec()) + seq, disabled = options + oc = Options(seq, usage, message, appname) + return parse_cmdline(oc, disabled, args=args) def create_opts(args): diff --git a/kitty/icat.py b/kitty/icat.py index 547a930cf..3cdc77d7c 100755 --- a/kitty/icat.py +++ b/kitty/icat.py @@ -2,7 +2,6 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2017, Kovid Goyal -import argparse import codecs import fcntl import mimetypes @@ -18,15 +17,12 @@ import tty import zlib from base64 import standard_b64encode from collections import namedtuple -from gettext import gettext as _ from math import ceil, floor from tempfile import NamedTemporaryFile from time import monotonic -try: - from kitty.constants import appname -except ImportError: - appname = '' +from kitty.constants import appname +from kitty.cli import parse_args try: fsenc = sys.getfilesystemencoding() or 'utf-8' @@ -44,20 +40,16 @@ class OpenFailed(ValueError): self.path = path -def option_parser(): - parser = argparse.ArgumentParser( - prog=appname + '-icat' if appname else 'icat', - description=_('Display images in the terminal') - ) - a = parser.add_argument - a( - 'items', - nargs='+', - help=_( - 'Image files or directories. Directories are scanned recursively.' +OPTIONS = '''\ +''' + + +def options_spec(): + if not hasattr(options_spec, 'ans'): + options_spec.ans = OPTIONS.format( + appname='{}-icat'.format(appname), ) - ) - return parser + return options_spec.ans Size = namedtuple('Size', 'rows cols width height') @@ -251,13 +243,17 @@ def main(args=sys.argv): raise SystemExit( 'Terminal does not support reporting screen sizes via the TIOCGWINSZ ioctl' ) - args = option_parser().parse_args(args[1:]) - if not args.items: + msg = ( + 'A cat like utility to display images in the terminal.' + ' You can specify multiple images files and/or directories.' + ' Directories are scanned recursively for image files.') + args, items = parse_args(args[1:], options_spec, 'image-file ...', msg, '{} icat'.format(appname)) + if not items: raise SystemExit('You must specify at least one file to cat') if not detect_support(): raise SystemExit('This terminal emulator does not support the graphics protocol, use a terminal emulator such as kitty that does support it') errors = [] - for item in args.items: + for item in items: try: if os.path.isdir(item): for x in scan(item):