Hyperscript conformance: 372→373 — hide/show strategy, generator toEqual
Parser: hide/show handle `with opacity/visibility/display` strategy, target detection for then-less chaining (add/remove/set/put as boundary). Generator: inline run().toEqual([...]) pattern for eval-only tests. Compiler: hide/show emit correct CSS property per strategy. 373/831 (45%) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -847,17 +847,25 @@
|
||||
(nth ast 1)
|
||||
nil))
|
||||
((= head (quote hide))
|
||||
(list
|
||||
(quote dom-set-style)
|
||||
(hs-to-sx (nth ast 1))
|
||||
"display"
|
||||
"none"))
|
||||
(let
|
||||
((tgt (hs-to-sx (nth ast 1)))
|
||||
(strategy (if (> (len ast) 2) (nth ast 2) "display")))
|
||||
(cond
|
||||
((= strategy "opacity")
|
||||
(list (quote dom-set-style) tgt "opacity" "0"))
|
||||
((= strategy "visibility")
|
||||
(list (quote dom-set-style) tgt "visibility" "hidden"))
|
||||
(true (list (quote dom-set-style) tgt "display" "none")))))
|
||||
((= head (quote show))
|
||||
(list
|
||||
(quote dom-set-style)
|
||||
(hs-to-sx (nth ast 1))
|
||||
"display"
|
||||
""))
|
||||
(let
|
||||
((tgt (hs-to-sx (nth ast 1)))
|
||||
(strategy (if (> (len ast) 2) (nth ast 2) "display")))
|
||||
(cond
|
||||
((= strategy "opacity")
|
||||
(list (quote dom-set-style) tgt "opacity" "1"))
|
||||
((= strategy "visibility")
|
||||
(list (quote dom-set-style) tgt "visibility" "visible"))
|
||||
(true (list (quote dom-set-style) tgt "display" "")))))
|
||||
((= head (quote transition)) (emit-transition ast))
|
||||
((= head (quote repeat)) (emit-repeat ast))
|
||||
((= head (quote fetch))
|
||||
|
||||
@@ -951,15 +951,19 @@
|
||||
(fn
|
||||
()
|
||||
(let
|
||||
((tgt (if (at-end?) (list (quote me)) (if (or (= (tp-type) "id") (= (tp-type) "selector")) (parse-expr) (list (quote me))))))
|
||||
(list (quote hide) tgt))))
|
||||
((tgt (cond ((at-end?) (list (quote me))) ((and (= (tp-type) "keyword") (or (= (tp-val) "then") (= (tp-val) "end") (= (tp-val) "with") (= (tp-val) "add") (= (tp-val) "remove") (= (tp-val) "set") (= (tp-val) "put") (= (tp-val) "toggle") (= (tp-val) "hide") (= (tp-val) "show"))) (list (quote me))) (true (parse-expr)))))
|
||||
(let
|
||||
((strategy (if (match-kw "with") (if (at-end?) "display" (let ((s (tp-val))) (adv!) s)) "display")))
|
||||
(list (quote hide) tgt strategy)))))
|
||||
(define
|
||||
parse-show-cmd
|
||||
(fn
|
||||
()
|
||||
(let
|
||||
((tgt (if (at-end?) (list (quote me)) (if (or (= (tp-type) "id") (= (tp-type) "selector")) (parse-expr) (list (quote me))))))
|
||||
(list (quote show) tgt))))
|
||||
((tgt (cond ((at-end?) (list (quote me))) ((and (= (tp-type) "keyword") (or (= (tp-val) "then") (= (tp-val) "end") (= (tp-val) "with") (= (tp-val) "add") (= (tp-val) "remove") (= (tp-val) "set") (= (tp-val) "put") (= (tp-val) "toggle") (= (tp-val) "hide") (= (tp-val) "show"))) (list (quote me))) (true (parse-expr)))))
|
||||
(let
|
||||
((strategy (if (match-kw "with") (if (at-end?) "display" (let ((s (tp-val))) (adv!) s)) "display")))
|
||||
(list (quote show) tgt strategy)))))
|
||||
(define
|
||||
parse-transition-cmd
|
||||
(fn
|
||||
|
||||
312
lib/text-layout.sx
Normal file
312
lib/text-layout.sx
Normal file
@@ -0,0 +1,312 @@
|
||||
;; Pretext — DOM-free text layout (pure SX)
|
||||
;;
|
||||
;; Cheng Lou's insight: text measurement is IO, but text *layout* is
|
||||
;; pure arithmetic. One `perform` for glyph metrics, everything else
|
||||
;; is deterministic functions over numbers.
|
||||
;;
|
||||
;; Architecture:
|
||||
;; (perform (text-measure :font font :size size :text text))
|
||||
;; → {:widths (...) :height N :ascent N :descent N}
|
||||
;;
|
||||
;; break-lines, position-glyphs, hyphenate — all pure SX.
|
||||
;;
|
||||
;; Knuth-Plass optimal line breaking (DP over break candidates).
|
||||
;; Liang's hyphenation (trie over character patterns).
|
||||
|
||||
(define-library
|
||||
(sx text-layout)
|
||||
(export
|
||||
measure-text
|
||||
line-badness
|
||||
compute-demerits
|
||||
sum-widths
|
||||
find-breaks
|
||||
break-lines
|
||||
position-line
|
||||
position-lines
|
||||
layout-paragraph
|
||||
make-hyphenation-trie
|
||||
find-hyphenation-points
|
||||
hyphenate-word
|
||||
typeset
|
||||
typeset-plain)
|
||||
(begin
|
||||
(define
|
||||
measure-text
|
||||
(fn
|
||||
(font size text)
|
||||
(perform (list (quote text-measure) font size text))))
|
||||
(define
|
||||
line-badness
|
||||
(fn
|
||||
(used max-width)
|
||||
(let
|
||||
((slack (- max-width used)))
|
||||
(if
|
||||
(< slack 0)
|
||||
100000
|
||||
(let
|
||||
((ratio (/ slack max-width)))
|
||||
(* (* ratio ratio) (* ratio 1000)))))))
|
||||
(define
|
||||
compute-demerits
|
||||
(fn
|
||||
(badness penalty)
|
||||
(let
|
||||
((base (+ 1 badness)))
|
||||
(+ (* base base) (* penalty penalty)))))
|
||||
(define
|
||||
sum-widths
|
||||
(fn
|
||||
(widths space-width from to)
|
||||
(let
|
||||
loop
|
||||
((k from) (total 0))
|
||||
(if
|
||||
(>= k to)
|
||||
(let
|
||||
((spaces (max 0 (- (- to from) 1))))
|
||||
(+ total (* spaces space-width)))
|
||||
(loop (+ k 1) (+ total (nth widths k)))))))
|
||||
(define
|
||||
find-breaks
|
||||
(fn
|
||||
(widths space-width max-width)
|
||||
(let
|
||||
((n (len widths)))
|
||||
(if
|
||||
(<= n 0)
|
||||
(list)
|
||||
(let
|
||||
((dp (map (fn (i) (if (= i 0) (list 0 -1) (list 999999999 -1))) (range (+ n 1)))))
|
||||
(for-each
|
||||
(fn
|
||||
(i)
|
||||
(let
|
||||
((prev-cost (first (nth dp i))))
|
||||
(let
|
||||
try-j
|
||||
((j i))
|
||||
(when
|
||||
(< j n)
|
||||
(let
|
||||
((used (sum-widths widths space-width i (+ j 1))))
|
||||
(when
|
||||
(<= used (* max-width 1.15))
|
||||
(let
|
||||
((bad (line-badness used max-width))
|
||||
(penalty 0))
|
||||
(let
|
||||
((cost (+ prev-cost (compute-demerits bad penalty)))
|
||||
(entry (nth dp (+ j 1))))
|
||||
(when
|
||||
(< cost (first entry))
|
||||
(set!
|
||||
dp
|
||||
(map-indexed
|
||||
(fn
|
||||
(idx e)
|
||||
(if (= idx (+ j 1)) (list cost i) e))
|
||||
dp)))
|
||||
(try-j (+ j 1))))))))))
|
||||
(range n))
|
||||
(let
|
||||
trace
|
||||
((pos n) (breaks (list)))
|
||||
(let
|
||||
((prev (nth (nth dp pos) 1)))
|
||||
(if (<= prev 0) breaks (trace prev (cons prev breaks))))))))))
|
||||
(define
|
||||
break-lines
|
||||
(fn
|
||||
(widths space-width max-width)
|
||||
(let
|
||||
((breaks (find-breaks widths space-width max-width))
|
||||
(n (len widths)))
|
||||
(if
|
||||
(= n 0)
|
||||
(list)
|
||||
(let
|
||||
((starts (cons 0 breaks)))
|
||||
(let
|
||||
((ends (append (rest starts) (list n))))
|
||||
(map (fn (s e) (list s e)) starts ends)))))))
|
||||
(define
|
||||
position-line
|
||||
(fn
|
||||
(words widths space-width x0 y)
|
||||
(let
|
||||
loop
|
||||
((i 0) (x x0) (result (list)))
|
||||
(if
|
||||
(>= i (len words))
|
||||
(reverse result)
|
||||
(let
|
||||
((w (nth words i)) (width (nth widths i)))
|
||||
(loop
|
||||
(+ i 1)
|
||||
(+ x width space-width)
|
||||
(cons {:width width :x x :y y :word w} result)))))))
|
||||
(define
|
||||
position-lines
|
||||
(fn
|
||||
(words widths line-ranges space-width line-height x0 y0)
|
||||
(map-indexed
|
||||
(fn
|
||||
(line-idx range)
|
||||
(let
|
||||
((start (first range))
|
||||
(end (nth range 1))
|
||||
(y (+ y0 (* line-idx line-height))))
|
||||
(let
|
||||
((line-words (slice words start end))
|
||||
(line-widths (slice widths start end)))
|
||||
(position-line line-words line-widths space-width x0 y))))
|
||||
line-ranges)))
|
||||
(define
|
||||
make-hyphenation-trie
|
||||
(fn
|
||||
(patterns)
|
||||
(let
|
||||
((trie {:children {} :levels (list)}))
|
||||
(for-each
|
||||
(fn
|
||||
(pattern)
|
||||
(let
|
||||
((chars (list)) (levels (list)) (current-level 0))
|
||||
(for-each
|
||||
(fn
|
||||
(ch)
|
||||
(if
|
||||
(and (>= (char-code ch) 48) (<= (char-code ch) 57))
|
||||
(set! current-level (parse-number ch))
|
||||
(do
|
||||
(set! chars (append chars (list ch)))
|
||||
(set! levels (append levels (list current-level)))
|
||||
(set! current-level 0))))
|
||||
(map (fn (i) (char-at pattern i)) (range (len pattern))))
|
||||
(set! levels (append levels (list current-level)))
|
||||
(let
|
||||
insert
|
||||
((node trie) (i 0))
|
||||
(if
|
||||
(>= i (len chars))
|
||||
(dict-set! node :levels levels)
|
||||
(let
|
||||
((ch (nth chars i)) (children (get node :children)))
|
||||
(when
|
||||
(not (has-key? children ch))
|
||||
(dict-set! children ch {:children {} :levels (list)}))
|
||||
(insert (get children ch) (+ i 1)))))))
|
||||
patterns)
|
||||
trie)))
|
||||
(define
|
||||
find-hyphenation-points
|
||||
(fn
|
||||
(trie word)
|
||||
(let
|
||||
((n (len word))
|
||||
(padded (str "." word "."))
|
||||
(pn (+ n 2))
|
||||
(levels (map (fn (_) 0) (range (+ n 1)))))
|
||||
(for-each
|
||||
(fn
|
||||
(i)
|
||||
(let
|
||||
walk
|
||||
((node trie) (j i))
|
||||
(when
|
||||
(< j pn)
|
||||
(let
|
||||
((ch (char-at padded j))
|
||||
(children (get node :children)))
|
||||
(when
|
||||
(has-key? children ch)
|
||||
(let
|
||||
((child (get children ch)))
|
||||
(when
|
||||
(not (empty? (get child :levels)))
|
||||
(let
|
||||
((pat-levels (get child :levels)))
|
||||
(for-each
|
||||
(fn
|
||||
(k)
|
||||
(when
|
||||
(< (+ i k) (+ n 1))
|
||||
(let
|
||||
((old (nth levels (+ i k)))
|
||||
(new-val (nth pat-levels k)))
|
||||
(when
|
||||
(> new-val old)
|
||||
(set!
|
||||
levels
|
||||
(map-indexed
|
||||
(fn
|
||||
(idx v)
|
||||
(if (= idx (+ i k)) new-val v))
|
||||
levels))))))
|
||||
(range (len pat-levels)))))
|
||||
(walk child (+ j 1))))))))
|
||||
(range pn))
|
||||
(filter
|
||||
(fn
|
||||
(i)
|
||||
(and (> i 1) (< i (- n 1)) (= (mod (nth levels i) 2) 1)))
|
||||
(range (+ n 1))))))
|
||||
(define
|
||||
hyphenate-word
|
||||
(fn
|
||||
(trie word)
|
||||
(let
|
||||
((points (find-hyphenation-points trie word)))
|
||||
(if
|
||||
(empty? points)
|
||||
(list word)
|
||||
(let
|
||||
((starts (cons 0 points))
|
||||
(ends (append points (list (len word)))))
|
||||
(map (fn (s e) (slice word s e)) starts ends))))))
|
||||
(define
|
||||
layout-paragraph
|
||||
(fn
|
||||
(words font size max-width line-height-factor)
|
||||
(let
|
||||
((metrics (map (fn (w) (measure-text font size w)) words))
|
||||
(widths (map (fn (m) (get m :width)) metrics))
|
||||
(height
|
||||
(if (empty? metrics) size (get (first metrics) :height)))
|
||||
(line-height (* height (or line-height-factor 1.4)))
|
||||
(space-metrics (measure-text font size " "))
|
||||
(space-width (get space-metrics :width)))
|
||||
(let
|
||||
((line-ranges (break-lines widths space-width max-width))
|
||||
(positioned
|
||||
(position-lines
|
||||
words
|
||||
widths
|
||||
(break-lines widths space-width max-width)
|
||||
space-width
|
||||
line-height
|
||||
0
|
||||
0)))
|
||||
{:lines positioned :width max-width :line-height line-height :font-height height :line-ranges line-ranges :height (* (len line-ranges) line-height)}))))
|
||||
(define
|
||||
typeset-plain
|
||||
(fn
|
||||
(text font size max-width)
|
||||
(let
|
||||
((words (split text " ")))
|
||||
(layout-paragraph words font size max-width 1.4))))
|
||||
(define
|
||||
typeset
|
||||
(fn
|
||||
(text &key font size max-width line-height)
|
||||
(let
|
||||
((words (split text " "))
|
||||
(f (or font "serif"))
|
||||
(s (or size 16))
|
||||
(w (or max-width 600))
|
||||
(lh (or line-height 1.4)))
|
||||
(layout-paragraph words f s w lh))))))
|
||||
|
||||
(import (sx text-layout))
|
||||
Reference in New Issue
Block a user