diff --git a/docs/sessions.rst b/docs/sessions.rst index d0719f99a..8b379c35a 100644 --- a/docs/sessions.rst +++ b/docs/sessions.rst @@ -159,6 +159,7 @@ all the major keywords you can use in kitty session files: launch tail -f /var/log/syslog # Focus the first tab (index 0) when the session loads + # You can also use a match expression like: focus_tab title:logs focus_tab 0 # Create a complex layout using multiple splits. Creates two columns of @@ -279,12 +280,14 @@ documentation for them. otherwise the window manager might block changing focus to prevent *focus stealing*. -``focus_tab [tab index]`` +``focus_tab [tab specifier]`` Set which tab should be active (focused) in the current OS Window. The tab - index is 0-based, so ``focus_tab 0`` will focus the first tab, ``focus_tab 1`` - the second tab, and so on. This is useful for session files that create - multiple tabs and want to ensure a specific tab is active when the session - is loaded. + specifier can be either a plain number (treated as a 0-based index) or a + match expression. For example, ``focus_tab 0`` will focus the first tab, + ``focus_tab 1`` the second tab, and ``focus_tab title:logs`` will focus the + tab whose title matches "logs". See :ref:`search_syntax` for the full syntax + of match expressions. This is useful for session files that create multiple + tabs and want to ensure a specific tab is active when the session is loaded. ``enabled_layouts comma separated list of layout names`` Set the layouts allowed in the current tab. Same syntax as diff --git a/kitty/session.py b/kitty/session.py index 0c4b32374..e6e175dd4 100644 --- a/kitty/session.py +++ b/kitty/session.py @@ -88,6 +88,7 @@ class Session: def __init__(self, default_title: str | None = None): self.tabs: list[Tab] = [] self.active_tab_idx = 0 + self.focus_tab_spec: str | None = None self.default_title = default_title self.os_window_size: WindowSizes | None = None self.os_window_class: str | None = None @@ -177,8 +178,8 @@ class Session: self.active_tab_idx = max(0, len(self.tabs) - 1) self.tabs[-1].active_window_idx = max(0, len(self.tabs[-1].windows) - 1) - def focus_tab(self, idx: int) -> None: - self.active_tab_idx = max(0, min(idx, len(self.tabs) - 1)) + def focus_tab(self, spec: str) -> None: + self.focus_tab_spec = spec def set_enabled_layouts(self, raw: str) -> None: self.tabs[-1].enabled_layouts = to_layout_names(raw) @@ -254,10 +255,7 @@ def parse_session( elif cmd == 'focus': ans.focus() elif cmd == 'focus_tab': - try: - ans.focus_tab(int(rest)) - except ValueError: - raise ValueError(f'focus_tab requires an integer tab index, got: {rest}') + ans.focus_tab(rest) elif cmd == 'focus_os_window': ans.focus_os_window = True elif cmd == 'enabled_layouts': diff --git a/kitty/tabs.py b/kitty/tabs.py index 011dd5871..d2eafa2f3 100644 --- a/kitty/tabs.py +++ b/kitty/tabs.py @@ -1064,6 +1064,24 @@ class TabManager: # {{{ self._add_tab(tab) if i == session.active_tab_idx: active_tab = tab + + # Handle focus_tab_spec if specified + if session.focus_tab_spec is not None: + spec = session.focus_tab_spec.strip() + # Try to parse as a plain number (index) + try: + idx = int(spec) + # Clamp to valid range + idx = max(0, min(idx, len(self.tabs) - 1)) + active_tab = self.tabs[idx] + except ValueError: + # Not a plain number, treat as match expression + from .fast_data_types import get_boss + boss = get_boss() + matched_tabs = list(boss.match_tabs(spec, self.tabs)) + if matched_tabs: + active_tab = matched_tabs[0] + if active_tab is not None: idx = self.tabs.index(active_tab) self._set_active_tab(idx)