Emacs configuration with support for Chinese input

使用 Emacs 三年多了,当时受了 一年成为 Emacs 高手 的蛊惑,且贪图便利,使用了 Purcell 的配置。正如 Purcell 所说,此配置相当 reasonable,用来入门确实让我受益匪浅,少走了很多弯路。三年使用下来,我需要添加的插件只有 org-refob-smiles ,配置更新也仅须 git pull 外加 M-x package-list-packages U x 即可,非常方便。

然而过去三年来,我主要是使用 Emacs 中的 org-mode 来记 Science 与 Tech 方面笔记,基于 Latex 的长文章 / 代码我更喜欢用 Atoms + Vim-mode 来写,基于 Markdown 的博客则用 MWeb 来写,中文笔记喜欢使用 Bears,最后却还是回到了苹果自带的免费 Notes。其他零零碎碎的场景则使用更为零碎的 app,餐馆用 DayGram + Workflowy,记账用随手记。非常不统一。

也就是说,我并不是一个“running my life with Emacs”的重症者,Purcell 配置的 Emacs 中也有大量我用不到,或者需要使用却不甚了解的插件。所以失业在家这个契机恰好也是让我重新审视 Emacs,看看我能否最大化地使用 Emacs。

对我而言,使用 Atoms 来写代码与 Latex,是由于我对使用 Emacs 调试还不甚熟练。所以这几天我花了一点时间重新阅读了 Emacs 手册。

使用 MWeb 来写博客与使用 Notes 来记笔记的原因则比较可笑,因为我基本上都是使用英文记录 Science & Tech 方面的笔记,我并没有配置 Emacs 的中文环境。所以需要使用中文来写东西时,我自然就得换用其他工具。这段时间准备面试,将 org-mode 的使用范围扩大到记录面试适宜,由于一亩三分地是一个主要资料来源,不免需要粘贴许多中文帖子进来。这时再将 Emacs 视为一个纯英文环境未免有些麻烦。而将 Emacs 中文化了,自然也不需要 MWeb 和 Notes 或者 Bear 什么事了。

而记录餐馆和消费的需求比较麻烦,本文暂且搁置,今后有空再来研究。

Emacs 中文输入法

Emacs 窗口左下角显示的是 character set 与 editing state,其中 character set 默认值为 U,即没有 input-mode 且使用 unicode 的 coding system,通过 C-\ ,可以非常方便地设置输入法并进行切换。

Emacs 有一些自带的中文输入法,比如拼音(chinese-py)、自然码(chinese-ziranma),还有一些四声(chinese-sisheng)、英汉(chinese-ecdict)等输入法。我将简体编码的一些输入法列于下表,详见 leim-lisp.el (可通过 M-x find-library 迅速查看)。

input mode 输入法
chinese-ccdospy 缩写拼音
chinese-ctlau 刘锡祥式粤音
chinese-punct 标点符号
chinese-py 拼音
chinese-qj 全角
chinese-sw 首尾
chinese-tonepy 带调拼音
chinese-ziranma 自然码

由于我习惯用微软双拼,故另外安装 pyim,配置如下:

(require 'pyim)
(require 'pyim-basedict)
(pyim-basedict-enable)
(setq default-input-method "pyim")
(setq pyim-default-scheme 'microsoft-shuangpin)
(setq pyim-page-tooltip 'popup)
(setq pyim-page-length 5)

Emacs 中文字体

Emacs 中默认的中文字体是 Arial Unicode MS,看着总觉得哪哪不顺眼,所以下一步就是更换个顺眼的中文字体。

此处我先参考了 zhuoqiang 的做法,将中英文字体设为等宽的:

;; Setting English Font
(set-face-attribute 'default nil :font "DejaVu Sans Mono 14")

;; Chinese Font
(dolist (charset '(kana han symbol cjk-misc bopomofo))
  (set-fontset-font (frame-parameter nil 'font)
                    charset (font-spec :family "PingFang SC"
                                       :size 16)))

并将他写的更为通用的设置字体的函数:

(defun qiang-set-font (english-fonts
                       english-font-size
                       chinese-fonts
                       &optional chinese-font-size))

略加修改为:

(defun qiang-set-font (english-fonts
                       english-font-size
                       chinese-fonts
                       &optional chinese-font-scale)
    ...
    :size (* chinese-font-scale english-font-size))

然而当我用 C-x C-+ 动态调整字体大小时,发现只有英文字体会缩放,中文则仍然蠢蠢地处于初始大小。查手册得可通过 face-font-rescale-alist 来设定特定字体缩放值。简单来说就是这样:

(setq face-font-rescale-alist '(("PingFang SC" . chinese-font-scale))

但是这样只能对苹方字体起效,如果一个一个对 font-list 中的字体修改 scale-factor 看起来又不简洁。于是我想到两种较为简便的设定方式:

;; First method
(setq face-font-rescale-alist
      (loop for x in zh-font-list
            collect (cons x chinese-font-scale)))
;; Second method
(setq face-font-rescale-alist
      (pairlis zh-font-list
               (make-list (list-length zh-font-list)
                           chinese-font-scale)))

效果拔群。

详细配置如下:

(defun qiang-font-existsp (font)
  (if (null (x-list-fonts font))
      nil
    t))

(defvar zh-font-list '("PingFang SC" "Hiragino Sans GB" "Microsoft Yahei" "Source Han Sans Normal" "STHeiti" "黑体" "新宋体" "宋体"))
(defvar en-font-list '("DejaVu Sans Mono" "Monaco" "Consolas" "Monospace" "Courier New"))

(defun qiang-make-font-string (font-name font-size)
  (if (and (stringp font-size)
           (equal ":" (string (elt font-size 0))))
      (format "%s%s" font-name font-size)
    (format "%s %s" font-name font-size)))

(defun qiang-set-font (english-fonts
                       english-font-size
                       chinese-fonts
                       &optional chinese-font-scale)

  (setq chinese-font-scale (or chinese-font-scale 1.2))

  (setq face-font-rescale-alist
        (loop for x in zh-font-list
              collect (cons x chinese-font-scale)))

  "english-font-size could be set to \":pixelsize=18\" or a integer.
If set/leave chinese-font-scale to nil, it will follow english-font-size"

  (require 'cl)                         ; for find if
  (let ((en-font (qiang-make-font-string
                  (find-if #'qiang-font-existsp english-fonts)
                  english-font-size))
        (zh-font (font-spec :family (find-if #'qiang-font-existsp chinese-fonts))))

    ;; Set the default English font
    (message "Set English Font to %s" en-font)
    (set-face-attribute 'default nil :font en-font)

    ;; Set Chinese font
    ;; Do not use 'unicode charset, it will cause the English font setting invalid
    (message "Set Chinese Font to %s" zh-font)
    (dolist (charset '(kana han symbol cjk-misc bopomofo))
      (set-fontset-font (frame-parameter nil 'font)
                        charset zh-font))))

(qiang-set-font en-font-list 14 zh-font-list)
Avatar
Runhan Yu
Data Engineer

My interests include big data, cloud computing, software development and biochemistry.

Related