Session files: Expand environment variables
This commit is contained in:
parent
11f98592f7
commit
60791bb57b
@ -57,6 +57,8 @@ Detailed list of changes
|
|||||||
|
|
||||||
- Allow using the cwd of the original process for :option:`launch --cwd` (:iss:`5672`)
|
- Allow using the cwd of the original process for :option:`launch --cwd` (:iss:`5672`)
|
||||||
|
|
||||||
|
- Session files: Expand environment variables (:disc:`5917`)
|
||||||
|
|
||||||
- Pass key events mapped to scroll actions to the program running in the terminal when the terminal is in alternate screen mode (:iss:`5839`)
|
- Pass key events mapped to scroll actions to the program running in the terminal when the terminal is in alternate screen mode (:iss:`5839`)
|
||||||
|
|
||||||
- Implement :ref:`edit-in-kitty <edit_file>` using the new ``kitten`` static executable (:iss:`5546`, :iss:`5630`)
|
- Implement :ref:`edit-in-kitty <edit_file>` using the new ``kitten`` static executable (:iss:`5546`, :iss:`5630`)
|
||||||
|
|||||||
@ -173,6 +173,11 @@ option in :file:`kitty.conf`. An example, showing all available commands:
|
|||||||
The :doc:`launch <launch>` command when used in a session file cannot create
|
The :doc:`launch <launch>` command when used in a session file cannot create
|
||||||
new OS windows, or tabs.
|
new OS windows, or tabs.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Environment variables of the for :code:`${NAME}` or :code:`$NAME` are
|
||||||
|
expanded in the session file, except in the *arguments* (not options) to the
|
||||||
|
launch command.
|
||||||
|
|
||||||
|
|
||||||
Creating tabs/windows
|
Creating tabs/windows
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|||||||
@ -760,9 +760,11 @@ class Boss:
|
|||||||
return None
|
return None
|
||||||
args.args = rest
|
args.args = rest
|
||||||
opts = create_opts(args)
|
opts = create_opts(args)
|
||||||
if args.session == '-':
|
if data['session_data']:
|
||||||
from .session import PreReadSession
|
from .session import PreReadSession
|
||||||
args.session = PreReadSession(data['stdin'])
|
args.session = PreReadSession(data['session_data'], data['environ'])
|
||||||
|
else:
|
||||||
|
args.session = ''
|
||||||
if not os.path.isabs(args.directory):
|
if not os.path.isabs(args.directory):
|
||||||
args.directory = os.path.join(data['cwd'], args.directory)
|
args.directory = os.path.join(data['cwd'], args.directory)
|
||||||
focused_os_window = os_window_id = 0
|
focused_os_window = os_window_id = 0
|
||||||
|
|||||||
@ -890,8 +890,8 @@ Detach from the controlling terminal, if any.
|
|||||||
completion=type:file ext:session relative:conf group:"Session files"
|
completion=type:file ext:session relative:conf group:"Session files"
|
||||||
Path to a file containing the startup :italic:`session` (tabs, windows, layout,
|
Path to a file containing the startup :italic:`session` (tabs, windows, layout,
|
||||||
programs). Use - to read from STDIN. See the :file:`README` file for details and
|
programs). Use - to read from STDIN. See the :file:`README` file for details and
|
||||||
an example. Environment variables are expanded, relative paths are resolved relative
|
an example. Environment variables in the file name are expanded,
|
||||||
to the kitty configuration directory.
|
relative paths are resolved relative to the kitty configuration directory.
|
||||||
|
|
||||||
|
|
||||||
--hold
|
--hold
|
||||||
|
|||||||
@ -80,12 +80,16 @@ def set_custom_ibeam_cursor() -> None:
|
|||||||
def talk_to_instance(args: CLIOptions) -> None:
|
def talk_to_instance(args: CLIOptions) -> None:
|
||||||
import json
|
import json
|
||||||
import socket
|
import socket
|
||||||
stdin = ''
|
session_data = ''
|
||||||
if args.session == '-':
|
if args.session == '-':
|
||||||
stdin = sys.stdin.read()
|
session_data = sys.stdin.read()
|
||||||
|
elif args.session:
|
||||||
|
with open(args.session) as f:
|
||||||
|
session_data = f.read()
|
||||||
|
|
||||||
data = {'cmd': 'new_instance', 'args': tuple(sys.argv), 'cmdline_args_for_open': getattr(sys, 'cmdline_args_for_open', []),
|
data = {'cmd': 'new_instance', 'args': tuple(sys.argv), 'cmdline_args_for_open': getattr(sys, 'cmdline_args_for_open', []),
|
||||||
'startup_id': os.environ.get('DESKTOP_STARTUP_ID'), 'activation_token': os.environ.get('XDG_ACTIVATION_TOKEN'),
|
'startup_id': os.environ.get('DESKTOP_STARTUP_ID'), 'activation_token': os.environ.get('XDG_ACTIVATION_TOKEN'),
|
||||||
'cwd': os.getcwd(), 'stdin': stdin}
|
'cwd': os.getcwd(), 'session_data': session_data, 'environ': dict(os.environ)}
|
||||||
notify_socket = None
|
notify_socket = None
|
||||||
if args.wait_for_single_instance_window_close:
|
if args.wait_for_single_instance_window_close:
|
||||||
address = f'\0{appname}-os-window-close-notify-{os.getpid()}-{os.geteuid()}'
|
address = f'\0{appname}-os-window-close-notify-{os.getpid()}-{os.geteuid()}'
|
||||||
@ -461,7 +465,7 @@ def _main() -> None:
|
|||||||
if cli_opts.detach:
|
if cli_opts.detach:
|
||||||
if cli_opts.session == '-':
|
if cli_opts.session == '-':
|
||||||
from .session import PreReadSession
|
from .session import PreReadSession
|
||||||
cli_opts.session = PreReadSession(sys.stdin.read())
|
cli_opts.session = PreReadSession(sys.stdin.read(), os.environ)
|
||||||
detach()
|
detach()
|
||||||
if cli_opts.replay_commands:
|
if cli_opts.replay_commands:
|
||||||
from kitty.client import main as client_main
|
from kitty.client import main as client_main
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import os
|
||||||
import shlex
|
import shlex
|
||||||
import sys
|
import sys
|
||||||
from typing import TYPE_CHECKING, Generator, Iterator, List, Optional, Tuple, Union
|
from contextlib import suppress
|
||||||
|
from functools import partial
|
||||||
|
from typing import TYPE_CHECKING, Callable, Generator, Iterator, List, Mapping, Optional, Tuple, Union
|
||||||
|
|
||||||
from .cli_stub import CLIOptions
|
from .cli_stub import CLIOptions
|
||||||
from .constants import kitten_exe
|
from .constants import kitten_exe
|
||||||
@ -12,7 +15,7 @@ from .options.types import Options
|
|||||||
from .options.utils import resize_window, to_layout_names, window_size
|
from .options.utils import resize_window, to_layout_names, window_size
|
||||||
from .os_window_size import WindowSize, WindowSizeData, WindowSizes
|
from .os_window_size import WindowSize, WindowSizeData, WindowSizes
|
||||||
from .typing import SpecialWindowInstance
|
from .typing import SpecialWindowInstance
|
||||||
from .utils import log_error, resolve_custom_file, resolved_shell
|
from .utils import expandvars, log_error, resolve_custom_file, resolved_shell
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .launch import LaunchSpec
|
from .launch import LaunchSpec
|
||||||
@ -73,11 +76,22 @@ class Session:
|
|||||||
raise ValueError(f'{val} is not a valid layout')
|
raise ValueError(f'{val} is not a valid layout')
|
||||||
self.tabs[-1].layout = val
|
self.tabs[-1].layout = val
|
||||||
|
|
||||||
def add_window(self, cmd: Union[None, str, List[str]]) -> None:
|
def add_window(self, cmd: Union[None, str, List[str]], expand: Callable[[str], str] = lambda x: x) -> None:
|
||||||
from .launch import parse_launch_args
|
from .launch import parse_launch_args
|
||||||
|
needs_expandvars = False
|
||||||
if isinstance(cmd, str):
|
if isinstance(cmd, str):
|
||||||
|
needs_expandvars = True
|
||||||
cmd = shlex.split(cmd)
|
cmd = shlex.split(cmd)
|
||||||
spec = parse_launch_args(cmd)
|
spec = parse_launch_args(cmd)
|
||||||
|
if needs_expandvars:
|
||||||
|
assert isinstance(cmd, list)
|
||||||
|
limit = len(cmd)
|
||||||
|
if len(spec.args):
|
||||||
|
with suppress(ValueError):
|
||||||
|
limit = cmd.index(spec.args[0])
|
||||||
|
cmd = [(expand(x) if i < limit else x) for i, x in enumerate(cmd)]
|
||||||
|
spec = parse_launch_args(cmd)
|
||||||
|
|
||||||
t = self.tabs[-1]
|
t = self.tabs[-1]
|
||||||
if t.next_title and not spec.opts.window_title:
|
if t.next_title and not spec.opts.window_title:
|
||||||
spec.opts.window_title = t.next_title
|
spec.opts.window_title = t.next_title
|
||||||
@ -113,7 +127,7 @@ class Session:
|
|||||||
self.tabs[-1].cwd = val
|
self.tabs[-1].cwd = val
|
||||||
|
|
||||||
|
|
||||||
def parse_session(raw: str, opts: Options) -> Generator[Session, None, None]:
|
def parse_session(raw: str, opts: Options, environ: Optional[Mapping[str, str]] = None) -> Generator[Session, None, None]:
|
||||||
|
|
||||||
def finalize_session(ans: Session) -> Session:
|
def finalize_session(ans: Session) -> Session:
|
||||||
from .tabs import SpecialWindow
|
from .tabs import SpecialWindow
|
||||||
@ -122,6 +136,9 @@ def parse_session(raw: str, opts: Options) -> Generator[Session, None, None]:
|
|||||||
t.windows.append(WindowSpec(SpecialWindow(cmd=resolved_shell(opts))))
|
t.windows.append(WindowSpec(SpecialWindow(cmd=resolved_shell(opts))))
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
if environ is None:
|
||||||
|
environ = os.environ
|
||||||
|
expand = partial(expandvars, env=environ, fallback_to_os_env=False)
|
||||||
ans = Session()
|
ans = Session()
|
||||||
ans.add_tab(opts)
|
ans.add_tab(opts)
|
||||||
for line in raw.splitlines():
|
for line in raw.splitlines():
|
||||||
@ -133,6 +150,8 @@ def parse_session(raw: str, opts: Options) -> Generator[Session, None, None]:
|
|||||||
else:
|
else:
|
||||||
cmd, rest = parts
|
cmd, rest = parts
|
||||||
cmd, rest = cmd.strip(), rest.strip()
|
cmd, rest = cmd.strip(), rest.strip()
|
||||||
|
if cmd != 'launch':
|
||||||
|
rest = expand(rest)
|
||||||
if cmd == 'new_tab':
|
if cmd == 'new_tab':
|
||||||
ans.add_tab(opts, rest)
|
ans.add_tab(opts, rest)
|
||||||
elif cmd == 'new_os_window':
|
elif cmd == 'new_os_window':
|
||||||
@ -142,7 +161,7 @@ def parse_session(raw: str, opts: Options) -> Generator[Session, None, None]:
|
|||||||
elif cmd == 'layout':
|
elif cmd == 'layout':
|
||||||
ans.set_layout(rest)
|
ans.set_layout(rest)
|
||||||
elif cmd == 'launch':
|
elif cmd == 'launch':
|
||||||
ans.add_window(rest)
|
ans.add_window(rest, expand)
|
||||||
elif cmd == 'focus':
|
elif cmd == 'focus':
|
||||||
ans.focus()
|
ans.focus()
|
||||||
elif cmd == 'focus_os_window':
|
elif cmd == 'focus_os_window':
|
||||||
@ -167,9 +186,10 @@ def parse_session(raw: str, opts: Options) -> Generator[Session, None, None]:
|
|||||||
|
|
||||||
class PreReadSession(str):
|
class PreReadSession(str):
|
||||||
|
|
||||||
def __new__(cls, val: str) -> 'PreReadSession':
|
def __new__(cls, val: str, associated_environ: Mapping[str, str]) -> 'PreReadSession':
|
||||||
ans: PreReadSession = str.__new__(cls, val)
|
ans: PreReadSession = str.__new__(cls, val)
|
||||||
ans.pre_read = True # type: ignore
|
ans.pre_read = True # type: ignore
|
||||||
|
ans.associated_environ = associated_environ # type: ignore
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
@ -179,11 +199,13 @@ def create_sessions(
|
|||||||
special_window: Optional['SpecialWindowInstance'] = None,
|
special_window: Optional['SpecialWindowInstance'] = None,
|
||||||
cwd_from: Optional['CwdRequest'] = None,
|
cwd_from: Optional['CwdRequest'] = None,
|
||||||
respect_cwd: bool = False,
|
respect_cwd: bool = False,
|
||||||
default_session: Optional[str] = None
|
default_session: Optional[str] = None,
|
||||||
) -> Iterator[Session]:
|
) -> Iterator[Session]:
|
||||||
if args and args.session:
|
if args and args.session:
|
||||||
|
environ: Optional[Mapping[str, str]] = None
|
||||||
if isinstance(args.session, PreReadSession):
|
if isinstance(args.session, PreReadSession):
|
||||||
session_data = '' + str(args.session)
|
session_data = '' + str(args.session)
|
||||||
|
environ = args.session.associated_environ # type: ignore
|
||||||
else:
|
else:
|
||||||
if args.session == '-':
|
if args.session == '-':
|
||||||
f = sys.stdin
|
f = sys.stdin
|
||||||
@ -191,7 +213,7 @@ def create_sessions(
|
|||||||
f = open(resolve_custom_file(args.session))
|
f = open(resolve_custom_file(args.session))
|
||||||
with f:
|
with f:
|
||||||
session_data = f.read()
|
session_data = f.read()
|
||||||
yield from parse_session(session_data, opts)
|
yield from parse_session(session_data, opts, environ=environ)
|
||||||
return
|
return
|
||||||
if default_session and default_session != 'none':
|
if default_session and default_session != 'none':
|
||||||
try:
|
try:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user