面向产品经理的Emacs教程:9. 稍微多了解一点Emacs Lisp

· 2593字 · 6分钟

1 课程回顾 🔗

通过上节课,我们给Org mode换了一层皮,完成了美化,现在在Org文件里写Emacs配置将会是一个很享受的事。当然Org mode的作用可不只是写配置文件。你在这个阶段,已经可以使用Org mode来写各种文档了,只要Markdown能完成的工作,Org mode都可以完成而且完成的更好。

今天,我们对Emacs配置中的最基本的元素Emacs Lisp语言做更深入一点点了解。在这之前,我们对于Emacs的配置,从来都是囫囵吞枣,Copy-paste为主,只知其然而不知其所以然,通过本节课,我们可以学习如何进一步地了解配置文件里的配置项了。

2 Emacs Lisp语言概述 🔗

GNU Emacs Lisp 语言是一种Lisp语言的变种,而Lisp语言诞生于1950年代,GNU Emacs Lisp语言受到诞生于1960年代的Maclisp语言,以及成为1980年代标准的Common Lisp的启发,它比Common Lisp简单,整个Emacs本身,也都是由C和Emacs Lisp语言写成的。

几乎90%以上的插件,都是由Emacs Lisp语言写成的,Emacs的配置文件本身(以 .el 结尾的文件)也都是Emacs Lisp语言写的,所以懂一点儿Emacs Lisp语言,能让我们更好的理解配置文件,也能让我们更自由地将Emacs改造成自己喜欢的样子!

3 Emacs Lisp的入门 🔗

Emacs Lisp语言有很多教程,作为初学者,我建议大家读 叶文彬写的Emacs Lisp简明教程,写得非常深入浅出,读完这个教程,就可以尝试读 GNU Emacs Lisp 官方文档 了。

在这个阶段,作为产品经理,我们只需要先搞清楚下面几个概念就可以了。

3.1 *scratch*缓冲区 🔗

我们的Emacs在启动后,有一个默认存在的名字叫 *scratch* 的缓冲区,这个缓冲区可以用来写 Emacs Lisp。

在我们当前的配置文件作用下,我们启动Emacs后默认的界面就是这个缓冲区,我们可以在这个缓冲区写上一段Emacs Lisp代码,如:

(message "hello world")

把光标放到最后一个括号后,然后按 C-x C-e 键,Emacs会执行这一条语句,也就是在迷你缓冲区打印出“hello world”字符串。

3.2 函数 🔗

3.2.1 函数的定义 🔗

在Emacs Lisp里,定义函数是下面这样的形式:

(defun 函数名 (参数列表)
  "对该函数的描述"
  函数体)

例如:

(defun hello-world (name)
  "Say hello to user whose name is NAME."
  (message "Hello, %s" name))

3.2.2 函数的调用 🔗

当我们定义好了一个函数,如何调用它呢,很简单,直接用括号括起来就可以了:

(hello-world "Randolph")

运行的结果如下:

“Hello, Randolph”

3.2.3 查看函数文档 🔗

我们使用 C-h f 快捷键,然后输入函数名,就可以查看函数的文档了。

Emacs是一个完全的self-documenting的编辑器,所谓的self-documenting,我的理解就是编辑器的所有命令、函数、变量等都可以通过在Emacs内查看相关的文档来了解具体的定义、用法、实现、调用关系等。而 C-h 这个快捷键,也将成为我们最常用到的快捷键之一。

3.3 变量 🔗

变量的有如下几种定义方法:

  1. 通过 setq 定义全局变量:

    (setq 变量名 变量值)                    ; 定义的同时赋值
    
  2. 通过 defvar 定义全局变量:

    (defvar 变量名 变量值
      "变量文档说明")                       ; 如果变量名在声明前有值,声明不会改变成声明时的那个值
    
  3. 通过 let 定义局部变量:

    (let ((局部变量名1 变量值1)
          (局部变量名2 变量值2))
      其他执行体)                           ; 执行完之后,局部变量就都失效了
    
    (let ((a 5)
          (b 4))
      (message (number-to-string (+ a b)))) ; ⇒ "9"
    
    (message a)                             ; ⇒ (void-variable a)
    

3.4 条件判断 🔗

