ZeroEmacs: 从零配置你的Emacs

当我们从 emacs -Q 启动 vanilla emacs 调试的时候,彷佛如回到了“刀耕火种”的蛮荒之初。

Emacs -Q 的四个痛点

尤其是四个经常用的功能:

  1. 查找文件: C-x C-f file-file 没有任何提示,全凭记忆力 image

  2. 查找函数:最依赖的 M-x execute-extended-command 同样依靠机械记忆:


    必须打出第一个完整的单词的情况下,按键tab才会给出适当的提示。

  3. 查找buffer:buffer的操作,只有先调用buffer-list然后再单击进入,因为switch-buffer也不会给提示:

  4. 搜索,最后最令人失望的一点是 C-s isearch-forward, 丝毫不能展示出emacs的优势,不能从mini-buffer中显示搜索结果,而只能如其他编辑器一般从正文中一个一个关键词盯着看。

基于以上的分析,调试 emacs 的所需的最小化配置,我们需要趁手的 1)查找文件 2)查找函数 3)查找buffer 4)搜索并从mini-buffer中展示关键词。

由此,ZeroEmacs从第一个安装包ivy起步。

加载个人专属配置

Emacs指定启动过程所调用的配置文件的命令为:

emacs -q -l ~/.zeroemacs/config.el

其中 -q(quick) 为 --no-init-file 不加载任何初始配置文件, 而核心的 -l 选项则为 -l file, --load-file。

有了 load-file,我们就能有一份专属的个人配置。

ZeroEmacs起步

首先创建 ~/.zeroemacs/ 目录与config.el 文件,并写入第一行配置:

(setq user-emacs-directory "~/.zeroemacs/")

然后启动zeroEmacs:

emacs -q -l ~/.zeroemacs/config.el

调用命令package-install 安装 counsel 包。

ivy分为三个部分:ivy,swiper和counsel;安装counsel的同时,另外两项将自动加载为依赖包。

安装成功后,将会看到所有的包都安装在~/.zeroemacs/elpa目录下:

image

之后,将ivy的配置写入confiig.el

