1 #+Title: Emacs Customizations
2 #+Author: Ricardo Wurmus
4 #+PROPERTY: noweb tangle
6 #+OPTIONS: tasks:nil toc:1
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.
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?
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.
18 :header-args: :noweb-ref compile-init
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.
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))
29 (org-babel-tangle-file orgfile)
30 (byte-compile-file target)
36 :header-args: :noweb-ref packages
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.
41 To install all packages via GNU Guix I can either use a manifest file or use the following invocation:
43 #+BEGIN_SRC shell :noweb-ref nil
48 emacs-all-the-icons-dired \
54 emacs-fill-column-indicator \
61 emacs-multiple-cursors \
66 emacs-page-break-lines \
72 emacs-shell-switcher \
74 emacs-smart-mode-line \
75 emacs-solarized-theme \
88 If there are packages that are not yet packaged for Guix, I can define them in this list:
91 (defvar my/packages '())
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.
98 (add-to-list 'package-archives
99 '("melpa" . "http://melpa.milkbox.net/packages/"))
103 If this is a fresh Emacs installation melpa needs to be initialised first:
106 (unless (file-exists-p "~/.emacs.d/elpa/archives/melpa")
107 (package-refresh-contents))
110 Now we are ready to install packages if they aren’t yet installed.
113 (defun packages-install (packages)
114 (mapc (lambda (package)
115 (when (not (package-installed-p package))
116 (package-install package)))
118 (delete-other-windows))
120 (defun init--install-packages ()
121 (packages-install my/packages))
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.
128 (init--install-packages)
131 (package-refresh-contents)
132 (init--install-packages))))
137 :header-args: :noweb-ref better-defaults
140 Emacs defaults are hostile to most people. They are what kept me from using Emacs for many years.
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.
145 (setq scroll-margin 7
147 scroll-conservatively 10000
148 scroll-preserve-screen-position 1)
151 Here are a few more simple tweaks:
154 ;; No splash screen please ...
155 (setq inhibit-startup-message t)
157 ;; display tool tips in echo area only
160 ;; by default Emacs will only resize the frame line by line
161 (setq frame-resize-pixelwise t)
163 ;; don’t force me to input “yes” or “no”
164 (defalias 'yes-or-no-p 'y-or-n-p)
166 ;; disable mouse scrolling
167 (mouse-wheel-mode -1)
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.
173 (add-to-list 'load-path (concat user-emacs-directory "lisp"))
176 Also tell Emacs that I want to have a separate file for all other customisations that are handled through =M-x customize=.
179 ;; Keep emacs Custom-settings in separate file
180 (setq custom-file (expand-file-name "custom.el" user-emacs-directory))
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]].
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)
194 (propertize shortened-text 'font-lock-face 'shadow 'help-echo text))))
196 (add-hook 'comint-preoutput-filter-functions 'my/comint-shorten-long-lines)
201 :header-args: :noweb-ref theme
204 Currently, I’m a fan of themes with muted colours. The =tao-yang-theme= is a light theme with very few colours.
207 (setq frame-background-mode 'light)
209 (load-theme 'solarized-light t)
212 Dired mode becomes much prettier with =all-the-icons=.
215 (add-hook 'dired-mode-hook 'all-the-icons-dired-mode)
220 :header-args: :noweb-ref default-fonts
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.
226 (set-frame-font "DejaVu Sans Mono")
227 (set-face-attribute 'variable-pitch nil :height 1.25 :family "Linux Biolinum")
232 :header-args: :noweb-ref guix
235 Store paths have long hashes. In most cases I don’t really care, so I use =guix-prettify-mode= to hide them.
238 (when (require 'guix-prettify nil t)
239 (global-guix-prettify-mode))
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.
245 (add-hook 'shell-mode-hook 'guix-build-log-minor-mode)
248 I’m monitoring the Guix build farm =berlin.guixsd.org=, which is hosted at the MDC.
251 (setq guix-hydra-url "https://berlin.guixsd.org")
254 For bug and patch tracking the Guix project uses debbugs. Here are some better defaults for using =debbugs-gnu= with Guix:
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"))
264 Oleg Pykhalov shared this useful snippet to list bugs for which I am listed as the owner.
267 (with-eval-after-load "debbugs-gnu"
268 (defun my/debbugs-gnu ()
270 (let ((debbugs-gnu-current-query `((submitter . ,user-mail-address))))
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:
278 (add-to-list 'yas-snippet-dirs "~/dev/gx/branches/master/etc/snippets")
284 :header-args: :noweb-ref manuals
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.
291 (set-face-attribute 'info-title-1 nil
292 :inherit 'variable-pitch
294 (set-face-attribute 'info-title-2 nil
295 :inherit 'variable-pitch
297 (set-face-attribute 'info-title-3 nil
298 :inherit 'variable-pitch
300 (set-face-attribute 'info-menu-header nil
301 :inherit 'variable-pitch
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.
308 (set-face-attribute 'Info-quoted nil
309 :inherit 'fixed-pitch
315 :header-args: :noweb-ref org-mode :noweb yes
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.
321 (require 'org-indent)
322 (setq org-ellipsis "⤵")
323 (setq org-src-fontify-natively t)
325 (global-set-key (kbd "C-c o l") 'org-store-link)
326 (global-set-key (kbd "C-c o a") 'org-agenda)
328 ;; TODO: make these available in org-mode only
329 (global-set-key (kbd "C-c o s") 'org-schedule)
331 (setq org-log-done t)
332 (setq org-return-follows-link t)
334 (setq org-directory "~/Documents/org")
335 (setq org-agenda-files (mapcar (lambda (x) (concat org-directory x))
341 (setq org-default-notes-file (concat org-directory "/notes.org"))
343 (setq org-agenda-custom-commands
344 '(("w" todo "WAITING" nil)
345 ("n" todo "NEXT" nil)
346 ("d" "Agenda + Next Actions" ((agenda) (todo "NEXT")))))
348 (setq org-fontify-done-headline t)
350 (require 'org-bullets)
351 (setq org-bullets-bullet-list '("◉" "○" "◇" "◇"))
352 (add-hook 'org-mode-hook
355 (variable-pitch-mode 1)
356 (visual-line-mode 1)))
360 (find-file (concat org-directory "/master.org")))
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.
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.
368 (set-face-attribute 'org-done nil :strike-through t)
369 (set-face-attribute 'org-headline-done nil
371 :foreground "light gray")
372 (set-face-attribute 'org-document-title nil
374 :foreground (face-attribute 'default :foreground))
375 (set-face-attribute 'org-level-1 nil
377 :foreground (face-attribute 'default :foreground))
378 (set-face-attribute 'org-level-2 nil
380 :foreground (face-attribute 'default :foreground))
381 (set-face-attribute 'org-level-3 nil
383 :foreground (face-attribute 'default :foreground))
384 (set-face-attribute 'org-level-4 nil
386 :foreground (face-attribute 'default :foreground))
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"
394 (dolist (face '(org-block-begin-line
399 org-document-info-keyword
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))
413 (set-face-attribute 'org-tag nil
414 :foreground (face-attribute 'default :foreground)
416 :height (face-attribute 'default :height)
419 :box '(:line-width -1 :color "#859900"))
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.
425 (setq org-indent-boundary-char ?*)
426 (set-face-attribute 'org-indent nil :foreground (face-attribute 'default :background))
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.
432 (setq org-tags-column 0)
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.
438 (defvar-local my/org-at-src-begin -1
439 "Variable that holds whether last position was an org source code block.")
441 (defvar-local my/org-src-begin-regexp
442 "^[ \t]*#\\+begin_src[ \t]+[^ \f\t\n\r\v]+[ \t]*")
444 (defvar my/ob-header-symbol ?☰
445 "Symbol used for babel headers")
447 (defun my/org-prettify-src--update ()
448 (let ((case-fold-search t)
449 (re my/org-src-begin-regexp)
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
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))))
481 (defun my/org-prettify-src ()
482 "Hide src options via `prettify-symbols-mode'.
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
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
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
498 (with-silent-modifications
499 (remove-text-properties (match-end 0)
500 (1+ (line-end-position))
502 (setq my/org-at-src-begin at-src-block)))
504 (defun my/org-prettify-symbols ()
505 (mapc (apply-partially 'add-to-list 'prettify-symbols-alist)
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)
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.
521 (global-set-key (kbd "C-c o c") 'org-capture)
522 (setq org-capture-templates
524 (file+headline (concat org-directory "/home.org") "Tasks")
527 (file+headline (concat org-directory "/email.org") "Email")
528 "* Reply to %:fromname%? :email:\n [%:date]\n To: %:to\n %a")))
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=.
534 (setq org-latex-pdf-process
535 '("lualatex -interaction nonstopmode -output-directory %o %f"
537 "lualatex -interaction nonstopmode -output-directory %o %f"
538 "lualatex -interaction nonstopmode -output-directory %o %f"))
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.
549 After editing source snippets with =C-c '= please don’t indent everything with two spaces. Just leave things as I edited them.
552 (setq org-edit-src-content-indentation 0)
555 All of this should be loaded lazily.
557 #+BEGIN_SRC elisp :noweb-ref org-mode-lazy
558 (with-eval-after-load "org"
563 * Editing files on remote systems
565 :header-args: :noweb-ref tramp
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.
572 (setq tramp-default-method "ssh")
574 (setq tramp-default-proxies-alist
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
583 '("mdc-berlin\\.net" "\\`rwurmus\\'" "/ssh:rwurmus@ssh1.mdc-berlin.de:")))
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\\'")
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)
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.
596 #+BEGIN_SRC elisp :noweb-ref nil
598 "Tell me if I’m connected to the network at work."
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)))))
607 :header-args: :noweb-ref shell :noweb yes
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.
612 #+BEGIN_SRC elisp :noweb-ref eshell
614 (setq eshell-history-size 10000)
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."
621 (if (string-match "\\`\\+\\([0-9]+\\)\\'" (car args))
622 (let* ((line (string-to-number (match-string 1 (pop args))))
625 (forward-line (1- line)))
626 (find-file (pop args)))))
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."
633 (let ((pieces (split-string (eshell/pwd) ":/")))
634 (insert (if (> (length pieces) 1)
635 (concat (car pieces) ":/")
638 (defun my/tramp-home ()
639 "Print home directory path on the remote host."
641 (let ((pieces (split-string (eshell/pwd) ":/")))
642 (insert (if (> (length pieces) 1)
643 (concat (car pieces) ":~/")
646 (define-key eshell-mode-map (kbd "C-c /") 'my/tramp-root)
647 (define-key eshell-mode-map (kbd "C-c ~") 'my/tramp-home)
650 Of course, all of this should only be loaded when Eshell is used.
653 (with-eval-after-load "eshell"
658 TODO: here’s the rest of my shell configuration:
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)
667 ;; use cat as the pager in shell mode, because shell-mode is not an
669 (setenv "PAGER" "cat")
671 ;; C-d on an empty line in the shell terminates the process.
672 (defun my/comint-delchar-or-eof-or-kill-buffer (arg)
674 (if (null (get-buffer-process (current-buffer)))
676 (comint-delchar-or-maybe-eof arg)))
678 (add-hook 'shell-mode-hook
680 ;; needed for proper display of "ls"
683 ;; load shared bash history
684 (setq comint-input-ring-file-name "~/.bash_history")
685 (comint-read-input-ring t)
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)))
692 ;; Show current path instead of just "*shell*<2>"
693 (setq uniquify-buffer-name-style 'forward)
699 :header-args: :noweb-ref magit :noweb yes
703 ;; full screen magit-status
704 (defadvice magit-status (around magit-fullscreen activate)
705 (window-configuration-to-register :magit-fullscreen)
707 (delete-other-windows))
709 (defun my/magit-quit-session ()
710 "Restores the previous window configuration and kills the magit buffer"
713 (jump-to-register :magit-fullscreen))
715 (defun my/magit-toggle-whitespace ()
716 "Toggles git option -w"
718 (if (member "-w" magit-diff-arguments)
719 (my/magit-dont-ignore-whitespace)
720 (my/magit-ignore-whitespace)))
722 (defun my/magit-ignore-whitespace ()
725 (add-to-list 'magit-diff-arguments "-w")
728 (defun my/magit-dont-ignore-whitespace ()
729 "Removes git option -w"
731 (setq magit-diff-arguments (remove "-w" magit-diff-arguments))
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)
737 (setq magit-diff-refine-hunk 'all)
740 #+BEGIN_SRC elisp :noweb-ref magit-lazy
743 (with-eval-after-load "magit"
746 (global-set-key (kbd "C-c m") 'magit-status)
751 :header-args: :noweb-ref completion :noweb yes
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.
756 #+BEGIN_SRC elisp :noweb-ref company-hook
757 (add-hook 'prog-mode-hook 'company-mode)
760 I like automatic completion, but it’s nice to also have a key to trigger completion.
763 (setq company-idle-delay 0.5)
764 (define-key company-mode-map (kbd "C-c <tab>") 'company-complete)
769 (let ((bg (face-attribute 'default :background)))
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))))))
778 I also use snippets for commonly typed expressions.
780 #+BEGIN_SRC elisp :noweb-ref yas
784 Load all of this lazily.
786 #+BEGIN_SRC elisp :noweb-ref completion-lazy
787 (with-eval-after-load "company"
792 (with-eval-after-load "yasnippet"
800 :header-args: :noweb-ref pretty-symbols
804 (defun my/pretty-js-symbols ()
805 (push '("===" . ?≡) prettify-symbols-alist)
806 (push '("function" . ?𝑓) prettify-symbols-alist))
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))
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))
821 * Resize buffer margins dynamically
823 :header-args: :noweb-ref resize-dynamically
826 I don’t want to have any margins by default.
829 (setq-default left-margin-width 0 right-margin-width 0)
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.
836 (add-hook 'org-mode-hook #'olivetti-mode)
839 * Multimedia with EMMS
841 :header-args: :noweb-ref emms :noweb yes
847 (setq simple-mpc-arguments "-h 192.168.178.20")
852 :header-args: :noweb-ref modeline
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.
860 (setq erc-track-position-in-mode-line 'before-modes)
863 I don’t like a cluttered modeline, so I like to hide mode lighters with =rich-minority=.
866 ;; clean up mode line
867 (require 'rich-minority)
868 (setq rm-whitelist '(" God" " Smartparens"))
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)
875 ;; show column position of point in status bar
876 (column-number-mode 1)
879 (setq sml/theme 'respectful)
886 :header-args: :noweb-ref lilypond
889 Activate Lilypond mode when I’m opening a Lilypond score.
892 (require 'lilypond-mode)
893 (add-to-list 'auto-mode-alist '("\\.ly\\'" . LilyPond-mode))
896 Enable =subword-mode= in Lilypond files because I use CamelCase for
900 (add-hook 'LilyPond-mode-hook 'subword-mode)
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.
905 #+BEGIN_SRC elisp :tangle nil
906 (with-eval-after-load "org"
907 (require 'ob-lilypond))
912 :header-args: :noweb-ref scheme :noweb yes
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.
918 (with-eval-after-load "geiser"
919 (setq geiser-active-implementations '(guile))
920 (setq geiser-guile-load-path '("~/dev/gx/branches/master")))
923 Automatically start =guix-devel-mode= when in =scheme-mode= because I’m likely working on Guix anyway.
926 (add-hook 'scheme-mode-hook 'guix-devel-mode)
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.
931 #+BEGIN_SRC elisp :noweb-ref paren-face-hook
932 (require 'paren-face)
933 (global-paren-face-mode 1)
936 Emacs also highlights matching parentheses, but it does so with a delay. Here I’m disabling the delay.
938 #+BEGIN_SRC elisp :noweb-ref show-paren-hook
940 (setq show-paren-delay 0)
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.
947 (add-hook 'prog-mode-hook (lambda () (smartparens-mode 1)))
950 Also enable =smartparens= when editing Elisp in the minibuffer.
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)))
958 (add-hook 'minibuffer-setup-hook 'conditionally-enable-smartparens-mode)
961 Some customisations for =smartparens=.
965 (require 'smartparens))
966 (with-eval-after-load "smartparens"
967 (setq smartparens-strict-mode t)
968 (sp-use-paredit-bindings))
971 TODO: the parentheses adjustments should happen only when I’m in programming mode.
980 :header-args: :noweb-ref god
986 (eval-after-load "god-mode"
988 (defadvice god-mode-lookup-key-sequence (before my-swap-x-t)
989 "Swap ?x and ?t KEY arguments."
991 (?x (ad-set-arg 0 ?t))
992 (?t (ad-set-arg 0 ?x))))
993 (ad-activate 'god-mode-lookup-key-sequence)
995 (defvar original-color (frame-parameter nil 'cursor-color))
996 (defun my-update-cursor ()
998 (set-cursor-color "Red")
999 (set-cursor-color original-color)))
1001 (define-key god-local-mode-map (kbd ".") 'repeat)
1003 (add-hook 'god-mode-enabled-hook 'my-update-cursor)
1004 (add-hook 'god-mode-disabled-hook 'my-update-cursor)
1006 ;; reliably toggle god-mode for all modes
1007 (setq god-exempt-major-modes nil)
1008 (setq god-exempt-predicates nil)
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)))
1016 (add-hook 'overwrite-mode-hook 'god-toggle-on-overwrite)
1017 (define-key god-local-mode-map (kbd "i") 'god-local-mode)
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)))
1025 ;; TODO: only do this on the Thinkpad
1026 (global-set-key (kbd "<mouse-3>") 'god-mode-all)
1031 :header-args: :noweb-ref email :noweb yes
1034 TODO: this is a big blob of email configuration. Document this properly!
1039 (require 'mu4e-contrib)
1041 (setq shr-color-visible-luminance-min 30)
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)
1049 (setq mu4e-use-fancy-chars t)
1050 (setq mu4e-headers-seen-mark '("" . ""))
1051 (setq mu4e-headers-unread-mark '("u" . "✉"))
1053 ;; Don't update list of emails automatically. I update the list
1054 ;; manually with "g".
1055 (setq mu4e-headers-auto-update nil)
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)
1062 ;; use imagemagick, if available
1063 (when (fboundp 'imagemagick-register-types)
1064 (imagemagick-register-types))
1066 (setq mu4e-attachment-dir "~/Downloads"
1067 mu4e-compose-signature-auto-include t)
1070 (add-hook 'message-mode-hook
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
1084 ;; TODO: don't match my own signature
1085 (list (concat me " " unread)
1086 "Mentioning me (unread)" ?R)
1087 (list (concat "maildir:\"/" maildir "/INBOX\"")
1089 (list "date:today..now"
1090 "Today's messages" ?t)
1091 (list "date:today..now"
1093 (list (concat "maildir:\"/" maildir "/INBOX\"" " " unread)
1094 "Unread messages" ?u)
1095 (list (concat guix " " unread)
1097 (list (concat guile " " unread)
1099 (list (concat fsfe " " unread)
1101 (list (concat emacs " " unread)
1103 (list (concat "maildir:\"/private/mailinglists\""
1109 "Unread list messages" ?m)
1110 (list (concat "maildir:\"/" maildir "/INBOX\" flag:flagged")
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)
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)
1122 ;;use msmtp instead of sendmail
1123 (setq sendmail-program "~/.guix-profile/bin/msmtp")
1126 (setq mml-secure-openpgp-encrypt-to-self t)
1127 (setq mml-secure-openpgp-sign-with-sender t)
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))
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)))))
1139 (add-hook 'mu4e-compose-mode-hook
1140 (defun my/mu4e-add-headers ()
1141 "Add some personal headers."
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"))))
1147 ;; Don't open a new window to show the results of signature validation
1148 (setq epa-popup-info-window nil)
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))
1157 I read and write email in different contexts. This is my context for private email:
1160 (setq my/mu4e-context-private
1165 (mu4e-message "Switch to the Private context")
1166 (my/set-mu4e-bookmarks "private"))
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)))))))
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"))))
1189 And here’s the context for work email:
1192 (setq my/mu4e-context-work
1197 (mu4e-message "Switch to the Work context")
1198 (my/set-mu4e-bookmarks "mdc-personal"))
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)))))))
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 "\
1221 Flfgrz nqzvavfgengbe
1222 OVZFO - Fpvragvsvp Ovbvasbezngvpf Cyngsbez
1223 Znk Qryoehrpx Pragre sbe Zbyrphyne Zrqvpvar
1225 Eboreg-Eöffyr-Fge. 10, 13125, Oreyva, Treznal
1226 Ohvyqvat 89, Ebbz 1.08
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))))))))
1232 When I’m using the workstation in the office, all I want is the work context.
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)))
1242 Load all of this email configuration code only when I start =mu4e=.
1244 #+BEGIN_SRC elisp :noweb-ref email-lazy
1247 (global-set-key (kbd "<f12>") 'mu4e)
1248 (with-eval-after-load "mu4e"
1255 :header-args: :noweb-ref work
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.
1262 (setq slack-buffer-emojify t)
1263 (setq slack-prefer-current-team t)
1264 (slack-register-team
1267 :client-id "4904408490.301670834180"
1268 :client-secret "ad378f271c6534cc1e8474e7fa5d0e23"
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)
1279 :header-args: :noweb-ref initial-after-packages
1282 This is even more stuff to be done after initialising packages. I still need to process all of this and clean it up.
1285 (require 'projectile)
1289 * TODO And even more
1291 :header-args: :noweb-ref old-init
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")))))
1302 ;; Make backups of files, even when they're in version control
1303 (setq vc-make-backup-files t)
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))
1309 (setq scss-compile-at-save nil)
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
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 「▷」
1328 (desktop-save-mode t)
1329 (setq desktop-restore-eager 5) ; restore buffers lazily
1330 (setq desktop-lazy-idle-delay 3) ; no hurry
1333 (setq ediff-diff-options "-w")
1335 ;; fewer backslashes in regexp builder
1336 (require 're-builder)
1337 (setq reb-re-syntax 'string)
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))
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)
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)
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)
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))))
1365 (defun company-maybe-turn-on-fci (&rest ignore)
1366 (when company-fci-mode-on-p (fci-mode 1)))
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)
1373 (global-set-key (kbd "M-@") 'er/expand-region)
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])
1383 ;; Use narrow tab width
1384 (set-default 'tab-width 4)
1387 (setq gnus-select-method '(nntp "news.gmane.org"))
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)
1395 ;; Revert stale document graphics buffers automatically when the files
1397 (add-hook 'doc-view-mode-hook 'auto-revert-mode)
1399 (page-break-lines-mode 1)
1400 (global-set-key (kbd "<C-prior>") 'backward-page)
1401 (global-set-key (kbd "<C-next>") 'forward-page)
1404 (add-to-list 'auto-mode-alist '("\\.html\\'" . sgml-mode))
1407 (eval-after-load "sgml-mode"
1410 (tagedit-add-paredit-like-keybindings)
1411 (tagedit-add-experimental-features)
1412 (add-hook 'html-mode-hook (lambda () (tagedit-mode 1)))))
1414 (delete-selection-mode 1) ; delete seleted text when typing
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))
1419 (require 'undo-tree)
1420 (global-undo-tree-mode 1)
1425 (setq pdf-info-epdfinfo-program "~/.guix-profile/bin/epdfinfo")
1427 (add-to-list 'auto-mode-alist '("\\.pdf\\'" . pdf-view-mode))
1429 ;; enable variable-pitch-mode in eww
1430 (add-hook 'eww-mode-hook
1432 (variable-pitch-mode 1)
1436 (add-hook 'erc-mode-hook
1440 (add-hook 'org-mode-hook
1446 Here are a few commands that I used pretty often:
1449 (defun my/new-empty-buffer ()
1450 "Open a new empty buffer."
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)
1458 ;; http://whattheemacsd.com/key-bindings.el-01.html#disqus_thread
1460 (defun my/goto-line-with-feedback ()
1461 "Show line numbers temporarily, while prompting for the line number input"
1463 (let ((line-numbers-off-p (not linum-mode)))
1466 (when line-numbers-off-p
1468 (call-interactively 'goto-line))
1469 (when line-numbers-off-p
1471 (global-set-key [remap goto-line] 'my/goto-line-with-feedback)
1473 ;; kill current buffer
1474 (global-set-key (kbd "C-x C-k") (lambda ()
1476 (kill-buffer (current-buffer))))
1478 ;; delete up to non-whitespace character
1479 (global-set-key (kbd "C-c d") (lambda ()
1481 (cycle-spacing -1 t nil)))
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))))
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"
1496 (set-face-attribute 'default nil
1498 (+ (face-attribute 'default :height)
1501 (defun my/zoom-out ()
1502 "Decrease font size by 10 points"
1504 (set-face-attribute 'default nil
1506 (- (face-attribute 'default :height)
1509 ;; change font size, interactively
1510 (global-set-key (kbd "C->") 'my/zoom-in)
1511 (global-set-key (kbd "C-<") 'my/zoom-out)
1513 ;; easier way to jump to other window
1514 (global-set-key (kbd "M-o") 'other-window)
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."
1521 (move-end-of-line nil)
1522 (newline-and-indent))
1524 (global-set-key [(shift return)] 'my/smart-open-line)
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))
1537 (setq new-kill-string (file-name-directory name)))
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))))
1546 * Putting it all together
1548 Having defined named code blocks in the sections above we can finally put them all together to build the init file
1550 #+BEGIN_SRC elisp :noweb yes :tangle yes
1565 <<resize-dynamically>>
1567 <<initial-after-packages>>
1570 (when (not (string= (system-name) (rot13 "ovzfo-flf02.zqp-oreyva.arg")))
1577 (global-set-key (kbd "C-x RET 1") (lambda () (interactive) (insert "¯\\_(ツ)_/¯")))
1580 * Variables :noexport:
1583 # eval: (add-hook 'org-babel-post-tangle-hook (lambda nil (byte-compile-file "~/.emacs.d/init.el")))