Add validation for built-in text property names in define-tp and improve tp-palette colors

Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-12-30 15:59:00 +00:00
parent f9e5ffaaf1
commit 03aa3d392a
3 changed files with 429 additions and 57 deletions

View File

@ -1,3 +1,22 @@
;;; tp-palette.el --- Color palette definitions for tp.el -*- lexical-binding: t -*-
;; Copyright (C) 2024
;;; Commentary:
;; This file provides color palette definitions for tp.el.
;; Colors are designed to work well in both light and dark themes.
;; Each palette entry has the format:
;; (:fg (LIGHT-FG . DARK-FG) :bg (LIGHT-BG . DARK-BG) :border (LIGHT-BORDER . DARK-BORDER))
;;
;; Color scheme inspired by:
;; - GitHub Primer Design System
;; - Tailwind CSS Color Palette
;; - Material Design Color System
;; - One Dark / One Light themes
;;; Code:
;; ============================================ ;; ============================================
;; 基础文本样式 (Basic Text Styles) ;; 基础文本样式 (Basic Text Styles)
;; ============================================ ;; ============================================
@ -48,62 +67,78 @@
;; ============================================ ;; ============================================
;; 代码高亮样式 (Code Syntax Highlighting Styles) ;; 代码高亮样式 (Code Syntax Highlighting Styles)
;; Based on One Dark/Light and popular editor themes
;; ============================================ ;; ============================================
(defvar tp-palette-syntax-keyword (defvar tp-palette-syntax-keyword
'(:fg ("#cf222e" . "#ff7b72") :bg ("#fff5f5" . "#2d1f1f") '(:fg ("#a626a4" . "#c678dd") :bg (nil . nil)
:border ("#ffcecb" . "#5c3d3d")) :border ("#a626a4" . "#c678dd"))
"关键字样式,如 if, else, def, class 等。") "关键字样式,如 if, else, def, class 等。紫色调。")
(defvar tp-palette-syntax-string (defvar tp-palette-syntax-string
'(:fg ("#0a3069" . "#a5d6ff") :bg ("#f0f7ff" . "#1a2634") '(:fg ("#50a14f" . "#98c379") :bg (nil . nil)
:border ("#c8e1ff" . "#3d5a73")) :border ("#50a14f" . "#98c379"))
"字符串样式。") "字符串样式。绿色调。")
(defvar tp-palette-syntax-comment (defvar tp-palette-syntax-comment
'(:fg ("#6e7781" . "#8b949e") :bg ("#f6f8fa" . "#161b22") '(:fg ("#a0a1a7" . "#5c6370") :bg (nil . nil)
:border ("#e1e4e8" . "#30363d")) :border ("#a0a1a7" . "#5c6370"))
"注释样式。") "注释样式。灰色调。")
(defvar tp-palette-syntax-function (defvar tp-palette-syntax-function
'(:fg ("#8250df" . "#d2a8ff") :bg ("#fbefff" . "#271d36") '(:fg ("#4078f2" . "#61afef") :bg (nil . nil)
:border ("#d8b9ff" . "#553d7a")) :border ("#4078f2" . "#61afef"))
"函数名样式。") "函数名样式。蓝色调。")
(defvar tp-palette-syntax-variable (defvar tp-palette-syntax-variable
'(:fg ("#953800" . "#ffa657") :bg ("#fff8f0" . "#2e2318") '(:fg ("#e45649" . "#e06c75") :bg (nil . nil)
:border ("#ffd8b5" . "#5c4427")) :border ("#e45649" . "#e06c75"))
"变量名样式。") "变量名样式。红色调。")
(defvar tp-palette-syntax-constant (defvar tp-palette-syntax-constant
'(:fg ("#0550ae" . "#79c0ff") :bg ("#f0f6ff" . "#1a2433") '(:fg ("#986801" . "#d19a66") :bg (nil . nil)
:border ("#b6d7ff" . "#3d5573")) :border ("#986801" . "#d19a66"))
"常量样式。") "常量样式。橙色调。")
(defvar tp-palette-syntax-type (defvar tp-palette-syntax-type
'(:fg ("#116329" . "#7ee787") :bg ("#f0fff4" . "#1a2e1f") '(:fg ("#c18401" . "#e5c07b") :bg (nil . nil)
:border ("#a7f0ba" . "#3d6647")) :border ("#c18401" . "#e5c07b"))
"类型样式,如类名、结构体名。") "类型样式,如类名、结构体名。黄色调。")
(defvar tp-palette-syntax-number (defvar tp-palette-syntax-number
'(:fg ("#0550ae" . "#79c0ff") :bg ("#f0f6ff" . "#1a2433") '(:fg ("#986801" . "#d19a66") :bg (nil . nil)
:border ("#b6d7ff" . "#3d5573")) :border ("#986801" . "#d19a66"))
"数字样式。") "数字样式。橙色调。")
(defvar tp-palette-syntax-operator
'(:fg ("#0184bc" . "#56b6c2") :bg (nil . nil)
:border ("#0184bc" . "#56b6c2"))
"运算符样式。青色调。")
(defvar tp-palette-syntax-preprocessor (defvar tp-palette-syntax-preprocessor
'(:fg ("#cf222e" . "#ff7b72") :bg ("#fff5f5" . "#2d1f1f") '(:fg ("#a626a4" . "#c678dd") :bg (nil . nil)
:border ("#ffcecb" . "#5c3d3d")) :border ("#a626a4" . "#c678dd"))
"预处理器样式,如 #include, #define。") "预处理器样式,如 #include, #define。紫色调。")
(defvar tp-palette-syntax-builtin (defvar tp-palette-syntax-builtin
'(:fg ("#0550ae" . "#79c0ff") :bg ("#f0f6ff" . "#1a2433") '(:fg ("#0184bc" . "#56b6c2") :bg (nil . nil)
:border ("#b6d7ff" . "#3d5573")) :border ("#0184bc" . "#56b6c2"))
"内置函数/方法样式。") "内置函数/方法样式。青色调。")
(defvar tp-palette-syntax-regexp (defvar tp-palette-syntax-regexp
'(:fg ("#116329" . "#7ee787") :bg ("#f0fff4" . "#1a2e1f") '(:fg ("#50a14f" . "#98c379") :bg (nil . nil)
:border ("#a7f0ba" . "#3d6647")) :border ("#50a14f" . "#98c379"))
"正则表达式样式。") "正则表达式样式。绿色调。")
(defvar tp-palette-syntax-attribute
'(:fg ("#986801" . "#d19a66") :bg (nil . nil)
:border ("#986801" . "#d19a66"))
"属性/装饰器样式。橙色调。")
(defvar tp-palette-syntax-tag
'(:fg ("#e45649" . "#e06c75") :bg (nil . nil)
:border ("#e45649" . "#e06c75"))
"HTML/XML 标签样式。红色调。")
;; ============================================ ;; ============================================
;; UI 界面样式 (UI Styles) ;; UI 界面样式 (UI Styles)
@ -164,7 +199,7 @@
"危险按钮样式(红色删除按钮)。") "危险按钮样式(红色删除按钮)。")
(defvar tp-palette-button-outline (defvar tp-palette-button-outline
'(:fg ("#0969da" . "#58a6ff") :bg ("transparent" . "transparent") '(:fg ("#0969da" . "#58a6ff") :bg (nil . nil)
:border ("#0969da" . "#58a6ff")) :border ("#0969da" . "#58a6ff"))
"轮廓按钮样式。") "轮廓按钮样式。")
@ -264,7 +299,7 @@
;; ============================================ ;; ============================================
(defvar tp-palette-region (defvar tp-palette-region
'(:fg ("unspecified" . "unspecified") :bg ("#add6ff" . "#264f78") '(:fg (nil . nil) :bg ("#add6ff" . "#264f78")
:border ("#54aeff" . "#388bfd")) :border ("#54aeff" . "#388bfd"))
"选区样式。") "选区样式。")
@ -274,7 +309,7 @@
"光标样式。") "光标样式。")
(defvar tp-palette-line-highlight (defvar tp-palette-line-highlight
'(:fg ("unspecified" . "unspecified") :bg ("#f6f8fa" . "#161b22") '(:fg (nil . nil) :bg ("#f6f8fa" . "#161b22")
:border ("#eaeef2" . "#21262d")) :border ("#eaeef2" . "#21262d"))
"当前行高亮样式。") "当前行高亮样式。")
@ -342,12 +377,12 @@
"Org 引用块样式。") "Org 引用块样式。")
(defvar tp-palette-org-link (defvar tp-palette-org-link
'(:fg ("#0969da" . "#58a6ff") :bg ("transparent" . "transparent") '(:fg ("#0969da" . "#58a6ff") :bg (nil . nil)
:border ("#0969da" . "#58a6ff")) :border ("#0969da" . "#58a6ff"))
"Org 链接样式。") "Org 链接样式。")
(defvar tp-palette-org-date (defvar tp-palette-org-date
'(:fg ("#8250df" . "#a371f7") :bg ("transparent" . "transparent") '(:fg ("#8250df" . "#a371f7") :bg (nil . nil)
:border ("#8250df" . "#a371f7")) :border ("#8250df" . "#a371f7"))
"Org 日期样式。") "Org 日期样式。")
@ -434,44 +469,293 @@
;; ============================================ ;; ============================================
(defvar tp-palette-rainbow-1 (defvar tp-palette-rainbow-1
'(:fg ("#0969da" . "#58a6ff") :bg ("transparent" . "transparent") '(:fg ("#e45649" . "#e06c75") :bg (nil . nil)
:border ("#0969da" . "#58a6ff")) :border ("#e45649" . "#e06c75"))
"彩虹括号第一层样式") "彩虹括号第一层样式(红色)")
(defvar tp-palette-rainbow-2 (defvar tp-palette-rainbow-2
'(:fg ("#8250df" . "#a371f7") :bg ("transparent" . "transparent") '(:fg ("#986801" . "#d19a66") :bg (nil . nil)
:border ("#8250df" . "#a371f7")) :border ("#986801" . "#d19a66"))
"彩虹括号第二层样式") "彩虹括号第二层样式(橙色)")
(defvar tp-palette-rainbow-3 (defvar tp-palette-rainbow-3
'(:fg ("#1a7f37" . "#3fb950") :bg ("transparent" . "transparent") '(:fg ("#c18401" . "#e5c07b") :bg (nil . nil)
:border ("#1a7f37" . "#3fb950")) :border ("#c18401" . "#e5c07b"))
"彩虹括号第三层样式") "彩虹括号第三层样式(黄色)")
(defvar tp-palette-rainbow-4 (defvar tp-palette-rainbow-4
'(:fg ("#953800" . "#ffa657") :bg ("transparent" . "transparent") '(:fg ("#50a14f" . "#98c379") :bg (nil . nil)
:border ("#953800" . "#ffa657")) :border ("#50a14f" . "#98c379"))
"彩虹括号第四层样式") "彩虹括号第四层样式(绿色)")
(defvar tp-palette-rainbow-5 (defvar tp-palette-rainbow-5
'(:fg ("#bf3989" . "#db61a2") :bg ("transparent" . "transparent") '(:fg ("#0184bc" . "#56b6c2") :bg (nil . nil)
:border ("#bf3989" . "#db61a2")) :border ("#0184bc" . "#56b6c2"))
"彩虹括号第五层样式") "彩虹括号第五层样式(青色)")
(defvar tp-palette-rainbow-6 (defvar tp-palette-rainbow-6
'(:fg ("#0598bc" . "#39c5cf") :bg ("transparent" . "transparent") '(:fg ("#4078f2" . "#61afef") :bg (nil . nil)
:border ("#0598bc" . "#39c5cf")) :border ("#4078f2" . "#61afef"))
"彩虹括号第六层样式。") "彩虹括号第六层样式(蓝色)。")
(defvar tp-palette-rainbow-7
'(:fg ("#a626a4" . "#c678dd") :bg (nil . nil)
:border ("#a626a4" . "#c678dd"))
"彩虹括号第七层样式(紫色)。")
(defvar tp-palette-rainbow-8
'(:fg ("#bf3989" . "#db61a2") :bg (nil . nil)
:border ("#bf3989" . "#db61a2"))
"彩虹括号第八层样式(粉色)。")
;; ============================================
;; 更多强调色 (Additional Accent Colors)
;; Inspired by Tailwind CSS color palette
;; ============================================
(defvar tp-palette-accent-teal
'(:fg ("#0d9488" . "#2dd4bf") :bg ("#ccfbf1" . "#134e4a")
:border ("#14b8a6" . "#0d9488"))
"蓝绿色强调样式。")
(defvar tp-palette-accent-indigo
'(:fg ("#4f46e5" . "#818cf8") :bg ("#e0e7ff" . "#312e81")
:border ("#6366f1" . "#4f46e5"))
"靛蓝色强调样式。")
(defvar tp-palette-accent-rose
'(:fg ("#e11d48" . "#fb7185") :bg ("#ffe4e6" . "#4c0519")
:border ("#f43f5e" . "#e11d48"))
"玫瑰色强调样式。")
(defvar tp-palette-accent-amber
'(:fg ("#d97706" . "#fbbf24") :bg ("#fef3c7" . "#451a03")
:border ("#f59e0b" . "#d97706"))
"琥珀色强调样式。")
(defvar tp-palette-accent-lime
'(:fg ("#65a30d" . "#a3e635") :bg ("#ecfccb" . "#1a2e05")
:border ("#84cc16" . "#65a30d"))
"青柠色强调样式。")
(defvar tp-palette-accent-emerald
'(:fg ("#059669" . "#34d399") :bg ("#d1fae5" . "#064e3b")
:border ("#10b981" . "#059669"))
"翡翠绿强调样式。")
(defvar tp-palette-accent-sky
'(:fg ("#0284c7" . "#38bdf8") :bg ("#e0f2fe" . "#0c4a6e")
:border ("#0ea5e9" . "#0284c7"))
"天蓝色强调样式。")
(defvar tp-palette-accent-violet
'(:fg ("#7c3aed" . "#a78bfa") :bg ("#ede9fe" . "#2e1065")
:border ("#8b5cf6" . "#7c3aed"))
"紫罗兰色强调样式。")
(defvar tp-palette-accent-fuchsia
'(:fg ("#c026d3" . "#e879f9") :bg ("#fae8ff" . "#4a044e")
:border ("#d946ef" . "#c026d3"))
"洋红色强调样式。")
;; ============================================
;; 更多徽章样式 (Additional Badge Styles)
;; ============================================
(defvar tp-palette-badge-info
'(:fg ("#ffffff" . "#ffffff") :bg ("#0969da" . "#1f6feb")
:border ("#0550ae" . "#388bfd"))
"信息徽章样式。")
(defvar tp-palette-badge-purple
'(:fg ("#ffffff" . "#ffffff") :bg ("#8250df" . "#8957e5")
:border ("#6e40c9" . "#a371f7"))
"紫色徽章样式。")
(defvar tp-palette-badge-pink
'(:fg ("#ffffff" . "#ffffff") :bg ("#bf3989" . "#db61a2")
:border ("#99306f" . "#f28cb1"))
"粉色徽章样式。")
;; ============================================
;; 按钮扩展样式 (Additional Button Styles)
;; ============================================
(defvar tp-palette-button-info
'(:fg ("#ffffff" . "#ffffff") :bg ("#0969da" . "#1f6feb")
:border ("#0550ae" . "#388bfd"))
"信息按钮样式(蓝色)。")
;; ============================================
;; Markdown 样式 (Markdown Styles)
;; ============================================
(defvar tp-palette-markdown-heading
'(:fg ("#0969da" . "#58a6ff") :bg (nil . nil)
:border ("#0969da" . "#58a6ff"))
"Markdown 标题样式。")
(defvar tp-palette-markdown-bold
'(:fg ("#24292f" . "#e6edf3") :bg (nil . nil)
:border ("#24292f" . "#e6edf3"))
"Markdown 粗体样式。")
(defvar tp-palette-markdown-italic
'(:fg ("#57606a" . "#8b949e") :bg (nil . nil)
:border ("#57606a" . "#8b949e"))
"Markdown 斜体样式。")
(defvar tp-palette-markdown-code
'(:fg ("#0550ae" . "#79c0ff") :bg ("#f6f8fa" . "#161b22")
:border ("#d0d7de" . "#30363d"))
"Markdown 行内代码样式。")
(defvar tp-palette-markdown-link
'(:fg ("#0969da" . "#58a6ff") :bg (nil . nil)
:border ("#0969da" . "#58a6ff"))
"Markdown 链接样式。")
(defvar tp-palette-markdown-blockquote
'(:fg ("#57606a" . "#8b949e") :bg ("#f6f8fa" . "#161b22")
:border ("#d0d7de" . "#30363d"))
"Markdown 引用块样式。")
(defvar tp-palette-markdown-list
'(:fg ("#9a6700" . "#d29922") :bg (nil . nil)
:border ("#9a6700" . "#d29922"))
"Markdown 列表标记样式。")
;; ============================================
;; 灰度色阶 (Grayscale Palette)
;; Inspired by Tailwind CSS neutral colors
;; ============================================
(defvar tp-palette-gray-50
'(:fg ("#24292f" . "#fafafa") :bg ("#fafafa" . "#171717")
:border ("#e5e5e5" . "#262626"))
"灰度 50 级别。")
(defvar tp-palette-gray-100
'(:fg ("#24292f" . "#f5f5f5") :bg ("#f5f5f5" . "#1c1c1c")
:border ("#e5e5e5" . "#262626"))
"灰度 100 级别。")
(defvar tp-palette-gray-200
'(:fg ("#24292f" . "#e5e5e5") :bg ("#e5e5e5" . "#262626")
:border ("#d4d4d4" . "#404040"))
"灰度 200 级别。")
(defvar tp-palette-gray-300
'(:fg ("#24292f" . "#d4d4d4") :bg ("#d4d4d4" . "#404040")
:border ("#a3a3a3" . "#525252"))
"灰度 300 级别。")
(defvar tp-palette-gray-400
'(:fg ("#24292f" . "#a3a3a3") :bg ("#a3a3a3" . "#525252")
:border ("#737373" . "#737373"))
"灰度 400 级别。")
(defvar tp-palette-gray-500
'(:fg ("#ffffff" . "#737373") :bg ("#737373" . "#737373")
:border ("#525252" . "#a3a3a3"))
"灰度 500 级别。")
(defvar tp-palette-gray-600
'(:fg ("#ffffff" . "#525252") :bg ("#525252" . "#a3a3a3")
:border ("#404040" . "#d4d4d4"))
"灰度 600 级别。")
(defvar tp-palette-gray-700
'(:fg ("#ffffff" . "#404040") :bg ("#404040" . "#d4d4d4")
:border ("#262626" . "#e5e5e5"))
"灰度 700 级别。")
(defvar tp-palette-gray-800
'(:fg ("#ffffff" . "#262626") :bg ("#262626" . "#e5e5e5")
:border ("#171717" . "#f5f5f5"))
"灰度 800 级别。")
(defvar tp-palette-gray-900
'(:fg ("#ffffff" . "#171717") :bg ("#171717" . "#f5f5f5")
:border ("#0a0a0a" . "#fafafa"))
"灰度 900 级别。")
;; ============================================
;; 优先级样式 (Priority Styles)
;; ============================================
(defvar tp-palette-priority-highest
'(:fg ("#ffffff" . "#ffffff") :bg ("#cf222e" . "#da3633")
:border ("#a40e26" . "#f85149"))
"最高优先级样式(红色)。")
(defvar tp-palette-priority-high
'(:fg ("#ffffff" . "#ffffff") :bg ("#bc4c00" . "#f0883e")
:border ("#953800" . "#ffa657"))
"高优先级样式(橙色)。")
(defvar tp-palette-priority-medium
'(:fg ("#24292f" . "#24292f") :bg ("#ffd33d" . "#d29922")
:border ("#bf8700" . "#bb8009"))
"中等优先级样式(黄色)。")
(defvar tp-palette-priority-low
'(:fg ("#ffffff" . "#ffffff") :bg ("#1a7f37" . "#238636")
:border ("#116329" . "#2ea043"))
"低优先级样式(绿色)。")
(defvar tp-palette-priority-lowest
'(:fg ("#ffffff" . "#ffffff") :bg ("#0969da" . "#1f6feb")
:border ("#0550ae" . "#388bfd"))
"最低优先级样式(蓝色)。")
;; ============================================
;; 进度状态样式 (Progress Status Styles)
;; ============================================
(defvar tp-palette-status-pending
'(:fg ("#57606a" . "#8b949e") :bg ("#f6f8fa" . "#21262d")
:border ("#d0d7de" . "#30363d"))
"待处理状态样式(灰色)。")
(defvar tp-palette-status-in-progress
'(:fg ("#0969da" . "#58a6ff") :bg ("#ddf4ff" . "#1f3d5c")
:border ("#54aeff" . "#388bfd"))
"进行中状态样式(蓝色)。")
(defvar tp-palette-status-review
'(:fg ("#8250df" . "#a371f7") :bg ("#fbefff" . "#271d36")
:border ("#c297ff" . "#6e40c9"))
"审核中状态样式(紫色)。")
(defvar tp-palette-status-completed
'(:fg ("#1a7f37" . "#3fb950") :bg ("#dafbe1" . "#1b4721")
:border ("#4ac26b" . "#238636"))
"已完成状态样式(绿色)。")
(defvar tp-palette-status-cancelled
'(:fg ("#cf222e" . "#f85149") :bg ("#ffebe9" . "#542426")
:border ("#ff8182" . "#f85149"))
"已取消状态样式(红色)。")
(defvar tp-palette-status-on-hold
'(:fg ("#9a6700" . "#d29922") :bg ("#fff8c5" . "#3d2e00")
:border ("#d4a72c" . "#9e6a03"))
"暂停状态样式(黄色)。")
;;; Utilities ;;; Utilities
(defun tp-palette-fg-color (symbol) (defun tp-palette-fg-color (symbol)
"Get the foreground color from palette SYMBOL."
(tp-parse-color (plist-get (symbol-value symbol) :fg))) (tp-parse-color (plist-get (symbol-value symbol) :fg)))
(defun tp-palette-bg-color (symbol) (defun tp-palette-bg-color (symbol)
"Get the background color from palette SYMBOL."
(tp-parse-color (plist-get (symbol-value symbol) :bg))) (tp-parse-color (plist-get (symbol-value symbol) :bg)))
(defun tp-palette-border-color (symbol) (defun tp-palette-border-color (symbol)
"Get the border color from palette SYMBOL."
(tp-parse-color (plist-get (symbol-value symbol) :border))) (tp-parse-color (plist-get (symbol-value symbol) :border)))
(provide 'tp-palette) (provide 'tp-palette)
;;; tp-palette.el ends here

