From ddb121b418be663f291c125e0e2a19f32fb83bc5 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 16 Oct 2023 22:32:51 +0530 Subject: [PATCH] Remote control launch: Fix the --copy-env option not copying current environment variables Fixes #6724 --- docs/changelog.rst | 2 ++ gen/go_code.py | 13 +++++++++---- kitty/launch.py | 12 +++++++++--- kitty/rc/launch.py | 21 +++++++++++++++++---- tools/cmd/at/launch.go | 26 ++++++++++++++++++++++++++ 5 files changed, 63 insertions(+), 11 deletions(-) create mode 100644 tools/cmd/at/launch.go diff --git a/docs/changelog.rst b/docs/changelog.rst index 7cab0f1a2..5fa562c9b 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -62,6 +62,8 @@ Detailed list of changes - Fix trailing bracket not ignored when detecting a multi-line URL with the trailing bracket as the first character on the last line (:iss:`6710`) +- Remote control launch: Fix the ``--copy-env`` option not copying current environment variables (:iss:`6724`) + 0.30.1 [2023-10-05] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/gen/go_code.py b/gen/go_code.py index 4ad77e09a..30ea21522 100755 --- a/gen/go_code.py +++ b/gen/go_code.py @@ -306,6 +306,7 @@ json_field_types: Dict[str, str] = { def go_field_type(json_field_type: str) -> str: + json_field_type = json_field_type.partition('=')[0] q = json_field_types.get(json_field_type) if q: return q @@ -324,6 +325,7 @@ class JSONField: field_def = line.split(':', 1)[0] self.required = False self.field, self.field_type = field_def.split('/', 1) + self.field_type, self.special_parser = self.field_type.partition('=')[::2] if self.field.endswith('+'): self.required = True self.field = self.field[:-1] @@ -370,14 +372,17 @@ def go_code_for_remote_command(name: str, cmd: RemoteCommand, template: str) -> if oq in option_map: o = option_map[oq] used_options.add(oq) + optstring = f'options_{name}.{o.go_var_name}' + if field.special_parser: + optstring = f'{field.special_parser}({optstring})' if field.field_type == 'str': - jc.append(f'payload.{field.struct_field_name} = escaped_string(options_{name}.{o.go_var_name})') + jc.append(f'payload.{field.struct_field_name} = escaped_string({optstring})') elif field.field_type == 'list.str': - jc.append(f'payload.{field.struct_field_name} = escape_list_of_strings(options_{name}.{o.go_var_name})') + jc.append(f'payload.{field.struct_field_name} = escape_list_of_strings({optstring})') elif field.field_type == 'dict.str': - jc.append(f'payload.{field.struct_field_name} = escape_dict_of_strings(options_{name}.{o.go_var_name})') + jc.append(f'payload.{field.struct_field_name} = escape_dict_of_strings({optstring})') else: - jc.append(f'payload.{field.struct_field_name} = options_{name}.{o.go_var_name}') + jc.append(f'payload.{field.struct_field_name} = {optstring}') elif field.field in handled_fields: pass else: diff --git a/kitty/launch.py b/kitty/launch.py index 5e0ba365b..c94049a69 100644 --- a/kitty/launch.py +++ b/kitty/launch.py @@ -96,6 +96,8 @@ by the shell (needs :ref:`shell_integration` to work). The special value oldest foreground process associated with the currently active window rather than the newest foreground process. Finally, the special value :code:`root` refers to the process that was originally started when the window was created. +When running via remote control a value of current uses the working directory +for the remote control invocation. --env @@ -322,10 +324,12 @@ def parse_launch_args(args: Optional[Sequence[str]] = None) -> LaunchSpec: return LaunchSpec(opts, args) -def get_env(opts: LaunchCLIOptions, active_child: Optional[Child] = None) -> Dict[str, str]: +def get_env(opts: LaunchCLIOptions, active_child: Optional[Child] = None, base_env: Optional[Dict[str,str]] = None) -> Dict[str, str]: env: Dict[str, str] = {} if opts.copy_env and active_child: env.update(active_child.foreground_environ) + if base_env is not None: + env.update(base_env) for x in opts.env: for k, v in parse_env(x, env): env[k] = v @@ -456,6 +460,7 @@ def _launch( active: Optional[Window] = None, is_clone_launch: str = '', rc_from_window: Optional[Window] = None, + base_env: Optional[Dict[str, str]] = None, ) -> Optional[Window]: active = active or boss.active_window_for_cwd if active: @@ -470,7 +475,7 @@ def _launch( if opts.os_window_title == 'current': tm = boss.active_tab_manager opts.os_window_title = get_os_window_title(tm.os_window_id) if tm else None - env = get_env(opts, active_child) + env = get_env(opts, active_child, base_env) remote_control_restrictions: Optional[Dict[str, Sequence[str]]] = None if opts.allow_remote_control and opts.remote_control_password: from kitty.options.utils import remote_control_password @@ -633,12 +638,13 @@ def launch( active: Optional[Window] = None, is_clone_launch: str = '', rc_from_window: Optional[Window] = None, + base_env: Optional[Dict[str, str]] = None, ) -> Optional[Window]: active = active or boss.active_window_for_cwd if opts.keep_focus and active: orig, active.ignore_focus_changes = active.ignore_focus_changes, True try: - return _launch(boss, opts, args, target_tab, force_target_tab, active, is_clone_launch, rc_from_window) + return _launch(boss, opts, args, target_tab, force_target_tab, active, is_clone_launch, rc_from_window, base_env) finally: if opts.keep_focus and active: active.ignore_focus_changes = orig diff --git a/kitty/rc/launch.py b/kitty/rc/launch.py index 2d5f6d795..9176e1462 100644 --- a/kitty/rc/launch.py +++ b/kitty/rc/launch.py @@ -2,7 +2,7 @@ # License: GPLv3 Copyright: 2020, Kovid Goyal -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING, Dict, Optional from kitty.cli_stub import LaunchCLIOptions from kitty.launch import launch as do_launch @@ -21,7 +21,7 @@ class Launch(RemoteCommand): args+/list.str: The command line to run in the new window, as a list, use an empty list to run the default shell match/str: The tab to open the new window in window_title/str: Title for the new window - cwd/str: Working directory for the new window + cwd/str=copy_local_cwd: Working directory for the new window env/list.str: List of environment variables of the form NAME=VALUE var/list.str: List of user variables of the form NAME=VALUE tab_title/str: Title for the new tab @@ -29,7 +29,7 @@ class Launch(RemoteCommand): keep_focus/bool: Boolean indicating whether the current window should retain focus or not copy_colors/bool: Boolean indicating whether to copy the colors from the current window copy_cmdline/bool: Boolean indicating whether to copy the cmdline from the current window - copy_env/bool: Boolean indicating whether to copy the environ from the current window + copy_env/list.str=copy_local_env: List of strings representing the local env vars hold/bool: Boolean indicating whether to keep window open after cmd exits location/choices.first.after.before.neighbor.last.vsplit.hsplit.split.default: Where in the tab to open the new window allow_remote_control/bool: Boolean indicating whether to allow remote control from the new window @@ -83,17 +83,30 @@ instead of the active tab default_opts = parse_launch_args()[0] opts = LaunchCLIOptions() for key, default_value in default_opts.__dict__.items(): + if key == 'copy_env': + continue val = payload_get(key) if val is None: val = default_value setattr(opts, key, val) + ceval = payload_get('copy_env') + opts.copy_env = False + base_env: Optional[Dict[str, str]] = None + if ceval: + if isinstance(ceval, list): + base_env = {} + for x in ceval: + k, v = x.partition('=')[::2] + base_env[k] = v + elif isinstance(ceval, bool): + opts.copy_env = ceval target_tab = None tabs = self.tabs_for_match_payload(boss, window, payload_get) if tabs and tabs[0]: target_tab = tabs[0] elif payload_get('type') not in ('background', 'os-window', 'tab', 'window'): return None - w = do_launch(boss, opts, payload_get('args') or [], target_tab=target_tab, rc_from_window=window) + w = do_launch(boss, opts, payload_get('args') or [], target_tab=target_tab, rc_from_window=window, base_env=base_env) return None if payload_get('no_response') else str(getattr(w, 'id', 0)) diff --git a/tools/cmd/at/launch.go b/tools/cmd/at/launch.go new file mode 100644 index 000000000..8ec4442dc --- /dev/null +++ b/tools/cmd/at/launch.go @@ -0,0 +1,26 @@ +// License: GPLv3 Copyright: 2023, Kovid Goyal, + +package at + +import ( + "fmt" + "os" +) + +var _ = fmt.Print + +func copy_local_env(copy_env bool) []string { + if copy_env { + return os.Environ() + } + return nil +} + +func copy_local_cwd(copy_cwd string) string { + if copy_cwd == "current" { + if c, e := os.Getwd(); e == nil { + copy_cwd = c + } + } + return copy_cwd +}