forked from ruricolist/spinneret
-
Notifications
You must be signed in to change notification settings - Fork 0
/
stream.lisp
139 lines (117 loc) · 4.15 KB
/
stream.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
(in-package :spinneret)
(defgeneric html-stream.base-stream (stream)
(:method ((stream stream))
stream))
(defgeneric html-stream-column (stream)
(:method ((x stream))
0))
(defclass html-stream (fundamental-character-output-stream)
((col :type (integer 0 *) :initform 0
:reader html-stream-column
:reader stream-line-column)
(line :type (integer 0 *) :initform 0)
(last-char :type character
;; The last char defaults to newline to get reasonable
;; behavior from fresh-line.
:initform #\Newline)
(elastic-newline :type boolean
:initform nil)
(base-stream :type stream
:initarg :base-stream
:reader html-stream.base-stream))
(:default-initargs))
(defun make-html-stream (base-stream)
(make-instance 'html-stream
:base-stream (assure stream base-stream)))
(defgeneric html-stream? (x)
(:method ((x t)) nil))
(defgeneric ensure-html-stream (x)
(:method ((x stream))
(if *print-pretty*
(make-html-stream x)
(assure stream x))))
(defgeneric elastic-newline (stream)
(:method ((x t))
(values)))
(defun newline (&optional s)
(when *print-pretty*
(terpri s)))
(defmethods html-stream (s col line last-char base-stream elastic-newline)
(:method ensure-html-stream (s)
s)
(:method html-stream? (s)
t)
(:method stream-start-line-p (s)
(= col 0))
(:method fire-elastic-newline (s (char (eql #\Newline)))
(nix elastic-newline))
(:method fire-elastic-newline (s (char character))
(when (nix elastic-newline)
(unless *pre*
(write-char #\Newline s))))
(:method stream-write-char (s (char (eql #\Newline)))
(nix elastic-newline)
;; Remember the starting value is -1.
(let ((indent (max 0 (get-indent))))
(write-char #\Newline base-stream)
(incf line)
;; (PRINC INDENT)
(setf col indent)
(loop repeat indent do
(write-char #\Space base-stream)))
(setf last-char #\Newline))
(:method stream-write-char (s char)
(fire-elastic-newline s char)
(incf col 1)
(write-char char base-stream)
(setf last-char char))
(:method stream-write-string (s string &optional (start 0) end)
(declare (type (or null array-index) start end))
(prog1 string
(let* ((end (or end (length string)))
(start (or start 0))
(len (assure array-index (- end start))))
(cond ((= len 0))
((= len 1)
(write-char (aref string start) s))
(t
(fire-elastic-newline s (aref string start))
(setf last-char (aref string (1- end)))
(multiple-value-bind (newline-count chars)
(nlet rec ((i start)
(lines 0)
(chars 0))
(eif (= i end)
(values lines chars)
(let ((c (aref string i)))
(eif (eql c #\Newline)
(rec (1+ i)
(1+ lines)
0)
(rec (1+ i)
lines
(1+ chars))))))
(declare (array-index newline-count chars))
(write-string string base-stream :start start :end end)
(eif (> newline-count 0)
(progn
(incf line newline-count)
(setf col chars))
(incf col chars))))))))
(:method stream-terpri (s)
(write-char #\Newline s))
(:method stream-fresh-line (s)
(prog1 (unless (eql last-char #\Newline)
(terpri s))
(assert (eql last-char #\Newline))))
(:method stream-finish-output (s)
(finish-output base-stream))
(:method stream-force-output (s)
(force-output base-stream))
(:method stream-advance-to-column (s c)
(loop while (< col c) do
(write-char #\Space s))
(assert (>= col c))
t)
(:method elastic-newline (s)
(setf elastic-newline t)))