When encountering errors in kitty.conf report them to the user instead of failing to start.

This commit is contained in:
Kovid Goyal 2019-04-28 20:53:59 +05:30
parent c6d3ede57e
commit c660840c19
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 44 additions and 17 deletions

View File

@ -114,6 +114,9 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
- Use negative values for :opt:`mouse_hide_wait` to hide the mouse cursor - Use negative values for :opt:`mouse_hide_wait` to hide the mouse cursor
immediately when pressing a key (:iss:`1534`) immediately when pressing a key (:iss:`1534`)
- When encountering errors in :file:`kitty.conf` report them to the user
instead of failing to start.
0.13.3 [2019-01-19] 0.13.3 [2019-01-19]
------------------------------ ------------------------------

View File

@ -742,7 +742,7 @@ class Boss:
self._run_kitten('ask', args) self._run_kitten('ask', args)
def show_error(self, title, msg): def show_error(self, title, msg):
self._run_kitten('show_error', ['--title', title], input_data=msg) self._run_kitten('show_error', args=['--title', title], input_data=msg)
def do_set_tab_title(self, title, tab_id): def do_set_tab_title(self, title, tab_id):
tm = self.active_tab_manager tm = self.active_tab_manager
@ -1068,3 +1068,11 @@ class Boss:
dbus_notification_activated(*args) dbus_notification_activated(*args)
else: else:
dbus_notification_created(*args) dbus_notification_created(*args)
def show_bad_config_lines(self, bad_lines):
def format_bad_line(bad_line):
return '{}:{} in line: {}\n'.format(bad_line.number, bad_line.exception, bad_line.line)
msg = '\n'.join(map(format_bad_line, bad_lines)).rstrip()
self.show_error(_('Errors in kitty.conf'), msg)

View File

@ -710,7 +710,7 @@ def compare_opts(opts):
compare_keymaps(final, initial) compare_keymaps(final, initial)
def create_opts(args, debug_config=False): def create_opts(args, debug_config=False, accumulate_bad_lines=None):
from .config import load_config from .config import load_config
config = tuple(resolve_config(SYSTEM_CONF, defconf, args.config)) config = tuple(resolve_config(SYSTEM_CONF, defconf, args.config))
if debug_config: if debug_config:
@ -729,7 +729,7 @@ def create_opts(args, debug_config=False):
if config: if config:
print(green('Loaded config files:'), ', '.join(config)) print(green('Loaded config files:'), ', '.join(config))
overrides = (a.replace('=', ' ', 1) for a in args.override or ()) overrides = (a.replace('=', ' ', 1) for a in args.override or ())
opts = load_config(*config, overrides=overrides) opts = load_config(*config, overrides=overrides, accumulate_bad_lines=accumulate_bad_lines)
if debug_config: if debug_config:
compare_opts(opts) compare_opts(opts)
return opts return opts

View File

