Skip to content
Switch branches/tags
Go to file
Cannot retrieve contributors at this time
17508 lines (14574 sloc) 686 KB

This is the GNU/Emacs config file of Karl Voit.

This file is named My init.el got some nifty Elisp code that converts/tangles (only) the Elisp blocks into the config.el. And here I explained how this is done. This generated file is interpreted by my GNU/Emacs on startup.

Note that all Elisp blocks part of a DISABLED heading or which are marked with :tangle no won’t be tangled to the config.el file. Unfortunately, within the Org-mode rendering of GitHub, you won’t see the =DISABLED= keyword nor the =:tangle no= parameter of the babel blocks. Please get the Org-mode file and open it in your Emacs directly.

Originally, I found this process on this web page. However, I adapted its code and write additional features.

Links that start with id: won’t work for you because they link to my personal Org-mode files.

Some tasks I plan to do with my

  • [ ] FIXXME: think of merging the default dir .emacs.d/quelpa/ with .emacs.d/contrib/
  • [ ] FIXXME: migrate existing GitHub projects to quelpa
  • [ ] FIXXME: check why Python “auto-complete mode” is disabled in my config
  • [ ] FIXXME: check why Python > Ropemacs (for refactoring) is disabled in my config
  • [ ] FIXXME: add a table to each package with its history in my config:
    2020-01-13installed because of X
    2020-01-15disabled because issue Y
    2020-01-19enabled again after fixing with Z

What Solution I Am Using for Which I Consider My Main Use-Cases

Corresponding to my general blog article What App am I Using for What and How? I maintain this brief overview of my most important workflows for Emacs and what package solution I am using for it. If you want to learn more about my workflows, I recommend reading and following my Blog Series: Using Org Mode Features (UOMF).

If you want to read how my init.el is auto-generated from this Org mode file, read this article.

I do not maintain links to the sections below since this would be a very fragile task anyway considering the way GitHub links work for now. Therefore: if you are interested in details on a certain workflow, please do search for corresponding keywords. There are seldom multiple search hits except for separate key binding definitions or hydras.

Installing software packagesuse-package with packages and git repositories
“Window management”eyebrowse with a decent hydra-bufferstop feature
Remembering bindings; cheatcheata hydra on F1 for each major mode; which-keytop feature
Naked and full-screen Emacsmy-toggle-naked-emacs()unclutter screen
Getting rid of scrollbarnyan-modeGet more horizontal screen space
Reducing clutter in mode linesmart-mode-line, mode-icons
Filter Org filessparse trees
Focus on Org sub-hierarchycustomized org-tree-to-indirect-buffer
Enhanced search/selection usabilityswiper, counsel, helm, helm-org-rifle
Switching buffersibuffer
Desktop notificationsalert
Inserting date/time-stampsmy-insert-timestamp(), my-insert-datestamp(),
Inserting characterschar-menu, M-x insert-char
Copying region to Org propertymy-org-region-to-property()very handy for org-contacts
Org to PDF exportnormal Org mode export via LaTeX (not pandoc)
Passwordsorg-crypt on Org mode headings
Coding pythonelpy, flycheck
Coding: Highlighting lines/wordsbm, highlight-symbol
Coding: Folding/unfoldingyafolding
Versioning filesmagit
Snippet managementyankpad with yasnippetadvanced workflows
Spell checkingflyspell with aspell
Formatting paragraphsmy-fill-or-unfill()
Formatting headingsmy-title-capitalization()
File managementdired with some very cool extensionsI’m still getting used to
Quickly jumping to a directorymy-dired-recent-dirs()top featurel; based on frecency
PDF readingpdf-toolsI’m still getting used to
PDF annotatingpdf-toolsI’m still getting used to
Record screencastsgif-screencast and non-emacs methods

What I’m not using at the moment:

  • projectile or other source code project management tools
  • vim bindings (evil, …)
  • configuration frameworks
  • reading and managing emails: I’d love to use notmuch but I can’t

You might want to take a look at the next section where I list my most important generic hydras in contrast to the F1-mapped mode hydras.


