]> git.elephly.net Git - software/emacs.git/blob - init.org
paredit --> smartparens.
[software/emacs.git] / init.org
1 #+Title: Emacs Customizations
2 #+Author: Ricardo Wurmus
3
4 #+PROPERTY: noweb tangle
5 #+PROPERTY: mkdirp t
6 #+OPTIONS: tasks:nil toc:1
7
8 * Introduction
9
10 My Emacs configuration is a mess. As I’m writing this my Emacs configuration stretches across multiple files, each containing various snippets of code that seemed like a good idea to group.
11
12 Unfortunately, there are a some things that don’t have a “natural” home. Enabling the same minor mode in various major modes is one of these cases—do I duplicate the hook and place it in a file for each major mode? Or do I write a new file for the minor mode in which I add it to the major modes at once?
13
14 With multiple files I spend too much time trying to find the best place for any bit of configuration I add. This slows me down and sometimes I just append to the main =init.el=, so I often feel that my configuration is in need of reorganisation. But configuring Emacs should be fun! I don’t want it to create an uncomfortable clean-up task as a side-effect. This is why I’m now trying to use a literate approach with =org-mode=. My Emacs configuration should be prose first and code second. In my experience, finding the right spot in prose for a new paragraph requires a lot less effort as the text itself acts as a connection between unrelated bits of code.
15
16 * How to use this?
17 :PROPERTIES:
18 :header-args: :noweb-ref compile-init
19 :END:
20
21 We take all code blocks in this file and assemble an =init.el= from it if the source file =init.org= is younger. At startup time we check if the =init.el= has to be regenerated. To get started you need to have an =init.el= with at least these contents.
22
23 #+BEGIN_SRC elisp
24 (let ((orgfile (expand-file-name (concat user-emacs-directory "init.org")))
25 (target (expand-file-name (concat user-emacs-directory "init.el"))))
26 (when (not (file-newer-than-file-p target orgfile))
27 (progn
28 (require 'org)
29 (org-babel-tangle-file orgfile)
30 (byte-compile-file target)
31 (load target))))
32 #+END_SRC
33
34 * Initialise packages
35 :PROPERTIES:
36 :header-args: :noweb-ref packages
37 :END:
38
39 Emacs is an operating system and I use it as such (see [[http://elephly.net/posts/2016-02-14-ilovefs-emacs.html][this blog post]]). I rely on quite a few extensions that have been made available on various ELPA repositories. Recently, I have moved to installing and managing Emacs packages like any other software package on my system with the functional package manager [[https://gnu.org/s/guix][GNU Guix]]. I find this more reliable, although at first it is slightly less convenient as I can no longer just use =package.el= but first need to package the Elisp code for Guix.
40
41 To install all packages via GNU Guix I can either use a manifest file or use the following invocation:
42
43 #+BEGIN_SRC shell :noweb-ref nil
44 guix install \
45 emacs \
46 emacs-ag \
47 emacs-all-the-icons \
48 emacs-all-the-icons-dired \
49 emacs-auctex \
50 emacs-company \
51 emacs-debbugs \
52 emacs-ess \
53 emacs-expand-region \
54 emacs-fill-column-indicator \
55 geiser \
56 emacs-god-mode \
57 emacs-guix \
58 emacs-lispy \
59 emacs-markdown-mode \
60 emacs-mmm-mode \
61 emacs-multiple-cursors \
62 emacs-olivetti \
63 emacs-org \
64 emacs-org-bullets \
65 emacs-org-trello \
66 emacs-page-break-lines \
67 emacs-paren-face \
68 emacs-pdf-tools \
69 emacs-perspective \
70 emacs-projectile \
71 emacs-rich-minority \
72 emacs-shell-switcher \
73 emacs-skewer-mode \
74 emacs-smart-mode-line \
75 emacs-solarized-theme \
76 emacs-tagedit \
77 emacs-typo \
78 emacs-undo-tree \
79 emacs-web-mode \
80 emacs-wget \
81 emacs-znc \
82 emacs-smartparens \
83 magit \
84 sicp \
85 mu
86 #+END_SRC
87
88 If there are packages that are not yet packaged for Guix, I can define them in this list:
89
90 #+BEGIN_SRC elisp
91 (defvar my/packages '())
92 #+END_SRC
93
94 I want these packages to be installed automatically. First I need to define in what repositories Emacs should look for the packages. I’m using packages from both the “melpa” and “marmalade” repositories.
95
96 #+BEGIN_SRC elisp
97 (require 'package)
98 (add-to-list 'package-archives
99 '("melpa" . "http://melpa.milkbox.net/packages/"))
100 (package-initialize)
101 #+END_SRC
102
103 If this is a fresh Emacs installation melpa needs to be initialised first:
104
105 #+BEGIN_SRC elisp
106 (unless (file-exists-p "~/.emacs.d/elpa/archives/melpa")
107 (package-refresh-contents))
108 #+END_SRC
109
110 Now we are ready to install packages if they aren’t yet installed.
111
112 #+BEGIN_SRC elisp
113 (defun packages-install (packages)
114 (mapc (lambda (package)
115 (when (not (package-installed-p package))
116 (package-install package)))
117 packages)
118 (delete-other-windows))
119
120 (defun init--install-packages ()
121 (packages-install my/packages))
122 #+END_SRC
123
124 Install packages as soon as this configuration is evaluated. If there’s an error (e.g. because a package by this name cannot be found) ask Emacs to refresh the list of packages and retry. If there’s an error again we just ignore it. It could be that it’s because there’s no Internet connection.
125
126 #+BEGIN_SRC elisp
127 (condition-case e
128 (init--install-packages)
129 (error
130 (ignore-errors
131 (package-refresh-contents)
132 (init--install-packages))))
133 #+END_SRC
134
135 * Better defaults
136 :PROPERTIES:
137 :header-args: :noweb-ref better-defaults
138 :END:
139
140 Emacs defaults are hostile to most people. They are what kept me from using Emacs for many years.
141
142 I’m easily confused by the way the cursor (point) keeps jumping around when scrolling by pages. Let the cursor keep its screen position constant even when scrolling by full screens and don’t jump around when scrolling.
143
144 #+BEGIN_SRC elisp
145 (setq scroll-margin 7
146 scroll-step 1
147 scroll-conservatively 10000
148 scroll-preserve-screen-position 1)
149 #+END_SRC
150
151 Here are a few more simple tweaks:
152
153 #+BEGIN_SRC elisp
154 ;; No splash screen please ...
155 (setq inhibit-startup-message t)
156
157 ;; display tool tips in echo area only
158 (tooltip-mode -1)
159
160 ;; by default Emacs will only resize the frame line by line
161 (setq frame-resize-pixelwise t)
162
163 ;; don’t force me to input “yes” or “no”
164 (defalias 'yes-or-no-p 'y-or-n-p)
165
166 ;; disable mouse scrolling
167 (mouse-wheel-mode -1)
168 #+END_SRC
169
170 Tell Emacs to look for my custom code in the =lisp= directory. This will not be needed in the future once everything is found in this Org document.
171
172 #+BEGIN_SRC elisp
173 (add-to-list 'load-path (concat user-emacs-directory "lisp"))
174 #+END_SRC
175
176 Also tell Emacs that I want to have a separate file for all other customisations that are handled through =M-x customize=.
177
178 #+BEGIN_SRC elisp
179 ;; Keep emacs Custom-settings in separate file
180 (setq custom-file (expand-file-name "custom.el" user-emacs-directory))
181 (load custom-file)
182 #+END_SRC
183
184 Emacs doesn’t deal well with very long lines. I often produce very long lines when hacking Scheme using Geiser, which regularly kills my session. The following snippet shortens long lines in =comint=-derived modes such as Geiser. This code was provided by wasamasa as [[http://emacs.stackexchange.com/a/5559/2005][an answer to my question on the Emacs StackExchange]].
185
186 #+BEGIN_SRC elisp
187 (defun my/comint-shorten-long-lines (text)
188 ;; Use \x22 here instead of straight double quotes because that would
189 ;; break the regular expression for prettifying Org src blocks.
190 (let* ((regexp "^\\(.\\{380\\}\\).*?\\(\x22?\\)$")
191 (shortened-text (replace-regexp-in-string regexp "\\1\\2" text)))
192 (if (string= shortened-text text)
193 text
194 (propertize shortened-text 'font-lock-face 'shadow 'help-echo text))))
195
196 (add-hook 'comint-preoutput-filter-functions 'my/comint-shorten-long-lines)
197 #+END_SRC
198
199 * Theme
200 :PROPERTIES:
201 :header-args: :noweb-ref theme
202 :END:
203
204 Currently, I’m a fan of themes with muted colours. The =tao-yang-theme= is a light theme with very few colours.
205
206 #+BEGIN_SRC elisp
207 (setq frame-background-mode 'light)
208 (require 'solarized)
209 (load-theme 'solarized-light t)
210 #+END_SRC
211
212 Dired mode becomes much prettier with =all-the-icons=.
213
214 #+BEGIN_SRC elisp
215 (add-hook 'dired-mode-hook 'all-the-icons-dired-mode)
216 #+END_SRC
217
218 * Default fonts
219 :PROPERTIES:
220 :header-args: :noweb-ref default-fonts
221 :END:
222
223 I like pretty faces. For coding I like to use the DejaVu Sans Mono font. In =org-mode= and in =eww= I like to use a font with variable pitch instead of the default mono-spaced font. I find Linux Biolinum pretty, especially when it’s rendered large.
224
225 #+BEGIN_SRC elisp
226 (set-frame-font "DejaVu Sans Mono")
227 (set-face-attribute 'variable-pitch nil :height 1.25 :family "Linux Biolinum")
228 #+END_SRC
229
230 * Guix
231 :PROPERTIES:
232 :header-args: :noweb-ref guix
233 :END:
234
235 Store paths have long hashes. In most cases I don’t really care, so I use =guix-prettify-mode= to hide them.
236
237 #+BEGIN_SRC elisp
238 (when (require 'guix-prettify nil t)
239 (global-guix-prettify-mode))
240 #+END_SRC
241
242 I’m often building Guix packages in the shell. =guix-build-log-minor-mode= gives me key bindings to fold and jump over build phases, and it adds pretty faces to the otherwise bland wall of text.
243
244 #+BEGIN_SRC elisp
245 (add-hook 'shell-mode-hook 'guix-build-log-minor-mode)
246 #+END_SRC
247
248 I’m monitoring the Guix build farm =berlin.guixsd.org=, which is hosted at the MDC.
249
250 #+BEGIN_SRC elisp
251 (setq guix-hydra-url "https://berlin.guixsd.org")
252 #+END_SRC
253
254 For bug and patch tracking the Guix project uses debbugs. Here are some better defaults for using =debbugs-gnu= with Guix:
255
256 #+BEGIN_SRC elisp
257 (eval-when-compile
258 (require 'debbugs-gnu))
259 (with-eval-after-load "debbugs-gnu"
260 (setq debbugs-gnu-default-packages '("guix" "guix-patches"))
261 (add-to-list 'debbugs-gnu-all-packages "guix-patches"))
262 #+END_SRC
263
264 Oleg Pykhalov shared this useful snippet to list bugs for which I am listed as the owner.
265
266 #+BEGIN_SRC elisp
267 (with-eval-after-load "debbugs-gnu"
268 (defun my/debbugs-gnu ()
269 (interactive)
270 (let ((debbugs-gnu-current-query `((submitter . ,user-mail-address))))
271 (debbugs-gnu nil))))
272 #+END_SRC
273
274 When working on Guix it helps to reduce boilerplate with snippets. I like to have YASnippet enabled and let it use the snippets that are provided with Guix:
275
276 #+BEGIN_SRC elisp
277 (require 'yasnippet)
278 (add-to-list 'yas-snippet-dirs "~/dev/gx/branches/master/etc/snippets")
279 (yas-global-mode)
280 #+END_SRC
281
282 * Manuals
283 :PROPERTIES:
284 :header-args: :noweb-ref manuals
285 :END:
286
287 Also in Info manuals I want to use variable-pitch fonts where possible. Unfortunately, Info manuals don’t contain enough semantic markup, so I cannot selectively use a monospace font for examples or inline code and use a variable pitch font for everything else. So I just use variable pitch in headings.
288
289 #+BEGIN_SRC elisp
290 (require 'info)
291 (set-face-attribute 'info-title-1 nil
292 :inherit 'variable-pitch
293 :height 1.3)
294 (set-face-attribute 'info-title-2 nil
295 :inherit 'variable-pitch
296 :height 1.3)
297 (set-face-attribute 'info-title-3 nil
298 :inherit 'variable-pitch
299 :height 1.3)
300 (set-face-attribute 'info-menu-header nil
301 :inherit 'variable-pitch
302 :height 1.1)
303 #+END_SRC
304
305 Since Emacs 25 there is a new face for quoted expressions in Info manuals. By default it uses the “courier” font, which looks terrible.
306
307 #+BEGIN_SRC elisp
308 (set-face-attribute 'Info-quoted nil
309 :inherit 'fixed-pitch
310 :family "Monospace")
311 #+END_SRC
312
313 * Org-mode
314 :PROPERTIES:
315 :header-args: :noweb-ref org-mode :noweb yes
316 :END:
317
318 This is my org mode configuration. Much of it is in one big blob and I haven’t yet taken the time to document it.
319
320 #+BEGIN_SRC elisp
321 (require 'org-indent)
322 (setq org-ellipsis "⤵")
323 (setq org-src-fontify-natively t)
324
325 (global-set-key (kbd "C-c o l") 'org-store-link)
326 (global-set-key (kbd "C-c o a") 'org-agenda)
327
328 ;; TODO: make these available in org-mode only
329 (global-set-key (kbd "C-c o s") 'org-schedule)
330
331 (setq org-log-done t)
332 (setq org-return-follows-link t)
333
334 (setq org-directory "~/Documents/org")
335 (setq org-agenda-files (mapcar (lambda (x) (concat org-directory x))
336 (list "/master.org"
337 "/work.org"
338 "/email.org"
339 "/study.org"
340 "/home.org")))
341 (setq org-default-notes-file (concat org-directory "/notes.org"))
342
343 (setq org-agenda-custom-commands
344 '(("w" todo "WAITING" nil)
345 ("n" todo "NEXT" nil)
346 ("d" "Agenda + Next Actions" ((agenda) (todo "NEXT")))))
347
348 (setq org-fontify-done-headline t)
349
350 (require 'org-bullets)
351 (setq org-bullets-bullet-list '("◉" "○" "◇" "◇"))
352 (add-hook 'org-mode-hook
353 (lambda ()
354 (org-bullets-mode 1)
355 (variable-pitch-mode 1)
356 (visual-line-mode 1)))
357
358 (defun gtd ()
359 (interactive)
360 (find-file (concat org-directory "/master.org")))
361 #+END_SRC
362
363 I don’t like the way org-mode looks by default. It’s noisy, too colourful and seems old-fashioned to the point of being somewhat unattractive. This is why I find it important to change some of the default faces.
364
365 Since I enable =variable-pitch-mode= in my org-mode buffers I also need to explicitly make a few faces inherit from the =fixed-width= face to be rendered with a monospaced font.
366
367 #+BEGIN_SRC elisp
368 (set-face-attribute 'org-done nil :strike-through t)
369 (set-face-attribute 'org-headline-done nil
370 :strike-through t
371 :foreground "light gray")
372 (set-face-attribute 'org-document-title nil
373 :height 1.6
374 :foreground (face-attribute 'default :foreground))
375 (set-face-attribute 'org-level-1 nil
376 :height 1.0
377 :foreground (face-attribute 'default :foreground))
378 (set-face-attribute 'org-level-2 nil
379 :height 0.9
380 :foreground (face-attribute 'default :foreground))
381 (set-face-attribute 'org-level-3 nil
382 :height 0.8
383 :foreground (face-attribute 'default :foreground))
384 (set-face-attribute 'org-level-4 nil
385 :height 0.8
386 :foreground (face-attribute 'default :foreground))
387
388 (set-face-attribute 'org-verbatim nil :background "#efe9d6")
389 (dolist (face '(org-meta-line
390 org-document-info-keyword
391 org-special-keyword))
392 (set-face-attribute face nil :foreground "#93a1a1"
393 :weight 'normal))
394 (dolist (face '(org-block-begin-line
395 org-block-end-line
396 org-block
397 org-table
398 org-meta-line
399 org-document-info-keyword
400 org-special-keyword
401 org-verbatim
402 org-todo
403 org-tag
404 org-done
405 org-hide
406 org-indent
407 org-checkbox))
408 (set-face-attribute face nil :inherit 'fixed-pitch)
409 ;; TODO: this is ugly. When scaling up the variable-pitch face the
410 ;; fixed-pitch face will become even larger.
411 (set-face-attribute face nil :height 0.8))
412
413 (set-face-attribute 'org-tag nil
414 :foreground (face-attribute 'default :foreground)
415 :weight 'normal
416 :height (face-attribute 'default :height)
417 :overline nil
418 :underline nil
419 :box '(:line-width -1 :color "#859900"))
420 #+END_SRC
421
422 To ensure that indented blocks line up with their headings despite using =variable-pitch-mode= we set the indentation character to =*= and hide it by setting the foreground colour to the same as the default background colour.
423
424 #+BEGIN_SRC elisp
425 (setq org-indent-boundary-char ?*)
426 (set-face-attribute 'org-indent nil :foreground (face-attribute 'default :background))
427 #+END_SRC
428
429 =variable-pitch-mode= also makes it impossible to align tags at a fixed column, so I don’t. Instead I just let tags appear right behind the heading.
430
431 #+BEGIN_SRC elisp
432 (setq org-tags-column 0)
433 #+END_SRC
434
435 The following snippet is an attempt to prettify the somewhat ugly headers of source code blocks in =org-mode=. The snippet was taken from [[https://pank.eu/blog/pretty-babel-src-blocks.html][the blog of Rasmus Pank]] and slightly modified to suit my needs.
436
437 #+BEGIN_SRC elisp
438 (defvar-local my/org-at-src-begin -1
439 "Variable that holds whether last position was an org source code block.")
440
441 (defvar-local my/org-src-begin-regexp
442 "^[ \t]*#\\+begin_src[ \t]+[^ \f\t\n\r\v]+[ \t]*")
443
444 (defvar my/ob-header-symbol ?☰
445 "Symbol used for babel headers")
446
447 (defun my/org-prettify-src--update ()
448 (let ((case-fold-search t)
449 (re my/org-src-begin-regexp)
450 found)
451 (save-excursion
452 (goto-char (point-min))
453 (while (re-search-forward re nil t)
454 (goto-char (match-end 0))
455 (let ((args (org-trim
456 (buffer-substring-no-properties (point)
457 (line-end-position)))))
458 (when (org-string-nw-p args)
459 (let ((new-cell (cons args my/ob-header-symbol)))
460 (cl-pushnew new-cell prettify-symbols-alist :test #'equal)
461 (cl-pushnew new-cell found :test #'equal)))))
462 (setq prettify-symbols-alist
463 (cl-set-difference prettify-symbols-alist
464 (cl-set-difference
465 (cl-remove-if-not
466 (lambda (elm)
467 (eq (cdr elm) my/ob-header-symbol))
468 prettify-symbols-alist)
469 found :test #'equal)))
470 ;; Clean up old font-lock-keywords.
471 (font-lock-remove-keywords nil prettify-symbols--keywords)
472 (setq prettify-symbols--keywords (prettify-symbols--make-keywords))
473 (font-lock-add-keywords nil prettify-symbols--keywords)
474 (while (re-search-forward re nil t)
475 (font-lock-flush (line-beginning-position) (line-end-position)))
476 ;; Toggle prettify-symbols-mode to restore composition of
477 ;; regions on which the "composition" text-property was deleted.
478 (prettify-symbols-mode -1)
479 (prettify-symbols-mode +1))))
480
481 (defun my/org-prettify-src ()
482 "Hide src options via `prettify-symbols-mode'.
483
484 `prettify-symbols-mode' is used because expanding is simple. It’s
485 not very efficient and maybe should be implemented using overlays."
486 (let* ((case-fold-search t)
487 (at-src-block (save-excursion
488 (beginning-of-line)
489 (looking-at my/org-src-begin-regexp))))
490 ;; Test if we moved out of a block.
491 (cond ((or (and my/org-at-src-begin
492 (not at-src-block))
493 ;; File was just opened.
494 (eq my/org-at-src-begin -1))
495 (my/org-prettify-src--update))
496 ;; Remove composition if at line
497 (at-src-block
498 (with-silent-modifications
499 (remove-text-properties (match-end 0)
500 (1+ (line-end-position))
501 '(composition)))))
502 (setq my/org-at-src-begin at-src-block)))
503
504 (defun my/org-prettify-symbols ()
505 (mapc (apply-partially 'add-to-list 'prettify-symbols-alist)
506 (cl-reduce 'append
507 (mapcar (lambda (x) (list x (cons (upcase (car x)) (cdr x))))
508 `(("#+begin_src" . ?✎) ;; ➤ 🖝 ➟ ➤ ✎
509 ("#+end_src" . ?□) ;; ⏹
510 ("#+header:" . ,my/ob-header-symbol)
511 ("#+begin_quote" . ?«)
512 ("#+end_quote" . ?»)))))
513 (turn-on-prettify-symbols-mode)
514 (add-hook 'post-command-hook 'my/org-prettify-src t t))
515 (add-hook 'org-mode-hook #'my/org-prettify-symbols)
516 #+END_SRC
517
518 I use the capture feature to quickly record ideas and tasks, and to create links to emails that I need to work on. This is much better than just leaving these emails in my Inbox.
519
520 #+BEGIN_SRC elisp
521 (global-set-key (kbd "C-c o c") 'org-capture)
522 (setq org-capture-templates
523 '(("t" "Task" entry
524 (file+headline (concat org-directory "/home.org") "Tasks")
525 "* %?\n %i\n %a")
526 ("m" "Email" entry
527 (file+headline (concat org-directory "/email.org") "Email")
528 "* Reply to %:fromname%? :email:\n [%:date]\n To: %:to\n %a")))
529 #+END_SRC
530
531 For exporting org documents to PDF I use =lualatex= instead of the default =pdflatex=. I also use =biber= to refresh the bibliography. This requires me to change the value of =org-latex-pdf-process=.
532
533 #+BEGIN_SRC elisp
534 (setq org-latex-pdf-process
535 '("lualatex -interaction nonstopmode -output-directory %o %f"
536 "biber %b"
537 "lualatex -interaction nonstopmode -output-directory %o %f"
538 "lualatex -interaction nonstopmode -output-directory %o %f"))
539 #+END_SRC
540
541 Org mode is an excellent environment for literate programming through Babel. Here I configure a couple of common languages to be used with Babel.
542
543 #+BEGIN_SRC elisp
544 (require 'ob-shell)
545 (require 'ob-scheme)
546 (require 'ob-R)
547 #+END_SRC
548
549 After editing source snippets with =C-c '= please don’t indent everything with two spaces. Just leave things as I edited them.
550
551 #+BEGIN_SRC elisp
552 (setq org-edit-src-content-indentation 0)
553 #+END_SRC
554
555 All of this should be loaded lazily.
556
557 #+BEGIN_SRC elisp :noweb-ref org-mode-lazy
558 (with-eval-after-load "org"
559 <<org-mode>>
560 )
561 #+END_SRC
562
563 * Editing files on remote systems
564 :PROPERTIES:
565 :header-args: :noweb-ref tramp
566 :END:
567
568 TRAMP is a really convenient way to edit files on remote systems from within the comfort of my cozy customised local Emacs session. I use it to edit files at work, to edit things on my server =elephly.net=, and even to edit things as root on the local system.
569
570 #+BEGIN_SRC elisp
571 (require 'tramp)
572 (setq tramp-default-method "ssh")
573
574 (setq tramp-default-proxies-alist
575 (list
576 ;; Do not use a proxy on the same system.
577 '((regexp-quote (system-name)) nil nil)
578 ;; For root connections to remote hosts, log in via ssh with normal
579 ;; user account first, then su/sudo to root
580 '("elephly\\.net\\'" "\\`root\\'" "/ssh:%h:")
581 ;; Pass through ssh1 as user ‘rwurmus’ to reach remote hosts on
582 ;; MDC network.
583 '("mdc-berlin\\.net" "\\`rwurmus\\'" "/ssh:rwurmus@ssh1.mdc-berlin.de:")))
584
585 ;; ssh1 runs a restricted shell session, so "exec ssh" cannot be used.
586 (add-to-list 'tramp-restricted-shell-hosts-alist
587 "\\`ssh1\\.mdc-berlin\\.de\\'")
588
589 ;; respect the PATH variable on the remote machine
590 (add-to-list 'tramp-remote-path 'tramp-own-remote-path)
591 (setq tramp-verbose 3)
592 #+END_SRC
593
594 I’m not using this yet. I’d like to figure out how to make TRAMP use =ssh1= as a proxy only when I’m not connected to the institute’s network.
595
596 #+BEGIN_SRC elisp :noweb-ref nil
597 (defun at-work-p ()
598 "Tell me if I’m connected to the network at work."
599 (with-temp-buffer
600 (call-process "ip" nil t nil "route" "list" "root" "141.80/16")
601 ;; There is no output when there is no route with this prefix.
602 (not (equalp (point-min) (point-max)))))
603 #+END_SRC
604
605 * Shell
606 :PROPERTIES:
607 :header-args: :noweb-ref shell :noweb yes
608 :END:
609
610 I used to like Eshell a lot. Eshell is a shell implemented in Elisp. It is well integrated with the rest of Emacs. For example, you can pipe commands to buffers and use TRAMP paths right on the command line. Nowadays I’m no longer using it much because for most purposes =shell-mode= is more mature. Nevertheless, here is some configuration to make Eshell a little more usable.
611
612 #+BEGIN_SRC elisp :noweb-ref eshell
613 (require 'eshell)
614 (setq eshell-history-size 10000)
615
616 ;; author: KaiGrossjohann on EmacsWiki
617 (defun eshell/ff (&rest args)
618 "Invoke `find-file' on the file.
619 \"ff +42 foo\" also goes to line 42 in the buffer."
620 (while args
621 (if (string-match "\\`\\+\\([0-9]+\\)\\'" (car args))
622 (let* ((line (string-to-number (match-string 1 (pop args))))
623 (file (pop args)))
624 (find-file file)
625 (forward-line (1- line)))
626 (find-file (pop args)))))
627
628 ;; convenience functions to input the remote root/home dir when in a
629 ;; directory on a remote host
630 (defun my/tramp-root ()
631 "Print root directory on the remote host."
632 (interactive)
633 (let ((pieces (split-string (eshell/pwd) ":/")))
634 (insert (if (> (length pieces) 1)
635 (concat (car pieces) ":/")
636 "/"))))
637
638 (defun my/tramp-home ()
639 "Print home directory path on the remote host."
640 (interactive)
641 (let ((pieces (split-string (eshell/pwd) ":/")))
642 (insert (if (> (length pieces) 1)
643 (concat (car pieces) ":~/")
644 "~/"))))
645
646 (define-key eshell-mode-map (kbd "C-c /") 'my/tramp-root)
647 (define-key eshell-mode-map (kbd "C-c ~") 'my/tramp-home)
648 #+END_SRC
649
650 Of course, all of this should only be loaded when Eshell is used.
651
652 #+BEGIN_SRC elisp
653 (with-eval-after-load "eshell"
654 <<eshell>>
655 )
656 #+END_SRC
657
658 TODO: here’s the rest of my shell configuration:
659
660 #+BEGIN_SRC elisp
661 (require 'shell-switcher)
662 (setq shell-switcher-mode t)
663 (add-hook 'eshell-mode-hook 'shell-switcher-manually-register-shell)
664 (add-hook 'shell-mode-hook 'shell-switcher-manually-register-shell)
665 (setq shell-switcher-new-shell-function 'shell-switcher-make-shell)
666
667 ;; use cat as the pager in shell mode, because shell-mode is not an
668 ;; ANSI terminal
669 (setenv "PAGER" "cat")
670
671 ;; C-d on an empty line in the shell terminates the process.
672 (defun my/comint-delchar-or-eof-or-kill-buffer (arg)
673 (interactive "p")
674 (if (null (get-buffer-process (current-buffer)))
675 (kill-buffer)
676 (comint-delchar-or-maybe-eof arg)))
677
678 (add-hook 'shell-mode-hook
679 (lambda ()
680 ;; needed for proper display of "ls"
681 (setq tab-width 8)
682
683 ;; load shared bash history
684 (setq comint-input-ring-file-name "~/.bash_history")
685 (comint-read-input-ring t)
686
687 (define-key shell-mode-map
688 (kbd "C-d") 'my/comint-delchar-or-eof-or-kill-buffer)
689 (define-key shell-mode-map
690 (kbd "<up>") 'comint-previous-matching-input-from-input)))
691
692 ;; Show current path instead of just "*shell*<2>"
693 (setq uniquify-buffer-name-style 'forward)
694 (require 'uniquify)
695 #+END_SRC
696
697 * Magit
698 :PROPERTIES:
699 :header-args: :noweb-ref magit :noweb yes
700 :END:
701
702 #+BEGIN_SRC elisp
703 ;; full screen magit-status
704 (defadvice magit-status (around magit-fullscreen activate)
705 (window-configuration-to-register :magit-fullscreen)
706 ad-do-it
707 (delete-other-windows))
708
709 (defun my/magit-quit-session ()
710 "Restores the previous window configuration and kills the magit buffer"
711 (interactive)
712 (kill-buffer)
713 (jump-to-register :magit-fullscreen))
714
715 (defun my/magit-toggle-whitespace ()
716 "Toggles git option -w"
717 (interactive)
718 (if (member "-w" magit-diff-arguments)
719 (my/magit-dont-ignore-whitespace)
720 (my/magit-ignore-whitespace)))
721
722 (defun my/magit-ignore-whitespace ()
723 "Adds git option -w"
724 (interactive)
725 (add-to-list 'magit-diff-arguments "-w")
726 (magit-refresh))
727
728 (defun my/magit-dont-ignore-whitespace ()
729 "Removes git option -w"
730 (interactive)
731 (setq magit-diff-arguments (remove "-w" magit-diff-arguments))
732 (magit-refresh))
733
734 (define-key magit-status-mode-map (kbd "q") 'my/magit-quit-session)
735 (define-key magit-status-mode-map (kbd "W") 'my/magit-toggle-whitespace)
736
737 (setq magit-diff-refine-hunk 'all)
738 #+END_SRC
739
740 #+BEGIN_SRC elisp :noweb-ref magit-lazy
741 (eval-when-compile
742 (require 'magit))
743 (with-eval-after-load "magit"
744 <<magit>>
745 )
746 (global-set-key (kbd "C-c m") 'magit-status)
747 #+END_SRC
748
749 * Completion
750 :PROPERTIES:
751 :header-args: :noweb-ref completion :noweb yes
752 :END:
753
754 Company mode provides automatic completion. I like to enable it in all programming modes. I don’t use the global company mode because that would enable it in =org-mode= where the pop-up looks terrible with =variable-pitch-mode= enabled.
755
756 #+BEGIN_SRC elisp :noweb-ref company-hook
757 (add-hook 'prog-mode-hook 'company-mode)
758 #+END_SRC
759
760 I like automatic completion, but it’s nice to also have a key to trigger completion.
761
762 #+BEGIN_SRC elisp
763 (setq company-idle-delay 0.5)
764 (define-key company-mode-map (kbd "C-c <tab>") 'company-complete)
765 #+END_SRC
766
767 #+BEGIN_SRC elisp
768 (require 'color)
769 (let ((bg (face-attribute 'default :background)))
770 (custom-set-faces
771 `(company-tooltip ((t (:inherit default :background ,(color-lighten-name bg 2)))))
772 `(company-scrollbar-bg ((t (:background ,(color-lighten-name bg 10)))))
773 `(company-scrollbar-fg ((t (:background ,(color-lighten-name bg 5)))))
774 `(company-tooltip-selection ((t (:inherit font-lock-function-name-face))))
775 `(company-tooltip-common ((t (:inherit font-lock-constant-face))))))
776 #+END_SRC
777
778 I also use snippets for commonly typed expressions.
779
780 #+BEGIN_SRC elisp :noweb-ref yas
781 (yas-global-mode 1)
782 #+END_SRC
783
784 Load all of this lazily.
785
786 #+BEGIN_SRC elisp :noweb-ref completion-lazy
787 (with-eval-after-load "company"
788 <<completion>>
789 )
790 <<company-hook>>
791
792 (with-eval-after-load "yasnippet"
793 <<yas>>
794 )
795 (require 'yasnippet)
796 #+END_SRC
797
798 * Pretty symbols
799 :PROPERTIES:
800 :header-args: :noweb-ref pretty-symbols
801 :END:
802
803 #+BEGIN_SRC elisp
804 (defun my/pretty-js-symbols ()
805 (push '("===" . ?≡) prettify-symbols-alist)
806 (push '("function" . ?𝑓) prettify-symbols-alist))
807
808 (defun my/pretty-r-symbols ()
809 (push '("%>%" . ?⤚) prettify-symbols-alist)
810 (push '("%$%" . ?⤜) prettify-symbols-alist)
811 (push '("==" . ?≡) prettify-symbols-alist)
812 (push '("function" . ?𝑓) prettify-symbols-alist))
813
814 (when (boundp 'global-prettify-symbols-mode)
815 (add-hook 'js2-mode-hook 'my/pretty-js-symbols)
816 (add-hook 'ess-mode-hook 'my/pretty-r-symbols)
817 (add-hook 'inferior-ess-mode-hook 'my/pretty-r-symbols)
818 (global-prettify-symbols-mode +1))
819 #+END_SRC
820
821 * Resize buffer margins dynamically
822 :PROPERTIES:
823 :header-args: :noweb-ref resize-dynamically
824 :END:
825
826 I don’t want to have any margins by default.
827
828 #+BEGIN_SRC elisp
829 (setq-default left-margin-width 0 right-margin-width 0)
830 #+END_SRC
831
832 When writing Org-mode documents or when browsing the web with Eww I prefer to see shorter lines. =olivetti-mode= adjusts the buffer margins such that the buffer contents are restricted in width and centered. I find this much more readable when editing Org documents or browsing with Eww.
833
834 #+BEGIN_SRC elisp
835 (require 'olivetti)
836 (add-hook 'org-mode-hook #'olivetti-mode)
837 #+END_SRC
838
839 * Multimedia with EMMS
840 :PROPERTIES:
841 :header-args: :noweb-ref emms :noweb yes
842 :END:
843
844 Not EMMS but MPC:
845
846 #+BEGIN_SRC elisp
847 (setq simple-mpc-arguments "-h 192.168.178.20")
848 #+END_SRC
849
850 * Modeline
851 :PROPERTIES:
852 :header-args: :noweb-ref modeline
853 :END:
854
855 Before setting up any modeline themes we load the IRC client ERC and
856 tell it where to place the indicator for channel activity.
857
858 #+BEGIN_SRC elisp
859 (require 'erc)
860 (setq erc-track-position-in-mode-line 'before-modes)
861 #+END_SRC
862
863 I don’t like a cluttered modeline, so I like to hide mode lighters with =rich-minority=.
864
865 #+BEGIN_SRC elisp
866 ;; clean up mode line
867 (require 'rich-minority)
868 (setq rm-whitelist '(" God" " Smartparens"))
869
870 ;; show time in modeline but not load average
871 (setq display-time-format "%H:%M")
872 (setq display-time-default-load-average nil)
873 (display-time-mode 1)
874
875 ;; show column position of point in status bar
876 (column-number-mode 1)
877
878 ;; smart mode line
879 (setq sml/theme 'respectful)
880 (sml/setup)
881 #+END_SRC
882
883
884 * Lilypond
885 :PROPERTIES:
886 :header-args: :noweb-ref lilypond
887 :END:
888
889 Activate Lilypond mode when I’m opening a Lilypond score.
890
891 #+BEGIN_SRC elisp
892 (require 'lilypond-mode)
893 (add-to-list 'auto-mode-alist '("\\.ly\\'" . LilyPond-mode))
894 #+END_SRC
895
896 Enable =subword-mode= in Lilypond files because I use CamelCase for
897 music variables.
898
899 #+BEGIN_SRC elisp
900 (add-hook 'LilyPond-mode-hook 'subword-mode)
901 #+END_SRC
902
903 I like to render Lilypond snippets in Org mode buffers. To do that I need to load the Lilypond backend first. However, I don’t think this should be enabled by default.
904
905 #+BEGIN_SRC elisp :tangle nil
906 (with-eval-after-load "org"
907 (require 'ob-lilypond))
908 #+END_SRC
909
910 * Scheme development
911 :PROPERTIES:
912 :header-args: :noweb-ref scheme :noweb yes
913 :END:
914
915 Geiser makes Scheme development really nice. It’s also used for Guix development in combination with =guix-devel-mode=, so I’m adding the Guix development directory to Guile’s load path in all Geiser sessions.
916
917 #+BEGIN_SRC elisp
918 (with-eval-after-load "geiser"
919 (setq geiser-active-implementations '(guile))
920 (setq geiser-guile-load-path '("~/dev/gx/branches/master")))
921 #+END_SRC
922
923 Automatically start =guix-devel-mode= when in =scheme-mode= because I’m likely working on Guix anyway.
924
925 #+BEGIN_SRC elisp
926 (add-hook 'scheme-mode-hook 'guix-devel-mode)
927 #+END_SRC
928
929 Parentheses don’t annoy me but I still prefer to have them fade into the background a little. This is what =paren-face-mode= does.
930
931 #+BEGIN_SRC elisp :noweb-ref paren-face-hook
932 (require 'paren-face)
933 (global-paren-face-mode 1)
934 #+END_SRC
935
936 Emacs also highlights matching parentheses, but it does so with a delay. Here I’m disabling the delay.
937
938 #+BEGIN_SRC elisp :noweb-ref show-paren-hook
939 (require 'paren)
940 (setq show-paren-delay 0)
941 (show-paren-mode 1)
942 #+END_SRC
943
944 Editing lispy languages is no fun without =smartparens= (or =paredit=), a mode to enforce balanced parentheses. Smartparens can be used not only for lispy languages but for all programming languages.
945
946 #+BEGIN_SRC elisp
947 (add-hook 'prog-mode-hook (lambda () (smartparens-mode 1)))
948 #+END_SRC
949
950 Also enable =smartparens= when editing Elisp in the minibuffer.
951
952 #+BEGIN_SRC elisp
953 ;; Enable `smartparens-mode' in the minibuffer, during `eval-expression'.
954 (defun conditionally-enable-smartparens-mode ()
955 (if (eq this-command 'eval-expression)
956 (smartparens-mode 1)))
957
958 (add-hook 'minibuffer-setup-hook 'conditionally-enable-smartparens-mode)
959 #+END_SRC
960
961 Some customisations for =smartparens=.
962
963 #+BEGIN_SRC elisp
964 (eval-when-compile
965 (require 'smartparens))
966 (with-eval-after-load "smartparens"
967 (setq smartparens-strict-mode t)
968 (sp-use-paredit-bindings))
969 #+END_SRC
970
971 TODO: the parentheses adjustments should happen only when I’m in programming mode.
972
973 #+BEGIN_SRC elisp
974 <<paren-face-hook>>
975 <<show-paren-hook>>
976 #+END_SRC
977
978 * God mode
979 :PROPERTIES:
980 :header-args: :noweb-ref god
981 :END:
982
983 #+BEGIN_SRC elisp
984 (eval-when-compile
985 (require 'god-mode))
986 (eval-after-load "god-mode"
987 '(progn
988 (defadvice god-mode-lookup-key-sequence (before my-swap-x-t)
989 "Swap ?x and ?t KEY arguments."
990 (case (ad-get-arg 0)
991 (?x (ad-set-arg 0 ?t))
992 (?t (ad-set-arg 0 ?x))))
993 (ad-activate 'god-mode-lookup-key-sequence)
994
995 (defvar original-color (frame-parameter nil 'cursor-color))
996 (defun my-update-cursor ()
997 (if god-local-mode
998 (set-cursor-color "Red")
999 (set-cursor-color original-color)))
1000
1001 (define-key god-local-mode-map (kbd ".") 'repeat)
1002
1003 (add-hook 'god-mode-enabled-hook 'my-update-cursor)
1004 (add-hook 'god-mode-disabled-hook 'my-update-cursor)
1005
1006 ;; reliably toggle god-mode for all modes
1007 (setq god-exempt-major-modes nil)
1008 (setq god-exempt-predicates nil)
1009
1010 (defun god-toggle-on-overwrite ()
1011 "Toggle god-mode on overwrite-mode."
1012 (if (bound-and-true-p overwrite-mode)
1013 (god-local-mode-pause)
1014 (god-local-mode-resume)))
1015
1016 (add-hook 'overwrite-mode-hook 'god-toggle-on-overwrite)
1017 (define-key god-local-mode-map (kbd "i") 'god-local-mode)
1018
1019 (global-set-key (kbd "C-x C-k") 'kill-this-buffer)
1020 (global-set-key (kbd "C-x C-1") 'delete-other-windows)
1021 (global-set-key (kbd "C-x C-2") 'split-window-below)
1022 (global-set-key (kbd "C-x C-3") 'split-window-right)
1023 (global-set-key (kbd "C-x C-0") 'delete-window)))
1024
1025 ;; TODO: only do this on the Thinkpad
1026 (global-set-key (kbd "<mouse-3>") 'god-mode-all)
1027 #+END_SRC
1028
1029 * Email
1030 :PROPERTIES:
1031 :header-args: :noweb-ref email :noweb yes
1032 :END:
1033
1034 TODO: this is a big blob of email configuration. Document this properly!
1035
1036 #+BEGIN_SRC elisp
1037 (require 'mu4e)
1038 (require 'org-mu4e)
1039 (require 'mu4e-contrib)
1040
1041 (setq shr-color-visible-luminance-min 30)
1042
1043 (setq mu4e-get-mail-command "offlineimap"
1044 mu4e-compose-signature-auto-include nil
1045 mu4e-compose-dont-reply-to-self t
1046 mu4e-update-interval 60
1047 mu4e-headers-include-related t)
1048
1049 (setq mu4e-use-fancy-chars t)
1050 (setq mu4e-headers-seen-mark '("" . ""))
1051 (setq mu4e-headers-unread-mark '("u" . "✉"))
1052
1053 ;; Don't update list of emails automatically. I update the list
1054 ;; manually with "g".
1055 (setq mu4e-headers-auto-update nil)
1056
1057 (setq mu4e-view-show-addresses t)
1058 (setq mu4e-hide-index-messages t)
1059 (setq mu4e-html2text-command 'mu4e-shr2text)
1060 (setq mu4e-view-show-images t)
1061
1062 ;; use imagemagick, if available
1063 (when (fboundp 'imagemagick-register-types)
1064 (imagemagick-register-types))
1065
1066 (setq mu4e-attachment-dir "~/Downloads"
1067 mu4e-compose-signature-auto-include t)
1068
1069 ;; pretty quotes!
1070 (add-hook 'message-mode-hook
1071 (lambda ()
1072 (require 'typo)
1073 (typo-mode 1)))
1074
1075 (defun my/set-mu4e-bookmarks (maildir)
1076 (let ((guix "(list:guix-devel.gnu.org OR list:bug-guix.gnu.org OR list:help-guix.gnu.org OR list:guix-sysadmin.gnu.org OR list:guix-security.gnu.org OR list:guix-patches.gnu.org OR list:guix-commits.gnu.org)")
1077 (guile "(list:guile-user.gnu.org OR list:guile-devel.gnu.org OR list:bug-guile.gnu.org)")
1078 (emacs "(list:emacs-devel.gnu.org)")
1079 (fsfe "(subject:\"Willkommen in der FSFE\" list:coordinators.lists.fsfe.org OR list:berlin.lists.fsfe.org OR list:newsletter-en@fsfeurope.org OR list:newsletter-de@fsfeurope.org)")
1080 (unread "(flag:unread AND NOT flag:trashed)")
1081 (me "(body:rekado OR body:Ricardo)"))
1082 (setq mu4e-bookmarks
1083 (list
1084 ;; TODO: don't match my own signature
1085 (list (concat me " " unread)
1086 "Mentioning me (unread)" ?R)
1087 (list (concat "maildir:\"/" maildir "/INBOX\"")
1088 "Inbox" ?i)
1089 (list "date:today..now"
1090 "Today's messages" ?t)
1091 (list "date:today..now"
1092 "Last 7 days" ?w)
1093 (list (concat "maildir:\"/" maildir "/INBOX\"" " " unread)
1094 "Unread messages" ?u)
1095 (list (concat guix " " unread)
1096 "Guix" ?1)
1097 (list (concat guile " " unread)
1098 "Guile" ?2)
1099 (list (concat fsfe " " unread)
1100 "FSFE" ?4)
1101 (list (concat emacs " " unread)
1102 "Emacs" ?7)
1103 (list (concat "maildir:\"/private/mailinglists\""
1104 " " unread
1105 " NOT " guix
1106 " NOT " guile
1107 " NOT " fsfe
1108 " NOT " emacs)
1109 "Unread list messages" ?m)
1110 (list (concat "maildir:\"/" maildir "/INBOX\" flag:flagged")
1111 "Flagged" ?f)))))
1112
1113 ; set up email sending with msmtp
1114 (setq mail-user-agent 'mu4e-user-agent)
1115 (setq mail-specify-envelope-from t)
1116 (setq mail-envelope-from 'header)
1117
1118 (setq message-kill-buffer-on-exit t)
1119 (setq message-sendmail-envelope-from 'header)
1120 (setq message-send-mail-function 'message-send-mail-with-sendmail)
1121
1122 ;;use msmtp instead of sendmail
1123 (setq sendmail-program "~/.guix-profile/bin/msmtp")
1124
1125 ;; Crypto
1126 (setq mml-secure-openpgp-encrypt-to-self t)
1127 (setq mml-secure-openpgp-sign-with-sender t)
1128
1129 (add-hook 'mu4e-compose-mode-hook
1130 (defun my/maybe-reply-encrypted ()
1131 "Encrypt automatically if parent message was also encrypted."
1132 (let ((msg mu4e-compose-parent-message))
1133 (when (and msg
1134 (or (member 'encrypted (mu4e-message-field msg :flags))
1135 (string-match "-----BEGIN PGP MESSAGE-----$"
1136 (mu4e-message-field msg :body-txt))))
1137 (mml-secure-message-sign-encrypt)))))
1138
1139 (add-hook 'mu4e-compose-mode-hook
1140 (defun my/mu4e-add-headers ()
1141 "Add some personal headers."
1142 (save-excursion
1143 (message-add-header "X-URL: https://elephly.net\n")
1144 (message-add-header "X-PGP-Key: https://elephly.net/rekado.pubkey\n")
1145 (message-add-header "X-PGP-Fingerprint: BCA6 89B6 3655 3801 C3C6 2150 197A 5888 235F ACAC"))))
1146
1147 ;; Don't open a new window to show the results of signature validation
1148 (setq epa-popup-info-window nil)
1149
1150 (require 'mu4e-actions)
1151 (add-to-list 'mu4e-view-actions
1152 '("git am" . mu4e-action-git-apply-mbox))
1153 (add-to-list 'mu4e-headers-actions
1154 '("git am" . mu4e-action-git-apply-mbox))
1155 #+END_SRC
1156
1157 I read and write email in different contexts. This is my context for private email:
1158
1159 #+BEGIN_SRC elisp
1160 (setq my/mu4e-context-private
1161 (make-mu4e-context
1162 :name "Private"
1163 :enter-func
1164 (lambda ()
1165 (mu4e-message "Switch to the Private context")
1166 (my/set-mu4e-bookmarks "private"))
1167 :match-func
1168 (lambda (msg)
1169 (when msg
1170 (or (mu4e-message-contact-field-matches
1171 msg :to (rot13 "erxnqb@ryrcuyl.arg"))
1172 (mu4e-message-contact-field-matches
1173 msg :from (rot13 "erxnqb@ryrcuyl.arg"))
1174 ;; Additional check if this is a mailing list email.
1175 (and (mu4e-message-field msg :mailing-list)
1176 (zerop (call-process "grep" nil nil nil
1177 "-E" "^Delivered-To: .*elephly.net"
1178 (mu4e-message-field msg :path)))))))
1179 :vars
1180 `((user-mail-address . ,(rot13 "erxnqb@ryrcuyl.arg"))
1181 (user-full-name . ,(rot13 "Evpneqb Jhezhf"))
1182 (mu4e-sent-folder . "/private/Sent")
1183 (mu4e-trash-folder . "/private/Trash")
1184 (mu4e-refile-folder . "/private/Archives")
1185 (mu4e-drafts-folder . "/private/Drafts")
1186 (mu4e-compose-signature . "Ricardo"))))
1187 #+END_SRC
1188
1189 And here’s the context for work email:
1190
1191 #+BEGIN_SRC elisp
1192 (setq my/mu4e-context-work
1193 (make-mu4e-context
1194 :name "Work"
1195 :enter-func
1196 (lambda ()
1197 (mu4e-message "Switch to the Work context")
1198 (my/set-mu4e-bookmarks "mdc-personal"))
1199 :match-func
1200 (lambda (msg)
1201 (when msg
1202 (or (mu4e-message-contact-field-matches
1203 msg :to (rot13 "evpneqb.jhezhf@zqp-oreyva.qr"))
1204 (mu4e-message-contact-field-matches
1205 msg :from (rot13 "evpneqb.jhezhf@zqp-oreyva.qr"))
1206 ;; Additional check if this is a mailing list email.
1207 (and (mu4e-message-field msg :mailing-list)
1208 (zerop (call-process "grep" nil nil nil
1209 "-E" "^Received: from .*mdc-berlin.de"
1210 (mu4e-message-field msg :path)))))))
1211 :vars
1212 `((user-mail-address . ,(rot13 "evpneqb.jhezhf@zqp-oreyva.qr"))
1213 (user-full-name . ,(rot13 "Evpneqb Jhezhf"))
1214 (mu4e-sent-folder . "/mdc-personal/Sent Items")
1215 (mu4e-trash-folder . "/mdc-personal/Deleted Items")
1216 (mu4e-refile-folder . "/mdc-personal/Archive")
1217 (mu4e-drafts-folder . "/mdc-personal/Drafts")
1218 (mu4e-compose-signature . ,(rot13 (concat "\
1219 Evpneqb Jhezhf
1220
1221 Flfgrz nqzvavfgengbe
1222 OVZFO - Fpvragvsvp Ovbvasbezngvpf Cyngsbez
1223 Znk Qryoehrpx Pragre sbe Zbyrphyne Zrqvpvar
1224
1225 Eboreg-Eöffyr-Fge. 10, 13125, Oreyva, Treznal
1226 Ohvyqvat 89, Ebbz 1.08
1227
1228 rznvy: evpneqb.jhezhf@zqp-oreyva.qr
1229 gry: +49 30 9406 " (number-to-string (+ (* 1 2 2 3 4 5 6) (expt 2 8) 100))))))))
1230 #+END_SRC
1231
1232 When I’m using the workstation in the office, all I want is the work context.
1233
1234 #+BEGIN_SRC elisp
1235 (setq mu4e-contexts
1236 (if (string= (system-name) (rot13 "ovzfo-flf02.zqp-oreyva.arg"))
1237 (list my/mu4e-context-work)
1238 (list my/mu4e-context-private
1239 my/mu4e-context-work)))
1240 #+END_SRC
1241
1242 Load all of this email configuration code only when I start =mu4e=.
1243
1244 #+BEGIN_SRC elisp :noweb-ref email-lazy
1245 (eval-when-compile
1246 (require 'mu4e))
1247 (global-set-key (kbd "<f12>") 'mu4e)
1248 (with-eval-after-load "mu4e"
1249 <<email>>
1250 )
1251 #+END_SRC
1252
1253 * Work
1254 :PROPERTIES:
1255 :header-args: :noweb-ref work
1256 :END:
1257
1258 At work I need to use both Trello and Slack. I don’t like to use their proprietary JavaScript applications, so I use Emacs clients instead.
1259
1260 #+BEGIN_SRC elisp
1261 (require 'slack)
1262 (setq slack-buffer-emojify t)
1263 (setq slack-prefer-current-team t)
1264 (slack-register-team
1265 :name "emacs-slack"
1266 :default t
1267 :client-id "4904408490.301670834180"
1268 :client-secret "ad378f271c6534cc1e8474e7fa5d0e23"
1269 :token
1270 "xoxp-4904408490-5010866676-355272950374-b9e4506c5894a17e87a9515555bab261" ;"xoxp-4904408490-5010866676-301686920628-9a9a0d359a69c649987e81c570abeb00"
1271 :subscribed-channels '(general pigx_talk sysadmin h89 galaxy-admins)
1272 :full-and-display-names t)
1273 #+END_SRC
1274
1275
1276
1277 * TODO More stuff
1278 :PROPERTIES:
1279 :header-args: :noweb-ref initial-after-packages
1280 :END:
1281
1282 This is even more stuff to be done after initialising packages. I still need to process all of this and clean it up.
1283
1284 #+BEGIN_SRC elisp
1285 (require 'projectile)
1286 (projectile-mode)
1287 #+END_SRC
1288
1289 * TODO And even more
1290 :PROPERTIES:
1291 :header-args: :noweb-ref old-init
1292 :END:
1293
1294 #+BEGIN_SRC elisp
1295 (setq backup-directory-alist
1296 `(;; Do not backup or auto-save remote files to prevent delays.
1297 (,tramp-file-name-regexp . nil)
1298 ;; Write backup files to a dedicated directory.
1299 ("." . ,(expand-file-name
1300 (concat user-emacs-directory "backups")))))
1301
1302 ;; Make backups of files, even when they're in version control
1303 (setq vc-make-backup-files t)
1304
1305 (add-to-list 'auto-mode-alist '("\\.markdown\\'" . markdown-mode))
1306 (add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-mode))
1307 (add-to-list 'auto-mode-alist '("\\.js$" . js2-mode))
1308
1309 (setq scss-compile-at-save nil)
1310
1311 ;; (setq whitespace-global-modes '(not erc-mode))
1312 ;; (global-whitespace-mode 1)
1313 ;; (set-face-attribute 'whitespace-space nil :background nil :foreground "gray20")
1314 ;; (set-face-attribute 'whitespace-newline nil :background nil :foreground "gray20")
1315 ;; (setq whitespace-style
1316 ;; '(face spaces tabs newline space-mark tab-mark newline-mark))
1317 ;; (setq whitespace-display-mappings
1318 ;; ;; all numbers are Unicode codepoint in decimal. try
1319 ;; ;; (insert-char 182 ) to see it
1320 ;; '(
1321 ;; (space-mark 32 [183] [46]) ; 32 SPACE, 183 MIDDLE DOT 「·」,
1322 ;; ; 46 FULL STOP 「.」
1323 ;; (newline-mark 10 [182 10]) ; 10 LINE FEED
1324 ;; (tab-mark 9 [9655 9] [92 9]) ; 9 TAB, 9655 WHITE
1325 ;; ; RIGHT-POINTING TRIANGLE 「▷」
1326 ;; ))
1327
1328 (desktop-save-mode t)
1329 (setq desktop-restore-eager 5) ; restore buffers lazily
1330 (setq desktop-lazy-idle-delay 3) ; no hurry
1331
1332 ;; ediff settings
1333 (setq ediff-diff-options "-w")
1334
1335 ;; fewer backslashes in regexp builder
1336 (require 're-builder)
1337 (setq reb-re-syntax 'string)
1338
1339 ;; remove prompt on killing process buffer
1340 (setq kill-buffer-query-functions
1341 (remq 'process-kill-buffer-query-function
1342 kill-buffer-query-functions))
1343
1344 ;; enable features that are disabled by default
1345 (put 'narrow-to-region 'disabled nil)
1346 (put 'erase-buffer 'disabled nil)
1347 (put 'narrow-to-page 'disabled nil)
1348
1349 (require 'fill-column-indicator)
1350 (setq fci-rule-use-dashes t)
1351 (setq fci-rule-color "#cccccc")
1352 (setq fci-dash-pattern 0.3)
1353 (add-hook 'prog-mode-hook 'fci-mode)
1354
1355 ;; This is a workaround to display the company-mode completion popup
1356 ;; when fci-mode is enabled. It was taken from here:
1357 ;; https://github.com/company-mode/company-mode/issues/180#issuecomment-55047120
1358 (defvar-local company-fci-mode-on-p nil)
1359
1360 (defun company-turn-off-fci (&rest ignore)
1361 (when (boundp 'fci-mode)
1362 (setq company-fci-mode-on-p fci-mode)
1363 (when fci-mode (fci-mode -1))))
1364
1365 (defun company-maybe-turn-on-fci (&rest ignore)
1366 (when company-fci-mode-on-p (fci-mode 1)))
1367
1368 (add-hook 'company-completion-started-hook 'company-turn-off-fci)
1369 (add-hook 'company-completion-finished-hook 'company-maybe-turn-on-fci)
1370 (add-hook 'company-completion-cancelled-hook 'company-maybe-turn-on-fci)
1371
1372 ;; expand region
1373 (global-set-key (kbd "M-@") 'er/expand-region)
1374
1375 ;; Swap C-t and C-x, so it's easier to type on Dvorak layout
1376 ;; `keyboard-translate` does not work when attaching an emacsclient to
1377 ;; a running emacs in daemon mode, so instead we define the key in the
1378 ;; key-translation-map.
1379 ;; http://lists.gnu.org/archive/html/help-gnu-emacs/2009-10/msg00505.html
1380 (define-key key-translation-map [?\C-x] [?\C-t])
1381 (define-key key-translation-map [?\C-t] [?\C-x])
1382
1383 ;; Use narrow tab width
1384 (set-default 'tab-width 4)
1385 (setq tab-width 4)
1386
1387 (setq gnus-select-method '(nntp "news.gmane.org"))
1388
1389 ;; disable away timestamp in ERC
1390 (setq erc-away-timestamp-format nil)
1391 (setq erc-timestamp-format nil)
1392 ;; don’t switch to a newly created IRC buffer
1393 (setq erc-join-buffer 'bury)
1394
1395 ;; Revert stale document graphics buffers automatically when the files
1396 ;; have changed.
1397 (add-hook 'doc-view-mode-hook 'auto-revert-mode)
1398
1399 (page-break-lines-mode 1)
1400 (global-set-key (kbd "<C-prior>") 'backward-page)
1401 (global-set-key (kbd "<C-next>") 'forward-page)
1402
1403
1404 (add-to-list 'auto-mode-alist '("\\.html\\'" . sgml-mode))
1405 (eval-when-compile
1406 (require 'tagedit))
1407 (eval-after-load "sgml-mode"
1408 '(progn
1409 (require 'tagedit)
1410 (tagedit-add-paredit-like-keybindings)
1411 (tagedit-add-experimental-features)
1412 (add-hook 'html-mode-hook (lambda () (tagedit-mode 1)))))
1413
1414 (delete-selection-mode 1) ; delete seleted text when typing
1415
1416 ;; don't let the cursor go into minibuffer prompt, HT Xah Lee
1417 (setq minibuffer-prompt-properties '(read-only t point-entered minibuffer-avoid-prompt face minibuffer-prompt))
1418
1419 (require 'undo-tree)
1420 (global-undo-tree-mode 1)
1421
1422 (savehist-mode)
1423
1424 ;; PDF view mode
1425 (setq pdf-info-epdfinfo-program "~/.guix-profile/bin/epdfinfo")
1426 (pdf-tools-install)
1427 (add-to-list 'auto-mode-alist '("\\.pdf\\'" . pdf-view-mode))
1428
1429 ;; enable variable-pitch-mode in eww
1430 (add-hook 'eww-mode-hook
1431 (lambda ()
1432 (variable-pitch-mode 1)
1433 (olivetti-mode 1)))
1434
1435 ;; pretty quotes!
1436 (add-hook 'erc-mode-hook
1437 (lambda ()
1438 (require 'typo)
1439 (typo-mode 1)))
1440 (add-hook 'org-mode-hook
1441 (lambda ()
1442 (require 'typo)
1443 (typo-mode 1)))
1444 #+END_SRC
1445
1446 Here are a few commands that I used pretty often:
1447
1448 #+BEGIN_SRC elisp
1449 (defun my/new-empty-buffer ()
1450 "Open a new empty buffer."
1451 (interactive)
1452 (let ((buf (generate-new-buffer "untitled")))
1453 (switch-to-buffer buf)
1454 (funcall (and initial-major-mode))
1455 (setq buffer-offer-save t)))
1456 (global-set-key (kbd "C-c n") 'my/new-empty-buffer)
1457
1458 ;; http://whattheemacsd.com/key-bindings.el-01.html#disqus_thread
1459 (require 'linum)
1460 (defun my/goto-line-with-feedback ()
1461 "Show line numbers temporarily, while prompting for the line number input"
1462 (interactive)
1463 (let ((line-numbers-off-p (not linum-mode)))
1464 (unwind-protect
1465 (progn
1466 (when line-numbers-off-p
1467 (linum-mode 1))
1468 (call-interactively 'goto-line))
1469 (when line-numbers-off-p
1470 (linum-mode -1)))))
1471 (global-set-key [remap goto-line] 'my/goto-line-with-feedback)
1472
1473 ;; kill current buffer
1474 (global-set-key (kbd "C-x C-k") (lambda ()
1475 (interactive)
1476 (kill-buffer (current-buffer))))
1477
1478 ;; delete up to non-whitespace character
1479 (global-set-key (kbd "C-c d") (lambda ()
1480 (interactive)
1481 (cycle-spacing -1 t nil)))
1482
1483 (defun ssh-dtach (host)
1484 "Open SSH connection to HOST and start dtach session."
1485 (interactive "sHost: ")
1486 (let ((explicit-shell-file-name "dtach")
1487 (explicit-dtach-args '("-A" "/tmp/emacs.dtach" "-z"
1488 "/bin/bash" "--noediting" "-login"))
1489 (default-directory (format "/ssh:%s:" host)))
1490 (shell (format "*ssh %s*" host))))
1491
1492 ;; http://blog.vivekhaldar.com/post/4809065853/dotemacs-extract-interactively-change-font-size
1493 (defun my/zoom-in ()
1494 "Increase font size by 10 points"
1495 (interactive)
1496 (set-face-attribute 'default nil
1497 :height
1498 (+ (face-attribute 'default :height)
1499 10)))
1500
1501 (defun my/zoom-out ()
1502 "Decrease font size by 10 points"
1503 (interactive)
1504 (set-face-attribute 'default nil
1505 :height
1506 (- (face-attribute 'default :height)
1507 10)))
1508
1509 ;; change font size, interactively
1510 (global-set-key (kbd "C->") 'my/zoom-in)
1511 (global-set-key (kbd "C-<") 'my/zoom-out)
1512
1513 ;; easier way to jump to other window
1514 (global-set-key (kbd "M-o") 'other-window)
1515
1516
1517 (defun my/smart-open-line ()
1518 "Insert an empty line after the current line.
1519 Position the cursor at its beginning, according to the current mode."
1520 (interactive)
1521 (move-end-of-line nil)
1522 (newline-and-indent))
1523
1524 (global-set-key [(shift return)] 'my/smart-open-line)
1525
1526 ;; http://stackoverflow.com/a/18814469/519736
1527 (defun my/copy-buffer-file-name (choice)
1528 "Copy the buffer-file-name to the kill-ring"
1529 (interactive "cCopy Buffer Name (F) Full, (D) Directory, (N) Name")
1530 (let ((new-kill-string)
1531 (name (if (eq major-mode 'dired-mode)
1532 (dired-get-filename)
1533 (or (buffer-file-name) ""))))
1534 (cond ((eq choice ?f)
1535 (setq new-kill-string name))
1536 ((eq choice ?d)
1537 (setq new-kill-string (file-name-directory name)))
1538 ((eq choice ?n)
1539 (setq new-kill-string (file-name-nondirectory name)))
1540 (t (message "Quit")))
1541 (when new-kill-string
1542 (message "%s copied" new-kill-string)
1543 (kill-new new-kill-string))))
1544 #+END_SRC
1545
1546 * Putting it all together
1547
1548 Having defined named code blocks in the sections above we can finally put them all together to build the init file
1549
1550 #+BEGIN_SRC elisp :noweb yes :tangle yes
1551 <<compile-init>>
1552 <<better-defaults>>
1553 <<theme>>
1554 <<packages>>
1555 <<default-fonts>>
1556 <<manuals>>
1557 <<tramp>>
1558 <<shell>>
1559 <<guix>>
1560 <<scheme>>
1561 <<god>>
1562 <<email-lazy>>
1563 <<completion-lazy>>
1564 <<pretty-symbols>>
1565 <<resize-dynamically>>
1566 <<emms-lazy>>
1567 <<initial-after-packages>>
1568 <<org-mode-lazy>>
1569 <<magit-lazy>>
1570 (when (not (string= (system-name) (rot13 "ovzfo-flf02.zqp-oreyva.arg")))
1571 <<lilypond>>
1572 )
1573 <<modeline>>
1574 <<work>>
1575 <<old-init>>
1576
1577 (global-set-key (kbd "C-x RET 1") (lambda () (interactive) (insert "¯\\_(ツ)_/¯")))
1578 #+END_SRC
1579
1580 * Variables :noexport:
1581
1582 # Local Variables:
1583 # eval: (add-hook 'org-babel-post-tangle-hook (lambda nil (byte-compile-file "~/.emacs.d/init.el")))
1584 # End: