mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-10-20 20:22:10 +00:00
Make the effective configuration file for the kitty process available in the cache directory
This can be parsed by kittens to load effective settings, thereby making things like --override and reloading of config also affect kittens that read kitty config. Still to be implemented on the kitten side.
This commit is contained in:
parent
b0c9262652
commit
76ebc591db
7 changed files with 73 additions and 30 deletions
|
|
@ -37,7 +37,7 @@ from .clipboard import (
|
|||
)
|
||||
from .colors import ColorSchemes, theme_colors
|
||||
from .conf.utils import BadLine, KeyAction, to_cmdline
|
||||
from .config import common_opts_as_dict, prepare_config_file_for_editing
|
||||
from .config import common_opts_as_dict, prepare_config_file_for_editing, store_effective_config
|
||||
from .constants import (
|
||||
RC_ENCRYPTION_PROTOCOL_VERSION,
|
||||
appname,
|
||||
|
|
@ -398,6 +398,7 @@ class Boss:
|
|||
set_boss(self)
|
||||
self.mappings: Mappings = Mappings(global_shortcuts, self.refresh_active_tab_bar)
|
||||
self.notification_manager: NotificationManager = NotificationManager(debug=self.args.debug_keyboard or self.args.debug_rendering)
|
||||
self.atexit.unlink(store_effective_config())
|
||||
|
||||
def startup_first_child(self, os_window_id: Optional[int], startup_sessions: Iterable[Session] = ()) -> None:
|
||||
si = startup_sessions or create_sessions(get_options(), self.args, default_session=get_options().startup_session)
|
||||
|
|
@ -2730,6 +2731,7 @@ class Boss:
|
|||
clear_caches()
|
||||
from .guess_mime_type import clear_mime_cache
|
||||
clear_mime_cache()
|
||||
store_effective_config()
|
||||
|
||||
def safe_delete_temp_file(self, path: str) -> None:
|
||||
if is_path_in_temp_dir(path):
|
||||
|
|
|
|||
|
|
@ -442,15 +442,20 @@ def generate_c_conversion(loc: str, ctypes: List[Union[Option, MultiOption]]) ->
|
|||
|
||||
def write_output(loc: str, defn: Definition, extra_after_type_defn: str = '') -> None:
|
||||
cls, tc = generate_class(defn, loc)
|
||||
with open(os.path.join(*loc.split('.'), 'options', 'types.py'), 'w') as f:
|
||||
f.write(f'{cls}\n')
|
||||
f.write(extra_after_type_defn)
|
||||
with open(os.path.join(*loc.split('.'), 'options', 'parse.py'), 'w') as f:
|
||||
f.write(f'{tc}\n')
|
||||
ctypes = []
|
||||
has_secret = []
|
||||
for opt in defn.root_group.iter_all_non_groups():
|
||||
if isinstance(opt, (Option, MultiOption)) and opt.ctype:
|
||||
ctypes.append(opt)
|
||||
if getattr(opt, 'has_secret', False):
|
||||
has_secret.append(opt.name)
|
||||
with open(os.path.join(*loc.split('.'), 'options', 'types.py'), 'w') as f:
|
||||
f.write(f'{cls}\n')
|
||||
f.write(extra_after_type_defn)
|
||||
if has_secret:
|
||||
f.write('\n\nsecret_options = ' + repr(tuple(has_secret)))
|
||||
with open(os.path.join(*loc.split('.'), 'options', 'parse.py'), 'w') as f:
|
||||
f.write(f'{tc}\n')
|
||||
if ctypes:
|
||||
c = generate_c_conversion(loc, ctypes)
|
||||
with open(os.path.join(*loc.split('.'), 'options', 'to-c-generated.h'), 'w') as f:
|
||||
|
|
|
|||
|
|
@ -220,7 +220,8 @@ class Option:
|
|||
|
||||
def __init__(
|
||||
self, name: str, defval: str, macos_default: Union[Unset, str], parser_func: ParserFuncType,
|
||||
long_text: str, documented: bool, group: 'Group', choices: Tuple[str, ...], ctype: str
|
||||
long_text: str, documented: bool, group: 'Group', choices: Tuple[str, ...], ctype: str,
|
||||
has_secret: bool = False,
|
||||
):
|
||||
self.name = name
|
||||
self.ctype = ctype
|
||||
|
|
@ -231,6 +232,7 @@ class Option:
|
|||
self.group = group
|
||||
self.parser_func = parser_func
|
||||
self.choices = choices
|
||||
self.has_secret = has_secret
|
||||
|
||||
@property
|
||||
def needs_coalescing(self) -> bool:
|
||||
|
|
@ -292,12 +294,13 @@ class MultiVal:
|
|||
|
||||
class MultiOption:
|
||||
|
||||
def __init__(self, name: str, parser_func: ParserFuncType, long_text: str, group: 'Group', ctype: str):
|
||||
def __init__(self, name: str, parser_func: ParserFuncType, long_text: str, group: 'Group', ctype: str, has_secret: bool = False):
|
||||
self.name = name
|
||||
self.ctype = ctype
|
||||
self.parser_func = parser_func
|
||||
self.long_text = long_text
|
||||
self.group = group
|
||||
self.has_secret = has_secret
|
||||
self.items: List[MultiVal] = []
|
||||
|
||||
def add_value(self, val_as_str: str, add_to_default: bool, documented: bool, only: Only) -> None:
|
||||
|
|
@ -701,7 +704,7 @@ class Definition:
|
|||
documented: bool = True, add_to_default: bool = False,
|
||||
only: Only = '', macos_default: Union[Unset, str] = unset,
|
||||
choices: Tuple[str, ...] = (),
|
||||
ctype: str = '',
|
||||
ctype: str = '', has_secret: bool = False,
|
||||
) -> None:
|
||||
if isinstance(defval, bool):
|
||||
defval = 'yes' if defval else 'no'
|
||||
|
|
@ -715,13 +718,13 @@ class Definition:
|
|||
raise TypeError(f'Cannot specify macos_default for is_multiple option: {name} use only instead')
|
||||
is_new = name not in self.multi_option_map
|
||||
if is_new:
|
||||
self.multi_option_map[name] = MultiOption(name, self.parser_func(option_type), long_text, self.current_group, ctype)
|
||||
self.multi_option_map[name] = MultiOption(name, self.parser_func(option_type), long_text, self.current_group, ctype, has_secret)
|
||||
mopt = self.multi_option_map[name]
|
||||
if is_new:
|
||||
self.current_group.append(mopt)
|
||||
mopt.add_value(defval, add_to_default, documented, only)
|
||||
return
|
||||
opt = Option(name, defval, macos_default, self.parser_func(option_type), long_text, documented, self.current_group, choices, ctype)
|
||||
opt = Option(name, defval, macos_default, self.parser_func(option_type), long_text, documented, self.current_group, choices, ctype, has_secret)
|
||||
self.current_group.append(opt)
|
||||
self.option_map[name] = opt
|
||||
|
||||
|
|
|
|||
|
|
@ -203,7 +203,8 @@ def parse_line(
|
|||
parse_conf_item: ItemParser,
|
||||
ans: Dict[str, Any],
|
||||
base_path_for_includes: str,
|
||||
accumulate_bad_lines: Optional[List[BadLine]] = None
|
||||
effective_config_lines: Callable[[str, str], None],
|
||||
accumulate_bad_lines: Optional[List[BadLine]] = None,
|
||||
) -> None:
|
||||
line = line.strip()
|
||||
if not line or line.startswith('#'):
|
||||
|
|
@ -225,9 +226,7 @@ def parse_line(
|
|||
with currently_parsing.set_file(f'<env var: {x}>'):
|
||||
_parse(
|
||||
NamedLineIterator(os.path.join(base_path_for_includes, ''), iter(os.environ[x].splitlines())),
|
||||
parse_conf_item,
|
||||
ans,
|
||||
accumulate_bad_lines
|
||||
parse_conf_item, ans, accumulate_bad_lines, effective_config_lines,
|
||||
)
|
||||
return
|
||||
else:
|
||||
|
|
@ -238,7 +237,7 @@ def parse_line(
|
|||
try:
|
||||
with open(val, encoding='utf-8', errors='replace') as include:
|
||||
with currently_parsing.set_file(val):
|
||||
_parse(include, parse_conf_item, ans, accumulate_bad_lines)
|
||||
_parse(include, parse_conf_item, ans, accumulate_bad_lines, effective_config_lines)
|
||||
except FileNotFoundError:
|
||||
log_error(
|
||||
'Could not find included config file: {}, ignoring'.
|
||||
|
|
@ -250,17 +249,22 @@ def parse_line(
|
|||
format(val)
|
||||
)
|
||||
return
|
||||
if not parse_conf_item(key, val, ans):
|
||||
if parse_conf_item(key, val, ans):
|
||||
effective_config_lines(key, line)
|
||||
else:
|
||||
log_error(f'Ignoring unknown config key: {key}')
|
||||
|
||||
|
||||
|
||||
def _parse(
|
||||
lines: Iterable[str],
|
||||
parse_conf_item: ItemParser,
|
||||
ans: Dict[str, Any],
|
||||
accumulate_bad_lines: Optional[List[BadLine]] = None
|
||||
accumulate_bad_lines: Optional[List[BadLine]] = None,
|
||||
effective_config_lines: Optional[Callable[[str, str], None]] = None,
|
||||
) -> None:
|
||||
name = getattr(lines, 'name', None)
|
||||
effective_config_lines = effective_config_lines or (lambda a, b: None)
|
||||
if name:
|
||||
base_path_for_includes = os.path.abspath(name) if name.endswith(os.path.sep) else os.path.dirname(os.path.abspath(name))
|
||||
else:
|
||||
|
|
@ -297,7 +301,7 @@ def _parse(
|
|||
next_line = ''
|
||||
try:
|
||||
with currently_parsing.set_line(line, line_num):
|
||||
parse_line(line, parse_conf_item, ans, base_path_for_includes, accumulate_bad_lines)
|
||||
parse_line(line, parse_conf_item, ans, base_path_for_includes, effective_config_lines, accumulate_bad_lines)
|
||||
except Exception as e:
|
||||
if accumulate_bad_lines is None:
|
||||
raise
|
||||
|
|
@ -310,11 +314,10 @@ def parse_config_base(
|
|||
lines: Iterable[str],
|
||||
parse_conf_item: ItemParser,
|
||||
ans: Dict[str, Any],
|
||||
accumulate_bad_lines: Optional[List[BadLine]] = None
|
||||
accumulate_bad_lines: Optional[List[BadLine]] = None,
|
||||
effective_config_lines: Optional[Callable[[str, str], None]] = None,
|
||||
) -> None:
|
||||
_parse(
|
||||
lines, parse_conf_item, ans, accumulate_bad_lines
|
||||
)
|
||||
_parse(lines, parse_conf_item, ans, accumulate_bad_lines, effective_config_lines)
|
||||
|
||||
|
||||
def merge_dicts(defaults: Dict[str, Any], newvals: Dict[str, Any]) -> Dict[str, Any]:
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import os
|
|||
from collections.abc import Generator, Iterable
|
||||
from contextlib import contextmanager, suppress
|
||||
from functools import partial
|
||||
from typing import Any, Optional
|
||||
from typing import Any, Callable, Optional
|
||||
|
||||
from .conf.utils import BadLine, parse_config_base
|
||||
from .conf.utils import load_config as _load_config
|
||||
|
|
@ -140,24 +140,37 @@ def finalize_mouse_mappings(opts: Options, accumulate_bad_lines: Optional[list[B
|
|||
opts.mousemap = mousemap
|
||||
|
||||
|
||||
def parse_config(lines: Iterable[str], accumulate_bad_lines: Optional[list[BadLine]] = None) -> dict[str, Any]:
|
||||
def parse_config(
|
||||
lines: Iterable[str], accumulate_bad_lines: Optional[list[BadLine]] = None, effective_config_lines: Optional[Callable[[str, str], None]] = None
|
||||
) -> dict[str, Any]:
|
||||
from .options.parse import create_result_dict, parse_conf_item
|
||||
ans: dict[str, Any] = create_result_dict()
|
||||
parse_config_base(
|
||||
lines,
|
||||
parse_conf_item,
|
||||
ans,
|
||||
accumulate_bad_lines=accumulate_bad_lines
|
||||
accumulate_bad_lines=accumulate_bad_lines,
|
||||
effective_config_lines=effective_config_lines,
|
||||
)
|
||||
return ans
|
||||
|
||||
|
||||
effective_config_lines: list[str] = []
|
||||
|
||||
|
||||
def load_config(*paths: str, overrides: Optional[Iterable[str]] = None, accumulate_bad_lines: Optional[list[BadLine]] = None) -> Options:
|
||||
from .options.parse import merge_result_dicts
|
||||
from .options.types import secret_options
|
||||
del effective_config_lines[:]
|
||||
|
||||
def add_effective_config_line(key: str, line: str) -> None:
|
||||
if key not in secret_options:
|
||||
effective_config_lines.append(line)
|
||||
|
||||
overrides = tuple(overrides) if overrides is not None else ()
|
||||
opts_dict, found_paths = _load_config(
|
||||
defaults, partial(parse_config, accumulate_bad_lines=accumulate_bad_lines), merge_result_dicts, *paths, overrides=overrides)
|
||||
defaults, partial(parse_config, accumulate_bad_lines=accumulate_bad_lines, effective_config_lines=add_effective_config_line),
|
||||
merge_result_dicts, *paths, overrides=overrides)
|
||||
opts = Options(opts_dict)
|
||||
|
||||
opts.alias_map = build_action_aliases(opts.kitten_alias, 'kitten')
|
||||
|
|
@ -178,6 +191,21 @@ def load_config(*paths: str, overrides: Optional[Iterable[str]] = None, accumula
|
|||
return opts
|
||||
|
||||
|
||||
def store_effective_config() -> str:
|
||||
import os
|
||||
import stat
|
||||
import tempfile
|
||||
dest = os.path.join(cache_dir(), 'effective-config')
|
||||
os.makedirs(dest, exist_ok=True)
|
||||
raw = '\n'.join(effective_config_lines)
|
||||
with suppress(FileNotFoundError), tempfile.NamedTemporaryFile('w', dir=dest) as tf:
|
||||
os.chmod(tf.name, stat.S_IRUSR | stat.S_IWUSR)
|
||||
print(raw, file=tf)
|
||||
path = os.path.join(dest, f'{os.getpid()}')
|
||||
os.replace(tf.name, path)
|
||||
return path
|
||||
|
||||
|
||||
class KittyCommonOpts(TypedDict):
|
||||
select_by_word_characters: str
|
||||
open_url_with: list[str]
|
||||
|
|
|
|||
|
|
@ -3002,7 +3002,7 @@ their stdout/stderr/stdin no longer work.
|
|||
|
||||
opt('+remote_control_password', '',
|
||||
option_type='remote_control_password',
|
||||
add_to_default=False,
|
||||
add_to_default=False, has_secret=True,
|
||||
long_text='''
|
||||
Allow other programs to control kitty using passwords. This option can be
|
||||
specified multiple times to add multiple passwords. If no passwords are present
|
||||
|
|
@ -3210,8 +3210,7 @@ is applied. See also :opt:`clipboard_control`.
|
|||
'''
|
||||
)
|
||||
|
||||
opt('file_transfer_confirmation_bypass', '',
|
||||
long_text='''
|
||||
opt('file_transfer_confirmation_bypass', '', has_secret=True, long_text='''
|
||||
The password that can be supplied to the :doc:`file transfer kitten
|
||||
</kittens/transfer>` to skip the transfer confirmation prompt. This should only
|
||||
be used when initiating transfers from trusted computers, over trusted networks
|
||||
|
|
|
|||
3
kitty/options/types.py
generated
3
kitty/options/types.py
generated
|
|
@ -1052,3 +1052,6 @@ nullable_colors = frozenset({
|
|||
'selection_foreground'
|
||||
'selection_background'
|
||||
})
|
||||
|
||||
|
||||
secret_options = ('remote_control_password', 'file_transfer_confirmation_bypass')
|
||||
Loading…
Add table
Add a link
Reference in a new issue