v0.8.0: tangle to XDG (~/.local/share/cl-tty/), remove stale memex .lisp files

This commit is contained in:
2026-05-18 13:04:10 -04:00
parent e3415cee73
commit af572d5a8c
67 changed files with 518 additions and 6301 deletions

View File

@@ -188,7 +188,7 @@ framebuffer backend class, constructor, diff/flush utilities, scissor macro,
and frame-inspection functions. It depends on ~:cl-tty.backend~ for the
~backend~ base class and protocol methods.
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defpackage :cl-tty.rendering
(:use :cl :cl-tty.backend)
(:export
@@ -206,7 +206,7 @@ and frame-inspection functions. It depends on ~:cl-tty.backend~ for the
Switch to the ~cl-tty.rendering~ package for all subsequent definitions.
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(in-package :cl-tty.rendering)
#+END_SRC
@@ -218,7 +218,7 @@ compared by value during diffing. All fields have sensible defaults so that
~make-cell~ with no arguments produces a blank space cell. The ~link-url~
slot enables OSC-8 hyperlink support for clickable text.
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defstruct cell
"A single terminal cell — character, colors, and attributes."
(char #\space :type character)
@@ -239,7 +239,7 @@ columns. Using ~:initial-element (make-cell)~ ensures every cell is a fresh
struct instance (not shared). The ~:element-type~ declaration is a hint for
potential optimizations.
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defun make-framebuffer (width height)
"Create a 2D array of CELL with dimensions HEIGHT x WIDTH."
(make-array (list height width)
@@ -253,13 +253,13 @@ Accessors that return the dimensions of a framebuffer array. These guard
against non-array values (returning 0) so that callers don't crash on nil or
uninitialized framebuffer slots.
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defun framebuffer-width (fb)
"Return the width (columns) of framebuffer FB."
(if (arrayp fb) (array-dimension fb 1) 0))
#+END_SRC
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defun framebuffer-height (fb)
"Return the height (rows) of framebuffer FB."
(if (arrayp fb) (array-dimension fb 0) 0))
@@ -274,7 +274,7 @@ plus scissor-clipping state. All drawing methods on this backend write to the
cell array instead of emitting escape sequences. The scissor coordinates are
used by ~%in-scissor-p~ to clip drawing during component rendering.
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defclass framebuffer-backend (backend)
((framebuffer :initform nil :accessor fb-framebuffer)
(scissor-x :initform 0 :accessor fb-scissor-x)
@@ -289,7 +289,7 @@ Constructor that creates a ~framebuffer-backend~ instance and initializes its
framebuffer array to the given dimensions (defaulting to 80x24, a common
terminal size).
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defun make-framebuffer-backend (&key (width 80) (height 24))
"Create a framebuffer-backend with a fresh framebuffer."
(let ((fb (make-instance 'framebuffer-backend)))
@@ -306,7 +306,7 @@ scissor rectangle. If either scissor dimension is nil (meaning no scissor is
set), the corresponding axis check is skipped, effectively treating the entire
framebuffer as the drawable area.
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defun %in-scissor-p (fb cx cy)
"Check if (CX, CY) falls within the current scissor rectangle."
(let ((sx (fb-scissor-x fb)) (sy (fb-scissor-y fb))
@@ -323,7 +323,7 @@ ultimately lands, ensuring consistent clipping behavior across all drawing
operations. Only cells within both the framebuffer dimensions and the active
scissor rectangle are written.
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defun %set-cell (fb x y char &key fg bg bold italic underline link-url)
"Set cell (X, Y) if within bounds and scissor."
(let ((cells (fb-framebuffer fb)))
@@ -346,7 +346,7 @@ clipping apply automatically. The ~&allow-other-keys~ permits passing
style-related keyword arguments that other backends may use but the framebuffer
does not need (e.g., reverse, dim, blink).
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defmethod draw-text ((fb framebuffer-backend) x y string fg bg
&key bold italic underline reverse dim blink
(link-url nil link-url-p)
@@ -365,7 +365,7 @@ Direct rendering onto a raw 2D framebuffer array (the type returned by
~make-framebuffer~). This lets application code call ~draw-text~ directly on a
framebuffer without wrapping it in a ~framebuffer-backend~.
#+begin_src lisp :tangle ../src/rendering/framebuffer.lisp
#+begin_src lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defmethod draw-text ((fb array) x y string fg bg
&key bold italic underline reverse dim blink
&allow-other-keys)
@@ -387,7 +387,7 @@ framebuffer without wrapping it in a ~framebuffer-backend~.
Allow clearing a raw 2D framebuffer array directly (same type as returned by
~make-framebuffer~). Resets all cells to blank defaults.
#+begin_src lisp :tangle ../src/rendering/framebuffer.lisp
#+begin_src lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defmethod backend-clear ((fb array))
(dotimes (y (array-dimension fb 0))
(dotimes (x (array-dimension fb 1))
@@ -401,7 +401,7 @@ color. This is used for clearing areas and rendering background fills for
panels and widgets. Iterates row by row, column by column, using ~%set-cell~ so
scissor clipping is respected.
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defmethod draw-rect ((fb framebuffer-backend) x y w h &key bg)
(dotimes (row h)
(dotimes (col w)
@@ -415,7 +415,7 @@ string at the top edge. Supports three border styles: :single, :double, and
:rounded, each using different corner and line characters. The title is drawn
starting two cells from the left edge, overwriting top-edge characters.
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defmethod draw-border ((fb framebuffer-backend) x y w h &key (style :single) title title-align fg bg)
(let* ((chars (case style
(:single '(#\+ #\- #\|))
@@ -447,7 +447,7 @@ Clears every cell in the framebuffer to a fresh default cell (space, no style).
This is the ~backend-clear~ protocol method specialized on ~framebuffer-backend~,
providing a full-frame reset used between render passes.
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defmethod backend-clear ((fb framebuffer-backend))
(let ((cells (fb-framebuffer fb)))
(dotimes (y (framebuffer-height cells))
@@ -464,7 +464,7 @@ stores the URL in the cell's ~link-url~ slot for later retrieval (e.g., on
mouse click). The actual OSC-8 escape sequence rendering is deferred to the
real backend during flush.
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defmethod draw-link ((fb framebuffer-backend) x y string url &key fg bg)
;; OSC 8 links are not rendered in framebuffer — store as text
(draw-text fb x y string fg bg :link-url url))
@@ -475,7 +475,7 @@ real backend during flush.
Renders a horizontal ellipsis (up to 3 periods) starting at position (X, Y).
Width is capped at 3 characters to prevent overflow into adjacent cells.
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defmethod draw-ellipsis ((fb framebuffer-backend) x y width &key fg bg)
(dotimes (i (min 3 width))
(%set-cell fb (+ x i) y #\. :fg fg :bg bg)))
@@ -490,7 +490,7 @@ same visual output. Uses ~eql~ for characters, symbols, and booleans, and
~equal~ for string comparison of ~link-url~. This predicate drives the diff
algorithm — only cells that differ are flushed.
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defun cells-equal-p (a b)
"Return T if two cells have identical content and style."
(and (eql (cell-char a) (cell-char b))
@@ -509,7 +509,7 @@ framebuffers and collect a list of (X Y CELL) triples for every cell that
changed. Using ~nreverse~ at the end ensures stable ordering (top-to-bottom,
left-to-right) without consing during accumulation.
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defun diff-framebuffers (prev curr)
"Compare PREV and CURR framebuffers. Return list of (X Y CELL) for changes."
(let ((changes nil)
@@ -533,7 +533,7 @@ minimal cursor movement (tracking the current row to avoid redundant cursor
positioning). Returns the count of changed cells so callers can monitor
rendering overhead.
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defun flush-framebuffer (prev-fb curr-fb backend)
"Diff PREV-FB and CURR-FB and flush changes to BACKEND.
Returns the number of changed cells."
@@ -564,7 +564,7 @@ Retrieves the hyperlink URL stored at cell position (X, Y) in a framebuffer
array. Returns nil if the cell is out of bounds or has no link. This enables
click-to-open-link functionality in the TUI.
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defun fb-cell-link-url (fb x y)
"Return the link URL at (X Y) in framebuffer FB, or nil."
(when (and (arrayp fb) (>= y 0) (< y (array-dimension fb 0))
@@ -580,7 +580,7 @@ mouse selection and clipboard operations. Normalizes coordinate order (so the
user can drag in any direction) and appends newlines between rows for natural
multi-line text.
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defun extract-text (fb x1 y1 x2 y2)
"Extract visible text from the rectangle between (X1,Y1) and (X2,Y2)."
(let ((x-min (max 0 (min x1 x2))) (x-max (max 0 (max x1 x2)))
@@ -604,7 +604,7 @@ for the duration of BODY. Saves and restores previous scissor state via
~unwind-protect~ for proper cleanup even on non-local exits. Using gensyms for
the state variables ensures no variable capture issues.
#+BEGIN_SRC lisp :tangle ../src/rendering/framebuffer.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/src/rendering/framebuffer.lisp
(defmacro with-scissor ((fb x y w h) &body body)
"Clip all drawing on FB to rectangle (X Y W H)."
(let ((old-x (gensym)) (old-y (gensym))
@@ -632,7 +632,7 @@ Setting up the test package with FiveAM, importing the rendering and backend
packages for use in all subsequent tests. This block tangles to the test file
that is loaded by the test runner.
#+BEGIN_SRC lisp :tangle ../tests/framebuffer-tests.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/tests/framebuffer-tests.lisp
(defpackage :cl-tty-framebuffer-test
(:use :cl :fiveam :cl-tty.rendering :cl-tty.backend))
(in-package :cl-tty-framebuffer-test)
@@ -647,7 +647,7 @@ Verify that the framebuffer constructor produces an array with the expected
dimensions. Height should match the first dimension (rows), width the second
dimension (columns).
#+BEGIN_SRC lisp :tangle ../tests/framebuffer-tests.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/tests/framebuffer-tests.lisp
(test make-framebuffer-creates-correct-size
(let ((fb (make-framebuffer 80 24)))
(is (= 24 (framebuffer-height fb)))
@@ -659,7 +659,7 @@ dimension (columns).
Cells created via MAKE-CELL with no arguments should default to a space
character with nil foreground and background — a blank, unstyled cell.
#+BEGIN_SRC lisp :tangle ../tests/framebuffer-tests.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/tests/framebuffer-tests.lisp
(test cell-defaults-are-space
(let ((cell (aref (make-framebuffer 10 10) 0 0)))
(is (eql #\space (cell-char cell)))
@@ -673,7 +673,7 @@ Drawing a string into the framebuffer backend should set the character and
foreground color at each cell position. Characters should appear at the expected
(x, y) offsets.
#+BEGIN_SRC lisp :tangle ../tests/framebuffer-tests.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/tests/framebuffer-tests.lisp
(test draw-text-on-fb-sets-cells
(let ((fb (make-framebuffer-backend)))
(draw-text fb 2 3 "abc" :red nil)
@@ -690,7 +690,7 @@ When drawing text that extends past the right edge of the framebuffer, cells
beyond the width should remain unchanged (space characters). This prevents
buffer overflow and undefined memory access.
#+BEGIN_SRC lisp :tangle ../tests/framebuffer-tests.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/tests/framebuffer-tests.lisp
(test draw-text-clips-at-bounds
(let ((fb (make-framebuffer-backend :width 10 :height 5)))
(draw-text fb 8 2 "hello" nil nil)
@@ -705,7 +705,7 @@ buffer overflow and undefined memory access.
Two framebuffers with identical cells should produce no changes. The diff
engine must short-circuit when no cells differ.
#+BEGIN_SRC lisp :tangle ../tests/framebuffer-tests.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/tests/framebuffer-tests.lisp
(test diff-identical-fbs-returns-empty
(let ((fb1 (make-framebuffer 80 24))
(fb2 (make-framebuffer 80 24)))
@@ -717,7 +717,7 @@ engine must short-circuit when no cells differ.
After modifying a single cell in one framebuffer, the diff engine should return
exactly one change with the correct coordinates and cell data.
#+BEGIN_SRC lisp :tangle ../tests/framebuffer-tests.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/tests/framebuffer-tests.lisp
(test diff-changed-fb-returns-changes
(let* ((fb1 (make-framebuffer 10 10))
(fb2 (make-framebuffer 10 10)))
@@ -735,7 +735,7 @@ exactly one change with the correct coordinates and cell data.
When a scissor rectangle is active, drawing operations outside the rectangle
should be clipped away. Operations inside the rectangle should proceed normally.
#+BEGIN_SRC lisp :tangle ../tests/framebuffer-tests.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/tests/framebuffer-tests.lisp
(test with-scissor-clips-drawing
(let ((fb (make-framebuffer-backend :width 20 :height 10)))
(with-scissor (fb 5 5 3 3)
@@ -753,7 +753,7 @@ should be diffed. This test verifies correct behavior at both the smaller and
larger end of the size mismatch — ensuring edge cells in the non-overlapping
region are ignored.
#+BEGIN_SRC lisp :tangle ../tests/framebuffer-tests.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/tests/framebuffer-tests.lisp
(test flush-different-sized-fbs-handles-edge-cells
(let* ((small-fb (make-framebuffer 5 5))
(large-fb (make-framebuffer 10 10))
@@ -775,7 +775,7 @@ region are ignored.
After drawing on a framebuffer backend and flushing to a real backend, at least
one cell change should be detected and forwarded to the output backend.
#+BEGIN_SRC lisp :tangle ../tests/framebuffer-tests.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/tests/framebuffer-tests.lisp
(test flush-fb-copies-to-backend
(let* ((real-be (make-simple-backend :output-stream (make-string-output-stream)))
(fb (make-framebuffer-backend)))
@@ -789,7 +789,7 @@ one cell change should be detected and forwarded to the output backend.
A cell without a hyperlink should return nil from ~fb-cell-link-url~, ensuring
the default state is correct and no spurious URL is reported.
#+BEGIN_SRC lisp :tangle ../tests/framebuffer-tests.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/tests/framebuffer-tests.lisp
(test fb-cell-link-url-returns-nil-for-blank-cell
(let ((fb (make-framebuffer 10 10)))
(is (null (fb-cell-link-url fb 5 5)))))
@@ -801,7 +801,7 @@ After drawing text with a link-url, the corresponding cell should return that
URL. Cells at other positions should still return nil. This validates that
link metadata is stored per-cell and correctly retrievable.
#+BEGIN_SRC lisp :tangle ../tests/framebuffer-tests.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/tests/framebuffer-tests.lisp
(test fb-cell-link-url-finds-link-url
(let ((fb (make-framebuffer-backend)))
(draw-text fb 0 0 "click" nil nil :link-url "https://example.com")
@@ -815,7 +815,7 @@ Querying a cell position outside the framebuffer dimensions should gracefully
return nil rather than erroring, which prevents crashes during mouse event
processing at the edges of the terminal.
#+BEGIN_SRC lisp :tangle ../tests/framebuffer-tests.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/tests/framebuffer-tests.lisp
(test fb-cell-link-url-out-of-bounds-returns-nil
(let ((fb (make-framebuffer 5 5)))
(is (null (fb-cell-link-url fb 10 10)))))
@@ -827,7 +827,7 @@ Extracting text from a single row of the framebuffer should return the
characters in that row as a contiguous string, preserving order and including
only visible characters.
#+BEGIN_SRC lisp :tangle ../tests/framebuffer-tests.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/tests/framebuffer-tests.lisp
(test extract-text-single-row
(let ((fb (make-framebuffer-backend)))
(draw-text fb 0 0 "hello" nil nil)
@@ -841,7 +841,7 @@ Extracting text from a rectangle spanning multiple rows should concatenate
rows with newline separators. This matches the expected behavior for clipboard
copy of rectangular selections in the TUI.
#+BEGIN_SRC lisp :tangle ../tests/framebuffer-tests.lisp
#+BEGIN_SRC lisp :tangle ~/.local/share/cl-tty/tests/framebuffer-tests.lisp
(test extract-text-multi-row
(let ((fb (make-framebuffer-backend)))
(draw-text fb 0 0 "abc" nil nil)