1 ;;; This is code was taken from lisppaste2 and is a quick hack
2 ;;; to colorize lisp examples in the html generated by Texinfo.
3 ;;; It is not general-purpose utility, though it could easily be
6 ;;;; colorize-package.lisp
10 (:export
:scan-string
:format-scan
:html-colorization
11 :find-coloring-type
:autodetect-coloring-type
12 :coloring-types
:scan
:scan-any
:advance
:call-parent-formatter
13 :*coloring-css
* :make-background-css
:*css-background-class
*
14 :colorize-file
:colorize-file-to-stream
:*version-token
*))
16 ;;;; coloring-css.lisp
18 (in-package :colorize
)
20 (defparameter *coloring-css
*
21 ".symbol { color: #770055; background-color: transparent; border: 0px; margin: 0px;}
22 a.symbol:link { color: #229955; background-color : transparent; text-decoration: none; border: 0px; margin: 0px; }
23 a.symbol:active { color : #229955; background-color : transparent; text-decoration: none; border: 0px; margin: 0px; }
24 a.symbol:visited { color : #229955; background-color : transparent; text-decoration: none; border: 0px; margin: 0px; }
25 a.symbol:hover { color : #229955; background-color : transparent; text-decoration: none; border: 0px; margin: 0px; }
26 .special { color : #FF5000; background-color : inherit; }
27 .keyword { color : #770000; background-color : inherit; }
28 .comment { color : #007777; background-color : inherit; }
29 .string { color : #777777; background-color : inherit; }
30 .character { color : #0055AA; background-color : inherit; }
31 .syntaxerror { color : #FF0000; background-color : inherit; }
32 span.paren1:hover { color : inherit; background-color : #BAFFFF; }
33 span.paren2:hover { color : inherit; background-color : #FFCACA; }
34 span.paren3:hover { color : inherit; background-color : #FFFFBA; }
35 span.paren4:hover { color : inherit; background-color : #CACAFF; }
36 span.paren5:hover { color : inherit; background-color : #CAFFCA; }
37 span.paren6:hover { color : inherit; background-color : #FFBAFF; }
40 (defvar *css-background-class
* "lisp-bg")
42 (defun for-css (thing)
43 (if (symbolp thing
) (string-downcase (symbol-name thing
))
46 (defun make-background-css (color &key
(class *css-background-class
*) (extra nil
))
47 (format nil
".~A { background-color: ~A; color: black; ~{~A; ~}}~:*~:*~:*
48 .~A:hover { background-color: ~A; color: black; ~{~A; ~}}~%"
50 (mapcar #'(lambda (extra)
51 (format nil
"~A : ~{~A ~}"
52 (for-css (first extra
))
53 (mapcar #'for-css
(cdr extra
))))
58 ;(in-package :colorize)
60 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
61 (defparameter *coloring-types
* nil
)
62 (defparameter *version-token
* (gensym)))
64 (defclass coloring-type
()
65 ((modes :initarg
:modes
:accessor coloring-type-modes
)
66 (default-mode :initarg
:default-mode
:accessor coloring-type-default-mode
)
67 (transition-functions :initarg
:transition-functions
:accessor coloring-type-transition-functions
)
68 (fancy-name :initarg
:fancy-name
:accessor coloring-type-fancy-name
)
69 (term-formatter :initarg
:term-formatter
:accessor coloring-type-term-formatter
)
70 (formatter-initial-values :initarg
:formatter-initial-values
:accessor coloring-type-formatter-initial-values
:initform nil
)
71 (formatter-after-hook :initarg
:formatter-after-hook
:accessor coloring-type-formatter-after-hook
:initform
(constantly ""))
72 (autodetect-function :initarg
:autodetect-function
:accessor coloring-type-autodetect-function
73 :initform
(constantly nil
))
74 (parent-type :initarg
:parent-type
:accessor coloring-type-parent-type
76 (visible :initarg
:visible
:accessor coloring-type-visible
79 (defun find-coloring-type (type)
80 (if (typep type
'coloring-type
)
82 (cdr (assoc (symbol-name type
) *coloring-types
* :test
#'string-equal
:key
#'symbol-name
))))
84 (defun autodetect-coloring-type (name)
86 (find name
*coloring-types
*
88 :test
#'(lambda (name type
)
89 (and (coloring-type-visible type
)
90 (funcall (coloring-type-autodetect-function type
) name
))))))
92 (defun coloring-types ()
93 (loop for type-pair in
*coloring-types
*
94 if
(coloring-type-visible (cdr type-pair
))
95 collect
(cons (car type-pair
)
96 (coloring-type-fancy-name (cdr type-pair
)))))
98 (defun (setf find-coloring-type
) (new-value type
)
100 (let ((found (assoc type
*coloring-types
*)))
102 (setf (cdr found
) new-value
)
103 (setf *coloring-types
*
104 (nconc *coloring-types
*
105 (list (cons type new-value
))))))
106 (setf *coloring-types
* (remove type
*coloring-types
* :key
#'car
))))
108 (defvar *scan-calls
* 0)
110 (defvar *reset-position
* nil
)
112 (defmacro with-gensyms
((&rest names
) &body body
)
113 `(let ,(mapcar #'(lambda (name)
114 (list name
`(make-symbol ,(symbol-name name
)))) names
)
117 (defmacro with-scanning-functions
(string-param position-place mode-place mode-wait-place
&body body
)
118 (with-gensyms (num items position not-preceded-by string item new-mode until advancing
)
119 `(labels ((advance (,num
)
120 (setf ,position-place
(+ ,position-place
,num
))
122 (peek-any (,items
&key
,not-preceded-by
)
124 (let* ((,items
(if (stringp ,items
)
125 (coerce ,items
'list
) ,items
))
126 (,not-preceded-by
(if (characterp ,not-preceded-by
)
127 (string ,not-preceded-by
) ,not-preceded-by
))
128 (,position
,position-place
)
129 (,string
,string-param
))
131 (< ,position
(length ,string
))
133 :test
#'(lambda (,string
,item
)
135 (format t
"looking for ~S in ~S starting at ~S~%"
136 ,item
,string
,position
)
137 (if (characterp ,item
)
138 (char= (elt ,string
,position
)
140 (search ,item
,string
:start2
,position
141 :end2
(min (length ,string
)
142 (+ ,position
(length ,item
))))))))))
143 (if (characterp ,item
)
144 (setf ,item
(string ,item
)))
148 (if (>= (- ,position
(length ,not-preceded-by
)) 0)
149 (not (string= (subseq ,string
150 (- ,position
(length ,not-preceded-by
))
158 (and *reset-position
*
159 (setf ,position-place
*reset-position
*))
161 (scan-any (,items
&key
,not-preceded-by
)
162 (let ((,item
(peek-any ,items
:not-preceded-by
,not-preceded-by
)))
163 (and ,item
(advance (length ,item
)))))
164 (peek (,item
&key
,not-preceded-by
)
165 (peek-any (list ,item
) :not-preceded-by
,not-preceded-by
))
166 (scan (,item
&key
,not-preceded-by
)
167 (scan-any (list ,item
) :not-preceded-by
,not-preceded-by
)))
168 (macrolet ((set-mode (,new-mode
&key
,until
(,advancing t
))
170 (list 'setf
',mode-place
,new-mode
)
171 (list 'setf
',mode-wait-place
172 (list 'lambda
(list ',position
)
173 (list 'let
(list (list '*reset-position
* ',position
))
174 (list 'values
,until
,advancing
)))))))
177 (defvar *formatter-local-variables
*)
179 (defmacro define-coloring-type
(name fancy-name
&key modes default-mode transitions formatters
180 autodetect parent formatter-variables
(formatter-after-hook '(constantly ""))
182 (with-gensyms (parent-type term type string current-mode position position-foobage mode-wait new-position advance
)
183 `(let ((,parent-type
(or (find-coloring-type ,parent
)
185 (error "No such coloring type: ~S" ,parent
)))))
186 (setf (find-coloring-type ,name
)
187 (make-instance 'coloring-type
188 :fancy-name
',fancy-name
189 :modes
(append ',modes
(if ,parent-type
(coloring-type-modes ,parent-type
)))
190 :default-mode
(or ',default-mode
191 (if ,parent-type
(coloring-type-default-mode ,parent-type
)))
193 `(:autodetect-function
,autodetect
))
194 :parent-type
,parent-type
195 :visible
(not ,invisible
)
196 :formatter-initial-values
(lambda nil
197 (list* ,@(mapcar #'(lambda (e)
198 `(cons ',(car e
) ,(second e
)))
201 (funcall (coloring-type-formatter-initial-values ,parent-type
))
203 :formatter-after-hook
(lambda nil
204 (symbol-macrolet ,(mapcar #'(lambda (e)
205 `(,(car e
) (cdr (assoc ',(car e
) *formatter-local-variables
*))))
208 (funcall ,formatter-after-hook
)
210 (funcall (coloring-type-formatter-after-hook ,parent-type
))
213 (symbol-macrolet ,(mapcar #'(lambda (e)
214 `(,(car e
) (cdr (assoc ',(car e
) *formatter-local-variables
*))))
217 (labels ((call-parent-formatter (&optional
(,type
(car ,term
))
218 (,string
(cdr ,term
)))
220 (funcall (coloring-type-term-formatter ,parent-type
)
221 (cons ,type
,string
))))
222 (call-formatter (&optional
(,type
(car ,term
))
223 (,string
(cdr ,term
)))
227 (t (lambda (,type text
)
228 (call-parent-formatter ,type text
))))
231 :transition-functions
233 ,@(loop for transition in transitions
234 collect
(destructuring-bind (mode &rest table
) transition
236 (lambda (,current-mode
,string
,position
)
237 (let ((,mode-wait
(constantly nil
))
238 (,position-foobage
,position
))
239 (with-scanning-functions ,string
,position-foobage
240 ,current-mode
,mode-wait
241 (let ((*reset-position
* ,position
))
243 (values ,position-foobage
,current-mode
244 (lambda (,new-position
)
245 (setf ,position-foobage
,new-position
)
246 (let ((,advance
(nth-value 1 (funcall ,mode-wait
,position-foobage
))))
247 (values ,position-foobage
,advance
)))))
250 (defun full-transition-table (coloring-type-object)
251 (let ((parent (coloring-type-parent-type coloring-type-object
)))
253 (append (coloring-type-transition-functions coloring-type-object
)
254 (full-transition-table parent
))
255 (coloring-type-transition-functions coloring-type-object
))))
257 (defun scan-string (coloring-type string
)
258 (let* ((coloring-type-object (or (find-coloring-type coloring-type
)
259 (error "No such coloring type: ~S" coloring-type
)))
260 (transitions (full-transition-table coloring-type-object
))
263 (current-mode (coloring-type-default-mode coloring-type-object
))
265 (current-wait (constantly nil
))
269 (flet ((finish-current (new-position new-mode new-wait
&key
(extend t
) push pop
)
270 (let ((to (if extend new-position current-position
)))
272 (setf result
(nconc result
273 (list (cons (cons current-mode mode-stack
)
274 (subseq string low-bound
281 (push current-mode mode-stack
)
282 (push current-wait wait-stack
))
283 (setf current-mode new-mode
284 current-position new-position
285 current-wait new-wait
))))
287 (if (> current-position
(length string
))
288 (return-from scan-string
290 (format *trace-output
* "Scan was called ~S times.~%"
292 (finish-current (length string
) nil
(constantly nil
))
295 (loop for transition in
297 (remove current-mode transitions
299 :test-not
#'(lambda (a b
)
306 (new-position new-mode new-wait
)
307 (funcall transition current-mode string current-position
)
308 (when (> new-position current-position
)
309 (finish-current new-position new-mode new-wait
:extend nil
:push t
)
314 (funcall current-wait current-position
)
316 (format t
"current-wait returns ~S ~S (mode is ~S, pos is ~S)~%" pos advance current-mode current-position
)
318 (when (> pos current-position
)
319 (finish-current (if advance
328 (incf current-position
)))
331 (defun format-scan (coloring-type scan
)
332 (let* ((coloring-type-object (or (find-coloring-type coloring-type
)
333 (error "No such coloring type: ~S" coloring-type
)))
334 (color-formatter (coloring-type-term-formatter coloring-type-object
))
335 (*formatter-local-variables
* (funcall (coloring-type-formatter-initial-values coloring-type-object
))))
336 (format nil
"~{~A~}~A"
337 (mapcar color-formatter scan
)
338 (funcall (coloring-type-formatter-after-hook coloring-type-object
)))))
340 (defun encode-for-pre (string)
341 (declare (simple-string string
))
342 (let ((output (make-array (truncate (length string
) 2/3)
343 :element-type
'character
346 (with-output-to-string (out output
)
347 (loop for char across string
349 ((#\
&) (write-string "&" out
))
350 ((#\
<) (write-string "<" out
))
351 ((#\
>) (write-string ">" out
))
352 (t (write-char char out
)))))
353 (coerce output
'simple-string
)))
355 (defun string-substitute (string substring replacement-string
)
356 "String substitute by Larry Hunter. Obtained from Google"
357 (let ((substring-length (length substring
))
361 (search substring string
)
362 (search substring string
:start2 last-end
)))
364 (concatenate 'string new-string
(subseq string last-end
)))
368 (subseq string last-end next-start
)
370 (setq last-end
(+ next-start substring-length
)))))
372 (defun decode-from-tt (string)
373 (string-substitute (string-substitute (string-substitute string
"&" "&")
377 (defun html-colorization (coloring-type string
)
378 (format-scan coloring-type
379 (mapcar #'(lambda (p)
381 (let ((tt (encode-for-pre (cdr p
))))
382 (if (and (> (length tt
) 0)
383 (char= (elt tt
(1- (length tt
))) #\
>))
384 (format nil
"~A~%" tt
) tt
))))
385 (scan-string coloring-type string
))))
387 (defun colorize-file-to-stream (coloring-type input-file-name s2
&key
(wrap t
) (css-background "default"))
388 (let* ((input-file (if (pathname-type (merge-pathnames input-file-name
))
389 (merge-pathnames input-file-name
)
390 (make-pathname :type
"lisp"
391 :defaults
(merge-pathnames input-file-name
))))
392 (*css-background-class
* css-background
))
393 (with-open-file (s input-file
:direction
:input
)
397 (loop (let ((line (read-line s nil nil
)))
400 (return-from done
)))))
401 (setf string
(format nil
"~{~A~%~}"
405 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">
406 <html><head><style type=\"text/css\">~A~%~A</style><body>
407 <table width=\"100%\"><tr><td class=\"~A\">
409 </tr></td></table></body></html>"
411 (make-background-css "white")
412 *css-background-class
*
413 (html-colorization coloring-type string
))
414 (write-string (html-colorization coloring-type string
) s2
))))))
416 (defun colorize-file (coloring-type input-file-name
&optional output-file-name
)
417 (let* ((input-file (if (pathname-type (merge-pathnames input-file-name
))
418 (merge-pathnames input-file-name
)
419 (make-pathname :type
"lisp"
420 :defaults
(merge-pathnames input-file-name
))))
421 (output-file (or output-file-name
422 (make-pathname :type
"html"
423 :defaults input-file
))))
424 (with-open-file (s2 output-file
:direction
:output
:if-exists
:supersede
)
425 (colorize-file-to-stream coloring-type input-file-name s2
))))
427 ;; coloring-types.lisp
429 ;(in-package :colorize)
431 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
432 (defparameter *version-token
* (gensym)))
434 (defparameter *symbol-characters
*
435 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*!%$&+-1234567890")
437 (defparameter *non-constituent
*
438 '(#\space
#\tab
#\newline
#\linefeed
#\page
#\return
439 #\" #\' #\
( #\
) #\
, #\
; #\` #\[ #\]))
441 (defparameter *special-forms
*
442 '("let" "load-time-value" "quote" "macrolet" "progn" "progv" "go" "flet" "the"
443 "if" "throw" "eval-when" "multiple-value-prog1" "unwind-protect" "let*"
444 "labels" "function" "symbol-macrolet" "block" "tagbody" "catch" "locally"
445 "return-from" "setq" "multiple-value-call"))
447 (defparameter *common-macros
*
448 '("loop" "cond" "lambda"))
450 (defparameter *open-parens
* '(#\
())
451 (defparameter *close-parens
* '(#\
)))
453 (define-coloring-type :lisp
"Basic Lisp"
454 :modes
(:first-char-on-line
:normal
:symbol
:escaped-symbol
:keyword
:string
:comment
455 :multiline
:character
456 :single-escaped
:in-list
:syntax-error
)
457 :default-mode
:first-char-on-line
461 (scan-any *symbol-characters
*)
462 (and (scan #\.
) (scan-any *symbol-characters
*))
463 (and (scan #\\) (advance 1)))
465 :until
(scan-any *non-constituent
*)
467 ((or (scan #\
:) (scan "#:"))
469 :until
(scan-any *non-constituent
*)
477 (scan-any *non-constituent
*)))
484 :until
(scan #\newline
)))
491 ((:normal
:first-char-on-line
)
498 :until
(scan #\newline
)))
504 :until
(scan #\newline
))))
508 :until
(scan "|#"))))
509 ((:symbol
:keyword
:escaped-symbol
:string
)
512 (set-mode :single-escaped
517 :formatter-variables
((paren-counter 0))
518 :formatter-after-hook
(lambda nil
520 (loop for i from paren-counter downto
1
521 collect
"</span></span>")))
523 (((:normal
:first-char-on-line
)
525 (declare (ignore type
))
529 (declare (ignore type
))
530 (labels ((color-parens (s)
531 (let ((paren-pos (find-if-not #'null
532 (mapcar #'(lambda (c)
534 (append *open-parens
*
537 (let ((before-paren (subseq s
0 paren-pos
))
538 (after-paren (subseq s
(1+ paren-pos
)))
539 (paren (elt s paren-pos
))
542 (when (member paren
*open-parens
* :test
#'char
=)
543 (setf count
(mod paren-counter
6))
546 (when (member paren
*close-parens
* :test
#'char
=)
547 (decf paren-counter
))
549 (format nil
"~A<span class=\"paren~A\">~C<span class=\"~A\">~A"
552 paren
*css-background-class
*
553 (color-parens after-paren
))
554 (format nil
"~A</span>~C</span>~A"
556 paren
(color-parens after-paren
))))
559 ((:symbol
:escaped-symbol
)
561 (declare (ignore type
))
562 (let* ((colon (position #\
: s
:from-end t
))
563 (new-s (or (and colon
(subseq s
(1+ colon
))) s
)))
566 (member new-s
*common-macros
* :test
#'string-equal
)
567 (member new-s
*special-forms
* :test
#'string-equal
)
569 (and (> (length new-s
) (length e
))
570 (string-equal e
(subseq new-s
0 (length e
)))))
572 (format nil
"<i><span class=\"symbol\">~A</span></i>" s
))
573 ((and (> (length new-s
) 2)
574 (char= (elt new-s
0) #\
*)
575 (char= (elt new-s
(1- (length new-s
))) #\
*))
576 (format nil
"<span class=\"special\">~A</span>" s
))
578 (:keyword
(lambda (type s
)
579 (declare (ignore type
))
580 (format nil
"<span class=\"keyword\">~A</span>"
582 ((:comment
:multiline
)
584 (declare (ignore type
))
585 (format nil
"<span class=\"comment\">~A</span>"
589 (declare (ignore type
))
590 (format nil
"<span class=\"character\">~A</span>"
594 (declare (ignore type
))
595 (format nil
"<span class=\"string\">~A</span>"
599 (call-formatter (cdr type
) s
)))
602 (declare (ignore type
))
603 (format nil
"<span class=\"syntaxerror\">~A</span>"
606 (define-coloring-type :scheme
"Scheme"
607 :autodetect
(lambda (text)
609 (search "scheme" text
:test
#'char-equal
)
610 (search "chicken" text
:test
#'char-equal
)))
616 :until
(scan-any *non-constituent
*)
620 :until
(scan #\
])))))
624 (declare (ignore type s
))
625 (let ((*open-parens
* (cons #\
[ *open-parens
*))
626 (*close-parens
* (cons #\
] *close-parens
*)))
627 (call-parent-formatter))))
628 ((:symbol
:escaped-symbol
)
630 (declare (ignore type
))
631 (let ((result (if (find-package :r5rs-lookup
)
632 (funcall (symbol-function (intern "SYMBOL-LOOKUP" :r5rs-lookup
))
635 (format nil
"<a href=\"~A\" class=\"symbol\">~A</a>"
636 result
(call-parent-formatter))
637 (call-parent-formatter)))))))
639 (define-coloring-type :elisp
"Emacs Lisp"
640 :autodetect
(lambda (name)
641 (member name
'("emacs")
642 :test
#'(lambda (name ext
)
643 (search ext name
:test
#'char-equal
))))
646 (((:symbol
:escaped-symbol
)
648 (declare (ignore type
))
649 (let ((result (if (find-package :elisp-lookup
)
650 (funcall (symbol-function (intern "SYMBOL-LOOKUP" :elisp-lookup
))
653 (format nil
"<a href=\"~A\" class=\"symbol\">~A</a>"
654 result
(call-parent-formatter))
655 (call-parent-formatter)))))))
657 (define-coloring-type :common-lisp
"Common Lisp"
658 :autodetect
(lambda (text)
659 (search "lisp" text
:test
#'char-equal
))
664 (set-mode :escaped-symbol
665 :until
(scan #\|
)))))
667 (((:symbol
:escaped-symbol
)
669 (declare (ignore type
))
670 (let* ((colon (position #\
: s
:from-end t
:test
#'char
=))
671 (to-lookup (if colon
(subseq s
(1+ colon
)) s
))
672 (result (if (find-package :clhs-lookup
)
673 (funcall (symbol-function (intern "SYMBOL-LOOKUP" :clhs-lookup
))
676 (format nil
"<a href=\"~A\" class=\"symbol\">~A</a>"
677 result
(call-parent-formatter))
678 (call-parent-formatter)))))))
680 (define-coloring-type :common-lisp-file
"Common Lisp File"
682 :default-mode
:in-list
685 (defvar *c-open-parens
* "([{")
686 (defvar *c-close-parens
* ")]}")
688 (defvar *c-reserved-words
*
689 '("auto" "break" "case" "char" "const"
690 "continue" "default" "do" "double" "else"
691 "enum" "extern" "float" "for" "goto"
692 "if" "int" "long" "register" "return"
693 "short" "signed" "sizeof" "static" "struct"
694 "switch" "typedef" "union" "unsigned" "void"
695 "volatile" "while" "__restrict" "_Bool"))
697 (defparameter *c-begin-word
* "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789")
698 (defparameter *c-terminators
* '(#\space
#\return
#\tab
#\newline
#\.
#\
/ #\-
#\
* #\
+ #\
{ #\
} #\
( #\
) #\' #\" #\
[ #\
] #\
< #\
> #\
#))
700 (define-coloring-type :basic-c
"Basic C"
701 :modes
(:normal
:comment
:word-ish
:paren-ish
:string
:char
:single-escape
:preprocessor
)
702 :default-mode
:normal
706 ((scan-any *c-begin-word
*)
708 :until
(scan-any *c-terminators
*)
715 (scan-any *c-open-parens
*)
716 (scan-any *c-close-parens
*))
726 :until
(advance 2))))
729 (set-mode :single-escape
730 :until
(advance 1)))))
733 :formatter-after-hook
(lambda nil
735 (loop for i from paren-counter downto
1
736 collect
"</span></span>")))
740 (declare (ignore type
))
744 (declare (ignore type
))
745 (format nil
"<span class=\"comment\">~A</span>"
749 (declare (ignore type
))
750 (format nil
"<span class=\"string\">~A</span>"
754 (declare (ignore type
))
755 (format nil
"<span class=\"character\">~A</span>"
759 (call-formatter (cdr type
) s
)))
762 (declare (ignore type
))
765 (if (eql (length s
) 1)
767 (when (member (elt s
0) (coerce *c-open-parens
* 'list
))
769 (setf count
(mod paren-counter
6))
770 (incf paren-counter
))
771 (when (member (elt s
0) (coerce *c-close-parens
* 'list
))
774 (setf count
(mod paren-counter
6)))
776 (format nil
"<span class=\"paren~A\">~A<span class=\"~A\">"
777 (1+ count
) s
*css-background-class
*)
778 (format nil
"</span>~A</span>"
783 (declare (ignore type
))
784 (if (member s
*c-reserved-words
* :test
#'string
=)
785 (format nil
"<span class=\"symbol\">~A</span>" s
)
789 (define-coloring-type :c
"C"
794 (set-mode :preprocessor
795 :until
(scan-any '(#\return
#\newline
))))))
799 (declare (ignore type
))
800 (format nil
"<span class=\"special\">~A</span>" s
)))))
802 (defvar *c
++-reserved-words
*
803 '("asm" "auto" "bool" "break" "case"
804 "catch" "char" "class" "const" "const_cast"
805 "continue" "default" "delete" "do" "double"
806 "dynamic_cast" "else" "enum" "explicit" "export"
807 "extern" "false" "float" "for" "friend"
808 "goto" "if" "inline" "int" "long"
809 "mutable" "namespace" "new" "operator" "private"
810 "protected" "public" "register" "reinterpret_cast" "return"
811 "short" "signed" "sizeof" "static" "static_cast"
812 "struct" "switch" "template" "this" "throw"
813 "true" "try" "typedef" "typeid" "typename"
814 "union" "unsigned" "using" "virtual" "void"
815 "volatile" "wchar_t" "while"))
817 (define-coloring-type :c
++ "C++"
823 :until
(scan-any '(#\return
#\newline
))))))
827 (declare (ignore type
))
828 (if (member s
*c
++-reserved-words
* :test
#'string
=)
829 (format nil
"<span class=\"symbol\">~A</span>"
833 (defvar *java-reserved-words
*
834 '("abstract" "boolean" "break" "byte" "case"
835 "catch" "char" "class" "const" "continue"
836 "default" "do" "double" "else" "extends"
837 "final" "finally" "float" "for" "goto"
838 "if" "implements" "import" "instanceof" "int"
839 "interface" "long" "native" "new" "package"
840 "private" "protected" "public" "return" "short"
841 "static" "strictfp" "super" "switch" "synchronized"
842 "this" "throw" "throws" "transient" "try"
843 "void" "volatile" "while"))
845 (define-coloring-type :java
"Java"
850 (declare (ignore type
))
851 (if (member s
*java-reserved-words
* :test
#'string
=)
852 (format nil
"<span class=\"symbol\">~A</span>"
856 (let ((terminate-next nil
))
857 (define-coloring-type :objective-c
"Objective C"
858 :autodetect
(lambda (text) (search "mac" text
:test
#'char
=))
859 :modes
(:begin-message-send
:end-message-send
)
863 (set-mode :begin-message-send
867 (set-mode :end-message-send
870 ((scan-any *c-begin-word
*)
873 (and (peek-any '(#\
:))
874 (setf terminate-next t
))
875 (and terminate-next
(progn
876 (setf terminate-next nil
)
878 (scan-any *c-terminators
*))
884 (set-mode :word-ish
:until
(advance 1) :advancing nil
)
885 (setf terminate-next t
))))
887 :formatter-variables
((is-keyword nil
) (in-message-send nil
))
889 ((:begin-message-send
891 (setf is-keyword nil
)
892 (setf in-message-send t
)
893 (call-formatter (cons :paren-ish type
) s
)))
896 (setf is-keyword nil
)
897 (setf in-message-send nil
)
898 (call-formatter (cons :paren-ish type
) s
)))
901 (declare (ignore type
))
903 (let ((result (if (find-package :cocoa-lookup
)
904 (funcall (symbol-function (intern "SYMBOL-LOOKUP" :cocoa-lookup
))
907 (format nil
"<a href=\"~A\" class=\"symbol\">~A</a>"
909 (if (member s
*c-reserved-words
* :test
#'string
=)
910 (format nil
"<span class=\"symbol\">~A</span>" s
)
913 (format nil
"<span class=\"keyword\">~A</span>" s
)
916 (setf is-keyword
(not is-keyword
))))))))
922 ;(asdf:oos 'asdf:load-op :colorize)
924 (defmacro with-each-stream-line
((var stream
) &body body
)
928 `(let ((,strm
,stream
)
930 (do ((,var
(read-line ,strm nil
,eof
) (read-line ,strm nil
,eof
)))
934 (defun system (control-string &rest args
)
935 "Interpolate ARGS into CONTROL-STRING as if by FORMAT, and
936 synchronously execute the result using a Bourne-compatible shell, with
937 output to *verbose-out*. Returns the shell's exit code."
938 (let ((command (apply #'format nil control-string args
)))
939 (format t
"; $ ~A~%" command
)
941 (sb-impl::process-exit-code
945 :input nil
:output
*standard-output
*))
947 (ext:process-exit-code
951 :input nil
:output
*verbose-out
*))
952 #+clisp
;XXX not exactly *verbose-out*, I know
953 (ext:run-shell-command command
:output
:terminal
:wait t
)
956 (defun strcat (&rest strings
)
957 (apply #'concatenate
'string strings
))
959 (defun string-starts-with (start str
)
960 (and (>= (length str
) (length start
))
961 (string-equal start str
:end2
(length start
))))
963 (defmacro string-append
(outputstr &rest args
)
964 `(setq ,outputstr
(concatenate 'string
,outputstr
,@args
)))
966 (defconstant +indent
+ 2
967 "Indentation used in the examples.")
969 (defun texinfo->raw-lisp
(code)
970 "Answer CODE with spurious Texinfo output removed. For use in
971 preprocessing output in a @lisp block before passing to colorize."
973 (with-output-to-string (output)
974 (do* ((last-position 0)
976 #0=(search #1="<span class=\"roman\">" code
977 :start2 last-position
:test
#'char-equal
)
979 ((eq nil next-position
)
980 (write-string code output
:start last-position
))
981 (write-string code output
:start last-position
:end next-position
)
982 (let ((end (search #2="</span>" code
983 :start2
(+ next-position
(length #1#))
984 :test
#'char-equal
)))
985 (assert (integerp end
) ()
986 "Missing ~A tag in HTML for @lisp block~%~
987 HTML contents of block:~%~A" #2# code
)
988 (write-string code output
989 :start
(+ next-position
(length #1#))
991 (setf last-position
(+ end
(length #2#))))))))
993 (defun process-file (from to
)
994 (with-open-file (output to
:direction
:output
:if-exists
:error
)
995 (with-open-file (input from
:direction
:input
)
996 (let ((line-processor nil
)
999 ((process-line-inside-pre (line)
1000 (cond ((string-starts-with "</pre>" line
)
1001 (with-input-from-string
1002 (stream (colorize:html-colorization
1005 (apply #'concatenate
'string
1006 (nreverse piece-of-code
)))))
1007 (with-each-stream-line (cline stream
)
1008 (format output
" ~A~%" cline
)))
1009 (write-line line output
)
1010 (setq piece-of-code
'()
1011 line-processor
#'process-regular-line
))
1012 (t (let ((to-append (subseq line
+indent
+)))
1013 (push (if (string= "" to-append
)
1015 to-append
) piece-of-code
)
1016 (push (string #\Newline
) piece-of-code
)))))
1017 (process-regular-line (line)
1018 (let ((len (some (lambda (test-string)
1019 (when (string-starts-with test-string line
)
1020 (length test-string
)))
1021 '("<pre class=\"lisp\">"
1022 "<pre class=\"smalllisp\">"))))
1024 (setq line-processor
#'process-line-inside-pre
)
1025 (write-string "<pre class=\"lisp\">" output
)
1026 (push (subseq line
(+ len
+indent
+)) piece-of-code
)
1027 (push (string #\Newline
) piece-of-code
))
1028 (t (write-line line output
))))))
1029 (setf line-processor
#'process-regular-line
)
1030 (with-each-stream-line (line input
)
1031 (funcall line-processor line
)))))))
1033 (defun process-dir (dir)
1034 (dolist (html-file (directory dir
))
1035 (let* ((name (namestring html-file
))
1036 (temp-name (strcat name
".temp")))
1037 (process-file name temp-name
)
1038 (system "mv ~A ~A" temp-name name
))))
1040 ;; (go "/tmp/doc/manual/html_node/*.html")
1044 (assert (first ext
:*args
*))
1045 (process-dir (first ext
:*args
*)))
1049 (assert (second sb-ext
:*posix-argv
*))
1050 (process-dir (second sb-ext
:*posix-argv
*))