Emacs:我已經(jīng)十年沒(méi)有按過(guò)保存按鍵了(附帶 auto-save.el 源碼解析)

有些插件還是要傳播一下的

很多VI黨經(jīng)常吐槽Emacs按鍵難按她按, 特別是保存一個(gè)文件還要?jiǎng)e扭的按 Ctrl + x Ctrl + s, 其實(shí)這個(gè)世界上很多Emacs高手一般看到這些誤解都是不吭聲的火本, 本來(lái)我也是不想吭聲的, 直到我去年上海參加 Gopher 2015 大會(huì)夜談會(huì)斧吐, 看到一波Eclipse高手和編輯器高手介紹自己寫(xiě)了一個(gè)多么方便的保存插件, 怎么點(diǎn)一下就很方便可以保存文件悍抑, 我當(dāng)時(shí)深深的忍住了罩阵, 但是我居然看到會(huì)上一個(gè) Google 工程師, 說(shuō)自己寫(xiě)了一個(gè) VI 插件序臂, 怎么怎么按什么快捷鍵就可以快速保存文件了。

對(duì)于我這種資深Google粉來(lái)說(shuō), 實(shí)在看不慣 Google 工程師這么low奥秆, 當(dāng)時(shí)就在臺(tái)上跟他們演示了什么叫做自動(dòng)保存.....

Emacs 的插件哲學(xué)

因?yàn)镋macs發(fā)展30多年到今天逊彭, 其實(shí)已經(jīng)不是一個(gè)簡(jiǎn)簡(jiǎn)單單編輯器了, Emacs是一個(gè)自給自足的操作系統(tǒng)构订, 就像我在《Emacs是什么》里面講的一樣侮叮, Emacs強(qiáng)調(diào)的是工具協(xié)調(diào)的生態(tài)。 Emacs的插件強(qiáng)調(diào)的不是按鍵要多么簡(jiǎn)潔悼瘾, 它強(qiáng)調(diào)的時(shí)候研究開(kāi)發(fā)者心理囊榜, 開(kāi)發(fā)者在編程場(chǎng)景中遇到最大的痛點(diǎn)是什么? 它強(qiáng)調(diào)的是開(kāi)發(fā)者不需要做任何按鍵就能自動(dòng)在后臺(tái)做好亥宿,最大化減少插件和開(kāi)發(fā)者之間的交互過(guò)程卸勺, 讓開(kāi)發(fā)者把所有時(shí)間都放在實(shí)際代碼和深度思考上。

所以很多Emacs插件的優(yōu)秀標(biāo)準(zhǔn)就是“潤(rùn)物細(xì)無(wú)聲”烫扼, 插件的存在感越少越好曙求, 最好什么按鍵都不要按, 插件作者已經(jīng)總結(jié)出開(kāi)發(fā)者在編程中思考的必經(jīng)之路映企, 一切都是順其自然的完成的悟狱。

自動(dòng)保存文件

說(shuō)到編輯器保存這個(gè)功能, 我最開(kāi)始學(xué)Emacs的時(shí)候按了一個(gè)月的 Ctrl + x Ctrl + s 就受不了了堰氓, 最讓人受不了的時(shí)候有時(shí)候辛辛苦苦寫(xiě)的代碼挤渐, 忘記按保存了, 這時(shí)候突然停電了双絮, 除了 WTF 就沒(méi)有任何然后了浴麻。那時(shí)候我就想為什么一定要手動(dòng)按 Ctrl + x Ctrl + s按鍵來(lái)保存呢?能否自動(dòng)保存所編輯的文件掷邦? 什么時(shí)候最合適呢白胀?

其實(shí)每個(gè)程序員編寫(xiě)程序的時(shí)候, 寫(xiě)著寫(xiě)著思維不是那么流暢了抚岗, 需要停下來(lái)稍微思考一下或杠, 其實(shí)這時(shí)候就是自動(dòng)保存的最佳時(shí)刻: 利用程序員猶豫下一個(gè)字符應(yīng)該敲什么的空隙時(shí)間就足夠保存一個(gè)文件了。

所以那時(shí)候就寫(xiě)了一個(gè) auto-save.el 來(lái)做這件事情宣蔚, 每當(dāng)Emacs發(fā)現(xiàn)你停止敲鍵盤超過(guò)1s鐘以后向抢, 它就會(huì)把所有沒(méi)有保存的文件全部保存一遍。

所以自從那時(shí)候我基本十年沒(méi)有在手動(dòng)保存過(guò)任何文件了胚委, 也從來(lái)沒(méi)有丟失過(guò)任何一行寫(xiě)的代碼挟鸠。 配置很簡(jiǎn)單, 下載 auto-save.el亩冬, 然后在 ~/.emacs 里面加上下面的代碼:

(require 'auto-save)            ;; 加載自動(dòng)保存模塊

(auto-save-enable)              ;; 開(kāi)啟自動(dòng)保存功能
(setq auto-save-slient t)       ;; 自動(dòng)保存的時(shí)候靜悄悄的艘希, 不要打擾我

懶人也可以從 init-auto-save.el 下載寫(xiě)好的配置文件硼身, 然后只在 ~/.emacs 寫(xiě)上下面配置文件就可以了:

(require 'init-auto-save)

到這里, 以后用Emacs就不用傻傻的按保存鍵了覆享, Emacs發(fā)現(xiàn)你手指頭沒(méi)有敲鍵盤的時(shí)候自動(dòng)保存的佳遂,沒(méi)事不會(huì)和你搶CPU的。 ;)

auto-save.el 源碼解析

對(duì)Elisp感興趣的同學(xué)可以繼續(xù)往下看 auto-save.el 的源碼解析:

;; defgroup 關(guān)鍵字的意思是定義一個(gè)工作組撒顿,執(zhí)行 Alt + x customize-group 命令的時(shí)候可以進(jìn)行圖形化的模塊配置
;; 第一個(gè)參數(shù)是模塊的名字丑罪, 比如 auto-save
;; 第二個(gè)參數(shù)是模塊默認(rèn)開(kāi)啟的狀態(tài), 在 elisp 中凤壁, t 表示 true, nil 表示 false
;; 第三個(gè)參數(shù)是對(duì)模塊的文本解釋
;; 第四個(gè)參數(shù)表示對(duì)外提供 auto-save 這個(gè) group
(defgroup auto-save nil
  "Auto save file when emacs idle."
  :group 'auto-save)

;; defcustom 關(guān)鍵字的意思是定義一個(gè)可以被用戶自定義的變量吩屹, 當(dāng)用戶執(zhí)行 Alt + x customize-variable 的時(shí)候就可以補(bǔ)全 auto-save-idle 這個(gè)變量, defcustom 和 defvar 的區(qū)別主要是 defcustom 用于提供一些參數(shù)讓用戶可以在 Emacs 中圖形化定制變量?jī)?nèi)容拧抖, defvar 這只有變量名和 List 內(nèi)容煤搜, 一般用于函數(shù)內(nèi)部變量值存儲(chǔ)用, 不對(duì)外拋出給用戶定制
;; 第一個(gè)參數(shù)是變量的名字 autos-ave-idle 
;; 第二個(gè)參數(shù)是變量的值徙鱼, 這里我們定義為 1s, 表示自動(dòng)保存的延遲秒數(shù)
;; 第三個(gè)參數(shù)是變量的解釋宅楞, 一般在 Alt + x describe-variable 的時(shí)候就會(huì)顯示具體變量的文檔描述
;; 第四個(gè)參數(shù)用于定義變量的類型, 這里定義為整形袱吆, 這樣在 customize-group 的時(shí)候只有輸入整型才是正確保存
;; 第五個(gè)參數(shù)表示這個(gè)變量屬于 auto-save 這個(gè)組厌衙, 主要作用就是 customize-group 的時(shí)候能夠在一個(gè)界面中設(shè)置同一組的所有變量 
(defcustom auto-save-idle 1
  "The idle seconds to auto save file."
  :type 'integer
  :group 'auto-save)

;; autos-save-slient 的作用就是一個(gè)boolean值得變量, 設(shè)置為 nil 的時(shí)候绞绒, 表示每次自動(dòng)保存都會(huì)在 minibuffer 提示婶希, 設(shè)置成 t 的時(shí)候就會(huì) shutup, 讓我安安靜靜寫(xiě)會(huì)代碼, 別鬧...
(defcustom auto-save-slient nil
  "Nothing to dirty minibuffer if this option is non-nil."
  :type 'boolean
  :group 'auto-save)

;; 這段代碼的作用就是避免 Emacs 在保存文件的時(shí)候生成一大堆垃圾的 #foo# 文件蓬衡, 這種文件最討厭了喻杈, 不但什么用都沒(méi)有, 反而污染代碼目錄狰晚, 刪除都刪的我手酸
;; 想當(dāng)年為了找到關(guān)閉這個(gè)腦殘功能的變量筒饰, 我把 emacs 幾百個(gè)帶有 save 的變量全部打出來(lái), 一個(gè)一個(gè)變量的試才找到你啊 (可惜當(dāng)年我英文不好壁晒, 不知道怎么描述我想要的效果)
(setq auto-save-default nil)

;; 前方高能核心代碼瓷们, 請(qǐng)集中注意力
(defun auto-save-buffers ()
  ;; 所有你在 Alt + x 以后可以調(diào)用的函數(shù)都要手動(dòng)加上 (interactive) , 否則這段代碼只能在 Elisp 解釋器中執(zhí)行秒咐, 但是不能直接被用戶從 Alt + x 調(diào)用谬晕, 就想 interactive 這個(gè)單詞的意思一樣
  (interactive)
 ;; 創(chuàng)建 autosave-buffer-list 這個(gè)變量, 用于保存所有需要遍歷的 buffer 列表
  (let ((autosave-buffer-list))
    ;; save-excursion 這個(gè)關(guān)鍵字的意思是携取, 所有在 save-excursion 里面的代碼不管怎么折騰都不會(huì)對(duì) save-excursion 之前的Emacs狀態(tài)進(jìn)行任何改變攒钳, 你可以理解為這個(gè)關(guān)鍵字的意思就是用于保護(hù)現(xiàn)場(chǎng)用的 ;)
    (save-excursion
      ;; dolist 的作用就和很多語(yǔ)言的 foreach 一個(gè)意思, 把 buffer-list 這個(gè)函數(shù)返回的所有 buffer 在循環(huán)內(nèi)賦值給 buf 這個(gè)變量雷滋, 并在 dolist 的作用域中執(zhí)行對(duì) buf 影響的代碼
      (dolist (buf (buffer-list))
        ;; 設(shè)置當(dāng)前代碼的 buffer 為 buf 變量值不撑, 如果沒(méi)有前面 save-excursion, 你會(huì)發(fā)現(xiàn)emacs會(huì)一直在快速的切換所有 buffer 的過(guò)程
        (set-buffer buf)
        ;; 如果當(dāng)前 buffer 有一個(gè)相關(guān)聯(lián)文件 (buffer-file-name), 同時(shí)當(dāng)前 buffer 已經(jīng)被用戶修改了 (buffer-modified-p) 的情況下就執(zhí)行自動(dòng)保存
        (if (and (buffer-file-name) (buffer-modified-p))
            (progn
              ;; 把當(dāng)前 buffer 的名字壓進(jìn) autosave-buffer-list 列表文兢, 用于后面的保存提示
              (push (buffer-name) autosave-buffer-list)
              (if auto-save-slient
                  ;; 如果 auto-save-slient 這個(gè)變量為 true, 就不顯示任何保存信息, 因?yàn)?Emacs 的保存函數(shù) (basic-save-buffer) 本身機(jī)會(huì) blabla 的告訴你文件已經(jīng)保存了燎孟, 所以我們用 with-temp-message 配合空字符串來(lái)禁止 with-temp-message 里面的代碼在 minibuffer 顯示任何內(nèi)容
                  (with-temp-message ""
                    (basic-save-buffer))
                (basic-save-buffer))
              )))
      ;; unless 的意思是除非 auto-save-slient 為 false 就執(zhí)行
      (unless auto-save-slient
        ;; cond 就是 elisp 版的 switch禽作, 用于條件語(yǔ)句對(duì)比執(zhí)行
        (cond
         ;; 如果 autosave-buffer-list 列表里面沒(méi)有任何一個(gè)文件需要保存, 我們就不要去煩用戶了揩页, 默默打醬油路過(guò)就好了

         ;; 如果有一個(gè)文件需要保存, 我們就說(shuō) Saved ... 
         ((= (length autosave-buffer-list) 1)
          (message "# Saved %s" (car autosave-buffer-list)))
         ;; 如果有多個(gè)文件需要保存烹俗, 就說(shuō) 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 函數(shù)的意思就是在 auto-save-idle 定義的描述以后自動(dòng)執(zhí)行 auto-save-buffers 函數(shù)
  ;; #' 的意思就是在 runtime 執(zhí)行的時(shí)候再展開(kāi) auto-save-buffers 函數(shù)
  (run-with-idle-timer auto-save-idle t #'auto-save-buffers)
  )

(provide 'auto-save)

最后

Enjoy, have fun! ;)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末爆侣,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子幢妄,更是在濱河造成了極大的恐慌兔仰,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蕉鸳,死亡現(xiàn)場(chǎng)離奇詭異乎赴,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)潮尝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門榕吼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人勉失,你說(shuō)我怎么就攤上這事羹蚣。” “怎么了乱凿?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵顽素,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我徒蟆,道長(zhǎng)胁出,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任段审,我火速辦了婚禮全蝶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘戚哎。我一直安慰自己裸诽,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布型凳。 她就那樣靜靜地躺著丈冬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪甘畅。 梳的紋絲不亂的頭發(fā)上埂蕊,一...
    開(kāi)封第一講書(shū)人閱讀 52,156評(píng)論 1 308
  • 那天往弓,我揣著相機(jī)與錄音,去河邊找鬼蓄氧。 笑死函似,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的喉童。 我是一名探鬼主播撇寞,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼堂氯!你這毒婦竟也來(lái)了蔑担?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤咽白,失蹤者是張志新(化名)和其女友劉穎啤握,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體晶框,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡排抬,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了授段。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蹲蒲。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖畴蒲,靈堂內(nèi)的尸體忽然破棺而出悠鞍,到底是詐尸還是另有隱情,我是刑警寧澤模燥,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布咖祭,位于F島的核電站,受9級(jí)特大地震影響蔫骂,放射性物質(zhì)發(fā)生泄漏么翰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一辽旋、第九天 我趴在偏房一處隱蔽的房頂上張望浩嫌。 院中可真熱鬧,春花似錦补胚、人聲如沸码耐。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)骚腥。三九已至,卻和暖如春瓶逃,著一層夾襖步出監(jiān)牢的瞬間束铭,已是汗流浹背廓块。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留契沫,地道東北人带猴。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像懈万,于是被迫代替她去往敵國(guó)和親拴清。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容