Emacs:我已经十年没有按过保存按键了(附带 auto-save.el 源码解析)

有些插件还是要传播一下的

很多VI党经常吐槽Emacs按键难按, 特别是保存一个文件还要别扭的按 Ctrl + x Ctrl + s, 其实这个世界上很多Emacs高手一般看到这些误解都是不吭声的, 本来我也是不想吭声的, 直到我去年上海参加 Gopher 2015 大会夜谈会, 看到一波Eclipse高手和编辑器高手介绍自己写了一个多么方便的保存插件, 怎么点一下就很方便可以保存文件, 我当时深深的忍住了, 但是我居然看到会上一个 Google 工程师, 说自己写了一个 VI 插件, 怎么怎么按什么快捷键就可以快速保存文件了。

对于我这种资深Google粉来说, 实在看不惯 Google 工程师这么low, 当时就在台上跟他们演示了什么叫做自动保存.....

Emacs 的插件哲学

因为Emacs发展30多年到今天, 其实已经不是一个简简单单编辑器了, Emacs是一个自给自足的操作系统, 就像我在《Emacs是什么》里面讲的一样, Emacs强调的是工具协调的生态。 Emacs的插件强调的不是按键要多么简洁, 它强调的时候研究开发者心理, 开发者在编程场景中遇到最大的痛点是什么? 它强调的是开发者不需要做任何按键就能自动在后台做好,最大化减少插件和开发者之间的交互过程, 让开发者把所有时间都放在实际代码和深度思考上。

所以很多Emacs插件的优秀标准就是“润物细无声”, 插件的存在感越少越好, 最好什么按键都不要按, 插件作者已经总结出开发者在编程中思考的必经之路, 一切都是顺其自然的完成的。

自动保存文件

说到编辑器保存这个功能, 我最开始学Emacs的时候按了一个月的 Ctrl + x Ctrl + s 就受不了了, 最让人受不了的时候有时候辛辛苦苦写的代码, 忘记按保存了, 这时候突然停电了, 除了 WTF 就没有任何然后了。那时候我就想为什么一定要手动按 Ctrl + x Ctrl + s按键来保存呢?能否自动保存所编辑的文件? 什么时候最合适呢?

其实每个程序员编写程序的时候, 写着写着思维不是那么流畅了, 需要停下来稍微思考一下, 其实这时候就是自动保存的最佳时刻: 利用程序员犹豫下一个字符应该敲什么的空隙时间就足够保存一个文件了。

所以那时候就写了一个 auto-save.el 来做这件事情, 每当Emacs发现你停止敲键盘超过1s钟以后, 它就会把所有没有保存的文件全部保存一遍。

所以自从那时候我基本十年没有在手动保存过任何文件了, 也从来没有丢失过任何一行写的代码。 配置很简单, 下载 auto-save.el, 然后在 ~/.emacs 里面加上下面的代码:

