Allow running the launch actions by invoking the kitty executable with +open

This commit is contained in:
Kovid Goyal 2022-02-05 17:39:48 +05:30
parent ced61096df
commit 39c77a9486
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
5 changed files with 56 additions and 15 deletions

View File

@ -42,6 +42,13 @@ def complete(args: List[str]) -> None:
complete_main(args[1:], entry_points, namespaced_entry_points) complete_main(args[1:], entry_points, namespaced_entry_points)
def open_urls(args: List[str]) -> None:
setattr(sys, 'cmdline_args_for_open', True)
sys.argv = ['kitty'] + args[1:]
from kitty.main import main as kitty_main
kitty_main()
def launch(args: List[str]) -> None: def launch(args: List[str]) -> None:
import runpy import runpy
sys.argv = args[1:] sys.argv = args[1:]
@ -129,6 +136,7 @@ def namespaced(args: List[str]) -> None:
namespaced_entry_points['complete'] = complete namespaced_entry_points['complete'] = complete
namespaced_entry_points['runpy'] = runpy namespaced_entry_points['runpy'] = runpy
namespaced_entry_points['launch'] = launch namespaced_entry_points['launch'] = launch
namespaced_entry_points['open'] = open_urls
namespaced_entry_points['kitten'] = run_kitten namespaced_entry_points['kitten'] = run_kitten
namespaced_entry_points['edit-config'] = edit_config_file namespaced_entry_points['edit-config'] = edit_config_file
namespaced_entry_points['shebang'] = shebang namespaced_entry_points['shebang'] = shebang

View File

@ -114,6 +114,12 @@ URLs onto the kitty dock icon to open them with kitty. The default actions are:
* Run shell scripts in a shell * Run shell scripts in a shell
* Open SSH urls using the ssh command * Open SSH urls using the ssh command
These actions can also be executed from the command line by running::
open -a kitty.app file_or_url ... (on macOS only)
or
kitty +open file_or_url ...
You can customize these actions by creating a :file:`launch-actions.conf` file You can customize these actions by creating a :file:`launch-actions.conf` file
in the kitty config directory, just like in the kitty config directory, just like
the :file:`open-actions.conf` file above. For example: the :file:`open-actions.conf` file above. For example:

View File

