mirror of
https://gitlab.com/luk3yx/minetest-formspec_ast.git
synced 2025-11-08 19:47:58 +01:00
Improve make_elements.py and fix some issues.
This commit is contained in:
parent
649acafff3
commit
9f28d8cae3
18
README.md
18
README.md
@ -40,13 +40,25 @@ A Minetest mod library to make modifying formspecs easier.
|
||||
The AST is similar (and generated from) [the formspec element list], however
|
||||
all attributes are lowercase.
|
||||
|
||||
[the formspec element list]: https://github.com/minetest/minetest/blob/dee2210/doc/lua_api.txt#L1959
|
||||
[the formspec element list]: https://minetest.gitlab.io/minetest/formspec/#elements
|
||||
|
||||
### Recent backwards incompatibilities
|
||||
|
||||
While I try to reduce backwards incompatibilities, sometimes they are necessary
|
||||
to either fix bugs in formspec_ast or for implementing new formspec features.
|
||||
|
||||
#### February 2021
|
||||
|
||||
- The `close_on_enter` value for `field_close_on_enter` is now a boolean
|
||||
instead of a string.
|
||||
- The `frame_count`, `frame_duration` and `frame_start` values in
|
||||
`animated_image` are now numbers.
|
||||
|
||||
#### September 2020
|
||||
|
||||
- The `style[]` element has a `selectors` field instead of `name`. Using
|
||||
`name` when unparsing formspecs still works, however parsed formspecs
|
||||
always use `selectors`.
|
||||
`name` when unparsing formspecs still works, however parsed formspecs
|
||||
always use `selectors`.
|
||||
|
||||
### Special cases
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
465
elements.yaml
Normal file
465
elements.yaml
Normal file
@ -0,0 +1,465 @@
|
||||
#
|
||||
# This file is automatically generated by make_elements.py and isn't actually
|
||||
# used by formspec_ast, however it is useful for comparing changes across
|
||||
# lua_api versions.
|
||||
#
|
||||
|
||||
anchor:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
animated_image:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [name, string]
|
||||
- [texture_name, string]
|
||||
- [frame_count, number]
|
||||
- [frame_duration, number]
|
||||
- [frame_start, number]
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [name, string]
|
||||
- [texture_name, string]
|
||||
- [frame_count, number]
|
||||
- [frame_duration, number]
|
||||
background:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [texture_name, string]
|
||||
- [auto_clip, boolean]
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [texture_name, string]
|
||||
background9:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [texture_name, string]
|
||||
- [auto_clip, boolean]
|
||||
- - [middle_x, number]
|
||||
- [middle_y, number]
|
||||
- [middle_x2, number]
|
||||
- [middle_y2, number]
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [texture_name, string]
|
||||
- [auto_clip, boolean]
|
||||
- - [middle_x, number]
|
||||
- [middle_y, number]
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [texture_name, string]
|
||||
- [auto_clip, boolean]
|
||||
- - [middle_x, number]
|
||||
bgcolor:
|
||||
- - [bgcolor, string]
|
||||
- [fullscreen, boolean]
|
||||
- [fbgcolor, string]
|
||||
- - [bgcolor, string]
|
||||
- [fullscreen, boolean]
|
||||
- - [bgcolor, string]
|
||||
box:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [color, string]
|
||||
button:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [name, string]
|
||||
- [label, string]
|
||||
button_exit:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [name, string]
|
||||
- [label, string]
|
||||
checkbox:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- [name, string]
|
||||
- [label, string]
|
||||
- [selected, boolean]
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- [name, string]
|
||||
- [label, string]
|
||||
container:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
container_end:
|
||||
- []
|
||||
dropdown:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [name, string]
|
||||
- - - [item, string]
|
||||
- '...'
|
||||
- [selected_idx, number]
|
||||
- [index_event, string]
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- [w, number]
|
||||
- [name, string]
|
||||
- - - [item, string]
|
||||
- '...'
|
||||
- [selected_idx, number]
|
||||
- [index_event, string]
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [name, string]
|
||||
- - - [item, string]
|
||||
- '...'
|
||||
- [selected_idx, number]
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- [w, number]
|
||||
- [name, string]
|
||||
- - - [item, string]
|
||||
- '...'
|
||||
- [selected_idx, number]
|
||||
field:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [name, string]
|
||||
- [label, string]
|
||||
- [default, string]
|
||||
- - [name, string]
|
||||
- [label, string]
|
||||
- [default, string]
|
||||
field_close_on_enter:
|
||||
- - [name, string]
|
||||
- [close_on_enter, boolean]
|
||||
formspec_version:
|
||||
- - [version, number]
|
||||
hypertext:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [name, string]
|
||||
- [text, string]
|
||||
image:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [texture_name, string]
|
||||
image_button:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [texture_name, string]
|
||||
- [name, string]
|
||||
- [label, string]
|
||||
- [noclip, boolean]
|
||||
- [drawborder, boolean]
|
||||
- [pressed_texture_name, string]
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [texture_name, string]
|
||||
- [name, string]
|
||||
- [label, string]
|
||||
image_button_exit:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [texture_name, string]
|
||||
- [name, string]
|
||||
- [label, string]
|
||||
item_image:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [item_name, string]
|
||||
item_image_button:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [item_name, string]
|
||||
- [name, string]
|
||||
- [label, string]
|
||||
label:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- [label, string]
|
||||
list:
|
||||
- - [inventory_location, string]
|
||||
- [list_name, string]
|
||||
- - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- ['', 'null']
|
||||
- - [inventory_location, string]
|
||||
- [list_name, string]
|
||||
- - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [starting_item_index, number]
|
||||
listcolors:
|
||||
- - [slot_bg_normal, string]
|
||||
- [slot_bg_hover, string]
|
||||
- [slot_border, string]
|
||||
- [tooltip_bgcolor, string]
|
||||
- [tooltip_fontcolor, string]
|
||||
- - [slot_bg_normal, string]
|
||||
- [slot_bg_hover, string]
|
||||
- [slot_border, string]
|
||||
- - [slot_bg_normal, string]
|
||||
- [slot_bg_hover, string]
|
||||
listring:
|
||||
- - [inventory_location, string]
|
||||
- [list_name, string]
|
||||
- []
|
||||
no_prepend:
|
||||
- []
|
||||
position:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
pwdfield:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [name, string]
|
||||
- [label, string]
|
||||
real_coordinates:
|
||||
- - [bool, boolean]
|
||||
scroll_container:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [scrollbar_name, string]
|
||||
- [orientation, string]
|
||||
- [scroll_factor, number]
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [scrollbar_name, string]
|
||||
- [orientation, string]
|
||||
scroll_container_end:
|
||||
- []
|
||||
scrollbar:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [orientation, string]
|
||||
- [name, string]
|
||||
- [value, string]
|
||||
scrollbaroptions:
|
||||
- - - [opt, table]
|
||||
- '...'
|
||||
set_focus:
|
||||
- - [name, string]
|
||||
- [force, boolean]
|
||||
- - [name, string]
|
||||
size:
|
||||
- - - [w, number]
|
||||
- [h, number]
|
||||
- - - [w, number]
|
||||
- [h, number]
|
||||
- [fixed_size, boolean]
|
||||
style:
|
||||
- - - - [selectors, string]
|
||||
- '...'
|
||||
- - [prop, table]
|
||||
- '...'
|
||||
- - - [name, string]
|
||||
- - [prop, table]
|
||||
- '...'
|
||||
style_type:
|
||||
- - - - [selectors, string]
|
||||
- '...'
|
||||
- - [prop, table]
|
||||
- '...'
|
||||
- - - [name, string]
|
||||
- - [prop, table]
|
||||
- '...'
|
||||
tabheader:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- [h, number]
|
||||
- [name, string]
|
||||
- - - [caption, string]
|
||||
- '...'
|
||||
- [current_tab, string]
|
||||
- [transparent, boolean]
|
||||
- [draw_border, boolean]
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [name, string]
|
||||
- - - [caption, string]
|
||||
- '...'
|
||||
- [current_tab, string]
|
||||
- [transparent, boolean]
|
||||
- [draw_border, boolean]
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- [name, string]
|
||||
- - - [caption, string]
|
||||
- '...'
|
||||
- [current_tab, string]
|
||||
- [transparent, boolean]
|
||||
- [draw_border, boolean]
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- [h, number]
|
||||
- [name, string]
|
||||
- - - [caption, string]
|
||||
- '...'
|
||||
- [current_tab, string]
|
||||
- [transparent, boolean]
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [name, string]
|
||||
- - - [caption, string]
|
||||
- '...'
|
||||
- [current_tab, string]
|
||||
- [transparent, boolean]
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- [name, string]
|
||||
- - - [caption, string]
|
||||
- '...'
|
||||
- [current_tab, string]
|
||||
- [transparent, boolean]
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- [h, number]
|
||||
- [name, string]
|
||||
- - - [caption, string]
|
||||
- '...'
|
||||
- [current_tab, string]
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [name, string]
|
||||
- - - [caption, string]
|
||||
- '...'
|
||||
- [current_tab, string]
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- [name, string]
|
||||
- - - [caption, string]
|
||||
- '...'
|
||||
- [current_tab, string]
|
||||
table:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [name, string]
|
||||
- - - [cells, string]
|
||||
- '...'
|
||||
- [selected_idx, number]
|
||||
tablecolumns:
|
||||
- - - - [type, string]
|
||||
- - [opt, table]
|
||||
- '...'
|
||||
- '...'
|
||||
tableoptions:
|
||||
- - - [opt, table]
|
||||
- '...'
|
||||
textarea:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [name, string]
|
||||
- [label, string]
|
||||
- [default, string]
|
||||
textlist:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [name, string]
|
||||
- - - [listelem, string]
|
||||
- '...'
|
||||
- [selected_idx, number]
|
||||
- [transparent, boolean]
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [name, string]
|
||||
- - - [listelem, string]
|
||||
- '...'
|
||||
- [selected_idx, number]
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [name, string]
|
||||
- - - [listelem, string]
|
||||
- '...'
|
||||
tooltip:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [tooltip_text, string]
|
||||
- [bgcolor, string]
|
||||
- [fontcolor, string]
|
||||
- - [gui_element_name, string]
|
||||
- [tooltip_text, string]
|
||||
- [bgcolor, string]
|
||||
- [fontcolor, string]
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [tooltip_text, string]
|
||||
- [bgcolor, string]
|
||||
- - [gui_element_name, string]
|
||||
- [tooltip_text, string]
|
||||
- [bgcolor, string]
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- - [w, number]
|
||||
- [h, number]
|
||||
- [tooltip_text, string]
|
||||
- - [gui_element_name, string]
|
||||
- [tooltip_text, string]
|
||||
vertlabel:
|
||||
- - - [x, number]
|
||||
- [y, number]
|
||||
- [label, string]
|
||||
103
make_elements.py
103
make_elements.py
@ -1,9 +1,10 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# A primitive script to parse lua_api.txt for formspec elements.
|
||||
# This script needs Python 3.8+ and ruamel.yaml to work.
|
||||
#
|
||||
|
||||
import copy, lua_dump, os, urllib.request
|
||||
import copy, json, lua_dump, os, re, ruamel.yaml, urllib.request
|
||||
|
||||
def _make_known(**kwargs):
|
||||
known = {}
|
||||
@ -14,9 +15,11 @@ def _make_known(**kwargs):
|
||||
|
||||
_known = _make_known(
|
||||
number=('x', 'y', 'w', 'h', 'selected_idx', 'version',
|
||||
'starting_item_index', 'scroll_factor'),
|
||||
'starting_item_index', 'scroll_factor', 'frame_count',
|
||||
'frame_duration', 'frame_start'),
|
||||
boolean=('auto_clip', 'fixed_size', 'transparent', 'draw_border', 'bool',
|
||||
'fullscreen', 'noclip', 'drawborder', 'selected', 'force'),
|
||||
'fullscreen', 'noclip', 'drawborder', 'selected', 'force',
|
||||
'close_on_enter'),
|
||||
table=('param', 'opt', 'prop'),
|
||||
null=('',),
|
||||
)
|
||||
@ -31,12 +34,16 @@ _aliases = {
|
||||
'cell': 'cells',
|
||||
}
|
||||
|
||||
def _fix_param_name(param):
|
||||
param = param.lower().strip().strip('<>').replace(' ', '_')
|
||||
param = _aliases.get(param, param)
|
||||
assert param != '...'
|
||||
return param
|
||||
|
||||
def _fix_param(param):
|
||||
if isinstance(param, str):
|
||||
if ',' not in param:
|
||||
param = param.lower().strip().strip('<>').replace(' ', '_')
|
||||
param = _aliases.get(param, param)
|
||||
assert param != '...'
|
||||
param = _fix_param_name(param)
|
||||
return (param, _known.get(param, 'string'))
|
||||
param = param.split(',')
|
||||
|
||||
@ -111,12 +118,6 @@ def _style_hook(params):
|
||||
params[0] = [(('selectors', 'string'), '...')]
|
||||
yield params
|
||||
|
||||
# Fix scroll_container
|
||||
@hook('scroll_container')
|
||||
def _scroll_container_hook(params):
|
||||
yield params
|
||||
yield params[:4]
|
||||
|
||||
# Fix dropdown
|
||||
@hook('dropdown')
|
||||
def _scroll_container_hook(params):
|
||||
@ -127,14 +128,29 @@ def _scroll_container_hook(params):
|
||||
yield params[:5]
|
||||
yield params
|
||||
|
||||
# Fix textlist
|
||||
@hook('textlist')
|
||||
def _textlist_hook(params):
|
||||
if len(params) > 5:
|
||||
yield params[:5]
|
||||
yield params
|
||||
|
||||
_param_re = re.compile(r'^\* `([^`]+)`(?: and `([^`]+)`)?:? ')
|
||||
def _raw_parse(data):
|
||||
# Get everything from the elements heading to the end of the next heading
|
||||
data = data.split('\nElements\n--------\n', 1)[-1].split('\n----', 1)[0]
|
||||
for line in data.split('\n'):
|
||||
if not line.startswith('### `') or not line.endswith('`'):
|
||||
|
||||
# Remove the next heading
|
||||
data = data.rsplit('\n', 1)[0]
|
||||
|
||||
# Get element data
|
||||
for elem_data in data.split('\n### '):
|
||||
lines = elem_data.split('\n')
|
||||
raw_elem = lines.pop(0)
|
||||
if not raw_elem.startswith('`') or not raw_elem.endswith('`'):
|
||||
continue
|
||||
|
||||
elem = line[5:-2]
|
||||
name, params = elem.split('[', 1)
|
||||
name, params = raw_elem[1:-2].split('[', 1)
|
||||
if params:
|
||||
params = _fix_param(params.split(';'))
|
||||
else:
|
||||
@ -143,9 +159,37 @@ def _raw_parse(data):
|
||||
if name in _hooks:
|
||||
for p in reversed(tuple(map(copy.deepcopy, _hooks[name](params)))):
|
||||
yield name, p
|
||||
else:
|
||||
continue
|
||||
|
||||
# Optional parameters
|
||||
optional_params = set()
|
||||
for line in lines:
|
||||
match = _param_re.match(line)
|
||||
if not match or 'optional' not in line.lower():
|
||||
continue
|
||||
|
||||
optional_params.add(_fix_param_name(match.group(1)))
|
||||
if p2 := match.group(2):
|
||||
optional_params.add(_fix_param_name(p2))
|
||||
|
||||
# if optional_params:
|
||||
# print('Optional', name, optional_params)
|
||||
|
||||
# Convert the optional parameters into a format formspec_ast can
|
||||
# understand without major changes
|
||||
while True:
|
||||
yield name, params
|
||||
|
||||
if not params:
|
||||
break
|
||||
last_param = params[-1]
|
||||
if (not isinstance(last_param, tuple) or
|
||||
not isinstance(last_param[0], str) or
|
||||
last_param[0] not in optional_params):
|
||||
break
|
||||
# print('Optional', name, last_param)
|
||||
params = params[:-1]
|
||||
|
||||
def parse(data):
|
||||
"""
|
||||
Returns a dict:
|
||||
@ -167,7 +211,8 @@ def parse(data):
|
||||
|
||||
return res
|
||||
|
||||
URL = 'https://github.com/minetest/minetest/raw/master/doc/lua_api.txt'
|
||||
# TODO: Fix model[] parsing then switch this back to the master branch
|
||||
URL = 'https://github.com/minetest/minetest/raw/050964b/doc/lua_api.txt'
|
||||
def fetch_and_parse(*, url=URL):
|
||||
with urllib.request.urlopen(url) as f:
|
||||
raw = f.read()
|
||||
@ -181,13 +226,33 @@ _comment = """
|
||||
|
||||
"""
|
||||
|
||||
_yaml_comment = """
|
||||
#
|
||||
# This file is automatically generated by make_elements.py and isn't actually
|
||||
# used by formspec_ast, however it is useful for comparing changes across
|
||||
# lua_api versions.
|
||||
#
|
||||
|
||||
"""
|
||||
|
||||
def main():
|
||||
dirname = os.path.dirname(__file__)
|
||||
filename = os.path.join(dirname, 'elements.lua')
|
||||
data = fetch_and_parse()
|
||||
|
||||
# Horrible code to create elements.yaml
|
||||
filename2 = os.path.join(dirname, 'elements.yaml')
|
||||
print('Writing to ' + filename2 + '...')
|
||||
with open(filename2, 'w') as f:
|
||||
f.write(_yaml_comment.lstrip())
|
||||
# Yuck - Unparsing then re-parsing the data as JSON is the easiest way
|
||||
# I can think of to convert tuples to lists.
|
||||
ruamel.yaml.dump(json.loads(json.dumps(data)), f)
|
||||
|
||||
print('Writing to ' + filename + '...')
|
||||
with open(filename, 'w') as f:
|
||||
f.write(_comment.lstrip())
|
||||
f.write(lua_dump.serialize(fetch_and_parse()))
|
||||
f.write(lua_dump.serialize(data))
|
||||
# elems = fetch_and_parse()
|
||||
# for elem in sorted(elems):
|
||||
# for def_ in elems[elem]:
|
||||
|
||||
13
tests.lua
13
tests.lua
@ -80,6 +80,8 @@ local fs = [[
|
||||
image[0,1;1,1;air.png]
|
||||
set_focus[name;true]
|
||||
dropdown[0,0;1;test;abc,def,ghi,jkl;2]
|
||||
field_close_on_enter[my-field;false]
|
||||
bgcolor[blue]
|
||||
]]
|
||||
fs = ('\n' .. fs):gsub('\n[ \n]*', '')
|
||||
|
||||
@ -161,7 +163,16 @@ test_parse_unparse(fs, {
|
||||
name = "test",
|
||||
item = {"abc", "def", "ghi", "jkl"},
|
||||
selected_idx = 2,
|
||||
}
|
||||
},
|
||||
{
|
||||
type = "field_close_on_enter",
|
||||
name = "my-field",
|
||||
close_on_enter = false,
|
||||
},
|
||||
{
|
||||
type = "bgcolor",
|
||||
bgcolor = "blue",
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user