View File

@ -3574,5 +3574,53 @@ When using tp-set (direct property setting), tp-name is NOT added."
;; Cleanup ;; Cleanup
(ignore-errors (makunbound 'tp-test-text))))) (ignore-errors (makunbound 'tp-test-text)))))
;;; ============================================================
;;; Built-in Text Property Name Validation Tests
;;; ============================================================
(ert-deftest tp-test-builtin-text-property-check ()
"Test that tp--builtin-text-property-p correctly identifies built-in properties."
;; Check known built-in properties
(should (tp--builtin-text-property-p 'face))
(should (tp--builtin-text-property-p 'display))
(should (tp--builtin-text-property-p 'invisible))
(should (tp--builtin-text-property-p 'help-echo))
(should (tp--builtin-text-property-p 'keymap))
(should (tp--builtin-text-property-p 'mouse-face))
(should (tp--builtin-text-property-p 'read-only))
(should (tp--builtin-text-property-p 'front-sticky))
(should (tp--builtin-text-property-p 'rear-nonsticky))
;; Check non-built-in properties
(should-not (tp--builtin-text-property-p 'tp-my-custom-layer))
(should-not (tp--builtin-text-property-p 'my-layer))
(should-not (tp--builtin-text-property-p 'custom-property)))
(ert-deftest tp-test-define-tp-rejects-builtin-names ()
"Test that define-tp rejects built-in text property names."
(tp-test-with-temp-buffer
;; Test that using 'face as a layer name raises an error
(should-error
(eval '(define-tp face () '(help-echo "test"))))
;; Test that using 'display as a layer name raises an error
(should-error
(eval '(define-tp display () '(face bold))))
;; Test that using 'invisible as a layer name raises an error
(should-error
(eval '(define-tp invisible () '(face bold))))
;; Test that using 'keymap as a layer name raises an error
(should-error
(eval '(define-tp keymap () '(face bold))))
;; Test that valid names work fine
(should (define-tp tp-test-valid-layer () '(face bold)))))
(ert-deftest tp-test-define-tp-parameterized-rejects-builtin ()
"Test that parameterized define-tp also rejects built-in names."
(tp-test-with-temp-buffer
;; Parameterized layer with built-in name should fail
(should-error
(eval '(define-tp display (value) `(face (:height ,value)))))
;; Valid parameterized layer should work
(should (define-tp tp-test-param-valid (value) `(face (:height ,value))))))
(provide 'tp-ert-tests) (provide 'tp-ert-tests)
;;; tp-ert-tests.el ends here ;;; tp-ert-tests.el ends here

42
tp.el
View File

@ -98,6 +98,39 @@ This is a list of (LAYER-NAME . CHANGED-VARS) pairs pending update.")
(defvar tp--batch-update-active nil (defvar tp--batch-update-active nil
"When non-nil, we are inside a `tp-with-batch-updates' form.") "When non-nil, we are inside a `tp-with-batch-updates' form.")
;;; --- Built-in Text Property Names ---
(defconst tp--builtin-text-properties
'(;; Display and appearance
face font-lock-face mouse-face display invisible intangible
;; Interaction and help
help-echo cursor keymap local-map pointer
;; Stickiness
front-sticky rear-nonsticky
;; Text modification
read-only insert-in-front-hooks insert-behind-hooks
modification-hooks point-entered point-left
;; Font and composition
fontified composition hard cursor-intangible
;; Line properties
line-height line-spacing wrap-prefix line-prefix
;; Field and input
field inhibit-line-move-field-capture
;; Button and widget
button category follow-link action
;; Syntax and parsing
syntax-table
;; Misc
yank-handler auto-composed evaporate face-alias)
"List of built-in Emacs text property names.
These property names are reserved and cannot be used as layer names in `define-tp'.
Layer names that conflict with these will cause errors during text property operations.")
(defun tp--builtin-text-property-p (name)
"Return non-nil if NAME is a built-in text property name.
NAME should be a symbol."
(memq name tp--builtin-text-properties))
;;;============================================================================ ;;;============================================================================
;;; Layer 1: Basic Utility Functions ;;; Layer 1: Basic Utility Functions
;;;============================================================================ ;;;============================================================================
@ -2329,10 +2362,17 @@ ARGLIST must be either:
- A list containing exactly one symbol for parameterized layers - A list containing exactly one symbol for parameterized layers
BODY is the property list expression. For parameterized layers, BODY is the property list expression. For parameterized layers,
it will be evaluated with the argument bound." it will be evaluated with the argument bound.
Note: NAME cannot be a built-in Emacs text property name like `face',
`display', `invisible', etc. See `tp--builtin-text-properties' for the
complete list of reserved names."
(declare (indent defun)) (declare (indent defun))
(unless (listp arglist) (unless (listp arglist)
(error "define-tp ARGLIST must be a list")) (error "define-tp ARGLIST must be a list"))
;; Check for built-in text property name conflict
(when (tp--builtin-text-property-p name)
(error "define-tp: '%s' is a built-in Emacs text property name and cannot be used as a layer name" name))
(cond (cond
;; Non-parameterized: empty arglist - store as (LAYER-NAME nil BODY-FORM) ;; Non-parameterized: empty arglist - store as (LAYER-NAME nil BODY-FORM)
((null arglist) ((null arglist)