Commit | Line | Data |
---|---|---|
35ea1ba4 AB |
1 | #+title: =aminb='s Emacs Init file |
2 | #+property: header-args :results silent :comments link :tangle ~/dotfiles/emacs/init.el | |
3 | ||
4 | * Intro | |
5 | ||
6 | TODO: description | |
7 | ||
abdc6c07 AB |
8 | * Contents :toc_1:noexport: |
9 | ||
10 | - [[#intro][Intro]] | |
11 | - [[#header][Header]] | |
12 | - [[#initial-setup][Initial setup]] | |
056eba24 | 13 | - [[#core][Core]] |
abdc6c07 AB |
14 | - [[#config][Config]] |
15 | - [[#footer][Footer]] | |
35ea1ba4 AB |
16 | |
17 | * Header | |
279a98e2 AB |
18 | :PROPERTIES: |
19 | :CUSTOM_ID: header | |
20 | :END: | |
35ea1ba4 AB |
21 | |
22 | ** First line | |
23 | ||
24 | #+begin_src emacs-lisp :comments none | |
25 | ;;; init.el --- Amin Bandali's Emacs config -*- lexical-binding: t ; eval: (view-mode 1)-*- | |
26 | #+end_src | |
27 | ||
28 | Enable =view-mode=, which both makes the file read-only (as a reminder | |
29 | that =init.el= is an auto-generated file, not supposed to be edited), | |
30 | and provides some convenient key bindings for browsing through the | |
31 | file. | |
32 | ||
33 | ** License | |
34 | ||
35 | #+begin_src emacs-lisp :comments none | |
36 | ;; Copyright (C) 2018 Amin Bandali <amin@aminb.org> | |
37 | ||
38 | ;; This program is free software: you can redistribute it and/or modify | |
39 | ;; it under the terms of the GNU General Public License as published by | |
40 | ;; the Free Software Foundation, either version 3 of the License, or | |
41 | ;; (at your option) any later version. | |
42 | ||
43 | ;; This program is distributed in the hope that it will be useful, | |
44 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
45 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
46 | ;; GNU General Public License for more details. | |
47 | ||
48 | ;; You should have received a copy of the GNU General Public License | |
49 | ;; along with this program. If not, see <https://www.gnu.org/licenses/>. | |
50 | #+end_src | |
51 | ||
52 | ** Commentary | |
53 | ||
6089982a | 54 | #+begin_src emacs-lisp :comments none |
35ea1ba4 AB |
55 | ;;; Commentary: |
56 | ||
57 | ;; Emacs configuration of Amin Bandali, computer scientist and functional | |
58 | ;; programmer. | |
59 | ||
60 | ;; THIS FILE IS AUTO-GENERATED FROM `init.org'. | |
61 | #+end_src | |
62 | ||
e4cd0d49 AB |
63 | ** Naming conventions |
64 | ||
65 | The conventions below were inspired by [[https://github.com/hlissner/doom-emacs][Doom]]'s conventions, found | |
66 | [[https://github.com/hlissner/doom-emacs/blob/5dacbb7cb1c6ac246a9ccd15e6c4290def67757c/core/core.el#L3-L17][here]]. Naturally, I use my initials, =ab=, instead of =doom=. | |
67 | ||
68 | #+begin_src emacs-lisp :comments none | |
69 | ;; Naming conventions: | |
70 | ;; | |
71 | ;; ab-... public variables or non-interactive functions | |
72 | ;; ab--... private anything (non-interactive), not safe for direct use | |
73 | ;; ab/... an interactive function; safe for M-x or keybinding | |
74 | ;; ab:... an evil operator, motion, or command | |
75 | ;; ab|... a hook function | |
76 | ;; ab*... an advising function | |
77 | ;; ab@... a hydra command | |
1446ab91 | 78 | ;; ...! a macro |
e4cd0d49 AB |
79 | #+end_src |
80 | ||
fd134a2b | 81 | * Initial setup |
279a98e2 AB |
82 | :PROPERTIES: |
83 | :CUSTOM_ID: initial-setup | |
84 | :END: | |
35ea1ba4 | 85 | |
6089982a | 86 | #+begin_src emacs-lisp :comments none |
35ea1ba4 AB |
87 | ;;; Code: |
88 | #+end_src | |
89 | ||
6089982a AB |
90 | ** Startup time |
91 | ||
92 | Measure and display startup time. Also, temporarily increase | |
93 | ~gc-cons-threshhold~ during startup to reduce reduce garbage | |
94 | collection frequency. Taken from [[https://github.com/dieggsy/dotfiles/tree/3d95bc08033920e077855caf545a975eba52d28d/emacs.d#startup-time][here]]. | |
95 | ||
96 | #+begin_src emacs-lisp | |
63a849c3 AB |
97 | (defconst ab--emacs-start-time (current-time)) |
98 | (defconst ab--gc-cons-threshold gc-cons-threshold) | |
99 | (defconst ab--gc-cons-percentage gc-cons-percentage) | |
100 | (defvar ab--file-name-handler-alist file-name-handler-alist) | |
6089982a AB |
101 | (setq gc-cons-threshold 400000000 |
102 | gc-cons-percentage 0.6 | |
103 | file-name-handler-alist nil | |
104 | ;; sidesteps a bug when profiling with esup | |
105 | esup-child-profile-require-level 0) | |
106 | #+end_src | |
107 | ||
108 | Reset the variables back to default after init. | |
109 | ||
110 | #+begin_src emacs-lisp | |
111 | (add-hook | |
112 | 'after-init-hook | |
113 | `(lambda () | |
63a849c3 AB |
114 | (setq gc-cons-threshold ab--gc-cons-threshold |
115 | gc-cons-percentage ab--gc-cons-percentage | |
116 | file-name-handler-alist ab--file-name-handler-alist) | |
6089982a | 117 | (let ((elapsed (float-time (time-subtract (current-time) |
63a849c3 | 118 | ab--emacs-start-time)))) |
6089982a AB |
119 | (message "Loading %s...done (%.3fs) [after-init]" |
120 | ,load-file-name elapsed)))) | |
121 | #+end_src | |
35ea1ba4 | 122 | |
fd134a2b AB |
123 | ** Package management |
124 | ||
204986be | 125 | *** =straight.el= |
fd134a2b AB |
126 | |
127 | #+begin_quote | |
128 | Next-generation, purely functional package manager for the Emacs | |
129 | hacker. | |
130 | #+end_quote | |
131 | ||
132 | =straight.el= allows me to have a fully reproducible Emacs setup. | |
133 | ||
134 | **** Bootstrap | |
135 | ||
136 | #+begin_src emacs-lisp | |
137 | (let ((bootstrap-file (concat user-emacs-directory "straight/repos/straight.el/bootstrap.el")) | |
138 | (bootstrap-version 3)) | |
139 | (unless (file-exists-p bootstrap-file) | |
140 | (with-current-buffer | |
141 | (url-retrieve-synchronously | |
142 | "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el" | |
143 | 'silent 'inhibit-cookies) | |
144 | (goto-char (point-max)) | |
145 | (eval-print-last-sexp))) | |
146 | (load bootstrap-file nil 'nomessage)) | |
147 | #+end_src | |
148 | ||
149 | **** Useful helpers | |
150 | ||
151 | #+begin_src emacs-lisp | |
1446ab91 | 152 | (defun ab/reload-init () |
fd134a2b AB |
153 | "Reload init.el." |
154 | (interactive) | |
155 | (straight-transaction | |
156 | (straight-mark-transaction-as-init) | |
157 | (message "Reloading init.el...") | |
158 | (load user-init-file nil 'nomessage) | |
159 | (message "Reloading init.el... done."))) | |
160 | ||
1446ab91 | 161 | (defun ab/eval-buffer () |
fd134a2b AB |
162 | "Evaluate the current buffer as Elisp code." |
163 | (interactive) | |
164 | (message "Evaluating %s..." (buffer-name)) | |
165 | (straight-transaction | |
166 | (if (null buffer-file-name) | |
167 | (eval-buffer) | |
168 | (when (string= buffer-file-name user-init-file) | |
169 | (straight-mark-transaction-as-init)) | |
170 | (load-file buffer-file-name))) | |
171 | (message "Evaluating %s... done." (buffer-name))) | |
172 | #+end_src | |
173 | ||
174 | *** =use-package= | |
175 | ||
176 | #+begin_quote | |
63a849c3 | 177 | A use-package declaration for simplifying your .emacs |
fd134a2b AB |
178 | #+end_quote |
179 | ||
180 | =use-package= is an awesome utility for managing and configuring | |
181 | packages in a neatly organized way and without compromising on | |
182 | performance. So let's install it using =striaght.el= and have it use | |
183 | =straight.el= for installing packages. | |
184 | ||
185 | #+begin_src emacs-lisp | |
186 | (straight-use-package 'use-package) | |
187 | (setq straight-use-package-by-default t) | |
188 | #+end_src | |
189 | ||
190 | ** No littering in =~/.emacs.d= | |
191 | ||
192 | #+begin_quote | |
63a849c3 | 193 | Help keeping ~/.emacs.d clean |
fd134a2b AB |
194 | #+end_quote |
195 | ||
196 | By default, even for Emacs' built-in packages, the configuration files | |
197 | and persistent data are all over the place. Use =no-littering= to help | |
198 | contain the mess. | |
199 | ||
200 | #+begin_src emacs-lisp | |
201 | (use-package no-littering | |
202 | :demand t | |
203 | :config | |
204 | (savehist-mode 1) | |
205 | (add-to-list 'savehist-additional-variables 'kill-ring) | |
206 | (save-place-mode 1) | |
207 | (setq auto-save-file-name-transforms | |
208 | `((".*" ,(no-littering-expand-var-file-name "auto-save/") t)))) | |
209 | #+end_src | |
210 | ||
bab98ee5 AB |
211 | ** Custom file (=custom.el=) |
212 | ||
213 | I'm not planning on using the custom file much, but even so, I | |
214 | definitely don't want it mixing with =init.el=. So, here, let's give | |
215 | it it's own file. | |
216 | ||
217 | #+begin_src emacs-lisp | |
fd134a2b AB |
218 | (setq custom-file (no-littering-expand-etc-file-name "custom.el")) |
219 | (when (file-exists-p custom-file) | |
220 | (load custom-file)) | |
bab98ee5 AB |
221 | #+end_src |
222 | ||
1446ab91 AB |
223 | ** Better =$PATH= handling |
224 | ||
225 | Let's use [[https://github.com/purcell/exec-path-from-shell][exec-path-from-shell]] to make Emacs use the =$PATH= as set up | |
226 | in my shell. | |
227 | ||
228 | #+begin_src emacs-lisp | |
229 | (use-package exec-path-from-shell | |
230 | :defer 1 | |
231 | :init | |
232 | (setq exec-path-from-shell-check-startup-files nil) | |
233 | :config | |
234 | (exec-path-from-shell-initialize) | |
235 | ;; while we're at it, let's fix access to our running ssh-agent | |
236 | (exec-path-from-shell-copy-env "SSH_AGENT_PID") | |
237 | (exec-path-from-shell-copy-env "SSH_AUTH_SOCK")) | |
238 | #+end_src | |
239 | ||
240 | ** Server | |
241 | ||
242 | Start server if not already running. Alternatively, can be done by | |
243 | issuing =emacs --daemon= in the terminal, which can be automated with | |
244 | a systemd service or using =brew services start emacs= on macOS. I use | |
245 | Emacs as my window manager (via =exwm=), so I always start Emacs on | |
246 | login; so starting the server from inside Emacs is good enough for me. | |
247 | ||
248 | See [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Server.html#Emacs-Server][Using Emacs as a Server]]. | |
249 | ||
250 | #+begin_src emacs-lisp | |
251 | (require 'server) | |
252 | (unless (server-running-p) | |
253 | (server-start)) | |
254 | #+end_src | |
255 | ||
1446ab91 AB |
256 | * Core |
257 | :PROPERTIES: | |
258 | :CUSTOM_ID: core | |
259 | :END: | |
260 | ||
261 | ** Defaults | |
262 | ||
263 | *** Disable disabled commands | |
264 | ||
265 | Emacs disables some commands by default that could persumably be | |
266 | confusing for novice users. Let's disable that. | |
267 | ||
268 | #+begin_src emacs-lisp | |
269 | (setq disabled-command-function nil) | |
270 | #+end_src | |
271 | ||
272 | *** Kill-ring | |
273 | ||
274 | Save what I copy into clipboard from other applications into Emacs' | |
275 | kill-ring, which would allow me to still be able to easily access it | |
276 | in case I kill (cut or copy) something else inside Emacs before | |
277 | yanking (pasting) what I'd originally intended to. | |
278 | ||
279 | #+begin_src emacs-lisp | |
280 | (setq save-interprogram-paste-before-kill t) | |
281 | #+end_src | |
282 | ||
283 | *** Keep more =*Messages*= | |
284 | ||
285 | #+begin_src emacs-lisp | |
286 | (setq message-log-max 10000) | |
287 | #+end_src | |
288 | ||
289 | *** Lazy-person-friendly yes/no prompts | |
290 | ||
291 | Lazy people would prefer to type fewer keystrokes, especially for yes | |
292 | or no questions. I'm lazy. | |
293 | ||
294 | #+begin_src emacs-lisp | |
295 | (defalias 'yes-or-no-p #'y-or-n-p) | |
296 | #+end_src | |
297 | ||
298 | *** =*scratch*= | |
299 | ||
300 | Let's customize the =*scratch*= buffer a bit. First off, I don't need | |
301 | the default hint. | |
302 | ||
303 | #+begin_src emacs-lisp | |
304 | (setq initial-scratch-message "") | |
305 | #+end_src | |
306 | ||
307 | Also, let's use Text mode as the major mode, in case I want to | |
308 | customize it (=*scratch*='s default major mode, Fundamental mode, | |
309 | can't really be customized). | |
310 | ||
311 | #+begin_src emacs-lisp | |
312 | (setq initial-major-mode 'text-mode) | |
313 | #+end_src | |
314 | ||
315 | *** More useful frame titles | |
316 | ||
317 | Show either the file name or the buffer name (in case the buffer isn't | |
318 | visiting a file). Borrowed from Emacs Prelude. | |
319 | ||
320 | #+begin_src emacs-lisp | |
321 | (setq frame-title-format | |
322 | '("" invocation-name " - " | |
323 | (:eval (if (buffer-file-name) | |
324 | (abbreviate-file-name (buffer-file-name)) | |
325 | "%b")))) | |
326 | #+end_src | |
327 | ||
056eba24 AB |
328 | ** Backups |
329 | ||
330 | Emacs' default backup settings aren't that great. Let's use more | |
331 | sensible options. See documentation for the ~make-backup-file~ | |
332 | variable. | |
333 | ||
334 | #+begin_src emacs-lisp | |
335 | (setq backup-by-copying t | |
336 | version-control t) | |
337 | #+end_src | |
338 | ||
abdc6c07 | 339 | * Config |
279a98e2 AB |
340 | :PROPERTIES: |
341 | :CUSTOM_ID: config | |
342 | :END: | |
abdc6c07 | 343 | |
bab98ee5 AB |
344 | ** Org |
345 | ||
346 | #+begin_src emacs-lisp | |
347 | (setq org-src-tab-acts-natively t | |
348 | org-src-preserve-indentation nil | |
349 | org-edit-src-content-indentation 0) | |
350 | #+end_src | |
351 | ||
35ea1ba4 | 352 | * Footer |
279a98e2 AB |
353 | :PROPERTIES: |
354 | :CUSTOM_ID: footer | |
355 | :END: | |
35ea1ba4 AB |
356 | |
357 | #+begin_src emacs-lisp :comments none | |
35ea1ba4 AB |
358 | ;;; init.el ends here |
359 | #+end_src |