diff --git a/kitty/child.py b/kitty/child.py index 2f5a8f6ac..1e10b1f8b 100644 --- a/kitty/child.py +++ b/kitty/child.py @@ -13,7 +13,7 @@ import kitty.fast_data_types as fast_data_types from .constants import handled_signals, is_freebsd, is_macos, kitten_exe, kitty_base_dir, shell_path, terminfo_dir from .types import run_once -from .utils import cmdline_for_hold, log_error, which +from .utils import cmdline_for_hold, log_error, resolved_shell, which if TYPE_CHECKING: from .window import CwdRequest @@ -229,7 +229,8 @@ class Child: hold: bool = False, pass_fds: tuple[int, ...] = (), remote_control_fd: int = -1, - hold_after_ssh: bool = False + hold_after_ssh: bool = False, + startup_command_via_shell_integration: Sequence[str] = (), ): self.is_clone_launch = is_clone_launch self.id = next(child_counter) @@ -247,12 +248,13 @@ class Child: self.cwd = os.path.abspath(cwd) self.stdin = stdin self.env = env or {} + self.startup_command_via_shell_integration = startup_command_via_shell_integration self.final_env:dict[str, str] = {} self.is_default_shell = bool(self.argv and self.argv[0] == shell_path) self.should_run_via_run_shell_kitten = is_macos and self.is_default_shell self.hold = hold - def get_final_env(self) -> dict[str, str]: + def get_final_env(self) -> tuple[dict[str, str], bool]: from kitty.options.utils import DELETE_ENV_VAR env = default_env().copy() opts = fast_data_types.get_options() @@ -294,7 +296,15 @@ class Child: self.is_clone_launch = '1' # free memory else: env.pop('KITTY_IS_CLONE_LAUNCH', None) - return env + must_run_startup_command_via_kitten = False + if self.startup_command_via_shell_integration: + from .shell_integration import join + scmd = self.argv or resolved_shell(fast_data_types.get_options()) + try: + env['KITTY_SI_RUN_COMMAND_AT_STARTUP'] = join(scmd[0], self.startup_command_via_shell_integration) + except Exception: + must_run_startup_command_via_kitten = True # unknown shell + return env, must_run_startup_command_via_kitten def fork(self) -> int | None: if self.forked: @@ -312,13 +322,13 @@ class Child: os.set_inheritable(stdin_read_fd, True) else: stdin_read_fd = stdin_write_fd = -1 - self.final_env = self.get_final_env() + self.final_env, must_run_startup_command_via_kitten = self.get_final_env() argv = list(self.argv) cwd = self.cwd pass_fds = self.pass_fds if self.remote_control_fd > -1: pass_fds += self.remote_control_fd, - if self.should_run_via_run_shell_kitten: + if self.should_run_via_run_shell_kitten or must_run_startup_command_via_kitten: # bash will only source ~/.bash_profile if it detects it is a login # shell (see the invocation section of the bash man page), which it # does if argv[0] is prefixed by a hyphen see @@ -338,6 +348,8 @@ class Child: if ksi == 'invalid': ksi = 'enabled' argv = [kitten_exe(), 'run-shell', '--shell', shlex.join(argv), '--shell-integration', ksi] + if must_run_startup_command_via_kitten: + argv.extend(self.startup_command_via_shell_integration) if is_macos and not pass_fds and not opts.forward_stdio: # In addition for getlogin() to work we need to run the shell # via the /usr/bin/login wrapper, sigh. diff --git a/kitty/launch.py b/kitty/launch.py index e925a3051..86daeb9b9 100644 --- a/kitty/launch.py +++ b/kitty/launch.py @@ -17,7 +17,7 @@ from .fast_data_types import add_timer, get_boss, get_options, get_os_window_tit from .options.utils import env as parse_env from .tabs import Tab, TabManager from .types import LayerShellConfig, OverlayType, run_once -from .utils import get_editor, log_error, resolve_custom_file, resolved_shell, which +from .utils import get_editor, log_error, resolve_custom_file, which from .window import CwdRequest, CwdRequestType, Watchers, Window @@ -776,19 +776,9 @@ def _launch( tab = tab_for_window(boss, opts, target_tab, next_to) watchers = load_watch_modules(opts.watcher) with Window.set_ignore_focus_changes_for_new_windows(opts.keep_focus): - startup_command_env_added = False - if startup_command_via_shell_integration: - from .shell_integration import join - try: - scmd = kw.get('cmd') or resolved_shell(get_options()) - env = env or {} - env['KITTY_SI_RUN_COMMAND_AT_STARTUP'] = join(scmd[0], startup_command_via_shell_integration) - startup_command_env_added = True - except Exception: - pass # shell is not a known shell - new_window: Window = tab.new_window( - env=env or None, watchers=watchers or None, is_clone_launch=is_clone_launch, next_to=next_to, **kw) + env=env or None, watchers=watchers or None, is_clone_launch=is_clone_launch, next_to=next_to, + startup_command_via_shell_integration=startup_command_via_shell_integration, **kw) if child_death_callback is not None: boss.monitor_pid(new_window.child.pid or 0, child_death_callback) if new_window.creation_spec: @@ -798,10 +788,6 @@ def _launch( new_window.creation_spec = new_window.creation_spec._replace(spacing=tuple(opts.spacing)) if opts.color: new_window.creation_spec = new_window.creation_spec._replace(colors=tuple(opts.color)) - if startup_command_env_added and new_window.creation_spec.env: - def is_not_scmd(x: tuple[str, str]) -> bool: - return x[0] != 'KITTY_SI_RUN_COMMAND_AT_STARTUP' - new_window.creation_spec = new_window.creation_spec._replace(env=tuple(filter(is_not_scmd, new_window.creation_spec.env))) if spacing: patch_window_edges(new_window, spacing) tab.relayout() diff --git a/kitty/tabs.py b/kitty/tabs.py index 9be92fe0e..55c385a14 100644 --- a/kitty/tabs.py +++ b/kitty/tabs.py @@ -534,7 +534,8 @@ class Tab: # {{{ hold: bool = False, pass_fds: tuple[int, ...] = (), remote_control_fd: int = -1, - hold_after_ssh: bool = False + hold_after_ssh: bool = False, + startup_command_via_shell_integration: Sequence[str] = (), ) -> Child: check_for_suitability = True if cmd is None: @@ -584,7 +585,9 @@ class Tab: # {{{ fenv['WINDOWID'] = str(pwid) ans = Child( cmd, cwd or self.cwd, stdin, fenv, cwd_from, is_clone_launch=is_clone_launch, - add_listen_on_env_var=add_listen_on_env_var, hold=hold, pass_fds=pass_fds, remote_control_fd=remote_control_fd, hold_after_ssh=hold_after_ssh) + add_listen_on_env_var=add_listen_on_env_var, hold=hold, pass_fds=pass_fds, + remote_control_fd=remote_control_fd, hold_after_ssh=hold_after_ssh, + startup_command_via_shell_integration=startup_command_via_shell_integration) ans.fork() return ans @@ -623,7 +626,8 @@ class Tab: # {{{ pass_fds: tuple[int, ...] = (), remote_control_fd: int = -1, next_to: Window | None = None, - hold_after_ssh: bool = False + hold_after_ssh: bool = False, + startup_command_via_shell_integration: Sequence[str] = (), ) -> Window: cs = WindowCreationSpec( use_shell=use_shell, cmd=cmd, has_stdin=bool(stdin), override_title=override_title, cwd_from=cwd_from, @@ -637,7 +641,8 @@ class Tab: # {{{ child = self.launch_child( use_shell=use_shell, cmd=cmd, stdin=stdin, cwd_from=cwd_from, cwd=cwd, env=env, is_clone_launch=is_clone_launch, add_listen_on_env_var=False if allow_remote_control and remote_control_passwords else True, - hold=hold, pass_fds=pass_fds, remote_control_fd=remote_control_fd, hold_after_ssh=hold_after_ssh + hold=hold, pass_fds=pass_fds, remote_control_fd=remote_control_fd, hold_after_ssh=hold_after_ssh, + startup_command_via_shell_integration=startup_command_via_shell_integration, ) window = Window( self, child, self.args, override_title=override_title,