└── README.org /README.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Emacs use-package 中文文档 2 | #+STARTUP: showall 3 | #+OPTIONS: ^:nil 4 | 5 | - 目标:use-package 是一个很好的 Emacs 配置隔离工具,符合规则书写配置可实现延迟加载,提升启动速度。对于我这样的 lisp 小白来说,是福音。 6 | - 翻译源:https://github.com/jwiegley/use-package 7 | - 更新频率:use-package 自身更新很快,但是核心使用方式基本没变化,所以频率不重要。 *欢迎PR* 8 | - 更新记录:: 9 | + <2023-02-05 Sun 19:12> 同步至当前最新 [[https://github.com/jwiegley/use-package/tree/77945e002f11440eae72d8730d3de218163d551e][77945e0]] 10 | + <2020-04-23 Thu 16:37> 首次更新 11 | 12 | ------------------------------------------------------------------------------------------------------------------------ 13 | 14 | =use-package= 宏允许你以性能导向、优雅、整洁的方式隔离你的 =.emacs= 配置文件。我创建它是因为我在 Emacs 中使用了 80 多个 package,管理起来非常困难。 15 | 有了 use-package 之后,我的总加载时间约为 2 秒,而没有功能上的损失。 16 | 17 | *注意:* =use-package= *不是* 一个包管理工具。尽管 =use-package= 的确有与包管理工具交互的(很有用)功能,但它的主要目的是配置和加载程序包。 18 | 19 | * Table Of Content :TOC: 20 | - [[#安装-use-package][安装 use-package]] 21 | - [[#入门][入门]] 22 | - [[#快捷键绑定key-binding][快捷键绑定(Key-binding)]] 23 | - [[#绑定快捷键到-keymaps][绑定快捷键到 keymaps]] 24 | - [[#绑定到局部-keymaps][绑定到局部 keymaps]] 25 | - [[#modes-和-interpreters][Modes 和 interpreters]] 26 | - [[#magic-handlers][Magic Handlers]] 27 | - [[#hooks][Hooks]] 28 | - [[#包定制][包定制]] 29 | - [[#自定义变量][自定义变量]] 30 | - [[#自定义-faces][自定义 faces]] 31 | - [[#延迟加载注意事项][延迟加载注意事项]] 32 | - [[#有关包加载的信息][有关包加载的信息]] 33 | - [[#条件加载][条件加载]] 34 | - [[#preface-之前的条件加载][:preface 之前的条件加载]] 35 | - [[#按顺序in-sequence加载包][按顺序(in sequence)加载包]] 36 | - [[#如果依赖项缺失则阻止加载][如果依赖项缺失,则阻止加载]] 37 | - [[#字节编译byte-compiling你的-emacs][字节编译(byte-compiling)你的 .emacs]] 38 | - [[#阻止包在编译时compile-time加载][阻止包在编译时(compile-time)加载]] 39 | - [[#扩展-load-path][扩展 load-path]] 40 | - [[#在包扩展期间捕获错误][在包扩展期间捕获错误]] 41 | - [[#diminishing-和-delighting-次要模式minor-modes][diminishing 和 delighting 次要模式(minor modes)]] 42 | - [[#包的安装][包的安装]] 43 | - [[#和其它包管理器一起使用][和其它包管理器一起使用]] 44 | - [[#收集统计][收集统计]] 45 | - [[#关键字扩展][关键字扩展]] 46 | - [[#use-package-ensure-system-package][=(use-package-ensure-system-package)=]] 47 | - [[#use-package-chords][=(use-package-chords)=]] 48 | - [[#一些计时timling结果][一些计时(timling)结果]] 49 | - [[#升级到-2x][升级到 2.x]] 50 | - [[#init-语义现在是一致的][=:init= 语义现在是一致的]] 51 | - [[#idle-被移除了][=:idle= 被移除了]] 52 | - [[#defer-现在一个可选的数字参数][=:defer= 现在一个可选的数字参数]] 53 | - [[#添加-preface-发生在-disable-之外的所有事情之前][添加 :preface, 发生在 :disable 之外的所有事情之前]] 54 | - [[#添加-functions用于向字节编辑器声明函数][添加 :functions,用于向字节编辑器声明函数]] 55 | - [[#use-packageel-在运行时不再需要][use-package.el 在运行时不再需要]] 56 | 57 | * 安装 use-package 58 | 59 | 从 [[https://github.com/jwiegley/use-package][这个]] Github 仓库中 clone 或者从 [[https://elpa.gnu.org/][GNU ELPA]] 安装(推荐)。 60 | 61 | * 入门 62 | 63 | =use-package= 最简单的声明方式: 64 | 65 | #+begin_src emacs-lisp 66 | ;; This is only needed once, near the top of the file 67 | (eval-when-compile 68 | ;; Following line is not needed if use-package.el is in ~/.emacs.d 69 | (add-to-list 'load-path "") 70 | (require 'use-package)) 71 | 72 | (use-package foo) 73 | #+end_src 74 | 75 | 这会加载 =foo= 包,但是只有 =foo= 在你的系统中可用的时候。如果没有的话,会打印告警日志到 =*Message*= buffer。 76 | 77 | 使用 =:init= 关键字在包加载之前执行代码。它接受一个或者多个形式(forms),直到下一个关键字出现: 78 | 79 | #+begin_src elisp 80 | (use-package foo 81 | :init 82 | (setq foo-variable t)) 83 | #+end_src 84 | 85 | 类似, =:config= 可以在包加载之后执行代码。在延迟加载的场景下(请参阅下面的有关自动加载的更多信息),config 中的代码会延迟到自动加载发生之后: 86 | 87 | #+begin_src elisp 88 | (use-package foo 89 | :init 90 | (setq foo-variable t) 91 | :config 92 | (foo-mode 1)) 93 | #+end_src 94 | 95 | 如你所想,你可以将 =:init= 和 =:config= 组合使用: 96 | 97 | #+begin_src elisp 98 | (use-package color-moccur 99 | :commands (isearch-moccur isearch-all) 100 | :bind (("M-s O" . moccur) 101 | :map isearch-mode-map 102 | ("M-o" . isearch-moccur) 103 | ("M-O" . isearch-moccur-all)) 104 | :init 105 | (setq isearch-lazy-highlight t) 106 | :config 107 | (use-package moccur-edit)) 108 | #+end_src 109 | 110 | 这种场景下,我想从 =color-moccur.el= 中自动加载 =isearch-moccur= 和 =isearch-all= 命令,然后进行全局层面和 =isearch-mode-map= 内部绑定(查看下一小结)。 111 | 当包实际被加载之后(通过使用这些命令中的一个), =moccur-edit= 也会被加载,以允许编辑 =moccur= buffer。 112 | 113 | 如果您自己加载非交互式函数,请使用 =:autoload= : 114 | 115 | #+begin_src elisp 116 | (use-package org-crypt 117 | :autoload org-crypt-use-before-save-magic) 118 | #+end_src 119 | 120 | * 快捷键绑定(Key-binding) 121 | 122 | 在加载模块时通常要做的是将模块的中的主要命令绑定快捷键: 123 | 124 | #+begin_src elisp 125 | (use-package ace-jump-mode 126 | :bind ("C-." . ace-jump-mode)) 127 | #+end_src 128 | 129 | 它做了两件事情:首先,它为 =ace-jump-mode= 命令创建了自动加载(autoload),延迟到你实际使用此命令时才会 =ace-jump-mode= ; 130 | 第二,它为此命令绑定了快捷键 =C-.= 。 131 | 加载之后,你可以使用 =M-x describe-personal-keybindings= 来查在 =.emacs= 配置中所有的此类快捷键。 132 | 133 | 另外一种更直接的方法也可以实现相同效果: 134 | 135 | #+begin_src elisp 136 | (use-package ace-jump-mode 137 | :commands ace-jump-mode 138 | :init 139 | (bind-key "C-." 'ace-jump-mode)) 140 | #+end_src 141 | 142 | 当你使用 =:commands= 关键字时,它会给这些命令创建自动加载,并延迟模块加载直到使用它们为止。因为 =:init= 总是会运行 -- 即便 =ace-jump-mode= 可能不在你的系统上 143 | -- 切记 =:init= 中的代码要保证都可以运行成功。 144 | 145 | =:bind= 关键字可以接受一个或多个 cons 列表。 146 | 147 | #+begin_src elisp 148 | (use-package hi-lock 149 | :bind (("M-o l" . highlight-lines-matching-regexp) 150 | ("M-o r" . highlight-regexp) 151 | ("M-o w" . highlight-phrase))) 152 | #+end_src 153 | 154 | 或者,命令名称可以被替换为 cons =(desc . command)= ,其中 =desc= 是一个描述绑定的 =command= 的字符串: 155 | 156 | #+begin_src elisp 157 | (use-package avy 158 | :bind ("C-:" ("Jump to char" . avy-goto-char) 159 | "M-g f" ("Jump to line" . avy-goto-line))) 160 | #+end_src 161 | 162 | =:commands= 通常也支持 symbols 和 symbols 列表。 163 | 164 | *注意:* 命令内部的字符串,特殊的键位比如 =tab= 或者 =F1=-=Fn= 必须要写在尖括号里面,比如 ="C-"= 。独立的特殊键(和某些组合)可以写在方括号里中, 165 | 比如: 用 =[tab]= 代替 == 。这种键位绑定语法类似 "kbd" 语法,从 https://www.gnu.org/software/emacs/manual/html_node/emacs/Init-Rebinding.html 获取更多信息。 166 | 167 | 举例: 168 | 169 | #+begin_src elisp 170 | (use-package helm 171 | :bind (("M-x" . helm-M-x) 172 | ("M-" . helm-find-files) 173 | ([f10] . helm-buffers-list) 174 | ([S-f10] . helm-recentf))) 175 | #+end_src 176 | 177 | 此外,使用 =:bind= 和 =bind-key= [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Remapping-Commands.html][重新映射命令]] 可以按照预期工作,因为当绑定是向量(vector)时,它直接传递给 =define-key= 。 178 | 所以下面的例子将重新绑定 =M-q= (原先是 =fill-paragraph= )到 =unfill-toggle= 。 179 | 180 | #+begin_src elisp 181 | (use-package unfill 182 | :bind ([remap fill-paragraph] . unfill-toggle)) 183 | #+end_src 184 | 185 | ** 绑定快捷键到 keymaps 186 | 187 | 通常, =:bind= 的预期是 command 是将从给定的包中自动加载的函数。但有些命令实际上是 keymap,这么做会不生效,因为 keymap 不是一个函数, 188 | 无法使用 Emacs 的 =autoload= 机制。 189 | 190 | 为了应对这种场景, =use-package= 提供了一个 =:bind= 变体叫 =:bind-keymap= 。唯一的不同是由 =:bind-keymap= 对应的“命令”必须是包中定义的 keymaps, 191 | 而不是 commmand 函数。它的处理方式是生成自定义代码来加载包含键盘映射的包,然后在你执行按键的时候首次加载,进而将键重新解释为前缀快捷键: 192 | 193 | 比如: 194 | 195 | #+begin_src elisp 196 | (use-package projectile 197 | :bind-keymap 198 | ("C-c p" . projectile-command-map)) 199 | #+end_src 200 | 201 | ** 绑定到局部 keymaps 202 | 203 | 与绑定到 keymap 不同,它绑定局部 keymap 中的一个快捷键,而且只有在包已经被加载之后才存在。 =use-package= 通过 =:map= 修饰符来支持它,将局部 keymap 绑定到: 204 | 205 | #+begin_src elisp 206 | (use-package helm 207 | :bind (:map helm-command-map 208 | ("C-c h" . helm-execute-persistent-action))) 209 | #+end_src 210 | 211 | 该语句会等到 =helm= 被加载的时候,绑定 =C-c h= 到 Helm 的局部 =helm-mode-map= 中的 =helm-execute-persistent-action= 。 212 | 213 | =:map= 可以指定多次,发生在 =:map= 之前的任何绑定都会认为是全局 keymap: 214 | 215 | #+begin_src elisp 216 | (use-package term 217 | :bind (("C-c t" . term) 218 | :map term-mode-map 219 | ("M-p" . term-send-up) 220 | ("M-n" . term-send-down) 221 | :map term-raw-map 222 | ("M-o" . other-window) 223 | ("M-p" . term-send-up) 224 | ("M-n" . term-send-down))) 225 | #+end_src 226 | 227 | *** 绑定到 repeat-maps 228 | 229 | 在局部 keymap 中有一个绑定特例是当该 keymap 使用为 =repeat-mode= 。这些 keymaps 通常专门为此定义。使用 =:repeat-map= 关键字, 230 | 并为其定义的 map 设定一个名称,然后(默认情况下)讲每个绑定到此 map 的命令设置 =repeat-map= 属性。 231 | 232 | 下面的代码创建了一个称为 =git-gutter+-repeat-map= 的 keymap,在其中进行四个绑定,然后为每个命令( =git-gutter+-next-hunk= 233 | =git-gutter+-previous-hunk= =git-gutter+-stage-hunks =git-gutter+-revert-hunk== )都设置了 =repeat-map= 属性。 234 | 235 | #+begin_src elisp 236 | (use-package git-gutter+ 237 | :bind 238 | (:repeat-map git-gutter+-repeat-map 239 | ("n" . git-gutter+-next-hunk) 240 | ("p" . git-gutter+-previous-hunk) 241 | ("s" . git-gutter+-stage-hunks) 242 | ("r" . git-gutter+-revert-hunk))) 243 | #+end_src 244 | 245 | 在 =:repeat-map= 范围内使用 =:exit= 来停止 =repeat-map= 属性设置。在 repeat map 中使用该命令,repeat map 将不再可用。 246 | 这对于在一系列的重复命令尾部使用很有用: 247 | 248 | #+begin_src elisp 249 | (use-package git-gutter+ 250 | :bind 251 | (:repeat-map my/git-gutter+-repeat-map 252 | ("n" . git-gutter+-next-hunk) 253 | ("p" . git-gutter+-previous-hunk) 254 | ("s" . git-gutter+-stage-hunks) 255 | ("r" . git-gutter+-revert-hunk) 256 | :exit 257 | ("c" . magit-commit-create) 258 | ("C" . magit-commit) 259 | ("b" . magit-blame))) 260 | #+end_src 261 | 262 | 指定 =:continue= 命令会 /强制/ 设置 =repeat-map= 属性(就跟没指定 =:exit= 一样),所以下面两个片段是等价的: 263 | 264 | #+begin_src elisp 265 | (use-package git-gutter+ 266 | :bind 267 | (:repeat-map my/git-gutter+-repeat-map 268 | ("n" . git-gutter+-next-hunk) 269 | ("p" . git-gutter+-previous-hunk) 270 | ("s" . git-gutter+-stage-hunks) 271 | ("r" . git-gutter+-revert-hunk) 272 | :exit 273 | ("c" . magit-commit-create) 274 | ("C" . magit-commit) 275 | ("b" . magit-blame))) 276 | #+end_src 277 | 278 | #+begin_src elisp 279 | (use-package git-gutter+ 280 | :bind 281 | (:repeat-map my/git-gutter+-repeat-map 282 | :exit 283 | ("c" . magit-commit-create) 284 | ("C" . magit-commit) 285 | ("b" . magit-blame) 286 | :continue 287 | ("n" . git-gutter+-next-hunk) 288 | ("p" . git-gutter+-previous-hunk) 289 | ("s" . git-gutter+-stage-hunks) 290 | ("r" . git-gutter+-revert-hunk))) 291 | #+end_src 292 | 293 | * Modes 和 interpreters 294 | 295 | 类似 =:bind= ,你可以使用 =:mode= 和 =:interpreter= 建立与 =auto-mode-alist= 和 =interpreter-mode-alist= 中的变量内部延迟绑定。 296 | 每个关键字的说明符都是可以 cons,cons 列表,字符串或者正则表达式。 297 | 298 | #+begin_src elisp 299 | (use-package ruby-mode 300 | :mode "\\.rb\\'" 301 | :interpreter "ruby") 302 | 303 | ;; The package is "python" but the mode is "python-mode": 304 | (use-package python 305 | :mode ("\\.py\\'" . python-mode) 306 | :interpreter ("python" . python-mode)) 307 | #+end_src 308 | 309 | 如果你没有设置 =:commands=, =:bind=, =:bind*=, =:bind-keymap=, =:bind-keymap*=, =:mode=, =:interpreter= 或者 =:hook= (这些都实现了 =:defer= ; 310 | 查看 =use-package= 的 docstring 获取每个文档的简要说明),你仍旧可以通过 =:defer= 关键字实现延迟加载: 311 | 312 | #+begin_src elisp 313 | (use-package ace-jump-mode 314 | :defer t 315 | :init 316 | (autoload 'ace-jump-mode "ace-jump-mode" nil t) 317 | (bind-key "C-." 'ace-jump-mode)) 318 | #+end_src 319 | 320 | 与下面这样效果完全相同: 321 | 322 | #+begin_src elisp 323 | (use-package ace-jump-mode 324 | :bind ("C-." . ace-jump-mode)) 325 | #+end_src 326 | 327 | * Magic Handlers 328 | 329 | 与 =:mode= 和 =:interpreter= 类似,你也可以使用 =:magic= 和 =:magic-fallback= 来实现如果文件的开头和给定的正则表达式匹配,则触发某些功能运行。 330 | 两者之间的区别在于 =:magic-fallback= 比 =:mode= 的优先级低。比如: 331 | 332 | #+begin_src elisp 333 | (use-package pdf-tools 334 | :load-path "site-lisp/pdf-tools/lisp" 335 | :magic ("%PDF" . pdf-view-mode) 336 | :config 337 | (pdf-tools-install :no-query)) 338 | #+end_src 339 | 340 | 这会为 =pdf-view-mode= 注册一个自动加载命令,延迟加载 =pdf-tools= 。如果 buffer 开头与字符串 ="%PDF"= 匹配,运行 =pdf-view-mode= 。 341 | 342 | * Hooks 343 | 344 | =:hook= 关键字允许将函数添加到包 hooks 上。因此,下面三种方式都是等价的: 345 | 346 | #+begin_src elisp 347 | (use-package ace-jump-mode 348 | :hook prog-mode) 349 | 350 | (use-package ace-jump-mode 351 | :hook (prog-mode . ace-jump-mode)) 352 | 353 | (use-package ace-jump-mode 354 | :commands ace-jump-mode 355 | :init 356 | (add-hook 'prog-mode-hook #'ace-jump-mode)) 357 | #+end_src 358 | 359 | 同样,应用到多个 hook 时,以下的内容也是等价的: 360 | 361 | #+begin_src elisp 362 | (use-package ace-jump-mode 363 | :hook (prog-mode text-mode)) 364 | 365 | (use-package ace-jump-mode 366 | :hook ((prog-mode text-mode) . ace-jump-mode)) 367 | 368 | (use-package ace-jump-mode 369 | :hook ((prog-mode . ace-jump-mode) 370 | (text-mode . ace-jump-mode))) 371 | 372 | (use-package ace-jump-mode 373 | :commands ace-jump-mode 374 | :init 375 | (add-hook 'prog-mode-hook #'ace-jump-mode) 376 | (add-hook 'text-mode-hook #'ace-jump-mode)) 377 | #+end_src 378 | 379 | 当使用 =:hook= 时要忽略 "-hook" 后缀,默认情况会自动追加。比如下面的代码不会生效,因为它实际上会扩展成 =prog-mode-hook-hook= 并不存在: 380 | 381 | #+begin_src elisp 382 | ;; DOES NOT WORK 383 | (use-package ace-jump-mode 384 | :hook (prog-mode-hook . ace-jump-mode)) 385 | #+end_src 386 | 387 | 如果你不喜欢这种行为的话,设置 =use-package-hook-name-suffix= 成 nil。默认情况下,它的值是 "-hook"。 388 | 389 | 使用 =:hook= 与 =:bind=, =:mode=, =:interpreter= 等,会导致被 hook 的函数隐式读取为 =:commands= (意味着它们将为该模块建立交互式 =autoload= 定义, 390 | 如果尚未定义函数的话),因为 =:defer t= 也被 =:hook= 隐含了。 391 | 392 | * 包定制 393 | 394 | ** 自定义变量 395 | 396 | =:custom= 关键字允许自定义包的自定义变量。 397 | 398 | #+begin_src elisp 399 | (use-package comint 400 | :custom 401 | (comint-buffer-maximum-size 20000 "Increase comint buffer size.") 402 | (comint-prompt-read-only t "Make the prompt read only.")) 403 | #+end_src 404 | 405 | 上面的文档字符串不强制。 406 | 407 | *注意:* 这些仅适用于想要在 use-packages 声明的地方保留自定义能力的人。功能上,相比与在 =:config= 块中使用 =setq= 的唯一优势在于: 408 | 自定义可能在分配值时执行代码。 409 | 410 | *注意:* 这些自定义值 *不会* 保存在 Emacs 的 =custom-file= 中,因此你要么使用 =:custom= 选项 *或者* 使用 =M-x customize-option= 411 | (它会将自定义值保存在 Emacs 的 =custom-file= 中),不要同时使用两者。 412 | 413 | ** 自定义 faces 414 | 415 | =custom-face= 关键字允许自定义包中的 faces。 416 | 417 | #+begin_src elisp 418 | (use-package eruby-mode 419 | :custom-face 420 | (eruby-standard-face ((t (:slant italic))))) 421 | 422 | (use-package example 423 | :custom-face 424 | (example-1-face ((t (:foreground "LightPink")))) 425 | (example-2-face ((t (:foreground "LightGreen"))) face-defspec-spec)) 426 | 427 | (use-package zenburn-theme 428 | :preface 429 | (setq my/zenburn-colors-alist 430 | '((fg . "#DCDCCC") (bg . "#1C1C1C") (cyan . "#93E0E3"))) 431 | :custom-face 432 | (region ((t (:background ,(alist-get my/zenburn-colors-alist 'cyan))))) 433 | :config 434 | (load-theme 'zenburn t)) 435 | #+end_src 436 | 437 | * 延迟加载注意事项 438 | 439 | =:commands= 等关键字提供了在特定事件发生时导致包加载的“触发器”。如果 =use-package= 无法确定是否有任何触发器的声明,它会在 Emacs 立即加载, 440 | 除非你指定了 =:defer t= 。你可以使用 =:demand t= 覆盖触发器的存在,让它强制立即加载。比如 =:hook= 表示当指定的 hook 运行时才触发。 441 | 442 | 绝大多数情况下,你都不需要手动设置 =:defer t= ,因为使用 =:bind= =:mode= 或者 =:interpreter= 都已经隐含了。通常,只有知道一些包会在某些情况下需要时间加载, 443 | 你才需要指定 =:defer= ,因此,即使 =use-package= 没有为你创建任何自动加载,你也可以设置延迟加载。 444 | 445 | 你可以使用 =:demand= 关键字覆盖包的延迟,即便你使用了 =:bind= , =:demand= 也会强制立即加载,而不是等快捷键绑定建立时自动加载。 446 | 447 | * 有关包加载的信息 448 | 449 | 加载包的时候,如果将 =use-package-verbose= 设置为 =t=, 或者加载包的时间超过了 0.1 秒,那么你会在 =*Message*= buffer 中看到一个信息(来表明加载活动)。 450 | 对于配置或者 =:config= 块执行时间超过 0.1 秒,也会出现相同的情况。通常情况,你应该保持 =:init= 尽可能的简单,而把更多的配置放到 =:config= 块中。 451 | 这样,延迟加载可以让你的 Emacs 启动更快。 452 | 453 | 另外,如果在包的初始化或者配置过程中出现了错误,不会阻止你的 Emacs 加载。相反,错误会被包捕获,然后报告给特殊的 =*Warning*= 弹出 buffer, 454 | 以便你使用功能正常的 Emacs 中调试。 455 | 456 | * 条件加载 457 | 458 | 你可以使用 =:if= 关键字来条件判断包模块的加载和初始化。 459 | 460 | 比如说,我只希望 =edit-server= 在我主要的,GUI Emacs,而不希望其它时候启动: 461 | 462 | #+begin_src elisp 463 | (use-package edit-server 464 | :if window-system 465 | :init 466 | (add-hook 'after-init-hook 'server-start t) 467 | (add-hook 'after-init-hook 'edit-server-start t)) 468 | #+end_src 469 | 470 | 在另外一个例子中,我们可以有条件的加载一些东西: 471 | 472 | #+begin_src elisp 473 | (use-package exec-path-from-shell 474 | :if (memq window-system '(mac ns)) 475 | :ensure t 476 | :config 477 | (exec-path-from-shell-initialize)) 478 | #+end_src 479 | 480 | =:disabled= 关键字可以关闭遇到困难的模块,或者停止加载你当前未使用的内容: 481 | 482 | #+begin_src elisp 483 | (use-package ess-site 484 | :disabled 485 | :commands R) 486 | #+end_src 487 | 488 | 当字节编译(byte-compiling)你的 =.emacs= 文件的时候,在输出中完全省略了禁用声明(的包),以加快启动时间。 489 | 490 | *注意:* =:when= 是 =:if= 的别名,而且 =:unless foo= 等价于 =:if (not foo)= 。 491 | 492 | ** :preface 之前的条件加载 493 | 494 | 如果你需要对 use-package form 进行条件判断,确保它出现在 =:preface= 执行之前,简单的方式是用 =:when= 把 use-package 包裹起来。 495 | 比如,下面的例子也会在 Mac 系统中停止 =:ensure= : 496 | 497 | #+begin_src elisp 498 | (when (memq window-system '(mac ns)) 499 | (use-package exec-path-from-shell 500 | :ensure t 501 | :config 502 | (exec-path-from-shell-initi 503 | #+end_src 504 | 505 | ** 按顺序(in sequence)加载包 506 | 507 | 有时需要在一个包加载完之后才会加载另外一个包,因为某些变量或函数在那之前不在作用域内。这种情况可以使用 =:after= 关键字来实现, 508 | 该关键字允许在满足确定条件后再加载。比如: 509 | 510 | #+begin_src elisp 511 | (use-package hydra 512 | :load-path "site-lisp/hydra") 513 | 514 | (use-package ivy 515 | :load-path "site-lisp/swiper") 516 | 517 | (use-package ivy-hydra 518 | :after (ivy hydra)) 519 | #+end_src 520 | 521 | 上面的例子中,所有的包都是按找出现的顺序加载的,因此 =:after= 的使用不是强制需要的。但是,使用 =:after= 之后,上面的代码就于与顺序无关了, 522 | 与原始的 init 文件中的顺序没有了隐式的依赖。 523 | 524 | 默认情况下, =:after (foo bar)= 与 =:after (:all foo bar)= 相同,意味着给定的加载包必须要等到 =foo= 和 =bar= 全部加载之后。 525 | 下面是一些其他的可能性: 526 | 527 | #+begin_src elisp 528 | :after (foo bar) 529 | :after (:all foo bar) 530 | :after (:any foo bar) 531 | :after (:all (:any foo bar) (:any baz quux)) 532 | :after (:any (:all foo bar) (:all baz quux)) 533 | #+end_src 534 | 535 | 当你使用嵌套的选择器时,比如 =(:any (:all foo bar) (:all baz quux))= ,意味着同时加载 =foo= 和 =bar= 或者 =baz= 和 =quux= 。 536 | 537 | *注意:* 如果你设置了 =use-package-always-defer= 为 =t= ,并且使用了 =:after= 关键字,那么你需要指定包的加载方式:比如,使用 =:bind= 。 538 | 如果你没有使用注册自动加载的机制之一,比如 =:bind= 或者 =:hook= ,而且您的包管理器没有提供自动加载功能,并且没有添加 =:demand t= 到这些声明中, 539 | 那么这些包永远不会被加载。 540 | 541 | ** 如果依赖项缺失,则阻止加载 542 | 543 | 虽然 =after= 关键字可以延迟加载直到依赖项加载,但遇到 =use-package= 声明时有依赖项不可用,那么简单的 =:requires= 关键字根本不会加载包。 544 | 这种情况下,“可用”意味着如果如果 =(featurep 'foo)= 的计算结果为非 nil 值,则 foo 可用。比如: 545 | 546 | #+begin_src elisp 547 | (use-package abbrev 548 | :requires foo) 549 | #+end_src 550 | 551 | 等价于: 552 | 553 | #+begin_src elisp 554 | (use-package abbrev 555 | :if (featurep 'foo)) 556 | #+end_src 557 | 558 | 为了方便,你可以指定依赖列表: 559 | 560 | #+begin_src elisp 561 | (use-package abbrev 562 | :requires (foo bar baz)) 563 | #+end_src 564 | 565 | 对于更复杂的逻辑,比如说 =:after= 支持的,只需要使用 =:if= 合适的 Lisp 表达式即可。 566 | 567 | * 字节编译(byte-compiling)你的 .emacs 568 | 569 | =use-package= 的另一个特性是 =.emacs= 被字节编译时总加载它可以加载的每一个文件。这会有助于消除有关未知变量和函数的虚假警告。 570 | 571 | 但是,有时候这样还不够。有些时候,出于字节编译的目的,使用 =:defines= 和 =:functions= 关键字仅用来引入虚拟变量和函数声明: 572 | 573 | #+begin_src elisp 574 | (use-package texinfo 575 | :defines texinfo-section-list 576 | :commands texinfo-mode 577 | :init 578 | (add-to-list 'auto-mode-alist '("\\.texi$" . texinfo-mode))) 579 | #+end_src 580 | 581 | 如果你想要函数缺失的警告静音(silence),你可使用 =:functions= : 582 | 583 | #+begin_src elisp 584 | (use-package ruby-mode 585 | :mode "\\.rb\\'" 586 | :interpreter "ruby" 587 | :functions inf-ruby-keys 588 | :config 589 | (defun my-ruby-mode-hook () 590 | (require 'inf-ruby) 591 | (inf-ruby-keys)) 592 | 593 | (add-hook 'ruby-mode-hook 'my-ruby-mode-hook)) 594 | #+end_src 595 | 596 | ** 阻止包在编译时(compile-time)加载 597 | 598 | 通常, =use-package= 在编译配置之前,在编译时加载每个包,确保必要的标识符都在满足字节编译器的范围内。 599 | 有时这样会引起问题,由于包可能有特殊的加载需求,你要使用 =use-pacakge= 的所有操作就是将配置添加到 =eval-after-load= hook。 600 | 这种情况下,使用 =:no-require= 关键字: 601 | 602 | #+begin_src elisp 603 | (use-package foo 604 | :no-require t 605 | :config 606 | (message "This is evaluated when `foo' is loaded")) 607 | #+end_src 608 | 609 | * 扩展 load-path 610 | 611 | 如果一个包为了加载需要一个目录添加到 =load-path= ,使用 =:load-path= 。它的参数是一个符号,一个函数,一个字符串或者一个字符串列表。 612 | 如果是相对路径,会基于 =user-package-directory= 扩展: 613 | 614 | #+begin_src elisp 615 | (use-package ess-site 616 | :load-path "site-lisp/ess/lisp/" 617 | :commands R) 618 | #+end_src 619 | 620 | *注意:* 当使用符号(symbol)或者函数提供动态的路径列表时,你必须要将此定义告知字节编译器,以便在字节编译时可用。 621 | 这是通过使用特殊形式的 =eval-and-compile= (与 =eval-when-compile= 相反)来完成的。进一步,这个值是在编译器期间确定下来的,为了避免在每次启动时再次查找相同的信息: 622 | 623 | #+begin_src elisp 624 | (eval-and-compile 625 | (defun ess-site-load-path () 626 | (shell-command "find ~ -path ess/lisp"))) 627 | 628 | (use-package ess-site 629 | :load-path (lambda () (list (ess-site-load-path))) 630 | :commands R) 631 | #+end_src 632 | 633 | * 在包扩展期间捕获错误 634 | 635 | 默认情况下,如果 =use-package-expand-minimally= 是 nil(默认值),use-package 将尝试捕捉并报告在你的 init 文件中扩展使用包声明期间发生的错误。 636 | 设置 =use-package-expand-minimally= 为 =t= 完全禁用此检查。 637 | 638 | 这种行为可以被局部使用 =:catch= 来覆盖。设置为 =t= 或者 =nil= ,在加载时启动或者禁用捕获错误。它也可以是带有两个参数的函数: 639 | 遇到错误时正在处理的关键字,和错误对象(由 =condition-case= 生成)。比如: 640 | 641 | #+begin_src elisp 642 | (use-package example 643 | ;; Note that errors are never trapped in the preface, since doing so would 644 | ;; hide definitions from the byte-compiler. 645 | :preface (message "I'm here at byte-compile and load time.") 646 | :init (message "I'm always here at startup") 647 | :config 648 | (message "I'm always here after the package is loaded") 649 | (error "oops") 650 | ;; Don't try to (require 'example), this is just an example! 651 | :no-require t 652 | :catch (lambda (keyword err) 653 | (message (error-message-string err)))) 654 | #+end_src 655 | 656 | 执行结果: 657 | 658 | #+begin_src 659 | I’m here at byte-compile and load time. 660 | I’m always here at startup 661 | Configuring package example... 662 | I’m always here after the package is loaded 663 | oops 664 | #+end_src 665 | 666 | * diminishing 和 delighting 次要模式(minor modes) 667 | 668 | =use-package= 还提供了内置的 diminish 和 delight 功能支持 -- 如果你已经安装了它们的话。它们的目标是在你的 mode-line 中删除或者更改次要模式的字符串。 669 | 670 | [[https://github.com/myrjola/diminish.el][diminish]] 用 =:diminish= 关键字调用,可以使用次要模式标识符传递,符号和它替换字符串的 cons,或者只是一个替换字符串,这种情况下,次要模式的标识符被猜测为 671 | 包名字加上 "-mode" : 672 | 673 | #+begin_src elisp 674 | (use-package abbrev 675 | :diminish abbrev-mode 676 | :config 677 | (if (file-exists-p abbrev-file-name) 678 | (quietly-read-abbrev-file))) 679 | #+end_src 680 | 681 | [[https://elpa.gnu.org/packages/delight.html][delight]] 用 =:delight= 关键字调用,传递了次要模式标识符,替换字符串或者引号包裹的 [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Mode-Line-Data.html][mode-line 数据]](这种情况下,次要模式符号被认为是包名称末尾加上 "-mode"), 682 | 这两个,或者两者的几个列表。如果没有提供任何参数,默认模式名字将完全被隐藏。 683 | 684 | #+begin_src elisp 685 | ;; Don't show anything for rainbow-mode. 686 | (use-package rainbow-mode 687 | :delight) 688 | 689 | ;; Don't show anything for auto-revert-mode, which doesn't match 690 | ;; its package name. 691 | (use-package autorevert 692 | :delight auto-revert-mode) 693 | 694 | ;; Remove the mode name for projectile-mode, but show the project name. 695 | (use-package projectile 696 | :delight '(:eval (concat " " (projectile-project-name)))) 697 | 698 | ;; Completely hide visual-line-mode and change auto-fill-mode to " AF". 699 | (use-package emacs 700 | :delight 701 | (auto-fill-function " AF") 702 | (visual-line-mode)) 703 | #+end_src 704 | 705 | * 包的安装 706 | 707 | 你可以使用 =use-package= 通过 =package.el= 从 ELPA 加载包。如果你在多台计算机之间共享 =.emacs= ,这会非常有用; 708 | 一旦在你的 =.emacs= 中声明,相关的包会自动下载。如果系统中不存在, =:ensure= 关键字会让包自动安装。 709 | 710 | #+begin_src elisp 711 | (use-package magit 712 | :ensure t) 713 | #+end_src 714 | 715 | 如果你需要安装与 =use-package= 命名的包不同的包,可以这样指定: 716 | 717 | #+begin_src elisp 718 | (use-package tex 719 | :ensure auctex) 720 | #+end_src 721 | 722 | 如果你希望此行为对所有的包都有效,打开 =use-package-always-ensure= : 723 | 724 | #+begin_src elisp 725 | (require 'use-package-ensure) 726 | (setq use-package-always-ensure t) 727 | #+end_src 728 | 729 | *注意:* =:ensure= 在包不存在时会自动安装包,但它并不能保持最新。如果你希望你的包自动升级,一个选项是使用 [[https://github.com/rranelli/auto-package-update.el][auto-package-update]] ,类似: 730 | 731 | #+begin_src elisp 732 | (use-package auto-package-update 733 | :config 734 | (setq auto-package-update-delete-old-versions t) 735 | (setq auto-package-update-hide-results t) 736 | (auto-package-update-maybe)) 737 | #+end_src 738 | 739 | 最后,在 Emacs 24.4 或者更高的版本上运行时,use-package 可以将包固定(pin)到特定的 archive,允许你混用和匹配来自不同 archive 的包。 740 | 大部分场景都是从 =melpa-stable= 和 =gnu= archive 中选择软件包,但是要使用来自 =melpa= 中的包,当你需要使用比 =stable= archives 741 | 可用版本更新的版本时,这也是一种使用场景。 742 | 743 | 默认情况下 =package.el= 由于版本控制的原因 =(> evil-20141208.623 evil-1.0.9)= 相比 =melpa-stable= 更喜欢 =mepla= ,因此即使你想只有一个包来自 =melpa= , 744 | 你需要把所有非 =melpa= 的包都要从这个 archive 下载。如果这个让你感到烦恼,你可以未 =use-package-always-pin= 设置一个默认值。 745 | 746 | 如果要手动保持包更新,而忽略上游更新,你可以将它固定(pin)为 =manual= ,只要没有该名称的存储库,就可以使用。 747 | 748 | 如果你固定(pin)的 archive 没有出现在 =package-archives= 的配置列表中, =use-package= 会抛出一个错误(除了上面提到的 =:manual= ): 749 | 750 | #+begin_src 751 | Archive 'foo' requested for package 'bar' is not available. 752 | #+end_src 753 | 754 | 举例: 755 | 756 | #+begin_src elisp 757 | (use-package company 758 | :ensure t 759 | :pin melpa-stable) 760 | 761 | (use-package evil 762 | :ensure t) 763 | ;; no :pin needed, as package.el will choose the version in melpa 764 | 765 | (use-package adaptive-wrap 766 | :ensure t 767 | ;; as this package is available only in the gnu archive, this is 768 | ;; technically not needed, but it helps to highlight where it 769 | ;; comes from 770 | :pin gnu) 771 | 772 | (use-package org 773 | :ensure t 774 | ;; ignore org-mode from upstream and use a manually installed version 775 | :pin manual) 776 | #+end_src 777 | 778 | *注意:* =:pin= 参数对于 emacs 版本小于 24.4 无效。 779 | 780 | ** 和其它包管理器一起使用 781 | 782 | 通过覆盖 =use-package-ensure-function= 和/或者 =use-package-pre-ensure-function= ,其它的包管理器可以重写 =:ensure= 使用它们而不是 =package.el= 。 783 | 目前,唯一执行此操作的包管理器是 [[https://github.com/raxod502/straight.el][straight.el]]。 784 | 785 | * 收集统计 786 | 787 | 如果你想查看已经加载包数量,它们到达了什么初始化阶段,它们花了多少时间(大约),在加载 =use-package= 之后打开 =use-package-compute-statistics= (在 788 | 任何使用 =use-package= forms 之前),然后运行命令 =M-x use-package-report= 查看结果。显示的 buffer 是一个列表,你可以在对应的列上使用 =S= 进行排序。 789 | 790 | * 关键字扩展 791 | 792 | 从 2.0 版本开始, =use-package= 基于可扩展的框架,使得包的作者添加新的关键字或者修改现有关键字变的很轻松。 793 | 794 | 现在,某些关键字扩展包含在 =use-package= 的发行版中,可以选择性安装。 795 | 796 | ** =(use-package-ensure-system-package)= 797 | 798 | =:ensure-system-package= 允许你确保系统的二进制文件和你的包声明一起存在。 799 | 800 | 首先,你希望保证 =exec-path= 能够识别你已经安装的二进制包的名称,[[https://github.com/purcell/exec-path-from-shell][exec-path-from-shell]] 是一个好的解决办法。 801 | 802 | 在你 =use-package= 加载之后启用扩展: 803 | 804 | #+begin_src elisp 805 | (use-package use-package-ensure-system-package 806 | :ensure t) 807 | #+end_src 808 | 809 | 下面是使用范例: 810 | 811 | #+begin_src elisp 812 | (use-package rg 813 | :ensure-system-package rg) 814 | #+end_src 815 | 816 | 它会期望全局的二进制 =rg= 存在。如果不存在的话,它会使用你的系统包管理器(使用 system-packages)尝试异步安装同名的二进制。 817 | 比如,macOS 用户可能会调用: =brew install rg= 。 818 | 819 | 如果包名字和二进制名字不同,你可以用 =(binary . package-name)= 的格式,即: 820 | 821 | #+begin_src elisp 822 | (use-package rg 823 | :ensure-system-package 824 | (rg . ripgrep)) 825 | #+end_src 826 | 827 | 在前面的 macOS 例子中,如果 =rg= 没找到的话,会调用: =brew install ripgrep= 。 828 | 829 | 如果你想自定义安装命令怎么办? 830 | 831 | #+begin_src elisp 832 | (use-package tern 833 | :ensure-system-package (tern . "npm i -g tern")) 834 | #+end_src 835 | 836 | =:ensure-system-package= 也可以通过 =(async-shell-command)= 调用安装。 837 | 838 | 你也可以安装传入一个列表: 839 | 840 | #+begin_src elisp 841 | (use-package ruby-mode 842 | :ensure-system-package 843 | ((rubocop . "gem install rubocop") 844 | (ruby-lint . "gem install ruby-lint") 845 | (ripper-tags . "gem install ripper-tags") 846 | (pry . "gem install pry"))) 847 | #+end_src 848 | 849 | 最后,如果包依赖项不提供全局可执行文件,你可以通过类似以下的字符串来检查文件路径的存在确保包存在: 850 | 851 | #+begin_src elisp 852 | (use-package dash-at-point 853 | :if (eq system-type 'darwin) 854 | :ensure-system-package 855 | ("/Applications/Dash.app" . "brew cask install dash")) 856 | #+end_src 857 | 858 | =:ensure-system-package= 会使用 =system-package-install= 来安装系统包,如果指定了自定义命令,将由 =async-shell-command= 执行。 859 | 860 | 配置变量 =system-packages-package-manager= 和 =system-packages-use-sudo= 会很有用,但是不适用于自定义命令。自定义命令如果需要的话,需要在命令本身上加上 =sudo= 。 861 | 862 | ** =(use-package-chords)= 863 | 864 | =:chords= 关键字允许你为 =use-package= 定义 =[[https://www.emacswiki.org/emacs/key-chord.el][key-chord]]= 绑定,与 =:bind= 关键字类似。 865 | 866 | 启动扩展: 867 | 868 | #+begin_src elisp 869 | (use-package use-package-chords 870 | :ensure t 871 | :config (key-chord-mode 1)) 872 | #+end_src 873 | 874 | 然后你可以使用与 =:bind= 类似的定义方法来绑定: 875 | 876 | #+begin_src elisp 877 | (use-package ace-jump-mode 878 | :chords (("jj" . ace-jump-char-mode) 879 | ("jk" . ace-jump-word-mode) 880 | ("jl" . ace-jump-line-mode))) 881 | #+end_src 882 | 883 | *** 如何创建扩展 884 | 885 | **** 第一步:添加一个关键字 886 | 887 | 第一步在 =use-package-keywords= 合适的位置添加你的关键字。这个列表决定了扩展代码中的执行顺序。你永远不要修改这个顺序, 888 | 但它为你提供了一个框架,你可以在其中决定何时触发你的关键字。 889 | 890 | **** 第二步:创建一个 normalizer 891 | 892 | 通过定义以关键字命名的函数来为关键字定义 normalizer,例如: 893 | 894 | #+begin_src elisp 895 | (defun use-package-normalize/:pin (name-symbol keyword args) 896 | (use-package-only-one (symbol-name keyword) args 897 | (lambda (label arg) 898 | (cond 899 | ((stringp arg) arg) 900 | ((symbolp arg) (symbol-name arg)) 901 | (t 902 | (use-package-error 903 | ":pin wants an archive name (a string)")))))) 904 | #+end_src 905 | 906 | normalizer 的工作是获取一参数列表(可能是 nil),并将其转换为应该出现在 =use-package= 使用的最终属性列表中的单个参数(仍旧是个列表)。 907 | 908 | **** 第三步:创建一个处理器 909 | 910 | 一旦你有一个 normalizer,你必须要为关键字创建一个处理器: 911 | 912 | #+begin_src elisp 913 | (defun use-package-handler/:pin (name-symbol keyword archive-name rest state) 914 | (let ((body (use-package-process-keywords name-symbol rest state))) 915 | ;; This happens at macro expansion time, not when the expanded code is 916 | ;; compiled or evaluated. 917 | (if (null archive-name) 918 | body 919 | (use-package-pin-package name-symbol archive-name) 920 | (use-package-concat 921 | body 922 | `((push '(,name-symbol . ,archive-name) 923 | package-pinned-packages)))))) 924 | #+end_src 925 | 926 | 处理器有两种方式来影响关键字:首先,它可以递归处理剩余关键字之前修改 =state= plist,影响关注状态的关键字(一个例子是 =:deferred= 927 | 关键字,不要与 =:defer= 混淆)。然后,一旦处理了剩余的关键字并返回了它们的结果 form,处理程序就可以操纵、扩展或直接忽略这些 forms。 928 | 929 | 每个处理器的任务是返回 /一个 forms 列表/ 来表示要插入的代码。它不需要一个 =progn= 列表,因为这是在其他地方自动处理的。 930 | 因此,在代码主体之前或者之后使用 =use-package-concat= 添加新功能的用法很常见,作为使用包扩展的结果,只发出必要的最少代码。 931 | 932 | **** 第四步:测试 933 | 934 | 在关键字插入到 =use-package-keywords= 之后,以及 normalizer 和处理器定义之后,你现在可以通过查看关键字的使用来测试它。 935 | 为此,使用 =M-x pp-macroexpand-last-sexp= 并将光标设置在 =(use-package ...)= 表达式之后。 936 | 937 | * 一些计时(timling)结果 938 | 939 | 在我的 iMac Retina 上,Emacs 24.4 的 "Mac port" 变种,大约配置了 218 个包(几乎所有的都是懒加载),加载了 0.57 秒。 940 | 但是,除了第一次使用 Emacs 时(由于自动加载),我没有损失任何功能。由于我对许多软件包使用了空闲加载,因此通常可以整体上减少延迟感知。 941 | 942 | 在 Linux 上,相同的配置用了 0.32 秒。 943 | 944 | 如果不用图形方式使用 Emacs,可以测试到绝对最小时间,通过以下命令完成: 945 | 946 | #+begin_src sh 947 | time emacs -l init.elc -batch --eval '(message "Hello, world!")' 948 | #+end_src 949 | 950 | 在 Mac 上平均 0.36 秒,在 Linux 上 0.26 秒。 951 | 952 | * 升级到 2.x 953 | 954 | ** =:init= 语义现在是一致的 955 | 956 | =:init= 的含义发生了变化:现在它 /总是/ 的包加载之前出现,无论 =:config= 推迟与否。这意味着可能需要将 =:init= 中的配置放到了 =:config= 中(在非延迟的情况下)。 957 | 对于延迟的情况,行为和之前相同。 958 | 959 | 也因为 =:init= 和 =:config= 现在意味着 "之前" 和 "之后",所以 =:pre-= 和 =:post-= 关键字消失了,它们也不再被需要了。 960 | 961 | 最后,即使存在包配置失败的情况,也尽力让你的 Emacs 启动。因此,在此更改之后,要检查你的 =*Message= buffer。最有可能的是, 962 | 在几个例子中使用 =:init= ,但应该跟更多地方使用 =:config= 。 963 | 964 | ** =:idle= 被移除了 965 | 966 | 我现在要删除此功能,是因为它可能出现让人生厌的不一致,考虑以下定义: 967 | 968 | #+begin_src elisp 969 | (use-package vkill 970 | :commands vkill 971 | :idle (some-important-configuration-here) 972 | :bind ("C-x L" . vkill-and-helm-occur) 973 | :init 974 | (defun vkill-and-helm-occur () 975 | (interactive) 976 | (vkill) 977 | (call-interactively #'helm-occur)) 978 | 979 | :config 980 | (setq vkill-show-all-processes t)) 981 | #+end_src 982 | 983 | 如果我加载我的 Eamcs 然后等到空闲计数器触发,然后这是事件的顺序: 984 | 985 | #+begin_src 986 | :init :idle :config 987 | #+end_src 988 | 989 | 但是,如果我加载 Emacs 并立即输入 =C-x L= 而不等待空闲计数器触发,它的事件顺序时这样的: 990 | 991 | #+begin_src 992 | :init :config :idle 993 | #+end_src 994 | 995 | 用户可能在闲置状态下使用 =featurep= 来测试这种情况,但这是我想避免的。 996 | 997 | ** =:defer= 现在一个可选的数字参数 998 | 999 | =:defer [N]= 会导致包加载 -- 如果还没有的话 -- 在 =N= 秒之后执行。 1000 | 1001 | #+begin_src elisp 1002 | (use-package back-button 1003 | :commands (back-button-mode) 1004 | :defer 2 1005 | :init 1006 | (setq back-button-show-toolbar-buttons nil) 1007 | :config 1008 | (back-button-mode 1)) 1009 | #+end_src 1010 | 1011 | ** 添加 :preface, 发生在 :disable 之外的所有事情之前 1012 | 1013 | =:preface= 可用于建立函数和变量定义 1)让字节编译器开心(它不会抱怨定义未知的函数,因为他们在保护块中),2)允许您定义在 =:if= 测试中使用的代码。 1014 | 1015 | *注意:* =:preface= 中指定的任何内容都会在加载时和字节编译时执行,为了确保 Lisp 求职器和字节编译器能看到定义,所以你应该避免 1016 | 在你的前沿中产生任何副作用,并将它限制在符号生命和定义上。 1017 | 1018 | ** 添加 :functions,用于向字节编辑器声明函数 1019 | 1020 | =:defines= 是为变量的, =:functions= 是为函数的。 1021 | 1022 | ** use-package.el 在运行时不再需要 1023 | 1024 | 也就是说你可以将以下内容放在 Emacs 的顶部,进一步减少加载时间: 1025 | 1026 | #+begin_src elisp 1027 | (eval-when-compile 1028 | (require 'use-package)) 1029 | (require 'diminish) ;; if you use :diminish 1030 | (require 'bind-key) ;; if you use any :bind variant 1031 | #+end_src 1032 | --------------------------------------------------------------------------------