Use an env var for fish shell integration

Less intrusive (in some ways) than adding symlinks to ~/.config/fish
Exploits the fact that fish loads scripts from XDG_DATA_DIRS on startup.
Thanks to @page-down for noticing
This commit is contained in:
Kovid Goyal 2021-10-27 14:59:11 +05:30
parent d31935b8eb
commit d3a3f99848
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
5 changed files with 42 additions and 40 deletions

View File

@ -17,6 +17,7 @@ from .cli import (
OptionDict, OptionSpecSeq, options_for_completion, parse_option_spec, OptionDict, OptionSpecSeq, options_for_completion, parse_option_spec,
prettify prettify
) )
from .constants import shell_integration_dir
from .fast_data_types import truncate_point_for_length, wcswidth from .fast_data_types import truncate_point_for_length, wcswidth
from .rc.base import all_command_names, command_for_name from .rc.base import all_command_names, command_for_name
from .shell import options_for_cmd from .shell import options_for_cmd
@ -128,6 +129,11 @@ def remote_control_command_names() -> Tuple[str, ...]:
# Shell specific code {{{ # Shell specific code {{{
def load_fish2_completion() -> str:
with open(os.path.join(shell_integration_dir, 'fish', 'vendor_completions.d', 'kitty.fish')) as f:
return f.read()
completion_scripts = { completion_scripts = {
'zsh': '''#compdef kitty 'zsh': '''#compdef kitty
@ -140,7 +146,7 @@ _kitty() {
fi fi
} }
compdef _kitty kitty compdef _kitty kitty
''', '''.__str__,
'bash': ''' 'bash': '''
_kitty_completions() { _kitty_completions() {
local src local src
@ -154,7 +160,7 @@ _kitty_completions() {
} }
complete -o nospace -F _kitty_completions kitty complete -o nospace -F _kitty_completions kitty
''', '''.__str__,
'fish': ''' 'fish': '''
function __kitty_completions function __kitty_completions
# Send all words up to the one before the cursor # Send all words up to the one before the cursor
@ -162,15 +168,8 @@ function __kitty_completions
end end
complete -f -c kitty -a "(__kitty_completions)" complete -f -c kitty -a "(__kitty_completions)"
''', '''.__str__,
'fish2': ''' 'fish2': load_fish2_completion,
if functions -q _ksi_completions
complete -f -c kitty -a "(_ksi_completions)"
else
complete -f -c kitty -a "(commandline -cop | kitty +complete fish)"
end
''',
} }
ParseResult = Tuple[List[str], bool] ParseResult = Tuple[List[str], bool]
@ -670,7 +669,7 @@ def find_completions(words: Sequence[str], new_word: bool, entry_points: Iterabl
def setup(cstyle: str) -> None: def setup(cstyle: str) -> None:
print(completion_scripts[cstyle]) print(completion_scripts[cstyle]())
def main(args: Sequence[str], entry_points: Iterable[str], namespaced_entry_points: Iterable[str]) -> None: def main(args: Sequence[str], entry_points: Iterable[str], namespaced_entry_points: Iterable[str]) -> None:

View File

@ -29,6 +29,7 @@ from .fonts.render import set_font_family
from .options.types import Options from .options.types import Options
from .os_window_size import initial_window_size_func from .os_window_size import initial_window_size_func
from .session import get_os_window_sizing_data from .session import get_os_window_sizing_data
from .shell_integration import setup_shell_integration
from .types import SingleKey from .types import SingleKey
from .utils import ( from .utils import (
detach, expandvars, log_error, single_instance, detach, expandvars, log_error, single_instance,
@ -150,9 +151,6 @@ def _run_app(opts: Options, args: CLIOptions, bad_lines: Sequence[BadLine] = ())
if not is_wayland() and not is_macos: # no window icons on wayland if not is_wayland() and not is_macos: # no window icons on wayland
set_x11_window_icon() set_x11_window_icon()
load_shader_programs.use_selection_fg = opts.selection_foreground is not None load_shader_programs.use_selection_fg = opts.selection_foreground is not None
if opts.shell_integration != 'disabled':
from .shell_integration import setup_shell_integration
setup_shell_integration()
with cached_values_for(run_app.cached_values_name) as cached_values: with cached_values_for(run_app.cached_values_name) as cached_values:
with startup_notification_handler(extra_callback=run_app.first_window_callback) as pre_show_callback: with startup_notification_handler(extra_callback=run_app.first_window_callback) as pre_show_callback:
window_id = create_os_window( window_id = create_os_window(
@ -274,7 +272,9 @@ def setup_environment(opts: Options, cli_opts: CLIOptions) -> None:
if cli_opts.listen_on and opts.allow_remote_control != 'n': if cli_opts.listen_on and opts.allow_remote_control != 'n':
cli_opts.listen_on = expand_listen_on(cli_opts.listen_on, from_config_file) cli_opts.listen_on = expand_listen_on(cli_opts.listen_on, from_config_file)
os.environ['KITTY_LISTEN_ON'] = cli_opts.listen_on os.environ['KITTY_LISTEN_ON'] = cli_opts.listen_on
set_default_env(opts.env.copy()) env = opts.env.copy()
setup_shell_integration(opts, env)
set_default_env(env)
def set_locale() -> None: def set_locale() -> None:

View File

@ -5,12 +5,11 @@
import os import os
import time import time
from contextlib import suppress from contextlib import suppress
from typing import Optional, Union from typing import Optional, Union, Dict
from .options.types import Options
from .config import atomic_save from .config import atomic_save
from .constants import shell_integration_dir from .constants import shell_integration_dir
from .fast_data_types import get_options
from .types import run_once
from .utils import log_error, resolved_shell from .utils import log_error, resolved_shell
posix_template = ''' posix_template = '''
@ -50,14 +49,14 @@ def setup_integration(shell_name: str, rc_path: str, template: str = posix_templ
atomic_write(rc_path, newrc) atomic_write(rc_path, newrc)
def setup_zsh_integration() -> None: def setup_zsh_integration(env: Dict[str, str]) -> None:
base = os.environ.get('ZDOTDIR', os.path.expanduser('~')) base = os.environ.get('ZDOTDIR', os.path.expanduser('~'))
rc = os.path.join(base, '.zshrc') rc = os.path.join(base, '.zshrc')
if os.path.exists(rc): # dont prevent zsh-newuser-install from running if os.path.exists(rc): # dont prevent zsh-newuser-install from running
setup_integration('zsh', rc) setup_integration('zsh', rc)
def setup_bash_integration() -> None: def setup_bash_integration(env: Dict[str, str]) -> None:
setup_integration('bash', os.path.expanduser('~/.bashrc')) setup_integration('bash', os.path.expanduser('~/.bashrc'))
@ -73,17 +72,16 @@ def atomic_symlink(destination: str, in_directory: str) -> None:
raise raise
def setup_fish_integration() -> None: def setup_fish_integration(env: Dict[str, str]) -> None:
base = os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')) if 'XDG_DATA_DIRS' in env:
base = os.path.join(base, 'fish') val = env.get('XDG_DATA_DIRS', '')
os.makedirs(os.path.join(base, 'completions'), exist_ok=True) dirs = list(filter(None, val.split(os.pathsep)))
path = os.path.join(shell_integration_dir, 'kitty.fish') else:
atomic_symlink(path, os.path.join(base, 'conf.d')) val = os.environ.get('XDG_DATA_DIRS', '')
from .complete import completion_scripts dirs = list(filter(None, val.split(os.pathsep)))
path = os.path.join(base, 'completions', 'kitty.fish') if shell_integration_dir not in dirs:
rc = safe_read(path) dirs.insert(0, shell_integration_dir)
if rc != completion_scripts['fish2']: env['XDG_DATA_DIRS'] = os.pathsep.join(dirs)
atomic_write(path, completion_scripts['fish2'])
SUPPORTED_SHELLS = { SUPPORTED_SHELLS = {
@ -100,19 +98,19 @@ def get_supported_shell_name(path: str) -> Optional[str]:
return None return None
@run_once def setup_shell_integration(opts: Options, env: Dict[str, str]) -> bool:
def setup_shell_integration() -> None: q = set(opts.shell_integration.split())
opts = get_options() if q & {'disabled', 'no-rc'}:
q = opts.shell_integration.split() return False
if opts.shell_integration == 'disabled' or 'no-rc' in q:
return
shell = get_supported_shell_name(resolved_shell(opts)[0]) shell = get_supported_shell_name(resolved_shell(opts)[0])
if shell is None: if shell is None:
return return False
func = SUPPORTED_SHELLS[shell] func = SUPPORTED_SHELLS[shell]
try: try:
func() func(env)
except Exception: except Exception:
import traceback import traceback
traceback.print_exc() traceback.print_exc()
log_error(f'Failed to setup shell integration for: {shell}') log_error(f'Failed to setup shell integration for: {shell}')
return False
return True

View File

@ -0,0 +1,5 @@
if functions -q _ksi_completions
complete -f -c kitty -a "(_ksi_completions)"
else
complete -f -c kitty -a "(commandline -cop | kitty +complete fish)"
end