literate: restructure all 19 org files with per-function blocks and prose
Every function, defclass, defstruct, defgeneric, defmethod, defmacro, defvar, and defparameter in every org file now has its own #+BEGIN_SRC block with literate prose above it explaining the design reasoning. Block counts before → after: package.org: 1 → 7 container-package.org: 1 → 1 (prose expanded) dirty.org: 4 → 6 render.org: 10 → 25 theme.org: 6 → 19 box-renderable.org: 9 → 29 scrollbox.org: 8 → 26 tabbar.org: 5 → 10 backend-protocol.org: 8 → 66 modern-backend.org: 17 → 53 detection.org: 4 → 6 layout-engine.org: 9 → 36 framebuffer.org: 8 → 37 markdown-renderer.org:13 → 38 dialog.org: 17 → 23 (merged dual structure) mouse.org: 4 → 25 select.org: 12 → 30 slot.org: 4 → 12 text-input.org: 11 → 53 Total: ~153 blocks → ~502 blocks Bugs fixed during restructuring: - render.org: stray π character typo (backenπd → backend) - modern-backend.org: sgr-attr missing closing paren + #+END_SRC - detection.org: invalid #\Esc character reference - select.org: extra closing paren in select-visible-options All 13 test suites pass at 100%.
This commit is contained in:
82
org/slot.org
82
org/slot.org
@@ -25,6 +25,9 @@ Slot modes:
|
||||
|
||||
** Implementation
|
||||
|
||||
The package provides the public API and exports all slot system symbols.
|
||||
Clients :use this package or refer to symbols qualified.
|
||||
|
||||
#+BEGIN_SRC lisp :tangle ../src/components/slot-package.lisp :noweb no
|
||||
(defpackage :cl-tty.slot
|
||||
(:use :cl)
|
||||
@@ -37,12 +40,30 @@ Slot modes:
|
||||
#:*slots*))
|
||||
#+END_SRC
|
||||
|
||||
*** Slot Storage: *slots*
|
||||
|
||||
The central registry is a hash table keyed by slot name (strings, for
|
||||
case-insensitive lookup via ~equal~). Each value is a list of
|
||||
~(order . render-fn)~ cons cells, sorted by order on insertion. The
|
||||
~:test #'equal~ ensures that ~:sidebar~ and ~\"sidebar\"~ map to the
|
||||
same key.
|
||||
|
||||
#+BEGIN_SRC lisp :tangle ../src/components/slot.lisp :noweb no
|
||||
(in-package :cl-tty.slot)
|
||||
|
||||
(defvar *slots* (make-hash-table :test #'equal)
|
||||
"Hash table mapping slot name (string) -> list of (order . render-fn) pairs.")
|
||||
#+END_SRC
|
||||
|
||||
*** defslot: Register a Render Function
|
||||
|
||||
~defslot~ inserts a new ~(order . render-fn)~ entry into the slot's
|
||||
entry list. If the slot has no previous entries a fresh list is
|
||||
created; otherwise the new entry is consed onto the existing list and
|
||||
the whole list is sorted by ~order~ ascending. The ~render-fn~ itself
|
||||
is returned so callers can use it inline or store it.
|
||||
|
||||
#+BEGIN_SRC lisp :tangle ../src/components/slot.lisp :noweb no
|
||||
(defun defslot (name &key (order 0) render-fn)
|
||||
(let* ((key (string name))
|
||||
(entries (gethash key *slots*)))
|
||||
@@ -53,15 +74,16 @@ Slot modes:
|
||||
render-fn)
|
||||
#+END_SRC
|
||||
|
||||
*** Bug Fixes (v1.0.0): nil handler guard in slot-render
|
||||
*** slot-render: Invoke All Render Functions
|
||||
|
||||
~slot-render~ called ~(apply (cdr entry) args)~ unconditionally, but
|
||||
~defslot~ stores ~(order . render-fn)~ pairs where ~render-fn~ can be
|
||||
~nil~ (if called without ~:render-fn~). This caused a type error when
|
||||
~apply~ received ~nil~ as the function argument.
|
||||
Iterates over the slot's registered entries and calls each non-nil
|
||||
render function with the supplied ~args~. Entries with a nil handler
|
||||
are silently skipped — this is important because ~defslot~ accepts an
|
||||
optional ~:render-fn~ keyword that defaults to ~nil~, and we must
|
||||
guard against calling ~apply~ on nil (a type error in Common Lisp).
|
||||
|
||||
Fix: Check ~(when fn)~ before calling ~apply~. Entries with a nil
|
||||
handler are silently skipped.
|
||||
Returns a list of results, one per non-nil render function. Returns
|
||||
~nil~ (via ~when~) if the slot has no registrations at all.
|
||||
|
||||
#+BEGIN_SRC lisp :tangle ../src/components/slot.lisp :noweb no
|
||||
(defun slot-render (slot-name &rest args)
|
||||
@@ -71,39 +93,85 @@ handler are silently skipped.
|
||||
(let ((fn (cdr entry)))
|
||||
(when fn (apply fn args))))
|
||||
entries))))
|
||||
#+END_SRC
|
||||
|
||||
*** slot-p: Check Slot Existence
|
||||
|
||||
Uses ~nth-value 1~ of ~gethash~ which returns ~t~ if the key is
|
||||
present (even if the value is ~nil~) or ~nil~ if absent. This is the
|
||||
canonical Common Lisp idiom for testing hash-table membership.
|
||||
|
||||
#+BEGIN_SRC lisp :tangle ../src/components/slot.lisp :noweb no
|
||||
(defun slot-p (slot-name)
|
||||
(nth-value 1 (gethash (string slot-name) *slots*)))
|
||||
#+END_SRC
|
||||
|
||||
*** clear-slot: Remove All Registrations
|
||||
|
||||
Calls ~remhash~ to delete the slot's entry from the hash table
|
||||
entirely. After this call ~slot-p~ returns false and ~slot-render~
|
||||
returns nil for the given slot name.
|
||||
|
||||
#+BEGIN_SRC lisp :tangle ../src/components/slot.lisp :noweb no
|
||||
(defun clear-slot (slot-name)
|
||||
(remhash (string slot-name) *slots*))
|
||||
#+END_SRC
|
||||
|
||||
*** list-slots: Enumerate Registered Slots
|
||||
|
||||
Iterates over all hash keys in ~*slots*~ and returns them as a list.
|
||||
Only slots that have been registered (i.e. have at least one entry)
|
||||
appear in the result.
|
||||
|
||||
#+BEGIN_SRC lisp :tangle ../src/components/slot.lisp :noweb no
|
||||
(defun list-slots ()
|
||||
(loop for key being the hash-keys of *slots* collect key))
|
||||
#+END_SRC
|
||||
|
||||
*** Tests
|
||||
|
||||
The test suite uses FiveAM and exercises each public function.
|
||||
|
||||
**** Test Package and Suite
|
||||
|
||||
#+BEGIN_SRC lisp :tangle ../tests/slot-tests.lisp :noweb no
|
||||
(defpackage :cl-tty-slot-test (:use :cl :cl-tty.slot :fiveam))
|
||||
(in-package :cl-tty-slot-test)
|
||||
|
||||
(def-suite slot-suite :description "Slot system tests")
|
||||
(in-suite slot-suite)
|
||||
#+END_SRC
|
||||
|
||||
**** defslot-register: Registering a slot makes it visible
|
||||
|
||||
#+BEGIN_SRC lisp :tangle ../tests/slot-tests.lisp :noweb no
|
||||
(def-test defslot-register ()
|
||||
(clear-slot :test-slot)
|
||||
(defslot :test-slot :order 1 :render-fn (lambda () "hello"))
|
||||
(is-true (slot-p :test-slot)))
|
||||
#+END_SRC
|
||||
|
||||
**** slot-render-calls: Registered functions are called in order
|
||||
|
||||
#+BEGIN_SRC lisp :tangle ../tests/slot-tests.lisp :noweb no
|
||||
(def-test slot-render-calls ()
|
||||
(clear-slot :test-slot)
|
||||
(defslot :test-slot :order 1 :render-fn (lambda () "a"))
|
||||
(defslot :test-slot :order 2 :render-fn (lambda () "b"))
|
||||
(is (equal '("a" "b") (slot-render :test-slot))))
|
||||
#+END_SRC
|
||||
|
||||
**** slot-render-empty: Unregistered slot returns nil
|
||||
|
||||
#+BEGIN_SRC lisp :tangle ../tests/slot-tests.lisp :noweb no
|
||||
(def-test slot-render-empty ()
|
||||
(clear-slot :ghost)
|
||||
(is-false (slot-render :ghost)))
|
||||
#+END_SRC
|
||||
|
||||
**** clear-slot-removes: Clearing a slot makes it absent
|
||||
|
||||
#+BEGIN_SRC lisp :tangle ../tests/slot-tests.lisp :noweb no
|
||||
(def-test clear-slot-removes ()
|
||||
(clear-slot :test-slot)
|
||||
(defslot :test-slot :order 1 :render-fn (lambda () "x"))
|
||||
|
||||
Reference in New Issue
Block a user