← Blog of oldlisper

Подстветка кода с помощью colorize

· 02.08.2013 00:00
· original author: archimag

Подстветка кода с помощью colorize

Для подсветки кода на Common Lisp я люблю использовать colorize, потому что она добавляет в разметку ссылки на Hyperspec. А вот структура разметки получается та ещё, я хочу другую, более современную. Но исходники colorize ужасны и копаться в них я не хочу. Поэтому я написал функцию, которую берёт оригинальную разметку, генерируемую colorize и превращает её в нечто более интересное (для меня):

  1. (defparameter *span-classes*
  2.   '("symbol" "special" "keyword" "comment" "string" "character"))
  3. (defun update-code-markup (markup)
  4.   (labels
  5.       ((bad-span-p (node)
  6.          (and (string-equal (xtree:local-name node) "span")
  7.               (not (member (xtree:attribute-value node "class") *span-classes*
  8.                            :test #'string-equal))))
  9.       ;;---------------------------------------
  10.       (comment-p (node)
  11.          (and (string-equal (xtree:local-name node) "span")
  12.               (string-equal (xtree:attribute-value node "class") "comment")))
  13.       ;;---------------------------------------
  14.       (br-p (node)
  15.          (string-equal (xtree:local-name node) "br"))
  16.       ;;---------------------------------------
  17.       (flatten-spans (node)
  18.          (iter (for el in (xtree:all-childs node))
  19.                (flatten-spans el))
  20.         ;;---------------------------------------
  21.         (when (comment-p node)
  22.            (setf (xtree:text-content node)
  23.                  (xtree:text-content node))
  24.            (xtree:insert-child-after (xtree:make-element "br") node))
  25.         ;;---------------------------------------
  26.         (when (bad-span-p node)
  27.            (iter (for el in (xtree:all-childs node))
  28.                  (xtree:insert-child-before (xtree:detach el) node))
  29.            (xtree:remove-child node))))
  30.    ;;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  31.    (html:with-parse-html (doc (format nil "<div>~A</div>" markup))
  32.       (let ((div (xtree:first-child (xtree:first-child (xtree:root doc)))))
  33.         (flatten-spans div)
  34.         (xtree:with-object (fragment (xtree:make-document-fragment doc))
  35.          ;;---------------------------------------
  36.          (let* ((pre (xtree:make-child-element fragment "div"))
  37.                  (ol (xtree:make-child-element pre "ol")))
  38.             (setf (xtree:attribute-value pre "class")
  39.                   "prettyprint linenums")
  40.             (setf (xtree:attribute-value ol "class")
  41.                   "linenums")
  42.            ;;---------------------------------------
  43.            (iter (for line in (split-sequence:split-sequence-if #'br-p (xtree:all-childs div)))
  44.                   (for i from 0)
  45.                   (let ((li (xtree:make-child-element ol "li")))
  46.                     (setf (xtree:attribute-value li "class")
  47.                           (format nil "L~s" i))
  48.                    ;;---------------------------------------
  49.                    (iter (for el in line)
  50.                           (xtree:append-child li (xtree:detach el))))))
  51.          ;;---------------------------------------
  52.          (html:serialize-html fragment :to-string))))))

Здесь можно видеть не только код, но и непосредственный результат.

Кстати, функция довольно большая. Читать "сплошное месиво кода" на CL бывает не очень приятно. В других языках код обычно "разряжают" с помощью пустых строк. Но в CL это эстетически смотрится как-то не очень. Так что я решил попробовать использовать линии в качестве разделителей. Пока мне кажется, что получается неплохо.