git.fiddlerwoaroof.com
src/editor-color-theme.lisp
f9928580
 (cl:in-package #:cl-user)
 
 (defpackage #:mfa-tool.editor-color-theme
   (:use #:cl)
   (:export #:all-color-themes
            #:color-theme-args
            #:color-theme
            #:define-color-theme
            #:remove-color-theme
            #:zenburn-paren-colors))
 
 (in-package #:mfa-tool.editor-color-theme)
 
 ;;; Configuration
 
 (defvar *foreground-color* nil)
 
 (defvar *background-color* nil)
 
 (defconstant +default-parenthesis-font-face-colours+ '(:red :black :darkgreen :darkorange3 :blue :purple))
 
 ;;; Implementation
 
 (defvar *all-color-themes* (make-hash-table :test 'string=))
 
 (defun all-color-themes ()
   (loop for key being the hash-keys in *all-color-themes*
         collect key))
 
 (defun color-theme-data (theme-name)
   (multiple-value-bind (color-theme-data found?)
       (gethash theme-name *all-color-themes*)
     (if found?
         color-theme-data
         (error "No color theme named ~s found." theme-name))))
 
 (defun color-theme-super-theme-names (theme-name)
   (first (color-theme-data theme-name)))
 
 (defun color-theme-args (theme-name)
   (rest (color-theme-data theme-name)))
 
 (defvar *all-editor-panes* (make-hash-table :test 'eq
                                             :weak-kind :key))
 
 (defun update-editor-pane (pane)
   (setf (capi:simple-pane-foreground pane) (or *foreground-color* :color_windowtext))
   (setf (capi:simple-pane-background pane) (or *background-color* :color_window))
 
   (let ((recolorize-p (editor::buffer-font-lock-mode-p (capi:editor-pane-buffer pane))))
     (when recolorize-p
       (gp:invalidate-rectangle pane)))
   (values))
 
 (defun update-editor-panes ()
   (maphash #'(lambda (pane value)
                (declare (ignore value))
                (update-editor-pane pane))
            *all-editor-panes*)
   (values))
 
 (defvar *editor-face-names*
   '(:region
     :show-point-face
     :interactive-input-face
     :highlight
     :non-focus-complete-face
     :font-lock-function-name-face
     :font-lock-comment-face
     :font-lock-type-face
     :font-lock-variable-name-face
     :font-lock-string-face
     :font-lock-keyword-face
     :font-lock-builtin-face
     :compiler-note-highlight
     :compiler-warning-highlight
     :compiler-error-highlight
     ))
 
 (defun set-color-theme (theme-name)
   (destructuring-bind (&rest color-theme-args
                        &key foreground background &allow-other-keys)
       (color-theme-args theme-name)
 
     (setf *foreground-color* (or foreground :color_windowtext))
     (setf *background-color* (or background :color_window))
 
     (lw:when-let (parenthesis-colors
                   (getf color-theme-args :parenthesis-font-face-colours
                         +default-parenthesis-font-face-colours+))
       (editor::set-parenthesis-colours parenthesis-colors))
 
     (dolist (name *editor-face-names*)
       (let* ((color-theme-args-for-face (getf color-theme-args name))
              (face-name (intern (string name) '#:editor))
              (face (editor:make-face face-name :if-exists t)))
         (apply 'editor:make-face face-name :if-exists :overwrite
                                            :documentation (or (getf color-theme-args-for-face :documentation)
                                                               (slot-value face 'documentation))
                                            color-theme-args-for-face))))
 
   theme-name)
 
 (defun color-theme (theme-name)
   (mapc 'set-color-theme (color-theme-super-theme-names theme-name))
   (set-color-theme theme-name)
 
   (update-editor-panes)
 
   theme-name)
 
 (defun define-color-theme (theme-name super-theme-names
                            &rest color-theme-args &key &allow-other-keys)
   (dolist (super-theme-name super-theme-names)
     (multiple-value-bind (color-theme-data found?)
         (gethash super-theme-name *all-color-themes*)
       (declare (ignore color-theme-data))
       (unless found?
         (warn "Inherited color theme ~s not defined." super-theme-name))))
 
   (setf (gethash theme-name *all-color-themes*) (list* super-theme-names color-theme-args))
 
   theme-name)
 
 (defun remove-color-theme (theme-name)
   (remhash theme-name *all-color-themes*))
 
 (sys::without-warning-on-redefinition
   (defmethod initialize-instance :around ((pane capi:editor-pane) &key &allow-other-keys)
     (multiple-value-prog1
         (call-next-method)
 
       (setf (gethash pane *all-editor-panes*) pane)
 
       (when *foreground-color*
         (setf (capi:simple-pane-foreground pane) *foreground-color*))
       (when *background-color*
         (setf (capi:simple-pane-background pane) *background-color*))))
   )
 
 ;; This makes it "work" after the podium is launched
 (defun is-editor-pane-p (obj)
   (and (typep obj 'capi:editor-pane)
        (not (eq obj (hcl:class-prototype (class-of obj))))))
 
 (defun cache-existing-pane (pane)
   (setf (gethash pane *all-editor-panes*) pane))
 
 (defun cache-if-pane (obj)
   (when (is-editor-pane-p obj)
     (cache-existing-pane obj)))
 
 #+:lispworks-personal-edition
 (hcl:sweep-all-objects #'cache-if-pane)
 
 
 ;;; Initial color themes
 
 (define-color-theme "default" ()
   :foreground nil :background nil
   :region '(:foreground :color_highlighttext
             :background :color_highlight)
   :show-point-face '(:background :green)
   :interactive-input-face '(:foreground :red3)
   :highlight '(:bold-p t)
   :non-focus-complete-face '(:background :tweak_background)
   :font-lock-function-name-face '(:foreground :blue)
   :font-lock-comment-face '(:foreground :firebrick)
   :font-lock-type-face '(:foreground :forestgreen)
   :font-lock-variable-name-face '(:foreground :darkgoldenrod)
   :font-lock-string-face '(:foreground :rosybrown)
   :font-lock-keyword-face '(:foreground :purple)
   :font-lock-builtin-face '(:foreground :orchid)
   :compiler-note-highlight '(:foreground :magenta)
   :compiler-warning-highlight '(:foreground :orange3)
   :compiler-error-highlight '(:foreground :red))
 
 (define-color-theme "plain" ()
   :foreground nil :background nil
   :region '(:foreground :color_highlighttext
             :background :color_highlight)
   :show-point-face '()
   :interactive-input-face '()
   :highlight '(:bold-p t)
   :non-focus-complete-face '(:background :tweak_background)
   :font-lock-function-name-face '()
   :font-lock-comment-face '()
   :font-lock-type-face '()
   :font-lock-variable-name-face '()
   :font-lock-string-face '()
   :font-lock-keyword-face '()
   :font-lock-builtin-face '()
   :compiler-note-highlight '()
   :compiler-warning-highlight '()
   :compiler-error-highlight '())
 
 (define-color-theme "emacs" ()
   :foreground nil :background nil
   :region '(:foreground :color_highlighttext
             :background :color_highlight)
   :show-point-face '(:background :green)
   :interactive-input-face '(:foreground :red3)
   :highlight '(:bold-p t)
   :non-focus-complete-face '(:background :tweak_background)
   :font-lock-function-name-face '(:foreground :blue)
   :font-lock-comment-face '(:foreground :gray40)
   :font-lock-type-face '(:foreground :forestgreen)
   :font-lock-variable-name-face '(:foreground :darkgoldenrod)
   :font-lock-string-face '(:foreground :rosybrown)
   :font-lock-keyword-face '(:foreground :purple)
   :font-lock-builtin-face '(:foreground :orchid)
   :compiler-note-highlight '(:foreground :magenta)
   :compiler-warning-highlight '(:foreground :orange3)
   :compiler-error-highlight '(:foreground :red))
 
 (define-color-theme "torte" ()
   :foreground (color:make-rgb 0.8s0 0.8s0 0.8s0)
   :background (color:make-rgb 0.0s0 0.0s0 0.0s0)
   :region '(:foreground :color_highlighttext
             :background :color_highlight)
   :show-point-face `(:background ,(color:make-rgb 0.6275s0 0.1255s0 0.9412s0))
   :interactive-input-face '(:foreground :pink)
   :highlight '(:bold-p t)
   :non-focus-complete-face '(:background :tweak_background)
   :font-lock-function-name-face `(:foreground ,(color:make-rgb 0.0s0 1.0s0 1.0s0))
   :font-lock-comment-face `(:foreground ,(color:make-rgb 0.5s0 0.6275s0 1.0s0))
   :font-lock-type-face `(:foreground ,(color:make-rgb 0.5s0 1.0s0 0.5s0))
   :font-lock-variable-name-face `(:foreground ,(color:make-rgb 1.0s0 1.0s0 1.0s0))
   :font-lock-string-face `(:foreground ,(color:make-rgb 1.0s0 0.6275s0 0.6275s0))
   :font-lock-keyword-face `(:foreground ,(color:make-rgb 1.0s0 1.0s0 0.0s0))
   :font-lock-builtin-face `(:foreground ,(color:make-rgb 1.0s0 1.0s0 0.0s0))
   :compiler-note-highlight '(:foreground :magenta)
   :compiler-warning-highlight '(:foreground :orange)
   :compiler-error-highlight '(:foreground :red))
 
 
 (defun make-rgb (red green blue &optional alpha)
   (color:make-rgb (/ red 255s0)
                   (/ green 255s0)
                   (/ blue 255s0)
                   (and alpha (/ alpha 255s0))))
 
 (defvar *solarized-color-table*
   '(:solarized-base03  (#x00 #x2b #x36)
     :solarized-base02  (#x07 #x36 #x42)
     :solarized-base01  (#x58 #x6e #x75)
     :solarized-base00  (#x65 #x7b #x83)
     :solarized-base0   (#x83 #x94 #x96)
     :solarized-base1   (#x93 #xa1 #xa1)
     :solarized-base2   (#xee #xe8 #xd5)
     :solarized-base3   (#xfd #xf6 #xe3)
     :solarized-yellow  (#xb5 #x89 #x00)
     :solarized-orange  (#xcb #x4b #x16)
     :solarized-red     (#xdc #x32 #x2f)
     :solarized-magenta (#xd3 #x36 #x82)
     :solarized-violet  (#x6c #x71 #xc4)
     :solarized-blue    (#x26 #x8b #xd2)
     :solarized-cyan    (#x2a #xa1 #x98)
     :solarized-green   (#x85 #x99 #x00)))
 
 (loop for list on *solarized-color-table* by #'cddr
       for name = (first list)
       for rgb = (second list)
       do
          (color:define-color-alias
              name
              (apply #'make-rgb rgb)))
 
 (define-color-theme "solarized-light" ()
   :foreground :solarized-base00
   :background :solarized-base3
   :region '(:foreground :solarized-base1
             :background :solarized-base3
             :inverse-p t)
   :highlight '(:background :solarized-base2)
   :font-lock-function-name-face '(:foreground :solarized-blue)
   :font-lock-comment-face '(:foreground :solarized-base1 :italic-p t)
   :font-lock-type-face '(:foreground :solarized-yellow)
   :font-lock-variable-name-face '(:foreground :solarized-blue)
   :font-lock-string-face '(:foreground :solarized-cyan)
   :font-lock-keyword-face '(:foreground :solarized-green)
   :font-lock-builtin-face '(:foreground :solarized-green)
   :compiler-note-highlight '(:foreground :solarized-green
                              :bold-p t)
   :compiler-warning-highlight '(:foreground :solarized-orange
                                 :bold-p t)
   :compiler-error-highlight '(:foreground :solarized-red
                               :inverse-p t)
   :show-point-face '(:foreground :solarized-cyan
                      :bold-p t :inverse-p t)
   :interactive-input-face '(:foreground :solarized-red)
   :non-focus-complete-face '(:background :solarized-base3)
   :parenthesis-font-face-colours '(:solarized-red
                                    :solarized-base01
                                    :solarized-green
                                    :solarized-orange
                                    :solarized-blue
                                    :solarized-magenta))
 
 (define-color-theme "solarized-dark" ()
   :foreground :solarized-base0
   :background :solarized-base03
   :region '(:foreground :solarized-base01
             :background :solarized-base03
             :inverse-p t)
   :highlight '(:background :solarized-base02)
   :font-lock-function-name-face '(:foreground :solarized-blue)
   :font-lock-comment-face '(:foreground :solarized-base01 :italic-p t)
   :font-lock-type-face '(:foreground :solarized-yellow)
   :font-lock-variable-name-face '(:foreground :solarized-blue)
   :font-lock-string-face '(:foreground :solarized-cyan)
   :font-lock-keyword-face '(:foreground :solarized-green)
   :font-lock-builtin-face '(:foreground :solarized-green)
   :compiler-note-highlight '(:foreground :solarized-green
                              :bold-p t)
   :compiler-warning-highlight '(:foreground :solarized-orange
                                 :bold-p t)
   :compiler-error-highlight '(:foreground :solarized-red
                               :inverse-p t)
   :show-point-face '(:foreground :solarized-cyan
                      :bold-p t :inverse-p t)
   :interactive-input-face '(:foreground :solarized-red)
   :non-focus-complete-face '(:background :solarized-base03)
   :parenthesis-font-face-colours '(:solarized-red
                                    :solarized-base1
                                    :solarized-green
                                    :solarized-orange
                                    :solarized-blue
                                    :solarized-magenta))
 
 (eval-when (:compile-toplevel :load-toplevel :execute)
   (defun hex->color (hex)
     (declare (optimize (speed 3) (safety 1) (debug 1)))
     (check-type hex (string 7))
     (flet ((extract-digits (string start end)
              (check-type string (simple-string 7))
              (parse-integer string
                             :start start
                             :end end
                             :radix 16)))
       (let* ((hex (coerce hex 'simple-string))
              (r (extract-digits hex 1 3))
              (g (extract-digits hex 3 5))
              (b (extract-digits hex 5 7)))
         (color:make-rgb (/ r 255.0)
                         (/ g 255.0)
                         (/ b 255.0))))))
 
 (eval-when (:compile-toplevel :load-toplevel :execute)
   (defparameter +zenburn-colors+
     `((zenburn-fg+2 ,(hex->color "#FFFFEF"))
       (zenburn-fg+1 ,(hex->color "#F5F5D6"))
       (zenburn-fg ,(hex->color "#DCDCCC"))
       (zenburn-fg-1 ,(hex->color "#A6A689"))
       (zenburn-fg-2 ,(hex->color "#656555"))
       (zenburn-black ,(hex->color "#000000"))
       (zenburn-bg-2 ,(hex->color "#000000"))
       (zenburn-bg-1 ,(hex->color "#111112"))
       (zenburn-bg-05 ,(hex->color "#383838"))
       (zenburn-bg ,(hex->color "#2A2B2E"))
       (zenburn-bg+05 ,(hex->color "#494949"))
       (zenburn-bg+1 ,(hex->color "#4F4F4F"))
       (zenburn-bg+2 ,(hex->color "#5F5F5F"))
       (zenburn-bg+3 ,(hex->color "#6F6F6F"))
       (zenburn-red+2 ,(hex->color "#ECB3B3"))
       (zenburn-red+1 ,(hex->color "#DCA3A3"))
       (zenburn-red ,(hex->color "#CC9393"))
       (zenburn-red-1 ,(hex->color "#BC8383"))
       (zenburn-red-2 ,(hex->color "#AC7373"))
       (zenburn-red-3 ,(hex->color "#9C6363"))
       (zenburn-red-4 ,(hex->color "#8C5353"))
       (zenburn-red-5 ,(hex->color "#7C4343"))
       (zenburn-red-6 ,(hex->color "#6C3333"))
       (zenburn-orange ,(hex->color "#DFAF8F"))
       (zenburn-yellow ,(hex->color "#F0DFAF"))
       (zenburn-yellow-1 ,(hex->color "#E0CF9F"))
       (zenburn-yellow-2 ,(hex->color "#D0BF8F"))
       (zenburn-green-5 ,(hex->color "#2F4F2F"))
       (zenburn-green-4 ,(hex->color "#3F5F3F"))
       (zenburn-green-3 ,(hex->color "#4F6F4F"))
       (zenburn-green-2 ,(hex->color "#5F7F5F"))
       (zenburn-green-1 ,(hex->color "#6F8F6F"))
       (zenburn-green ,(hex->color "#7F9F7F"))
       (zenburn-green+1 ,(hex->color "#8FB28F"))
       (zenburn-green+2 ,(hex->color "#9FC59F"))
       (zenburn-green+3 ,(hex->color "#AFD8AF"))
       (zenburn-green+4 ,(hex->color "#BFEBBF"))
       (zenburn-cyan ,(hex->color "#93E0E3"))
       (zenburn-blue+3 ,(hex->color "#BDE0F3"))
       (zenburn-blue+2 ,(hex->color "#ACE0E3"))
       (zenburn-blue+1 ,(hex->color "#94BFF3"))
       (zenburn-blue ,(hex->color "#8CD0D3"))
       (zenburn-blue-1 ,(hex->color "#7CB8BB"))
       (zenburn-blue-2 ,(hex->color "#6CA0A3"))
       (zenburn-blue-3 ,(hex->color "#5C888B"))
       (zenburn-blue-4 ,(hex->color "#4C7073"))
       (zenburn-blue-5 ,(hex->color "#366060"))
       (zenburn-magenta ,(hex->color "#DC8CC3")))))
 
 (defmacro with-zenburn-colors (&body body)
   `(let ,+zenburn-colors+
      (declare (ignorable ,@(mapcar 'car +zenburn-colors+)))
      ,@body))
 
 (with-zenburn-colors
   (define-color-theme "zenburn" ()
     :foreground zenburn-fg
     :background zenburn-bg
     :region `(:foreground ,zenburn-fg+1
               :background ,zenburn-bg+1)
     :show-point-face `(:background ,zenburn-bg+2)
     :interactive-input-face `(:foreground ,zenburn-red)
     :highlight '(:bold-p t)
     :non-focus-complete-face `(:background :tweak_background)
     :font-lock-function-name-face `(:foreground ,zenburn-blue)
     :font-lock-comment-face `(:foreground ,zenburn-fg-1)
     :font-lock-type-face `(:foreground ,zenburn-green)
     :font-lock-variable-name-face `(:foreground ,zenburn-yellow)
     :font-lock-string-face `(:foreground ,zenburn-orange)
     :font-lock-keyword-face `(:foreground ,zenburn-cyan)
     :font-lock-builtin-face `(:foreground ,zenburn-blue+1)
     :compiler-note-highlight `(:foreground ,zenburn-fg+1)
     :compiler-warning-highlight `(:foreground ,zenburn-orange)
     :compiler-error-highlight `(:foreground ,zenburn-red+1)))
 
 (defun zenburn-paren-colors ()
   (with-zenburn-colors
     (capi:set-editor-parenthesis-colors
      (list zenburn-red
            zenburn-green
            zenburn-blue-1
            zenburn-green+1
            zenburn-blue+1
            zenburn-green+2
            zenburn-orange
            zenburn-cyan
            zenburn-magenta
            zenburn-yellow))))
 
 ;;; Show presence when loaded
 (pushnew :editor-color-theme *features*)