Commit Graph

8 Commits

Author SHA1 Message Date
Kinneyzhang
bc4907e1fc perf(region): smarter lazy re-flow scheduling
- a dirty area larger than the lazy threshold (big paste, revert)
  is chunked into the background queue instead of freezing the
  command loop with one synchronous re-justification
- queued chunks intersecting the visible span move to the queue
  front on scroll (window-scroll-functions): scrolling into an
  unprocessed area no longer waits for the whole queue
- background ticks work under a time budget
  (ekp-auto-justify-tick-budget, default 5 ms): several small
  chunks per tick, guaranteed progress of at least one, peek-then-
  pop so an aborted justification never loses a chunk
- ticks also yield while an input-method composition is active

3 new ERT tests (84 total).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 01:11:05 +08:00
Kinneyzhang
f0c7927644 feat: whole-buffer commands, DWIM, M-q remap, org/markdown wiring
- new commands ekp-justify-buffer / ekp-unjustify-buffer
- ekp-justify-region / ekp-unjustify-region without an active region
  now act on the hard paragraph at point (DWIM) instead of erroring
- ekp-auto-justify-mode remaps fill-paragraph to the new
  ekp-refill-paragraph — plain M-q treated glue spaces and soft
  breaks as content and destroyed the original whitespace
- ekp-org-setup / ekp-markdown-setup wire the existing skip-face
  presets in one call; enabling the mode in an Org/Markdown buffer
  applies them automatically unless the user configured their own.
  The markdown setup also stops font-lock from managing the display
  property: refontification was stripping the pixel glue and
  wrecking the layout permanently.
- justify runs font-lock-ensure over the region first when
  face-based verbatim detection is configured — paragraphs jit-lock
  had never fontified (off-screen code blocks) were not skipped
- showcase/demo buffers disable undo recording (width sweeps
  generated thousands of useless undo entries)

5 new ERT tests (81 total).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 01:08:40 +08:00
Kinneyzhang
a12a3c1191 feat: coexist with the Emacs ecosystem — isearch, kill ring, windows
- isearch searches the logical text: entering isearch temporarily
  un-justifies (spans remembered as markers), exiting restores the
  layout.  Justified CJK lines contain real injected spaces, which
  made even same-line phrases unfindable.
- the kill ring receives the logical text via a buffer-local
  filter-buffer-substring-function: yanking justified text elsewhere
  transports words, not the source window's pixel layout.
- multi-window: the layout width is now the narrowest window showing
  the buffer (was: whichever window resized last, leaving the other
  window overflowing), and window-configuration-change-hook catches
  the buffer becoming displayed, splits, and closed windows.
- paragraphs containing field or read-only text (comint prompts)
  are never re-written; interactive justify/unjustify on read-only
  buffers signals instead of editing through inhibit-read-only.
- edit re-flows wait for an active input-method composition (quail
  preedit) to finish instead of re-writing the text under it.

4 new ERT tests (76 total).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 01:05:15 +08:00
Kinneyzhang
ede518f10f fix: editor-state integrity — saving, stickiness, modified flag, undo
Justification is a re-layout of the same logical text; it now behaves
that way toward the rest of the editor:

- saving a justified buffer writes the LOGICAL text: before-save-hook
  unjustifies (spans remembered as markers), after-save-hook restores
  the justified view; the deterministic pair stays off undo history.
  Previously soft newlines and break hyphens were silently persisted
  to disk and the original whitespace was unrecoverable after reopen.
- renderer marker properties (ekp-glue/soft-break/soft-hyphen/hidden/
  justified) are registered in text-property-default-nonsticky: text
  typed after a glue no longer inherits the marker and is no longer
  deleted as a synthesized space by the next re-flow.
- pure re-layout preserves buffer-modified-p when it was nil: no more
  lock files, auto-saves and "buffer modified" prompts from merely
  enabling ekp-auto-justify-mode.
- changes applied by undo are not re-dirtied (no timer war against
  the user's undo sequence).
- switching major modes tears justified state down cleanly
  (change-major-mode-hook), widening first so narrowing never leaves
  justified orphans.

5 new ERT tests (72 total).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 01:00:27 +08:00
Kinneyzhang
dc2f6da846 feat: visible-first lazy re-flow for large buffers; packaging polish
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>
2026-07-26 23:23:25 +08:00
Kinneyzhang
4d9a018fbf feat: verbatim protection — code blocks and mode hooks
Paragraphs stay verbatim (never justified, byte-identical through
justify/auto-mode/unjustify) when they carry the ekp-verbatim text
property, wear a face from ekp-region-skip-faces (point it at
org-block / markdown-code-face and code blocks are exempt), or match
the buffer-local ekp-region-skip-predicate — three layers of the same
general mechanism, no mode-specific code.  Inline code inside prose
is the existing ekp-no-break atom (rigid, unbreakable, unhyphenated).

New commands: ekp-verbatim-region / ekp-clear-verbatim-region.
Core engine untouched: block protection lives entirely in the region
layer, so skipped text never even reaches the tokenizer.

Tests: 66 ERT green (property / face / predicate paths, exact
roundtrips around justified prose).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 21:40:43 +08:00
Kinneyzhang
720b1cd0e4 feat: parshape and first-line indentation (Elisp 2D path)
ekp-parshape gives each line its own (INDENT . WIDTH) — TeX \parshape
— with the last entry repeating; ekp-first-line-indent is sugar for
the CJK 2-em paragraph convention (t derives 2 em from the paragraph's
CJK font, or pass pixels).  Line-number-dependent widths require the
(position × line-count) DP, so both ride the loose engine and bypass
the C module, exactly like looseness.

Indents render as leading spacers marked ekp-glue, so buffer
roundtrips remain character- and property-exact.  Composes with
alignment modes (indent adds to the centering lead) and protrusion.

Tests: 63 ERT green; fuzz 300/300.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 21:37:58 +08:00
Kinneyzhang
95ed2b32d1 feat: buffer-level justification — region commands and auto-justify mode
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>
2026-07-26 20:23:03 +08:00