From 0fb1835af100b722b1c855551dde471b95ab03ea Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Aug 2025 09:46:03 +0530 Subject: [PATCH] Cache parsed save as session options spec --- kitty/cli.py | 29 +++++++++++++++++++++++------ kitty/session.py | 16 +++++++++------- kitty/simple_cli_definitions.py | 8 ++++---- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/kitty/cli.py b/kitty/cli.py index 68f8d6b8e..42395213a 100644 --- a/kitty/cli.py +++ b/kitty/cli.py @@ -601,6 +601,17 @@ def apply_preparsed_cli_flags(preparsed_from_c: PreparsedCLIFlags, ans: Any, cre return preparsed_from_c[1] +def parse_cmdline_inner( + args: list[str], oc: Options, disabled: OptionSpecSeq, names_map: dict[str, OptionDict], values_map: dict[str, OptionDict], ans: Any +) -> list[str]: + preparsed = parse_cli_from_spec(args, names_map, values_map) + leftover_args = apply_preparsed_cli_flags(preparsed, ans, lambda: oc) + for opt in disabled: + if not isinstance(opt, str): + setattr(ans, opt['dest'], defval_for_opt(opt)) + return leftover_args + + def parse_cmdline(oc: Options, disabled: OptionSpecSeq, ans: Any, args: list[str] | None = None) -> list[str]: names_map = oc.names_map.copy() values_map = oc.values_map.copy() @@ -611,18 +622,24 @@ def parse_cmdline(oc: Options, disabled: OptionSpecSeq, ans: Any, args: list[str names_map['version'] = {'type': 'bool-set', 'aliases': ('--version', '-v')} # type: ignore values_map['version'] = False try: - preparsed = parse_cli_from_spec(sys.argv[1:] if args is None else args, names_map, values_map) + return parse_cmdline_inner(sys.argv[1:] if args is None else args, oc, disabled, names_map, values_map, ans) except Exception as e: raise SystemExit(str(e)) - leftover_args = apply_preparsed_cli_flags(preparsed, ans, lambda: oc) - for opt in disabled: - if not isinstance(opt, str): - setattr(ans, opt['dest'], defval_for_opt(opt)) + +spec_cache: dict[str, tuple[Options, OptionSpecSeq]] = {} + + +def cached_parse_cmdline(spec: str, args: list[str], ans: Any) -> list[str]: + if (x := spec_cache.get(spec)) is None: + seq, disabled = parse_option_spec(spec) + oc = Options(seq, '', '', '') + x = spec_cache[spec] = oc, disabled + oc, disabled = x + leftover_args = parse_cmdline_inner(args, oc, disabled, oc.names_map, oc.values_map, ans) return leftover_args - def options_for_completion() -> OptionSpecSeq: raw = '--help -h\ntype=bool-set\nShow help for {appname} command line options\n\n{raw}'.format( appname=appname, raw=kitty_options_spec()) diff --git a/kitty/session.py b/kitty/session.py index 80bcd8e30..b28173b9e 100644 --- a/kitty/session.py +++ b/kitty/session.py @@ -467,17 +467,19 @@ def save_as_session_part2(boss: BossType, opts: SaveAsSessionOptions, path: str) boss.edit_file(path) +def parse_save_as_options_spec_args(args: list[str]) -> tuple[SaveAsSessionOptions, list[str]]: + from kitty.cli import cached_parse_cmdline + ans = SaveAsSessionOptions() + leftover_args = cached_parse_cmdline(save_as_session_options(), args, ans) + return ans, leftover_args + + def default_save_as_session_opts() -> SaveAsSessionOptions: - from kitty.cli import parse_args - return parse_args( - [], save_as_session_options, result_class=SaveAsSessionOptions)[0] + return parse_save_as_options_spec_args([])[0] def save_as_session(boss: BossType, cmdline: Sequence[str]) -> None: - from kitty.cli import parse_args - opts: SaveAsSessionOptions - opts, args = parse_args( - list(cmdline), save_as_session_options, result_class=SaveAsSessionOptions) + opts, args = parse_save_as_options_spec_args(list(cmdline)) path = args[0] if args else '' if path: save_as_session_part2(boss, opts, path) diff --git a/kitty/simple_cli_definitions.py b/kitty/simple_cli_definitions.py index 709053ad7..6fdfbfa0a 100644 --- a/kitty/simple_cli_definitions.py +++ b/kitty/simple_cli_definitions.py @@ -8,7 +8,7 @@ import re import sys from dataclasses import dataclass from enum import Enum, auto -from typing import Any, Iterator, TypedDict +from typing import Any, Iterator, Sequence, TypedDict try: from kitty.constants import appname, is_macos @@ -118,7 +118,7 @@ class OptionDict(TypedDict): completion: CompletionSpec -OptionSpecSeq = list[str | OptionDict] +OptionSpecSeq = Sequence[str | OptionDict] def parse_option_spec(spec: str | None = None) -> tuple[OptionSpecSeq, OptionSpecSeq]: @@ -129,8 +129,8 @@ def parse_option_spec(spec: str | None = None) -> tuple[OptionSpecSeq, OptionSpe lines = spec.splitlines() prev_line = '' prev_indent = 0 - seq: OptionSpecSeq = [] - disabled: OptionSpecSeq = [] + seq: list[str | OptionDict] = [] + disabled: list[str | OptionDict] = [] mpat = re.compile('([a-z]+)=(.+)') current_cmd: OptionDict = { 'dest': '', 'aliases': (), 'help': '', 'choices': (),