Perlude也算是emacs上一个很有名的配置的了,在github上有2000多的start。

安装

curl -L https://github.com/bbatsov/prelude/raw/master/utils/installer.sh | sh

更新

更新插件:

M-x package-list-packages RET U x

更新Perlude本身:

cd ~/.emacs.d && git pull

开关模块

perlude包含了许多模块,但是默认并没有全部开启。模块配置文件在~/.emacs.d/prelude-modules.el中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
;; Emacs IRC client
(require 'prelude-erc)
(require 'prelude-ido) ;; Super charges Emacs completion for C-x C-f and more
;; (require 'prelude-helm) ;; Interface for narrowing and search
;; (require 'prelude-helm-everywhere) ;; Enable Helm everywhere
(require 'prelude-company)
;; (require 'prelude-key-chord) ;; Binds useful features to key combinations
;; (require 'prelude-mediawiki)
;; (require 'prelude-evil)

;;; Programming languages support
(require 'prelude-c)
;; (require 'prelude-clojure)
...

需要什么模块,去掉前面的注释就可以了。

配置文件

那我们需要在哪里添加我们自己的自定义配置呢?Perlude提供了一个personal目录。里面的所有elisp脚本都会被被调用

personal/preload中的所有elisp脚本会在Prelude开始前调用。

Helm

默认,prelude不开启helm。

(require 'prelude-helm-everywhere)

安装失败

File error: https://melpa.org/packages/helm-ag-20160119.454.el, Not found

这是helm-ag模块安装失败了。目前原因不明,先把这个模块关闭吧,.emacs.d/modules/prelude-helm-everywhere.el

1
;; (prelude-require-packages '(helm-descbinds helm-ag))  注释掉这一行

源码阅读笔记

load-file-name:
load-file-name is a variable defined in C source code'. Full name of file being loaded byload’.

load-file-name保存当前被load读取的文件的路径

(defvar prelude-dir (file-name-directory load-file-name)
    "The root dir of the Emacs Prelude distribution.")

这句定义了prelude的工作目录

expand-file-name:

add-to-list:如果列表中不存在该元素,添加到列表中

入口文件-init.el

.emacs.d/init.el是配置的入口文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
;;; init.el --- Prelude's configuration entry point.
(defvar current-user
(getenv
(if (equal system-type 'windows-nt) "USERNAME" "USER")))

(message "Prelude is powering up... Be patient, Master %s!" current-user)

(when (version< emacs-version "24.1")
(error "Prelude requires at least GNU Emacs 24.1, but you're running %s" emacs-version))

;; Always load newest byte code
(setq load-prefer-newer t)

(defvar prelude-dir (file-name-directory load-file-name)
"The root dir of the Emacs Prelude distribution.")
(defvar prelude-core-dir (expand-file-name "core" prelude-dir)
"The home of Prelude's core functionality.")
(defvar prelude-modules-dir (expand-file-name "modules" prelude-dir)
"This directory houses all of the built-in Prelude modules.")
(defvar prelude-personal-dir (expand-file-name "personal" prelude-dir)
"This directory is for your personal configuration.

Users of Emacs Prelude are encouraged to keep their personal configuration
changes in this directory. All Emacs Lisp files there are loaded automatically
by Prelude.")
(defvar prelude-personal-preload-dir (expand-file-name "preload" prelude-personal-dir)
"This directory is for your personal configuration, that you want loaded before Prelude.")
(defvar prelude-vendor-dir (expand-file-name "vendor" prelude-dir)
"This directory houses packages that are not yet available in ELPA (or MELPA).")
(defvar prelude-savefile-dir (expand-file-name "savefile" prelude-dir)
"This folder stores all the automatically generated save/history-files.")
(defvar prelude-modules-file (expand-file-name "prelude-modules.el" prelude-dir)
"This files contains a list of modules that will be loaded by Prelude.")

(unless (file-exists-p prelude-savefile-dir)
(make-directory prelude-savefile-dir))

(defun prelude-add-subfolders-to-load-path (parent-dir)
"Add all level PARENT-DIR subdirs to the `load-path'."
(dolist (f (directory-files parent-dir))
(let ((name (expand-file-name f parent-dir)))
(when (and (file-directory-p name)
(not (string-prefix-p "." f)))
(add-to-list 'load-path name)
(prelude-add-subfolders-to-load-path name)))))

;; add Prelude's directories to Emacs's `load-path'
(add-to-list 'load-path prelude-core-dir)
(add-to-list 'load-path prelude-modules-dir)
(add-to-list 'load-path prelude-vendor-dir)
(prelude-add-subfolders-to-load-path prelude-vendor-dir)

;; reduce the frequency of garbage collection by making it happen on
;; each 50MB of allocated data (the default is on every 0.76MB)
(setq gc-cons-threshold 50000000)

;; warn when opening files bigger than 100MB
(setq large-file-warning-threshold 100000000)

;; preload the personal settings from `prelude-personal-preload-dir'
(when (file-exists-p prelude-personal-preload-dir)
(message "Loading personal configuration files in %s..." prelude-personal-preload-dir)
(mapc 'load (directory-files prelude-personal-preload-dir 't "^[^#].*el$")))

(message "Loading Prelude's core...")

;; the core stuff
(require 'prelude-packages)
(require 'prelude-custom) ;; Needs to be loaded before core, editor and ui
(require 'prelude-ui)
(require 'prelude-core)
(require 'prelude-mode)
(require 'prelude-editor)
(require 'prelude-global-keybindings)

;; OSX specific settings
(when (eq system-type 'darwin)
(require 'prelude-osx))

(message "Loading Prelude's modules...")

;; the modules
(if (file-exists-p prelude-modules-file)
(load prelude-modules-file)
(message "Missing modules file %s" prelude-modules-file)
(message "You can get started by copying the bundled example file"))

;; config changes made through the customize UI will be store here
(setq custom-file (expand-file-name "custom.el" prelude-personal-dir))

;; load the personal settings (this includes `custom-file')
(when (file-exists-p prelude-personal-dir)
(message "Loading personal configuration files in %s..." prelude-personal-dir)
(mapc 'load (directory-files prelude-personal-dir 't "^[^#].*el$")))

(message "Prelude is ready to do thy bidding, Master %s!" current-user)

(prelude-eval-after-init
;; greet the use with some useful tip
(run-at-time 5 nil 'prelude-tip-of-the-day))

;;; init.el ends here

安装插件模块-prelude-packages.el

入口文件中,第一个require的是prelude-packages:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
;;; prelude-packages.el --- Emacs Prelude: default package selection.
;; 使用了common lisp扩展。但是这种写法不好,没有cl-前缀,官方推荐使用(require ‘cl-lib)。
;; https://www.emacswiki.org/emacs/CommonLispForEmacs
(require 'cl)
(require 'package)

(add-to-list 'package-archives
'("melpa" . "https://melpa.org/packages/") t)
;; set package-user-dir to be relative to Prelude install path
(setq package-user-dir (expand-file-name "elpa" prelude-dir))
(package-initialize)

;; 定义需要安装的插件列表,这些插件是基础插件,在第一次启动的时候会全部装上。
(defvar prelude-packages
'(ace-window
avy
anzu
beacon
browse-kill-ring
dash
discover-my-major
diff-hl
diminish
easy-kill
epl
expand-region
flycheck
gist
git-timemachine
gitconfig-mode
gitignore-mode
god-mode
grizzl
guru-mode
ov
projectile
magit
move-text
operate-on-number
smart-mode-line
smartparens
smartrep
undo-tree
volatile-highlights
zenburn-theme
)
"A list of packages to ensure are installed at launch.")

;; 判断插件列表中的插件是否都安装完毕
(defun prelude-packages-installed-p ()
"Check if all packages in `prelude-packages' are installed."
(every #'package-installed-p prelude-packages))

(defun prelude-require-package (package)
"Install PACKAGE unless already installed."
(unless (memq package prelude-packages)
(add-to-list 'prelude-packages package))
(unless (package-installed-p package)
(package-install package)))

(defun prelude-require-packages (packages)
"Ensure PACKAGES are installed.
Missing packages are installed automatically."
(mapc #'prelude-require-package packages))

(define-obsolete-function-alias 'prelude-ensure-module-deps 'prelude-require-packages)

(defun prelude-install-packages ()
"Install all packages listed in `prelude-packages'."
(unless (prelude-packages-installed-p)
;; check for new packages (package versions)
(message "%s" "Emacs Prelude is now refreshing its package database...")
(package-refresh-contents)
(message "%s" " done.")
;; install the missing packages
(prelude-require-packages prelude-packages)))

;; 安装基础插件
(prelude-install-packages)

(defun prelude-list-foreign-packages ()
"Browse third-party packages not bundled with Prelude.

Behaves similarly to `package-list-packages', but shows only the packages that
are installed and are not in `prelude-packages'. Useful for
removing unwanted packages."
(interactive)
(package-show-package-list
(set-difference package-activated-list prelude-packages)))

(defmacro prelude-auto-install (extension package mode)
"When file with EXTENSION is opened triggers auto-install of PACKAGE.
PACKAGE is installed only if not already present. The file is opened in MODE."
`(add-to-list 'auto-mode-alist
`(,extension . (lambda ()
(unless (package-installed-p ',package)
(package-install ',package))
(,mode)))))

(defvar prelude-auto-install-alist
'(("\\.clj\\'" clojure-mode clojure-mode)
("\\.cmake\\'" cmake-mode cmake-mode)
("CMakeLists\\.txt\\'" cmake-mode cmake-mode)
("\\.coffee\\'" coffee-mode coffee-mode)
("\\.css\\'" css-mode css-mode)
("\\.csv\\'" csv-mode csv-mode)
("\\.d\\'" d-mode d-mode)
("\\.dart\\'" dart-mode dart-mode)
("\\.elm\\'" elm-mode elm-mode)
("\\.ex\\'" elixir-mode elixir-mode)
("\\.exs\\'" elixir-mode elixir-mode)
("\\.elixir\\'" elixir-mode elixir-mode)
("\\.erl\\'" erlang erlang-mode)
("\\.feature\\'" feature-mode feature-mode)
("\\.go\\'" go-mode go-mode)
("\\.groovy\\'" groovy-mode groovy-mode)
("\\.haml\\'" haml-mode haml-mode)
("\\.hs\\'" haskell-mode haskell-mode)
("\\.json\\'" json-mode json-mode)
("\\.kv\\'" kivy-mode kivy-mode)
("\\.latex\\'" auctex LaTeX-mode)
("\\.less\\'" less-css-mode less-css-mode)
("\\.lua\\'" lua-mode lua-mode)
("\\.markdown\\'" markdown-mode markdown-mode)
("\\.md\\'" markdown-mode markdown-mode)
("\\.ml\\'" tuareg tuareg-mode)
("\\.pp\\'" puppet-mode puppet-mode)
("\\.php\\'" php-mode php-mode)
("\\.proto\\'" protobuf-mode protobuf-mode)
("\\.pyd\\'" cython-mode cython-mode)
("\\.pyi\\'" cython-mode cython-mode)
("\\.pyx\\'" cython-mode cython-mode)
("PKGBUILD\\'" pkgbuild-mode pkgbuild-mode)
("\\.rs\\'" rust-mode rust-mode)
("\\.sass\\'" sass-mode sass-mode)
("\\.scala\\'" scala-mode2 scala-mode)
("\\.scss\\'" scss-mode scss-mode)
("\\.slim\\'" slim-mode slim-mode)
("\\.styl\\'" stylus-mode stylus-mode)
("\\.swift\\'" swift-mode swift-mode)
("\\.textile\\'" textile-mode textile-mode)
("\\.thrift\\'" thrift thrift-mode)
("\\.yml\\'" yaml-mode yaml-mode)
("\\.yaml\\'" yaml-mode yaml-mode)
("Dockerfile\\'" dockerfile-mode dockerfile-mode)))

;; markdown-mode doesn't have autoloads for the auto-mode-alist
;; so we add them manually if it's already installed
(when (package-installed-p 'markdown-mode)
(add-to-list 'auto-mode-alist '("\\.markdown\\'" . gfm-mode))
(add-to-list 'auto-mode-alist '("\\.md\\'" . gfm-mode)))

(when (package-installed-p 'pkgbuild-mode)
(add-to-list 'auto-mode-alist '("PKGBUILD\\'" . pkgbuild-mode)))

;; build auto-install mappings
(mapc
(lambda (entry)
(let ((extension (car entry))
(package (cadr entry))
(mode (cadr (cdr entry))))
(unless (package-installed-p package)
(prelude-auto-install extension package mode))))
prelude-auto-install-alist)

(provide 'prelude-packages)
;; Local Variables:
;; byte-compile-warnings: (not cl-functions)
;; End:

;;; prelude-packages.el ends here

prelude-packages.el首先安装基础的插件。然后针对不同的文件,实现了自动安装对应主模式。

安装的基本代码是:

1
2
3
4
5
6
7
(add-to-list 'package-archives
'("melpa" . "https://melpa.org/packages/") t)
(package-initialize)

(package-refresh-contents)
(package-install 'color-theme-solarized)
(load-theme 'solarized-dark t)

在emacs24后加入了包管理,所以大大方便了插件的安装。(package-refresh-contents)这句是必要的,这是通知包管理去获取插件列表。如果不获取列表就进行package-install会保存,提示插件不存在

自动安装主要是通过auto-mode-alist这个变量实现的。这个list的每个元素的结构是(REGEXP . FUNCTION)。前者是用来判断后缀名的正则表达式。后者是当打开这种后缀文件时,调用的方法。

prelude使用这个list,添加了很多程序代码的自动安装宏:

1
2
3
4
5
6
7
8
(defmacro prelude-auto-install (extension package mode)
"When file with EXTENSION is opened triggers auto-install of PACKAGE.
PACKAGE is installed only if not already present. The file is opened in MODE."
`(add-to-list 'auto-mode-alist
`(,extension . (lambda ()
(unless (package-installed-p ',package)
(package-install ',package))
(,mode)))))

宏的相关知识可以看这篇文章。不过感觉可以完全不用宏实现吧?

(every #'package-installed-p prelude-packages)这里的#是什么效果?–其实是#',是function的缩写。告诉emacs解释器,后面跟着的表达式是函数,可以使用优化手段了。

用户可控配置定义-prelude-custom.el

prelude定义了一些用户可以修改的配置。prelude-custom.el是这些配置项定义的地方。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
;; customize
(defgroup prelude nil
"Emacs Prelude configuration."
:prefix "prelude-"
:group 'convenience)

(defcustom prelude-auto-save t
"Non-nil values enable Prelude's auto save."
:type 'boolean
:group 'prelude)

(defcustom prelude-guru t
"Non-nil values enable `guru-mode'."
:type 'boolean
:group 'prelude)

(defcustom prelude-whitespace t
"Non-nil values enable Prelude's whitespace visualization."
:type 'boolean
:group 'prelude)

(defcustom prelude-clean-whitespace-on-save t
"Cleanup whitespace from file before it's saved.
Will only occur if `prelude-whitespace' is also enabled."
:type 'boolean
:group 'prelude)

(defcustom prelude-flyspell t
"Non-nil values enable Prelude's flyspell support."
:type 'boolean
:group 'prelude)

(defcustom prelude-user-init-file (expand-file-name "personal/"
user-emacs-directory)
"Path to your personal customization file.
Prelude recommends you only put personal customizations in the
personal folder. This variable allows you to specify a specific
folder as the one that should be visited when running
`prelude-find-user-init-file'. This can be easily set to the desired buffer
in lisp by putting `(setq prelude-user-init-file load-file-name)'
in the desired elisp file."
:type 'string
:group 'prelude)

(defcustom prelude-indent-sensitive-modes
'(conf-mode coffee-mode haml-mode python-mode slim-mode yaml-mode)
"Modes for which auto-indenting is suppressed."
:type 'list
:group 'prelude)

(defcustom prelude-yank-indent-modes '(LaTeX-mode TeX-mode)
"Modes in which to indent regions that are yanked (or yank-popped).
Only modes that don't derive from `prog-mode' should be listed here."
:type 'list
:group 'prelude)

(defcustom prelude-yank-indent-threshold 1000
"Threshold (# chars) over which indentation does not automatically occur."
:type 'number
:group 'prelude)

(defcustom prelude-theme 'zenburn
"The default color theme, change this in your /personal/preload config."
:type 'symbol
:group 'prelude)

(defcustom prelude-shell (getenv "SHELL")
"The default shell to run with `prelude-visit-term-buffer'"
:type 'string
:group 'prelude)

(provide 'prelude-custom)

;;; prelude-custom.el ends here

defgroup TODO

defcustom TODO

界面相关配置-prelude-ui.el

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
;; 关闭工具条
;; the toolbar is just a waste of valuable screen estate
;; in a tty tool-bar-mode does not properly auto-load, and is
;; already disabled anyway
(when (fboundp 'tool-bar-mode)
(tool-bar-mode -1))

(menu-bar-mode -1)

;; 禁用光标闪烁
;; the blinking cursor is nothing, but an annoyance
(blink-cursor-mode -1)

;; 关闭欢迎界面
;; disable startup screen
(setq inhibit-startup-screen t)

;; nice scrolling
(setq scroll-margin 0
scroll-conservatively 100000
scroll-preserve-screen-position 1)

;; mode line settings
(line-number-mode t)
(column-number-mode t)
(size-indication-mode t)

;; enable y/n answers
(fset 'yes-or-no-p 'y-or-n-p)

;;
;; more useful frame title, that show either a file or a
;; buffer name (if the buffer isn't visiting a file)
(setq frame-title-format
'("" invocation-name " Prelude - " (:eval (if (buffer-file-name)
(abbreviate-file-name (buffer-file-name))
"%b"))))

;; 设置主题。默认prelude使用zenburn主题
;; use zenburn as the default theme
(when prelude-theme
(load-theme prelude-theme t))

(require 'smart-mode-line)
(setq sml/no-confirm-load-theme t)
;; delegate theming to the currently active theme
(setq sml/theme nil)
(add-hook 'after-init-hook #'sml/setup)

;; 启用beacon-mode,这个模式会在光标移动后闪亮当前行,效果图见下方
;; show the cursor when moving after big movements in the window
(require 'beacon)
(beacon-mode +1)

(provide 'prelude-ui)
;;; prelude-ui.el ends here