From 1e6fe7785a1b699a21355eb8772f1ab3d23160cf Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 29 Mar 2021 11:56:48 +0530 Subject: [PATCH] Dont set the EDITOR env var in child processes It isnt really needed, since the various kittens dont rely on it anymore, instead calling get_editor() to get the path to the editor. Has the nice side-effect of not needing to run the shell at startup to read its environment. Now the shell is only run if the user calls the edit config file kitten. Fixes #3426 --- kitty/boss.py | 2 +- kitty/cli.py | 7 +++++++ kitty/main.py | 48 +++---------------------------------------- kitty/utils.py | 55 ++++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 62 insertions(+), 50 deletions(-) diff --git a/kitty/boss.py b/kitty/boss.py index 3c6c12daa..8a3bf2bde 100755 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -866,7 +866,7 @@ class Boss: confpath = prepare_config_file_for_editing() # On macOS vim fails to handle SIGWINCH if it occurs early, so add a # small delay. - cmd = [kitty_exe(), '+runpy', 'import os, sys, time; time.sleep(0.05); os.execvp(sys.argv[1], sys.argv[1:])'] + get_editor() + [confpath] + cmd = [kitty_exe(), '+runpy', 'import os, sys, time; time.sleep(0.05); os.execvp(sys.argv[1], sys.argv[1:])'] + get_editor(self.opts) + [confpath] self.new_os_window(*cmd) def get_output(self, source_window: Window, num_lines: Optional[int] = 1) -> str: diff --git a/kitty/cli.py b/kitty/cli.py index 11c672354..a44d1799e 100644 --- a/kitty/cli.py +++ b/kitty/cli.py @@ -852,3 +852,10 @@ def create_opts(args: CLIOptions, debug_config: bool = False, accumulate_bad_lin print('Running under:', green('Wayland' if is_wayland(opts) else 'X11')) compare_opts(opts) return opts + + +def create_default_opts() -> OptionsStub: + from .config import load_config + config = tuple(resolve_config(SYSTEM_CONF, defconf, ())) + opts = load_config(*config) + return opts diff --git a/kitty/main.py b/kitty/main.py index 0846e7d27..02a202513 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -7,7 +7,7 @@ import os import shutil import sys from contextlib import contextmanager, suppress -from typing import Dict, Generator, List, Mapping, Optional, Sequence +from typing import Dict, Generator, List, Optional, Sequence from .borders import load_borders_program from .boss import Boss @@ -32,8 +32,8 @@ from .os_window_size import initial_window_size_func from .session import get_os_window_sizing_data from .types import SingleKey from .utils import ( - detach, expandvars, find_exe, log_error, read_shell_environment, - single_instance, startup_notification_handler, unix_socket_paths + detach, expandvars, log_error, single_instance, + startup_notification_handler, unix_socket_paths ) from .window import load_shader_programs @@ -225,39 +225,6 @@ def macos_cmdline(argv_args: List[str]) -> List[str]: return ans -def resolve_editor_cmd(editor: str, shell_env: Mapping[str, str]) -> Optional[str]: - import shlex - editor_cmd = shlex.split(editor) - editor_exe = (editor_cmd or ('',))[0] - if editor_exe and os.path.isabs(editor_exe): - return editor - if not editor_exe: - return None - - def patched(exe: str) -> str: - editor_cmd[0] = exe - return ' '.join(map(shlex.quote, editor_cmd)) - - if shell_env is os.environ: - q = find_exe(editor_exe) - if q: - return patched(q) - elif 'PATH' in shell_env: - import shlex - q = shutil.which(editor_exe, path=shell_env['PATH']) - if q: - return patched(q) - - -def get_editor_from_env(shell_env: Mapping[str, str]) -> Optional[str]: - for var in ('VISUAL', 'EDITOR'): - editor = shell_env.get(var) - if editor: - editor = resolve_editor_cmd(editor, shell_env) - if editor: - return editor - - def expand_listen_on(listen_on: str, from_config_file: bool) -> str: listen_on = expandvars(listen_on) if '{kitty_pid}' not in listen_on and from_config_file: @@ -275,15 +242,6 @@ def expand_listen_on(listen_on: str, from_config_file: bool) -> str: def setup_environment(opts: OptionsStub, cli_opts: CLIOptions) -> None: - if opts.editor == '.': - editor = get_editor_from_env(os.environ) - if not editor: - shell_env = read_shell_environment(opts) - editor = get_editor_from_env(shell_env) - if editor: - os.environ['EDITOR'] = editor - else: - os.environ['EDITOR'] = opts.editor from_config_file = False if not cli_opts.listen_on and opts.listen_on.startswith('unix:'): cli_opts.listen_on = opts.listen_on diff --git a/kitty/utils.py b/kitty/utils.py index 4aeb21e69..a61976660 100644 --- a/kitty/utils.py +++ b/kitty/utils.py @@ -462,12 +462,49 @@ def natsort_ints(iterable: Iterable[str]) -> List[str]: return sorted(iterable, key=alphanum_key) -@run_once -def get_editor() -> List[str]: +def resolve_editor_cmd(editor: str, shell_env: Mapping[str, str]) -> Optional[str]: + import shlex + editor_cmd = shlex.split(editor) + editor_exe = (editor_cmd or ('',))[0] + if editor_exe and os.path.isabs(editor_exe): + return editor + if not editor_exe: + return None + + def patched(exe: str) -> str: + editor_cmd[0] = exe + return ' '.join(map(shlex.quote, editor_cmd)) + + if shell_env is os.environ: + q = find_exe(editor_exe) + if q: + return patched(q) + elif 'PATH' in shell_env: + import shutil + q = shutil.which(editor_exe, path=shell_env['PATH']) + if q: + return patched(q) + + +def get_editor_from_env(env: Mapping[str, str]) -> Optional[str]: + for var in ('VISUAL', 'EDITOR'): + editor = env.get(var) + if editor: + editor = resolve_editor_cmd(editor, env) + if editor: + return editor + + +def get_editor_from_env_vars(opts: Optional[Options] = None) -> List[str]: import shlex import shutil - for ans in (os.environ.get('VISUAL'), os.environ.get('EDITOR'), 'vim', - 'nvim', 'vi', 'emacs', 'kak', 'micro', 'nano', 'vis'): + + editor = get_editor_from_env(os.environ) + if not editor: + shell_env = read_shell_environment(opts) + editor = get_editor_from_env(shell_env) + + for ans in (editor, 'vim', 'nvim', 'vi', 'emacs', 'kak', 'micro', 'nano', 'vis'): if ans and shutil.which(shlex.split(ans)[0]): break else: @@ -475,6 +512,16 @@ def get_editor() -> List[str]: return shlex.split(ans) +def get_editor(opts: Optional[Options] = None) -> List[str]: + if opts is None: + from .cli import create_default_opts + opts = create_default_opts() + if opts.editor == '.': + return get_editor_from_env_vars() + import shlex + return shlex.split(opts.editor) + + def is_path_in_temp_dir(path: str) -> bool: if not path: return False