(require 'auto-save)            ;; 加载自动保存模块

(auto-save-enable)              ;; 开启自动保存功能
(setq auto-save-slient t)       ;; 自动保存的时候静悄悄的, 不要打扰我

懒人也可以从 init-auto-save.el 下载写好的配置文件, 然后只在 ~/.emacs 写上下面配置文件就可以了:

(require 'init-auto-save)

到这里, 以后用Emacs就不用傻傻的按保存键了, Emacs发现你手指头没有敲键盘的时候自动保存的,没事不会和你抢CPU的。 ;)

auto-save.el 源码解析

对Elisp感兴趣的同学可以继续往下看 auto-save.el 的源码解析:

;; defgroup 关键字的意思是定义一个工作组,执行 Alt + x customize-group 命令的时候可以进行图形化的模块配置
;; 第一个参数是模块的名字, 比如 auto-save
;; 第二个参数是模块默认开启的状态, 在 elisp 中, t 表示 true, nil 表示 false
;; 第三个参数是对模块的文本解释
;; 第四个参数表示对外提供 auto-save 这个 group
(defgroup auto-save nil
  "Auto save file when emacs idle."
  :group 'auto-save)

;; defcustom 关键字的意思是定义一个可以被用户自定义的变量, 当用户执行 Alt + x customize-variable 的时候就可以补全 auto-save-idle 这个变量, defcustom 和 defvar 的区别主要是 defcustom 用于提供一些参数让用户可以在 Emacs 中图形化定制变量内容, defvar 这只有变量名和 List 内容, 一般用于函数内部变量值存储用, 不对外抛出给用户定制
;; 第一个参数是变量的名字 autos-ave-idle 
;; 第二个参数是变量的值, 这里我们定义为 1s, 表示自动保存的延迟秒数
;; 第三个参数是变量的解释, 一般在 Alt + x describe-variable 的时候就会显示具体变量的文档描述
;; 第四个参数用于定义变量的类型, 这里定义为整形, 这样在 customize-group 的时候只有输入整型才是正确保存
;; 第五个参数表示这个变量属于 auto-save 这个组, 主要作用就是 customize-group 的时候能够在一个界面中设置同一组的所有变量 
(defcustom auto-save-idle 1
  "The idle seconds to auto save file."
  :type 'integer
  :group 'auto-save)

;; autos-save-slient 的作用就是一个boolean值得变量, 设置为 nil 的时候, 表示每次自动保存都会在 minibuffer 提示, 设置成 t 的时候就会 shutup, 让我安安静静写会代码, 别闹...
(defcustom auto-save-slient nil
  "Nothing to dirty minibuffer if this option is non-nil."
  :type 'boolean
  :group 'auto-save)

;; 这段代码的作用就是避免 Emacs 在保存文件的时候生成一大堆垃圾的 #foo# 文件, 这种文件最讨厌了, 不但什么用都没有, 反而污染代码目录, 删除都删的我手酸
;; 想当年为了找到关闭这个脑残功能的变量, 我把 emacs 几百个带有 save 的变量全部打出来, 一个一个变量的试才找到你啊 (可惜当年我英文不好, 不知道怎么描述我想要的效果)
(setq auto-save-default nil)

;; 前方高能核心代码, 请集中注意力
(defun auto-save-buffers ()
  ;; 所有你在 Alt + x 以后可以调用的函数都要手动加上 (interactive) , 否则这段代码只能在 Elisp 解释器中执行, 但是不能直接被用户从 Alt + x 调用, 就想 interactive 这个单词的意思一样
  (interactive)
 ;; 创建 autosave-buffer-list 这个变量, 用于保存所有需要遍历的 buffer 列表
  (let ((autosave-buffer-list))
    ;; save-excursion 这个关键字的意思是, 所有在 save-excursion 里面的代码不管怎么折腾都不会对 save-excursion 之前的Emacs状态进行任何改变, 你可以理解为这个关键字的意思就是用于保护现场用的 ;)
    (save-excursion
      ;; dolist 的作用就和很多语言的 foreach 一个意思, 把 buffer-list 这个函数返回的所有 buffer 在循环内赋值给 buf 这个变量, 并在 dolist 的作用域中执行对 buf 影响的代码
      (dolist (buf (buffer-list))
        ;; 设置当前代码的 buffer 为 buf 变量值, 如果没有前面 save-excursion, 你会发现emacs会一直在快速的切换所有 buffer 的过程
        (set-buffer buf)
        ;; 如果当前 buffer 有一个相关联文件 (buffer-file-name), 同时当前 buffer 已经被用户修改了 (buffer-modified-p) 的情况下就执行自动保存
        (if (and (buffer-file-name) (buffer-modified-p))
            (progn
              ;; 把当前 buffer 的名字压进 autosave-buffer-list 列表, 用于后面的保存提示
              (push (buffer-name) autosave-buffer-list)
              (if auto-save-slient
                  ;; 如果 auto-save-slient 这个变量为 true, 就不显示任何保存信息, 因为 Emacs 的保存函数 (basic-save-buffer) 本身机会 blabla 的告诉你文件已经保存了, 所以我们用 with-temp-message 配合空字符串来禁止 with-temp-message 里面的代码在 minibuffer 显示任何内容
                  (with-temp-message ""
                    (basic-save-buffer))
                (basic-save-buffer))
              )))
      ;; unless 的意思是除非 auto-save-slient 为 false 就执行
      (unless auto-save-slient
        ;; cond 就是 elisp 版的 switch, 用于条件语句对比执行
        (cond
         ;; 如果 autosave-buffer-list 列表里面没有任何一个文件需要保存, 我们就不要去烦用户了, 默默打酱油路过就好了

         ;; 如果有一个文件需要保存, 我们就说 Saved ... 
         ((= (length autosave-buffer-list) 1)
          (message "# Saved %s" (car autosave-buffer-list)))
         ;; 如果有多个文件需要保存, 就说 Saved ... files
         ((> (length autosave-buffer-list) 1)
          (message "# Saved %d files: %s"
                   (length autosave-buffer-list)
                   (mapconcat 'identity autosave-buffer-list ", ")))))
      )))

(defun auto-save-enable ()
  (interactive)
  ;; run-with-idle-timer 函数的意思就是在 auto-save-idle 定义的描述以后自动执行 auto-save-buffers 函数
  ;; #' 的意思就是在 runtime 执行的时候再展开 auto-save-buffers 函数
  (run-with-idle-timer auto-save-idle t #'auto-save-buffers)
  )

(provide 'auto-save)

最后

Enjoy, have fun! ;)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,873评论 4 370
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,483评论 1 306
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,525评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,595评论 0 218
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,018评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,958评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,118评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,873评论 0 208
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,643评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,813评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,293评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,615评论 3 262
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,306评论 3 242
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,170评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,968评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,107评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,894评论 2 278

推荐阅读更多精彩内容