Handle aliased flags

This commit is contained in:
Kovid Goyal
2022-08-17 16:33:41 +05:30
parent 2ca8ae8e5f
commit a32251cab4
4 changed files with 50 additions and 12 deletions

View File

@@ -4,7 +4,7 @@
import os import os
import subprocess import subprocess
import sys import sys
from typing import List from typing import Dict, List, Tuple
import kitty.constants as kc import kitty.constants as kc
from kitty.cli import OptionDict, OptionSpecSeq, parse_option_spec from kitty.cli import OptionDict, OptionSpecSeq, parse_option_spec
@@ -21,22 +21,29 @@ def replace(template: str, **kw: str) -> str:
return template return template
go_type_map = {'bool-set': 'bool', 'int': 'int', 'float': 'float64', '': 'string', 'list': '[]string', 'choices': 'string'}
class Option: class Option:
def __init__(self, x: OptionDict) -> None: def __init__(self, x: OptionDict) -> None:
flags = sorted(x['aliases'], key=len) flags = sorted(x['aliases'], key=len)
short = '' short = ''
self.aliases = []
if len(flags) > 1 and not flags[0].startswith("--"): if len(flags) > 1 and not flags[0].startswith("--"):
short = flags[0][1:] short = flags[0][1:]
long = flags[-1][2:] del flags[0]
if not long: self.short, self.long = short, x['name'].replace('_', '-')
raise TypeError(f'No long flag for {x} with flags {flags}') for f in flags:
self.short, self.long = short, long q = f[2:]
if q != self.long:
self.aliases.append(q)
self.usage = serialize_as_go_string(x['help'].strip()) self.usage = serialize_as_go_string(x['help'].strip())
self.type = x['type'] self.type = x['type']
self.dest = x['dest'] self.dest = x['dest']
self.default = x['default'] self.default = x['default']
self.obj_dict = x self.obj_dict = x
self.go_type = go_type_map[self.type]
def to_flag_definition(self, base: str = 'ans.Flags()') -> str: def to_flag_definition(self, base: str = 'ans.Flags()') -> str:
if self.type == 'bool-set': if self.type == 'bool-set':
@@ -73,18 +80,33 @@ class Option:
raise TypeError(f'Unknown type of CLI option: {self.type} for {self.long}') 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: def build_go_code(name: str, cmd: RemoteCommand, seq: OptionSpecSeq, template: str) -> str:
template = '\n' + template[len('//go:build exclude'):] template = '\n' + template[len('//go:build exclude'):]
NO_RESPONSE_BASE = 'true' if cmd.no_response else 'false' NO_RESPONSE_BASE = 'true' if cmd.no_response else 'false'
af: List[str] = [] af: List[str] = []
a = af.append a = af.append
alias_map = {}
for x in seq: for x in seq:
if isinstance(x, str): if isinstance(x, str):
continue continue
o = Option(x) o = Option(x)
if o.aliases:
alias_map[o.long] = tuple(o.aliases)
a(o.to_flag_definition()) a(o.to_flag_definition())
if o.dest == 'no_response': if o.dest == 'no_response':
continue continue
ans = replace( ans = replace(
template, template,
CMD_NAME=name, __FILE__=__file__, CLI_NAME=name.replace('_', '-'), 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()), LONG_DESC=serialize_as_go_string(cmd.desc.strip()),
NO_RESPONSE_BASE=NO_RESPONSE_BASE, ADD_FLAGS_CODE='\n'.join(af), NO_RESPONSE_BASE=NO_RESPONSE_BASE, ADD_FLAGS_CODE='\n'.join(af),
WAIT_TIMEOUT=str(cmd.response_timeout), WAIT_TIMEOUT=str(cmd.response_timeout),
ALIAS_NORMALIZE_CODE=render_alias_map(alias_map)
) )
return ans return ans
@@ -122,7 +145,7 @@ var IsFrozenBuild bool = false
template = f.read() template = f.read()
for name in all_command_names(): for name in all_command_names():
cmd = command_for_name(name) 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) code = build_go_code(name, cmd, opts, template)
dest = f'tools/cmd/at/{name}_generated.go' dest = f'tools/cmd/at/{name}_generated.go'
if os.path.exists(dest): if os.path.exists(dest):

