Manateelazycat 的 Emacs 启动文件 site-start.el 研究: Difference between revisions

From 清冽之泉
Jump to navigation Jump to search
 
(6 intermediate revisions by the same user not shown)
Line 54: Line 54:
### let* 把 dolist 包含在内,是划定 let 定义的变量的使用范围
### let* 把 dolist 包含在内,是划定 let 定义的变量的使用范围
### dir 就是把 search-dir 加上后斜杠后的结果
### dir 就是把 search-dir 加上后斜杠后的结果
## dolist 对列表执行操作 {{#tip-text: let* | 第一行:顺序绑定变量<br>第二行:后面的变量可以引用前面的变量<br>第三行:适合有依赖关系的局部变量}}
## dolist 对列表执行操作
### subdir 是待处理的 VAR
### subdir 是待处理的 VAR
### cl-remove-if 函数的结果是 LIST
### cl-remove-if 函数的结果是 LIST
Line 60: Line 60:
#### lambda 函数,判断不适宜目录
#### lambda 函数,判断不适宜目录
#### cl-remove-if 把 dir 里符合上边两个判断的文件夹移除
#### cl-remove-if 把 dir 里符合上边两个判断的文件夹移除
###
### 执行的操作是,在前提条件下,把 subdir-path 加入 load-path 列表末尾
### 执行的操作是,在前提条件下,把 subdir-path 加入 load-path 列表末尾
#### 拼接 dir 和 subdir,达到找到下一级有效目录的效果
#### 拼接 dir 和 subdir,达到找到下一级有效目录的效果
#### cl-some 用 file-regular-p 过滤存有特定后缀文件的文件夹
#### cl-some 用 file-regular-p 过滤存有特定后缀文件的文件夹
# 运行 add-subdirs-to-load-path,"/usr/share/emacs/lazycat" 就是 search-dir
# 运行 add-subdirs-to-load-path,"/usr/share/emacs/lazycat" 就是 search-dir
{{#tip-text: let* | 第一行:顺序绑定变量<br>第二行:后面的变量可以引用前面的变量<br>第三行:适合有依赖关系的局部变量}}


== 先验知识 ==
== 先验知识 ==
Line 103: Line 98:


所有原生函数,都可以通过 C-h f 查询核心结构、用法用途。
所有原生函数,都可以通过 C-h f 查询核心结构、用法用途。
最重要的事:其实 Emacs 本身自带 Eldoc 模式,在 Emacs 里读 elisp 代码,它本身就会显示以下函数的用法。
<syntaxhighlight lang="elisp">
<syntaxhighlight lang="elisp">
# let* 的核心结构
# let* 的核心结构

Latest revision as of 19:16, 10 July 2026

Manateelazycat 的 Emacs 启动文件 site-start.el 写得很妙,值得研究。

源代码

(require 'cl-lib)

(tool-bar-mode -1)                      ;禁用工具栏
(menu-bar-mode -1)                      ;禁用菜单栏
(scroll-bar-mode -1)                    ;禁用滚动条

(defun add-subdirs-to-load-path (search-dir)
  (interactive)
  (let* ((dir (file-name-as-directory search-dir)))
    (dolist (subdir
             ;; 过滤出不必要的目录,提升Emacs启动速度
             (cl-remove-if
              #'(lambda (subdir)
                  (or
                   ;; 不是目录的文件都移除
                   (not (file-directory-p (concat dir subdir)))
                   ;; 父目录、 语言相关和版本控制目录都移除
                   (member subdir '("." ".." 
                                    "dist" "node_modules" "__pycache__" 
                                    "RCS" "CVS" "rcs" "cvs" ".git" ".github")))) 
              (directory-files dir)))
      (let ((subdir-path (concat dir (file-name-as-directory subdir))))
        ;; 目录下有 .el .so .dll 文件的路径才添加到 `load-path' 中,提升Emacs启动速度
        (when (cl-some #'(lambda (subdir-file)
                           (and (file-regular-p (concat subdir-path subdir-file))
                                ;; .so .dll 文件指非Elisp语言编写的Emacs动态库
                                (member (file-name-extension subdir-file) '("el" "so" "dll"))))
                       (directory-files subdir-path))

          ;; 注意:`add-to-list' 函数的第三个参数必须为 t ,表示加到列表末尾
          ;; 这样Emacs会从父目录到子目录的顺序搜索Elisp插件,顺序反过来会导致Emacs无法正常启动
          (add-to-list 'load-path subdir-path t))

        ;; 继续递归搜索子目录
        (add-subdirs-to-load-path subdir-path)))))

(add-subdirs-to-load-path "/usr/share/emacs/lazycat")

(require 'init)

详细解析

  1. (require 'cl-lib) 为要用到的 cl 做铺势
  2. -1 的三个选项很简单,不用谈
  3. 自定义了一个函数,叫 add-subdirs-to-load-path,它的变量是 search-dir
    1. interactive 意为可交互唤起,简单略
    2. let* 定义 dir
      1. 这里只定义了一个 dir,其实可以定义多个局部变量
      2. 其实可以再加很多局部变量,如 ( (dir (one)) (zhangsan (two)) (lisi (three)) ),所以用了三层括号
      3. let* 把 dolist 包含在内,是划定 let 定义的变量的使用范围
      4. dir 就是把 search-dir 加上后斜杠后的结果
    3. dolist 对列表执行操作
      1. subdir 是待处理的 VAR
      2. cl-remove-if 函数的结果是 LIST
        1. lambda 函数,判断非目录
        2. lambda 函数,判断不适宜目录
        3. cl-remove-if 把 dir 里符合上边两个判断的文件夹移除
      3. 执行的操作是,在前提条件下,把 subdir-path 加入 load-path 列表末尾
        1. 拼接 dir 和 subdir,达到找到下一级有效目录的效果
        2. cl-some 用 file-regular-p 过滤存有特定后缀文件的文件夹
  4. 运行 add-subdirs-to-load-path,"/usr/share/emacs/lazycat" 就是 search-dir

先验知识

在 Emacs 里,(require 'sth) 相当于告诉 Emacs,在 load-path 里找 sth.el 或 sth.elc 等文件。

所以 load-path 如此重要,相当于环境变量。

elisp 里的魔法黑话字符
简写 全写 人话
'x (quote x) 别执行,当数据
#'x (function x) 别执行,当函数
'("A" "B") (quote ("A" "B")) 这是列表,不是函数调用
#'(lambda (x) x) (function (lambda (x) x)) 这是匿名函数
`(a ,b c) (quasiquote (a (unquote b) c)) 大模板里局部求值

所有原生函数,都可以通过 C-h f 查询核心结构、用法用途。

最重要的事:其实 Emacs 本身自带 Eldoc 模式,在 Emacs 里读 elisp 代码,它本身就会显示以下函数的用法。

# let* 的核心结构
# 按顺序绑定变量后边的变量可以用前面的变量的值
(let* VARLIST BODY...)

# let 的核心结构
# 同时绑定变量后边的变量寻求前面的变量的值时会报错因为它们同时诞生
(let VARLIST BODY...)

# dolist 的核心结构
(dolist
(VAR LIST [RESULT])
BODY ...)

# cl-remove-if 的核心结构
# cl-remove-if 的作用Remove all items satisfying PREDICATE in SEQ.
(cl-remove-if PREDICATE SEQ [KEYWORD VALUE]...)

# cl-some 的核心结构
# cl-some 的作用Say whether PREDICATE is true for any element in the SEQ sequences.
(cl-some PREDICATE SEQ...)

# concat 的核心结构
(concat &rest SEQUENCES)