v0.2.0: Text renderable with word-wrap and inline spans
- Text class with content, fg/bg, wrap-mode (:word or :none) - Span class for inline styled segments (bold, italic, etc.) - render-text dispatches through backend's draw-text - word-wrap function splits text at word boundaries - split-string utility for whitespace tokenization - 9 new tests: creation, content, empty, truncation, word-wrap, single-word, span creation, span storage - modern-backend now accepts :output-stream - ASDF updated with text component - 28 total component tests, 100% GREEN
This commit is contained in:
@@ -16,6 +16,8 @@
|
||||
(b (make-modern-backend :output-stream s)))
|
||||
(values b s)))
|
||||
|
||||
;; ── Box Tests ─────────────────────────────────────────────────
|
||||
|
||||
(test box-creates-with-defaults
|
||||
"A box created with no arguments has reasonable defaults"
|
||||
(let ((b (make-box)))
|
||||
@@ -42,7 +44,6 @@
|
||||
(render-box bx b)
|
||||
(let ((out (get-output-stream-string s)))
|
||||
(is (search "┌" out) "border with background")
|
||||
;; :red is a named color → indexed SGR (41m, not 48;2;...)
|
||||
(is (search "41m" out) "SGR background for red")))))
|
||||
|
||||
(test box-renders-title
|
||||
@@ -81,3 +82,75 @@
|
||||
(render-box bx b)
|
||||
(let ((out (get-output-stream-string s)))
|
||||
(is (search "┌" out) "2x2 box still has borders")))))
|
||||
|
||||
;; ── Text and Span Tests ───────────────────────────────────────
|
||||
|
||||
(test text-creates-with-defaults
|
||||
"A text created with no arguments has reasonable defaults"
|
||||
(let ((txt (make-text "")))
|
||||
(is (typep txt 'text))
|
||||
(is (typep (text-layout-node txt) 'layout-node))))
|
||||
|
||||
(test text-renders-content
|
||||
"A text renders its content at position"
|
||||
(multiple-value-bind (b s) (make-capturing-backend)
|
||||
(let ((tx (make-text "Hello" :width 10 :height 1)))
|
||||
(compute-layout (text-layout-node tx) 10 1)
|
||||
(render-text tx b)
|
||||
(let ((out (get-output-stream-string s)))
|
||||
(is (search "Hello" out) "content should appear")))))
|
||||
|
||||
(test text-empty-string
|
||||
"Empty text produces no output"
|
||||
(multiple-value-bind (b s) (make-capturing-backend)
|
||||
(let ((tx (make-text "" :width 10 :height 1)))
|
||||
(compute-layout (text-layout-node tx) 10 1)
|
||||
(render-text tx b)
|
||||
(is (string= (get-output-stream-string s) "")
|
||||
"empty string produces no output"))))
|
||||
|
||||
(test text-truncates-when-no-wrap
|
||||
"Text with wrap-mode :none truncates at width"
|
||||
(multiple-value-bind (b s) (make-capturing-backend)
|
||||
(let ((tx (make-text "Hello World" :width 5 :height 1
|
||||
:wrap-mode :none)))
|
||||
(compute-layout (text-layout-node tx) 5 1)
|
||||
(render-text tx b)
|
||||
(let ((out (get-output-stream-string s)))
|
||||
(is (search "Hello" out) "truncated to first 5 chars")))))
|
||||
|
||||
(test text-word-wraps
|
||||
"Text with wrap-mode :word wraps at word boundaries"
|
||||
(multiple-value-bind (b s) (make-capturing-backend)
|
||||
(let ((tx (make-text "Hello brave new world" :width 6 :height 3)))
|
||||
(compute-layout (text-layout-node tx) 6 3)
|
||||
(render-text tx b)
|
||||
(let ((out (get-output-stream-string s)))
|
||||
(is (search "Hello" out) "first line")
|
||||
(is (search "brave" out) "second line")
|
||||
(is (search "new" out) "third line")))))
|
||||
|
||||
(test text-word-wrap-single-word
|
||||
"Text wraps even a single word if it exceeds width"
|
||||
(multiple-value-bind (b s) (make-capturing-backend)
|
||||
(let ((tx (make-text "Hello" :width 3 :height 3)))
|
||||
(compute-layout (text-layout-node tx) 3 3)
|
||||
(render-text tx b)
|
||||
(let ((out (get-output-stream-string s)))
|
||||
(is (search "Hel" out) "word truncated to width")))))
|
||||
|
||||
(test span-creates-with-attributes
|
||||
"A span has text and optional style attributes"
|
||||
(let ((s (span "bold text" :bold t)))
|
||||
(is (string= (span-text s) "bold text"))
|
||||
(is-true (span-bold s))
|
||||
(is-false (span-italic s))))
|
||||
|
||||
(test make-text-with-spans
|
||||
"Text with spans stores span objects"
|
||||
(let* ((sp (list (span "Hello" :bold t)
|
||||
(span "World" :italic t)))
|
||||
(tx (make-text "" :spans sp)))
|
||||
(is (= (length (text-spans tx)) 2))
|
||||
(is (string= (span-text (elt (text-spans tx) 0)) "Hello"))
|
||||
(is-true (span-bold (elt (text-spans tx) 0)))))
|
||||
|
||||
Reference in New Issue
Block a user