diff --git a/kitty/complete.py b/kitty/complete.py index d64d4a68f..10a114540 100644 --- a/kitty/complete.py +++ b/kitty/complete.py @@ -505,6 +505,16 @@ def complete_remote_command(ans: Completions, cmd_name: str, words: Sequence[str complete_alias_map(ans, words, new_word, alias_map, complete_args=args_completer) +def complete_launch_wrapper(ans: Completions, words: Sequence[str], new_word: bool, allow_files: bool = True) -> None: + from kitty.launch import clone_safe_opts + aliases, alias_map = options_for_cmd('launch') + alias_map = {k: v for k, v in alias_map.items() if v['dest'] in clone_safe_opts()} + args_completer: Optional[CompleteArgsFunc] = None + if allow_files: + args_completer = remote_files_completer('Files', ('*',)) + complete_alias_map(ans, words, new_word, alias_map, complete_args=args_completer) + + def path_completion(prefix: str = '') -> Tuple[List[str], List[str]]: prefix = prefix.replace(r'\ ', ' ') dirs, files = [], [] @@ -664,7 +674,12 @@ def complete_kitten(ans: Completions, kitten: str, words: Sequence[str], new_wor def find_completions(words: Sequence[str], new_word: bool, entry_points: Iterable[str], namespaced_entry_points: Iterable[str]) -> Completions: ans = Completions() - if not words or words[0] != 'kitty': + if not words: + return ans + if words[0] in ('edit-in-kitty', 'clone-in-kitty'): + complete_launch_wrapper(ans, words[1:], new_word, allow_files=words[0] != 'clone-in-kitty') + return ans + if words[0] != 'kitty': return ans words = words[1:] if not words or (len(words) == 1 and not new_word): diff --git a/kitty/launch.py b/kitty/launch.py index 59c5aa315..8d85dc4e5 100644 --- a/kitty/launch.py +++ b/kitty/launch.py @@ -6,8 +6,8 @@ import os import shutil from contextlib import suppress from typing import ( - Any, Container, Dict, Iterable, Iterator, List, NamedTuple, Optional, - Sequence, Tuple + Any, Container, Dict, FrozenSet, Iterable, Iterator, List, NamedTuple, + Optional, Sequence, Tuple ) from .boss import Boss @@ -498,15 +498,20 @@ def launch( return None +@run_once +def clone_safe_opts() -> FrozenSet[str]: + return frozenset(( + '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' + )) + + def parse_opts_for_clone(args: List[str]) -> Tuple[LaunchCLIOptions, List[str]]: unsafe, unsafe_args = parse_launch_args(args) default_opts, default_args = parse_launch_args() # only copy safe options, those that dont lead to local code exec - 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' - ): + for x in clone_safe_opts(): setattr(default_opts, x, getattr(unsafe, x)) return default_opts, unsafe_args diff --git a/shell-integration/zsh/kitty-integration b/shell-integration/zsh/kitty-integration index 77985aef3..ba85ca150 100644 --- a/shell-integration/zsh/kitty-integration +++ b/shell-integration/zsh/kitty-integration @@ -106,6 +106,8 @@ _ksi_deferred_init() { builtin unset "functions[_kitty]" builtin autoload -Uz -- $comp_dir/_kitty compdef _kitty kitty + compdef _kitty edit-in-kitty + compdef _kitty clone-in-kitty fi # If compdef is not set, compinit has not run yet. In this case we must