One self-contained demo buffer for every feature, driven by single
keys, with per-reflow milliseconds in the header line:
-/+ width ±20 px (arrows: ±4) d animated width sweep + fps report
a cycle 4 alignments p hanging punctuation
i first-line indent (2 em) s wedge parshape (Elisp 2D path)
c C engine vs pure Elisp w follow window width (auto-mode)
The sample text bundles a verbatim-protected code block, an inline
no-break atom, and NBSP-joined figures; the engine indicator honestly
shows when a feature (indent/parshape) bypasses C. Launch:
emacs -Q -L /path/to/emacs-kp -l tests/ekp-showcase.el -f ekp-showcase
Verified interactively in GUI Emacs (justify 480→360 px, protrusion +
indent on: state and timing update live).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Buffers past ekp-auto-justify-lazy-threshold (20k chars) no longer
re-justify wholesale on a width change: the visible span updates
synchronously and the rest follows in polite background chunks
(run-with-timer ticks that yield to pending input). Measured on a
300-paragraph, 132k-char article: perceived latency 332 ms → 15 ms,
background completion ~0.5 s.
Fixed in the process: adjacent chunk markers shared a boundary, and
the earlier chunk's delete+insert pushed the next chunk's start
marker back over the freshly inserted text — chunks grew linearly
(2.2k, 4.9k, 7.4k… chars) and background work went quadratic (14 s).
Start markers now use insertion-type t. Regression test asserts the
chunked result equals the one-shot result property-for-property.
Also: org/markdown skip-face presets (ekp-region-org-skip-faces /
ekp-region-markdown-skip-faces), MELPA header hygiene (Version + URL
on the main file, stale Package-Requires dropped from ekp-hyphen.el),
readme notes.
67 ERT green; fuzz 300/300.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
readme/readme_zh gain Typography and verbatim-protection sections
(alignment, hanging punctuation, parshape/first-line indent,
no-break characters, halfwidth kinsoku) plus the honest limitation
notes (mid-line CLREQ compression and left protrusion are not
renderable in Emacs). DEVELOPER/DEVELOPER_ZH document the break-
permission layer, alignment extra-stretch, protrusion lockstep and
per-line specs (§5.1) and the C 1.4 API surface.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The renderer (shared by both engines) is now lossless: synthesized
glue carries the original text it replaced (ekp-glue), soft line
breaks carry the whitespace swallowed around the break
(ekp-soft-break), break hyphens are marked (ekp-soft-hyphen), and
paragraph-edge whitespace survives as zero-display ekp-hidden text.
Orphaned ekp--combine-glues-and-boxes / ekp--interleave removed.
New ekp-region.el:
- ekp-justify-region / ekp-unjustify-region: in-place justification
with exact structural restore (character- and property-exact),
robust to edits made while justified
- ekp-auto-justify-mode: keeps the buffer justified at the window
width; debounced re-flow on window resize, incremental
per-paragraph re-justification after edits (served by the
paragraph cache)
- fix found in live GUI testing: buffer-local members of
window-size-change-functions receive the WINDOW as argument and
may run with an unrelated buffer current; the handler now resolves
window and buffer explicitly (regression test included)
Tests: 47 ERT (36 core + 11 region) passing; 300-case fuzz 0
failures (C/elisp parity unchanged); byte-compile clean with
error-on-warn. Verified interactively in GUI Emacs: justified at
1403px maximized, auto-reflowed to 614px on frame resize.
docs: interactive-use section in readme.md / readme_zh.md
ci: byte-compile list includes ekp-region.el
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- 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>