@ -5,11 +5,13 @@
import os import os
import re import re
import shlex import shlex
from collections import namedtuple
from ..rgb import to_color as as_color from ..rgb import to_color as as_color
from ..utils import log_error from ..utils import log_error
key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$') key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$')
BadLine = namedtuple('BadLine', 'number line exception')
def to_color(x): def to_color(x):
@ -90,22 +92,28 @@ def parse_line(line, type_map, special_handling, ans, all_keys, base_path_for_in
ans[key] = val ans[key] = val
def _parse(lines, type_map, special_handling, ans, all_keys): def _parse(lines, type_map, special_handling, ans, all_keys, accumulate_bad_lines=None):
name = getattr(lines, 'name', None) name = getattr(lines, 'name', None)
if name: if name:
base_path_for_includes = os.path.dirname(os.path.abspath(name)) base_path_for_includes = os.path.dirname(os.path.abspath(name))
else: else:
from ..constants import config_dir from ..constants import config_dir
base_path_for_includes = config_dir base_path_for_includes = config_dir
for line in lines: for i, line in enumerate(lines):
parse_line(line, type_map, special_handling, ans, all_keys, base_path_for_includes) try:
parse_line(line, type_map, special_handling, ans, all_keys, base_path_for_includes)
except Exception as e:
if accumulate_bad_lines is None:
raise
accumulate_bad_lines.append(BadLine(i + 1, line.rstrip(), e))
def parse_config_base( def parse_config_base(
lines, defaults, type_map, special_handling, ans, check_keys=True lines, defaults, type_map, special_handling, ans, check_keys=True,
accumulate_bad_lines=None
): ):
all_keys = defaults._asdict() if check_keys else None all_keys = defaults._asdict() if check_keys else None
_parse(lines, type_map, special_handling, ans, all_keys) _parse(lines, type_map, special_handling, ans, all_keys, accumulate_bad_lines)
def create_options_class(keys): def create_options_class(keys):

View File

@ -8,6 +8,7 @@ import re
import sys import sys
from collections import namedtuple from collections import namedtuple
from contextlib import contextmanager from contextlib import contextmanager
from functools import partial
from . import fast_data_types as defines from . import fast_data_types as defines
from .conf.definition import as_conf_file, config_lines from .conf.definition import as_conf_file, config_lines
@ -439,7 +440,7 @@ def option_names_for_completion():
yield from special_handlers yield from special_handlers
def parse_config(lines, check_keys=True): def parse_config(lines, check_keys=True, accumulate_bad_lines=None):
ans = {'symbol_map': {}, 'keymap': {}, 'sequence_map': {}, 'key_definitions': [], 'env': {}} ans = {'symbol_map': {}, 'keymap': {}, 'sequence_map': {}, 'key_definitions': [], 'env': {}}
parse_config_base( parse_config_base(
lines, lines,
@ -447,7 +448,8 @@ def parse_config(lines, check_keys=True):
type_map, type_map,
special_handling, special_handling,
ans, ans,
check_keys=check_keys check_keys=check_keys,
accumulate_bad_lines=accumulate_bad_lines
) )
return ans return ans
@ -617,8 +619,11 @@ def finalize_keys(opts):
opts.sequence_map = sequence_map opts.sequence_map = sequence_map
def load_config(*paths, overrides=None): def load_config(*paths, overrides=None, accumulate_bad_lines=None):
opts = _load_config(Options, defaults, parse_config, merge_configs, *paths, overrides=overrides) parser = parse_config
if accumulate_bad_lines is not None:
parser = partial(parse_config, accumulate_bad_lines=accumulate_bad_lines)
opts = _load_config(Options, defaults, parser, merge_configs, *paths, overrides=overrides)
finalize_keys(opts) finalize_keys(opts)
if opts.background_opacity < 1.0 and opts.macos_titlebar_color: if opts.background_opacity < 1.0 and opts.macos_titlebar_color:
log_error('Cannot use both macos_titlebar_color and background_opacity') log_error('Cannot use both macos_titlebar_color and background_opacity')

View File

@ -115,7 +115,7 @@ def get_new_os_window_trigger(opts):
return new_os_window_trigger return new_os_window_trigger
def _run_app(opts, args): def _run_app(opts, args, bad_lines=()):
new_os_window_trigger = get_new_os_window_trigger(opts) new_os_window_trigger = get_new_os_window_trigger(opts)
if is_macos and opts.macos_custom_beam_cursor: if is_macos and opts.macos_custom_beam_cursor:
set_custom_ibeam_cursor() set_custom_ibeam_cursor()
@ -132,18 +132,20 @@ def _run_app(opts, args):
args.cls or appname, load_all_shaders) args.cls or appname, load_all_shaders)
boss = Boss(window_id, opts, args, cached_values, new_os_window_trigger) boss = Boss(window_id, opts, args, cached_values, new_os_window_trigger)
boss.start() boss.start()
if bad_lines:
boss.show_bad_config_lines(bad_lines)
try: try:
boss.child_monitor.main_loop() boss.child_monitor.main_loop()
finally: finally:
boss.destroy() boss.destroy()
def run_app(opts, args): def run_app(opts, args, bad_lines=()):
set_scale(opts.box_drawing_scale) set_scale(opts.box_drawing_scale)
set_options(opts, is_wayland, args.debug_gl, args.debug_font_fallback) set_options(opts, is_wayland, args.debug_gl, args.debug_font_fallback)
set_font_family(opts, debug_font_matching=args.debug_font_fallback) set_font_family(opts, debug_font_matching=args.debug_font_fallback)
try: try:
_run_app(opts, args) _run_app(opts, args, bad_lines)
finally: finally:
free_font_data() # must free font data before glfw/freetype/fontconfig/opengl etc are finalized free_font_data() # must free font data before glfw/freetype/fontconfig/opengl etc are finalized
@ -262,12 +264,13 @@ def _main():
talk_to_instance(args) talk_to_instance(args)
return return
init_glfw(args.debug_keyboard) # needed for parsing native keysyms init_glfw(args.debug_keyboard) # needed for parsing native keysyms
opts = create_opts(args) bad_lines = []
opts = create_opts(args, accumulate_bad_lines=bad_lines)
setup_environment(opts, args) setup_environment(opts, args)
try: try:
with setup_profiling(args): with setup_profiling(args):
# Avoid needing to launch threads to reap zombies # Avoid needing to launch threads to reap zombies
run_app(opts, args) run_app(opts, args, bad_lines)
finally: finally:
glfw_terminate() glfw_terminate()