Allow using the full launch command in session files
Note this is slightly backward incompatible.
This commit is contained in:
parent
e9e8ef7210
commit
35517d3e6f
@ -11,6 +11,11 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
|
||||
used by full screen terminal programs and even games, see
|
||||
:doc:`keyboard-protocol` (:iss:`3248`)
|
||||
|
||||
- **Backward incompatibility**: Session files now use the full :doc:`launch <launch>`
|
||||
command with all its capabilities. However, the syntax of the command is
|
||||
slightly different from before. In particular watchers are now specified
|
||||
directly on launch and env vars are set using ``--env``.
|
||||
|
||||
- diff kitten: Implement recursive diff over SSH (:iss:`3268`)
|
||||
|
||||
- ssh kitten: Allow using python instead of the shell on the server, useful if
|
||||
|
||||
@ -340,7 +340,7 @@ For example:
|
||||
launch zsh
|
||||
# Create a window with some environment variables set and run
|
||||
# vim in it
|
||||
launch env FOO=BAR vim
|
||||
launch --env FOO=BAR vim
|
||||
# Set the title for the next window
|
||||
title Chat with x
|
||||
launch irssi --profile x
|
||||
@ -367,14 +367,9 @@ For example:
|
||||
focus
|
||||
launch emacs
|
||||
|
||||
# Add a watcher that will be called with various events that occur
|
||||
# on all subsequent windows. See the documentation of the launch command
|
||||
# for details on watchers.
|
||||
watcher /some/python/file.py
|
||||
launch mpd
|
||||
launch irssi
|
||||
# Remove the watcher for further windows
|
||||
watcher clear
|
||||
.. note::
|
||||
The :doc:`launch <launch>` command when used in a session file
|
||||
cannot create new OS windows, or tabs.
|
||||
|
||||
|
||||
Mouse features
|
||||
|
||||
@ -177,12 +177,8 @@ class Boss:
|
||||
cocoa_set_notification_activated_callback(notification_activated)
|
||||
|
||||
def startup_first_child(self, os_window_id: Optional[int]) -> None:
|
||||
from kitty.launch import load_watch_modules
|
||||
startup_sessions = create_sessions(self.opts, self.args, default_session=self.opts.startup_session)
|
||||
watchers = load_watch_modules(self.args.watcher)
|
||||
for startup_session in startup_sessions:
|
||||
if watchers is not None and watchers.has_watchers:
|
||||
startup_session.add_watchers_to_all_windows(watchers)
|
||||
self.add_os_window(startup_session, os_window_id=os_window_id)
|
||||
os_window_id = None
|
||||
if self.args.start_as != 'normal':
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
|
||||
from functools import lru_cache
|
||||
from typing import Any, Dict, List, Optional, Sequence, Tuple
|
||||
from typing import Any, Dict, List, NamedTuple, Optional, Sequence
|
||||
|
||||
from .boss import Boss
|
||||
from .child import Child
|
||||
@ -13,7 +13,7 @@ from .cli_stub import LaunchCLIOptions
|
||||
from .constants import resolve_custom_file
|
||||
from .fast_data_types import set_clipboard_string
|
||||
from .tabs import Tab
|
||||
from .utils import find_exe, set_primary_selection, read_shell_environment
|
||||
from .utils import find_exe, read_shell_environment, set_primary_selection
|
||||
from .window import Watchers, Window
|
||||
|
||||
try:
|
||||
@ -22,6 +22,11 @@ except ImportError:
|
||||
TypedDict = Dict[str, Any]
|
||||
|
||||
|
||||
class LaunchSpec(NamedTuple):
|
||||
opts: LaunchCLIOptions
|
||||
args: List[str]
|
||||
|
||||
|
||||
@lru_cache(maxsize=2)
|
||||
def options_spec() -> str:
|
||||
return '''
|
||||
@ -159,13 +164,13 @@ Set the WM_NAME property on X11 for the newly created OS Window when using
|
||||
''' + WATCHER_DEFINITION
|
||||
|
||||
|
||||
def parse_launch_args(args: Optional[Sequence[str]] = None) -> Tuple[LaunchCLIOptions, List[str]]:
|
||||
def parse_launch_args(args: Optional[Sequence[str]] = None) -> LaunchSpec:
|
||||
args = list(args or ())
|
||||
try:
|
||||
opts, args = parse_args(result_class=LaunchCLIOptions, args=args, ospec=options_spec)
|
||||
except SystemExit as e:
|
||||
raise ValueError from e
|
||||
return opts, args
|
||||
return LaunchSpec(opts, args)
|
||||
|
||||
|
||||
def get_env(opts: LaunchCLIOptions, active_child: Child) -> Dict[str, str]:
|
||||
@ -234,7 +239,13 @@ class LaunchKwds(TypedDict):
|
||||
stdin: Optional[bytes]
|
||||
|
||||
|
||||
def launch(boss: Boss, opts: LaunchCLIOptions, args: List[str], target_tab: Optional[Tab] = None) -> Optional[Window]:
|
||||
def launch(
|
||||
boss: Boss,
|
||||
opts: LaunchCLIOptions,
|
||||
args: List[str],
|
||||
target_tab: Optional[Tab] = None,
|
||||
force_target_tab: bool = False
|
||||
) -> Optional[Window]:
|
||||
active = boss.active_window_for_cwd
|
||||
active_child = getattr(active, 'child', None)
|
||||
env = get_env(opts, active_child)
|
||||
@ -310,6 +321,9 @@ def launch(boss: Boss, opts: LaunchCLIOptions, args: List[str], target_tab: Opti
|
||||
set_clipboard_string(stdin)
|
||||
else:
|
||||
set_primary_selection(stdin)
|
||||
else:
|
||||
if force_target_tab:
|
||||
tab = target_tab
|
||||
else:
|
||||
tab = tab_for_window(boss, opts, target_tab)
|
||||
if tab is not None:
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
import shlex
|
||||
import sys
|
||||
from typing import Generator, List, Optional, Union
|
||||
from typing import Generator, List, Optional, Sequence, Union
|
||||
|
||||
from .cli_stub import CLIOptions
|
||||
from .config_data import to_layout_names
|
||||
@ -14,7 +14,6 @@ from .options_stub import Options
|
||||
from .os_window_size import WindowSize, WindowSizeData, WindowSizes
|
||||
from .typing import SpecialWindowInstance
|
||||
from .utils import log_error, resolved_shell
|
||||
from .window import Watchers
|
||||
|
||||
|
||||
def get_os_window_sizing_data(opts: Options, session: Optional['Session'] = None) -> WindowSizeData:
|
||||
@ -27,44 +26,31 @@ def get_os_window_sizing_data(opts: Options, session: Optional['Session'] = None
|
||||
|
||||
class Tab:
|
||||
|
||||
def __init__(self, opts: Options, name: str, watchers: Watchers):
|
||||
self.windows: List['SpecialWindowInstance'] = []
|
||||
def __init__(self, opts: Options, name: str):
|
||||
from .launch import LaunchSpec
|
||||
self.windows: List[Union[LaunchSpec, 'SpecialWindowInstance']] = []
|
||||
self.name = name.strip()
|
||||
self.active_window_idx = 0
|
||||
self.enabled_layouts = opts.enabled_layouts
|
||||
self.layout = (self.enabled_layouts or ['tall'])[0]
|
||||
self.cwd: Optional[str] = None
|
||||
self.next_title: Optional[str] = None
|
||||
self.watchers: Watchers = watchers.copy()
|
||||
|
||||
|
||||
class Session:
|
||||
|
||||
def __init__(self, default_title: Optional[str] = None):
|
||||
def __init__(self, default_title: Optional[str] = None, default_watchers: Sequence[str] = ()):
|
||||
self.tabs: List[Tab] = []
|
||||
self.default_watchers = list(default_watchers)
|
||||
self.active_tab_idx = 0
|
||||
self.default_title = default_title
|
||||
self.os_window_size: Optional[WindowSizes] = None
|
||||
self.os_window_class: Optional[str] = None
|
||||
self.watchers = Watchers()
|
||||
|
||||
def add_watchers_to_all_windows(self, watchers: Watchers) -> None:
|
||||
def add(w: 'SpecialWindowInstance') -> 'SpecialWindowInstance':
|
||||
if w.watchers is None:
|
||||
return w._replace(watchers=watchers)
|
||||
wt = w.watchers.copy()
|
||||
wt.add(watchers)
|
||||
return w._replace(watchers=wt)
|
||||
|
||||
for tab in self.tabs:
|
||||
tab.windows = [add(w) for w in tab.windows]
|
||||
|
||||
def add_tab(self, opts: Options, name: str = '') -> None:
|
||||
if self.tabs and not self.tabs[-1].windows:
|
||||
del self.tabs[-1]
|
||||
if self.tabs:
|
||||
self.tabs[-1].watchers = self.watchers.copy()
|
||||
self.tabs.append(Tab(opts, name, self.watchers))
|
||||
self.tabs.append(Tab(opts, name))
|
||||
|
||||
def set_next_title(self, title: str) -> None:
|
||||
self.tabs[-1].next_title = title.strip()
|
||||
@ -75,22 +61,18 @@ class Session:
|
||||
self.tabs[-1].layout = val
|
||||
|
||||
def add_window(self, cmd: Union[None, str, List[str]]) -> None:
|
||||
if cmd:
|
||||
cmd = shlex.split(cmd) if isinstance(cmd, str) else cmd
|
||||
else:
|
||||
cmd = None
|
||||
from .tabs import SpecialWindow
|
||||
from .launch import parse_launch_args
|
||||
if isinstance(cmd, str):
|
||||
cmd = shlex.split(cmd)
|
||||
spec = parse_launch_args(cmd)
|
||||
if self.default_watchers:
|
||||
spec.opts.watcher = list(spec.opts.watcher) + self.default_watchers
|
||||
t = self.tabs[-1]
|
||||
watchers: Optional[Watchers] = None
|
||||
if self.watchers.has_watchers:
|
||||
watchers = self.watchers.copy()
|
||||
sw = SpecialWindow(cmd, cwd=t.cwd, override_title=t.next_title or self.default_title, watchers=watchers)
|
||||
t.windows.append(sw)
|
||||
spec.opts.cwd = spec.opts.cwd or t.cwd
|
||||
t.windows.append(spec)
|
||||
t.next_title = None
|
||||
|
||||
def add_special_window(self, sw: 'SpecialWindowInstance') -> None:
|
||||
if self.watchers.has_watchers:
|
||||
sw = sw._replace(watchers=self.watchers.copy())
|
||||
self.tabs[-1].windows.append(sw)
|
||||
|
||||
def focus(self) -> None:
|
||||
@ -112,10 +94,7 @@ def parse_session(raw: str, opts: Options, default_title: Optional[str] = None)
|
||||
from .tabs import SpecialWindow
|
||||
for t in ans.tabs:
|
||||
if not t.windows:
|
||||
w: Optional[Watchers] = None
|
||||
if t.watchers.has_watchers:
|
||||
w = t.watchers.copy()
|
||||
t.windows.append(SpecialWindow(cmd=resolved_shell(opts), watchers=w, override_title=default_title))
|
||||
t.windows.append(SpecialWindow(cmd=resolved_shell(opts), override_title=default_title))
|
||||
return ans
|
||||
|
||||
ans = Session(default_title)
|
||||
@ -133,9 +112,7 @@ def parse_session(raw: str, opts: Options, default_title: Optional[str] = None)
|
||||
ans.add_tab(opts, rest)
|
||||
elif cmd == 'new_os_window':
|
||||
yield finalize_session(ans)
|
||||
wt = ans.watchers
|
||||
ans = Session(default_title)
|
||||
ans.watchers = wt.copy()
|
||||
ans.add_tab(opts, rest)
|
||||
elif cmd == 'layout':
|
||||
ans.set_layout(rest)
|
||||
@ -155,14 +132,6 @@ def parse_session(raw: str, opts: Options, default_title: Optional[str] = None)
|
||||
ans.os_window_size = WindowSizes(WindowSize(*w), WindowSize(*h))
|
||||
elif cmd == 'os_window_class':
|
||||
ans.os_window_class = rest
|
||||
elif cmd == 'watcher':
|
||||
from .launch import load_watch_modules
|
||||
if rest == 'clear':
|
||||
ans.watchers = Watchers()
|
||||
else:
|
||||
watchers = load_watch_modules((rest,))
|
||||
if watchers is not None:
|
||||
ans.watchers.add(watchers)
|
||||
else:
|
||||
raise ValueError('Unknown command in session file: {}'.format(cmd))
|
||||
yield finalize_session(ans)
|
||||
@ -194,7 +163,8 @@ def create_sessions(
|
||||
else:
|
||||
yield from parse_session(session_data, opts, getattr(args, 'title', None))
|
||||
return
|
||||
ans = Session()
|
||||
default_watchers = args.watcher if args else ()
|
||||
ans = Session(default_watchers=default_watchers)
|
||||
current_layout = opts.enabled_layouts[0] if opts.enabled_layouts else 'tall'
|
||||
ans.add_tab(opts)
|
||||
ans.tabs[-1].layout = current_layout
|
||||
|
||||
@ -137,7 +137,11 @@ class Tab: # {{{
|
||||
|
||||
def startup(self, session_tab: 'SessionTab') -> None:
|
||||
for cmd in session_tab.windows:
|
||||
if isinstance(cmd, SpecialWindowInstance):
|
||||
self.new_special_window(cmd)
|
||||
else:
|
||||
from .launch import launch
|
||||
launch(get_boss(), cmd.opts, cmd.args, target_tab=self, force_target_tab=True)
|
||||
self.windows.set_active_window_group_for(self.windows.all_windows[session_tab.active_window_idx])
|
||||
|
||||
def serialize_state(self) -> Dict[str, Any]:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user