View File

@@ -26,6 +26,7 @@ from .typing import BadLineType, TypedDict
class OptionDict(TypedDict): class OptionDict(TypedDict):
dest: str dest: str
name: str
aliases: FrozenSet[str] aliases: FrozenSet[str]
help: str help: str
choices: FrozenSet[str] choices: FrozenSet[str]
@@ -244,7 +245,7 @@ def parse_option_spec(spec: Optional[str] = None) -> Tuple[OptionSpecSeq, Option
mpat = re.compile('([a-z]+)=(.+)') mpat = re.compile('([a-z]+)=(.+)')
current_cmd: OptionDict = { current_cmd: OptionDict = {
'dest': '', 'aliases': frozenset(), 'help': '', 'choices': frozenset(), 'dest': '', 'aliases': frozenset(), 'help': '', 'choices': frozenset(),
'type': '', 'condition': False, 'default': None, 'completion': {}, 'type': '', 'condition': False, 'default': None, 'completion': {}, 'name': ''
} }
empty_cmd = current_cmd empty_cmd = current_cmd
@@ -261,9 +262,10 @@ def parse_option_spec(spec: Optional[str] = None) -> Tuple[OptionSpecSeq, Option
continue continue
if line.startswith('--'): if line.startswith('--'):
parts = line.split(' ') parts = line.split(' ')
defdest = parts[0][2:].replace('-', '_')
current_cmd = { current_cmd = {
'dest': parts[0][2:].replace('-', '_'), 'aliases': frozenset(parts), 'help': '', 'dest': defdest, 'aliases': frozenset(parts), 'help': '',
'choices': frozenset(), 'type': '', 'choices': frozenset(), 'type': '', 'name': defdest,
'default': None, 'condition': True, 'completion': {} 'default': None, 'condition': True, 'completion': {}
} }
state = METADATA state = METADATA

View File

@@ -362,6 +362,10 @@ func show_usage(cmd *cobra.Command, use_pager bool) error {
return nil return nil
} }
func FlagNormalizer(name string) string {
return strings.ReplaceAll(name, "_", "-")
}
func CreateCommand(cmd *cobra.Command) *cobra.Command { func CreateCommand(cmd *cobra.Command) *cobra.Command {
cmd.Annotations = make(map[string]string) cmd.Annotations = make(map[string]string)
if cmd.Run == nil && cmd.RunE == nil { if cmd.Run == nil && cmd.RunE == nil {
@@ -379,6 +383,10 @@ func CreateCommand(cmd *cobra.Command) *cobra.Command {
cmd.SilenceUsage = true cmd.SilenceUsage = true
cmd.PersistentFlags().SortFlags = false cmd.PersistentFlags().SortFlags = false
cmd.Flags().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 return cmd
} }
@@ -402,9 +410,6 @@ func Init(root *cobra.Command) {
root.SetHelpFunc(show_help) root.SetHelpFunc(show_help)
root.SetHelpCommand(&cobra.Command{Hidden: true}) root.SetHelpCommand(&cobra.Command{Hidden: true})
root.CompletionOptions.DisableDefaultCmd = 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 { func Execute(root *cobra.Command) error {

View File

@@ -6,6 +6,7 @@ package at
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag"
"kitty/tools/cli" "kitty/tools/cli"
"kitty/tools/utils" "kitty/tools/utils"
@@ -25,6 +26,12 @@ func run_CMD_NAME(cmd *cobra.Command, args []string) (err error) {
return 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 { func setup_CMD_NAME(root *cobra.Command) *cobra.Command {
ans := cli.CreateCommand(&cobra.Command{ ans := cli.CreateCommand(&cobra.Command{
Use: "CLI_NAME [options]", Use: "CLI_NAME [options]",
@@ -33,6 +40,7 @@ func setup_CMD_NAME(root *cobra.Command) *cobra.Command {
RunE: run_CMD_NAME, RunE: run_CMD_NAME,
}) })
ADD_FLAGS_CODE ADD_FLAGS_CODE
ans.Flags().SetNormalizeFunc(aliasNormalizeFunc_CMD_NAME)
return ans return ans
} }