Automap a bunch of json fields from identically named options

This commit is contained in:
Kovid Goyal
2022-08-29 12:10:23 +05:30
parent b33a684357
commit ef71b071db
11 changed files with 42 additions and 28 deletions

View File

@@ -4,13 +4,15 @@
import io import io
import json import json
import os import os
import sys
from contextlib import contextmanager, suppress from contextlib import contextmanager, suppress
from typing import Dict, Iterator, List, Tuple, Union from typing import Dict, Iterator, List, Tuple, Union
import kitty.constants as kc import kitty.constants as kc
from kittens.tui.operations import Mode from kittens.tui.operations import Mode
from kitty.cli import ( from kitty.cli import (
GoOption, OptionSpecSeq, parse_option_spec, serialize_as_go_string GoOption, OptionSpecSeq, go_options_for_seq, parse_option_spec,
serialize_as_go_string
) )
from kitty.key_encoding import config_mod_map from kitty.key_encoding import config_mod_map
from kitty.key_names import ( from kitty.key_names import (
@@ -98,24 +100,34 @@ def build_go_code(name: str, cmd: RemoteCommand, seq: OptionSpecSeq, template: s
alias_map = {} alias_map = {}
od: List[str] = [] od: List[str] = []
ov: List[str] = [] ov: List[str] = []
for x in seq: option_map: Dict[str, GoOption] = {}
if isinstance(x, str): for o in go_options_for_seq(seq):
continue field_dest = o.go_var_name.rstrip('_')
o = GoOption(name, x) option_map[field_dest] = o
if o.aliases: if o.aliases:
alias_map[o.long] = tuple(o.aliases) alias_map[o.long] = tuple(o.aliases)
a(o.to_flag_definition()) a(o.to_flag_definition())
if o.dest in ('no_response', 'response_timeout'): if o.dest in ('no_response', 'response_timeout'):
continue continue
od.append(f'{o.go_var_name} {o.go_type}') od.append(f'{o.go_var_name} {o.go_type}')
ov.append(o.set_flag_value()) ov.append(o.set_flag_value(f'options_{name}'))
jd: List[str] = [] jd: List[str] = []
json_fields = []
for line in cmd.protocol_spec.splitlines(): for line in cmd.protocol_spec.splitlines():
line = line.strip() line = line.strip()
if ':' not in line: if ':' not in line:
continue continue
f = JSONField(line) f = JSONField(line)
json_fields.append(f)
jd.append(f.go_declaration()) jd.append(f.go_declaration())
jc: List[str] = []
for field in json_fields:
if field.field in option_map:
o = option_map[field.field]
jc.append(f'payload.{field.struct_field_name} = options_{name}.{o.go_var_name}')
else:
print(f'Cant map field: {field.field} for cmd: {name}', file=sys.stderr)
continue
ans = replace( ans = replace(
template, template,
@@ -129,6 +141,7 @@ def build_go_code(name: str, cmd: RemoteCommand, seq: OptionSpecSeq, template: s
OPTIONS_DECLARATION_CODE='\n'.join(od), OPTIONS_DECLARATION_CODE='\n'.join(od),
SET_OPTION_VALUES_CODE='\n'.join(ov), SET_OPTION_VALUES_CODE='\n'.join(ov),
JSON_DECLARATION_CODE='\n'.join(jd), JSON_DECLARATION_CODE='\n'.join(jd),
JSON_INIT_CODE='\n'.join(jc),
STRING_RESPONSE_IS_ERROR='true' if cmd.string_return_is_error else 'false', STRING_RESPONSE_IS_ERROR='true' if cmd.string_return_is_error else 'false',
) )
return ans return ans

View File

@@ -49,8 +49,7 @@ go_getter_map = {
class GoOption: class GoOption:
def __init__(self, cmd_name: str, x: OptionDict) -> None: def __init__(self, x: OptionDict) -> None:
self.cmd_name = cmd_name
flags = sorted(x['aliases'], key=len) flags = sorted(x['aliases'], key=len)
short = '' short = ''
self.aliases = [] self.aliases = []
@@ -107,13 +106,19 @@ class GoOption:
else: else:
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 set_flag_value(self, cmd: str = 'cmd') -> str: def set_flag_value(self, struct_name: str, cmd: str = 'cmd') -> str:
func = go_getter_map[self.type] func = go_getter_map[self.type]
ans = f'{self.go_var_name}_temp, err := {cmd}.Flags().{func}("{self.long}")\n if err != nil {{ return err }}' ans = f'{self.go_var_name}_temp, err := {cmd}.Flags().{func}("{self.long}")\n if err != nil {{ return err }}'
ans += f'\noptions_{self.cmd_name}.{self.go_var_name} = {self.go_var_name}_temp' ans += f'\n{struct_name}.{self.go_var_name} = {self.go_var_name}_temp'
return ans return ans
def go_options_for_seq(seq: 'OptionSpecSeq') -> Iterator[GoOption]:
for x in seq:
if not isinstance(x, str):
yield GoOption(x)
CONFIG_HELP = '''\ CONFIG_HELP = '''\
Specify a path to the configuration file(s) to use. All configuration files are Specify a path to the configuration file(s) to use. All configuration files are
merged onto the builtin :file:`{conf_name}.conf`, overriding the builtin values. merged onto the builtin :file:`{conf_name}.conf`, overriding the builtin values.

View File

@@ -17,7 +17,6 @@ class CloseTab(RemoteCommand):
protocol_spec = __doc__ = ''' protocol_spec = __doc__ = '''
match/str: Which tab to close match/str: Which tab to close
no_response/bool: Boolean indicating whether to wait for a response
self/bool: Boolean indicating whether to close the tab of the window the command is run in self/bool: Boolean indicating whether to close the tab of the window the command is run in
ignore_no_match/bool: Boolean indicating whether no matches should be ignored or return an error ignore_no_match/bool: Boolean indicating whether no matches should be ignored or return an error
''' '''

View File

@@ -16,7 +16,6 @@ if TYPE_CHECKING:
class CloseWindow(RemoteCommand): class CloseWindow(RemoteCommand):
protocol_spec = __doc__ = ''' protocol_spec = __doc__ = '''
match/str: Which window to close match/str: Which window to close
no_response/bool: Boolean indicating whether to wait for a response
self/bool: Boolean indicating whether to close the window the command is run in self/bool: Boolean indicating whether to close the window the command is run in
ignore_no_match/bool: Boolean indicating whether no matches should be ignored or return an error ignore_no_match/bool: Boolean indicating whether no matches should be ignored or return an error
''' '''

View File

@@ -60,6 +60,7 @@ screen edges).
--clear-selection --clear-selection
type=bool-set
Clear the selection in the matched window, if any. Clear the selection in the matched window, if any.

View File

@@ -42,7 +42,6 @@ class Launch(RemoteCommand):
stdin_add_formatting/bool: Boolean indicating whether to add formatting codes to stdin stdin_add_formatting/bool: Boolean indicating whether to add formatting codes to stdin
stdin_add_line_wrap_markers/bool: Boolean indicating whether to add line wrap markers to stdin stdin_add_line_wrap_markers/bool: Boolean indicating whether to add line wrap markers to stdin
spacing/list.str: A list of spacing specifications, see the docs for the set-spacing command spacing/list.str: A list of spacing specifications, see the docs for the set-spacing command
no_response/bool: Boolean indicating whether to send back the window id
marker/str: Specification for marker for new window, for example: "text 1 ERROR" marker/str: Specification for marker for new window, for example: "text 1 ERROR"
logo/str: Path to window logo logo/str: Path to window logo
logo_position/str: Window logo position as string or empty string to use default logo_position/str: Window logo position as string or empty string to use default

View File

@@ -23,7 +23,6 @@ class NewWindow(RemoteCommand):
window_type/choices.kitty.os: One of :code:`kitty` or :code:`os` window_type/choices.kitty.os: One of :code:`kitty` or :code:`os`
new_tab/bool: Boolean indicating whether to open a new tab new_tab/bool: Boolean indicating whether to open a new tab
tab_title/str: Title for the new tab tab_title/str: Title for the new tab
no_response/bool: Boolean indicating whether to send back the window id
''' '''
short_desc = 'Open new window' short_desc = 'Open new window'

View File

@@ -21,7 +21,6 @@ class ScrollWindow(RemoteCommand):
And the second item being either 'p' for pages or 'l' for lines or 'u' And the second item being either 'p' for pages or 'l' for lines or 'u'
for unscrolling by lines. for unscrolling by lines.
match/str: The window to scroll match/str: The window to scroll
no_response/bool: Boolean indicating whether to wait for a response
''' '''
short_desc = 'Scroll the specified windows' short_desc = 'Scroll the specified windows'

View File

@@ -17,7 +17,6 @@ class SignalChild(RemoteCommand):
protocol_spec = __doc__ = ''' protocol_spec = __doc__ = '''
signals/list.str: The signals, a list of names, such as :code:`SIGTERM`, :code:`SIGKILL`, :code:`SIGUSR1`, etc. signals/list.str: The signals, a list of names, such as :code:`SIGTERM`, :code:`SIGKILL`, :code:`SIGUSR1`, etc.
match/str: Which windows to send the signals to match/str: Which windows to send the signals to
no_response/bool: Boolean indicating whether to wait for a response
''' '''
short_desc = 'Send a signal to the foreground process in the specified windows' short_desc = 'Send a signal to the foreground process in the specified windows'

View File

@@ -26,9 +26,10 @@ type CMD_NAME_json_type struct {
JSON_DECLARATION_CODE JSON_DECLARATION_CODE
} }
var CMD_NAME_json CMD_NAME_json_type func create_payload_CMD_NAME(io_data *rc_io_data, flags *pflag.FlagSet, args []string) (err error) {
payload := CMD_NAME_json_type{}
func create_payload_CMD_NAME(io_data *rc_io_data, args []string) (err error) { JSON_INIT_CODE
io_data.rc.Payload = payload
return return
} }
@@ -70,7 +71,7 @@ func run_CMD_NAME(cmd *cobra.Command, args []string) (err error) {
timeout: time.Duration(timeout * float64(time.Second)), timeout: time.Duration(timeout * float64(time.Second)),
string_response_is_err: STRING_RESPONSE_IS_ERROR, string_response_is_err: STRING_RESPONSE_IS_ERROR,
} }
err = create_payload_CMD_NAME(&io_data, args) err = create_payload_CMD_NAME(&io_data, cmd.Flags(), args)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -3,14 +3,14 @@
package utils package utils
type RemoteControlCmd struct { type RemoteControlCmd struct {
Cmd string `json:"cmd"` Cmd string `json:"cmd"`
Version [3]int `json:"version"` Version [3]int `json:"version"`
NoResponse bool `json:"no_response,omitempty"` NoResponse bool `json:"no_response,omitempty"`
Payload *interface{} `json:"payload,omitempty"` Payload interface{} `json:"payload,omitempty"`
Timestamp int64 `json:"timestamp,omitempty"` Timestamp int64 `json:"timestamp,omitempty"`
Password string `json:"password,omitempty"` Password string `json:"password,omitempty"`
Async string `json:"async,omitempty"` Async string `json:"async,omitempty"`
CancelAsync bool `json:"cancel_async,omitempty"` CancelAsync bool `json:"cancel_async,omitempty"`
} }
type EncryptedRemoteControlCmd struct { type EncryptedRemoteControlCmd struct {