Option to make saved session files relocatable

This commit is contained in:
Kovid Goyal 2025-08-17 11:56:32 +05:30
parent f8c3a721f7
commit c2389a1adc
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
3 changed files with 19 additions and 7 deletions

View file

@ -501,13 +501,13 @@ def list_os_windows(
'background_opacity': bo,
}
def serialize_state_as_session(self, ser_opts: SaveAsSessionOptions | None = None) -> Iterator[str]:
def serialize_state_as_session(self, session_path: str = '', ser_opts: SaveAsSessionOptions | None = None) -> Iterator[str]:
if ser_opts is None:
ser_opts = default_save_as_session_opts()
s = {current_focused_os_window_id(): 2, last_focused_os_window_id(): 1}
for i, os_window_id in enumerate(sorted(self.os_window_map, key=lambda wid: s.get(wid, 0))):
tm = self.os_window_map[os_window_id]
yield from tm.serialize_state_as_session(is_first=i==0, ser_opts=ser_opts)
yield from tm.serialize_state_as_session(session_path, is_first=i==0, ser_opts=ser_opts)
@property
def all_tab_managers(self) -> Iterator[TabManager]:

View file

@ -492,6 +492,13 @@ def save_as_session_options() -> str:
running some dangerous command like :file:`rm` or :file:`mv` or similar in a shell, it will be re-run when
the session is executed if you use this option. Note that this option requires :ref:`shell_integration`
to work.
--relocatable
type=bool-set
When saving the working directory for windows, do so as paths relative to the directory in which
the session file will be saved. This allows the session file to work even when its containing
directory is moved elsewhere.
'''
@ -500,7 +507,7 @@ def save_as_session_part2(boss: BossType, opts: SaveAsSessionOptions, path: str)
return
from .config import atomic_save
path = os.path.abspath(os.path.expanduser(path))
session = '\n'.join(boss.serialize_state_as_session(opts))
session = '\n'.join(boss.serialize_state_as_session(path, opts))
atomic_save(session.encode(), path)
if not opts.save_only:
boss.edit_file(path)

View file

@ -295,12 +295,17 @@ def serialize_state(self) -> dict[str, Any]:
'name': self.name,
}
def serialize_state_as_session(self, ser_opts: SaveAsSessionOptions) -> list[str]:
def serialize_state_as_session(self, session_path: str, ser_opts: SaveAsSessionOptions) -> list[str]:
import shlex
launch_cmds = []
active_idx = self.windows.active_group_idx
groups = tuple(self.windows.iter_all_layoutable_groups())
cwds = {w.id: w.cwd_for_serialization for g in groups for w in g}
session_base_dir = os.path.dirname(session_path) if session_path else ''
def make_relative(cwd: str) -> str:
if session_base_dir and ser_opts.relocatable:
cwd = os.path.relpath(cwd, session_base_dir)
return cwd
cwds = {w.id: make_relative(w.cwd_for_serialization) for g in groups for w in g}
from collections import Counter
most_common_cwd, _ = Counter(cwds.values()).most_common(1)[0]
for i, g in enumerate(groups):
@ -1214,7 +1219,7 @@ def serialize_state(self) -> dict[str, Any]:
'active_tab_idx': self.active_tab_idx,
}
def serialize_state_as_session(self, ser_opts: SaveAsSessionOptions, is_first: bool = False) -> list[str]:
def serialize_state_as_session(self, session_path: str, ser_opts: SaveAsSessionOptions, is_first: bool = False) -> list[str]:
ans = []
hmap = {tab_id: i for i, tab_id in enumerate(self.active_tab_history)}
if (at := self.active_tab) is not None:
@ -1222,7 +1227,7 @@ def serialize_state_as_session(self, ser_opts: SaveAsSessionOptions, is_first: b
def skey(tab: Tab) -> int:
return hmap.get(tab.id, -1)
for tab in sorted(self, key=skey):
ans.extend(tab.serialize_state_as_session(ser_opts))
ans.extend(tab.serialize_state_as_session(session_path, ser_opts))
if ans:
prefix = [] if is_first else ['', '', 'new_os_window']
if self.wm_class and self.wm_class != appname: