ekp/DEVELOPER.md
2026-01-25 13:50:46 +08:00

6.8 KiB

Developer Documentation for Emacs-KP

This document details the internal architecture, API, and algorithms of emacs-kp. It is intended for contributors and advanced users who want to understand how the package works or extend it.

1. Architecture Overview

emacs-kp follows a layered architecture to separate text processing, layout computation, and rendering.

┌─────────────────────────────────────────────────────────────────┐
│                      User API Layer (ekp.el)                    │
│  ekp-pixel-justify  ekp-pixel-range-justify  ekp-clear-caches   │
└─────────────────────────────────────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────┐
│                    Caching Layer (ekp-utils.el)                 │
│  ekp--get-para (paragraph cache)  ekp-dp-cache (DP result cache)│
└─────────────────────────────────────────────────────────────────┘
                               │
               ┌───────────────┴───────────────┐
               ▼                               ▼
┌─────────────────────────┐     ┌─────────────────────────┐
│   Pure Elisp Path       │     │   C Module Path         │
│   ekp--dp-cache-elisp   │     │   ekp--dp-cache-via-c   │
│   (O(n²) DP in Elisp)   │     │   (calls C for DP)      │
└─────────────────────────┘     └─────────────────────────┘
                                               │
                                               ▼
                                 ┌─────────────────────────┐
                                 │   C Dynamic Module      │
                                 │   ekp_break_with_prefixes│
                                 │   (8-thread parallel)   │
                                 └─────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────┐
│                    Rendering Layer                               │
│  ekp--render-justified (apply breaks, insert glue pixels)       │
└─────────────────────────────────────────────────────────────────┘

2. Elisp Core (ekp.el)

Data Structures

ekp-para Struct

The central data structure is ekp-para, which represents a preprocessed paragraph. It is cached to avoid re-tokenizing and re-measuring text.

(cl-defstruct ekp-para
  string           ; Original text with properties
  latin-font       ; Detected Latin font
  cjk-font         ; Detected CJK font
  boxes            ; Vector of box strings
  boxes-widths     ; Vector of box pixel widths
  boxes-types      ; Vector of (start-type . end-type)
  glues-types      ; Vector of glue type symbols (lws, mws, cws, nws)
  hyphen-pixel     ; Width of hyphen character
  hyphen-positions ; Vector of hyphenable box indices
  ideal-prefixs    ; Prefix sum: ideal widths (for O(1) width calc)
  min-prefixs      ; Prefix sum: minimum widths
  max-prefixs      ; Prefix sum: maximum widths
  dp-cache)        ; Hash table: line-pixel → DP result

Glue Types

  • lws: Latin Word Space (between Latin words)
  • mws: Mixed Word Space (between Latin and CJK)
  • cws: CJK Word Space (between CJK chars)
  • nws: No Word Space (fixed)

Core Functions

(ekp-pixel-justify STRING LINE-PIXEL)

Justifies STRING to LINE-PIXEL width.

  1. Checks cache for existing ekp-para.
  2. If miss, creates ekp-para (tokenize, measure, hyphenate).
  3. Calls DP engine (Elisp or C) to get breaks.
  4. Renders result using display properties (specifically space display property for glues).

(ekp-pixel-range-justify STRING MIN-PIXEL MAX-PIXEL)

Finds the "best" width within a range. Uses ternary search (O(log n)) to minimize demerits. Useful for finding the optimal width for a specific paragraph.

(ekp-param-set ...)

Sets the 9 spacing parameters (Ideal/Stretch/Shrink for LWS/MWS/CWS).

3. C Dynamic Module (ekp_c)

For large texts, the C module provides ~20x speedup by parallelizing the O(n²) Dynamic Programming phase.

Source Structure

  • ekp_c/ekp.c: Emacs module entry point.
  • ekp_c/ekp_kp.c: The Knuth-Plass algorithm implementation.
  • ekp_c/ekp_thread_pool.c: Worker thread pool.
  • ekp_c/ekp_hyphen.c: Liang's hyphenation algorithm.

C API (exposed to Elisp)

(ekp-c-init)

Initializes the module and thread pool.

(ekp-c-break-with-prefixes ...)

The low-level DP function. It takes flat arrays (pointers) from Elisp:

  • Prefix sums (ideal, min, max)
  • Glue parameters per box
  • Hyphen positions
  • Target line width

It returns a list of break indices and total cost.

Memory Model

  • Zero Copy: Elisp passes pointers to vector data directly to C.
  • Flat Arrays: Data is structured as parallel arrays for cache efficiency.
  • Thread Safety: The module uses a fixed thread pool. The DP algorithm uses a wavefront pattern for parallelizing the inner loop.

4. Algorithm Details

The Knuth-Plass Algorithm

Based on the 1981 paper "Breaking Paragraphs into Lines".

Cost Function (Demerits): D = (LinePenalty + Badness)² + Penalty²

Badness: 100 * |Adjustment / Flexibility|³

CJK Extensions

  • Boxes: Each CJK character is a separate box.
  • Glues: Specific glue types for CJK-CJK and CJK-Latin transitions allow fine-tuning spacing (e.g., adding slight breathing room between English and Chinese).

Hyphenation

Uses Frank Liang's algorithm (standard in TeX).

  • Patterns are loaded from dictionaries/*.dic.
  • ekp-hyphen.el handles this in pure Elisp.
  • C module has its own implementation (ekp_hyphen.c) for speed if needed, though currently Elisp handles tokenization.