fix: preserve property defaults and Emacs 31 compatibility
This commit is contained in:
parent
97cb6a6e35
commit
711b0b1abe
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,3 +5,4 @@ archive
|
|||||||
*.o
|
*.o
|
||||||
*.elc
|
*.elc
|
||||||
AGENTS.md
|
AGENTS.md
|
||||||
|
!AGENTS.md
|
||||||
|
|||||||
@ -2,6 +2,13 @@
|
|||||||
|
|
||||||
## phase-kp-overhaul-20260726
|
## phase-kp-overhaul-20260726
|
||||||
|
|
||||||
|
- 2026-09-01 restore Emacs 31.1 strict byte compilation without behavior
|
||||||
|
changes (`task044`, `issue029`):
|
||||||
|
`.phrase/phases/phase-kp-overhaul-20260726/change_post_audit_hardening_20260820.md`
|
||||||
|
- 2026-08-31 restore the Emacs 31 fresh-source baseline while preserving
|
||||||
|
multibyte policy ownership and real-buffer marker noninheritance
|
||||||
|
(`task043`):
|
||||||
|
`.phrase/phases/phase-kp-overhaul-20260726/change_post_audit_hardening_20260820.md`
|
||||||
- 2026-08-20 plan and implement post-audit hardening (`task037`–`task042`,
|
- 2026-08-20 plan and implement post-audit hardening (`task037`–`task042`,
|
||||||
`task030` source-fresh evaluator continuation; `issue022`–`issue028`):
|
`task030` source-fresh evaluator continuation; `issue022`–`issue028`):
|
||||||
`.phrase/phases/phase-kp-overhaul-20260726/change_post_audit_hardening_20260820.md`
|
`.phrase/phases/phase-kp-overhaul-20260726/change_post_audit_hardening_20260820.md`
|
||||||
|
|||||||
@ -28,3 +28,4 @@
|
|||||||
- issue026 [ ] [Showcase loses automatic inline policy and test runners can false-green.](../phases/phase-kp-overhaul-20260726/issue_showcase_test_freshness_20260820.md)
|
- issue026 [ ] [Showcase loses automatic inline policy and test runners can false-green.](../phases/phase-kp-overhaul-20260726/issue_showcase_test_freshness_20260820.md)
|
||||||
- issue027 [x] [Manual integrations remain installed with no projection owner.](../phases/phase-kp-overhaul-20260726/issue_integration_no_projection_20260820.md)
|
- issue027 [x] [Manual integrations remain installed with no projection owner.](../phases/phase-kp-overhaul-20260726/issue_integration_no_projection_20260820.md)
|
||||||
- issue028 [x] [Locked live evaluator can measure stale candidate bytecode.](../phases/phase-kp-overhaul-20260726/issue_live_evaluator_source_freshness_20260820.md)
|
- issue028 [x] [Locked live evaluator can measure stale candidate bytecode.](../phases/phase-kp-overhaul-20260726/issue_live_evaluator_source_freshness_20260820.md)
|
||||||
|
- issue029 [x] [Emacs 31.1 WERROR rejects obsolete single-binding let macros.](../phases/phase-kp-overhaul-20260726/issue_emacs31_strict_compile_20260901.md)
|
||||||
|
|||||||
@ -1,5 +1,38 @@
|
|||||||
# Change: Post-Audit Hardening 2026-08-20
|
# Change: Post-Audit Hardening 2026-08-20
|
||||||
|
|
||||||
|
## 2026-09-01 — Restore Emacs 31.1 strict byte compilation
|
||||||
|
|
||||||
|
- **Modify** `ekp.el`, `ekp-utils.el`, and `ekp-hyphen.el` by replacing six
|
||||||
|
obsolete single-binding `if-let`/`when-let` forms with the behavior-identical
|
||||||
|
starred variants.
|
||||||
|
- **Modify** `tests/ekp-live-commit-evaluator.el` in the same mechanical way so
|
||||||
|
maintained performance tooling is strict-compile clean too.
|
||||||
|
- **Add** `issue029` and close `task044` with exact environment and evidence.
|
||||||
|
- **Behavior/Risk:** no data flow, branch, public API, layout, hyphenation,
|
||||||
|
font, module-path, or evaluator behavior changes.
|
||||||
|
- **Evidence:** root `make`; Emacs 31.1 WERROR production and changed-tool
|
||||||
|
compilation; normal and seed-20260901 random ERT 296/296; C build; 300-case
|
||||||
|
parity fuzz; release invariants; local 49-entry dictionary manifest/hash;
|
||||||
|
checkdoc; and diff-check. The user explicitly excluded the network-backed
|
||||||
|
fixed-upstream dictionary fetch, so it is not claimed.
|
||||||
|
|
||||||
|
## 2026-08-31 — Restore the Emacs 31 fresh-source baseline
|
||||||
|
|
||||||
|
- **Modify** `ekp.el` and `ekp-buffer.el` so layout and projection marker
|
||||||
|
properties update the toplevel default of Emacs 31's automatically
|
||||||
|
buffer-local `text-property-default-nonsticky`; new and existing buffers
|
||||||
|
that have no explicit override now inherit the nonsticky protocol.
|
||||||
|
- **Modify** `tests/ekp-tests.el` and `tests/ekp-buffer-tests.el` so policy
|
||||||
|
ownership fixtures remain mutable multibyte strings while mutating ASCII
|
||||||
|
code points that Emacs 31 permits in place. The tests still prove that
|
||||||
|
cache keys own the original policy values.
|
||||||
|
- **Behavior/Risk:** no layout algorithm or public policy changes. This fixes
|
||||||
|
marker noninheritance in real buffers and removes an Emacs 31-only fixture
|
||||||
|
mutation error.
|
||||||
|
- **Evidence:** focused ERT 3/3; source-first normal and seed-20260831 random
|
||||||
|
ERT 296/296; source load, release invariants, 49-entry dictionary manifest,
|
||||||
|
and pinned dictionary update checks pass.
|
||||||
|
|
||||||
## 2026-08-20 — Lock follow-up goals and task boundaries
|
## 2026-08-20 — Lock follow-up goals and task boundaries
|
||||||
|
|
||||||
- **Add** `plan_post_audit_hardening_20260820.md` with the evidence-backed
|
- **Add** `plan_post_audit_hardening_20260820.md` with the evidence-backed
|
||||||
|
|||||||
@ -0,0 +1,44 @@
|
|||||||
|
# issue029 — Emacs 31.1 strict compilation rejects obsolete let macros
|
||||||
|
|
||||||
|
Status: resolved
|
||||||
|
|
||||||
|
## Environment
|
||||||
|
|
||||||
|
- GNU Emacs 31.1 development build `fac653279dcb`
|
||||||
|
- macOS Apple Silicon
|
||||||
|
- EKP HEAD `97cb6a6e3506`
|
||||||
|
|
||||||
|
## Reproduction
|
||||||
|
|
||||||
|
Run the repository CI byte-compilation command with
|
||||||
|
`byte-compile-error-on-warn` set to `t` against the four production files.
|
||||||
|
|
||||||
|
## Expected vs actual
|
||||||
|
|
||||||
|
- Expected: production and maintained test tools compile with warnings treated
|
||||||
|
as errors on the recorded Emacs executable.
|
||||||
|
- Actual: Emacs 31.1 rejects six single-binding `if-let`/`when-let` forms in
|
||||||
|
production; strict changed-tool compilation also finds three such forms in
|
||||||
|
`tests/ekp-live-commit-evaluator.el`.
|
||||||
|
|
||||||
|
## Investigation and fix
|
||||||
|
|
||||||
|
The macros became obsolete in Emacs 31.1. Every affected form has exactly one
|
||||||
|
binding, so the starred replacement has identical binding, truth, body, and
|
||||||
|
fallback behavior and needs no compatibility branch. Replace only those nine
|
||||||
|
macro names; do not alter layout or evaluator data flow.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
`task044` passed root integration compilation; WERROR production and changed
|
||||||
|
evaluator compilation; normal and seed-20260901 random ERT 296/296; warning-clean
|
||||||
|
C build; 300-case C/Elisp parity fuzz; release invariants; local 49-entry
|
||||||
|
dictionary manifest/hash; checkdoc; and diff-check. The network-backed
|
||||||
|
fixed-upstream dictionary fetch was stopped and excluded at the user's explicit
|
||||||
|
direction; no upstream-provenance claim is made.
|
||||||
|
|
||||||
|
Resolved At: 2026-09-01
|
||||||
|
|
||||||
|
Resolved By: C1a nine-repository verification
|
||||||
|
|
||||||
|
Commit: pending (protected pre-existing dirty worktree)
|
||||||
@ -80,3 +80,15 @@ Stop only when all tasks in this plan have current issue/task evidence,
|
|||||||
closed issues have user-visible or contract-level verification, `issue018`
|
closed issues have user-visible or contract-level verification, `issue018`
|
||||||
is either genuinely closed by its locked gate or explicitly remains open with
|
is either genuinely closed by its locked gate or explicitly remains open with
|
||||||
fresh measurements, and the complete diff is committed and reviewed.
|
fresh measurements, and the complete diff is committed and reviewed.
|
||||||
|
|
||||||
|
## 2026-09-01 Emacs 31 strict-compile follow-up
|
||||||
|
|
||||||
|
- `task044` / `issue029` replace the six remaining single-binding
|
||||||
|
`if-let`/`when-let` forms in production and the three forms in the live
|
||||||
|
evaluator with their semantically identical starred variants.
|
||||||
|
- No layout, hyphenation, font-selection, module-path, or evaluator behavior
|
||||||
|
may change. Existing ERT coverage locks those paths; Emacs 31.1 WERROR byte
|
||||||
|
compilation is the regression gate for the compatibility defect itself.
|
||||||
|
- Closure requires root integration compilation, strict production and changed
|
||||||
|
test-tool compilation, the complete normal and seeded-random ERT suites, C
|
||||||
|
build/parity fuzz, release/dictionary checks, checkdoc, and diff-check.
|
||||||
|
|||||||
@ -1,5 +1,23 @@
|
|||||||
# Tasks: Post-Audit Hardening 2026-08-20
|
# Tasks: Post-Audit Hardening 2026-08-20
|
||||||
|
|
||||||
|
- task044 [x] Restore Emacs 31.1 WERROR compilation by replacing obsolete
|
||||||
|
single-binding `if-let`/`when-let` forms with behavior-identical
|
||||||
|
`if-let*`/`when-let*` forms in production and the live evaluator.
|
||||||
|
- Source: `issue029` and the C1a nine-repository strict-compile gate.
|
||||||
|
- Validation: root `make`; Emacs 31.1 WERROR production and changed-tool
|
||||||
|
compilation; normal/random ERT 296/296; C build and 300-case fuzz;
|
||||||
|
release, local 49-entry dictionary manifest/hash, checkdoc, and diff gates.
|
||||||
|
The network-backed fixed-upstream dictionary fetch was explicitly excluded
|
||||||
|
by the user and is not claimed as evidence.
|
||||||
|
|
||||||
|
- task043 [x] Restore the Emacs 31 fresh-source baseline without weakening
|
||||||
|
cache-ownership or marker-noninheritance contracts. Keep policy fixtures
|
||||||
|
mutable and multibyte, and install EKP marker properties in the global
|
||||||
|
`text-property-default-nonsticky` default used by real buffers.
|
||||||
|
- Source: Emacs 31 fresh-source failures in the M0a baseline.
|
||||||
|
- Validation: focused ERT 3/3, normal and seeded-random ERT 296/296,
|
||||||
|
source-load, release, and 49-entry dictionary gates.
|
||||||
|
|
||||||
- task037 [x] Validate public buffer widths before mutation and make rejected
|
- task037 [x] Validate public buffer widths before mutation and make rejected
|
||||||
`ekp-justify-region` requests failure-atomic. Add red tests for zero,
|
`ekp-justify-region` requests failure-atomic. Add red tests for zero,
|
||||||
negative, non-integer, and projection-preservation cases.
|
negative, non-integer, and projection-preservation cases.
|
||||||
|
|||||||
144
AGENTS.md
Normal file
144
AGENTS.md
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
# 核心协议:意图识别(强制执行)
|
||||||
|
|
||||||
|
在处理任何请求之前,你必须先识别用户的意图并遵循相应的协议。
|
||||||
|
|
||||||
|
## 1. 🌱 启动 / 立项 / 模糊想法
|
||||||
|
**触发条件**:用户想要开启新项目、新阶段,或者只有一个模糊的想法。
|
||||||
|
**行动**:
|
||||||
|
1. **扫描**:读取 `.phrase/modules/pr_faq.md` 的 YAML 元数据以确认匹配。
|
||||||
|
2. **加载**:仅当匹配成功时,完整读取该文件内容。
|
||||||
|
3. **执行**:扮演“严格的产品经理”角色。进行访谈以起草亚马逊风格的 PR/FAQ。
|
||||||
|
4. **约束**:在 PR/FAQ 最终确定之前,禁止开始编写代码或拆解任务。
|
||||||
|
|
||||||
|
## 2. 🔨 编码 / 重构 / 审查
|
||||||
|
**触发条件**:用户请求代码实现、Bug 修复、重构或代码审查。
|
||||||
|
**行动**:
|
||||||
|
1. **扫描**:读取 `.phrase/modules/linus_coding.md` 的 YAML 元数据以确认匹配。
|
||||||
|
2. **加载**:仅当匹配成功时,完整读取该文件内容。
|
||||||
|
3. **执行**:扮演“Linus Torvalds”角色。
|
||||||
|
4. **约束**:在编码前和编码过程中,严格执行“5 层思考模型”和“好品味”判断。
|
||||||
|
|
||||||
|
## 3. ✍️ 文案 / 营销 / 文档
|
||||||
|
**触发条件**:用户需要撰写 README、发布说明、产品介绍或营销文案。
|
||||||
|
**行动**:
|
||||||
|
1. **扫描**:读取 `.phrase/modules/copywriting.md` 的 YAML 元数据以确认匹配。
|
||||||
|
2. **加载**:仅当匹配成功时,完整读取该文件内容。
|
||||||
|
3. **执行**:扮演“转化率文案专家”角色。
|
||||||
|
4. **约束**:遵循“结论先行”、“降低成本”、“可感知的具体”等 10 大原则。
|
||||||
|
|
||||||
|
## 4. 🌐 浏览器 / 网页自动化 / 爬虫
|
||||||
|
**触发条件**:用户需要访问网页、抓取数据、截图、测试 Web UI 或填写表单。
|
||||||
|
**行动**:
|
||||||
|
1. **扫描**:读取 `.phrase/modules/agent-browser.md` 的 YAML 元数据以确认匹配。
|
||||||
|
2. **检查**:确保环境中已安装 `agent-browser` 依赖。
|
||||||
|
3. **加载**:仅当匹配成功且依赖满足时,完整读取该文件内容。
|
||||||
|
4. **执行**:使用 CLI 工具进行浏览器自动化操作。
|
||||||
|
|
||||||
|
## 5. 📋 任务执行(默认)
|
||||||
|
**触发条件**:用户想要执行一个具体的、已定义的任务。
|
||||||
|
**行动**:遵循下方的“文档驱动开发”工作流。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
“文档驱动开发(Doc-Driven Development)”:先锁定文档 → 拆 `taskNNN` → 实现与验证 → 回写文档。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. 原则(按优先级)
|
||||||
|
- 仓库既有规范 > 本文;冲突时按 `README`/`STYLEGUIDE` 等执行,并在 `issue_*`/`change_*` 记录取舍。
|
||||||
|
- 文档为事实来源:需求、交互、接口只能来自 `spec/plan/tech-refer/adr`。
|
||||||
|
- 单次仅处理一个原子任务;所有改动可追溯到 `taskNNN` 与其依据(`spec`/`issue`/`adr`)。
|
||||||
|
- 每个 `taskNNN` 必须说明验证方式(测试或手动步骤)。
|
||||||
|
- 实现完成必须回写:`task_*`、`change_*`,必要时更新 `spec_*`/`issue_*`/`adr_*`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 仓库结构与文档
|
||||||
|
- 代码根:`App/`, `Core/`, `UI/`, `Shared/`, `Tests/`, `Assets/`, `Samples/`, `Schemas/`, `StackWM-Bridging-Header.h`。保持分层清晰,`Tests/` 镜像核心模块。
|
||||||
|
- 文档根:`.phrase/`
|
||||||
|
- 阶段:`.phrase/phases/phase-<purpose>-<YYYYMMDD>/`
|
||||||
|
- 全局索引:`.phrase/docs/`
|
||||||
|
- `Docs/` 为外部文档,可继续独立存放。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Phase 工作流
|
||||||
|
1. **Phase Gate**(仅当用户明确开启新阶段):在新 `phase-*` 目录创建最小集 `spec_*`, `plan_*`, `task_*`, 视需求补 `tech-refer_*`/`adr_*`,`issue_*` 可后置。
|
||||||
|
2. **In-Phase Loop**(默认):
|
||||||
|
- 新需求 → 更新当前 `plan_*` → 拆 `taskNNN`。
|
||||||
|
- 实现 → 在 `task_*` 中新增/更新并执行对应任务。
|
||||||
|
- Bug → 在 `.phrase/docs/ISSUES.md` 登记 `issueNNN`,在 phase 写详情,再拆 `taskNNN`。
|
||||||
|
- 不可逆决策 → 先写 `adr_*` 或在 `tech-refer_*` 增 “Decision”。
|
||||||
|
3. **Task 闭环**:完成后需
|
||||||
|
1) 将 `task_*` 条目标记 `[x]`
|
||||||
|
2) 在 phase `change_*` 记录条目,并于 `.phrase/docs/CHANGE.md` 加索引
|
||||||
|
3) 若影响交互,更新对应 `spec_*`
|
||||||
|
4) 若解决问题,更新 `ISSUES.md` 和 issue 详情(含验证结论)
|
||||||
|
|
||||||
|
当目标与当前 phase purpose 明显不同、需要独立里程碑或架构大重构时,可建议开启新 phase,但需用户确认。
|
||||||
|
|
||||||
|
### Phase 生命周期
|
||||||
|
- 开启阶段:在 `.phrase/phases/phase-<purpose>-<date>/` 下创建 `spec/plan/task/...`。
|
||||||
|
- 阶段完结:用户确认后,将整个目录重命名为 `DONE-phase-<purpose>-<date>/`,同时把主要文档也按规则改为 `DONE-PLAN-*`、`DONE-TASK-*` 等,确保一眼可见结项状态。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Task / Issue 规范
|
||||||
|
- `taskNNN` 为三位递增 ID(`task001` 起),不可重排或复用;拆分/合并需创建新 ID 并在原任务注明流向。
|
||||||
|
- 任何对 `task_*` 的增删改/勾选都要在当前 phase `change_*` 记录一次,可批量合并但必须可追溯。
|
||||||
|
- 原子任务标准:一次工作会话可完成、产出可观察、可独立验证,既不过细也不过粗。
|
||||||
|
- Issue:
|
||||||
|
- 全局索引:`.phrase/docs/ISSUES.md` 用 `issueNNN [ ]/[x]` 并链接 phase 详情。
|
||||||
|
- 详情文件 `issue_<purpose>_<YYYYMMDD>.md` 需含环境、复现、调查、根因、修复、验证、关联的 `taskNNN`/提交。
|
||||||
|
- 用户可感知问题需在标记 `[x]` 前获得确认,并记录 `Resolved At/By/Commit`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Build / Test / Dev
|
||||||
|
- 首选仓库入口:若提供 Makefile、GitHub Actions、或 scripts/,优先使用。
|
||||||
|
- windows系统下 emacs 路径: "C:\Users\26289\Apps\emacs-30.2\emacs-30.2\bin"
|
||||||
|
- 常见 Elisp 验证方式:
|
||||||
|
- 运行 ERT 测试:emacs -Q --batch -L . -l <test-file> -f ert-run-tests-batch-and-exit
|
||||||
|
- 交互手动验证:emacs -Q -L . -l <pkg>.el 后在 UI 中 M-x 执行命令
|
||||||
|
- 可选:lint/格式(按仓库约定),例如 package-lint、checkdoc、byte-compile(若项目采用)。
|
||||||
|
- 测试用例全部写在根目录的 tests/ 目录下
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 编码与验证
|
||||||
|
- 遵循仓库已有编码规范(缩进、命名、lexical-binding 等)。
|
||||||
|
- 明确支持的 Emacs 版本范围;涉及 API 差异时要写清楚 fallback 或条件分支策略。
|
||||||
|
- 尽量保持改动最小化:除非任务是“清理”,否则避免批量格式化与无关重排。
|
||||||
|
- 关键路径加可诊断日志(遵循项目 logging 方案)。
|
||||||
|
- 测试优先覆盖核心逻辑;UI/系统胶水可提供手动验证步骤。测试必须确定性,必要时注入依赖或 mock。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 文档更新与 Changelog
|
||||||
|
- `change_*`:phase 内的真实变更记录;每个完成的 `taskNNN` 至少一条,包含日期、文件/路径、Add|Modify|Delete、受影响函数、行为/风险说明,按时间倒序。
|
||||||
|
- `.phrase/docs/CHANGE.md`:仅索引与摘要,指向对应 phase `change_*` 条目;可按工作会话批量更新。
|
||||||
|
- `spec_*`/`plan_*`/`tech-refer_*`/`adr_*`/`issue_*` 均需随变更回写(增量即可),保持单一事实来源。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 提交、PR 与安全
|
||||||
|
- 默认使用 Conventional Commits(`feat:`, `fix:`, `docs:`, `test:`, `chore:` 等),一份提交聚焦单个 `taskNNN`。
|
||||||
|
- PR 描述需列出关联的 `taskNNN`/`issueNNN`、动机、行为变化、验证方式、风险/回滚方案,并在 UI 变化时附截图/GIF。
|
||||||
|
- 禁止提交密钥、token、证书、真实用户数据;涉及权限/配置的任务,需在 `spec_*` 和 `tech-refer_*` 清楚描述失败反馈、API 边界与排查方式。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. 模板速览
|
||||||
|
- `spec`: Summary / Goals & Non-goals / User Flows(操作→反馈→回退)/ Edge Cases / Acceptance Criteria
|
||||||
|
- `plan`: Milestones / Scope / Priorities / Risks & Dependencies /(可选)Rollback
|
||||||
|
- `tech-refer`: Options / Proposed Approach / Interfaces & APIs / Trade-offs / Risks & Mitigations
|
||||||
|
- `task`: `task001 [ ] 产出 + 验证方式 + 影响范围`
|
||||||
|
- `issue`: `issueNNN [ ] Summary + Environment + Repro + Expected vs Actual + Investigation + Fix + Verification + User Confirmation + Resolved At/By/Commit`
|
||||||
|
- `adr`: Context / Decision / Alternatives / Consequences / Rollback
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. 协作表达提示
|
||||||
|
- 解释方案时优先描述用户操作(快捷键/鼠标/命令)、可见反馈、撤销/失败路径、边界情况。
|
||||||
|
- 引用文档时用“文件名 + 小节”口语化说明,不逐字背诵。
|
||||||
|
- 提供可选方案时说明它们属于当前还是后续里程碑,帮助用户决策。
|
||||||
@ -253,7 +253,7 @@ caps the narrowest live window at N pixels."
|
|||||||
"Unicode whitespace that cannot use the ASCII-space projection path.")
|
"Unicode whitespace that cannot use the ASCII-space projection path.")
|
||||||
|
|
||||||
(dolist (property ekp-buffer--owned-properties)
|
(dolist (property ekp-buffer--owned-properties)
|
||||||
(setf (alist-get property text-property-default-nonsticky) t))
|
(ekp--make-text-property-default-nonsticky property))
|
||||||
|
|
||||||
;;; Setup
|
;;; Setup
|
||||||
|
|
||||||
|
|||||||
@ -169,7 +169,7 @@ LANGUAGE identifies the dictionary in that condition."
|
|||||||
(lambda (m) (string (string-to-number
|
(lambda (m) (string (string-to-number
|
||||||
(match-string 1 m) 16)))
|
(match-string 1 m) 16)))
|
||||||
line))
|
line))
|
||||||
(when-let ((parsed (ekp-hyphen--parse-pattern line)))
|
(when-let* ((parsed (ekp-hyphen--parse-pattern line)))
|
||||||
(puthash (car parsed) (cdr parsed) patterns)
|
(puthash (car parsed) (cdr parsed) patterns)
|
||||||
(setq maxlen (max maxlen (length (car parsed))))))))
|
(setq maxlen (max maxlen (length (car parsed))))))))
|
||||||
(forward-line 1)))
|
(forward-line 1)))
|
||||||
@ -227,7 +227,7 @@ by default the dictionary's own LEFTHYPHENMIN/RIGHTHYPHENMIN apply
|
|||||||
(let ((path (or (and lang (ekp-hyphen--resolve-lang lang)) file)))
|
(let ((path (or (and lang (ekp-hyphen--resolve-lang lang)) file)))
|
||||||
(unless path
|
(unless path
|
||||||
(signal 'ekp-hyphen-dictionary-not-found (list lang)))
|
(signal 'ekp-hyphen-dictionary-not-found (list lang)))
|
||||||
(when-let ((count (gethash path ekp-hyphen--unsupported-cache)))
|
(when-let* ((count (gethash path ekp-hyphen--unsupported-cache)))
|
||||||
(signal 'ekp-hyphen-unsupported-pattern
|
(signal 'ekp-hyphen-unsupported-pattern
|
||||||
(list (or lang path) path count)))
|
(list (or lang path) path count)))
|
||||||
(let ((h (or (gethash path ekp-hyphen--cache)
|
(let ((h (or (gethash path ekp-hyphen--cache)
|
||||||
|
|||||||
@ -122,7 +122,7 @@ Use the blank glyph of STRING's Latin font; for a monospace font
|
|||||||
that width is the space's own advance."
|
that width is the space's own advance."
|
||||||
;; font is monospace, use the pixel of blank
|
;; font is monospace, use the pixel of blank
|
||||||
;; as word spacing pixel
|
;; as word spacing pixel
|
||||||
(if-let ((font-family (ekp-monospace-p string)))
|
(if-let* ((font-family (ekp-monospace-p string)))
|
||||||
(ekp--measured-width
|
(ekp--measured-width
|
||||||
(propertize " " 'face `(:family ,font-family)))
|
(propertize " " 'face `(:family ,font-family)))
|
||||||
(let* ((letter (ekp-get-latin-letter string))
|
(let* ((letter (ekp-get-latin-letter string))
|
||||||
@ -134,14 +134,14 @@ that width is the space's own advance."
|
|||||||
(defun ekp-latin-font (string)
|
(defun ekp-latin-font (string)
|
||||||
"Return the font family used for STRING's Latin letters.
|
"Return the font family used for STRING's Latin letters.
|
||||||
Fall back to the default face family when STRING has no Latin letter."
|
Fall back to the default face family when STRING has no Latin letter."
|
||||||
(if-let ((letter (ekp-get-latin-letter string)))
|
(if-let* ((letter (ekp-get-latin-letter string)))
|
||||||
(ekp-font-family letter)
|
(ekp-font-family letter)
|
||||||
(face-attribute 'default :family)))
|
(face-attribute 'default :family)))
|
||||||
|
|
||||||
(defun ekp-cjk-font (string)
|
(defun ekp-cjk-font (string)
|
||||||
"Return the font family used for STRING's CJK characters.
|
"Return the font family used for STRING's CJK characters.
|
||||||
Fall back to the family of a sample CJK glyph when STRING has none."
|
Fall back to the family of a sample CJK glyph when STRING has none."
|
||||||
(if-let ((letter (ekp-get-cjk-letter string)))
|
(if-let* ((letter (ekp-get-cjk-letter string)))
|
||||||
(ekp-font-family letter)
|
(ekp-font-family letter)
|
||||||
(ekp-font-family "牛")))
|
(ekp-font-family "牛")))
|
||||||
|
|
||||||
@ -344,7 +344,7 @@ by merging boxes."
|
|||||||
|
|
||||||
(defun ekp-c-module-dir ()
|
(defun ekp-c-module-dir ()
|
||||||
"Return the C module directory."
|
"Return the C module directory."
|
||||||
(when-let ((root-dir (ekp-root-dir)))
|
(when-let* ((root-dir (ekp-root-dir)))
|
||||||
(expand-file-name "ekp_c" root-dir)))
|
(expand-file-name "ekp_c" root-dir)))
|
||||||
|
|
||||||
(defun ekp-c-module-file ()
|
(defun ekp-c-module-file ()
|
||||||
|
|||||||
14
ekp.el
14
ekp.el
@ -1790,8 +1790,8 @@ POLICY-ANALYSIS is a precomputed result from `ekp--analyze-policies'."
|
|||||||
(while (and (>= tail 0) (= (aref old tail) ?\s))
|
(while (and (>= tail 0) (= (aref old tail) ?\s))
|
||||||
(setq tail (1- tail)))
|
(setq tail (1- tail)))
|
||||||
(when (>= tail 0)
|
(when (>= tail 0)
|
||||||
(when-let ((space
|
(when-let* ((space
|
||||||
(cl-position ?\s old :from-end t :end (1+ tail))))
|
(cl-position ?\s old :from-end t :end (1+ tail))))
|
||||||
(1+ space))))))
|
(1+ space))))))
|
||||||
|
|
||||||
(defun ekp--para-has-box-type-p (para type)
|
(defun ekp--para-has-box-type-p (para type)
|
||||||
@ -3606,11 +3606,19 @@ Only property-free, context-stable 1D layouts take this fast path."
|
|||||||
'(ekp-glue ekp-soft-break ekp-soft-hyphen ekp-hidden ekp-justified)
|
'(ekp-glue ekp-soft-break ekp-soft-hyphen ekp-hidden ekp-justified)
|
||||||
"Text properties owned by the lossless render/inversion protocol.")
|
"Text properties owned by the lossless render/inversion protocol.")
|
||||||
|
|
||||||
|
(defun ekp--make-text-property-default-nonsticky (property)
|
||||||
|
"Make PROPERTY nonsticky by default in every buffer."
|
||||||
|
(let ((defaults (copy-tree
|
||||||
|
(default-toplevel-value
|
||||||
|
'text-property-default-nonsticky))))
|
||||||
|
(setf (alist-get property defaults) t)
|
||||||
|
(set-default-toplevel-value 'text-property-default-nonsticky defaults)))
|
||||||
|
|
||||||
;; Text typed next to a marker character must never inherit the
|
;; Text typed next to a marker character must never inherit the
|
||||||
;; marker: a self-inserted char inheriting `ekp-glue' would be treated
|
;; marker: a self-inserted char inheriting `ekp-glue' would be treated
|
||||||
;; as a synthesized space by the next unjustification and deleted.
|
;; as a synthesized space by the next unjustification and deleted.
|
||||||
(dolist (prop ekp--layout-marker-properties)
|
(dolist (prop ekp--layout-marker-properties)
|
||||||
(setf (alist-get prop text-property-default-nonsticky) t))
|
(ekp--make-text-property-default-nonsticky prop))
|
||||||
|
|
||||||
(defun ekp--hide-string (string)
|
(defun ekp--hide-string (string)
|
||||||
"Return STRING marked `ekp-hidden' and displayed as nothing."
|
"Return STRING marked `ekp-hidden' and displayed as nothing."
|
||||||
|
|||||||
@ -3481,8 +3481,8 @@ a zero budget still makes progress (exactly one chunk)."
|
|||||||
(ekp-buffer-test--with-text ""
|
(ekp-buffer-test--with-text ""
|
||||||
(text-mode)
|
(text-mode)
|
||||||
(let* ((ekp-use-c-module nil)
|
(let* ((ekp-use-c-module nil)
|
||||||
(suffix (copy-sequence "uX"))
|
(suffix (copy-sequence (string-as-multibyte "uX")))
|
||||||
(line-start-extra (copy-sequence "《"))
|
(line-start-extra (copy-sequence "《X"))
|
||||||
(ekp-number-unit-suffixes (list suffix))
|
(ekp-number-unit-suffixes (list suffix))
|
||||||
(ekp-token-break-policies '((number-unit . no-break)))
|
(ekp-token-break-policies '((number-unit . no-break)))
|
||||||
(ekp-kinsoku-profile 'custom)
|
(ekp-kinsoku-profile 'custom)
|
||||||
@ -3499,18 +3499,20 @@ a zero budget still makes progress (exactly one chunk)."
|
|||||||
(apply original arguments))))
|
(apply original arguments))))
|
||||||
(setq first (cdr (ekp-buffer--live-plan-entry text width)))
|
(setq first (cdr (ekp-buffer--live-plan-entry text width)))
|
||||||
(setq key (caar ekp-buffer--live-plan-cache))
|
(setq key (caar ekp-buffer--live-plan-cache))
|
||||||
|
(should (multibyte-string-p suffix))
|
||||||
|
(should (multibyte-string-p line-start-extra))
|
||||||
(should (seq-some (lambda (string) (equal string "uX"))
|
(should (seq-some (lambda (string) (equal string "uX"))
|
||||||
(ekp-buffer-test--strings-in-tree key)))
|
(ekp-buffer-test--strings-in-tree key)))
|
||||||
(should (seq-some (lambda (string) (equal string "《"))
|
(should (seq-some (lambda (string) (equal string "《X"))
|
||||||
(ekp-buffer-test--strings-in-tree key)))
|
(ekp-buffer-test--strings-in-tree key)))
|
||||||
(store-substring suffix 1 "Y")
|
(store-substring suffix 1 "Y")
|
||||||
(store-substring line-start-extra 0 "》")
|
(store-substring line-start-extra 1 "Y")
|
||||||
(let ((changed (cdr (ekp-buffer--live-plan-entry text width))))
|
(let ((changed (cdr (ekp-buffer--live-plan-entry text width))))
|
||||||
(should-not (eq changed first))
|
(should-not (eq changed first))
|
||||||
(should (= calls 2)))
|
(should (= calls 2)))
|
||||||
(should (seq-some (lambda (string) (equal string "uX"))
|
(should (seq-some (lambda (string) (equal string "uX"))
|
||||||
(ekp-buffer-test--strings-in-tree key)))
|
(ekp-buffer-test--strings-in-tree key)))
|
||||||
(should (seq-some (lambda (string) (equal string "《"))
|
(should (seq-some (lambda (string) (equal string "《X"))
|
||||||
(ekp-buffer-test--strings-in-tree key)))))))
|
(ekp-buffer-test--strings-in-tree key)))))))
|
||||||
|
|
||||||
(provide 'ekp-buffer-tests)
|
(provide 'ekp-buffer-tests)
|
||||||
|
|||||||
@ -71,19 +71,19 @@
|
|||||||
|
|
||||||
(defun ekp-live-commit-evaluator--env-number (name fallback)
|
(defun ekp-live-commit-evaluator--env-number (name fallback)
|
||||||
"Return numeric environment variable NAME, or FALLBACK."
|
"Return numeric environment variable NAME, or FALLBACK."
|
||||||
(if-let ((value (getenv name)))
|
(if-let* ((value (getenv name)))
|
||||||
(string-to-number value)
|
(string-to-number value)
|
||||||
fallback))
|
fallback))
|
||||||
|
|
||||||
(defun ekp-live-commit-evaluator--env-numbers (name fallback)
|
(defun ekp-live-commit-evaluator--env-numbers (name fallback)
|
||||||
"Return comma-separated numeric environment variable NAME, or FALLBACK."
|
"Return comma-separated numeric environment variable NAME, or FALLBACK."
|
||||||
(if-let ((value (getenv name)))
|
(if-let* ((value (getenv name)))
|
||||||
(mapcar #'string-to-number (split-string value "," t "[ \t]+"))
|
(mapcar #'string-to-number (split-string value "," t "[ \t]+"))
|
||||||
fallback))
|
fallback))
|
||||||
|
|
||||||
(defun ekp-live-commit-evaluator--env-strings (name fallback)
|
(defun ekp-live-commit-evaluator--env-strings (name fallback)
|
||||||
"Return comma-separated string environment variable NAME, or FALLBACK."
|
"Return comma-separated string environment variable NAME, or FALLBACK."
|
||||||
(if-let ((value (getenv name)))
|
(if-let* ((value (getenv name)))
|
||||||
(split-string value "," t "[ \t]+")
|
(split-string value "," t "[ \t]+")
|
||||||
fallback))
|
fallback))
|
||||||
|
|
||||||
|
|||||||
@ -2052,7 +2052,13 @@ module is bypassed automatically (it has no looseness support)."
|
|||||||
'(ekp-glue ekp-soft-break ekp-soft-hyphen ekp-hidden
|
'(ekp-glue ekp-soft-break ekp-soft-hyphen ekp-hidden
|
||||||
ekp-justified)))
|
ekp-justified)))
|
||||||
(dolist (property ekp--layout-marker-properties)
|
(dolist (property ekp--layout-marker-properties)
|
||||||
(should (eq (alist-get property text-property-default-nonsticky) t))))
|
(should
|
||||||
|
(eq (alist-get property
|
||||||
|
(default-value 'text-property-default-nonsticky))
|
||||||
|
t))
|
||||||
|
(with-temp-buffer
|
||||||
|
(should
|
||||||
|
(eq (alist-get property text-property-default-nonsticky) t)))))
|
||||||
|
|
||||||
(ert-deftest ekp-test-layout-plan-maps-source-gaps-and-breaks ()
|
(ert-deftest ekp-test-layout-plan-maps-source-gaps-and-breaks ()
|
||||||
"The semantic plan must retain source offsets for every visual decision."
|
"The semantic plan must retain source offsets for every visual decision."
|
||||||
@ -2813,8 +2819,8 @@ returned the stale looseness-0 layout for the same (string, width)."
|
|||||||
"Policy-analysis cache keys must own mutable public policy strings."
|
"Policy-analysis cache keys must own mutable public policy strings."
|
||||||
(ekp-tests--with-clean-state
|
(ekp-tests--with-clean-state
|
||||||
(let* ((ekp-use-c-module nil)
|
(let* ((ekp-use-c-module nil)
|
||||||
(suffix (copy-sequence "uX"))
|
(suffix (copy-sequence (string-as-multibyte "uX")))
|
||||||
(line-start-extra (copy-sequence "《"))
|
(line-start-extra (copy-sequence "《X"))
|
||||||
(ekp-number-unit-suffixes (list suffix))
|
(ekp-number-unit-suffixes (list suffix))
|
||||||
(ekp-token-break-policies '((number-unit . no-break)))
|
(ekp-token-break-policies '((number-unit . no-break)))
|
||||||
(ekp-kinsoku-profile 'custom)
|
(ekp-kinsoku-profile 'custom)
|
||||||
@ -2823,6 +2829,8 @@ returned the stale looseness-0 layout for the same (string, width)."
|
|||||||
(width 16)
|
(width 16)
|
||||||
cache-key first-plan)
|
cache-key first-plan)
|
||||||
(setq first-plan (ekp-layout-plan text width))
|
(setq first-plan (ekp-layout-plan text width))
|
||||||
|
(should (multibyte-string-p suffix))
|
||||||
|
(should (multibyte-string-p line-start-extra))
|
||||||
(should
|
(should
|
||||||
(seq-some
|
(seq-some
|
||||||
(lambda (interval)
|
(lambda (interval)
|
||||||
@ -2835,13 +2843,13 @@ returned the stale looseness-0 layout for the same (string, width)."
|
|||||||
(should cache-key)
|
(should cache-key)
|
||||||
(should (seq-some (lambda (string) (equal string "uX"))
|
(should (seq-some (lambda (string) (equal string "uX"))
|
||||||
(ekp-tests--strings-in-tree cache-key)))
|
(ekp-tests--strings-in-tree cache-key)))
|
||||||
(should (seq-some (lambda (string) (equal string "《"))
|
(should (seq-some (lambda (string) (equal string "《X"))
|
||||||
(ekp-tests--strings-in-tree cache-key)))
|
(ekp-tests--strings-in-tree cache-key)))
|
||||||
(store-substring suffix 1 "Y")
|
(store-substring suffix 1 "Y")
|
||||||
(store-substring line-start-extra 0 "》")
|
(store-substring line-start-extra 1 "Y")
|
||||||
(should (seq-some (lambda (string) (equal string "uX"))
|
(should (seq-some (lambda (string) (equal string "uX"))
|
||||||
(ekp-tests--strings-in-tree cache-key)))
|
(ekp-tests--strings-in-tree cache-key)))
|
||||||
(should (seq-some (lambda (string) (equal string "《"))
|
(should (seq-some (lambda (string) (equal string "《X"))
|
||||||
(ekp-tests--strings-in-tree cache-key)))
|
(ekp-tests--strings-in-tree cache-key)))
|
||||||
(let ((changed-plan (ekp-layout-plan text width)))
|
(let ((changed-plan (ekp-layout-plan text width)))
|
||||||
(should-not
|
(should-not
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user