- fix: ekp-param-set silently reset after first justify (now persists;
ekp-param-reset added)
- fix: narrow-width CJK returned empty string (data loss); two-pass
emergency-break strategy guarantees output for any input
- fix: K-P penalties never synced to C module; space-box metrics
divergence between C and Elisp engines
- fix: para cache ignored ekp-latin-lang (stale hyphenation after
language switch) and used collision-prone sxhash keys
- fix: fullwidth letters/digits misclassified as CJK punctuation
- fix: combining chars split from their base char in the tokenizer
- fix: punctuation-wrapped words (word!/(word)/word;) never hyphenated
- fix: renderer double-counted stripped space widths; negative glue
clamped; batch/tty font detection no longer crashes
- feat: real looseness support via (position × line-count) DP
- perf: O(1) line metrics and gap counts via prefix arrays (inner loop
previously allocated O(n) subsequences → O(n³) total); box measurement
dedupe; eq fast-path para lookup; prebuilt per-para glue arrays
→ zh justify 7547ms → 96ms (compiled elisp) / 57ms (C);
range-justify 68.5s → 0.48s / 34ms; C module itself 3–19× faster
- test: 36 batch-safe ERT tests + 300-case property fuzz (C/elisp
byte-identical output, zero content loss) replacing ad-hoc suite
- docs: readme/readme_zh/DEVELOPER/DEVELOPER_ZH/ekp_c-README rewritten
to match the implementation; phase handoff in .phrase/phases/
BREAKING: requires Emacs 29.1+; C module must be rebuilt (v1.1, new
arities); ekp-threshold-factor / ekp-flagged-penalty /
ekp-forced-break-penalty removed; Rust module stubs removed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
113 lines
4.1 KiB
EmacsLisp
113 lines
4.1 KiB
EmacsLisp
;;; ekp-demo.el --- Interactive demos for EKP -*- lexical-binding: t; -*-
|
|
|
|
;;; Commentary:
|
|
|
|
;; Interactive, GUI-only demonstrations. Evaluate the file in a
|
|
;; graphical Emacs session, then run the forms in the comments.
|
|
;; Automated tests live in ekp-tests.el.
|
|
|
|
;;; Code:
|
|
|
|
(require 'ekp)
|
|
|
|
(defun ekp-demo--file-content (file)
|
|
(with-temp-buffer
|
|
(insert-file-contents
|
|
(expand-file-name file (expand-file-name "tests" (ekp-root-dir))))
|
|
(buffer-substring (point-min) (point-max))))
|
|
|
|
(defun ekp-demo-propertize (string properties &optional start end)
|
|
"Add PROPERTIES to a copy of STRING without clobbering existing ones."
|
|
(let* ((string (copy-sequence string))
|
|
(start (or start 0))
|
|
(end (or end (length string))))
|
|
(while properties
|
|
(let ((prop (pop properties))
|
|
(value (pop properties)))
|
|
(pcase prop
|
|
('face (add-face-text-property start end value t string))
|
|
('display (add-display-text-property
|
|
start end (car value) (cadr value) string))
|
|
(_ (put-text-property start end prop value string)))))
|
|
string))
|
|
|
|
(defun ekp-demo--pop-buffer (height &rest strings)
|
|
(declare (indent defun))
|
|
(let ((buffer (pop-to-buffer "*ekp-demo*"
|
|
`(display-buffer-at-bottom
|
|
(window-height . ,(or height 10))))))
|
|
(with-current-buffer buffer
|
|
(local-set-key "q" 'quit-window)
|
|
(let ((inhibit-read-only t))
|
|
(erase-buffer)
|
|
(apply #'insert strings)))
|
|
buffer))
|
|
|
|
(defun ekp-demo-str (cjk latin &optional font)
|
|
"Load a test text (see tests/text-*.txt) with optional FONT face."
|
|
(let ((file (concat "text"
|
|
(and cjk (concat "-" cjk))
|
|
(and latin (concat "-" latin))
|
|
".txt")))
|
|
(if font
|
|
(ekp-demo-propertize (ekp-demo--file-content file)
|
|
`(face (:family ,font)))
|
|
(ekp-demo--file-content file))))
|
|
|
|
(defun ekp-demo-justify (cjk latin font pixel)
|
|
"Justify a sample text at PIXEL width and show it."
|
|
(setq ekp-latin-lang (or latin "en_US"))
|
|
(ekp-demo--pop-buffer 35
|
|
(ekp-pixel-justify (ekp-demo-str cjk latin font) pixel)))
|
|
|
|
(defun ekp-demo-range-justify (cjk latin font min max)
|
|
"Find the optimal width in [MIN, MAX] and show the result."
|
|
(setq ekp-latin-lang (or latin "en_US"))
|
|
(ekp-demo--pop-buffer 35
|
|
(car (ekp-pixel-range-justify (ekp-demo-str cjk latin font) min max))))
|
|
|
|
;; (ekp-demo-justify nil "en_US" "Times New Roman" 699)
|
|
;; (ekp-demo-justify "zh" "en_US" "Cascadia Next SC" 666)
|
|
;; (ekp-demo-justify nil "de_DE" "Georgia" 666)
|
|
;; (ekp-demo-range-justify "zh" "en_US" nil 666 690)
|
|
|
|
(defun ekp-demo-animate (min-pixel max-pixel &optional inc)
|
|
"Animate justification from MIN-PIXEL to MAX-PIXEL."
|
|
(let ((str (ekp-demo-str "zh" "en_US"))
|
|
(pixel-lst (number-sequence min-pixel max-pixel (or inc 1)))
|
|
(buf (get-buffer-create "*ekp-demo-animate*")))
|
|
(save-window-excursion
|
|
(delete-other-windows)
|
|
(switch-to-buffer buf)
|
|
(with-current-buffer buf
|
|
(dolist (pixel pixel-lst)
|
|
(erase-buffer)
|
|
(insert (ekp-pixel-justify str pixel))
|
|
(goto-char (point-min))
|
|
(sit-for 0.00001))))))
|
|
|
|
;; (ekp-demo-animate 400 800 1)
|
|
|
|
(defun ekp-demo-mixed-faces ()
|
|
"Show that per-paragraph fonts and faces survive justification."
|
|
(let* ((str (ekp-demo-str "zh" "en_US"))
|
|
(lst (split-string str "\n" t)))
|
|
(setq lst (list
|
|
(ekp-demo-propertize
|
|
(ekp-demo-propertize (nth 0 lst)
|
|
'(face (:family "Comic Sans MS")))
|
|
'(face (:height 1.3 :foreground "cyan")) 0 2)
|
|
(ekp-demo-propertize
|
|
(ekp-demo-propertize (nth 1 lst)
|
|
'(face (:family "Cascadia Next SC")))
|
|
'(face (:height 1.3 :foreground "green")) 0 2)))
|
|
(ekp-clear-caches)
|
|
(ekp-demo--pop-buffer 30
|
|
"\n" (ekp-pixel-justify (string-join lst "\n\n") 683))))
|
|
|
|
;; (ekp-demo-mixed-faces)
|
|
|
|
(provide 'ekp-demo)
|
|
|
|
;;; ekp-demo.el ends here
|