Emacs Lisp有以下几个基本的条件判断:

  1. if

    (if 条件
        条件满足时的执行体
      条件不满足时的执行体)
    
    (if (eq system-type 'darwin)
        (message "这是Mac系统")
      (message "这不是Mac系统"))            ; ⇒ "这是Mac系统"
    
  2. when

    (when 条件
      条件满足时的执行体)
    
    (when (eq system-type 'darwin)
      (message "这是Mac系统"))              ; ⇒ "这是Mac系统"
    
  3. cond

    (cond (条件1 条件1执行体)
          (条件2 条件2执行体)
          ...
          (t 都不满足时执行体))
    
    (cond ((eq system-type 'darwin) (message "这是Mac系统"))
          ((eq system-type 'gnu/linux) (message "这是Linux系统"))
          (t (message "这是其他系统")))     ; ⇒ "这是Mac系统"
    

3.5 循环 🔗

(while 条件
  执行体)

(defun factorial (n)
  (let ((res 1))
    (while (> n 1)
      (setq res (* res n))
      (setq n (- n 1)))
    res))

(factorial 5)                           ; ⇒ 120

3.6 顺序执行 🔗

progn 这个函数可以把多个表达式放在一起顺序执行:

(progn
  (message "step 1")
  (message "step 2")
  (message "step 3"))

4 Emacs 里查看帮助文档 🔗

我们对Emacs Lisp有了以上最基础的认识后,就可以灵活地通过 C-h 相关的按键,来查看变量、函数的定义和实现了。 C-h 系列命令也是我们能够对自己的配置文件进行进一步理解和改造的基础,非常重要。

表 1: C-h 系列命令
按键 命令 解释
C-h k describe-key 查看按键绑定的是什么命令
C-h f describe-function 查看函数定义
C-h v describe-variable 查看变量定义
C-h m describe-mode 查看模式定义,以及该模式下的快捷键

4.1 helpful帮助增强 🔗

helpful 插件提供了帮助增强,让帮助文档更加易读。

(use-package helpful
  :ensure t
  :commands (helpful-callable helpful-variable helpful-command helpful-key helpful-mode)
  :bind (([remap describe-command] . helpful-command)
         ("C-h f" . helpful-callable)
         ("C-h v" . helpful-variable)
         ("C-h s" . helpful-symbol)
         ("C-h S" . describe-syntax)
         ("C-h m" . describe-mode)
         ("C-h F" . describe-face)
         ([remap describe-key] . helpful-key))
  )

下图是Emacs自带的帮助文档:

这是安装了 helpful 插件后的帮助界面,可以看到信息更加归整,还可以展示具体的定义源代码,非常方便。

4.2 which-key快捷键增强 🔗

我们在第一课时,在讲到快捷键时提到了 which-key 插件,通过这款插件,我们可以不用刻意去记住各种快捷键,它会自动帮我们展示出相应的快捷键候选,以及每个候选对应的命令。

(use-package which-key
  :ensure t
  :hook (after-init . which-key-mode)
  :config
  (which-key-add-key-based-replacements
    "C-c !" "flycheck"
    "C-c @" "hideshow"
    "C-c i" "ispell"
    "C-c t" "hl-todo"
    "C-x a" "abbrev"
    "C-x n" "narrow"
    "C-x t" "tab")
  :custom
  (which-key-idle-delay 0.7)
  (which-key-add-column-padding 1))

安装完该插件后,当我们按下快捷键的某一些前缀按键例如 C-h 时,whichkey 插件将自动的帮我们显示出所有以 C-h 作为前缀的快捷键候选,以及这些快捷键对应的命令,我们只需要根据它的提示,按下后续按键即可,如 C-h f 代表的是 helpful-callable 命令:

5 结语 🔗

经过今天的课程学习,我们对Emacs Lisp有了一定的了解,我们也学会了通过 C-h 系列命令来查看具体的变量和函数的含义,这样,当我们遇到配置文件里不明白的设置的时候,就可以通过 C-h s 来查看具体含义是什么,我们可以从文档里了解怎么修改配置,从而尝试选择自己最喜欢的配置,Emacs是自由的!

经过今天的课程,配置文件的快照见:emacs-config-l9.org

你也可以在 这里 查看最新的配置文件。