Get rid of --debug-config

Instead have a keybind that shows the configuration used
by the currently running kitty instance
This commit is contained in:
Kovid Goyal 2021-06-05 14:27:24 +05:30
parent a1b87f445b
commit 4a71afaf96
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
9 changed files with 195 additions and 170 deletions

View File

@ -21,10 +21,11 @@ Steps to reproduce the behavior:
If applicable, add screenshots to help explain your problem.
**Environment details**
OS: Name and version of operating system(s)
```
Output of kitty --debug-config
Press Ctrl+Shift+F6 in kitty, to copy debug output about kitty and its
configuration to the clipboard and paste it here.
On older versions of kitty, run kitty --debug-config instead
```
**Additional context**

View File

@ -116,6 +116,7 @@ Toggle maximized :sc:`toggle_maximized`
Input unicode character :sc:`input_unicode_character` (also :kbd:`^+⌘+space` on macOS)
Click URL using the keyboard :sc:`open_url`
Reset the terminal :sc:`reset_terminal`
Debug :file:`kitty.conf` :sc:`debug_config`
Pass current selection to program :sc:`pass_selection_to_program`
Edit |kitty| config file :sc:`edit_config_file`
Open a |kitty| shell :sc:`kitty_shell`

View File

@ -50,8 +50,8 @@ from .typing import PopenType, TypedDict
from .utils import (
func_name, get_editor, get_primary_selection, is_path_in_temp_dir,
log_error, open_url, parse_address_spec, parse_uri_list,
platform_window_id, remove_socket_file, safe_print, set_primary_selection,
single_instance, startup_notification_handler
platform_window_id, read_shell_environment, remove_socket_file, safe_print,
set_primary_selection, single_instance, startup_notification_handler
)
from .window import MatchPatternType, Window
@ -874,11 +874,29 @@ class Boss:
s.shutdown(socket.SHUT_RDWR)
s.close()
def display_scrollback(self, window: Window, data: Optional[bytes], cmd: List[str]) -> None:
def display_scrollback(self, window: Window, data: Union[bytes, str], input_line_number: int = 0, title: str = '') -> None:
def prepare_arg(x: str) -> str:
x = x.replace('INPUT_LINE_NUMBER', str(input_line_number))
x = x.replace('CURSOR_LINE', str(window.screen.cursor.y + 1))
x = x.replace('CURSOR_COLUMN', str(window.screen.cursor.x + 1))
return x
cmd = list(map(prepare_arg, get_options().scrollback_pager))
if not os.path.isabs(cmd[0]):
import shutil
exe = shutil.which(cmd[0])
if not exe:
env = read_shell_environment(get_options())
if env and 'PATH' in env:
exe = shutil.which(cmd[0], path=env['PATH'])
if exe:
cmd[0] = exe
tab = self.active_tab
if tab is not None:
bdata = data.encode('utf-8') if isinstance(data, str) else data
tab.new_special_window(
SpecialWindow(cmd, data, _('History'), overlay_for=window.id, cwd=window.cwd_of_child),
SpecialWindow(cmd, bdata, title or _('History'), overlay_for=window.id, cwd=window.cwd_of_child),
copy_colors_from=self.active_window
)
@ -1677,8 +1695,8 @@ class Boss:
def show_kitty_env_vars(self) -> None:
w = self.active_window
if w:
output = '\n'.join(f'{k}={v}' for k, v in os.environ.items()).encode('utf-8')
self.display_scrollback(w, output, ['less'])
output = '\n'.join(f'{k}={v}' for k, v in os.environ.items())
self.display_scrollback(w, output, title=_('Current kitty env vars'))
def open_file(self, path: str) -> None:
if path == ":cocoa::application launched::":
@ -1697,3 +1715,12 @@ class Boss:
self.new_window(path)
if w is not None:
tab.remove_window(w)
def debug_config(self) -> None:
from .debug_config import debug_config
w = self.active_window
if w is not None:
output = debug_config(get_options())
set_clipboard_string(re.sub(r'\x1b.+?m', '', output))
output += '\n\x1b[35mThis debug output has been copied to the clipboard\x1b[m'
self.display_scrollback(w, output, title=_('Current kitty options'))

