HS: hide strategy config (+3 tests)
Three parts: (a) `runtime.sx` hs-hide-one!/hs-show-one! consult a new
`_hs-hide-strategies` dict (and `_hs-default-hide-strategy` override)
before falling through to the built-in display/opacity/etc. cases. The
strategy fn is called directly with (op, el, arg). New setters
`hs-set-hide-strategies!` and `hs-set-default-hide-strategy!`. (b)
`generate-sx-tests.py` `_hs_config_setup_ops` recognises
`_hyperscript.config.defaultHideShowStrategy = "X"`, `delete …default…`,
and `hideShowStrategies = { NAME: function (op, el, arg) { if …
classList.add/remove } }` with brace-matched function body extraction.
(c) Pre-setup emitter handles `__hs_config__` pseudo-name by emitting
the SX expression as-is (not a window.X = Y assignment).
Suite hs-upstream-hide: 12/16 → 15/16. Remaining test
(`hide element then show element retains original display`) needs
`on click 1 hide` / `on click 2 show` count-filtered events — separate
feature. Smoke 0-195: 162/195 unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -783,6 +783,58 @@ def _window_setup_ops(assign_body):
|
||||
return out
|
||||
|
||||
|
||||
def _hs_config_setup_ops(body):
|
||||
"""Translate `_hyperscript.config.X = ...` assignments into SX ops.
|
||||
Recognises `defaultHideShowStrategy = "name"` and `hideShowStrategies = { NAME: fn }`
|
||||
for simple classList.add/remove-based strategies. Returns list of SX expr strings.
|
||||
Empty list means no recognised ops; caller should skip (don't drop the block)."""
|
||||
ops = []
|
||||
# defaultHideShowStrategy = "name"
|
||||
for dm in re.finditer(
|
||||
r'_hyperscript\.config\.defaultHideShowStrategy\s*=\s*"([^"]+)"',
|
||||
body,
|
||||
):
|
||||
ops.append(f'(hs-set-default-hide-strategy! "{dm.group(1)}")')
|
||||
for dm in re.finditer(
|
||||
r"_hyperscript\.config\.defaultHideShowStrategy\s*=\s*'([^']+)'",
|
||||
body,
|
||||
):
|
||||
ops.append(f'(hs-set-default-hide-strategy! "{dm.group(1)}")')
|
||||
# delete _hyperscript.config.defaultHideShowStrategy
|
||||
if re.search(r'delete\s+_hyperscript\.config\.defaultHideShowStrategy', body):
|
||||
ops.append('(hs-set-default-hide-strategy! nil)')
|
||||
# hideShowStrategies = { NAME: function(op, element, arg) { IF-ELSE } }
|
||||
# Nested braces — locate the function body by manual brace-matching.
|
||||
sm = re.search(
|
||||
r'_hyperscript\.config\.hideShowStrategies\s*=\s*\{\s*'
|
||||
r'(\w+)\s*:\s*function\s*\(\s*\w+\s*,\s*\w+\s*,\s*\w+\s*\)\s*\{',
|
||||
body,
|
||||
)
|
||||
if sm:
|
||||
name = sm.group(1)
|
||||
start = sm.end()
|
||||
depth = 1
|
||||
i = start
|
||||
while i < len(body) and depth > 0:
|
||||
if body[i] == '{': depth += 1
|
||||
elif body[i] == '}': depth -= 1
|
||||
i += 1
|
||||
fn_body = body[start:i - 1] if depth == 0 else ''
|
||||
hm = re.search(
|
||||
r'if\s*\(\s*\w+\s*==\s*"hide"\s*\)\s*\{\s*'
|
||||
r'\w+\.classList\.add\(\s*"([^"]+)"\s*\)\s*;?\s*\}\s*'
|
||||
r'else\s*\{\s*\w+\.classList\.remove\(\s*"([^"]+)"\s*\)\s*;?\s*\}',
|
||||
fn_body, re.DOTALL,
|
||||
)
|
||||
if hm:
|
||||
cls = hm.group(1)
|
||||
ops.append(
|
||||
f'(hs-set-hide-strategies! {{:{name} '
|
||||
f'(fn (op el arg) (if (= op "hide") (dom-add-class el "{cls}") (dom-remove-class el "{cls}")))}})'
|
||||
)
|
||||
return ops
|
||||
|
||||
|
||||
def _extract_detail_expr(opts_src):
|
||||
"""Extract `detail: ...` from an event options block like `, { detail: X }`.
|
||||
Returns an SX expression string, defaulting to `nil`."""
|
||||
@@ -920,8 +972,29 @@ def parse_dev_body(body, elements, var_names):
|
||||
else:
|
||||
pre_setups.append((name, sx_val))
|
||||
continue
|
||||
# _hyperscript.config.X = ... setups (hideShowStrategies etc.)
|
||||
hs_config_ops = _hs_config_setup_ops(m.group(1))
|
||||
if hs_config_ops:
|
||||
for op_expr in hs_config_ops:
|
||||
if seen_html:
|
||||
ops.append(op_expr)
|
||||
else:
|
||||
pre_setups.append(('__hs_config__', op_expr))
|
||||
continue
|
||||
# fall through
|
||||
|
||||
# evaluate(() => _hyperscript.config.X = ...) single-line variant.
|
||||
m = re.match(r'evaluate\(\s*\(\)\s*=>\s*(_hyperscript\.config\..+?)\s*\)\s*$', stmt_na, re.DOTALL)
|
||||
if m:
|
||||
hs_config_ops = _hs_config_setup_ops(m.group(1))
|
||||
if hs_config_ops:
|
||||
for op_expr in hs_config_ops:
|
||||
if seen_html:
|
||||
ops.append(op_expr)
|
||||
else:
|
||||
pre_setups.append(('__hs_config__', op_expr))
|
||||
continue
|
||||
|
||||
# evaluate(() => document.querySelector(SEL).innerHTML = VAL) — DOM reset.
|
||||
m = re.match(
|
||||
r"evaluate\(\s*\(\)\s*=>\s*document\.querySelector\(\s*(['\"])([^'\"]+)\1\s*\)"
|
||||
@@ -1215,7 +1288,10 @@ def generate_test_pw(test, elements, var_names, idx):
|
||||
# Pre-`html(...)` setups — emit before element creation so activation
|
||||
# (init handlers etc.) sees the expected globals.
|
||||
for name, sx_val in pre_setups:
|
||||
lines.append(f' (host-set! (host-global "window") "{name}" {sx_val})')
|
||||
if name == '__hs_config__':
|
||||
lines.append(f' {sx_val}')
|
||||
else:
|
||||
lines.append(f' (host-set! (host-global "window") "{name}" {sx_val})')
|
||||
|
||||
# Compile script blocks so `def X()` functions are available. Wrap in
|
||||
# guard because not all script forms (e.g. `behavior`) are implemented
|
||||
|
||||
Reference in New Issue
Block a user