From 47b4c94c618e24ae7063d84aa5cba8b3cf6d214f Mon Sep 17 00:00:00 2001 From: Jackie Li Date: Sun, 26 Oct 2025 11:08:43 +0000 Subject: [PATCH 1/3] implement --base-dir and --focus-tab to save_as_session --- kitty/session.py | 14 ++++++++++++++ kitty/tabs.py | 9 ++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/kitty/session.py b/kitty/session.py index e6e175dd4..e6eb3a898 100644 --- a/kitty/session.py +++ b/kitty/session.py @@ -627,6 +627,16 @@ If specified, only save all windows (and their parent tabs/OS Windows) that matc search expression. See :ref:`search_syntax` for details on the search language. In particular if you want to only save windows that are present in the currently active session, use :code:`--match=session:.`. + + +--base-dir +When specified, relative session filenames will be saved to this directory instead of the current +working directory. Absolute paths are not affected by this option. + + +--focus-tab +type=bool-set +When enabled, add a focus_tab command to the saved session file to preserve the currently active tab. ''' @@ -634,6 +644,10 @@ def save_as_session_part2(boss: BossType, opts: SaveAsSessionOptions, path: str) if not path: return from .config import atomic_save + # Handle --base-dir option + if opts.base_dir and not os.path.isabs(path): + base_dir = os.path.abspath(os.path.expanduser(opts.base_dir)) + path = os.path.join(base_dir, path) path = os.path.abspath(os.path.expanduser(path)) session = '\n'.join(boss.serialize_state_as_session(path, opts)) os.makedirs(os.path.dirname(path), exist_ok=True) diff --git a/kitty/tabs.py b/kitty/tabs.py index d2eafa2f3..1baf10a34 100644 --- a/kitty/tabs.py +++ b/kitty/tabs.py @@ -1317,7 +1317,10 @@ class TabManager: # {{{ hmap[at.id] = len(self.active_tab_history) + 1 def skey(tab: Tab) -> int: return hmap.get(tab.id, -1) - for tab in sorted(self, key=skey): + active_tab_index = -1 + for i, tab in enumerate(sorted(self, key=skey)): + if tab is self.active_tab: + active_tab_index = i ans.extend(tab.serialize_state_as_session(session_path, matched_windows, ser_opts)) if ans: prefix = [] if is_first else ['', '', 'new_os_window'] @@ -1326,6 +1329,10 @@ class TabManager: # {{{ if self.wm_name and self.wm_name != appname: prefix.append(f'os_window_name {self.wm_name}') ans = prefix + ans + # Add focus_tab command if --focus-tab option is enabled + if ser_opts.focus_tab and active_tab_index >= 0: + ans.append('') + ans.append(f'focus_tab {active_tab_index}') return ans @property From 2a129f2b3b80f2e1c8d26964822fbb6652057f5b Mon Sep 17 00:00:00 2001 From: Jackie Li Date: Sun, 26 Oct 2025 17:32:23 +0000 Subject: [PATCH 2/3] remove --focus-tab and add docs --- docs/changelog.rst | 4 ++++ docs/sessions.rst | 11 +++++++++++ kitty/session.py | 15 ++++++++------- kitty/tabs.py | 9 +-------- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 345d147b2..de1a302c2 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -189,6 +189,10 @@ Detailed list of changes or a match expression for flexible tab selection, allowing sessions to preserve the active tab state (:doc:`sessions`) +- :ac:`save_as_session`: Add ``--base-dir`` option to specify a base directory + for saving session files with relative paths, useful when the current working + directory is not the desired location (:doc:`sessions`) + 0.43.1 [2025-10-01] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/sessions.rst b/docs/sessions.rst index 8b379c35a..85379d0c1 100644 --- a/docs/sessions.rst +++ b/docs/sessions.rst @@ -102,6 +102,17 @@ for a path at which to save the session file. Specify the path and the session will be saved there with the exact setup you created. The saved file will even be opened in your editor for you to review, automatically. +.. tip:: + If you want session files to be saved to a specific directory regardless of + your current working directory, use the ``--base-dir`` option. For example:: + + map f7>s save_as_session --use-foreground-process --base-dir ~/.local/share/kitty/sessions + + This is particularly useful when kitty is launched from system-wide shortcuts + where the working directory might not be your home directory. Note that + ``--relocatable`` is typically not used with ``--base-dir``, since relocatable + is meant for session files that are co-located with their project directories. + If instead, you want to create these by hand, see the example below which shows all the major keywords you can use in kitty session files: diff --git a/kitty/session.py b/kitty/session.py index e6eb3a898..9f72e2e7e 100644 --- a/kitty/session.py +++ b/kitty/session.py @@ -631,12 +631,10 @@ use :code:`--match=session:.`. --base-dir When specified, relative session filenames will be saved to this directory instead of the current -working directory. Absolute paths are not affected by this option. - - ---focus-tab -type=bool-set -When enabled, add a focus_tab command to the saved session file to preserve the currently active tab. +working directory. This is useful when kitty is launched from locations where the working directory +is not your home directory, such as from system-wide shortcuts. Note that :code:`--relocatable` is +typically not used with :code:`--base-dir`, since relocatable is meant for session files that are +co-located with their project directories. ''' @@ -649,7 +647,10 @@ def save_as_session_part2(boss: BossType, opts: SaveAsSessionOptions, path: str) base_dir = os.path.abspath(os.path.expanduser(opts.base_dir)) path = os.path.join(base_dir, path) path = os.path.abspath(os.path.expanduser(path)) - session = '\n'.join(boss.serialize_state_as_session(path, opts)) + # When --base-dir is specified, use it as the session_path for relocatable calculation + # This makes relative paths relative to base_dir instead of the session file location + session_path_for_serialize = os.path.join(opts.base_dir, 'dummy') if opts.base_dir else path + session = '\n'.join(boss.serialize_state_as_session(session_path_for_serialize, opts)) os.makedirs(os.path.dirname(path), exist_ok=True) atomic_save(session.encode(), path) if not opts.save_only: diff --git a/kitty/tabs.py b/kitty/tabs.py index 1baf10a34..d2eafa2f3 100644 --- a/kitty/tabs.py +++ b/kitty/tabs.py @@ -1317,10 +1317,7 @@ class TabManager: # {{{ hmap[at.id] = len(self.active_tab_history) + 1 def skey(tab: Tab) -> int: return hmap.get(tab.id, -1) - active_tab_index = -1 - for i, tab in enumerate(sorted(self, key=skey)): - if tab is self.active_tab: - active_tab_index = i + for tab in sorted(self, key=skey): ans.extend(tab.serialize_state_as_session(session_path, matched_windows, ser_opts)) if ans: prefix = [] if is_first else ['', '', 'new_os_window'] @@ -1329,10 +1326,6 @@ class TabManager: # {{{ if self.wm_name and self.wm_name != appname: prefix.append(f'os_window_name {self.wm_name}') ans = prefix + ans - # Add focus_tab command if --focus-tab option is enabled - if ser_opts.focus_tab and active_tab_index >= 0: - ans.append('') - ans.append(f'focus_tab {active_tab_index}') return ans @property From eedd56ff930974fb8aa68b4554ce2c4280659698 Mon Sep 17 00:00:00 2001 From: Jackie Li Date: Sun, 26 Oct 2025 17:47:58 +0000 Subject: [PATCH 3/3] cleanup + keep it simple --- kitty/session.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/kitty/session.py b/kitty/session.py index 9f72e2e7e..4922f352c 100644 --- a/kitty/session.py +++ b/kitty/session.py @@ -642,15 +642,11 @@ def save_as_session_part2(boss: BossType, opts: SaveAsSessionOptions, path: str) if not path: return from .config import atomic_save - # Handle --base-dir option if opts.base_dir and not os.path.isabs(path): base_dir = os.path.abspath(os.path.expanduser(opts.base_dir)) path = os.path.join(base_dir, path) path = os.path.abspath(os.path.expanduser(path)) - # When --base-dir is specified, use it as the session_path for relocatable calculation - # This makes relative paths relative to base_dir instead of the session file location - session_path_for_serialize = os.path.join(opts.base_dir, 'dummy') if opts.base_dir else path - session = '\n'.join(boss.serialize_state_as_session(session_path_for_serialize, opts)) + session = '\n'.join(boss.serialize_state_as_session(path, opts)) os.makedirs(os.path.dirname(path), exist_ok=True) atomic_save(session.encode(), path) if not opts.save_only: