implement: slot modes (:stack, :replace, :single-winner)
Add :mode parameter to defslot with three behaviors: - :stack (default) — accumulate all registrations, render in order - :replace — each registration replaces previous entries - :single-winner — first registration wins, rest ignored Mode is set on first defslot call and frozen for subsequent calls to prevent conflicting mode specifications from different plugins. Store slot data as plist (:mode <keyword> :entries <list>) instead of bare entries list. Add 5 new tests covering mode-specific behavior. All 9 slot tests pass. All 13 suites pass at 100%.
This commit is contained in:
@@ -1,24 +1,49 @@
|
||||
(in-package :cl-tty.slot)
|
||||
|
||||
(defvar *slots* (make-hash-table :test #'equal)
|
||||
"Hash table mapping slot name (string) -> list of (order . render-fn) pairs.")
|
||||
(defvar *slots* (make-hash-table :test 'equal)
|
||||
"Hash table mapping slot name (string) -> plist of slot data.
|
||||
Each entry: (:mode <mode> :entries <(order . render-fn) list>).")
|
||||
|
||||
(defun defslot (name &key (order 0) render-fn)
|
||||
(defun defslot (name &key (order 0) render-fn (mode :stack))
|
||||
(let* ((key (string name))
|
||||
(entries (gethash key *slots*)))
|
||||
(if (null entries)
|
||||
(setf (gethash key *slots*) (list (cons order render-fn)))
|
||||
(slot (gethash key *slots*)))
|
||||
(if (null slot)
|
||||
;; First registration — set mode and create entry
|
||||
(setf (gethash key *slots*)
|
||||
(sort (cons (cons order render-fn) entries) #'< :key #'car))))
|
||||
(list :mode mode
|
||||
:entries (list (cons order render-fn))))
|
||||
;; Existing slot — respect frozen mode
|
||||
(let ((entries (getf slot :entries)))
|
||||
(ecase (getf slot :mode)
|
||||
(:stack
|
||||
(setf (getf slot :entries)
|
||||
(sort (cons (cons order render-fn) entries)
|
||||
#'< :key #'car)))
|
||||
(:replace
|
||||
(setf (getf slot :entries)
|
||||
(list (cons order render-fn))))
|
||||
(:single-winner
|
||||
;; First registration already present — no-op
|
||||
(values))))))
|
||||
render-fn)
|
||||
|
||||
(defun slot-render (slot-name &rest args)
|
||||
(let ((entries (gethash (string slot-name) *slots*)))
|
||||
(when entries
|
||||
(mapcar (lambda (entry)
|
||||
(let ((fn (cdr entry)))
|
||||
(when fn (apply fn args))))
|
||||
entries))))
|
||||
(let ((slot (gethash (string slot-name) *slots*)))
|
||||
(when slot
|
||||
(let ((mode (getf slot :mode))
|
||||
(entries (getf slot :entries)))
|
||||
(ecase mode
|
||||
(:stack
|
||||
(mapcar (lambda (entry)
|
||||
(let ((fn (cdr entry)))
|
||||
(when fn (apply fn args))))
|
||||
entries))
|
||||
(:replace
|
||||
(let ((fn (cdar (last entries))))
|
||||
(when fn (apply fn args))))
|
||||
(:single-winner
|
||||
(let ((fn (cdar entries)))
|
||||
(when fn (apply fn args)))))))))
|
||||
|
||||
(defun slot-p (slot-name)
|
||||
(nth-value 1 (gethash (string slot-name) *slots*)))
|
||||
|
||||
Reference in New Issue
Block a user