From 9f6025ee128aec98de3c5c22abdc6c9da07a2702 Mon Sep 17 00:00:00 2001 From: Kinneyzhang Date: Thu, 6 Aug 2026 23:42:01 +0800 Subject: [PATCH] feat(ecss): finalize independent cascade contract Add shorthand-aware direct declaration composition, keep the public API consumer-neutral, and record the initial standalone release contract. Verified: make check; make package-lint. --- CHANGELOG.md | 12 ++++++++++++ docs/api.en.md | 3 ++- docs/api.zh.md | 3 ++- ecss-cascade.el | 13 +++++++++++++ tests/ecss-cascade-tests.el | 12 ++++++++++++ 5 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7b9b3d5 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,12 @@ +# Changelog + +All notable changes to `ecss` are documented here. + +## 0.1.0 (Unreleased) + +### Added + +- Independent property schemas, generic subjects and adapters, selector parsing and matching, stylesheets, complete cascade ordering, CSS-wide values, custom properties, computed values, diagnostics, and provenance. +- Pure Ebox-neutral public APIs with no TP dependency and no Buffer mutation. +- `ecss-merge-declarations` for shorthand-aware, presence-preserving composition of direct declaration groups. +- Standalone tests and compile, checkdoc, package-lint, and whitespace verification targets. diff --git a/docs/api.en.md b/docs/api.en.md index c5752d1..5b1d6dc 100644 --- a/docs/api.en.md +++ b/docs/api.en.md @@ -9,8 +9,9 @@ - `(ecss-schema-set-property SCHEMAS ID)` returns a defensive schema copy. - `(ecss-schema-set-property-ids SCHEMAS)` returns stable registration order. - `(ecss-expand-declarations SCHEMAS DECLARATIONS)` validates properties and expands each shorthand once. +- `(ecss-merge-declarations SCHEMAS &rest DECLARATION-GROUPS)` expands and merges groups from left to right. Later groups replace the same longhand, while explicit nil remains a declaration. -A property ID is a symbol containing `/`, such as `ebox/color`. Custom properties need no schema and use `--name` symbols. `initial` is normalized and validated once at schema registration; already computed initial and inherited values do not run the normalizer again. +A property ID is a symbol containing `/`, such as `demo/color`. Custom properties need no schema and use `--name` symbols. `initial` is normalized and validated once at schema registration; already computed initial and inherited values do not run the normalizer again. ## Subjects and selectors diff --git a/docs/api.zh.md b/docs/api.zh.md index c421e46..6e590a8 100644 --- a/docs/api.zh.md +++ b/docs/api.zh.md @@ -9,8 +9,9 @@ - `(ecss-schema-set-property SCHEMAS ID)` 返回 defensive schema copy。 - `(ecss-schema-set-property-ids SCHEMAS)` 返回稳定注册顺序。 - `(ecss-expand-declarations SCHEMAS DECLARATIONS)` 校验属性并只展开一次 shorthand。 +- `(ecss-merge-declarations SCHEMAS &rest DECLARATION-GROUPS)` 按从左到右的优先级展开并合并多组声明;后组覆盖相同 longhand,显式 nil 不会被当作缺省。 -Property ID 必须是包含 `/` 的 symbol,例如 `ebox/color`。Custom property 不需要 schema,使用 `--name` symbol。`initial` 在 schema 注册时规范化并校验一次;已计算的 initial/inherited value 不会再次执行 normalizer。 +Property ID 必须是包含 `/` 的 symbol,例如 `demo/color`。Custom property 不需要 schema,使用 `--name` symbol。`initial` 在 schema 注册时规范化并校验一次;已计算的 initial/inherited value 不会再次执行 normalizer。 ## Subject 与 selector diff --git a/ecss-cascade.el b/ecss-cascade.el index fb14cce..cc1927c 100644 --- a/ecss-cascade.el +++ b/ecss-cascade.el @@ -268,6 +268,19 @@ (cl-loop for (property value) on declarations by #'cddr append (ecss--expand-declaration schemas property value))) +;;;###autoload +(defun ecss-merge-declarations (schemas &rest declaration-groups) + "Merge DECLARATION-GROUPS into canonical longhands using SCHEMAS. +Groups are applied from left to right. Later values replace earlier values for +the same expanded longhand, including explicit nil declarations." + (ecss--schema-set-check schemas) + (let (result) + (dolist (declarations declaration-groups result) + (cl-loop for (property value) + on (ecss-expand-declarations schemas declarations) by #'cddr + do (setq result + (plist-put result property (copy-tree value))))))) + (defun ecss-stylesheet-create () "Create an empty independent stylesheet." (ecss--make-stylesheet :rules nil :layers nil :source-order 0)) diff --git a/tests/ecss-cascade-tests.el b/tests/ecss-cascade-tests.el index 287cbcf..d708200 100644 --- a/tests/ecss-cascade-tests.el +++ b/tests/ecss-cascade-tests.el @@ -89,6 +89,18 @@ do (should (ecss--important-p value)) do (should (= (ecss--important-value value) 3))))) +(ert-deftest ecss-cascade-test-merge-declarations-compacts-expanded-longhands () + "Later declaration groups should replace expanded longhands without loss." + (let ((schemas (ecss-cascade-test--schemas))) + (should + (equal + (ecss-merge-declarations + schemas + '(app/padding 1 app/color "blue") + '(app/padding-left 4 app/color nil)) + '(app/padding-top 1 app/padding-right 1 + app/padding-bottom 1 app/padding-left 4 app/color nil))))) + (ert-deftest ecss-cascade-test-shorthand-and-longhand-follow-declaration-order () (let ((schemas (ecss-cascade-test--schemas)) (subject (ecss-cascade-test--subject)))