Start work on a command to clone the current working env into a new kitty window

This commit is contained in:
Kovid Goyal 2022-04-13 15:02:22 +05:30
parent 7788f48dd5
commit c19e69855a
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 97 additions and 4 deletions

View File

@ -10,7 +10,7 @@ from .cli import parse_args
from .cli_stub import LaunchCLIOptions from .cli_stub import LaunchCLIOptions
from .constants import kitty_exe, shell_path from .constants import kitty_exe, shell_path
from .fast_data_types import ( from .fast_data_types import (
get_os_window_title, patch_color_profiles, set_clipboard_string get_boss, get_os_window_title, patch_color_profiles, set_clipboard_string
) )
from .options.utils import env as parse_env from .options.utils import env as parse_env
from .tabs import Tab, TabManager from .tabs import Tab, TabManager
@ -224,7 +224,7 @@ def parse_launch_args(args: Optional[Sequence[str]] = None) -> LaunchSpec:
return LaunchSpec(opts, args) return LaunchSpec(opts, args)
def get_env(opts: LaunchCLIOptions, active_child: Optional[Child]) -> Dict[str, str]: def get_env(opts: LaunchCLIOptions, active_child: Optional[Child] = None) -> Dict[str, str]:
env: Dict[str, str] = {} env: Dict[str, str] = {}
if opts.copy_env and active_child: if opts.copy_env and active_child:
env.update(active_child.foreground_environ) env.update(active_child.foreground_environ)
@ -342,7 +342,8 @@ def launch(
opts: LaunchCLIOptions, opts: LaunchCLIOptions,
args: List[str], args: List[str],
target_tab: Optional[Tab] = None, target_tab: Optional[Tab] = None,
force_target_tab: bool = False force_target_tab: bool = False,
base_env: Optional[Dict[str, str]] = None
) -> Optional[Window]: ) -> Optional[Window]:
active = boss.active_window_for_cwd active = boss.active_window_for_cwd
if active: if active:
@ -357,7 +358,11 @@ def launch(
if opts.os_window_title == 'current': if opts.os_window_title == 'current':
tm = boss.active_tab_manager tm = boss.active_tab_manager
opts.os_window_title = get_os_window_title(tm.os_window_id) if tm else None opts.os_window_title = get_os_window_title(tm.os_window_id) if tm else None
env = get_env(opts, active_child) if base_env:
env = base_env.copy()
env.update(get_env(opts))
else:
env = get_env(opts, active_child)
kw: LaunchKwds = { kw: LaunchKwds = {
'allow_remote_control': opts.allow_remote_control, 'allow_remote_control': opts.allow_remote_control,
'cwd_from': None, 'cwd_from': None,
@ -470,3 +475,55 @@ def launch(
new_window.set_logo(opts.logo, opts.logo_position or '', opts.logo_alpha) new_window.set_logo(opts.logo, opts.logo_position or '', opts.logo_alpha)
return new_window return new_window
return None return None
def parse_opts_for_clone(args: List[str]) -> LaunchCLIOptions:
unsafe, unsafe_args = parse_launch_args(args)
default_opts, default_args = parse_launch_args()
for x in (
'window_title', 'tab_title', 'type', 'keep_focus', 'cwd', 'env', 'hold',
'location', 'os_window_class', 'os_window_name', 'os_window_title',
'logo', 'logo_position', 'logo_alpha', 'color'
):
setattr(default_opts, x, getattr(unsafe, x))
return default_opts
def clone_and_launch(msg: str, window: Window) -> None:
import base64
from .child import cmdline_of_process
args = []
env: Dict[str, str] = {}
cwd = ''
pid = -1
for x in msg.split(','):
k, v = x.split('=', 1)
if k == 'pid':
pid = int(v)
continue
v = base64.standard_b64decode(v).decode('utf-8', 'replace')
if k == 'a':
args.append(v)
elif k == 'env':
for line in v.split('\0'):
if line:
try:
k, v = line.split('=', 1)
except ValueError:
continue
env[k] = v
elif k == 'cwd':
cwd = v.rstrip()
opts = parse_opts_for_clone(args)
if cwd:
opts.cwd = cwd
opts.copy_colors = True
try:
cmdline = cmdline_of_process(pid)
except Exception:
cmdline = []
if not cmdline:
cmdline = list(window.child.argv)
launch(get_boss(), opts, cmdline, base_env=env)

View File

@ -1086,6 +1086,7 @@ dispatch_dcs(Screen *screen, PyObject DUMP_UNUSED *dump_callback) {
} else IF_SIMPLE_PREFIX("echo|", handle_remote_echo) } else IF_SIMPLE_PREFIX("echo|", handle_remote_echo)
} else IF_SIMPLE_PREFIX("ssh|", handle_remote_ssh) } else IF_SIMPLE_PREFIX("ssh|", handle_remote_ssh)
} else IF_SIMPLE_PREFIX("ask|", handle_remote_askpass) } else IF_SIMPLE_PREFIX("ask|", handle_remote_askpass)
} else IF_SIMPLE_PREFIX("clone|", handle_remote_clone)
#undef IF_SIMPLE_PREFIX #undef IF_SIMPLE_PREFIX
} else { } else {
REPORT_ERROR("Unrecognized DCS @ code: 0x%x", screen->parser_buf[1]); REPORT_ERROR("Unrecognized DCS @ code: 0x%x", screen->parser_buf[1]);

View File

@ -473,6 +473,7 @@ class Window:
self.watchers = global_watchers().copy() self.watchers = global_watchers().copy()
self.last_focused_at = 0. self.last_focused_at = 0.
self.started_at = monotonic() self.started_at = monotonic()
self.current_clone_data = ''
self.current_mouse_event_button = 0 self.current_mouse_event_button = 0
self.current_clipboard_read_ask: Optional[bool] = None self.current_clipboard_read_ask: Optional[bool] = None
self.prev_osc99_cmd = NotificationCommand() self.prev_osc99_cmd = NotificationCommand()
@ -1009,6 +1010,19 @@ class Window:
if tab is not None: if tab is not None:
tab.move_window_to_top_of_group(self) tab.move_window_to_top_of_group(self)
def handle_remote_clone(self, msg: str) -> None:
if not msg:
if self.current_clone_data:
cdata, self.current_clone_data = self.current_clone_data, ''
from .launch import clone_and_launch
clone_and_launch(cdata, self)
self.current_clone_data = ''
return
num, rest = msg.split(':', 1)
if num == '0' or len(self.current_clone_data) > 1024 * 1024:
self.current_clone_data = ''
self.current_clone_data += msg
def handle_remote_askpass(self, msg: str) -> None: def handle_remote_askpass(self, msg: str) -> None:
from .shm import SharedMemory from .shm import SharedMemory
with SharedMemory(name=msg, readonly=True) as shm: with SharedMemory(name=msg, readonly=True) as shm:

View File

@ -354,3 +354,24 @@ _ksi_deferred_init() {
# to unfunction themselves when invoked. Unfunctioning is done by calling code. # to unfunction themselves when invoked. Unfunctioning is done by calling code.
builtin unfunction _ksi_deferred_init builtin unfunction _ksi_deferred_init
} }
clone-in-kitty() {
builtin local data="pid=$$,cwd=$(builtin pwd -P | builtin command base64),env=$(builtin command env -0 | builtin command base64)"
while :; do
case "$1" in
"") break;;
*) data="$data,a=$(builtin printf "%s" "$1" | builtin command base64)";;
esac
shift
done
data="${data//[[:space:]]}"
builtin local pos=0
builtin local chunk_num=0
while [ $pos -lt ${#data} ]; do
builtin local chunk="${data:$pos:2048}"
pos=$(($pos+2048))
builtin print -nu "$_ksi_fd" '\eP@kitty-clone|'"${chunk}:"'\e\\'
chunk_num=$(($chunk_num+1))
done
builtin print -nu "$_ksi_fd" '\eP@kitty-clone|\e\\'
}