Start work on a unified interface for launching processes
This commit is contained in:
@@ -1006,14 +1006,20 @@ class Boss:
|
|||||||
def _new_window(self, args, cwd_from=None):
|
def _new_window(self, args, cwd_from=None):
|
||||||
tab = self.active_tab
|
tab = self.active_tab
|
||||||
if tab is not None:
|
if tab is not None:
|
||||||
|
allow_remote_control = False
|
||||||
location = None
|
location = None
|
||||||
if args and args[0].startswith('!'):
|
if args and args[0].startswith('!'):
|
||||||
location = args[0][1:].lower()
|
location = args[0][1:].lower()
|
||||||
args = args[1:]
|
args = args[1:]
|
||||||
|
if args and args[0] == '@':
|
||||||
|
args = args[1:]
|
||||||
|
allow_remote_control = True
|
||||||
if args:
|
if args:
|
||||||
return tab.new_special_window(self.args_to_special_window(args, cwd_from=cwd_from), location=location)
|
return tab.new_special_window(
|
||||||
|
self.args_to_special_window(args, cwd_from=cwd_from),
|
||||||
|
location=location, allow_remote_control=allow_remote_control)
|
||||||
else:
|
else:
|
||||||
return tab.new_window(cwd_from=cwd_from, location=location)
|
return tab.new_window(cwd_from=cwd_from, location=location, allow_remote_control=allow_remote_control)
|
||||||
|
|
||||||
def new_window(self, *args):
|
def new_window(self, *args):
|
||||||
self._new_window(args)
|
self._new_window(args)
|
||||||
@@ -1025,6 +1031,11 @@ class Boss:
|
|||||||
cwd_from = w.child.pid_for_cwd if w is not None else None
|
cwd_from = w.child.pid_for_cwd if w is not None else None
|
||||||
self._new_window(args, cwd_from=cwd_from)
|
self._new_window(args, cwd_from=cwd_from)
|
||||||
|
|
||||||
|
def launch(self, *args):
|
||||||
|
from kitty.launch import parse_launch_args, launch
|
||||||
|
opts, args = parse_launch_args(args)
|
||||||
|
launch(self, opts, args)
|
||||||
|
|
||||||
def move_tab_forward(self):
|
def move_tab_forward(self):
|
||||||
tm = self.active_tab_manager
|
tm = self.active_tab_manager
|
||||||
if tm is not None:
|
if tm is not None:
|
||||||
|
|||||||
@@ -160,12 +160,8 @@ class Child:
|
|||||||
child_fd = pid = None
|
child_fd = pid = None
|
||||||
forked = False
|
forked = False
|
||||||
|
|
||||||
def __init__(self, argv, cwd, opts, stdin=None, env=None, cwd_from=None):
|
def __init__(self, argv, cwd, opts, stdin=None, env=None, cwd_from=None, allow_remote_control=False):
|
||||||
self.allow_remote_control = False
|
self.allow_remote_control = allow_remote_control
|
||||||
if argv and argv[0] == '@':
|
|
||||||
self.allow_remote_control = True
|
|
||||||
if len(argv) > 1:
|
|
||||||
argv = argv[1:]
|
|
||||||
self.argv = argv
|
self.argv = argv
|
||||||
if cwd_from is not None:
|
if cwd_from is not None:
|
||||||
try:
|
try:
|
||||||
@@ -271,6 +267,13 @@ class Child:
|
|||||||
except Exception:
|
except Exception:
|
||||||
return list(self.argv)
|
return list(self.argv)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def foreground_cmdline(self):
|
||||||
|
try:
|
||||||
|
return cmdline_of_process(self.pid_for_cwd) or self.cmdline
|
||||||
|
except Exception:
|
||||||
|
return self.cmdline
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def environ(self):
|
def environ(self):
|
||||||
try:
|
try:
|
||||||
@@ -296,3 +299,14 @@ class Child:
|
|||||||
def foreground_cwd(self):
|
def foreground_cwd(self):
|
||||||
with suppress(Exception):
|
with suppress(Exception):
|
||||||
return cwd_of_process(self.pid_for_cwd) or None
|
return cwd_of_process(self.pid_for_cwd) or None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def foreground_environ(self):
|
||||||
|
try:
|
||||||
|
return environ_of_process(self.pid_for_cwd)
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
return environ_of_process(self.pid)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return {}
|
||||||
|
|||||||
@@ -50,7 +50,8 @@ func_with_args, args_funcs = key_func()
|
|||||||
|
|
||||||
@func_with_args(
|
@func_with_args(
|
||||||
'pass_selection_to_program', 'new_window', 'new_tab', 'new_os_window',
|
'pass_selection_to_program', 'new_window', 'new_tab', 'new_os_window',
|
||||||
'new_window_with_cwd', 'new_tab_with_cwd', 'new_os_window_with_cwd'
|
'new_window_with_cwd', 'new_tab_with_cwd', 'new_os_window_with_cwd',
|
||||||
|
'launch'
|
||||||
)
|
)
|
||||||
def shlex_parse(func, rest):
|
def shlex_parse(func, rest):
|
||||||
return func, to_cmdline(rest)
|
return func, to_cmdline(rest)
|
||||||
|
|||||||
140
kitty/launch.py
Normal file
140
kitty/launch.py
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPLv3 Copyright: 2019, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
|
||||||
|
from kitty.cli import parse_args
|
||||||
|
|
||||||
|
|
||||||
|
def options_spec():
|
||||||
|
if not hasattr(options_spec, 'ans'):
|
||||||
|
OPTIONS = '''
|
||||||
|
--window-title --title
|
||||||
|
The title to set for the new window. By default, title is controlled by the
|
||||||
|
child process.
|
||||||
|
|
||||||
|
|
||||||
|
--tab-title
|
||||||
|
The title for the new tab if launching in a new tab. By default, the title
|
||||||
|
of the actie window in the tab is used as the tab title.
|
||||||
|
|
||||||
|
|
||||||
|
--type
|
||||||
|
type=choices
|
||||||
|
default=window
|
||||||
|
choices=window,tab,os-window
|
||||||
|
Where to launch the child process, in a new kitty window in the current tab,
|
||||||
|
a new tab, or a new OS window.
|
||||||
|
|
||||||
|
|
||||||
|
--cwd
|
||||||
|
The working directory for the newly launched child. Use the special value
|
||||||
|
:code:`current` to use the working directory of the currently active window.
|
||||||
|
|
||||||
|
|
||||||
|
--env
|
||||||
|
type=list
|
||||||
|
Environment variables to set in the child process. Can be specified multiple
|
||||||
|
times to set different environment variables.
|
||||||
|
Syntax: :italic:`name=value`.
|
||||||
|
|
||||||
|
|
||||||
|
--copy-colors
|
||||||
|
type=bool-set
|
||||||
|
Set the colors of the newly created window to be the same as the colors in the
|
||||||
|
currently active window.
|
||||||
|
|
||||||
|
|
||||||
|
--copy-cmdline
|
||||||
|
type=bool-set
|
||||||
|
Ignore any specified command line and instead use the command line from the
|
||||||
|
currently active window.
|
||||||
|
|
||||||
|
|
||||||
|
--copy-env
|
||||||
|
type=bool-set
|
||||||
|
Copy the environment variables from the currently active window into the
|
||||||
|
newly launched child process.
|
||||||
|
|
||||||
|
|
||||||
|
--location
|
||||||
|
type=choices
|
||||||
|
default=last
|
||||||
|
choices=first,neighbor,last
|
||||||
|
Where to place the newly created window when it is added to a tab which
|
||||||
|
already has existing windows in it.
|
||||||
|
|
||||||
|
|
||||||
|
--allow-remote-control
|
||||||
|
type=bool-set
|
||||||
|
Programs running in this window can control kitty (if remote control is
|
||||||
|
enabled). Note that any program with the right level of permissions can still
|
||||||
|
write to the pipes of any other program on the same computer and therefore can
|
||||||
|
control kitty. It can, however, be useful to block programs running on other
|
||||||
|
computers (for example, over ssh) or as other users.
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
options_spec.ans = OPTIONS
|
||||||
|
return options_spec.ans
|
||||||
|
|
||||||
|
|
||||||
|
def parse_launch_args(args):
|
||||||
|
args = list(args or ())
|
||||||
|
try:
|
||||||
|
opts, args = parse_args(args=args, ospec=options_spec)
|
||||||
|
except SystemExit as e:
|
||||||
|
raise ValueError from e
|
||||||
|
return opts, args
|
||||||
|
|
||||||
|
|
||||||
|
def get_env(opts, active_child):
|
||||||
|
env = {}
|
||||||
|
if opts.copy_env and active_child:
|
||||||
|
env.update(active_child.foreground_environ)
|
||||||
|
for x in opts.env:
|
||||||
|
parts = x.split('=', 1)
|
||||||
|
if len(parts) == 2:
|
||||||
|
env[parts[0]] = parts[1]
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
|
def launch(boss, opts, args):
|
||||||
|
if opts.type == 'tab':
|
||||||
|
tm = boss.active_tab_manager
|
||||||
|
tab = tm.new_tab(empty_tab=True)
|
||||||
|
if opts.tab_title:
|
||||||
|
tab.set_title(opts.tab_title)
|
||||||
|
elif opts.type == 'os-window':
|
||||||
|
oswid = boss.add_os_window()
|
||||||
|
tm = boss.os_window_map[oswid]
|
||||||
|
tab = tm.new_tab(empty_tab=True)
|
||||||
|
if opts.tab_title:
|
||||||
|
tab.set_title(opts.tab_title)
|
||||||
|
else:
|
||||||
|
tab = boss.active_tab
|
||||||
|
active = boss.active_window_for_cwd
|
||||||
|
active_child = getattr(active, 'child', None)
|
||||||
|
kw = {
|
||||||
|
'env': get_env(opts, active_child) or None,
|
||||||
|
'allow_remote_control': opts.allow_remote_control
|
||||||
|
}
|
||||||
|
if opts.cwd:
|
||||||
|
if opts.cwd == 'current':
|
||||||
|
if active_child:
|
||||||
|
kw['cwd_from'] = active_child.pid_for_cwd
|
||||||
|
else:
|
||||||
|
kw['cwd'] = opts.cwd
|
||||||
|
if opts.location != 'last':
|
||||||
|
kw['location'] = opts.location
|
||||||
|
if opts.window_title:
|
||||||
|
kw['override_title'] = opts.window_title
|
||||||
|
if opts.copy_colors and active:
|
||||||
|
kw['copy_colors_from'] = active
|
||||||
|
cmd = args or None
|
||||||
|
if opts.copy_cmdline and active_child:
|
||||||
|
cmd = active_child.foreground_cmdline
|
||||||
|
if cmd:
|
||||||
|
kw['cmd'] = cmd
|
||||||
|
|
||||||
|
return tab.new_window(**kw)
|
||||||
@@ -236,7 +236,7 @@ class Tab: # {{{
|
|||||||
if self.current_layout.remove_all_biases():
|
if self.current_layout.remove_all_biases():
|
||||||
self.relayout()
|
self.relayout()
|
||||||
|
|
||||||
def launch_child(self, use_shell=False, cmd=None, stdin=None, cwd_from=None, cwd=None, env=None):
|
def launch_child(self, use_shell=False, cmd=None, stdin=None, cwd_from=None, cwd=None, env=None, allow_remote_control=False):
|
||||||
if cmd is None:
|
if cmd is None:
|
||||||
if use_shell:
|
if use_shell:
|
||||||
cmd = resolved_shell(self.opts)
|
cmd = resolved_shell(self.opts)
|
||||||
@@ -252,7 +252,7 @@ class Tab: # {{{
|
|||||||
except Exception:
|
except Exception:
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
ans = Child(cmd, cwd or self.cwd, self.opts, stdin, fenv, cwd_from)
|
ans = Child(cmd, cwd or self.cwd, self.opts, stdin, fenv, cwd_from, allow_remote_control=allow_remote_control)
|
||||||
ans.fork()
|
ans.fork()
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
@@ -263,9 +263,10 @@ class Tab: # {{{
|
|||||||
def new_window(
|
def new_window(
|
||||||
self, use_shell=True, cmd=None, stdin=None, override_title=None,
|
self, use_shell=True, cmd=None, stdin=None, override_title=None,
|
||||||
cwd_from=None, cwd=None, overlay_for=None, env=None, location=None,
|
cwd_from=None, cwd=None, overlay_for=None, env=None, location=None,
|
||||||
copy_colors_from=None
|
copy_colors_from=None, allow_remote_control=False
|
||||||
):
|
):
|
||||||
child = self.launch_child(use_shell=use_shell, cmd=cmd, stdin=stdin, cwd_from=cwd_from, cwd=cwd, env=env)
|
child = self.launch_child(
|
||||||
|
use_shell=use_shell, cmd=cmd, stdin=stdin, cwd_from=cwd_from, cwd=cwd, env=env, allow_remote_control=allow_remote_control)
|
||||||
window = Window(self, child, self.opts, self.args, override_title=override_title, copy_colors_from=copy_colors_from)
|
window = Window(self, child, self.opts, self.args, override_title=override_title, copy_colors_from=copy_colors_from)
|
||||||
if overlay_for is not None:
|
if overlay_for is not None:
|
||||||
overlaid = next(w for w in self.windows if w.id == overlay_for)
|
overlaid = next(w for w in self.windows if w.id == overlay_for)
|
||||||
@@ -276,8 +277,8 @@ class Tab: # {{{
|
|||||||
self._add_window(window, location=location)
|
self._add_window(window, location=location)
|
||||||
return window
|
return window
|
||||||
|
|
||||||
def new_special_window(self, special_window, location=None, copy_colors_from=None):
|
def new_special_window(self, special_window, location=None, copy_colors_from=None, allow_remote_control=False):
|
||||||
return self.new_window(False, *special_window, location=location, copy_colors_from=copy_colors_from)
|
return self.new_window(False, *special_window, location=location, copy_colors_from=copy_colors_from, allow_remote_control=allow_remote_control)
|
||||||
|
|
||||||
def close_window(self):
|
def close_window(self):
|
||||||
if self.windows:
|
if self.windows:
|
||||||
|
|||||||
Reference in New Issue
Block a user