;; remove comment characters when using but-hunter to bi-sect this file:
; -------------------------------------------------------------------------------------------
; (package-initialize)
; (defvar my-init-el-start-time (current-time) "Time when init.el was started")
; (setq my-user-emacs-directory "~/.emacs.d/")
; (add-to-list 'load-path (concat my-user-emacs-directory "contrib/org-mode/contrib/lisp"))
; (add-to-list 'load-path (concat my-user-emacs-directory "contrib/org-mode/lisp"))
; (require 'org)
; -------------------------------------------------------------------------------------------

literate test 1

Testing the noweb principle as shown on this page:

  • [2021-02-05 Fri 11:09] issue: my (very fast) custom function to tangle does not handle noweb syntax and Holger’s page (the original author of the tangle function) doesn’t exist any more.
;; nowebtest3
(setq nowebtest3 t)
;; nowebtest1

Function keys

Here are my function-key-mappings that are included in most hydra help screens:

(setq my-f-key-settings (concat
"⇧             Git   ←change→ ┃  yp-exp yp-map            ┃  Project minimap  Beginner  Bright
"                                                                                                     (propertize
"    F1        F2    F3   F4  ┃  F5     F6       F7  F8   ┃  F9      F10      F11       F12
"                                                                                                      'face '(:foreground "green"))
"    Hydra  Windows        ★  ┃  spell  (←) error →  fix  ┃  Search  Menu     maximize  naked


General settings

Here, I do set some very general settings for my GNU/Emacs.


(defvar my-config-el-start-time (current-time) "Time when config.el was started")
;(profiler-start 'cpu);; test startup performance - create report with M-x profiler-report

;; from
(setq my-config-el-start-time-iso (concat
 (format-time-string "%Y-%m-%dT%T")
 ((lambda (x) (concat (substring x 0 3) ":" (substring x 3 5)))
  (format-time-string "%z"))))

2011-04-20: turn off backup files

(setq-default backup-inhibited t)

set start of week to Monday (not sunday)

(setq-default calendar-week-start-day 1)

omit usage of TAB for C-x r o: indent-tabs-mode

(setq-default indent-tabs-mode nil)

append and update time-stamps for Time-stamp: <> in headers:

(add-hook 'write-file-hooks 'time-stamp)

set warning of opening large files to 100MB

(setq-default large-file-warning-threshold 100000000)

do not add double space after periods Real sentence in Emacs : emacs

(setq-default sentence-end-double-space nil) M-x toggle-truncate-lines

(setq-default truncate-lines t)

elisp - Emacs truncate lines in all buffers - Stack Overflow

(setq-default global-visual-line-mode t)

inhibit the startup screen

(setq inhibit-startup-screen t)

English time-stamps in Org-mode (instead of localized German ones):

(setq system-time-locale "C")

Adaptive cursor width | Pragmatic Emacs: make cursor the width of the character it is under; i.e. full width of a TAB:

(setq x-stretch-cursor t)

Remember the position of a buffer and go to that position when re-opening the file: (2018-07-26 disabled because it is not always a good thing to do)

(setq-default save-place t)
(setq save-place-file (expand-file-name ".places" user-emacs-directory))
(save-place-mode 1)
(load-file (concat my-user-emacs-directory "private.el"))

Tip via irreal. I share the same rationale: I never answer anything different to “y”. So I disabled it:

(setq confirm-kill-processes nil)

Moves the mouse cursor out of the way when the text cursor seems to crash: documentation.

Possible values: banish, exile, jump, animate, proteus

(when (display-mouse-p) (setq mouse-avoidance-mode "animate"))

A tip from this wonderful blog article: map scroll-lock mode to the corresponding scroll-lock key:

(global-set-key (kbd "<Scroll_Lock>") 'scroll-lock-mode)

To avoid “Error saving to X clipboard manager.” as well as endless loop on exiting (see this):

(setq x-select-enable-clipboard-manager nil)

Turn off header when printing with M-x ps-print-buffer-with-faces and format according to this page:

(setq ps-paper-type 'a4
	  ps-font-size 9.0
	  ps-print-header nil
	  ps-landscape-mode nil
	  ps-number-of-columns 1)

Open file system read-only files as read-only in Emacs as well: (via this article)

(setq view-read-only t)

I’m not using abbrev mode and get annoyed by its questions:

;(save-abbrevs 'silently) ;; Silently save abbrevs:
(setq save-abbrevs nil) ;; not saving abbrevs
(setq-default abbrev-mode nil)

Guru mode

(defun disable-guru-mode ()
  (guru-mode -1)
(add-hook 'prelude-prog-mode-hook 'disable-guru-mode t)


Configure the package manager(s) of my GNU/Emacs.


MELPA packages are usually built automatically from a project’s repository; the GNU repository has stable releases that are explicitly submitted to it.

package-user-dir holds the directory where Emacs package manager installs its local copies of the packages:

(setq gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3") ;; from
(setq package-user-dir (concat my-user-emacs-directory "elpa"))
(require 'package)

;;2019-12-07;;  ;;(add-to-list 'package-archives '("marmalade" . ""))
;;2019-12-07;;  ;;OLD:(add-to-list 'package-archives '("melpa" . ""));; moved to
;;2019-12-07;;  (add-to-list 'package-archives '("melpa" . ""))
;;2019-12-07;;  ;;unstable;; (add-to-list 'package-archives '("melpa" . ""))
;;2019-12-07;;  ;; 2017-03-26: from
;;2019-12-07;;  (add-to-list 'package-archives '("org" . ""))
;;2019-12-07;;  (add-to-list 'package-archives '("gnu" . ""))

;; 2019-12-07: severe issues with old package versions brings me to remove old config and start with this from docu:
(setq package-archives '(
                         ("melpa" . "")
                         ("gnu" . "")


Add elpy repository:

(add-to-list 'package-archives '("elpy" . ""))

fix certificate issue

Bugfixing: 2016-01-26: fix certificate issue: “gnutls.c: [0] (Emacs) fatal error: The TLS connection was non-properly terminated.”

(if (fboundp 'gnutls-available-p)
    (fmakunbound 'gnutls-available-p))
(setq tls-program '("gnutls-cli --tofu -p %p %h")
      imap-ssl-program '("gnutls-cli --tofu -p %p %s")
      smtpmail-stream-type 'starttls
      starttls-extra-arguments '("--tofu")

Initialize misc packages

;; 2015-11-25:
;(when (string-equal system-type "windows-nt")
;   (add-to-list 'load-path (concat package-user-dir "/use-package-20190405.2047"))
   (require 'use-package))
;(require 'diminish)
(require 'bind-key)

(setq package-enable-at-startup nil)
(unless (package-installed-p 'use-package)
  (package-install 'use-package))

(use-package f
  :ensure t
(use-package ivy
  :ensure t
    ;; 2019-12-07: this is found in (Version: 0.13.0)
    ;;    but not in the elpa package version 0.13.0 on my disk. Don't know why/how, just adding it as a workaround:
    (defcustom ivy-use-group-face-if-no-groups t
      "If t, and the expression has no subgroups, highlight whole match as a group.
    It will then use the second face (first of the \"group\" faces)
    of `ivy-minibuffer-faces'.  Otherwise, always use the first face
    in this case."
      :type 'boolean)
(use-package ht
  :ensure t


DISABLED because of:

Debugger entered--Lisp error: (file-error "Cannot open load file" "No such file or directory" "use-package-core")
  eval-buffer(#<buffer  *load*-463556> nil "/home/vk/.emacs.d/elpa/quelpa-use-package-20190210.1938/quelpa-use-package.el" nil t)  ; Reading at buffer position 1691
  load-with-code-conversion("/home/vk/.emacs.d/elpa/quelpa-use-package-20190210.1938/quelpa-use-package.el" "/home/vk/.emacs.d/elpa/quelpa-use-package-20190210.1938/quelpa-use-package.el" nil t)
  eval-buffer(#<buffer  *load*-218457> nil "/home/vk/.emacs.d/config.el" nil t)  ; Reading at buffer position 4033
  load-with-code-conversion("/home/vk/.emacs.d/config.el" "/home/vk/.emacs.d/config.el" nil nil)
  load("/home/vk/.emacs.d/config.el" nil nil t)

… including auto-update:

(if (require 'quelpa nil t)
    (url-insert-file-contents "")


   :fetcher git
   :url ""))
(require 'quelpa-use-package)

After that it is possible to call use-package with the :quelpa keyword:


;; installs abc-mode with quelpa
(use-package abc-mode :quelpa)

;; does the same (`t' is optional)
(use-package abc-mode :quelpa t)

;; again... (if the package would have another name)
(use-package abc-mode :quelpa abc-mode)

;; passes upgrade parameter to quelpa
(use-package abc-mode :quelpa (:upgrade t))

;; uses the given recipe
(use-package abc-mode
  :quelpa (abc-mode :fetcher github :repo "mkjunker/abc-mode"))

;; recipe with plist arguments
(use-package abc-mode
  :quelpa ((abc-mode :fetcher github :repo "mkjunker/abc-mode") :upgrade t))

Read for upgrading quelpa packages.


Many times, I do need to uppercase or lowercase a word. Those commands offer me quick shortcuts to do so.

See: id:2014-03-04-M-l-subword

(global-set-key [M-l] 'downcase-word)
(global-set-key [M-u] 'upcase-word)
(global-set-key [M-c] 'capitalize-word)

yes-or-no-p: prefer y/n

«True #Emacs Knights are lazy and hate typing yes/no - they prefer y/n instead. Use this (fset ‘yes-or-no-p ‘y-or-n-p) in your config.» … from:

(fset 'yes-or-no-p 'y-or-n-p)

Deletes duplicate entries of the history of the minibuffer

«If the value of this variable is t, that means when adding a new history element, all previous identical elements are deleted.» from:

(setq history-delete-duplicates t)

This is also necessary to avoid duplicate entries when searching with helm on Emacs 27 as shown here.

Pasting with the mouse without moving the point

«middle-clicking pastes at the current location instead of moving it» from:

(setq mouse-yank-at-point t)

Un-setting some keys

Here, I do unset some keys I don’t use so that they are not in my way when I accidentially use them:

;;   \C-v   scroll up
;;   \C-t   transpose-chars
(dolist (key '("\C-v" "\C-t"))
    (global-unset-key key))

delete-trailing-whitespace before saving

I don’t see any use of trailing whitespace. Previously, I had a function to remove them mapped to my-map SPC but then I found out that adding this as a general before-save-hook does the job automatically:

2019-12-14: disabled because it consumed 20-40% of the time when saving large files. 22% on

;;(bind-key (kbd "SPC") #'delete-trailing-whitespace my-map)
;  (define-key org-mode-map (kbd "C-c C-, SPC") #'delete-trailing-whitespace);; workaround since line above doesn't work

;; 2016-02-06:
(add-hook 'before-save-hook 'delete-trailing-whitespace)

Maximize frame window

Details: id:2016-03-27-maximize-window-init.el

[2020-12-10 Thu] Good read: Maximize the Emacs Frame on Startup | Emacs Redux


Alternative from: Tip: Setting initial frame size and position

;; Set initial frame size and position
(defun my/set-initial-frame ()
  (let* ((base-factor 0.70)
	(a-width (* (display-pixel-width) base-factor))
        (a-height (* (display-pixel-height) base-factor))
        (a-left (truncate (/ (- (display-pixel-width) a-width) 2)))
	(a-top (truncate (/ (- (display-pixel-height) a-height) 2))))
    (set-frame-position (selected-frame) a-left a-top)
    (set-frame-size (selected-frame) (truncate a-width)  (truncate a-height) t)))
(setq frame-resize-pixelwise t)

Also mentioned:

I believe this works both in windows and in character terminals:

(setq default-frame-alist '((left . 0) (width . 141) (fullscreen . fullheight)))

(You might have to change 141 to something larger if you have a huge monitor.)

Window Management

See hydra-buffers() near the end of this file for a nice summary.

See GitHub - karthink/popper: Emacs minor-mode to summon and dismiss buffers easily for defining pop-up buffers.

my-vsplit-last-buffer() my-hsplit-last-buffer ()

This is using the last buffer for splitting windows instead of the current one:

From this emacs config which stole it from Sacha and reddit:

(defun my-vsplit-last-buffer ()
  (other-window 1 nil)

(defun my-hsplit-last-buffer ()
  (other-window 1 nil)

(bind-key "C-x 2" 'my-vsplit-last-buffer)
(bind-key "C-x 3" 'my-hsplit-last-buffer)

my-frame-is-landscape() my-frame-is-portrait()

Following frame-width and frame-height values are returned when the Emacs frame (the thing which is called “window” on OS-level) is either higher or wider:

(frame-width) ;; portrait frame: 73; landscape frame: 190; quadratic frame: 47
(frame-height);; portrait frame: 56; landscape frame: 60 ; quadratic frame: 47

In order to find out whether or not there is more space in the horizontal or in the vertical line, I divide the width by two. This is because characters (the measure returned by (frame-width) and (frame-height)) are higher than wide approximately by factor two as well:

(if (< (/ (frame-width) 2) (frame-height))
  (message "portrait frame")
(message "landscape frame")

So I define functions to check the frame aspect that return boolean values:

(defun my-frame-is-landscape ()
  "Return true if Emacs frame is landscape and not portrait mode"
  (< (/ (frame-width) 2) (frame-height))

;; (if (my-frame-is-landscape)
;;  (message "portrait frame")
;; (message "landscape frame")
;; )

(defun my-frame-is-portrait ()
  "Return true if Emacs frame is portrait and not landscape mode"
  (not (my-frame-is-landscape))

(if (my-frame-is-portrait)
  (message "The frame is in landscape mode")
  (message "The frame is in portrait mode")

Default split direction according to frame aspect ratio

On wide screens, I want my default split direction being side-by-side (vertical split). On tilted/high screens, the default split should be up/down (horizontal split). (Source)

Note: this might be no good idea when you are not working with single/maximized windows like I prefer for now.

The values of the thresholds on sting (30”, landscape) before I started overwriting them here:

split-width-threshold  ;; Its value is 9999; Original value was 160
split-height-threshold ;; Its value is 80
(if (my-frame-is-landscape)
  (setq split-width-threshold nil);; for vertical split
  (setq split-width-threshold 1)  ;; for horizontal split


2021-06-05started to use bookmarks after watching this video
(setq bookmark-save-flag 1) ;; save bookmarks on every change

(setq bookmark-default-file (concat my-user-emacs-directory "/var/bookmark-default.el")) ;; this was set somewhere else; I did not look how/why. I just set it here again to make it explicit.

use-package and quelpa

My setup is using John Wiegley’s use-package for configuration and startup of external libraries. This has many advantages: flexibility, startup performance, readability.

Bootstrap use-package is stolen from this file:

(unless (package-installed-p 'use-package)
  (package-install 'use-package))

  (require 'use-package))

(use-package use-package
   :ensure t
   ;;:pin MELPA
   (require 'use-package))

Until 2020-06-30, I only used use-package and manually cloned git repositories I included with hard-coded path. The latter were seldomly updated (if ever). With quelpa, there seems to be a much better way.

(use-package quelpa
  :ensure t
  (setq quelpa-upgrade-interval 7);; upgrade all packages once a week according to
  (add-hook #'after-init-hook #'quelpa-upgrade-all-maybe)

The quelpa-use-package package offers a more or less transparent bridge between sites like GitHub and the use-package features.

(use-package quelpa-use-package
  :ensure t


Here are some examples for future reference:

A very simple example for installing a package via package management and ensure it is installed when Emacs launches:

(use-package dumb-jump
  :ensure t
  :defer 110

An example of loading a local package (not from Melpa or other package service):

(use-package define-word
   :if (my-system-type-is-gnu)
   :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/mypackage/")))
   :after org

Key binding example from docu:

(use-package helm
  :bind (("M-x" . helm-M-x)
         ("M-<f5>" . helm-find-files)
         ([f10] . helm-buffers-list)
         ([S-f10] . helm-recentf)))

more binding examples:

  :bind (:map my-map ("SPC" . yankpad-insert))

  :bind (("M-f" . sp-forward-sexp)
         ("M-b" . sp-backward-sexp)


(bind-keys :map pdf-view-mode-map
        ("f9" . hydra-pdftools/body)
        ("<s-spc>" .  pdf-view-scroll-down-or-next-page)
        ("g"  . pdf-view-first-page)
        ("G"  . pdf-view-last-page)
        ("l"  . image-forward-hscroll)
        ("h"  . image-backward-hscroll)
        ("j"  . pdf-view-next-page)
        ("k"  . pdf-view-previous-page)
        ("e"  . pdf-view-goto-page)
        ("u"  . pdf-view-revert-buffer)
        ("al" . pdf-annot-list-annotations)
        ("ad" . pdf-annot-delete)
        ("aa" . pdf-annot-attachment-dired)
        ("am" . pdf-annot-add-markup-annotation)
        ("at" . pdf-annot-add-text-annotation)
        ("y"  . pdf-view-kill-ring-save)
        ("i"  . pdf-misc-display-metadata)
        ("s"  . pdf-occur)
        ("b"  . pdf-view-set-slice-from-bounding-box)
        ("r"  . pdf-view-reset-slice))

Use of :defer

:defer also accepts an optional numeric argument which causes the package to be loaded after N seconds of idle time.


Emacs becomes idle when it starts waiting for user input, and it remains idle until the user provides some input.

My GNU/Emacs 26 takes approximately 70s to start. This is very long but it includes all my many “autostart” activities that also cover visibility settings in opened Org mode files and my complex agenda.

Therefore, my defer times start with 90s which is clearly after the startup.

Ocurrences in config.el (without disabled or not tangled code):

grep ":defer " ~/.emacs.d/config.el |  # extract all occurrences of ":defer" from the tangled init file
  sed 's/;;.*//' |         # remove emacs-lisp comments
  sed 's/ //g' |           # remove all space characters to normalize strings
  sed 's/defer/defer /' |  # add space character again only after ":defer"
  sort |                   # sort the resulting strings alphabetically
  uniq -c ;                # uniq them and count their occurrences
  date                     # add the current time stamp in order to know how recent this output is
      1 :defer 110
     21 :defer 110
      6 :defer 120
     11 :defer 90
Tue Apr 16 10:31:52 WEDT 2019



It sets some default paths in order to separate automatically created files and directories.

(use-package no-littering
   :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/no-littering/")))
(require 'no-littering)


host-specific log file: my-log-hostspecific()

Here’s the idea: on every Emacs startup, a file like ~/.emacs.d/var/log/HOSTNAME.txt is overwritten with the current time-stamp and emacs-version. The path scheme follows the no-littering package I’m using as well.

While loading miscellaneous packages, I might decide to append a version string.

This way, I get a list of files from all of my hosts sharing the same Emacs configuration. Each of these files hold the time of the last startup and the version strings of interesting packages. This simplifies generating bug reports and finding issues with version conflicts.

Here is an example file content for one host:

Started on 2019-04-16T14:35:04+02:00
emacs-version 26.0.90
cygwin-mount-version 1.4.8
yas--version 0.11.0
org-version 9.1.6
plantuml-mode-version 1.2.3
magit-version 2.10.3

So how is it done? Here we go. Let’s define the common file name, one per host:

(setq my-var-log-hostname-file (concat no-littering-var-directory "log/host-" system-name ".txt"))

At Emacs startup, overwrite the file content and initializing it with the current time stamp:

(write-region (concat "Started on " my-config-el-start-time-iso "\n") nil my-var-log-hostname-file)

Define the function that is called to append lines to the file:

(defun my-log-hostspecific (mystring mycommand)
  "append a string and the result of a command to the my-var-log-hostname-file file"
  (write-region (concat mystring " " mycommand "\n") nil my-var-log-hostname-file t)

And let’s use this new function to log the version of the GNU/Emacs that is starting:

(my-log-hostspecific "emacs-version" emacs-version)

general log file: my-log-misc()

I’m using a central logging file for all kind of logging messages. I started with my agenda genreation performance.

Where do I log to?

(setq my-log-file (concat no-littering-var-directory "log/misc.log"))

How do I log?

(defun my-log-misc (message)
   (let ((current-timestamp
          (format-time-string "%Y-%m-%dT%T")
          ((lambda (x) (concat (substring x 0 3) ":" (substring x 3 5)))
          (format-time-string "%z")))
    (concat (format-message "%s %s: %s\n" current-timestamp system-name message))
     nil my-log-file "append"))


(my-log "foo bar")


Using this function, I am able to easily load lisp files within my Emacs config hierarchy. It contains minimal error handling for a missing file.


(defun my-load-local-el (part)
  "load lisp file and warn if not found"
  (let ((fullname (concat my-user-emacs-directory part)))
    (if (file-exists-p fullname)
	(load fullname)
      (message (format "Loading %s (source)...failed" fullname)))))

Server mode

Start Emacs as a server process: new files can be visited via emacsclient (instead of parallel emacs instances). Therefore, I don’t have to run multiple instances (which occupies RAM storage) and I am able to open new files instantly.



Emacs config switch depending on hostname or operating system: Idea found here: Single dot emacs file and per-computer configuration | SIGQUIT

This is so cool: with those functions, I am able to maintain one single Emacs configuration for all of my hosts. If there is something I want to do or do not on a specific platform or host, those functions allow me to express my restrictions easily:

;; Get current system's name
(defun my-insert-system-name()
  "Get current system's name"
  (insert (format "%s" system-name))

;; Get current system type
(defun my-insert-system-type()
  "Get current system type"
  (insert (format "%s" system-type))

;; Check if system is Darwin/Mac OS X
(defun my-system-type-is-darwin ()
  "Return true if system is darwin-based (Mac OS X)"
  (string-equal system-type "darwin")

;; Check if system is Microsoft Windows
(defun my-system-type-is-windows ()
  "Return true if system is Windows-based (at least up to Win7)"
  (string-equal system-type "windows-nt")

;; Check if system is GNU/Linux
(defun my-system-type-is-gnu ()
  "Return true if system is GNU/Linux-based"
  (string-equal system-type "gnu/linux")

Here are host-specific functions which I should not use if possible because with them, I lose some generic approach:

(defun my-system-is-floyd-or-sting ()
  "Return true if the system we are running on is floyd or sting"
    (string-equal system-name "floyd")
    (string-equal system-name "floyd.lan")
    (string-equal system-name "sting")
    (string-equal system-name "sting.lan")

(defun my-system-is-sting ()
  "Return true if the system we are running on is sting"
    (string-equal system-name "sting")
    (string-equal system-name "sting.lan")

(defun my-system-is-floyd ()
  "Return true if the system we are running on is floyd"
    (string-equal system-name "floyd")
    (string-equal system-name "floyd.lan")

(defun my-system-is-rise ()
  "Return true if the system we are running on is floyd"
    (string-equal system-name "rise")

(defun my-system-is-blanche ()
  "Return true if the system we are running on is blanche"
  (or (string-equal system-name "blanche") (string-equal system-name "blanche.lan"))

(defun my-system-is-karl-voit-at ()
  "Return true if the system we are running on is"
  (string-equal system-name "")

(defun my-system-is-powerplantlinux ()
  "Return true if the system we are running on is powerplant"
   (string-equal system-name "powerplant")
   (string-equal system-name "powerplant.lan")

System-specific paths

The system PATH variable provides access to executables. However, I do tend to use programs which are not part of the PATH variable of the operating system as well. Therefore, I do extend the Emacs variable exec-path (further down and following headings).

;; setting path so that Emacs finds aspell and such
(when (my-system-type-is-darwin)
  (setenv "PATH"
	  (concat (getenv "PATH")
  (setq exec-path (append exec-path
  (add-to-list 'load-path "/opt/local/share/emacs/site-lisp")

  ;; 2011-04-20: allow typing of german umlauts in OS X by Alt-u followed by u,o,a,...
  (setq mac-option-modifier nil)

  (setq org-ditaa-jar-path "~/data/hosts/blanche/config/ditaa.jar")

  ;; setting path to color-theme-mode.el from MacPorts
  (add-to-list 'load-path "/opt/local/share/emacs/site-lisp/color-theme-6.6.0")


(when (my-system-type-is-gnu)
  (setq org-ditaa-jar-path "/usr/share/ditaa/ditaa.jar")

setting path so that Emacs finds aspell and such:

(if (my-system-type-is-windows)

    ;;disabled;(setenv "PATH"
    ;;disabled;               (concat (getenv "PATH")
    ;;disabled;		  ":/Users/vk/bin:/usr/local/texlive/2010/bin/x86_64-darwin:/opt/local/bin:/opt/local/sbin"))
    (setq exec-path (append exec-path
     			     '("C:/Program Files (x86)/Aspell/bin"
     			       ;;disabled; "/usr/local/texlive/2010/bin/x86_64-darwin"
     			       ;;disabled; "/usr/local/teTeX/bin/powerpc-apple-darwin-current"
    ;;disabled;(add-to-list 'load-path "/opt/local/share/emacs/site-lisp")

  ;; on all other systems:

Where my Org mode files reside. They are used all over this config and therefore, this has to be defined early:

(cond ((string-equal system-name "GRZN17009")
          (setq my-org-files-path "c:/Users/karl.voit/org/"))
      ((string-equal system-name "Cosmo")
          (setq my-org-files-path "c:/Users/John/AppData/Roaming/org/")
          (setq my-org-files-path "~/org/"))
;;(message (format "Set \"my-org-files-path\" to: %s" my-org-files-path))

Setting the system-specific path for my-webarchive-tsfile-dir-path

;; different hosts do have the dir at different locations:
(setq my-webarchive-tsfile-dir-path (cond ((my-system-is-sting) "/home/vk/archive/backup/sting/webarchive")
                            ((my-system-is-rise) "/home/vk/Downloads/webarchive")
                            (t "/home/vk/Downloads/");; fallback path (should NOT be used)

Emax64 settings

2019-11-09: settings according to the emax64 default .emacs file:

(when (my-system-type-is-windows)
;;  ;; Set repositories
;;  (require 'package)
;;  (setq-default
;;   load-prefer-newer t
;;   package-enable-at-startup nil)
;;  (add-to-list 'package-archives '("melpa" . "") t)
;;  (add-to-list 'package-archives '("org" . "") t)
;;  (setq package-user-dir "~/emax/elpa")
;;  (package-initialize)

;;  ;; Install dependencies
;;  (unless (and (package-installed-p 'delight)
;;               (package-installed-p 'use-package))
;;    (package-refresh-contents)
;;    (package-install 'delight t)
;;    (package-install 'use-package t))
;;  (setq-default
;;   use-package-always-defer t
;;   use-package-always-ensure t)

;;  ;; Use latest Org
;;  (use-package org
;;    ;;:pin org
;;    :ensure org-plus-contrib)

  (defvar emax-root (concat (expand-file-name "~") "/emax"))
  (defvar emax-bin (concat emax-root "/bin"))
  (defvar emax-bin64 (concat emax-root "/bin64"))
  (defvar emax-mingw64 (concat emax-root "/mingw64/bin"))
  (defvar emax-lisp (concat emax-root "/lisp"))

  (setq exec-path (cons emax-bin exec-path))
  (setenv "PATH" (concat emax-bin ";" (getenv "PATH")))

  (setq exec-path (cons emax-bin64 exec-path))
  (setenv "PATH" (concat emax-bin64 ";" (getenv "PATH")))

  (setq exec-path (cons emax-mingw64 exec-path))
  (setenv "PATH" (concat emax-mingw64 ";" (getenv "PATH")))

  (setenv "PATH" (concat "C:\\msys64\\usr\\bin;C:\\msys64\\mingw64\\bin;" (getenv "PATH")))

  (dolist (dir '("~/emax/" "~/emax/bin/" "~/emax/bin64/" "~/emax/mingw64/bin/" "~/emax/lisp/" "~/emax/elpa/" "~/bin/"))
  (add-to-list 'load-path dir))

  (set-language-environment 'utf-8)
  (setq locale-coding-system 'utf-8)
  (set-default-coding-systems 'utf-8)
  (set-terminal-coding-system 'utf-8)
  (prefer-coding-system 'utf-8)

  ;; Tangle configuration
  (org-babel-load-file (expand-file-name "~/emax/" user-emacs-directory))



(when (my-system-type-is-windows)
  ;  (setq-default
  ;      (defvar mp/font-family            "Consolas"  "The font to use.")
  ;  )

    ;; Running Windows Powershell from within Emacs
    ; (setq explicit-shell-file-name "c:\\windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe")
    ; (setq explicit-powershell.exe-args '("-Command" "-" )) ; interactive, but no command prompt

    ; (autoload 'powershell "powershell" "Run powershell as a shell within emacs." t)

    ;; Changes made for Aspell
  ;;  (setq-default ispell-program-name "~/emax/mingw64/bin/aspell.exe")
    (setq-default ispell-program-name "~/bin/aspell.cmd")
    (setq-default ispell-extra-args  '("--sug-mode=ultra"))
    ;; (setq ispell-dictionary "en_US")

    ;; Set "DICTDIR" variable
    (setenv "DICTDIR" (concat emax-mingw64 "/lib/aspell-0.60/"))

    ;; Automatically enable flyspell-mode in text-mode
    ;;(require 'flyspell)
    ;;(add-hook 'text-mode-hook 'flyspell-mode)
    (setq text-mode-hook '(lambda() (flyspell-mode t) ))
    ;;(setq text-mode-hook '(lambda()
    ;;                        (flyspell-mode t)))

    ;;(dolist (hook '(text-mode-hook))
    ;;  (add-hook hook (lambda () (flyspell-mode 1))))
    ;;(dolist (hook '(change-log-mode-hook log-edit-mode-hook))
    ;;  (add-hook hook (lambda () (flyspell-mode -1))))

    ;;(setq flyspell-issue-message-flag nil)

    ;;(require 'auto-dictionary)
    ;;(add-hook 'flyspell-mode-hook (lambda () (auto-dictionary-mode 1)))

    (require 'ispell)

;     (setq epg-gpg-home-directory "~/emax/mingw64/bin/")
;     (setq epg-gpg-program "~/emax/mingw64/bin/gpg.exe")
;     (setq epg-gpgconf-program "~/emax/mingw64/bin/gpgconf.exe")

;    (pdf-tools-install :no-query)

Cygwin Paths (Windows)

As mentioned in the chapter of system-specific paths, I do use programs which are not part of the PATH variable of the operating system. Cygwin executables (in form of babun) are one example of this kind of programs.


(when (and (my-system-type-is-windows) (string-equal system-name "GRZN17009"))

Hard-coding the cygwin install path (for babun):

  • id:2016-04-22-magit-not-working-on-windows
(setq cygwin-root-directory "c:/Users/karl.voit/.babun/cygwin/")

Check if Cygwin/babun inst found on the install path given:

(if (file-directory-p cygwin-root-directory)

OLD method of extending the path:

(setenv "PATH"
         ;;"c:\\cygwin64\\usr\\local\\bin" ";"  ;; Cygwin
         ;;"c:\\cygwin64\\bin" ";"  ;; Cygwin
         "C:\\Users\\karl.voit\\.babun\\cygwin\\bin" ";"
         "C:\\Users\\karl.voit\\.babun\\cygwin\\usr\\local\\bin" ";"
         "C:\\Python36\\" ";"
         "C:\\Program\ Files\ \(x86\)\\Java\\jre1.8.0_144\\bin" ";"
         (getenv "PATH")))

Extending the path:

(setq exec-path (cons (concat cygwin-root-directory "bin/") exec-path)) ;; Babun
(setq exec-path (cons (concat cygwin-root-directory "usr/local/bin/") exec-path)) ;; Babun
(setq exec-path (cons "C:/Program Files (x86)/Java/jre1.8.0_144/bin" exec-path)) ;; Babun

Adding cygwin mounts:

(use-package cygwin-mount)

Adding cygwin bash shell

;;(setq shell-file-name "c:/cygwin64/bin/bash") ;; Cygwin
(setq shell-file-name (concat cygwin-root-directory "bin/zsh")) ;; Babun
;;(setq shell-file-name (concat cygwin-root-directory "bin/bash")) ;; Babun
(setenv "SHELL" shell-file-name)
(setq explicit-shell-file-name shell-file-name)
(setq ediff-shell shell-file-name)
(setq explicit-shell-args '("--login" "-i"))
(setq w32-quote-process-args ?\")

id:2015-11-02-tramp-windows-babel and Docu: help:tramp-methods

(setq tramp-default-method "plink")

requires: setup-cygwin.el and cygwin-mount.el in the contrib dir:

(add-to-list 'load-path (concat my-user-emacs-directory "contrib/"))
(require 'setup-cygwin)

END of Cygwin/babun configuration

(message "»»» I could not locate the cygwin path")

end of Cygwin config

(my-log-hostspecific "cygwin-mount-version" cygwin-mount-version)
);; end of if-windows

Starting GNU/Emacs on Windows

First, I create a batch file which starts the emacs.exe with optional Org-mode files as parameters:


REM Here, invoke some syncronization mechanism like Unison:
REM "C:\Program Files\bin\unison-2.40.102-gtk.exe" grmlvrs

REM As of 2017, I switched from Unison to Syncthing

"C:\Program Files\emacs-24.5-bin-i686-mingw32\bin\emacs.exe"

REM Re-syncing after leaving Emacs:
REM "C:\Program Files\bin\unison-2.40.102-gtk.exe" grmlvrs


This batch file is included in a Visual Basic file. This way, I am able to start my GNU/Emacs using misc app-launcher solutions: batch files are not listed in typical app-launchers whereas VBS files work at least with my Hotstrings:

C:\Users\Karl.Voit\bin\orgmode.vbs or in Cygwin /home/karl.voit/bin/orgmode.vbs

CreateObject("Wscript.Shell").Run "C:\Users\Karl.Voit\bin\windows-start-orgmode.bat", 0, True

Looking for binaries

Some Emacs configuration snippets relate to external programs such as LaTeX. Instead of (a) blindly evaluating those snippets or (b) using per-host-configuration for them, I do prefer to check whether or not those programs are installed on the local host instead. This is just the sane way of doing those things.

In detail, it gets a bit dirty for Windows, since there are some tools that are installed but not listed in the PATH environment exec-path. See below for some workarounds for that.


my-binary-found(binaryname) returns the path where a binary executable can be found within the exec-path.

It also checks certain operating system/binary combinations which aren’t likely in the exec-path.

  (defun my-binary-found(binaryname)
    "Returns the path where a binary executable can be found.

It also checks certain operating system/binary combinations which aren't likely in the exec path."
     ((and (my-system-type-is-windows) (string= binaryname "firefox"))
      (when (file-exists-p "C:/Program Files/Mozilla Firefox/firefox.exe")
        (concat "C:/Program Files/Mozilla Firefox/firefox.exe")
     ((and (my-system-type-is-windows) (string= binaryname "python"))
      (when (file-exists-p "C:/Python27/python.exe")
        (concat "C:/Python27/python.exe")
     ((and (my-system-type-is-windows) (string= binaryname "outlook"))
      (when (file-exists-p "C:/Program Files/Microsoft Office/Office16/OUTLOOK.EXE")
        (concat "C:/Program Files/Microsoft Office/Office16/OUTLOOK.EXE")
     ;; this is the default check for all binaries which got no special handling above:
      (locate-file binaryname exec-path exec-suffixes 1))


(message (concat "pdflatex found on: " (my-binary-found "pdflatex")))

(if (my-binary-found "pdflatex")
  (message "LaTeX found")
  (message "LaTeX not found")

my-binary-not-found-list and my-eval-if-binary-or-warn()

my-eval-if-binary-or-warn (binaryname &optional warningtext) checks if a binary can be found in the path via my-binary-found().

If not found, a warning message is printed which can be defined as an optional parameter as well. Additionally, the not found binaries are collected in the variable my-binary-not-found-list.

(defvar my-binary-not-found-list nil
  "Holds a list of binaries which could not be found via my-eval-if-binary-or-warn()"

(defun my-eval-if-binary-or-warn (binaryname &optional warningtext)
  "Checks if a binary can be found in the path via my-binary-found().

If not found, a warning message is printed which can be defined as an optional parameter as well.
Additionally, the not found binaries are collected in the variable my-binary-not-found-list."
  (or warningtext (setq warningtext (concat "»»» I could not locate the PATH-binary for: " binaryname)))
  (let* ((binarypath (my-binary-found binaryname)))
    (if binarypath
       ;; binary was found in exec-path
       (concat binarypath)
        ;; binary NOT found in exec-path:
        (message warningtext)
        (if my-binary-not-found-list
            (add-to-list 'my-binary-not-found-list binaryname)
          (setq my-binary-not-found-list (list binaryname))

Example usages:

(my-eval-if-binary-or-warn "yyy" "This is a warning text for yyy")
(my-eval-if-binary-or-warn "xxx")
(my-eval-if-binary-or-warn "xxx" "This is a warning text for xxx")
(my-eval-if-binary-or-warn "zzz" "This is a warning text for xxx")

(message "Binaries not found: %s" my-binary-not-found-list)

Example output for different hosts

This heading ist just for collecting example outputs:

sting output:

pdflatexTeX binary: /usr/bin/pdflatex
python binary: /usr/bin/python
firefox binary: /usr/bin/firefox
chrome binary:
aspell binary: /usr/bin/aspell
ispell binary:
pandoc binary: /usr/bin/pandoc
ditaa binary: /usr/bin/ditaa
gnuplot binary: /usr/bin/gnuplot
git binary: /usr/bin/git
Outlook binary:
grep binary: /bin/grep
scss binary: /usr/bin/scss
ag binary: /usr/bin/ag
biber binary: /usr/bin/biber

Windows output:

pdflatex binary: c:/Program Files/MiKTeX_2.9/miktex/bin/pdflatex.exe
python binary:
ipython binary:
firefox binary:
chrome binary:
aspell binary:
ispell binary:
pandoc binary: c:/Users/karl.voit/AppData/Local/Pandoc/pandoc.exe
ditaa binary:
gnuplot binary:
git binary:
Outlook binary:
grep binary:
scss binary:
ag binary:
biber binary: c:/Program Files/MiKTeX_2.9/miktex/bin/biber.exe
Binaries not found in checks above: (ag scss grep Outlook git gnuplot ditaa ispell aspell chrome firefox ipython python)

After moving system-specific paths above this checks: only aspell was found:

Binaries not found in checks above: (ag scss grep Outlook git gnuplot ditaa ispell chrome firefox ipython python)

but on Windows, there are following things installed:

  • [ ] python
  • [ ] ipython
  • [ ] firefox
  • [ ] chrome
  • [ ] (a/i?)spell
  • [ ] Outlook
    • real path: “C:\Program Files (x86)\Microsoft Office\root\Office16\OUTLOOK.EXE”
    • also holds for OUTLOOK.EXE and OUTLOOK
    • where outlook is also unsuccessful :-(
(message "★★★★★★★★★★")
(message (concat "pdflatex binary: " (my-binary-found "pdflatex")))
(message (concat "python binary: " (my-binary-found "python")))
(message (concat "ipython binary: " (my-binary-found "ipython")))
(message (concat "firefox binary: " (my-binary-found "firefox")))
(message (concat "chrome binary: " (my-binary-found "chrome")))
(message (concat "aspell binary: " (my-binary-found "aspell")))
(message (concat "ispell binary: " (my-binary-found "ispell")))
(message (concat "pandoc binary: " (my-binary-found "pandoc")))
(message (concat "ditaa binary: " (my-binary-found "ditaa")))
(message (concat "gnuplot binary: " (my-binary-found "gnuplot")))
(message (concat "git binary: " (my-binary-found "git")))
(message (concat "Outlook binary: " (my-binary-found "Outlook")))
(message (concat "grep binary: " (my-binary-found "grep")))
(message (concat "scss binary: " (my-binary-found "scss")))
(message (concat "ag binary: " (my-binary-found "ag")))
(message (concat "biber binary: " (my-binary-found "biber")))
(message "★★★★★★★★★★")

Test queries

Here, I do probe for some tools mostly because I want to test my code above.

When I am using tool-specific settings below, I do add comment characters to disable the check at this stage:

;;(my-eval-if-binary-or-warn "pdflatex")
;;(my-eval-if-binary-or-warn "python")
(my-eval-if-binary-or-warn "ipython")
;;(my-eval-if-binary-or-warn "firefox")
(my-eval-if-binary-or-warn "chrome")
;;(my-eval-if-binary-or-warn "aspell")
;;(my-eval-if-binary-or-warn "pandoc")
(my-eval-if-binary-or-warn "ditaa")
;;(my-eval-if-binary-or-warn "gnuplot")
;;(my-eval-if-binary-or-warn "git")
;;(my-eval-if-binary-or-warn "outlook")
(my-eval-if-binary-or-warn "grep")
;;(my-eval-if-binary-or-warn "scss")
(my-eval-if-binary-or-warn "ag")
(my-eval-if-binary-or-warn "biber")

System-specific browse-url-browser

Here, I do hard-code my preferred browser that is used when I open URLs within Emacs:

(setq firefox-path (my-eval-if-binary-or-warn "firefox"))
(setq chrome-path (my-eval-if-binary-or-warn "google-chrome"))

  (setq browse-url-browser-function 'browse-url-default-macosx-browser)
  (setq browse-url-browser-function 'browse-url-generic
	browse-url-generic-program firefox-path)
  (setq browse-url-browser-function 'browse-url-generic
	browse-url-generic-program chrome-path)

(setq browse-url-browser-function 'browse-url-generic
      browse-url-generic-program "chromium-browser")

  • Edit-server for Chrome
;(use-package edit-server)
(my-load-local-el "contrib/edit-server.el")
;won't work; (use-package edit-server
;won't work;    :load-path "~/.emacs.d/contrib/"
;won't work;    :config
;won't work;    (edit-server-start)
;won't work;    )

(if (locate-library "edit-server")
      ;(use-package edit-server)
      (setq edit-server-new-frame nil)

2017-06-20: A little trick with EWW : emacs - presents code to interactively select your browser of choice.


The (sub-)headings here deal with the visual appeal of my GNU/Emacs. I like dark themes and minimized interfaces. Therefore, I hide everyting I do not use.

Interesting read:

Show current column: 2020-01-01 disabled because of performance impact (re-drawing modeline at each keystroke)

(setq column-number-mode t)

Cursor settings:

;; Prevent the cursor from blinking
;(blink-cursor-mode 0)
(set-cursor-color "IndianRed")

Flat mode-line styling: 2014-05-24: from

(set-face-attribute 'mode-line nil :box nil)
(set-face-attribute 'mode-line-inactive nil :box nil)


2021-01-08I switch back to light theme (leuven or default) for now

Since a couple of major versions, GNU/Emacs has a built-in theme manager. This is for dealing with the themes.

(setq calendar-location-name "Graz, AT")
(setq calendar-latitude 47.07)
(setq calendar-longitude 15.43)
(use-package theme-changer)
(change-theme 'whiteboard 'misterioso)  ;; day and night theme

My favorite dark themes: wombat, misterioso, zenburn, material

;(load-theme 'wombat t) ;; dark theme
;;   (load-theme 'misterioso t)
;;   (load-theme 'zenburn t)
;;   (load-theme 'material t) ;; from
;;              issues with *bold* stuff in org-mode :-(

My favorite light themes: leuven, whiteboard, solarized-light,

;;   (load-theme 'leuven t) ;; from
;;   (load-theme 'whiteboard t)
;;   (load-theme 'solarized-light t)
(defadvice load-theme (before theme-dont-propagate activate) (mapcar #'disable-theme custom-enabled-themes))

Only one window on startup

«Make [current] WINDOW fill its frame.»

(add-hook 'emacs-startup-hook 'delete-other-windows t)

Font and Font sizes

2019-11-15: disabled because not used for a very long time:

(defun my-increase-fontsize ()
  "Sets the font to bigger size"
  (set-face-attribute 'default (selected-frame) :height 130)
(defun my-normal-fontsize ()
  "Sets the font to normal size"
  (set-face-attribute 'default (selected-frame) :height 100)

I was using DejaVu Sans Mono a while ago:

(set-face-attribute 'default nil :font "DejaVu Sans Mono-10")
;(add-to-list 'default-frame-alist
;                       '(font . "DejaVu Sans Mono-10"))
(add-to-list 'default-frame-alist '(font . "JetBrains Mono-12"))
;; (add-to-list 'default-frame-alist '(line-spacing . 0.2))
  • [2021-05-14 Fri] I’m switching to FantasqueSansMono-NoLoopK because JetBrains Mono does not emphasize boldface boldly enough for me
(add-to-list 'default-frame-alist '(font . "Fantasque Sans Mono-12"))

Host-specific font sizes: values are in 1/10pt → 100 are 10pt

;;(when (my-system-type-is-gnu)
;;  (my-increase-fontsize);; increase fonts on some hosts by default
;;  )
(when (my-system-type-is-darwin)
  (set-face-attribute 'default (selected-frame) :height 170);; 2011-04-20: increase/set font size
(when (my-system-type-is-windows)
  ;;(set-face-attribute 'default (selected-frame) :height 150)
  ;;(set-face-attribute 'default (selected-frame) :height 130);; 2016-08-19 let's test 130 after 150 seems too big
  (set-face-attribute 'default (selected-frame) :height 110);; 2017-09-06 detego
(when (my-system-is-floyd)
  ;; (set-face-attribute 'default (selected-frame) :height 100) ;; 2020-08-20: switched back to 105
  ;; (set-face-attribute 'default (selected-frame) :height 105);; until 2019-12-23 -> not working
  (set-face-attribute 'default nil :height 105);; 2020-08-22 new command from
(when (my-system-is-sting)
  ;;(set-face-attribute 'default (selected-frame) :height 110) ;; before 2018-02-24 (a bit large)
  ;;(set-face-attribute 'default (selected-frame) :height 105) ;; before 2019-10-24: I want to try smaller font
  ;;(set-face-attribute 'default (selected-frame) :height 102) ;; before 2019-12-04: even smaller on reduced resolution
  (set-face-attribute 'default (selected-frame) :height 110) ;; 2019-12-04: bigger font on native 30" resolution
(when (my-system-is-rise)
  (set-face-attribute 'default (selected-frame) :height 100)

Different font size for mode-line (from this stackexchange page):

(let ((faces '(mode-line
      (lambda (face) (set-face-attribute face nil :font "DejaVu Sans Mono-8"))

Modeline with icons

There are two potentially nice packages in order to beautify my modeline even further:

FIXXME: So far, I did not try them because my current modeline is beautiful enough. Maybe in the future.

UTF-8 and codings

Activate UTF-8 mode:

(setq locale-coding-system 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(prefer-coding-system 'utf-8)

When I paste from the Windows clipboard, I tend to get messed up Umlauts and special characters. This ought to fix it but I think that this does not work either:

(cond ((my-system-type-is-windows)
       ;; on Windows, 'utf-8 does not work properly when system
       ;; clipboard gets yanked
       (setq selection-coding-system 'utf-16le-dos)

       ;; For example: =\344= instead of =ä= on Windows 7:
       ;;(set-selection-coding-system 'iso-latin-1-dos)

       (set-selection-coding-system 'utf-8)
       (set-selection-coding-system 'utf-8)

;; 2013-12-10 IRC #Emacs
(set-clipboard-coding-system 'utf-8)

;; in addition to the lines above:

(set-default-coding-systems 'utf-8)
;; backwards compatibility as default-buffer-file-coding-system
;; is deprecated in 23.2.
(if (boundp 'buffer-file-coding-system)
    ;; NOTE: default-buffer-file-coding-system is obsolete; use
    ;;       buffer-file-coding-system if found
    (setq-default buffer-file-coding-system 'utf-8)
  (setq default-buffer-file-coding-system 'utf-8))
;; Treat clipboard input as UTF-8 string first; compound text next, etc.
(setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING))


[…] One problem with the universal coding system argument is that it only cares about Emacs’s settings, not those of your shell or system. That’s a problem, because tools like Python use the environment variable PYTHONIOENCODING to set the coding system for the Python interpreter.

I have written the following code that advises the universal-coding-system-argument function so it also, temporarily for just that command, sets a user-supplied list of environment variables to the coding system. […]

(defvar universal-coding-system-env-list '("PYTHONIOENCODING")
  "List of environment variables \\[universal-coding-system-argument] should set")

(defadvice universal-coding-system-argument (around provide-env-handler activate)
  "Augments \\[universal-coding-system-argument] so it also sets environment variables

Naively sets all environment variables specified in
`universal-coding-system-env-list' to the literal string
representation of the argument `coding-system'.

No guarantees are made that the environment variables set by this advice support
the same coding systems as Emacs."
  (let ((process-environment (copy-alist process-environment)))
    (dolist (extra-env universal-coding-system-env-list)
      (setenv extra-env (symbol-name (ad-get-arg 0))))

Entering emojis

2021-12-20Idea from Inserting Emoji with Input Methods - Mastering Emacs

You need to type C-u C-\, enter “Emoji” (TAB-completion works) and then one of the defined emojis below such as :P or :thumb:.

  • [ ] add more emojis!
 "Emoji" "UTF-8" "😎" t
 "Emoji input mode for people that really, really like Emoji"
 '(("\t" . quail-completion))
 t t nil nil nil nil nil nil nil t)

 (":)" ?😀)
 (":P" ?😋)
 (":D" ?😂)
 (":thumb:" ?👍)
 (":think:" ?🤔)
 (":roll:" ?🙄)
 (":weary:" ?😩)
 (":wink:" ?😉)
 (":cry:" ?😢)
 (":kiss:" ?😘)
 (":unamused:" ?😒)
; ("::" ?)
; ("::" ?)

my-map: my own keyboard shortcut prefix

About defining keys: Emacs: How to Define Keys

If you are not satisfied with the default setup of Emacs keyboard shortcuts, you start with defining your own keyboard shortcuts (bindings).

To avoid binding conflicts with libraries/packages, it is a good habit of using a keyboard shortcut prefix no-one else is using. So if you stick to this prefix, you’ve got your own «name-space» where you are able to define your bindings freely.

My approach is to use my-map as a mapping which is bound to C-c C-,= . So my personal bindings start with =C-c C-,= such as =C-c C-, - for decreasing the font size of GNU/Emacs.

2015-11-10: Following code was replaced by bind-key below:

;; 2011-04-20, 2013-04-08: defining «C-c C-,» as my own prefix:
;; NOTE: (info "(elisp) Key Binding Conventions") warns about user prefixes other than C-c
(global-unset-key (kbd "C-c C-,")); causes error: "Invalid modifier in string"
;; same as: (global-unset-key (kbd "C-c C-,"))
(define-prefix-command 'my-map)

2019-12-04: With Org mode version 9.2, C-c C-,= got mapped to =org-insert-structure-template. In order to avoid any conflict situation (also for communication with peers), I switch to a new binding: C-c C-k which is bound to org-kill-note-or-show-branches but it’s a less popular function.

Using the bind-key package: (OLD method without use-package:)

(require 'bind-key);;

;;(org-defkey org-mode-map (kbd "C-c C-,") nil);; clear binding

 :prefix-map my-map
 :prefix-docstring "My own keyboard map"
 :prefix "C-c C-,"
 ;; 2013-03-31:
 ("-" . text-scale-decrease)
 ("+" . text-scale-increase)
 ("=" . text-scale-increase);; because "+" needs "S-=" and I might forget to press shift

New method that allows overwriting of bindings (see this comment using the after feature of use-package):

(use-package bind-key
  :ensure t
  :bind (:prefix-map my-map
         :prefix-docstring "My own keyboard map"
         :prefix "C-c C-,"
         ("-" . text-scale-decrease)
         ("+" . text-scale-increase))
         ("=" . text-scale-increase);; because "+" needs "S-=" and I might forget to press shift
  :after org)

Usage example:

(bind-key "m w" #'mark-word my-map)


 :map my-map
 ("f" . forward-char)
 ("b" . backward-char))

or for use-package():

:bind (:map my-map ("8" . bm-toggle))

Alternative tipp: in case you run out of keybinding spaces, you can take a look at hydra and the “defhydra hydra-k” method. Hydra lists a menu of options and the hydra-k offers a prefix for it.

See 33min30s of the video linked in: Irreal: Hydra Video

Misc modes/packages (part I)

which-key - displays the available key bindings automatically

I got the recommendation via this reddit thread with the arguments, that which-key is inferior to guide-key which I used before::

  • more active
  • more features
  • more contributions

when I do press my prefix for my-map and wait a bit, I get a popup buffer that tells me what bindings I am able to use.

(use-package which-key
  :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/emacs-which-key/")))
  :ensure t
  :defer 120
  :config ;; executed after loading package
  (add-to-list 'which-key-replacement-alist '(("TAB" . nil) . ("" . nil)))
  (add-to-list 'which-key-replacement-alist '(("RET" . nil) . ("" . nil)))
  (add-to-list 'which-key-replacement-alist '(("DEL" . nil) . ("" . nil)))
  (add-to-list 'which-key-replacement-alist '(("SPC" . nil) . ("" . nil)))


Please note this pull request that explains why my characters are non-standard ones. It also adds a symbol for ESC.


2021-12-02added due to error

Recently, I got the error and: Symbol’s value as variable is void: project-switch-commands when running Magit. See also this thread on reddit. I don’t know how this was caused.

However, loading the project package which was already in my Elpa directory does help. OK, so it is:

(use-package project
  :ensure t

My helper functions (part I)

Here I defined some functions I am using in the configuration below.


From time to time, I want to measure, how long an Elisp snippet ran. This can be done with following code.


(defmacro measure-time (&rest body)
  "Measure the time it takes to evaluate BODY."
  `(let ((time (current-time)))
     (message " Execution time: %.06f" (float-time (time-since time)))))

my-title-capitalization(): Proper English Title Capitalization of a Marked Region → my-map C

Read where I wrote a verbose description of the topic and my solution.

;; additionally to the list defined in title-capitalization:
(defvar my-do-not-capitalize-words '("lazyblorg" "mutt")
  "My personal list of words that doesn't get capitalized in titles.")

(defun title-capitalization (beg end)
  "Proper English title capitalization of a marked region"
  ;; - before: the presentation of this heading of my own from my keyboard and yet
  ;; - after:  The Presentation of This Heading of My Own from My Keyboard and Yet
  ;; - before: a a a a a a a a
  ;; - after:  A a a a a a a A
  (interactive "r")
    (let* (
	   ;; basic list of words which don't get capitalized according to simplified rules:
           (do-not-capitalize-basic-words '("a" "ago" "an" "and" "as" "at" "but" "by" "for"
                                            "from" "in" "into" "it" "next" "nor" "of" "off"
                                            "on" "onto" "or" "over" "past" "so" "the" "till"
                                            "to" "up" "yet"
                                            "n" "t" "es" "s"))
	   ;; if user has defined 'my-do-not-capitalize-words, append to basic list:
           (do-not-capitalize-words (if (boundp 'my-do-not-capitalize-words)
                                        (append do-not-capitalize-basic-words my-do-not-capitalize-words )
      ;; go to begin of first word:
      (goto-char beg)
      (capitalize-word 1)
      ;; go through the region, word by word:
      (while (< (point) end)
        (skip-syntax-forward "^w" end)
        (let ((word (thing-at-point 'word)))
          (if (stringp word)
              ;; capitalize current word except it is list member:
              (if (member (downcase word) do-not-capitalize-words)
                  (downcase-word 1)
                (capitalize-word 1)))))
      ;; capitalize last word in any case:
      (backward-word 1)
      (if (and (>= (point) beg)
               (not (member (or (thing-at-point 'word) "s")
                            '("n" "t" "es" "s"))))
          (capitalize-word 1))))

(ert-deftest my-title-capitalization ()
  "Tests proper English title capitalization"
  (should (string= (with-temp-buffer
		     (insert "the presentation of this heading of my own from my keyboard and yet\n")
		     (goto-char (point-min))
		     (set-mark-command nil)
		     (goto-char (point-max))
		     ;(transient-mark-mode 1)
		   "The Presentation of This Heading of My Own from My Keyboard and Yet\n"
(bind-key "c" #'title-capitalization my-map)


Toggle the windows split between horizontally and vertically. I usually don’t use it though.


(defun my-toggle-vertical-horizontal-split ()
  "Switch window split from horizontally to vertically, or vice versa.

i.e. change right window to bottom, or change bottom window to right."
  (require 'windmove)
  (let ((done))
    (dolist (dirs '((right . down) (down . right)))
      (unless done
        (let* ((win (selected-window))
               (nextdir (car dirs))
               (neighbour-dir (cdr dirs))
               (next-win (windmove-find-other-window nextdir win))
               (neighbour1 (windmove-find-other-window neighbour-dir win))
               (neighbour2 (if next-win (with-selected-window next-win
                                          (windmove-find-other-window neighbour-dir next-win)))))
          ;;(message "win: %s\nnext-win: %s\nneighbour1: %s\nneighbour2:%s" win next-win neighbour1 neighbour2)
          (setq done (and (eq neighbour1 neighbour2)
                          (not (eq (minibuffer-window) next-win))))
          (if done
              (let* ((other-buf (window-buffer next-win)))
                (delete-window next-win)
                (if (eq nextdir 'right)
                (set-window-buffer (windmove-find-other-window neighbour-dir) other-buf))))))))

;(bind-key "|" 'my-toggle-split-and-single-window my-map)

my-yank-windows → my-map y

Yanking from the windows clipboard results in messed up lists. When using this special yank function, common list formatting is fixed for Org-mode syntax.

  • id:2016-05-22-my-yank-windows
(when (my-system-type-is-windows)

  (defun my-yank-windows ()
    "yanks from clipboard and replaces typical (list) markup"
    (let ((mybegin (point)))              ;; mark beginning of line as start point
        (narrow-to-region mybegin (point))  ;; ignore everything outside of region
        (recode-region (point-min) (point-max) 'latin-1 'windows-1252); fix char encoding, e.g.: \366 -> ö
        (goto-char (point-min))
        (while (search-forward "\"	" nil t)
          (replace-match "- " nil t))
        (goto-char (point-min))
        (while (search-forward "o	" nil t)
          (replace-match "  - " nil t))
        (while (search-forward "" nil t)
          (replace-match "\"" nil t))
        (while (search-forward "" nil t)
          (replace-match "\"" nil t))
        (while (search-forward "" nil t)
          (replace-match "'" nil t))
        (while (search-forward "" nil t)
          (replace-match "-" nil t))
        ;;(while (search-forward "1.	" nil t) ;; FIXXME: replace with regex-methods for numbers in general
        ;; (replace-match "1. " nil t))

  (bind-key "y" 'my-yank-windows my-map)


my-fill-or-unfill() paragraph

M-q does fix paragraph formatting and is one of my most favorite commands in GNU/Emacs. If you need to go back to «one line per paragraph», this function offers a toggle function for M-q. Applied twice, it re-formats the current paragraph to one line. Very handy for copy/paste to web forms or such where you need one paragraph per line.

(defun my-fill-or-unfill ()
  "Like `fill-paragraph', but unfill if used twice."
  (let ((fill-column
         (if (eq last-command 'my-fill-or-unfill)
             (progn (setq this-command nil)
    (call-interactively 'fill-paragraph nil (vector nil t))))

(global-set-key [remap fill-paragraph]


Some times, I want to use an external application for opening a certain file instead of opening it in Emacs. This can be done using following function:

(defun my-open-in-external-app (&optional file)
  "Open the current FILE or dired marked files in external app.
   The app is chosen from your OS's preference."
  (message "%s" (concat "my-open-in-external-app called with \"" file "\" as argument"))
  ;; FIXXME: add check if FILE is an existing file; show error message if not
  (let ( doIt
           ((string-equal major-mode "dired-mode") (dired-get-marked-files))
           ((not file) (list (buffer-file-name)))
           (file (list file)))))

    (setq doIt (if (<= (length myFileList) 5)
                 (y-or-n-p "Open more than 5 files? ") ) )

    (when doIt
        (mapc (lambda (fPath) (w32-shell-execute "open" (replace-regexp-in-string "/" "\\" fPath t t)) ) myFileList))
       ((string-equal system-type "darwin")
        (mapc (lambda (fPath) (shell-command (format "open \"%s\"" fPath)) )  myFileList) )
        (mapc (lambda (fPath) (let ((process-connection-type nil)) (start-process "" nil "xdg-open" fPath)) ) myFileList)
) ) ) ) )



(defun my-buffer-exists (bufname)
  (not (eq nil (get-buffer bufname)))


I got this code from pragmaticemacs:

;; example:                                                                ;;
;; from                                      ;;
(defun my-comment-box (b e)
"Draw a box comment around the region but arrange for the region to extend to at least the fill column. Place the point after the comment box."

(interactive "r")

(let ((e (copy-marker e t)))
  (goto-char b)
  (insert-char ?  (- fill-column (current-column)))
  (comment-box b e 1)
  (goto-char e)
  (set-marker e nil)))

my-scroll-up-command() my-scroll-down-command()

Usually, C-p and C-n are mapped to scroll-down-command and scroll-up-command. Contrary to this, I like to scroll only half of a screen so that I can follow the content in a better way.

The following implementation was inspired by (and copied from)

(defun my-window-half-height ()
  (max 1 (/ (1- (window-height (selected-window))) 2)))

(defun my-scroll-up-half ()
  (scroll-up (my-window-half-height)))

(defun my-scroll-down-half ()
  (scroll-down (my-window-half-height)))


2021-03-26added to my config after test looks awesome
(defun my-screenshot-svg ()
  "Save a screenshot of the current frame as an SVG image.
Saves to a temp file to /tmp/ and puts the filename in the kill ring."
  (let* ((filename (make-temp-file "Emacs" nil ".svg"))
         (data (x-export-frames nil 'svg)))
    (with-temp-file filename
      (insert data))
    (kill-new filename)
    (message filename)))

Resulting image can ge opened, e.g., in inkscape. Using the context menu feature “Enter group #…” you can go as deep into the elements as you wish. It really looks amazing.

Spell checking

«Flyspell enables on-the-fly spell checking in Emacs by the means of a minor mode.»

Please do evaluate this only if “aspell” is found on the system:

(when (my-eval-if-binary-or-warn "aspell")

General settings

setting path to flyspell-mode.el from MacPorts:

(when (my-system-type-is-darwin)
  (add-to-list 'load-path "/opt/local/share/emacs/lisp/textmodes")

save to user dictionary without asking:

(setq ispell-silently-savep t)



(autoload 'flyspell-mode "flyspell" "On-the-fly spelling checking" t)

Dictionary Settings

;(set-default 'ispell-local-dictionary my-german-ispell-dictionary)

;;(autoload 'flyspell-mode "flyspell" "On-the-fly ispell." t)
(setq flyspell-issue-welcome-flag nil)

(when (my-system-type-is-windows)
  (setq flyspell-default-dictionary "american")
(when (my-system-type-is-gnu)
  (setq flyspell-default-dictionary "de_AT")

from here to my-toggle-ispell-english-deutsch: see id:2014-01-06-aspell-issue

(eval-after-load "ispell"
  '(add-to-list 'ispell-dictionary-alist
                   "[a-zA-ZäöüßÄÖÜ]" "[^a-zA-ZäöüßÄÖÜ]" "[']" t
                  ("-C" "-d" "de_DE-neu.multi")
                  "~latin1" iso-8859-1)))

(when (my-system-type-is-windows)
    ;; use english on powerplantwin:
    (let ((langs '("american" "german8")))
      (setq lang-ring (make-ring (length langs)))
      (dolist (elem langs) (ring-insert lang-ring elem)))
(when (my-system-type-is-gnu)
    ;; use US english on powerplantwin:
    (let ((langs '("de_AT" "en_US")))
      (setq lang-ring (make-ring (length langs)))
      (dolist (elem langs) (ring-insert lang-ring elem)))
;;  ;; use american english on all other systems:
;;  (let ((langs '("german8" "american")))
;;    (setq lang-ring (make-ring (length langs)))
;;    (dolist (elem langs) (ring-insert lang-ring elem)))
;;  )


my-toggle-ispell-language() because I use two languages and switch between them:

(defun my-toggle-ispell-language ()
  (let ((lang (ring-ref lang-ring -1)))
    (ring-insert lang-ring lang)
    (ispell-change-dictionary lang)))


2021-06-20disabled since I can’t remember that it did anything for me

This mode determines the dictionary language for the current buffer according to the text found. It switches language automatically when you switch the language you’re typing.

What a relief for bilingual people like me (German/English).

(use-package auto-dictionary
   :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/auto-dictionary-mode/")))

(require 'auto-dictionary)
;;(add-hook 'text-mode-hook 'flyspell-mode) ;; according to
;;(add-hook 'flyspell-mode-hook (lambda () (auto-dictionary-mode 1)))

Spellchecking Source Code

Modes for programming languages; check spelling only in comments/strings

(add-hook          'c-mode-hook 'flyspell-prog-mode)
(add-hook         'sh-mode-hook 'flyspell-prog-mode)
(add-hook        'c++-mode-hook 'flyspell-prog-mode)
(add-hook       'ruby-mode-hook 'flyspell-prog-mode)
(add-hook      'cperl-mode-hook 'flyspell-prog-mode)
(add-hook     'python-mode-hook 'flyspell-prog-mode)
(add-hook   'autoconf-mode-hook 'flyspell-prog-mode)
(add-hook   'autotest-mode-hook 'flyspell-prog-mode)
(add-hook   'makefile-mode-hook 'flyspell-prog-mode)
(add-hook 'emacs-lisp-mode-hook 'flyspell-prog-mode)


2018-06-19: disabled most bindings because I moved those functions to a hydra with F5

(define-key global-map [(f5)] 'flyspell-mode)
(bind-key "fm" 'flyspell-mode my-map);; also mapped to F5
(bind-key "fr" 'flyspell-region my-map)
(bind-key "fl" 'my-toggle-ispell-language my-map);; also mapped to Shift-F5
(define-key global-map [(shift f5)] 'my-toggle-ispell-language)
(bind-key "ft" 'my-toggle-ispell-language my-map);; can't remember if l(anguage) or t(oggle)
(bind-key "fn" 'flyspell-goto-next-error my-map)
(bind-key "ff" 'flyspell-correct-word-before-point my-map)

For quickly correcting text, I keep F7 (next error) and F8 (fix) for going through the findings one by one:

(define-key global-map [(f7)] 'flyspell-goto-next-error)
(define-key global-map [(f8)] 'flyspell-correct-word-before-point)

End of aspell

);; when aspell found


«Flycheck is a modern on-the-fly syntax checking extension for GNU Emacs, intended as replacement for the older Flymake extension which is part of GNU Emacs.»

(use-package flycheck
  :ensure t
  (setq flycheck-flake8-maximum-line-length 200);


German blog article on snippet systems:

I do recommend to use snippet systems for quickly inserting static (words/numbers, sentences, paragraphs, …) or dynamic (current date/time) text.

Most snippets, I do define in a system-wide tool so that I am able to use them in every program. Some snippets I do define and use only within Emacs. Yasnippet and yankpad offers me very advanced functionality to define and use most elaborate snippets. Those snippets vary from simple ones (e.g., check-lists for packing for vacations) to rather advanced ones (e.g., a complete lecture organization with many tasks and their dependencies).


Yasnippet is the snippet tool to use within Emacs:

(use-package yasnippet
  ;:load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/yasnippet/")))
  :demand t
  :mode ("/\\.emacs\\.d/etc/yasnippet/snippets/" . snippet-mode)
  :diminish yas-minor-mode
  ;;:defer 90
  (yas-load-directory (concat my-user-emacs-directory "etc/yasnippet/snippets/"))
  (yas-global-mode 1)

  ;;disabled;(my-load-local-el "contrib/yasnippet/yasnippet.el")
  ;;(autoload 'yas-minor-mode "yasnippet")

  ;;disabled 2015-04-01 - issues did not vanish;; ;;
  ;;disabled 2015-04-01 - issues did not vanish;; ;; How to I use alternative keys, i.e. not TAB?
  ;;disabled 2015-04-01 - issues did not vanish;; ;; see id:2015-02-01-yas-expand-not-TAB
  ;;disabled 2015-04-01 - issues did not vanish;; (define-key yas-minor-mode-map (kbd "<tab>") nil)
  ;;disabled 2015-04-01 - issues did not vanish;; (define-key yas-minor-mode-map (kbd "TAB") nil)
  ;;disabled 2015-04-01 - issues did not vanish;; (define-key yas-minor-mode-map (kbd "<f4>") 'yas-expand)

  (my-log-hostspecific "yas--version" yas--version)

As of 2019-12-07, I moved more or less all snippets from plain yasnippet to yankpad.


yankpad is an add-on that enables easy management of yasnippet snippets within an Org-mode file. I do define Org-mode-independent snippets with the basic yasnippet methods. Any snippet that is used within Org-mode only is defined in my yankpad file.

  • see also: id:2016-08-08-yankpad-test
(use-package yankpad
  :ensure t
  ;;:defer 110
  (setq yankpad-file (concat my-org-files-path ""))
  :bind (:map my-map ("SPC" . yankpad-insert)
                     ("y" . yankpad-expand)
  (bind-key "<S-f5>" 'yankpad-expand)
  (bind-key "<S-f6>" 'yankpad-map)
  (setq yankpad-default-category "org-mode")


company (completion)

2021-04-04stolen config from for playing with LSP
(use-package company
    :ensure t
    (setq company-idle-delay 0)
    (setq company-minimum-prefix-length 3)

    ;; (add-hook 'after-init-hook 'global-company-mode)

    (add-hook 'python-mode-hook 'company-mode)

    ;; disable company mode in org mode:  (doesn't work → company was enabled all the time; disabled global company mode and enabled it only for Python)
    ;; (add-hook 'org-mode-hook (lambda () (company-mode -1)))


This is a section where all Git-related configuration is happening.

magit → my-map g

«Magit is an interface to the version control system Git, implemented as an Emacs package. Magit aspires to be a complete Git porcelain. While we cannot (yet) claim that Magit wraps and improves upon each and every Git command, it is complete enough to allow even experienced Git users to perform almost all of their daily version control tasks directly from within Emacs. While many fine Git clients exist, only Magit and Git itself deserve to be called porcelains.»

Some people start using Emacs just to be able to use this nifty Git interface of Magit.

;;2018-07-09 test when using GitHub version;;(setq with-editor-file-name-history-exclude 1) ;;

(use-package magit
  ;;2018-07-09 disabled because lots of errors on load;; :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/magit/lisp/")))

  :if (and (my-eval-if-binary-or-warn "git") (my-system-type-is-gnu))
  :ensure t ;; install package if not found OR: (setq use-package-always-ensure t)
  ;;  :bind (:map magit-status-mode-map ("q" magit-quit-session))
  :config ;; executed after loading package

  (setq magit-diff-refine-hunk 'all) ;; enable in-line diff highlighting

  ;; full screen magit-status
  (defadvice magit-status (around magit-fullscreen activate)
    (window-configuration-to-register :magit-fullscreen)

  (defun magit-quit-session ()
    "restores the previous window configuration and kills the magit buffer"
    (jump-to-register :magit-fullscreen))
(bind-key "g" #'magit-status my-map)

smeargle (highlighting)

Disabled 2020-01-07 because I don’t use it.

  • smeargle - Highlighting Regions by Last Updated Time (my-map c)
    • M-x smeargle - Highlight regions by last updated time.
    • M-x smeargle-age - Highlight regions by age of changes.
    • M-x smeargle-clear - Clear overlays in current buffer
(use-package smeargle
  :ensure t
  :defer 110
  :config ;; executed after loading package
  ;;:bind (:map my-map ("c" . smeargle)) ;; CONFLICTS with my title capitalization

You can set highlighted colors of smeargle by changing smeargle-colors:

 '(smeargle-colors '((older-than-1day   . "red")
                     (older-than-3day   . "green")
                     (older-than-1week  . "yellow")
                     (older-than-2week  . nil)
                     (older-than-1month . "orange")
                     (older-than-3month . "pink")
                     (older-than-6month . "cyan")
                     (older-than-1year . "grey50"))))

And you can also set colors of smeargle-commits by smeargle-age-colors:

 '(smeargle-age-colors '((0 . nil)
                         (1 . "grey80")
                         (2 . "grey70")
                         (3 . "grey60")
                         (4 . "grey50")
                         (5 . "grey40")
                         (6 . "grey30")
                         (7 . "grey20")
                         (8 . "grey10"))))

You can specify parameters until smeargle-age-threshold. age is set to smeargle-age-threshold if actual age of changes is older than smeargle-age-threshold. Default value of smeargle-age-threshold is 7.

Misc settings:

(global-set-key (kbd "C-x v s") 'smeargle)
(global-set-key (kbd "C-x v c") 'smeargle-commits)

;; Highlight regions at opening file
(add-hook 'find-file-hook 'smeargle)

;; Updating after save buffer
(add-hook 'after-save-hook 'smeargle)

End of smeargle:

);; end of smeargle


2019-12-31: DISABLED for performance reasons:

GitGutter is visualizing modified lines within the source code buffer.

(use-package git-gutter
  :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/emacs-git-gutter/")))

Either you use the global minor mode …

(global-git-gutter-mode +1)

;; inactivate git-gutter-mode in asm-mode and image-mode
  '(git-gutter:disabled-modes '(asm-mode image-mode)))

… or activate the minor mode for specific modes:

(add-hook 'python-mode-hook 'git-gutter-mode)
(add-hook 'ruby-mode-hook 'git-gutter-mode)

Keyboard bindings: Jump to next/previous hunk

(global-set-key (kbd "S-<f2>") 'git-gutter:previous-hunk)
(global-set-key (kbd "S-<f3>") 'git-gutter:next-hunk)

More bindings:

(global-set-key (kbd "C-x C-g") 'git-gutter)
(global-set-key (kbd "C-x v =") 'git-gutter:popup-hunk)

;; Jump to next/previous hunk
(global-set-key (kbd "C-x p") 'git-gutter:previous-hunk)
(global-set-key (kbd "C-x n") 'git-gutter:next-hunk)

;; Stage current hunk
(global-set-key (kbd "C-x v s") 'git-gutter:stage-hunk)

;; Revert current hunk
(global-set-key (kbd "C-x v r") 'git-gutter:revert-hunk)

;; Mark current hunk
(global-set-key (kbd "C-x v SPC") #'git-gutter:mark-hunk)

If you set git-gutter:update-interval seconds larger than 0, git-gutter updates diff information in real-time by idle timer.

'(git-gutter:update-interval 2))

Pass option to ‘git diff’ command: You can pass git diff option to set git-gutter:diff-option.

;; ignore all spaces
 '(git-gutter:diff-option "-w"))

Customize visualization:

 '(git-gutter:modified-sign "~ ") ;; two space
 '(git-gutter:added-sign "++")    ;; multiple character is OK
 '(git-gutter:deleted-sign "--"))

(set-face-background 'git-gutter:modified "purple") ;; background color
(set-face-foreground 'git-gutter:added "green")
(set-face-foreground 'git-gutter:deleted "red")

You can change minor-mode name in mode-line to set git-gutter:lighter. Default is GitGutter. First character should be a space.

 '(git-gutter:lighter " GG"))

End of git-gutter:



2019-12-08: disabled because I was not using it.

Step through historic versions: (2018-09-02: repo has been removed)

A page with an animated GIF showing its functionality.

Step through historic versions of git controlled file using everyone’s favourite editor

Visit a git-controlled file and issue M-x git-timemachine (or bind it to a keybinding of your choice). If you just need to toggle the time machine you can use M-x git-timemachine-toggle.

Use the following keys to navigate historic version of the file

pVisit previous historic version
nVisit next historic version
wCopy the abbreviated hash of the current historic version
WCopy the full hash of the current historic version
gGoto nth revision
qExit the time machine.
bRun magit-blame on the currently visited revision (if magit available).
(use-package git-timemachine
  :ensure t
  :defer 120

Source -> start from selected revision instead of HEAD

(defun my-git-timemachine-show-selected-revision ()
  "Show last (current) revision of file."
  (let (collection)
    (setq collection
          (mapcar (lambda (rev)
                    ;; re-shape list for the ivy-read
                    (cons (concat (substring (nth 0 rev) 0 7) "|" (nth 5 rev) "|" (nth 6 rev)) rev))
    (ivy-read "commits:"
              :action (lambda (rev)
                        (git-timemachine-show-revision rev)))))

Open git snapshot with the selected version. Based on ivy-mode:

(defun my-git-timemachine ()
  "Open git snapshot with the selected version.  Based on ivy-mode."
  (unless (featurep 'git-timemachine)
    (require 'git-timemachine))
  (git-timemachine--start #'my-git-timemachine-show-selected-revision))

end of git-timemachine:

);; end of git-timemachine



This requires ov.el:

(use-package ov
   :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/ov/")))
(defun unpackaged/magit-log--add-date-headers (&rest _ignore)
  "Add date headers to Magit log buffers."
  (when (derived-mode-p 'magit-log-mode)
      (ov-clear 'date-header t)
      (goto-char (point-min))
      (cl-loop with last-age
               for this-age = (-some--> (ov-in 'before-string 'any (line-beginning-position) (line-end-position))
                                        (overlay-get it 'before-string)
                                        (get-text-property 0 'display it)
                                        (s-match (rx (group (1+ digit) ; number
                                                            " "
                                                            (1+ (not blank))) ; unit
                                                     (1+ blank) eos)
               do (when (and this-age
                             (not (equal this-age last-age)))
                    (ov (line-beginning-position) (line-beginning-position)
                        'after-string (propertize (concat " " this-age "\n")
                                                  'face 'magit-section-heading)
                        'date-header t)
                    (setq last-age this-age))
               do (forward-line 1)
               until (eobp)))))

(define-minor-mode unpackaged/magit-log-date-headers-mode
  "Display date/time headers in `magit-log' buffers."
  :global t
  (if unpackaged/magit-log-date-headers-mode
        ;; Enable mode
        (add-hook 'magit-post-refresh-hook #'unpackaged/magit-log--add-date-headers)
        (advice-add #'magit-setup-buffer-internal :after #'unpackaged/magit-log--add-date-headers))
    ;; Disable mode
    (remove-hook 'magit-post-refresh-hook #'unpackaged/magit-log--add-date-headers)
    (advice-remove #'magit-setup-buffer-internal #'unpackaged/magit-log--add-date-headers)))

End of Git

) ;; end of magit use-package
;(my-log-hostspecific "magit-version" magit-version) ;; doesn't work (anywhere here) for some reason

highlight-symbol → my-map h

This package does highlight all occurrences of a given word. Very handy when programming: visualize all occurrences of a variable/function.

Note: This package here somehow overlaps with the “hover” feature of a LSP server that supports it. In that case, you should disable the highlight-symbol-mode below.

(use-package highlight-symbol
  :ensure t
  :defer 110
  ;;(bind-key (kbd "h") #'highlight-symbol my-map)
  :bind (:map my-map ("h" . highlight-symbol))
  :hook (python-mode . highlight-symbol-mode)
;; original: (global-set-key [(control f3)] 'highlight-symbol)
;; original: (global-set-key [f3] 'highlight-symbol-next)
;; original: (global-set-key [(shift f3)] 'highlight-symbol-prev)
;; original: (global-set-key [(meta f3)] 'highlight-symbol-query-replace)


2020-12-10initial setup
(use-package projectile
  :ensure t
  :defer 110

Language Server Protocol (LSP) → C-c l

2021-04-03initial setup for testing with Python/pyls

Why? Python code completion was always a bit of a drag. I don’t code that often. When there was a long break between my Python sessions, somehow code completion was a fragile thing that often did not work. Because of this thread I do give LSP a try.


(use-package lsp-mode
  :ensure t
  ;; set prefix for lsp-command-keymap (few alternatives - "C-l", "C-c l")
  (setq lsp-keymap-prefix "C-c l")
  :hook ((python-mode . lsp)
         ;; if you want which-key integration
         ;; (lsp-mode . lsp-enable-which-key-integration) ;; 2021-04-04: I'm using lsp-deferred instead
  :commands lsp
  • [ ] FIXXME: maybe customize
    • lsp-pyls-plugins-flake8-ignore
    • lsp-pyls-plugins-pycodestyle-max-line-length
    • lsp-pyls-plugins-flake8-max-line-length


(use-package lsp-ui
    :ensure t
    :commands lsp-ui-mode
    :hook (lsp-mode . lsp-ui-mode)
    (setq lsp-ui-sideline-enable t)
    (setq lsp-ui-sideline-show-hover nil)
    (setq lsp-ui-doc-position 'bottom)
    ;; lsp config stuff
    (setq lsp-enable-links t)
    (setq lsp-signature-render-documentation t)
    (setq lsp-headerline-breadcrumb-enable t)
    (setq lsp-ui-doc-enable t)
    (setq lsp-completion-enable-additional-text-edit nil)
    (setq web-mode-enable-current-element-highlight t)

for helm:

(use-package helm-lsp :ensure t :commands helm-lsp-workspace-symbol)


;; ;; if you are ivy user
;; (use-package lsp-ivy :commands lsp-ivy-workspace-symbol)
;; (use-package lsp-treemacs :commands lsp-treemacs-errors-list)

;; optionally if you want to use debugger
;; watch to get an idea
;; (use-package dap-mode :ensure t)
;; (use-package dap-python :ensure t) ;; to load the dap adapter for your language

;;;; optional if you want which-key integration
;;(use-package which-key
;;    :ensure t
;;    :config
;;    (which-key-mode))


This heading contains configurations for editing Elisp code.

separate color for highlightning () brackets:

;; ######################################################
(defface paren-face
  '((((class color) (background dark))
     (:foreground "grey30"))
    (((class color) (background light))
     (:foreground "grey60")))
  "Face used to dim parentheses.")
(defun egoge-dim-parens ()
  (font-lock-add-keywords nil
			  '(("(\\|)" . 'paren-face))))
(add-hook 'emacs-lisp-mode-hook 'egoge-dim-parens)

Do not use Auto Fill Mode for Lisp mode:

(add-hook 'emacs-lisp-mode-hook 'turn-off-auto-fill)

When editing code that uses parenthesis, enabling this does highlight the matching parenthesis:

(show-paren-mode t)


for using unit tests of yasnippet (see id:2013-02-07yasnippetdebuggen and yasnippet-tests.el)

(my-load-local-el "contrib/cl-lib.el")
(my-load-local-el "contrib/ert.el")
(my-load-local-el "contrib/ert-x.el")

buttercup - Elisp test suite

Buttercup is a behavior-driven development framework for testing Emacs Lisp code. It allows to group related tests so they can share common set-up and tear-down code, and allows the programmer to “spy” on functions to ensure they are called with the right arguments during testing.

The framework is heavily inspired by Jasmine.

Disabled for now because I do not use it at the moment.

(use-package buttercup
  :ensure t
;  :if (my-system-is-floyd)
  :defer 110

smartparens - highlight corresponding parens

(use-package smartparens
  (smartparens-global-mode 1)
  (show-smartparens-global-mode +1)

  :bind (;; ("M-n" . sp-next-sexp)
         ;; ("M-p" . sp-previous-sexp)
         ("M-f" . sp-forward-sexp)
         ("M-b" . sp-backward-sexp)

  ;; Enable smartparens everywhere
  (use-package smartparens-config)

  ;; ;; Require and disable paredit because some packages rely on it.
  ;; (use-package paredit)
  ;; (disable-paredit-mode)

   smartparens-strict-mode t
   sp-autoinsert-if-followed-by-word t
   sp-autoskip-closing-pair 'always
   ;;sp-base-key-bindings 'paredit
   sp-hybrid-kill-entire-symbol nil)

  ;; (sp-use-paredit-bindings)

  ;; (sp-with-modes '(markdown-mode gfm-mode rst-mode)
  ;;   (sp-local-pair "*" "*" :bind "C-*")
  ;;   (sp-local-tag "2" "**" "**")
  ;;   (sp-local-tag "s" "```scheme" "```")
  ;;   (sp-local-tag "<"  "<_>" "</_>" :transform 'sp-match-sgml-tags))

  ;; ;; Close a backtick with another backtick in clojure-mode
  ;; (sp-local-pair 'clojure-mode "`" "`" :when '(sp-in-string-p))

  (sp-local-pair 'emacs-lisp-mode "`" nil :when '(sp-in-string-p))

s - «The long lost Emacs string manipulation library.»

(use-package s
  :ensure t
  :defer 90

Elisp: → my-map er|el|ef

Misc bindings for Elisp:

(bind-key "er" #'eval-region my-map)
(bind-key "el" #'find-library my-map)
(bind-key "ef" #'find-function-at-point my-map)


I found the macrostep package through the Irreal article John Wiegley and Sacha Chua on use-package which links 2015-04-01 Emacs package highlight: use-package from YouTube.

(use-package macrostep
  ;;:ensure t
  :defer 120
  :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/macrostep/")))
  :config ;; executed after loading package
  (define-key emacs-lisp-mode-map (kbd "C-c e") 'macrostep-expand)

From the README:

The standard keybindings in macrostep-mode are the following:

e, =, RET
expand the macro form following point one step
c, u, DEL
collapse the form following point
q, C-c C-c
collapse all expanded forms and exit macrostep-mode
n, TAB
jump to the next macro form in the expansion
p, M-TAB
jump to the previous macro form in the expansion

It’s not very useful to enable and disable macrostep-mode directly. Instead, bind macrostep-expand to a key in emacs-lisp-mode-map, for example C-c e:

 (define-key emacs-lisp-mode-map (kbd "C-c e") 'macrostep-expand)

You can then enter macrostep-mode and expand a macro form completely by typing C-c e e e ... as many times as necessary.

Exit macrostep-mode by typing q or C-c C-c, or by successively typing c to collapse all surrounding expansions.


2021-07-31I wanted to test the Elisp formatter for formatting my code
(use-package elisp-format
  :ensure t
  :defer 110


2021-04-04shift from previous elpy-based setup to a LSB-based setup

This heading contains configurations for editing Python code. Python is the programming language I prefer for my private projects. I do like its easy-to-read syntax, providing a high level of maintainability. It also ships with a large set of libraries.

Selected keyboard commands (summary): (see defined hydra for python mode!)

M-hhide/show current functionhs-cycle
C-c l g gfind definitionlsp-find-definition
C-c l g rfind referenceslsp-find-references
<missing>function overview

BEGIN of Python-related stuff

The code blocks here are only executed when python is found on the current system:

(when (my-eval-if-binary-or-warn "python")

auto-mode-list for Python files

(add-to-list 'auto-mode-alist '("\\.py$" . python-mode))
(add-to-list 'auto-mode-alist '("\\.py$" . company-mode))

  (add-hook 'python-mode-hook
      (lambda ()

pyls (LSP)

sudo python3 -m pip install 'python-language-server[all]'

or even better:

python3 -m pip install --user 'python-language-server[all]'


sudo python3 -m pip install pyls-mypy

On [2021-08-27 Fri] my Emacs wrote:

Warning (emacs): The palantir python-language-server (pyls) is unmaintained; a maintained fork is the python-lsp-server (pylsp) project; you can install it with pip via: pip install python-lsp-server Disable showing Disable logging

So I did:

sudo python3 -m pip uninstall 'python-language-server[all]'
sudo python3 -m pip install python-lsp-server

jedi (LSP)

[2021-04-04 Sun 16:56] disabled: I want to test pure pyls

Setup according to

(use-package lsp-jedi
  :ensure t
  (with-eval-after-load "lsp-mode"
    (add-to-list 'lsp-disabled-clients 'pyls)
    (add-to-list 'lsp-enabled-clients 'jedi)))

(setq lsp-ui-doc-show-with-cursor nil)

Switch to ipython (NOT YET TESTED)

From this reddit and this solution: NOT TESTED YET! (2017-10-02)

2019-01-20: this reddit thread suggests overwriting python-shell-interpreter in order to switch to Python 3. (not tested/confirmed yet)

(if (my-eval-if-binary-or-warn "ipython3")
    (setq python-shell-interpreter "ipython3"
        python-shell-interpreter-args "--simple-prompt -i")
    ;; else:
    (when (my-eval-if-binary-or-warn "python3")
      (setq python-shell-interpreter "python3")

Note: Org mode babel is using org-babel-python-command which is set elsewhere (to python3 as of 2019-03-05).

flymake, flycheck

2021-04-04I’m unsure if this is still necessary after switching to LSP/pyls

FlyMake performs on-the-fly syntax checks on the files being edited using the external syntax check tool (usually the compiler). Highlights erroneous lines and displays associated error messages.

Unfortunately, this project is outdated and last change was 3 years ago.

For a modern alternative, check out Flycheck.

(when (load "flymake" t)
  (defun flymake-pyflakes-init ()
    (let* ((temp-file (flymake-init-create-temp-buffer-copy
           (local-file (file-relative-name
                        (file-name-directory buffer-file-name))))
      (list "~/bin/pycheckers"  (list local-file))))
  (add-to-list 'flymake-allowed-file-name-masks
               '("\\.py\\'" flymake-pyflakes-init)))

better than flymake (and maintained): Flycheck

(add-hook 'python-mode-hook
    (lambda ()
      (unless (eq buffer-file-name nil) (flymake-mode 1)) ;dont invoke flymake on temporary buffers for the interpreter
      (local-set-key [f6] 'flymake-goto-prev-error)
      (local-set-key [f7] 'flymake-goto-next-error)


I found hideshow-orgmode via this reddit thread. It provides (Python) code outlining similar to Org-mode TAB visibility cycling. This is a very nice thing to have. So far, I used yafolding-mode for it.

This needs the hideshow minor mode M-x hideshow-minor-mode enabled to work.

  (use-package hideshow-orgmode
    :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/hideshow-orgmode")))
    (add-hook 'python-mode-hook (lambda ()
                                (hs-minor-mode 1)
                                (global-set-key (kbd "M-h") 'hs-cycle)
                                (global-set-key (kbd "M-H") 'hs-cycle-all)



sphinx-doc is an emacs minor mode for inserting docstring skeleton for Python functions and methods. The structure of the docstring is as per the requirement of the Sphinx documentation generator.

  (use-package sphinx-doc
    :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/sphinx-doc.el")))
    :ensure t
    (add-hook 'python-mode-hook (lambda ()
                                  (require 'sphinx-doc)
                                  (sphinx-doc-mode t)))


let: Symbol’s value as variable is void: sphinx-doc-python-indent

… when applied. I could not find anything related.

elpygen → C-c i

From GitHub: “Generate Python function/method stub for a call-like symbol under point”

Just write a_function_call(first_named, 2, second_named), invoke the command elpygen-implement and elpygen either jumps to the existing function or it generates a stub for a new one. The example above results in def a_function_call(first_named, arg1, second_named):\npass.

(use-package elpygen
  :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/elpygen")))
  :ensure t
  (require 'elpygen)
  (define-key python-mode-map (kbd "C-c i") 'elpygen-implement)

END of Python-related stuff

);; if python binary found


Usually, I do file management in tmux and zsh. There are my aliases, my functions, the features I like. Having a file manager in Emacs does have some advantages.

Dired tipps from ergoemacs. Mastering Emacs on Dired.

Here are some minor tweaks related to file management in Emacs with dired.

From Emacsrocks #16: two window file management. From the documentation:

If non-nil, Dired tries to guess a default target directory. This means: if there is a Dired buffer displayed in the next window, use its current directory, instead of this Dired buffer’s current directory.

(setq dired-dwim-target t)
;;(setq dired-listing-switches "-al  --group-directories-first")

from: Marcin Borkowski: 2018-12-10 Lesser known Dired stuff: create parent directories when slashes are used when renaming files:

;(setq wdired-create-parent-directories t)  FIXXME: enable when my old installation gets an update

dired-garbage-files-regexp - define garbage file regex

In dired, you can press %& which calls dired-flag-garbage-files(). By default, it uses the regular expression =”\(?:\.\(?:aux\|bak\|dvi\|log\|orig\|rej\|toc\)\)\’”= which does not include miscelaneous file extensions I am working with.

Therefore, I overwrite the regex with my own values:

(setq dired-garbage-files-regexp "\\(?:\\.\\(?:aux\\|bak\\|bbl\\|bcf\\|dvi\\|log\\|rej\\|toc\\)\\)\\'")

dired-details → (

  • [ ] FIXXME 2018-07-06: think of switching to dired-details+ (which offers some functionality I currently do not use).

Also from Emacsrocks #16: hide and show file details with ( and ):

(use-package dired-details
  :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/")))
  ;;:defer 90
  (setq-default dired-details-hidden-string "")

dired-x, dired+, dired-icon

Enabling already pre-insalled dired-x:

(require 'dired-x)

Dired+ has lots of useful features

(use-package dired+
  :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/dired-plus/")))
;;  :ensure t
  ;;:defer 90
  (diredp-toggle-find-file-reuse-dir 1) ;;

2019-02-05: disabled because loading a new dired buffer took very long with it although it really looked nicely:

(use-package dired-icon
:ensure t
;;:defer 110
(add-hook 'dired-mode-hook 'dired-icon-mode)

Bind backspace to visit higher level directory:

(define-key dired-mode-map (kbd "<backspace>") 'diredp-up-directory-reuse-dir-buffer)


  • [ ] ivy-dired-history is an alternative to:
(use-package helm-dired-history
:ensure t
(require 'savehist)
(add-to-list 'savehist-additional-variables 'helm-dired-history-variable)
(savehist-mode 1)
(with-eval-after-load 'dired
  (require 'helm-dired-history)
  (define-key dired-mode-map "," 'dired))

dired-recent → C-z

This is one of the killer-features of (any) file-browser application: offer a quick search of recently visited directories. I never maintain any directory bookmarks any more. I use a similar feature in my zsh via z. You might also look into GoTo (on steroids) of fman.

“Press C-x C-d to select a previously visited directory to open.”

(use-package dired-recent
   :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/dired-recent.el/")))
   ;;:defer 90
   (require 'dired-recent)
   (dired-recent-mode 1)
   (setq dired-recent-max-directories nil) ;; nil means to remember all

Make better use of dired-recent-open using ivy:

(defun my-dired-recent-dirs ()
  "Present a list of recently used directories and open the selected one in dired"
    (let ((dir (ivy-read "Directory: "
                         :re-builder #'ivy--regex
                         :sort nil
                         :initial-input nil)))
      (dired dir)))

(global-set-key (kbd "C-z") 'my-dired-recent-dirs)

Maybe, this can be optimized by using frecency for scoring entries (like in fman or this feature-request for FreeCommander). So far, I’m quite happy with the current feature.

dired-narrow → /

From with

It narrows down the list of files using a pattern. g resets the view.

(use-package dired-narrow
  :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/dired-hacks/")))
  ;;:defer 90
  :bind (:map dired-mode-map
              ("/" . dired-narrow)))




  • dired-open-xdg try to open the file using xdg-open
  • dired-open-guess-shell-alist try to open the file by launching applications from dired-guess-shell-alist-user
  • dired-open-call-function-by-extension call an elisp function based on extension.
(use-package dired-open
  ;;:defer 90
  :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/dired-hacks/")))




  • dired-open-xdg try to open the file using xdg-open
  • dired-open-guess-shell-alist try to open the file by launching applications from dired-guess-shell-alist-user
  • dired-open-call-function-by-extension call an elisp function based on extension.
(use-package dired-collapse
  :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/dired-hacks/")))
  ;;:defer 90


Image-dired extensions:

Emacs manual:

To enter Image-Dired, mark the image files you want to look at in the Dired buffer, using m as usual. Then type C-t d (image-dired-display-thumbs). This creates and switches to a buffer containing image-dired, corresponding to the marked files.

You can also enter Image-Dired directly by typing M-x image-dired. This prompts for a directory; specify one that has image files. This creates thumbnails for all the images in that directory, and displays them all in the thumbnail buffer. This takes a long time if the directory contains many image files, and it asks for confirmation if the number of image files exceeds image-dired-show-all-from-dir-max-files.

FIXXME: 2018-06-30: There is a issue on floyd. I get following error when I mark files and invoke C-t d:

error in process sentinel: image-diredx--invoke-process: Wrong type argument: processp, [nil 23351 46922 931735 nil image-dired-thumb-queue-run nil nil 787000]
error in process sentinel: Wrong type argument: processp, [nil 23351 46922 931735 nil image-dired-thumb-queue-run nil nil 787000]
Marking files...
377 files newly marked
image-diredx--invoke-process: Wrong type argument: processp, [nil 23351 47004 248326 nil image-dired-thumb-queue-run nil nil 669000]
(use-package image-dired+
  :ensure t
  :defer 110
  (require 'image-dired+)
  (image-diredx-async-mode 1)
  (image-diredx-adjust-mode 1)
  (define-key image-dired-thumbnail-mode-map "\C-n" 'image-diredx-next-line)
  (define-key image-dired-thumbnail-mode-map "\C-p" 'image-diredx-previous-line)
  (define-key image-dired-thumbnail-mode-map "g" 'revert-buffer);; revert all thumbnails if `image-diredx-async-mode' is on
  (define-key image-dired-thumbnail-mode-map "x" 'image-diredx-flagged-delete);; Delete confirmation prompt with thumbnails
  (setq image-dired-track-movement nil) ;; Suppress unknown cursor movements
  (setq image-dired-external-viewer "/usr/bin/geeqie") ;; FIXXME: configure different for Windows

dired-ranger → M-c; M-v; M-m


With the multi-stage operations, you can gather files from multiple dired buffers into a single “clipboard”, then copy or move all of them to the target location. Another huge advantage is that if the target dired buffer is already opened, switching to it via ido or ibuffer is often faster than selecting the path.

Call dired-ranger-copy to add marked files (or the file under point if no files are marked) to the “clipboard”. With non-nil prefix argument, add the marked files to the current clipboard.

Past clipboards are stored in dired-ranger-copy-ring so you can repeat the past pastes.

Call dired-ranger-paste or dired-ranger-move to copy or move the files in the current clipboard to the current dired buffer. With raw prefix argument (usually C-u), the clipboard is not cleared, so you can repeat the copy operation in another dired buffer.

(use-package dired-ranger
  :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/dired-hacks/")))
  :defer 110 ;; has to be after dired+ in able to overwrite its bindings below
  (define-key dired-mode-map (kbd "M-c") 'dired-ranger-copy)  ;; overwrites: diredp-capitalize-this-file
  (define-key dired-mode-map (kbd "M-v") 'dired-ranger-paste) ;; overwrites: scroll-down-command
  (define-key dired-mode-map (kbd "M-m") 'dired-ranger-move)  ;; overwrites: back-to-indentation


Via EmacsWiki and download from

(use-package dired-isearch
  :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/")))
  :defer 110

Not necessary any more. There is built-in functionality which I just have to re-map to C-s (because I don’t need reverse or RegEx search):

(define-key dired-mode-map (kbd "C-s") 'dired-isearch-filenames)

looking for: filetags, date2name, appendfilename

(setq my-filetags-file (my-binary-found "filetags"))
(when (and (not my-filetags-file) (my-system-type-is-gnu))
    (progn (message "using manual src path for \"filetags\"")
           (setq my-filetags-file (my-binary-found "/home/vk/src/filetags/filetags/"))

(setq my-date2name-file (my-binary-found "date2name"))
(when (and (not my-date2name-file) (my-system-type-is-gnu))
    (progn (message "using manual src path for \"date2name\"")
           (setq my-date2name-file (my-binary-found "/home/vk/src/date2name/date2name/"))

(setq my-appendfilename-file (my-binary-found "appendfilename"))
(when (and (not my-appendfilename-file) (my-system-type-is-gnu))
    (progn (message "using manual src path for \"appendfilename\"")
           (setq my-appendfilename-file (my-binary-found "/home/vk/src/appendfilename/appendfilename/"))

my-dired-filetags → M-t

(defun my-dired-filetags ()
  "Run \"filetags\" on current or marked files"
  (let* ((marked-files (f-uniquify (dired-get-marked-files)))) ;; apply to single file or marked files
    (when (string-equal system-name "GRZN17009")
      ;;works            (let ((proc (start-process "cmd" nil "cmd.exe" "/C" "start" "cmd.exe" "/K" "echo \"Hello\"")))
      ;;(message (concat "CALLING: c:/Users/karl.voit/bin/filetags.bat -sv " marked-files))
      (let ((proc (start-process "cmd" nil "cmd.exe" "/C" "start" "cmd.exe" "/C"
                                 "c:/Users/karl.voit/bin/filetags.bat -sv"
                                 (shell-quote-argument marked-files)))
        (set-process-query-on-exit-flag proc nil)))
      ;;(dired-do-shell-command "cmd.exe /K c:/Users/karl.voit/bin/filetags.bat *" nil marked-files)

    (when (string-equal system-name "cosmo")
      (let ((proc (start-process "cmd" nil "cmd.exe" "/C" "start" "cmd.exe" "/C"
                                 "C:/Users/John/AppData/Roaming/filetags.bat -sv"
                                 (shell-quote-argument marked-files)))
        (set-process-query-on-exit-flag proc nil)))

    (when (my-system-type-is-gnu)
      ;; FIXXME: maybe add check for binary: (when (my-eval-if-binary-or-warn "xfce4-terminal")
      ;; --disable-server → provides a blocking terminal window (instead of a non-blocking one which swallows the revert-buffer afterward)
        (dired-do-shell-command (concat "xfce4-terminal --disable-server --geometry=100x20+330+5 --hide-menubar -x " my-filetags-file " --interactive *") nil marked-files)

  (revert-buffer nil t t) ;; refresh listing of files

(define-key dired-mode-map (kbd "M-t") 'my-dired-filetags)

my-dired-appendfilename → M-a

(defun my-dired-appendfilename ()
  "Run \"appendfilename\" on current or marked files"
  (let* ((marked-files (f-uniquify (dired-get-marked-files)))) ;; apply to single file or marked files
    (when (my-system-type-is-windows)
      (dired-do-shell-command "FIXXME xfce4-terminal --geometry=100x20+330+5 --hide-menubar -x /home/vk/bin/filetags --interactive *" nil marked-files)
    (when (my-system-type-is-gnu)
      ;; FIXXME: maybe add check for binary: (when (my-eval-if-binary-or-warn "xfce4-terminal")
      ;; --disable-server → provides a blocking terminal window (instead of a non-blocking one which swallows the revert-buffer afterward)
          (dired-do-shell-command (concat "xfce4-terminal --disable-server --geometry=90x5+330+5 --hide-menubar -x " my-appendfilename-file " *") nil marked-files)
  (revert-buffer nil t t)

(define-key dired-mode-map (kbd "M-a") 'my-dired-appendfilename)

my-dired-filetags-filter → F1

(defun my-dired-filetags-filter ()
  "Run \"filetags --filter\" on current or marked files"
  (let* ((marked-files (f-uniquify (dired-get-marked-files)))) ;; apply to single file or marked files
    (when (my-system-type-is-windows)
      (dired-do-shell-command "FIXXME xfce4-terminal --geometry=100x20+330+5 --hide-menubar -x /home/vk/bin/filetags --interactive *" nil marked-files)
    (when (my-system-type-is-gnu)
      ;; FIXXME: maybe add check for binary: (when (my-eval-if-binary-or-warn "xfce4-terminal")
      (dired-do-shell-command (concat "xfce4-terminal --geometry=90x5+330+5 --hide-menubar -x " my-filetags-file " --filter *") nil marked-files)

my-dired-filetags-filter-recursive → F1

(defun my-dired-filetags-filter-recursive ()
  "Run \"filetags --filter --recursive\" on current or marked files"
  (let* ((marked-files (f-uniquify (dired-get-marked-files)))) ;; apply to single file or marked files
    (when (my-system-type-is-windows)
      (dired-do-shell-command "FIXXME xfce4-terminal --geometry=100x20+330+5 --hide-menubar -x /home/vk/bin/filetags --interactive *" nil marked-files)
    (when (my-system-type-is-gnu)
      ;; FIXXME: maybe add check for binary: (when (my-eval-if-binary-or-warn "xfce4-terminal")
      (dired-do-shell-command (concat "xfce4-terminal --geometry=90x5+330+5 --hide-menubar -x " my-filetags-file " --filter --recursive *") nil marked-files)

my-dired-tagtrees → F1

(defun my-dired-tagtrees ()
  "Run \"filetags --tagtrees\" on current or marked files"
  (let* ((marked-files (f-uniquify (dired-get-marked-files)))) ;; apply to single file or marked files
    (when (my-system-type-is-windows)
      (dired-do-shell-command "filetags --tagtrees --tagtrees-handle-no-tag no-tags" nil marked-files)
    (when (my-system-type-is-gnu)
      ;; FIXXME: maybe add check for binary: (when (my-eval-if-binary-or-warn "xfce4-terminal")
      (dired-do-shell-command (concat "xfce4-terminal --geometry=90x5+330+5 --hide-menubar -x " my-filetags-file " --tagtrees --tagtrees-handle-no-tag no-tags *") nil marked-files)

my-dired-tagtrees-recursive → F1

(defun my-dired-tagtrees-recursive ()
  "Run \"filetags --tagtrees --recursive\" on current or marked files"
  (let* ((marked-files (f-uniquify (dired-get-marked-files)))) ;; apply to single file or marked files
    (when (my-system-type-is-windows)
      (dired-do-shell-command "C:\\Python36\\Scripts\\filetags.exe --tagtrees --recursive --tagtrees-handle-no-tag no-tags *" nil marked-files)
    (when (my-system-type-is-gnu)
      ;; FIXXME: maybe add check for binary: (when (my-eval-if-binary-or-warn "xfce4-terminal")
      (dired-do-shell-command (concat "xfce4-terminal --geometry=90x5+330+5 --hide-menubar -x " my-filetags-file " --tagtrees --recursive --tagtrees-handle-no-tag no-tags *") nil marked-files)

my-dired-date2name → F1

(defun my-dired-date2name ()
  "Run \"time2name\" on current or marked files"
  (let* ((marked-files (f-uniquify (dired-get-marked-files)))) ;; apply to single file or marked files
      (dired-do-shell-command (concat my-date2name-file " *") nil marked-files)
  (revert-buffer nil t t) ;; refresh listing of files

my-dired-time2name → F1

(defun my-dired-time2name ()
  "Run \"date2name --withtime\" on current or marked files"
  (let* ((marked-files (f-uniquify (dired-get-marked-files)))) ;; apply to single file or marked files
    (dired-do-shell-command (concat my-date2name-file " --withtime *") nil marked-files)
  (revert-buffer nil t t) ;; refresh listing of files

my-open-in-external-app → C-c C-o


(defun my-dired-open-in-external-app ()
  "Open the current file or dired marked files in external app.
The app is chosen from your OS's preference.
URL `'
Version 2016-10-15"
  (let* (
          (if (string-equal major-mode "dired-mode")
            (list (buffer-file-name))))
         ($do-it-p (if (<= (length $file-list) 5)
                     (y-or-n-p "Open more than 5 files? "))))
    (when $do-it-p
       ((string-equal system-type "windows-nt")
         (lambda ($fpath)
           (w32-shell-execute "open" (replace-regexp-in-string "/" "\\" $fpath t t))) $file-list))
       ((string-equal system-type "darwin")
         (lambda ($fpath)
            (concat "open " (shell-quote-argument $fpath))))  $file-list))
       ((string-equal system-type "gnu/linux")
         (lambda ($fpath) (let ((process-connection-type nil))
                            (start-process "" nil "xdg-open" $fpath))) $file-list))))))
(define-key dired-mode-map (kbd "C-c C-o") 'my-dired-open-in-external-app)

Alternative: openwith from this GitHub page mentioned by Using Emacs 71 Openwith.

my-dired-open-in-file-manager → F1

This is xah-show-in-desktop() from and adapted a bit:

  • file-truename() to translate relative paths to absolute
  • thunar as default manager on Linux
  • [ ] FIXXME: OS switching commands
(defun my-dired-open-in-file-manager ()
  "Show current file in desktop.
 (Mac Finder, Windows Explorer, Linux file manager)
 This command can be called when in a file or in `dired'.
URL `'
Version 2018-01-13 adapted by Karl Voit 2018-07-01"
  (let (($path (file-truename (if (buffer-file-name) (buffer-file-name) default-directory ))))
     ((string-equal system-type "windows-nt")
      (w32-shell-execute "explore" (replace-regexp-in-string "/" "\\" $path t t)))
     ((string-equal system-type "darwin")
      (if (eq major-mode 'dired-mode)
          (let (($files (dired-get-marked-files )))
            (if (eq (length $files) 0)
                 (concat "open " (shell-quote-argument default-directory)))
               (concat "open -R " (shell-quote-argument (car (dired-get-marked-files )))))))
         (concat "open -R " $path))))
     ((string-equal system-type "gnu/linux")
      (let (
            (process-connection-type nil)
            (openFileProgram (if (file-exists-p "/usr/bin/thunar")
        (start-process "" nil openFileProgram $path))
      ;; (shell-command "xdg-open .") ;; 2013-02-10 this sometimes froze emacs till the folder is closed. eg with nautilus

my-dired-mark-images-and-thumbnail → F1

(defun my-dired-mark-images-and-thumbnail ()
  "Mark all image files in dired (png, PNG, jpg, JPG, jpeg, JPEG) and start image-dired+."
  (dired-mark-extension '("png" "jpg" "PNG" "JPG" "jpeg" "JPEG" "tif" "tiff" "TIF" "TIFF" "gif" "GIF"))

my-dired-copy-filename-as-absolute-link → F1

I often link files in Org mode documents with their full path such as

[[file:c:/Users/karl.voit/2del/2018-07-04T15.35.42 Outlook - changing recurring events deletes all exceptions -- screenshots.png]]

This would require

  1. select a file
  2. copy the absolute path name in dired via M-0 w
  3. switch to the Org buffer
  4. type
  5. yank the kill ring element containing the absolute path
  6. finish the link with ]]

Following function avoids human error and reduces this process to:

  1. select a file
  2. call the function below via my hydra
  3. switch to a Org buffer
  4. yank the working link from the kill ring
(defun my-dired-copy-filename-as-absolute-link (&optional arg)
  "Copy current file name with absolute path as [[file:<absolute path>]] link.
   If the universal argument is given, the path is omitted in the link description."
  (interactive "P")
  (dired-copy-filename-as-kill 0) ;; current absolute path to kill ring
  (let* ((path (current-kill 0))) ;; get topmost kill ring element
  (if (equal arg '(4))
    ;; universal argument is set:
    (kill-new (concat "[[file:" path "][" (helm-basename path) "]]")) ;; write back new/modified kill ring element
    ;; universal argument is not set:
    (kill-new (concat "[[file:" path "]]")) ;; write back new/modified kill ring element

my-dired-copy-filename-as-tsfile-link → F1

Same explanation as my-dired-copy-filename-as-absolute-link holds but with the result being a tsfile: link I use to link to Memacs-indexed files (see Org Mode customized links):

(defun my-dired-copy-filename-as-tsfile-link ()
  "Copy current file name with its basename as [[tsfile:<basename>]] custom org-mode link."
  (dired-copy-filename-as-kill) ;; current file name to kill ring
  (let* ((filename (current-kill 0))) ;; get topmost kill ring element
    (kill-new (concat "[[tsfile:" filename "]]")) ;; write back new/modified kill ring element


Sometimes, I need the current directory showed in my tmux session running in background. This is how it’s done.

  • [ ] FIXXME: find a working solution for Windows 10
(defun my-dired-open-here-in-tmux-shell()
        (message (concat "NOT Opening current dir in Windows/Cygwin tmux (NOT IMPLEMENTED AND TESTED YET): " dired-directory))
        ;; see id:2018-09-02-open-dir-in-tmux for details on the development and Windows issues
        ;;(mapc (lambda (fPath) (w32-shell-execute "open" (replace-regexp-in-string "/" "\\" fPath t t)) ) myFileList)
       ((string-equal system-type "darwin")
        (message (concat "NOT Opening current dir in macOS tmux (NOT IMPLEMENTED AND TESTED YET): " dired-directory))
        (message (concat "Opening current dir in linux tmux: " dired-directory))
        ;; OLD: (shell-command (concat "tmux new-window 'cd \"" dired-directory "\"; pwd; zsh -i'"))
        (dired-smart-shell-command "xfce4-terminal --execute tmux")


2020-04-13setup for testing

This package got recommended for a specific issue I faced using interactive Python scripts.

(use-package orly
  ;;:if (my-system-type-is-gnu)
  :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/orly/")))
  ;;:defer 110
  :after org

filetags.el → F1 provides filetags-dired-update-tags() which is an Elisp-native re-implementation of my filetags:

(use-package filetags
  :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/filetags.el/")))
  (setq filetags-enforce-controlled-vocabulary nil) ;; let me invent new tags on the fly (might not be a good idea anyway!)
  (setq filetags-load-controlled-vocabulary-from-file t) ;; read CV from .filetags files within same or upper directories
  ;;ignored;; (setq filetags-controlled-vocabulary '("confidential" "restricted" "public" "internal" "internaluse"
  ;;ignored;;  "license" "demos" "proposals" "flipcharts" "data" "draft" "final" "screenshots" "travel"
  ;;ignored;;  "templates" "contracts" "processes"))

date2name → F1 provides date2name-dired-add-date-to-name() which is an Elisp-native re-implementation of my date2name:

(use-package date2name
  :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/date2name.el/")))
  (setq date2name-enable-smart-separation-character-chooser t)

  ;; 2018-07-21:
  (defun file-attribute-modification-time (attributes)
    "extracts the modification time from ATTRIBUTES"
    (nth 5 attributes))

  • [ ] FIXXME: replace with it if it is well tested
  • [ ] FIXXME: map to a direct key
  • [ ] FIXXME: add to cheatsheet (hydra): ask for day when used with universal prefix
  • [ ] FIXXME: do for time2name as well


2020-04-11installed for testing purposes
2020-04-11disabled because it did not work on my files

From this reddit thread I found dired-show-readme which displays README files of various formats (using pandoc conversion) within the directory they are located.

Although I don’t need this particular function, I set it up to test it.

(use-package dired-show-readme
  :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/dired-show-readme/")))
  ;:defer 110
  ;:after dired
  (setq dired-show-readme-position "bottom")
  ;; (setq dired-show-readme-pandoc-executable "/usr/bin/pandoc");; on sting, this is the default
  (add-hook 'dired-mode-hook 'dired-show-readme-mode)


isync + notmuch

2021-02-13disabled since I can’t use business email with Emacs

This is my email setup for notmuch I use together with isync/mbsync/

(use-package notmuch
   :if (my-system-type-is-gnu)

   (load-library "smtpmail")

   (setq smtpmail-default-smtp-server my-business-mail-server
         smtpmail-local-domain my-business-mail-server)

   (setq send-mail-function    'smtpmail-send-it
         smtpmail-smtp-server  my-business-mail-server
         smtpmail-stream-type  'starttls
         smtpmail-smtp-service 587
         mail-host-address my-business-mail-host-address)

   ;; Somehow, this variable gets overwritten by something to 'message-send-mail-with-outlook
   ;; and therefore it's been set in "custom variables" as well.
   (setq message-send-mail-function 'message-smtpmail-send-it)

   (setq notmuch-message-headers '("Subject" "To" "Cc" "Date" "Reply-To"))
   (setq notmuch-mua-cite-function 'message-cite-original-without-signature)
   (setq notmuch-saved-searches
     '((:name "inbox" :query "tag:inbox" :key "i")
       (:name "unread" :query "tag:unread" :key "u")
       (:name "flagged" :query "tag:flagged" :key "f")
       (:name "sent" :query "tag:sent" :key "t")
       (:name "drafts" :query "tag:draft" :key "d")
       (:name "all mail" :query "*" :key "a")
       (:name "Sent" :query "folder:archive")))
   (setq message-default-headers "Reply-to: \nCC: \nBCC: \n")
   (setq notmuch-fcc-dirs "archive")
   ;;(when (my-system-is-rise)
   ;;   (setq notmuch-command "notmuch.cmd")

   (add-hook 'notmuch-message-mode-hook #'footnote-mode)
   (add-hook 'notmuch-message-mode-hook #'auto-dictionary-mode)
   (add-hook 'notmuch-message-mode-hook #'flyspell-mode)

   ;; ignore email header for flyspell (and thus dictionary mode):
   ;; as of 2019-12-21: doesn't work as it uses in German emails the English one :-(
   (add-hook 'notmuch-message-mode-hook
             '(lambda () (setq flyspell-generic-check-word-p

   (defun my-notmuch-sync-and-update-index-and-buffer ()
     "Update the index after sync."
     (call-process (concat my-user-emacs-directory "bin/") nil nil t "");; necessary to untag my own emails with -new

;   (when (not (my-system-is-rise))
     (define-key notmuch-hello-mode-map "g" 'my-notmuch-sync-and-update-index-and-buffer)
;   )


switch to open notmuch biffer or open new one:

(defun my-notmuch (&optional arg)
  "Opens the already opened notmuch buffer or opens new one instead."
  (interactive "P")
  (when (my-system-type-is-gnu)
      (if (my-buffer-exists "*notmuch-hello*")
          (switch-to-buffer "*notmuch-hello*")

;; (bind-key "n" 'my-notmuch my-map) → within "Key bindings" heading since binding overwrites a standard Org mode binding


Provides Org mode links to notmuch emails: copy with C-c l (org-notmuch-store-link) + paste with C-c C-l

(use-package org-notmuch
   :if (my-system-type-is-gnu)
   :load-path  (lambda () (expand-file-name (concat my-user-emacs-directory "contrib/")))

Adding a key mapping for “d” to add “deleted” tag and remove “inbox” and “unread”: Source

(define-key notmuch-search-mode-map "d"
  (lambda ()
    "mark message as deleted"
    (notmuch-show-tag (list "+deleted" "-inbox" "-unread"))))

(define-key notmuch-show-mode-map "d"
  (lambda ()
    "mark message as deleted"
    (notmuch-show-tag (list "+deleted" "-inbox" "-unread"))))

(define-key notmuch-tree-mode-map "d"
  (lambda ()
    "mark message as deleted"
    (notmuch-show-tag (list "+deleted" "-inbox" "-unread"))))

Open the current email in Thunderbird:

(define-key notmuch-search-mode-map "T"
  (lambda ()
    "open in Thunderbird"
    ;; FIXXME: I can't find the appropriate method to get current message ID in search results:
    (my-open-message-id-in-thunderbird (notmuch-show-get-message-ids-for-open-messages t))

(define-key notmuch-show-mode-map "T"
  (lambda ()
    "open in Thunderbird"
    (my-open-message-id-in-thunderbird (notmuch-show-get-message-id t))

As of 2021-01-07, I stop using notmuch (again) because of:

  • Thunderbird is good enough for now
  • I still need Thunderbird in parallel → I prefer to avoid the switching back and forth effort + additional Maildir data for notmuch
  • no appointment inviation handling on receiving
  • not being able to create standard appointment invitations
  • no HTML or rendering HTML content takes some time
  • notmuch does not offer an out-of-the-box possibility to delete emails:


In search you are normally looking at threads, not individual messages—that’s why you have notmuch-search-find-thread-id. But if you write a function that picks an appropriate ID from the result of notmuch-search-find-stable-query that should be the easiest way to go.


As /u/oritron explained, in default search view, you get results not in form of message ids, but in form on thread ids, even if there is a single message in that thread.

However, if you use notmuch-tree (see M-x notmuch-tree), you will get message ids, because threads will be represented as a tree. To access message id for highlighted/selected mail, see notmuch-show-stash-message-id function.

For or certain (Confluence notification) emails, rendering the HTML content takes 20-30 seconds, blocking my Emacs instance which is very upsetting at last to me

My approach is to use lynx for rendering html, because it is really fast. If I need to look it further, I open that message part in browser (go with cursor to text/html box, hit . o and notmuch will ask you for viewer. Enter firefox or chromium… You can’t beat html rendering with the real browser :D

To set lynx as default html renderer use this:

(setq mm-text-html-renderer ‘lynx) or check other options.


2021-02-26With an unintended upgrade from Thunderbird 68 to 78, the original thunderlink is broken.
2021-02-28Setup of cb_thunderlink as a TB 78.x replacement
2021-05-26Moved from Thunderbird to GNOME Evolution, disabling this

I got this workflow from

Internal notes: id:2020-01-09-link-emails-from-to-Thunderbird-Org

Helper function to open any given message-id in Thunderbird:

;; modify this for your system
(setq thunderbird-program "/usr/bin/thunderbird")

(defun my-open-message-id-in-thunderbird (message-id)
"open an email with a given message-ID in Thunderbird"
 (concat "thunderlink: " message-id)
 (concat "thunderlink://messageid=" message-id)
;; modify this for your system
(setq thunderlink-program "/home/vk/cb_thunderlink/cb_thunderlink")

(defun my-open-message-id-in-thunderbird (message-id)
"open an email with a given message-ID in Thunderbird"
 (concat "thunderlink: " message-id)
 (concat "thunderlink://messageid=" message-id)
;; with org-mac-link message:// links are handed over to the macOS system,
;; which has built-in handling. On Windows and Linux, we can use thunderlink!
(when (not (string-equal system-type "darwin"))

  (defun org-message-thunderlink-open (slash-message-id)
    "Handler for org-link-set-parameters that converts a standard message:// link into
   a thunderlink and then invokes thunderbird."
    ;; remove any / at the start of slash-message-id to create real message-id
    (let ((message-id
           (replace-regexp-in-string (rx bos (* ":"))
      (my-open-message-id-in-thunderbird message-id)

  ;; on message://aoeu link, this will call handler with //aoeu
  (org-link-set-parameters "messageid" :follow #'org-message-thunderlink-open)

Note that the link colors are set via my-set-linkcolors.

  • test link for testing color: 2020-07-27T15:57:45.000Z First Last: Subject


  1. install “Thunderlink” add-on in Thunderbird
  2. close Thunderbird
  3. manually add the snippet below to the prefs.js of the Thunderbird profile
  4. activate the Elisp code above within Emacs
  5. start Thunderbird
user_pref("extensions.thunderlink.custom-tl-string-1-title", "Org mode message-ID");
user_pref("extensions.thunderlink.custom-tl-string-1", "[[messageid:<messageid>][<time> <sender>: <subject>]]");
user_pref("extensions.thunderlink.custom-tl-string-1-selection-delimiter", " / ");
user_pref("extensions.thunderlink.custom-tl-string-1-clipboard-checkbox", true);
user_pref("extensions.thunderlink.custom-tl-string-1-tagcheckbox", false);
user_pref("extensions.thunderlink.custom-tl-string-1-tag", 1);
user_pref("extensions.thunderlink.custom-tl-string-1-appendtofile-checkbox", false);
user_pref("extensions.thunderlink.custom-tl-string-1-appendtofile-path", "");

Possible values for the thunderlink template:

<thunderlink> <messageid> <subject> <filteredSubject> <sender> <tbexe> <time>

My feature request for more format options:

Workflow to use it:

  1. Click on the message with the secondary mouse button (context menu)
  2. Select: “Thunderlink …” > “Org mode message-ID”
    • alternatively: C-M-1 (1 reflecting the format position in the Thunderlink menu)
  3. Switch to Emacs and yank from the clipboard

You should then get something similar to:

[[][[1/9/2020 - 11:30:41 First Last <>: Things I wanted to say to you]]

When C-c C-o onto it, Thunderbird should pop up showing the email with the corresponding message ID.

GNOME Evolution

2021-05-26migrated from Thunderbird to GNOME Evolution, re-creating the workflow here

Helper function to open any given message-id in Evolution:

;; modify this for your system
(defun my-open-message-id-in-evolution (message-id)
"open an email with a given message-ID in Evolution"
 (concat "mid: " message-id)
 "run" "org.gnome.Evolution" (concat "mid:<" message-id ">")

(org-link-set-parameters "messageid" :follow #'my-open-message-id-in-evolution)
  (defun my-convert-mail-header-to-org-link ()
  "Assumes an email header in the killring, parses it and returns an org mode link for it."

    (yank) ;; yank from clipboard
    (goto-char (point-min)) ;; start from top
    (re-search-forward "^Message-Id:.+<\\(.+\\)>[ ]*$" nil nil 1)
    (setq messageid (match-string 1))

    (goto-char (point-min))
    (re-search-forward "^From:[	 ]+\\(.+?\\)[ ]*$" nil nil 1)
    (setq from (string-replace "\"" "" (match-string 1)))

    (goto-char (point-min))
    (re-search-forward "^Subject:[	 ]+\\(.+?\\)[ ]*$" nil nil 1)
    (setq subject (match-string 1))

    (goto-char (point-min))
    (re-search-forward "^Date:[	 ]+\\(.+?\\)[ ]*$" nil nil 1)
    (setq rawdate (match-string 1))
    (setq date
    (let ((time (date-to-time rawdate)))
      (set-time-zone-rule t) ;; Use Universal time.
      (prog1 (format-time-string "%Y-%m-%d %H:%M" time)
        (set-time-zone-rule nil))))

    ;;(message (concat "MID: " messageid " F:" from " S:" subject "RD:" rawdate " D:" date))
    (insert (concat "[[messageid:" messageid "][" date " " from ": " subject "]]"))

(bind-key "e" #'my-convert-mail-header-to-org-link my-map)

Note that the link colors are set via my-set-linkcolors.

  • test link for testing color: 2020-07-27T15:57:45.000Z First Last: Subject


  1. install GNOME Evolution in version 3.39+ because Evolution added the command line switch with this version.
    • flatpak install --user --from
  2. if you need the flatpak version (because your distro has only older versions), you should be finished
  3. if you did not install Evolution via flatpak, adapt the code above
  4. Evolution: Account preferences → Receiving Options → [X] Synchronize remote mail locally in all folders

Workflow to use it:

  1. FIXXME: find a workflow to generate Message-ID links in Evolution

You should then get something similar to:

[[][[1/9/2020 - 11:30:41 First Last <>: Things I wanted to say to you]]

When C-c C-o onto it, Evolution should pop up showing the email with the corresponding message ID.


LaTeX is a powerful text setting system I use for creating letters, books, presentations, and so forth.

AucTeX is an awesome handy mode for working with TeX code.

BEGIN of LaTeX settings (only if “pdflatex” is found on the system):

(when (my-eval-if-binary-or-warn "pdflatex")

General LaTeX settings:

(autoload 'tex-site "tex-site.el")  ;; acticate AucTeX and set general preferences

(setq TeX-PDF-mode t)  ;; compile to PDF using pdflatex (instead to DVI)

(add-hook 'LaTeX-mode-hook 'turn-on-auto-fill) ;; word-wrap in TeX files

(setq TeX-auto-save nil);; avoid auto directories; read
(setq TeX-parse-self t)

;(setq-default TeX-master nil);; 2015-03-22 deactivated because it doesn't seem to have any influence: id:2013-12-31-org-master-file
(make-variable-buffer-local 'TeX-master) ;; I think this is need because the variable is not buffer local until Auctex is active

Synctex is a nice add-on that synchronizes the editing tool (Emacs/AucTeX) with a PDF viewing tool (e.g., Okular):

(when (my-eval-if-binary-or-warn "synctex")
  (add-hook 'LaTeX-mode-hook 'TeX-source-correlate-mode)
  (setq TeX-source-correlate-method 'synctex)

Define system-specific PDF viewers and further Synctex settings:

(defun okular-make-url () (concat
                     (expand-file-name (funcall file (TeX-output-extension) t)
                                       (file-name-directory (TeX-master-file)))

(defun skim-make-url () (
                   " "
                   (expand-file-name (funcall file (TeX-output-extension) t)
                                     (file-name-directory (TeX-master-file)))
                   " "

(setq TeX-view-program-list '(
                        ("Okular" "okular --unique %u")
                        ("Skim" "/Applications/ %q")

(when (my-system-type-is-gnu)
  (setq TeX-view-program-selection '((output-pdf "Okular") (output-dvi "Okular")))
  (eval-after-load "tex"
    '(add-to-list 'TeX-expand-list '("%u" okular-make-url))

(when (my-system-type-is-darwin)
  (setq TeX-view-program-selection '((output-pdf "Skim")))
  (eval-after-load "tex"
    '(add-to-list 'TeX-expand-list '("%q" skim-make-url))

(add-hook ‘LaTeX-mode-hook
(lambda ()
  (add-to-list ‘TeX-expand-list
  ‘(“%u” okular-make-url))))

CDLaTeX is a minor mode for Emacs supporting fast insertion of environment templates and math stuff in LaTeX.

I tried it once but do not use it any more.

(when (or (my-system-type-is-gnu) (my-system-is-powerplantlinux))
  (my-load-local-el "contrib/cdlatex.el")

RefTeX is the package for LaTeX that does manage references.

  • reftex
  • TeX-fold-mode
(add-hook 'LaTeX-mode-hook 'turn-on-reftex)   ; with AUCTeX LaTeX mode
(add-hook 'LaTeX-mode-hook '(lambda () (TeX-fold-mode 1)))

BibLaTeX provides bibliographic facilities. Biber is a moder implementation of it. I prefer it for generating references and its indexes.

(eval-after-load "tex"
  '(add-to-list 'TeX-command-list
          '("Biber" "biber %s" TeX-run-Biber nil t :help "Run Biber") t))

(defun TeX-run-Biber (name command file)
  "Create a process for NAME using COMMAND to format FILE with Biber."
  (let ((process (TeX-run-command name command file)))
    (setq TeX-sentinel-function 'TeX-Biber-sentinel)
    (if TeX-process-asynchronous
      (TeX-synchronous-sentinel name file process))))

(defun TeX-Biber-sentinel (process name)
  "Cleanup TeX output buffer after running Biber."
  (goto-char (point-max))
   ;; Check whether Biber reports any warnings or errors.
   ((re-search-backward (concat
                   "^(There \\(?:was\\|were\\) \\([0-9]+\\) "
                   "\\(warnings?\\|error messages?\\))") nil t)
    ;; Tell the user their number so that she sees whether the
    ;; situation is getting better or worse.
    (message (concat "Biber finished with %s %s. "
               "Type `%s' to display output.")
             (match-string 1) (match-string 2)
    (message (concat "Biber finished successfully. "
               "Run LaTeX again to get citations right."))))
  (setq TeX-command-next TeX-command-default))
(setq reftex-bibpath-environment-variables
(defun guess-TeX-master (filename)
    "Guess the master file for FILENAME from currently open files according to their extension."
    (let ((candidate nil)
          (filename (file-name-nondirectory filename)))
        (dolist (buffer (buffer-list))
          (with-current-buffer buffer
            (let ((name (buffer-name))
                  (file buffer-file-name))
              ;(if (and file (string-match "\\.\(org\|tex\)$" file))
              (if (and file (string-match "\\.org$" file))
                    (goto-char (point-min))
                    (if (re-search-forward (concat "\\\\input{" filename "}") nil t)
                        (setq candidate file))
                    (if (re-search-forward (concat "\\\\include{" (file-name-sans-extension filename) "}") nil t)
                        (setq candidate file))))))))
      (if candidate
          (message "TeX master document: %s" (file-name-nondirectory candidate)))

;; ONLY for special file modes with a recognized extension!
;; Causes Lisp error (that's a afact) when used with buffers like *scratch* (that's my guess)
;;(setq TeX-master (guess-TeX-master (buffer-file-name)))

END of LaTeX settings



«Gnuplot is a portable command-line driven graphing utility for Linux, OS/2, MS Windows, OSX, VMS, and many other platforms.»

Also very handy when visualizing table data within Org-mode! (see Org-mode/babel configuration)

Example: place the cursor within the table and evaluate org-plot/gnuplot or use the keyboard shortcut C-c " g

WhenHow many
[2016-11-17 Thu]3
[2016-11-23 Wed]4
[2016-12-10 Sat]1
;; gnuplot
(when (my-eval-if-binary-or-warn "gnuplot")
  (use-package gnuplot
    :ensure t
    :defer 110
    :if (my-system-type-is-gnu)

Org mode

Org-mode is my digital life. I do almost anything within Org-mode:

I am doing Personal Information Management (PIM) for decades, tried many different methods and tools. With Org-mode my quest for the best PIM-tool out there finally came to an end. It’s an endless pile of Lego-bricks from where I can draw some bricks and assemble it to meet my requirements. Therefore, Org-mode is something different for everybody. It depends, what Lego bricks you took and how you combined them. Org-mode scales