diff --git a/kittens/runner.py b/kittens/runner.py new file mode 100644 index 000000000..a6133b508 --- /dev/null +++ b/kittens/runner.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +# License: GPL v3 Copyright: 2018, Kovid Goyal + + +import importlib +import os +import sys +from functools import partial + + +def import_kitten_main_module(config_dir, kitten): + if kitten.endswith('.py'): + path = os.path.expanduser(kitten) + if not os.path.isabs(path): + path = os.path.join(config_dir, path) + path = os.path.abspath(path) + if os.path.dirname(path): + sys.path.insert(0, os.path.dirname(path)) + with open(path) as f: + src = f.read() + code = compile(src, path, 'exec') + g = {'__name__': 'kitten'} + exec(code, g) + return {'start': g['main'], 'end': g['handle_result']} + else: + m = importlib.import_module('kittens.{}.main'.format(kitten)) + return {'start': m.main, 'end': m.handle_result} + + +def create_kitten_handler(kitten, orig_args): + from kitty.constants import config_dir + m = import_kitten_main_module(config_dir, kitten) + return partial(m['end'], [kitten] + orig_args) + + +def launch(args): + config_dir, kitten = args[:2] + del args[:2] + args = [kitten] + args + os.environ['KITTY_CONFIG_DIRECTORY'] = config_dir + from kittens.tui.operations import clear_screen, reset_mode + m = import_kitten_main_module(config_dir, kitten) + result = m['start'](args) + print(reset_mode('ALTERNATE_SCREEN') + clear_screen(), end='') + if result is not None: + import json + print('OK:', json.dumps(result)) + + +def main(): + try: + args = sys.argv[1:] + launch(args) + except Exception: + print('Unhandled exception running kitten:') + import traceback + traceback.print_exc() + input('Press Enter to quit...') diff --git a/kittens/unicode_input/main.py b/kittens/unicode_input/main.py index 0556b5653..76c314eed 100644 --- a/kittens/unicode_input/main.py +++ b/kittens/unicode_input/main.py @@ -5,7 +5,6 @@ import os import string import subprocess -import sys from functools import lru_cache from gettext import gettext as _ @@ -454,26 +453,24 @@ class UnicodeInput(Handler): self.refresh() -def run_loop(args): +def main(args): loop = Loop() with cached_values_for('unicode-input') as cached_values: handler = UnicodeInput(cached_values) loop.loop(handler) if handler.current_char and loop.return_code == 0: - print('OK:', hex(ord(handler.current_char))[2:]) try: handler.recent.remove(ord(handler.current_char)) except Exception: pass recent = [ord(handler.current_char)] + handler.recent cached_values['recent'] = recent[:len(DEFAULT_SET)] - return loop.return_code + return handler.current_char + if loop.return_code != 0: + raise SystemExit(loop.return_code) -def main(args=sys.argv): - try: - raise SystemExit(run_loop(args)) - except Exception: - import traceback - traceback.print_exc() - input(_('Press Enter to quit.')) +def handle_result(args, current_char, target_window_id, boss): + w = boss.window_id_map.get(target_window_id) + if w is not None: + w.paste(current_char) diff --git a/kittens/url_hints/main.py b/kittens/url_hints/main.py index 970ce9e8f..0f2d1b9a4 100644 --- a/kittens/url_hints/main.py +++ b/kittens/url_hints/main.py @@ -4,7 +4,6 @@ import re import string -import subprocess import sys from collections import namedtuple from functools import lru_cache, partial @@ -12,7 +11,6 @@ from gettext import gettext as _ from kitty.cli import parse_args from kitty.key_encoding import ESCAPE, backspace_key, enter_key -from kitty.utils import command_for_open from ..tui.handler import Handler from ..tui.loop import Loop @@ -178,16 +176,7 @@ def run_loop(args, lines, index_map): handler = URLHints(lines, index_map) loop.loop(handler) if handler.chosen and loop.return_code == 0: - if args.in_kitty: - import json - print('OK:', json.dumps({'url': handler.chosen, 'program': args.program, 'action': 'open_with'})) - else: - cmd = command_for_open(args.program) - ret = subprocess.Popen(cmd + [handler.chosen]).wait() - if ret != 0: - print('URL handler "{}" failed with return code: {}'.format(' '.join(cmd), ret), file=sys.stderr) - input('Press Enter to quit') - loop.return_code = ret + return {'url': handler.chosen, 'program': args.program} raise SystemExit(loop.return_code) @@ -215,7 +204,7 @@ def run(args, source_file=None): input(_('No URLs found, press Enter to abort.')) return - run_loop(args, lines, index_map) + return run_loop(args, lines, index_map) OPTIONS = partial('''\ @@ -234,16 +223,10 @@ expression instead. default={0} Comma separated list of recognized URL prefixes. Defaults to: {0} - - ---in-kitty -type=bool-set -Output the URL instead of opening it. Intended for use from within -kitty. '''.format, ','.join(sorted(URL_PREFIXES))) -def main(args=sys.argv): +def main(args): msg = 'Highlight URLs inside the specified text' try: args, items = parse_args(args[1:], OPTIONS, '[path to file or omit to use stdin]', msg, 'url_hints') @@ -251,9 +234,9 @@ def main(args=sys.argv): print(e.args[0], file=sys.stderr) input(_('Press Enter to quit')) return 1 - try: - run(args, (items or [None])[0]) - except Exception: - import traceback - traceback.print_exc() - input(_('Press Enter to quit')) + return run(args, (items or [None])[0]) + + +def handle_result(args, data, target_window_id, boss): + program = data['program'] + boss.open_url(data['url'], None if program == 'default' else program) diff --git a/kitty/boss.py b/kitty/boss.py index 70252af8a..426c78f1b 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -15,7 +15,7 @@ from .cli import create_opts, parse_args from .config import ( MINIMUM_FONT_SIZE, initial_window_size, prepare_config_file_for_editing ) -from .constants import appname, editor, set_boss +from .constants import appname, editor, set_boss, config_dir from .fast_data_types import ( ChildMonitor, create_os_window, current_os_window, destroy_global_data, destroy_sprite_map, get_clipboard_string, glfw_post_empty_event, @@ -471,14 +471,7 @@ class Boss: self.new_os_window(*cmd) def input_unicode_character(self): - w = self.active_window - tab = self.active_tab - if w is not None and tab is not None and w.overlay_for is None: - overlay_window = tab.new_special_window( - SpecialWindow( - ['kitty', '+runpy', 'from kittens.unicode_input.main import main; main()'], - overlay_for=w.id)) - overlay_window.action_on_close = partial(self.send_unicode_character, w.id) + self.run_kitten('none', 'unicode_input') def get_output(self, source_window, num_lines=1): output = '' @@ -489,19 +482,6 @@ class Boss: output += str(s.linebuf.line(i)) return output - def send_unicode_character(self, target_window_id, source_window): - w = self.window_id_map.get(target_window_id) - if w is not None: - output = self.get_output(source_window) - if output.startswith('OK: '): - try: - text = chr(int(output.partition(' ')[2], 16)) - except Exception: - import traceback - traceback.print_exc() - else: - w.paste(text) - def set_tab_title(self): w = self.active_window tab = self.active_tab @@ -524,34 +504,35 @@ class Boss: tab.set_title(title) break - def run_simple_kitten(self, type_of_input, kitten, *args): + def run_kitten(self, type_of_input, kitten, *args): import shlex w = self.active_window tab = self.active_tab if w is not None and tab is not None and w.overlay_for is None: cmdline = args[0] if args else '' args = shlex.split(cmdline) if cmdline else [] - if kitten == 'url_hints': - args[0:0] = ['--in-kitty', '--program', self.opts.open_url_with] + orig_args = args[:] + args[0:0] = [config_dir, kitten] if type_of_input in ('text', 'history', 'ansi', 'ansi-history'): data = w.as_text(as_ansi='ansi' in type_of_input, add_history='history' in type_of_input).encode('utf-8') elif type_of_input == 'none': data = None else: raise ValueError('Unknown type_of_input: {}'.format(type_of_input)) + from kittens.runner import create_kitten_handler + end_kitten = create_kitten_handler(kitten, orig_args) overlay_window = tab.new_special_window( SpecialWindow( - ['kitty', '+runpy', 'from kittens.{}.main import main; main()'.format(kitten)] + args, + ['kitty', '+runpy', 'from kittens.runner import main; main()'] + args, stdin=data, overlay_for=w.id)) - if kitten == 'url_hints': - overlay_window.action_on_close = self.open_hinted_url + overlay_window.action_on_close = partial(self.on_kitten_finish, w.id, end_kitten) - def open_hinted_url(self, source_window): + def on_kitten_finish(self, target_window_id, end_kitten, source_window): output = self.get_output(source_window, num_lines=None) if output.startswith('OK: '): - cmd = json.loads(output.partition(' ')[2].strip()) - open_url(cmd['url'], cmd['program']) + data = json.loads(output.partition(' ')[2].strip()) + end_kitten(data, target_window_id, self) def kitty_shell(self, window_type): cmd = ['kitty', '@'] @@ -580,12 +561,12 @@ class Boss: old_focus.focus_changed(False) tab.active_window.focus_changed(True) - def open_url(self, url): + def open_url(self, url, program=None): if url: - open_url(url, self.opts.open_url_with) + open_url(url, program or self.opts.open_url_with) - def open_url_lines(self, lines): - self.open_url(''.join(lines)) + def open_url_lines(self, lines, program=None): + self.open_url(''.join(lines), program) def destroy(self): self.shutting_down = True diff --git a/kitty/config.py b/kitty/config.py index e327035a1..7ae6cc5a6 100644 --- a/kitty/config.py +++ b/kitty/config.py @@ -108,7 +108,9 @@ def parse_key_action(action): args = tuple(map(parse_key_action, filter(None, parts))) elif func == 'send_text': args = rest.split(' ', 1) - elif func == 'run_simple_kitten': + elif func in ('run_kitten', 'run_simple_kitten'): + if func == 'run_simple_kitten': + func = 'run_kitten' args = rest.split(' ', 2) elif func == 'goto_tab': args = (max(0, int(rest)), ) @@ -389,7 +391,7 @@ def parse_defaults(lines, check_keys=False): Options, defaults = init_config(default_config_path, parse_defaults) actions = frozenset(all_key_actions) | frozenset( - 'combine send_text goto_tab goto_layout set_font_size new_tab_with_cwd new_window_with_cwd new_os_window_with_cwd'. + 'run_simple_kitten combine send_text goto_tab goto_layout set_font_size new_tab_with_cwd new_window_with_cwd new_os_window_with_cwd'. split() ) no_op_actions = frozenset({'noop', 'no-op', 'no_op'}) diff --git a/kitty/kitty.conf b/kitty/kitty.conf index 733833979..50e1b13d3 100644 --- a/kitty/kitty.conf +++ b/kitty/kitty.conf @@ -435,8 +435,8 @@ map ctrl+shift+f2 edit_config_file # Open a currently visible URL using the keyboard. The program used to open the URL is specified in open_url_with. # You can customize how the URLs are detected and opened by specifying command line options to # url_hints. For example: -# map ctrl+shift+e run_simple_kitten text url_hints --program firefox --regex "http://[^ ]+" -map ctrl+shift+e run_simple_kitten text url_hints +# map ctrl+shift+e run_kitten text url_hints --program firefox --regex "http://[^ ]+" +map ctrl+shift+e run_kitten text url_hints # Open the kitty shell in a new window/tab/overlay/os_window to control kitty using commands. map ctrl+shift+escape kitty_shell window