diff --git a/gen-rc-go.py b/gen-rc-go.py index ca62aa627..33079e680 100755 --- a/gen-rc-go.py +++ b/gen-rc-go.py @@ -95,6 +95,40 @@ class Option: return ans +json_field_types: Dict[str, str] = { + 'bool': 'bool', 'str': 'string', 'list.str': '[]string', 'dict.str': 'map[string]string', 'float': 'float64', 'int': 'int', + 'scroll_amount': '[2]interface{}', 'spacing': 'interface{}', 'colors': 'interface{}', +} + + +def go_field_type(json_field_type: str) -> str: + q = json_field_types.get(json_field_type) + if q: + return q + if json_field_type.startswith('choices.'): + return 'string' + if '.' in json_field_type: + p, r = json_field_type.split('.', 1) + p = {'list': '[]', 'dict': 'map[string]'}[p] + return p + go_field_type(r) + raise TypeError(f'Unknown JSON field type: {json_field_type}') + + +class JSONField: + + def __init__(self, line: str) -> None: + field_def = line.split(':', 1)[0] + self.required = False + self.field, self.field_type = field_def.split('/', 1) + if self.field.endswith('+'): + self.required = True + self.field = self.field[:-1] + self.struct_field_name = self.field[0].upper() + self.field[1:] + + def go_declaration(self) -> str: + return self.struct_field_name + ' ' + go_field_type(self.field_type) + f'`json:"{self.field},omitempty"`' + + def render_alias_map(alias_map: Dict[str, Tuple[str, ...]]) -> str: if not alias_map: return '' @@ -125,6 +159,13 @@ def build_go_code(name: str, cmd: RemoteCommand, seq: OptionSpecSeq, template: s continue od.append(f'{o.go_var_name} {o.go_type}') ov.append(o.set_flag_value()) + jd: List[str] = [] + for line in cmd.protocol_spec.splitlines(): + line = line.strip() + if ':' not in line: + continue + f = JSONField(line) + jd.append(f.go_declaration()) ans = replace( template, @@ -136,6 +177,7 @@ def build_go_code(name: str, cmd: RemoteCommand, seq: OptionSpecSeq, template: s ALIAS_NORMALIZE_CODE=render_alias_map(alias_map), OPTIONS_DECLARATION_CODE='\n'.join(od), SET_OPTION_VALUES_CODE='\n'.join(ov), + JSON_DECLARATION_CODE='\n'.join(jd), ) return ans diff --git a/kitty/rc/__init__.py b/kitty/rc/__init__.py index e69de29bb..8b1378917 100644 --- a/kitty/rc/__init__.py +++ b/kitty/rc/__init__.py @@ -0,0 +1 @@ + diff --git a/kitty/rc/base.py b/kitty/rc/base.py index 3fc94341f..c7230623d 100644 --- a/kitty/rc/base.py +++ b/kitty/rc/base.py @@ -175,6 +175,7 @@ class RemoteCommand: defaults: Optional[Dict[str, Any]] = None is_asynchronous: bool = False options_class: Type[RCOptions] = RCOptions + protocol_spec: str = '' def __init__(self) -> None: self.desc = self.desc or self.short_desc diff --git a/kitty/rc/close_tab.py b/kitty/rc/close_tab.py index f3e16567a..f21a4e5ba 100644 --- a/kitty/rc/close_tab.py +++ b/kitty/rc/close_tab.py @@ -15,7 +15,7 @@ if TYPE_CHECKING: class CloseTab(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' 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 diff --git a/kitty/rc/close_window.py b/kitty/rc/close_window.py index e7066edbc..e87cf4cb6 100644 --- a/kitty/rc/close_window.py +++ b/kitty/rc/close_window.py @@ -14,7 +14,7 @@ if TYPE_CHECKING: class CloseWindow(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' 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 diff --git a/kitty/rc/create_marker.py b/kitty/rc/create_marker.py index 41903dc39..5da4ede5a 100644 --- a/kitty/rc/create_marker.py +++ b/kitty/rc/create_marker.py @@ -16,7 +16,7 @@ if TYPE_CHECKING: class CreateMarker(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' match/str: Which window to create the marker in self/bool: Boolean indicating whether to create marker in the window the command is run in marker_spec/list.str: A list or arguments that define the marker specification, for example: ['text', '1', 'ERROR'] diff --git a/kitty/rc/detach_tab.py b/kitty/rc/detach_tab.py index 3a27b815a..90da785d6 100644 --- a/kitty/rc/detach_tab.py +++ b/kitty/rc/detach_tab.py @@ -14,7 +14,7 @@ if TYPE_CHECKING: class DetachTab(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' match/str: Which tab to detach target_tab/str: Which tab to move the detached tab to the OS window it is run in self/bool: Boolean indicating whether to detach the tab the command is run in diff --git a/kitty/rc/detach_window.py b/kitty/rc/detach_window.py index 16f1a2f39..b1f36cd0f 100644 --- a/kitty/rc/detach_window.py +++ b/kitty/rc/detach_window.py @@ -15,7 +15,7 @@ if TYPE_CHECKING: class DetachWindow(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' match/str: Which window to detach target_tab/str: Which tab to move the detached window to self/bool: Boolean indicating whether to detach the window the command is run in diff --git a/kitty/rc/disable_ligatures.py b/kitty/rc/disable_ligatures.py index 3c75962f5..99c6f00ad 100644 --- a/kitty/rc/disable_ligatures.py +++ b/kitty/rc/disable_ligatures.py @@ -15,7 +15,7 @@ if TYPE_CHECKING: class DisableLigatures(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' strategy+/choices.never.always.cursor: One of :code:`never`, :code:`always` or :code:`cursor` match_window/str: Window to change opacity in match_tab/str: Tab to change opacity in diff --git a/kitty/rc/env.py b/kitty/rc/env.py index a59e7152e..6fd847b43 100644 --- a/kitty/rc/env.py +++ b/kitty/rc/env.py @@ -11,7 +11,7 @@ from .base import ( class Env(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' env+/dict.str: Dictionary of environment variables to values. Empty values cause the variable to be removed. ''' diff --git a/kitty/rc/focus_tab.py b/kitty/rc/focus_tab.py index ef73b4237..344d832a5 100644 --- a/kitty/rc/focus_tab.py +++ b/kitty/rc/focus_tab.py @@ -15,7 +15,7 @@ if TYPE_CHECKING: class FocusTab(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' match/str: The tab to focus no_response/bool: Boolean indicating whether to wait for a response ''' diff --git a/kitty/rc/focus_window.py b/kitty/rc/focus_window.py index d41428ac5..7c39238e1 100644 --- a/kitty/rc/focus_window.py +++ b/kitty/rc/focus_window.py @@ -16,7 +16,7 @@ if TYPE_CHECKING: class FocusWindow(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' match/str: The window to focus no_response/bool: Boolean indicating whether to wait for a response ''' diff --git a/kitty/rc/get_colors.py b/kitty/rc/get_colors.py index 034a27378..35076fb8c 100644 --- a/kitty/rc/get_colors.py +++ b/kitty/rc/get_colors.py @@ -18,7 +18,7 @@ if TYPE_CHECKING: class GetColors(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' match/str: The window to get the colors for configured/bool: Boolean indicating whether to get configured or current colors ''' diff --git a/kitty/rc/get_text.py b/kitty/rc/get_text.py index d70f48865..af18ccd53 100644 --- a/kitty/rc/get_text.py +++ b/kitty/rc/get_text.py @@ -15,7 +15,7 @@ if TYPE_CHECKING: class GetText(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' match/str: The window to get text from extent/choices.screen.first_cmd_output_on_screen.last_cmd_output.last_visited_cmd_output.all.selection: \ One of :code:`screen`, :code:`first_cmd_output_on_screen`, :code:`last_cmd_output`, \ diff --git a/kitty/rc/goto_layout.py b/kitty/rc/goto_layout.py index 8254d8bf7..398f6868f 100644 --- a/kitty/rc/goto_layout.py +++ b/kitty/rc/goto_layout.py @@ -19,7 +19,7 @@ def layout_names() -> Iterable[str]: class GotoLayout(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' layout+/str: The new layout name match/str: Which tab to change the layout of ''' diff --git a/kitty/rc/kitten.py b/kitty/rc/kitten.py index 85f278ade..0d6399fb3 100644 --- a/kitty/rc/kitten.py +++ b/kitty/rc/kitten.py @@ -14,7 +14,7 @@ if TYPE_CHECKING: class Kitten(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' kitten+/str: The name of the kitten to run args/list.str: Arguments to pass to the kitten as a list match/str: The window to run the kitten over diff --git a/kitty/rc/last_used_layout.py b/kitty/rc/last_used_layout.py index 2741732d6..7ca5d1604 100644 --- a/kitty/rc/last_used_layout.py +++ b/kitty/rc/last_used_layout.py @@ -14,7 +14,7 @@ if TYPE_CHECKING: class LastUsedLayout(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' match/str: Which tab to change the layout of all/bool: Boolean to match all tabs no_response/bool: Boolean indicating whether to wait for a response diff --git a/kitty/rc/launch.py b/kitty/rc/launch.py index 779325337..c56bb439e 100644 --- a/kitty/rc/launch.py +++ b/kitty/rc/launch.py @@ -21,7 +21,7 @@ if TYPE_CHECKING: class Launch(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' args+/list.str: The command line to run in the new window, as a list, use an empty list to run the default shell match/str: The tab to open the new window in window_title/str: Title for the new window @@ -36,7 +36,7 @@ class Launch(RemoteCommand): hold/bool: Boolean indicating whether to keep window open after cmd exits location/choices.first.after.before.neighbor.last.vsplit.hsplit.split.default: Where in the tab to open the new window allow_remote_control/bool: Boolean indicating whether to allow remote control from the new window - remote_control_password/list/str: A list of remote control passwords + remote_control_password/list.str: A list of remote control passwords stdin_source/choices.none.@selection.@screen.@screen_scrollback.@alternate.@alternate_scrollback.\ @first_cmd_output_on_screen.@last_cmd_output.@last_visited_cmd_output: Where to get stdin for the process from stdin_add_formatting/bool: Boolean indicating whether to add formatting codes to stdin diff --git a/kitty/rc/ls.py b/kitty/rc/ls.py index 5ea3ee948..e7ffed755 100644 --- a/kitty/rc/ls.py +++ b/kitty/rc/ls.py @@ -16,7 +16,7 @@ if TYPE_CHECKING: class LS(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' all_env_vars/bool: Whether to send all environment variables for every window rather than just differing ones ''' diff --git a/kitty/rc/new_window.py b/kitty/rc/new_window.py index 3b2eb9774..89146c03f 100644 --- a/kitty/rc/new_window.py +++ b/kitty/rc/new_window.py @@ -14,7 +14,7 @@ if TYPE_CHECKING: class NewWindow(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' args+/list.str: The command line to run in the new window, as a list, use an empty list to run the default shell match/str: The tab to open the new window in title/str: Title for the new window diff --git a/kitty/rc/remove_marker.py b/kitty/rc/remove_marker.py index 2840e7c32..16a6f0291 100644 --- a/kitty/rc/remove_marker.py +++ b/kitty/rc/remove_marker.py @@ -15,7 +15,7 @@ if TYPE_CHECKING: class RemoveMarker(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' match/str: Which window to remove the marker from self/bool: Boolean indicating whether to detach the window the command is run in ''' diff --git a/kitty/rc/resize_os_window.py b/kitty/rc/resize_os_window.py index fc9b91f09..7b43b288c 100644 --- a/kitty/rc/resize_os_window.py +++ b/kitty/rc/resize_os_window.py @@ -13,7 +13,7 @@ if TYPE_CHECKING: class ResizeOSWindow(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' match/str: Which window to resize self/bool: Boolean indicating whether to close the window the command is run in incremental/bool: Boolean indicating whether to adjust the size incrementally diff --git a/kitty/rc/resize_window.py b/kitty/rc/resize_window.py index cc375c2f3..00e10b7e5 100644 --- a/kitty/rc/resize_window.py +++ b/kitty/rc/resize_window.py @@ -13,7 +13,7 @@ if TYPE_CHECKING: class ResizeWindow(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' match/str: Which window to resize self/bool: Boolean indicating whether to resize the window the command is run in increment/int: Integer specifying the resize increment diff --git a/kitty/rc/scroll_window.py b/kitty/rc/scroll_window.py index b473dde05..b8d0f26bd 100644 --- a/kitty/rc/scroll_window.py +++ b/kitty/rc/scroll_window.py @@ -15,7 +15,7 @@ if TYPE_CHECKING: class ScrollWindow(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' amount+/list.scroll_amount: The amount to scroll, a two item list with the first item being \ either a number or the keywords, start and end. \ And the second item being either 'p' for pages or 'l' for lines or 'u' diff --git a/kitty/rc/select_window.py b/kitty/rc/select_window.py index 3f567d1c9..9f3ae9e4d 100644 --- a/kitty/rc/select_window.py +++ b/kitty/rc/select_window.py @@ -17,7 +17,7 @@ if TYPE_CHECKING: class SelectWindow(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' match/str: The tab to open the new window in self/bool: Boolean, if True use tab the command was run in title/str: A title for this selection diff --git a/kitty/rc/send_text.py b/kitty/rc/send_text.py index 9e3158993..974e57aee 100644 --- a/kitty/rc/send_text.py +++ b/kitty/rc/send_text.py @@ -63,13 +63,13 @@ class FocusChangedSession(SessionAction): class SendText(RemoteCommand): - ''' - data+/send_text: The data being sent. Can be either: text: followed by text or base64: followed by standard base64 encoded bytes + protocol_spec = __doc__ = ''' + data+/str: The data being sent. Can be either: text: followed by text or base64: followed by standard base64 encoded bytes match/str: A string indicating the window to send text to match_tab/str: A string indicating the tab to send text to all/bool: A boolean indicating all windows should be matched. exclude_active/bool: A boolean that prevents sending text to the active window - session_id/internal: A string that identifies a "broadcast session" + session_id/str: A string that identifies a "broadcast session" ''' short_desc = 'Send arbitrary text to specified windows' desc = ( diff --git a/kitty/rc/set_background_image.py b/kitty/rc/set_background_image.py index 38b74319c..25ff18bea 100644 --- a/kitty/rc/set_background_image.py +++ b/kitty/rc/set_background_image.py @@ -23,8 +23,8 @@ layout_choices = 'tiled,scaled,mirror-tiled,clamped,configured' class SetBackgroundImage(RemoteCommand): - f''' - data+/image_data: Chunk of at most 512 bytes of PNG data, base64 encoded. Must send an empty chunk to indicate end of image. \ + protocol_spec = __doc__ = ''' + data+/str: Chunk of at most 512 bytes of PNG data, base64 encoded. Must send an empty chunk to indicate end of image. \ Or the special value - to indicate image must be removed. match/str: Window to change opacity in layout/choices.{layout_choices.replace(",", ".")}: The image layout diff --git a/kitty/rc/set_background_opacity.py b/kitty/rc/set_background_opacity.py index 5dcd3711f..806ea0f20 100644 --- a/kitty/rc/set_background_opacity.py +++ b/kitty/rc/set_background_opacity.py @@ -16,7 +16,7 @@ if TYPE_CHECKING: class SetBackgroundOpacity(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' opacity+/float: A number between 0.1 and 1 match_window/str: Window to change opacity in match_tab/str: Tab to change opacity in diff --git a/kitty/rc/set_colors.py b/kitty/rc/set_colors.py index e89733cc5..af7de43dc 100644 --- a/kitty/rc/set_colors.py +++ b/kitty/rc/set_colors.py @@ -54,7 +54,7 @@ def parse_colors(args: Iterable[str]) -> Dict[str, Optional[int]]: class SetColors(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' colors+/dict.colors: An object mapping names to colors as 24-bit RGB integers or null for nullable colors match_window/str: Window to change colors in match_tab/str: Tab to change colors in diff --git a/kitty/rc/set_enabled_layouts.py b/kitty/rc/set_enabled_layouts.py index 45ac5d4ea..61938f472 100644 --- a/kitty/rc/set_enabled_layouts.py +++ b/kitty/rc/set_enabled_layouts.py @@ -22,7 +22,7 @@ def layout_names() -> Iterable[str]: class SetEnabledLayouts(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' layouts+/list.str: The list of layout names match/str: Which tab to change the layout of configured/bool: Boolean indicating whether to change the configured value diff --git a/kitty/rc/set_font_size.py b/kitty/rc/set_font_size.py index e4e61a2dd..9c680621f 100644 --- a/kitty/rc/set_font_size.py +++ b/kitty/rc/set_font_size.py @@ -13,7 +13,7 @@ if TYPE_CHECKING: class SetFontSize(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' size+/float: The new font size in pts (a positive number) all/bool: Boolean whether to change font size in the current window or all windows increment_op/choices.+.-: The string ``+`` or ``-`` to interpret size as an increment diff --git a/kitty/rc/set_spacing.py b/kitty/rc/set_spacing.py index c37730a7e..60c1cd882 100644 --- a/kitty/rc/set_spacing.py +++ b/kitty/rc/set_spacing.py @@ -67,7 +67,7 @@ def parse_spacing_settings(args: Iterable[str]) -> Dict[str, Optional[float]]: class SetSpacing(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' settings+/dict.spacing: An object mapping margins/paddings using canonical form {'margin-top': 50, 'padding-left': null} etc match_window/str: Window to change paddings and margins in match_tab/str: Tab to change paddings and margins in diff --git a/kitty/rc/set_tab_color.py b/kitty/rc/set_tab_color.py index e7b2e7c72..cb5cb0bc0 100644 --- a/kitty/rc/set_tab_color.py +++ b/kitty/rc/set_tab_color.py @@ -37,7 +37,7 @@ def parse_colors(args: ArgsType) -> Dict[str, Optional[int]]: class SetTabColor(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' colors+/dict.colors: An object mapping names to colors as 24-bit RGB integers. A color value of null indicates it should be unset. match/str: Which tab to change the color of self/bool: Boolean indicating whether to use the tab of the window the command is run in diff --git a/kitty/rc/set_tab_title.py b/kitty/rc/set_tab_title.py index ad889ceb2..b4efa28c1 100644 --- a/kitty/rc/set_tab_title.py +++ b/kitty/rc/set_tab_title.py @@ -15,7 +15,7 @@ if TYPE_CHECKING: class SetTabTitle(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' title+/str: The new title match/str: Which tab to change the title of ''' diff --git a/kitty/rc/set_window_logo.py b/kitty/rc/set_window_logo.py index 4847d8448..915129241 100644 --- a/kitty/rc/set_window_logo.py +++ b/kitty/rc/set_window_logo.py @@ -20,8 +20,8 @@ if TYPE_CHECKING: class SetWindowLogo(RemoteCommand): - ''' - data+/image_data: Chunk of at most 512 bytes of PNG data, base64 encoded. Must send an empty chunk to indicate end of image. \ + protocol_spec = __doc__ = ''' + data+/str: Chunk of at most 512 bytes of PNG data, base64 encoded. Must send an empty chunk to indicate end of image. \ Or the special value :code:`-` to indicate image must be removed. position/str: The logo position as a string, empty string means default alpha/float: The logo alpha between :code:`0` and :code:`1`. :code:`-1` means use default diff --git a/kitty/rc/set_window_title.py b/kitty/rc/set_window_title.py index d64c29c0e..9e32fa86f 100644 --- a/kitty/rc/set_window_title.py +++ b/kitty/rc/set_window_title.py @@ -14,7 +14,7 @@ if TYPE_CHECKING: class SetWindowTitle(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' title/str: The new title match/str: Which windows to change the title in temporary/bool: Boolean indicating if the change is temporary or permanent diff --git a/kitty/rc/signal_child.py b/kitty/rc/signal_child.py index a199d2240..a99daa532 100644 --- a/kitty/rc/signal_child.py +++ b/kitty/rc/signal_child.py @@ -14,7 +14,7 @@ if TYPE_CHECKING: class SignalChild(RemoteCommand): - ''' + protocol_spec = __doc__ = ''' 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 no_response/bool: Boolean indicating whether to wait for a response diff --git a/tools/cmd/at/template.go b/tools/cmd/at/template.go index 32318614a..c4ca4d21d 100644 --- a/tools/cmd/at/template.go +++ b/tools/cmd/at/template.go @@ -18,6 +18,12 @@ type options_CMD_NAME_type struct { var options_CMD_NAME options_CMD_NAME_type +type CMD_NAME_json_type struct { + JSON_DECLARATION_CODE +} + +var CMD_NAME_json CMD_NAME_json_type + func run_CMD_NAME(cmd *cobra.Command, args []string) (err error) { SET_OPTION_VALUES_CODE