@ -5,12 +5,13 @@
import json import json
import os import os
import re import re
import sys
from contextlib import suppress from contextlib import suppress
from functools import partial from functools import partial
from gettext import gettext as _ from gettext import gettext as _
from typing import ( from typing import (
Any, Callable, Container, Dict, Iterable, Iterator, List, Optional, Tuple, Any, Callable, Container, Dict, Iterable, Iterator, List, Optional,
Union Sequence, Tuple, Union
) )
from weakref import WeakValueDictionary from weakref import WeakValueDictionary
@ -273,8 +274,8 @@ def update_keymap(self) -> None:
for sc in self.global_shortcuts.values(): for sc in self.global_shortcuts.values():
self.keymap.pop(sc, None) self.keymap.pop(sc, None)
def startup_first_child(self, os_window_id: Optional[int]) -> None: def startup_first_child(self, os_window_id: Optional[int], startup_sessions: Sequence[Optional[Session]] = ()) -> None:
startup_sessions = create_sessions(get_options(), self.args, default_session=get_options().startup_session) startup_sessions = startup_sessions or create_sessions(get_options(), self.args, default_session=get_options().startup_session)
for startup_session in startup_sessions: for startup_session in startup_sessions:
self.add_os_window(startup_session, os_window_id=os_window_id) self.add_os_window(startup_session, os_window_id=os_window_id)
os_window_id = None os_window_id = None
@ -546,6 +547,10 @@ def peer_message_received(self, msg_bytes: bytes, peer_id: int) -> Union[bytes,
from .cli_stub import CLIOptions from .cli_stub import CLIOptions
startup_id = data.get('startup_id') startup_id = data.get('startup_id')
args, rest = parse_args(data['args'][1:], result_class=CLIOptions) args, rest = parse_args(data['args'][1:], result_class=CLIOptions)
cmdline_args_for_open = data.get('cmdline_args_for_open')
if cmdline_args_for_open:
self.launch_urls(*cmdline_args_for_open, no_replace_window=True)
return None
args.args = rest args.args = rest
opts = create_opts(args) opts = create_opts(args)
if args.session == '-': if args.session == '-':
@ -746,6 +751,13 @@ def start(self, first_os_window_id: int) -> None:
if not getattr(self, 'io_thread_started', False): if not getattr(self, 'io_thread_started', False):
self.child_monitor.start() self.child_monitor.start()
self.io_thread_started = True self.io_thread_started = True
urls: List[str] = getattr(sys, 'cmdline_args_for_open', [])
sess = create_sessions(get_options(), self.args, special_window=SpecialWindow([kitty_exe(), '+runpy', 'input()']))
if urls:
delattr(sys, 'cmdline_args_for_open')
self.startup_first_child(first_os_window_id, startup_sessions=tuple(sess))
self.launch_urls(*urls)
else:
self.startup_first_child(first_os_window_id) self.startup_first_child(first_os_window_id)
if get_options().update_check_interval > 0 and not hasattr(self, 'update_check_started'): if get_options().update_check_interval > 0 and not hasattr(self, 'update_check_started'):
@ -2234,19 +2246,21 @@ def show_kitty_env_vars(self) -> None:
output = '\n'.join(f'{k}={v}' for k, v in os.environ.items()) output = '\n'.join(f'{k}={v}' for k, v in os.environ.items())
self.display_scrollback(w, output, title=_('Current kitty env vars'), report_cursor=False) self.display_scrollback(w, output, title=_('Current kitty env vars'), report_cursor=False)
def launch_url(self, url: str) -> None: def launch_urls(self, *urls: str, no_replace_window: bool = False) -> None:
if url == ":cocoa::application launched::": if urls == (":cocoa::application launched::",):
self.cocoa_application_launched = True self.cocoa_application_launched = True
return return
from .open_actions import actions_for_launch
from .launch import force_window_launch from .launch import force_window_launch
actions = list(actions_for_launch(url)) from .open_actions import actions_for_launch
actions = []
for url in urls:
actions.extend(actions_for_launch(url))
tab = self.active_tab tab = self.active_tab
if tab is not None: if tab is not None:
w = tab.active_window w = tab.active_window
else: else:
w = None w = None
needs_window_replaced = not self.cocoa_application_launched or not self.os_window_map and w is not None and w.id == 1 needs_window_replaced = not no_replace_window and (not self.cocoa_application_launched or not self.os_window_map) and w is not None and w.id == 1
def clear_initial_window() -> None: def clear_initial_window() -> None:
if needs_window_replaced and tab is not None and w is not None: if needs_window_replaced and tab is not None and w is not None:
@ -2254,7 +2268,7 @@ def clear_initial_window() -> None:
if not actions: if not actions:
with force_window_launch(needs_window_replaced): with force_window_launch(needs_window_replaced):
self.launch(kitty_exe(), '+runpy', f'print("The url:", {url!r}, "is of unknown type, cannot open it.");' self.launch(kitty_exe(), '+runpy', f'print("The url:", {urls[0]!r}, "is of unknown type, cannot open it.");'
'from kitty.utils import hold_till_enter; hold_till_enter(); raise SystemExit(1)') 'from kitty.utils import hold_till_enter; hold_till_enter(); raise SystemExit(1)')
clear_initial_window() clear_initial_window()
else: else:

View File

@ -1050,7 +1050,7 @@ process_cocoa_pending_actions(void) {
if (cocoa_pending_actions_data.open_urls_count) { if (cocoa_pending_actions_data.open_urls_count) {
for (unsigned cpa = 0; cpa < cocoa_pending_actions_data.open_urls_count; cpa++) { for (unsigned cpa = 0; cpa < cocoa_pending_actions_data.open_urls_count; cpa++) {
if (cocoa_pending_actions_data.open_urls[cpa]) { if (cocoa_pending_actions_data.open_urls[cpa]) {
call_boss(launch_url, "s", cocoa_pending_actions_data.open_urls[cpa]); call_boss(launch_urls, "s", cocoa_pending_actions_data.open_urls[cpa]);
free(cocoa_pending_actions_data.open_urls[cpa]); free(cocoa_pending_actions_data.open_urls[cpa]);
cocoa_pending_actions_data.open_urls[cpa] = NULL; cocoa_pending_actions_data.open_urls[cpa] = NULL;
} }

View File

@ -60,7 +60,7 @@ def talk_to_instance(args: CLIOptions) -> None:
stdin = '' stdin = ''
if args.session == '-': if args.session == '-':
stdin = sys.stdin.read() stdin = sys.stdin.read()
data = {'cmd': 'new_instance', 'args': tuple(sys.argv), data = {'cmd': 'new_instance', 'args': tuple(sys.argv), 'cmdline_args_for_open': getattr(sys, 'cmdline_args_for_open', []),
'startup_id': os.environ.get('DESKTOP_STARTUP_ID'), 'startup_id': os.environ.get('DESKTOP_STARTUP_ID'),
'cwd': os.getcwd(), 'stdin': stdin} 'cwd': os.getcwd(), 'stdin': stdin}
notify_socket = None notify_socket = None
@ -367,7 +367,20 @@ def _main() -> None:
cwd_ok = False cwd_ok = False
if not cwd_ok: if not cwd_ok:
os.chdir(os.path.expanduser('~')) os.chdir(os.path.expanduser('~'))
cli_opts, rest = parse_args(args=args, result_class=CLIOptions) if getattr(sys, 'cmdline_args_for_open', False):
usage = 'file_or_url ...'
appname = 'kitty +open'
msg = (
'Run kitty and open the specified files or URLs in it, using launch-actions.conf. For details'
' see https://sw.kovidgoyal.net/kitty/open_actions/#scripting-the-opening-of-files-with-kitty-on-macos'
'\n\nAll the normal kitty options can be used.')
else:
usage = msg = appname = None
cli_opts, rest = parse_args(args=args, result_class=CLIOptions, usage=usage, message=msg, appname=appname)
if getattr(sys, 'cmdline_args_for_open', False):
setattr(sys, 'cmdline_args_for_open', rest)
cli_opts.args = []
else:
cli_opts.args = rest cli_opts.args = rest
if cli_opts.detach: if cli_opts.detach:
if cli_opts.session == '-': if cli_opts.session == '-':