(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*)