diff --git a/gen-rc-go.py b/gen-rc-go.py index 8d2007ebd..00c160aa3 100755 --- a/gen-rc-go.py +++ b/gen-rc-go.py @@ -4,7 +4,7 @@ import os import subprocess import sys -from typing import List +from typing import Dict, List, Tuple import kitty.constants as kc from kitty.cli import OptionDict, OptionSpecSeq, parse_option_spec @@ -21,22 +21,29 @@ def replace(template: str, **kw: str) -> str: return template +go_type_map = {'bool-set': 'bool', 'int': 'int', 'float': 'float64', '': 'string', 'list': '[]string', 'choices': 'string'} + + class Option: def __init__(self, x: OptionDict) -> None: flags = sorted(x['aliases'], key=len) short = '' + self.aliases = [] if len(flags) > 1 and not flags[0].startswith("--"): short = flags[0][1:] - long = flags[-1][2:] - if not long: - raise TypeError(f'No long flag for {x} with flags {flags}') - self.short, self.long = short, long + del flags[0] + self.short, self.long = short, x['name'].replace('_', '-') + for f in flags: + q = f[2:] + if q != self.long: + self.aliases.append(q) self.usage = serialize_as_go_string(x['help'].strip()) self.type = x['type'] self.dest = x['dest'] self.default = x['default'] self.obj_dict = x + self.go_type = go_type_map[self.type] def to_flag_definition(self, base: str = 'ans.Flags()') -> str: if self.type == 'bool-set': @@ -73,18 +80,33 @@ class Option: raise TypeError(f'Unknown type of CLI option: {self.type} for {self.long}') +def render_alias_map(alias_map: Dict[str, Tuple[str, ...]]) -> str: + if not alias_map: + return '' + amap = 'switch name {\n' + for name, aliases in alias_map.items(): + for alias in aliases: + amap += f'\ncase "{alias}":\nname = "{name}"\n' + amap += '}' + return amap + + def build_go_code(name: str, cmd: RemoteCommand, seq: OptionSpecSeq, template: str) -> str: template = '\n' + template[len('//go:build exclude'):] NO_RESPONSE_BASE = 'true' if cmd.no_response else 'false' af: List[str] = [] a = af.append + alias_map = {} for x in seq: if isinstance(x, str): continue o = Option(x) + if o.aliases: + alias_map[o.long] = tuple(o.aliases) a(o.to_flag_definition()) if o.dest == 'no_response': continue + ans = replace( template, CMD_NAME=name, __FILE__=__file__, CLI_NAME=name.replace('_', '-'), @@ -92,6 +114,7 @@ def build_go_code(name: str, cmd: RemoteCommand, seq: OptionSpecSeq, template: s LONG_DESC=serialize_as_go_string(cmd.desc.strip()), NO_RESPONSE_BASE=NO_RESPONSE_BASE, ADD_FLAGS_CODE='\n'.join(af), WAIT_TIMEOUT=str(cmd.response_timeout), + ALIAS_NORMALIZE_CODE=render_alias_map(alias_map) ) return ans @@ -122,7 +145,7 @@ var IsFrozenBuild bool = false template = f.read() for name in all_command_names(): cmd = command_for_name(name) - opts = parse_option_spec(cmd.options_spec)[0] + opts = parse_option_spec(cmd.options_spec or '\n\n')[0] code = build_go_code(name, cmd, opts, template) dest = f'tools/cmd/at/{name}_generated.go' if os.path.exists(dest): diff --git a/kitty/cli.py b/kitty/cli.py index e4a4ddfdb..33d33fd49 100644 --- a/kitty/cli.py +++ b/kitty/cli.py @@ -26,6 +26,7 @@ from .typing import BadLineType, TypedDict class OptionDict(TypedDict): dest: str + name: str aliases: FrozenSet[str] help: str choices: FrozenSet[str] @@ -244,7 +245,7 @@ def parse_option_spec(spec: Optional[str] = None) -> Tuple[OptionSpecSeq, Option mpat = re.compile('([a-z]+)=(.+)') current_cmd: OptionDict = { 'dest': '', 'aliases': frozenset(), 'help': '', 'choices': frozenset(), - 'type': '', 'condition': False, 'default': None, 'completion': {}, + 'type': '', 'condition': False, 'default': None, 'completion': {}, 'name': '' } empty_cmd = current_cmd @@ -261,9 +262,10 @@ def parse_option_spec(spec: Optional[str] = None) -> Tuple[OptionSpecSeq, Option continue if line.startswith('--'): parts = line.split(' ') + defdest = parts[0][2:].replace('-', '_') current_cmd = { - 'dest': parts[0][2:].replace('-', '_'), 'aliases': frozenset(parts), 'help': '', - 'choices': frozenset(), 'type': '', + 'dest': defdest, 'aliases': frozenset(parts), 'help': '', + 'choices': frozenset(), 'type': '', 'name': defdest, 'default': None, 'condition': True, 'completion': {} } state = METADATA diff --git a/tools/cli/infrastructure.go b/tools/cli/infrastructure.go index ecdfae9fa..67afc416c 100644 --- a/tools/cli/infrastructure.go +++ b/tools/cli/infrastructure.go @@ -362,6 +362,10 @@ func show_usage(cmd *cobra.Command, use_pager bool) error { return nil } +func FlagNormalizer(name string) string { + return strings.ReplaceAll(name, "_", "-") +} + func CreateCommand(cmd *cobra.Command) *cobra.Command { cmd.Annotations = make(map[string]string) if cmd.Run == nil && cmd.RunE == nil { @@ -379,6 +383,10 @@ func CreateCommand(cmd *cobra.Command) *cobra.Command { cmd.SilenceUsage = true cmd.PersistentFlags().SortFlags = false cmd.Flags().SortFlags = false + cmd.Flags().SetNormalizeFunc(func(fs *pflag.FlagSet, name string) pflag.NormalizedName { + return pflag.NormalizedName(FlagNormalizer(name)) + }) + cmd.PersistentFlags().SetNormalizeFunc(cmd.Flags().GetNormalizeFunc()) return cmd } @@ -402,9 +410,6 @@ func Init(root *cobra.Command) { root.SetHelpFunc(show_help) root.SetHelpCommand(&cobra.Command{Hidden: true}) root.CompletionOptions.DisableDefaultCmd = true - root.SetGlobalNormalizationFunc(func(fs *pflag.FlagSet, name string) pflag.NormalizedName { - return pflag.NormalizedName(strings.ReplaceAll(name, "_", "-")) - }) } func Execute(root *cobra.Command) error { diff --git a/tools/cmd/at/template.go b/tools/cmd/at/template.go index 34f2917d0..c118a767d 100644 --- a/tools/cmd/at/template.go +++ b/tools/cmd/at/template.go @@ -6,6 +6,7 @@ package at import ( "github.com/spf13/cobra" + "github.com/spf13/pflag" "kitty/tools/cli" "kitty/tools/utils" @@ -25,6 +26,12 @@ func run_CMD_NAME(cmd *cobra.Command, args []string) (err error) { return } +func aliasNormalizeFunc_CMD_NAME(f *pflag.FlagSet, name string) pflag.NormalizedName { + name = cli.FlagNormalizer(name) + ALIAS_NORMALIZE_CODE + return pflag.NormalizedName(name) +} + func setup_CMD_NAME(root *cobra.Command) *cobra.Command { ans := cli.CreateCommand(&cobra.Command{ Use: "CLI_NAME [options]", @@ -33,6 +40,7 @@ func setup_CMD_NAME(root *cobra.Command) *cobra.Command { RunE: run_CMD_NAME, }) ADD_FLAGS_CODE + ans.Flags().SetNormalizeFunc(aliasNormalizeFunc_CMD_NAME) return ans }