View File

@ -2,22 +2,19 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
import os
import re
import sys
from collections import deque
from typing import (
Any, Callable, Dict, FrozenSet, Generator, Iterable, Iterator, List, Match,
Optional, Sequence, Set, Tuple, Type, TypeVar, Union, cast
Any, Callable, Dict, FrozenSet, Iterator, List, Match, Optional, Sequence,
Tuple, Type, TypeVar, Union, cast
)
from .cli_stub import CLIOptions
from .conf.utils import KeyAction, resolve_config
from .constants import appname, defconf, is_macos, is_wayland, str_version
from .options.types import Options as KittyOpts, defaults
from .options.utils import MouseMap
from .types import MouseEvent, SingleKey
from .typing import BadLineType, SequenceMap, TypedDict
from .conf.utils import resolve_config
from .constants import appname, defconf, is_macos, str_version
from .options.types import Options as KittyOpts
from .typing import BadLineType, TypedDict
class OptionDict(TypedDict):
@ -701,13 +698,6 @@ type=bool-set
Print out information about the selection of fallback fonts for characters not present in the main font.
--debug-config
type=bool-set
Print out information about the system and kitty configuration. Note that this only
reads the standard kitty.conf not any extra configuration or alternative conf files
that were specified on the command line.
--execute -e
type=bool-set
!
@ -759,131 +749,13 @@ def parse_args(
SYSTEM_CONF = '/etc/xdg/kitty/kitty.conf'
ShortcutMap = Dict[Tuple[SingleKey, ...], KeyAction]
def mod_to_names(mods: int) -> Generator[str, None, None]:
from .fast_data_types import (
GLFW_MOD_ALT, GLFW_MOD_CAPS_LOCK, GLFW_MOD_CONTROL, GLFW_MOD_HYPER,
GLFW_MOD_META, GLFW_MOD_NUM_LOCK, GLFW_MOD_SHIFT, GLFW_MOD_SUPER
)
modmap = {'shift': GLFW_MOD_SHIFT, 'alt': GLFW_MOD_ALT, 'ctrl': GLFW_MOD_CONTROL, ('cmd' if is_macos else 'super'): GLFW_MOD_SUPER,
'hyper': GLFW_MOD_HYPER, 'meta': GLFW_MOD_META, 'num_lock': GLFW_MOD_NUM_LOCK, 'caps_lock': GLFW_MOD_CAPS_LOCK}
for name, val in modmap.items():
if mods & val:
yield name
def print_shortcut(key_sequence: Iterable[SingleKey], action: KeyAction) -> None:
from .fast_data_types import glfw_get_key_name
keys = []
for key_spec in key_sequence:
names = []
mods, is_native, key = key_spec
names = list(mod_to_names(mods))
if key:
kname = glfw_get_key_name(0, key) if is_native else glfw_get_key_name(key, 0)
names.append(kname or f'{key}')
keys.append('+'.join(names))
print('\t', ' > '.join(keys), action)
def print_shortcut_changes(defns: ShortcutMap, text: str, changes: Set[Tuple[SingleKey, ...]]) -> None:
if changes:
print(title(text))
for k in sorted(changes):
print_shortcut(k, defns[k])
def compare_keymaps(final: ShortcutMap, initial: ShortcutMap) -> None:
added = set(final) - set(initial)
removed = set(initial) - set(final)
changed = {k for k in set(final) & set(initial) if final[k] != initial[k]}
print_shortcut_changes(final, 'Added shortcuts:', added)
print_shortcut_changes(initial, 'Removed shortcuts:', removed)
print_shortcut_changes(final, 'Changed shortcuts:', changed)
def flatten_sequence_map(m: SequenceMap) -> ShortcutMap:
ans: Dict[Tuple[SingleKey, ...], KeyAction] = {}
for key_spec, rest_map in m.items():
for r, action in rest_map.items():
ans[(key_spec,) + (r)] = action
return ans
def compare_mousemaps(final: MouseMap, initial: MouseMap) -> None:
added = set(final) - set(initial)
removed = set(initial) - set(final)
changed = {k for k in set(final) & set(initial) if final[k] != initial[k]}
def print_mouse_action(trigger: MouseEvent, action: KeyAction) -> None:
names = list(mod_to_names(trigger.mods)) + [f'b{trigger.button+1}']
when = {-1: 'repeat', 1: 'press', 2: 'doublepress', 3: 'triplepress'}.get(trigger.repeat_count, trigger.repeat_count)
grabbed = 'grabbed' if trigger.grabbed else 'ungrabbed'
print('\t', '+'.join(names), when, grabbed, action)
def print_changes(defns: MouseMap, changes: Set[MouseEvent], text: str) -> None:
if changes:
print(title(text))
for k in sorted(changes):
print_mouse_action(k, defns[k])
print_changes(final, added, 'Added mouse actions:')
print_changes(initial, removed, 'Removed mouse actions:')
print_changes(final, changed, 'Changed mouse actions:')
def compare_opts(opts: KittyOpts) -> None:
from .config import load_config
print('\nConfig options different from defaults:')
default_opts = load_config()
ignored = ('keymap', 'sequence_map', 'mousemap', 'map', 'mouse_map')
changed_opts = [
f for f in sorted(defaults._fields)
if f not in ignored and getattr(opts, f) != getattr(defaults, f)
]
field_len = max(map(len, changed_opts)) if changed_opts else 20
fmt = '{{:{:d}s}}'.format(field_len)
for f in changed_opts:
print(title(fmt.format(f)), getattr(opts, f))
compare_mousemaps(opts.mousemap, default_opts.mousemap)
final_, initial_ = opts.keymap, default_opts.keymap
final: ShortcutMap = {(k,): v for k, v in final_.items()}
initial: ShortcutMap = {(k,): v for k, v in initial_.items()}
final_s, initial_s = map(flatten_sequence_map, (opts.sequence_map, default_opts.sequence_map))
final.update(final_s)
initial.update(initial_s)
compare_keymaps(final, initial)
def create_opts(args: CLIOptions, debug_config: bool = False, accumulate_bad_lines: Optional[List[BadLineType]] = None) -> KittyOpts:
def create_opts(args: CLIOptions, accumulate_bad_lines: Optional[List[BadLineType]] = None) -> KittyOpts:
from .config import load_config
config = tuple(resolve_config(SYSTEM_CONF, defconf, args.config))
if debug_config:
print(version(add_rev=True))
print(' '.join(os.uname()))
if is_macos:
import subprocess
print(' '.join(subprocess.check_output(['sw_vers']).decode('utf-8').splitlines()).strip())
if os.path.exists('/etc/issue'):
with open('/etc/issue', encoding='utf-8', errors='replace') as f:
print(f.read().strip())
if os.path.exists('/etc/lsb-release'):
with open('/etc/lsb-release', encoding='utf-8', errors='replace') as f:
print(f.read().strip())
config = tuple(x for x in config if os.path.exists(x))
if config:
print(green('Loaded config files:'), ', '.join(config))
overrides = (a.replace('=', ' ', 1) for a in args.override or ())
opts = load_config(*config, overrides=overrides, accumulate_bad_lines=accumulate_bad_lines)
if debug_config:
if not is_macos:
print('Running under:', green('Wayland' if is_wayland(opts) else 'X11'))
compare_opts(opts)
return opts

143
kitty/debug_config.py Normal file
View File

@ -0,0 +1,143 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
import os
from functools import partial
from typing import Callable, Dict, Generator, Iterable, Set, Tuple
from .cli import green, title, version
from .conf.utils import KeyAction
from .constants import is_macos, is_wayland
from .options.types import Options as KittyOpts, defaults
from .options.utils import MouseMap
from .types import MouseEvent, SingleKey
from .typing import SequenceMap
ShortcutMap = Dict[Tuple[SingleKey, ...], KeyAction]
def mod_to_names(mods: int) -> Generator[str, None, None]:
from .fast_data_types import (
GLFW_MOD_ALT, GLFW_MOD_CAPS_LOCK, GLFW_MOD_CONTROL, GLFW_MOD_HYPER,
GLFW_MOD_META, GLFW_MOD_NUM_LOCK, GLFW_MOD_SHIFT, GLFW_MOD_SUPER
)
modmap = {'shift': GLFW_MOD_SHIFT, 'alt': GLFW_MOD_ALT, 'ctrl': GLFW_MOD_CONTROL, ('cmd' if is_macos else 'super'): GLFW_MOD_SUPER,
'hyper': GLFW_MOD_HYPER, 'meta': GLFW_MOD_META, 'num_lock': GLFW_MOD_NUM_LOCK, 'caps_lock': GLFW_MOD_CAPS_LOCK}
for name, val in modmap.items():
if mods & val:
yield name
def print_shortcut(key_sequence: Iterable[SingleKey], action: KeyAction, print: Callable) -> None:
from .fast_data_types import glfw_get_key_name
keys = []
for key_spec in key_sequence:
names = []
mods, is_native, key = key_spec
names = list(mod_to_names(mods))
if key:
kname = glfw_get_key_name(0, key) if is_native else glfw_get_key_name(key, 0)
names.append(kname or f'{key}')
keys.append('+'.join(names))
print('\t', ' > '.join(keys), action)
def print_shortcut_changes(defns: ShortcutMap, text: str, changes: Set[Tuple[SingleKey, ...]], print: Callable) -> None:
if changes:
print(title(text))
for k in sorted(changes):
print_shortcut(k, defns[k], print)
def compare_keymaps(final: ShortcutMap, initial: ShortcutMap, print: Callable) -> None:
added = set(final) - set(initial)
removed = set(initial) - set(final)
changed = {k for k in set(final) & set(initial) if final[k] != initial[k]}
print_shortcut_changes(final, 'Added shortcuts:', added, print)
print_shortcut_changes(initial, 'Removed shortcuts:', removed, print)
print_shortcut_changes(final, 'Changed shortcuts:', changed, print)
def flatten_sequence_map(m: SequenceMap) -> ShortcutMap:
ans: Dict[Tuple[SingleKey, ...], KeyAction] = {}
for key_spec, rest_map in m.items():
for r, action in rest_map.items():
ans[(key_spec,) + (r)] = action
return ans
def compare_mousemaps(final: MouseMap, initial: MouseMap, print: Callable) -> None:
added = set(final) - set(initial)
removed = set(initial) - set(final)
changed = {k for k in set(final) & set(initial) if final[k] != initial[k]}
def print_mouse_action(trigger: MouseEvent, action: KeyAction) -> None:
names = list(mod_to_names(trigger.mods)) + [f'b{trigger.button+1}']
when = {-1: 'repeat', 1: 'press', 2: 'doublepress', 3: 'triplepress'}.get(trigger.repeat_count, trigger.repeat_count)
grabbed = 'grabbed' if trigger.grabbed else 'ungrabbed'
print('\t', '+'.join(names), when, grabbed, action)
def print_changes(defns: MouseMap, changes: Set[MouseEvent], text: str) -> None:
if changes:
print(title(text))
for k in sorted(changes):
print_mouse_action(k, defns[k])
print_changes(final, added, 'Added mouse actions:')
print_changes(initial, removed, 'Removed mouse actions:')
print_changes(final, changed, 'Changed mouse actions:')
def compare_opts(opts: KittyOpts, print: Callable) -> None:
from .config import load_config
print()
print('Config options different from defaults:')
default_opts = load_config()
ignored = ('keymap', 'sequence_map', 'mousemap', 'map', 'mouse_map')
changed_opts = [
f for f in sorted(defaults._fields)
if f not in ignored and getattr(opts, f) != getattr(defaults, f)
]
field_len = max(map(len, changed_opts)) if changed_opts else 20
fmt = '{{:{:d}s}}'.format(field_len)
for f in changed_opts:
print(title(fmt.format(f)), str(getattr(opts, f)))
compare_mousemaps(opts.mousemap, default_opts.mousemap, print)
final_, initial_ = opts.keymap, default_opts.keymap
final: ShortcutMap = {(k,): v for k, v in final_.items()}
initial: ShortcutMap = {(k,): v for k, v in initial_.items()}
final_s, initial_s = map(flatten_sequence_map, (opts.sequence_map, default_opts.sequence_map))
final.update(final_s)
initial.update(initial_s)
compare_keymaps(final, initial, print)
def debug_config(opts: KittyOpts) -> str:
from io import StringIO
out = StringIO()
p = partial(print, file=out)
p(version(add_rev=True))
p(' '.join(os.uname()))
if is_macos:
import subprocess
p(' '.join(subprocess.check_output(['sw_vers']).decode('utf-8').splitlines()).strip())
if os.path.exists('/etc/issue'):
with open('/etc/issue', encoding='utf-8', errors='replace') as f:
p(f.read().strip())
if os.path.exists('/etc/lsb-release'):
with open('/etc/lsb-release', encoding='utf-8', errors='replace') as f:
p(f.read().strip())
if not is_macos:
p('Running under:' + green('Wayland' if is_wayland() else 'X11'))
if opts.config_paths:
p(green('Loaded config files:'))
p(' ', '\n '.join(opts.config_paths))
if opts.config_overrides:
p(green('Loaded config overrides:'))
p(' ', '\n '.join(opts.config_overrides))
compare_opts(opts, p)
return out.getvalue()

View File

@ -310,9 +310,6 @@ def _main() -> None:
os.chdir(os.path.expanduser('~'))
cli_opts, rest = parse_args(args=args, result_class=CLIOptions)
cli_opts.args = rest
if cli_opts.debug_config:
create_opts(cli_opts, debug_config=True)
return
if cli_opts.detach:
if cli_opts.session == '-':
from .session import PreReadSession

View File

@ -3280,6 +3280,9 @@ map('Reset the terminal',
only="macos"
)
map('Debug kitty configuration',
'debug_config kitty_mod+f6 debug_config'
)
map('Send arbitrary text on key presses',
'send_text ctrl+shift+alt+h send_text all Hello World',

View File

@ -804,6 +804,8 @@ defaults.map = [
KeyDefinition(True, KeyAction('set_background_opacity', ('default',)), 1024, False, 97, (SingleKey(mods=0, is_native=False, key=100),)),
# reset_terminal
KeyDefinition(False, KeyAction('clear_terminal', ('reset', True)), 1024, False, 57349, ()),
# debug_config
KeyDefinition(False, KeyAction('debug_config'), 1024, False, 57369, ()),
]
if is_macos:
defaults.map.append(KeyDefinition(False, KeyAction('copy_to_clipboard'), 8, False, 99, ()))

View File

@ -42,8 +42,7 @@ from .types import MouseEvent, ScreenGeometry, WindowGeometry
from .typing import BossType, ChildType, EdgeLiteral, TabType, TypedDict
from .utils import (
color_as_int, get_primary_selection, load_shaders, log_error, open_cmd,
open_url, parse_color_set, read_shell_environment, sanitize_title,
set_primary_selection
open_url, parse_color_set, sanitize_title, set_primary_selection
)
MatchPatternType = Union[Pattern[str], Tuple[Pattern[str], Optional[Pattern[str]]]]
@ -902,27 +901,7 @@ class Window:
def show_scrollback(self) -> None:
text = self.as_text(as_ansi=True, add_history=True, add_wrap_markers=True)
data = self.pipe_data(text, has_wrap_markers=True)
def prepare_arg(x: str) -> str:
x = x.replace('INPUT_LINE_NUMBER', str(data['input_line_number']))
x = x.replace('CURSOR_LINE', str(data['cursor_y']))
x = x.replace('CURSOR_COLUMN', str(data['cursor_x']))
return x
cmd = list(map(prepare_arg, get_options().scrollback_pager))
if not os.path.isabs(cmd[0]):
import shutil
exe = shutil.which(cmd[0])
if not exe:
env = read_shell_environment(get_options())
if env and 'PATH' in env:
exe = shutil.which(cmd[0], path=env['PATH'])
if exe:
cmd[0] = exe
bdata: Union[str, bytes, None] = data['text']
if isinstance(bdata, str):
bdata = bdata.encode('utf-8')
get_boss().display_scrollback(self, bdata, cmd)
get_boss().display_scrollback(self, data['text'], data['input_line_number'])
def paste_bytes(self, text: Union[str, bytes]) -> None:
# paste raw bytes without any processing