Emacs 重塑计划 - 2. 主题与配色 + lisp函数
一个编辑器除了其功能外,另一个对编辑者非常重要的特点,就是他的颜值了。只有一个符合你的审美、视觉使用习惯的编辑器,才能让你免于疲劳和乏味。
因此我们势必要给我们的Emacs增加外观特征。
主题
关于Emacs的主题,Emacs自带以及互联网上其实有非常多的选择。我曾经用过monokai
,tango-dark
还有dracula
等。
LazyCat 创建了一个他自己的主题版本,是一个相当丰富的主题和配色设置。我肯定是没有这个精力从零开始打造一个主题,但是通过借鉴和模仿,构建一个属于我的主题配色是完全可行的。
自定义选项组
首先,我们要定义一个关于Emacs主题的自定义选项组(custom group),命名为lialittis-themes
,这个组用于组织和管理与主题相关的各种选项,以及一些全局变量等。例如:
(defgroup lialittis-themes nil
"Options for lialittis-themes."
:group 'faces)
(defcustom lialittis-themes-padded-modeline nil
"Default value for padded-modeline setting for themes that support it."
:group 'lialittis-themes
:type '(choice integer boolean))
(defcustom lialittis-themes-enable-bold t
"If nil, bold will be disabled across all faces."
:group 'lialittis-themes
:type 'boolean)
(defcustom lialittis-themes-enable-italic t
"If nil, italics will be disabled across all faces."
:group 'lialittis-themes
:type 'boolean)
(defvar lialittis-themes--colors nil)
(defvar lialittis--min-colors '(257 256 16))
(defvar lialittis--quoted-p nil)
(defvar lialittis-themes--faces nil)
辅助函数 - 分析部分lazycat主题中的代码
我们需要预先加载 cl-lib 库,该库提供了一些通用的 Emacs Lisp 函数,比如列表操作和迭代器等。在这里可能被用于一些辅助函数或者宏的定义。
def-lazycat-theme
定义一个宏,用于定义一个LAZYCAT主题。
(defmacro def-lazycat-theme (name docstring defs &optional extra-faces extra-vars)
"Define a LAZYCAT theme, named NAME (a symbol)."
(declare (doc-string 2))
(let ((lazycat-themes--colors defs))
`(let* ((bold lazycat-themes-enable-bold)
(italic lazycat-themes-enable-italic)
,@defs)
(setq lazycat-themes--colors
(list ,@(cl-loop for (var val) in defs
collect `(cons ',var ,val))))
(deftheme ,name ,docstring)
(custom-theme-set-faces
',name ,@(lazycat-themes-prepare-facelist extra-faces))
(custom-theme-set-variables
',name ,@(lazycat-themes-prepare-varlist extra-vars))
(unless bold (set-face-bold 'bold nil))
(unless italic (set-face-italic 'italic nil))
(provide-theme ',name))))
(defmacro def-lazycat-theme (name docstring defs &optional extra-faces extra-vars) ...)
有四个参数:name
是主题名称(一个符号),docstring
是主题的文档字符串,defs
是主题的定义,extra-faces
和 extra-vars
是额外的面部和变量。 (let ((lazycat-themes--colors defs)) ...)
局部变量 lazycat-themes--colors
, defs
是一个列表,包含了主题的颜色定义。 (let* ((bold lazycat-themes-enable-bold) ...) ...)
定义了几个局部变量,bold
和 italic
变量分别表示是否启用了粗体和斜体效果,它们的值取自于全局变量 lazycat-themes-enable-bold
和 lazycat-themes-enable-italic
。(setq lazycat-themes--colors ...)
将 lazycat-themes--colors
设置为一个新的列表,其中包含了主题的各种颜色定义。这些定义是从 defs
中提取并转换成 (variable . value)
对的形式。 (deftheme ,name ,docstring)
使用 deftheme
宏定义了一个新的 Emacs 主题,名称为 name
,并附带了相应的文档字符串 docstring
。 (custom-theme-set-faces ...)
用于设置主题的面部,使用了 custom-theme-set-faces
函数,并调用了一个辅助函数 lazycat-themes-prepare-facelist
,该函数用于处理额外的面部列表 extra-faces
。 (custom-theme-set-variables ...)
设置主题的变量,使用了 custom-theme-set-variables
函数,并调用 lazycat-themes-prepare-varlist
,该函数用于处理额外的变量列表 extra-vars
。 (unless bold (set-face-bold 'bold nil))
和 (unless italic (set-face-italic 'italic nil))
根据 bold
和 italic
变量的值来设置粗体和斜体效果。如果它们的值为 nil
,则分别禁用对应的效果。最后(provide-theme ',name)
提供了刚刚定义的主题,使得其他代码可以使用并加载这个主题。
下面演示一下lazycat是如何使用这个宏的:
(def-lazycat-theme lazycat-dark
"A dark theme inspired by Atom One Dark"
;; name default 256 16
((bg '("#242525" nil nil ))
(bg-alt '("#333333" nil nil ))
(base0 '("#1B2229" "black" "black" ))
(base1 '("#1c1f24" "#1e1e1e" "brightblack" ))
(base2 '("#202328" "#2e2e2e" "brightblack" ))
(base3 '("#23272e" "#262626" "brightblack" ))
(base4 '("#3f444a" "#3f3f3f" "brightblack" ))
(base5 '("#5B6268" "#525252" "brightblack" ))
(base6 '("#73797e" "#6b6b6b" "brightblack" ))
(base7 '("#9ca0a4" "#979797" "brightblack" ))
(base8 '("#DFDFDF" "#dfdfdf" "white" ))
(fg '("#00CE00" "#bfbfbf" "brightwhite" ))
(fg-alt '("green4" "#2d2d2d" "white" ))
(grey base4)
(red '("#ff6c6b" "#ff6655" "red" ))
(orange '("#da8548" "#dd8844" "brightred" ))
(green '("#98be65" "#99bb66" "green" ))
(teal '("#4db5bd" "#44b9b1" "brightgreen" ))
(yellow '("#ECBE7B" "#ECBE7B" "yellow" ))
(blue '("#51afef" "#51afef" "brightblue" ))
(dark-blue '("#2257A0" "#2257A0" "blue" ))
(magenta '("#c678dd" "#c678dd" "brightmagenta"))
(violet '("#a9a1e1" "#a9a1e1" "magenta" ))
(cyan '("#46D9FF" "#46D9FF" "brightcyan" ))
(dark-cyan '("#5699AF" "#5699AF" "cyan" ))
;; face categories -- required for all themes
(highlight "green")
(vertical-bar (lazycat-darken base1 0.1))
(selection dark-blue)
(builtin "#00b8ff")
(comments "#a7a7a7")
(doc-comments "#aaaaaa")
(constants "#bd00ff")
(functions "gold2")
(keywords "#004FFF")
(methods cyan)
(operators "cyan3")
(type "#00b8ff")
(strings "#DFD67A")
(variables "gold2")
(numbers orange)
(region "#3F90F7")
(region-fg "#FFF")
(error red)
(warning yellow)
(success green)
(vc-modified orange)
(vc-added green)
(vc-deleted red)
;; custom categories
(hidden `(,(car bg) "black" "black"))
(-modeline-bright lazycat-dark-brighter-modeline)
(-modeline-pad
(when lazycat-dark-padded-modeline
(if (integerp lazycat-dark-padded-modeline) lazycat-dark-padded-modeline 4)))
(modeline-fg fg)
(modeline-fg-alt base5)
(modeline-bg
(if -modeline-bright
(lazycat-darken blue 0.475)
`(,(lazycat-darken (car bg-alt) 0.15) ,@(cdr base0))))
(modeline-bg-l
(if -modeline-bright
(lazycat-darken blue 0.45)
`(,(lazycat-darken (car bg-alt) 0.1) ,@(cdr base0))))
(modeline-bg-inactive `(,(lazycat-darken (car bg-alt) 0.1) ,@(cdr bg-alt)))
(modeline-bg-inactive-l `(,(car bg-alt) ,@(cdr base1))))
;; --- extra faces ------------------------
;; ...
;; --- major-mode faces -------------------
;; css-mode / scss-mode
(css-proprietary-property :foreground orange)
(css-property :foreground green)
(css-selector :foreground blue)
;; markdown-mode
(markdown-markup-face :foreground base5)
(markdown-header-face :inherit 'bold :foreground red)
((markdown-code-face &override) :background (lazycat-lighten base3 0.05))
;; org-mode
(org-hide :foreground hidden)
(solaire-org-hide-face :foreground hidden)
;; secondary region.
(secondary-selection :background grey)
)
;; --- extra variables ---------------------
())
好了,我们可以看到,这里定义了一系列变量,用于表示不同颜色的值。例如,bg 表示背景颜色,它被设置为一个字符串列表 ("#242525" nil nil),这个列表表示在不同的色彩深度下的值。 (-modeline-bright lazycat-dark-brighter-modeline)
这行代码设置了一个局部变量 -modeline-bright
,其值取自 lazycat-dark-brighter-modeline
,用于控制模式栏是否明亮。 (modeline-fg fg)
,(modeline-fg-alt base5)
...定义了模式栏(modeline)的前景和背景颜色。我们也可以定义额外的面部设置。
lazycat-color
;;;###autoload
(defun lazycat-color (name &optional type)
"Retrieve a specific color named NAME (a symbol) from the current theme."
(let ((colors (if (listp name)
name
(cdr-safe (assq name lazycat-themes--colors)))))
(and colors
(cond ((listp colors)
(let ((i (or (plist-get '(256 1 16 2 8 3) type) 0)))
(if (> i (1- (length colors)))
(car (last colors))
(nth i colors))))
(t colors)))))
LazyCat 定义了一个名为lazycat-color
的函数,用于从当前主题中检索指定名称的颜色。其中,;;;###autoload
是一种特殊的注释格式,用于告诉 Emacs 在加载这个文件时自动定义函数。这个注释通常用于将函数定义与它们的使用位置分开,以减少启动时间。(defun lazycat-color (name &optional type) ...)
这个宏定义了一个函数 lazycat-color,它接受两个参数:name 是要检索的颜色名称(一个符号),type 是一个可选参数,用于指定颜色的类型。(let ((colors ... )))
中 let 表达式定义了一个局部变量 colors,它用于存储从当前主题中检索到的颜色数据。如果 name 是一个列表,那么 colors 就是这个列表本身;否则,通过 assq 函数查找 lazycat-themes--colors 中与 name 关联的颜色数据。(and colors ... )
如果 colors 不为 nil,则执行 cond 表达式内部的条件。