(setq user-emacs-directory "~/.zeroemacs/")
;; 加载路径
(add-to-list 'load-path "~/.zeroemacs/elpa/ivy-0.13.1/")
(add-to-list 'load-path "~/.zeroemacs/elpa/counsel-0.13.1/")
(add-to-list 'load-path "~/.zeroemacs/elpa/swiper-0.13.1/")

;;分别加载这三个包
(require 'ivy)
(require 'swiper)
(require 'counsel)

(ivy-mode 1)

(global-set-key "\C-s" 'swiper-isearch)
(global-set-key (kbd "M-x") 'counsel-M-x)
(global-set-key (kbd "C-x C-f") 'counsel-find-file)
(global-set-key "\C-xb" 'ivy-switch-buffer)

(provide 'config.el)
;;; config.el ends here

如此,ZeroEmacs的迈出最小化配置的第一步。

6 个赞

感觉有一些地方不是很好。

emacs-user-directory 不应该硬编码

可以通过 (file-name-directory (or load-file-name buffer-file-name)) 来获取。

如果用 package.el 的话不应该手工管理 load-path

因为存在隐式的依赖,应该通过 (package-initialize) 进行初始化。

需要 bootstrap 的逻辑来让配置在新环境可以复用

类似这样的写法

(setq package-selected-packages
      '(ivy))

(unless package-archive-contents
  (package-refresh-contents))

(package-install-selected-packages)

对于新手来说 kbd 用或不用最好保持一致。

"\C-xb" 或是 (kbd "C-x b") 选一种风格。

provide 的名字不应该包含 .el

应该使用 (provide 'config),但这里不应该写,因为是入口文件。

counsel 需要显示激活

使用 (counsel-mode 1)

7 个赞

都 -Q 了,不應該什麼都不要嘛?

另外,如果是想要一個由於主要配置掛了不得不用的,應該是從自己的配置中找出想要的,然後把該有的也直接扔倉庫pin住版本,同時直接 require 就行,不應該再用 package.el 了(

3 个赞

是呢,就想从zero开始,逐步从doom的配置中找出来想要的。

感谢,学习了,按照建议逐一修订。

package.el 有点复杂,还没开始学。

最近也开始搞 vanilla emacs, 原因是doom用着很好,几乎很完美。 但是在编辑某些源文件时持续的卡顿,让人心情烦躁。 因此尝试了下-Q, 发现在裸版下编辑文件极度丝滑。然后决心配置一个简单的能提供项目最基本功能编辑环境,目前在进行中。。。

2 个赞
;; init-emacs.el
;; emacs -Q -l init-emacs.el

(setq package-enable-at-startup nil)
(setq package-archives
      '(("melpa-cn" . "http://mirrors.tuna.tsinghua.edu.cn/elpa/melpa/")
        ("org-cn"   . "http://mirrors.tuna.tsinghua.edu.cn/elpa/org/")
        ("gnu-cn"   . "http://mirrors.tuna.tsinghua.edu.cn/elpa/gnu/")))

(package-initialize)

(require 'cl-lib)
(defvar my/packages '(use-package counsel quelpa-use-package))

(defun my/packages-installed-p ()
  (cl-loop for pkg in my/packages
	    when (not (package-installed-p pkg)) do (cl-return nil)
	    finally (cl-return t)))

(unless (my/packages-installed-p)
  (message "%s" "Refreshing package database...")
  (package-refresh-contents)
  (dolist (pkg my/packages)
    (when (not (package-installed-p pkg))
	  (package-install pkg))))

(require 'use-package)
(require 'quelpa-use-package)

(use-package counsel
  :config
  (global-set-key (kbd "M-x") 'counsel-M-x)
  (global-set-key (kbd "C-x C-f") 'counsel-find-file)
  (global-set-key (kbd "C-h f") 'counsel-describe-function)
  (global-set-key (kbd "C-h v") 'counsel-describe-variable))

这个是我用来测试 emacs 包的初始配置,蛮好用的。再添加新的配置也比较方便

1 个赞

终于有人闲的从零开始折腾Emacs配置了 :kissing_smiling_eyes:
我想要不要引入一些工具,比如use-package来简化这个配置
配置文件要不要切分成

  1. 编程语言相关
  2. 代码补全相关
  3. 一些插件
  4. 图形界面
  5. 自定义设置custom
  6. 其他杂项

其实不需要 use-package,就用 define-key, with-eval-after-load, require, autoload 之类的就可以。

我有个视频讲到这个,下面是一个实例:

3 个赞

开启counsel-mode后,类似find-file和counsel-find-file功能上应该差不多了吧,不需要用counsel-find-file来替换find-file?

1 个赞

是直接remap掉了一大部分

(defvar counsel-mode-map
  (let ((map (make-sparse-keymap)))
    (dolist (binding
              '((execute-extended-command . counsel-M-x)
                (describe-bindings . counsel-descbinds)
                (describe-function . counsel-describe-function)
                (describe-variable . counsel-describe-variable)
                (describe-symbol . counsel-describe-symbol)
                (apropos-command . counsel-apropos)
                (describe-face . counsel-describe-face)
                (list-faces-display . counsel-faces)
                (find-file . counsel-find-file)
                (find-library . counsel-find-library)
                (imenu . counsel-imenu)
                (load-library . counsel-load-library)
                (load-theme . counsel-load-theme)
                (yank-pop . counsel-yank-pop)
                (info-lookup-symbol . counsel-info-lookup-symbol)
                (pop-to-mark-command . counsel-mark-ring)
                (geiser-doc-look-up-manual . counsel-geiser-doc-look-up-manual)
                (bookmark-jump . counsel-bookmark)))
      (define-key map (vector 'remap (car binding)) (cdr binding)))
    map)
  "Map for `counsel-mode'.
Remaps built-in functions to counsel replacements.")
1 个赞

我也有个配置用来测试新package/以及qa提bug用 :joy:
如果嫌use-package 太重了 可以参考一下purcell的require-package这个函数

;;; On-demand installation of packages
    (require 'cl-lib)
    (defun require-package (package &optional min-version no-refresh)
      "Install given PACKAGE, optionally requiring MIN-VERSION.
    If NO-REFRESH is non-nil, the available package lists will not be
    re-downloaded in order to locate PACKAGE."
      (or (package-installed-p package min-version)
          (let* ((known (cdr (assoc package package-archive-contents)))
                 (versions (mapcar #'package-desc-version known)))
            (if (cl-find-if (lambda (v) (version-list-<= min-version v)) versions)
                (package-install package)
              (if no-refresh
                  (error "No version of %s >= %S is available" package min-version)
                (package-refresh-contents)
                (require-package package min-version t))))))
1 个赞

引用下 redguardtoo 用来测试 package 的最小配置

我觉得,既然是从零配置,每添加一个功能,最好能阐述理由。

在Emacs中,bookmarks是中央控制台,是交通指挥枢纽,是机场的塔台,是Navigation Control Center,是我们随身携带的地图。

应用ZeroEmacs得心应手,如回家一般的熟悉,自己的各种瓶瓶罐罐摆放在何处,应该将bookmarks从doom迁移过来。

(setq bookmark-default-file "~/.doom.d/bookmarks")

load-file 加载之后,无论在哪种配置上,都稳坐指挥台。

ZeroEmacs目前的所有配置:

(setq user-emacs-directory "~/.zeroemacs/")

(add-to-list 'load-path "~/.zeroemacs/elpa/ivy-0.13.1/")
(add-to-list 'load-path "~/.zeroemacs/elpa/counsel-0.13.1/")
(add-to-list 'load-path "~/.zeroemacs/elpa/swiper-0.13.1/")

(require 'ivy)
(require 'swiper)
(require 'counsel)

(ivy-mode 1)

(global-set-key "\C-s" 'swiper-isearch)
(global-set-key (kbd "M-x") 'counsel-M-x)
(global-set-key (kbd "C-x C-f") 'counsel-find-file)
(global-set-key "\C-xb" 'ivy-switch-buffer)

;; Set bookmarks
(setq bookmark-default-file "~/.doom.d/bookmarks")

(provide 'config)
;;; config.el ends here

楼主怎么不用 (package-initialize),否则每加一个包都要 add-to-list 'load-path 包路径一次再 require 一次,包少还好,包多了还是有点麻烦的。

是不是用了(package-initialize)但是没生效,还是有其它目的

谢谢,之前不知道 package-initialize 这个函数。

01 哈哈,每次配置崩了,就要瞬回一次蛮荒时代,简直没法想自己是怎么调教成功这个小怪兽的

1 个赞

今天也是, doom 因为网络的原因差点没重装好,才体会到有点基本配置做家底真很nice,至少能倒退回青铜和铁器时代。

设置好Emacs最高频的应用bookmarks这个中央控制台和全局地图,同时也要配置好查看每个街区的局部地图工具 dired。

dire默认开启状态,展示文件的全部信息:

这对文件的打开,查找,重命名带来不便,因此按键 ( 调用 dired-hide-details-mode 将详情关掉。

;; Set dired
(add-hook 'dired-mode-hook #'dired-hide-details-mode)

设置好bookmarks和dired,全局的工作告一段落。

顺带手给ZeroEmacs创建快捷启动 alias

# zeroemacs from terminal
alias zemacs="emacs -nw -Q -l ~/.zeroemacs/config.el"
# zeroemacs from GUI
alias zeroemacs="emacs -Q -l ~/.zeroemacs/config.el"