My org-babel based emacs configuration

Last update: <2020-07-10 Fri 14:19>

Table of Contents

  1. Preparing for lift-off
  2. Package Management
  3. Personal information
  4. Global Generic settings
  5. Internationalisation and multi-language features
  6. Visual
  7. Buffers and files
  8. Modes
  9. Org-mode
  10. Key and mouse bindings
  11. Terminals, Character encodings and emulation
  12. Completion
  13. Editing control
  14. Remote editing
  15. Browser integration
  16. Messaging and chatting
  17. Development settings
  18. Finale

This is my emacs configuration file that is loaded with org-babel-load-file in the Emacs init file. The intent is to have as much of my Emacs configuration in here as possible. The system works as a literal programming system where with a tangle the elisp code that actually makes up my configuration is extracted automatically and loaded.

This file was created from many separate elisp files and has not been fully sanitized yet. Following the history of that may be interesting to some as well.

This document is irregularly published and lives in different locations:

Preparing for lift-off

The base of the emacs configuration is in the ~/.emacs.d/ directory, so add this to my loadpaths first and give it a name.

(setq emacs-directory "~/.emacs.d/")

The first thing I want to take care of is to make customizations possible and stored in a place to my liking. I want to load this first so anything in the configuration I define explicitly overrides it.

(setq custom-file (concat emacs-directory "custom.el")
      config-file (concat emacs-directory "mrb.org"))
(load custom-file)

Now that we have the locations of the configuration determined, I want a way to have them forcibly compiled.

(defun mrb/compile-config ()
  (interactive)
  (org-babel-load-file config-file)
  (byte-recompile-directory emacs-directory 0)
)

The config file mrb.org gets openened a lot because I tend to fiddle with this config at least once a day. So, it warrants it's own keybinding.

(defun mrb/open-config ()
  (interactive)
  (find-file config-file))
;; I wanted something with '~'
(global-set-key (kbd "C-~") 'mrb/open-config)

Most of the time the editting process starts at the command line. Because I use emacs in server mode, this needs a little script. The script does two things:

  1. check if we already have the emacs server, if not, start it;
  2. treat input from stdin a little bit special.

And edit the file obviously.

# Wrapper for emacsclient usage to handle specific cases:
# - no filename given
# - stdin editting
# - force tty
# - adjustment to systemd invocation
ME=$(basename $0)
EC=`which emacsclient`

# Default argument needs our socket in any case
ARGS="--socket-name=default --suppress-output"

# Set to false when done
debug() { false $@; }

# I could let emacsclient do this, but not using systemd, it
# is hardcoded to exec emacs --daemon, sigh
# This goes along an emacs.service file obviously
if ( ! systemctl -q --user is-active emacs );then
    debug "Emacs server not found, starting...."
    systemctl --user start emacs;
fi

# Assert that the socket file exists
[ ! -f $XDG_RUNTIME_DIR/emacs/default ] || debug "Socket file not found???\n"

# Handle special 'stdin' case
if [ $# -ge 1 ] && [ "$1" == "-" ]; then
    TMPSTDIN="$(mktemp /tmp/emacsstdinXXX)";
    cat >"$TMPSTDIN";
    # Recurse or call with flags below
    $ME $TMPSTDIN;
    rm $TMPSTDIN;
    exit 0;
fi

# We're basically expecting X windows, but if not, we good
# force tty: call this like 'DISPLAY="" edit ....' see my aliases
if [ $DISPLAY ]; then
    debug "X-display value seen, using GUI"
    # Add --create-frame/--no-wait if there is no match with -noframe/-wait in name
    [ `expr match "$ME" '.*-noframe'` -eq 0 ] && ARGS=$ARGS' --create-frame'
    [ `expr match "$ME" '.*-wait'` -eq 0  ] &&  ARGS=$ARGS' --no-wait'
else
    # tty always waits, as it is in the terminal, create-frame has no meaning
    debug "Starting tty emacs"
    ARGS=$ARGS' -tty'
fi

# Go with the original arguments
exec $EC ${ARGS} "$@"

The above script is the result of trying to get the server based setup of Emacs a little better. The thoughts leading to the above script are included below.

Emacs can run as a server meaning that its process is always active and a specific client program called emacsclient can be used to connect to it to do the actual user interaction and let me edit and use the lisp interpreter.

It's far from trivial, for me at least, on how to properly configure this, assuming you have some special wishes. I've certainly spent my time on trying to get it right.

Things that I'm sure of I want

Here's what I know for sure on what I want:

  1. I want to have at least one server process for 'default' editting so emacs does not have to start a process for every file; the goal here is to have fast opening of files to edit. This is critical. I'm assuming that for this a server-like-setup is needed.
  2. Given that I tweak emacs a lot, it must be very easy to either disable the server temporarily or have it restart easily so I can test new changes without shutting me out of emacs completely.
  3. The previous item also implies that I want to run multiple independent emacsen next to eachother, regardless if they are run as a daemon or not.
  4. Emacs must work on a non X-windows screen, but extra effort to get that is OK.
  5. Be able to use '–' as placeholder for stdin to be able to pipe information directly into editor
  6. allow specific handlers, like mailto:

Things I see potential for, but not sure of

Having a server/servers opens a couple of options which I may like, but not sure on how to do or even if I understand them properly

Package Management

Package handling, do this early so emacs knows where to find things.

First of all, define the package archives I need; for now, just the default gnu augmented with the melpa repository.

;; Need to load
(if (version< emacs-version "27")
  (package-initialize))

(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(add-to-list 'package-archives '("melpa-stable" . "https://stable.melpa.org/packages/") t)
(add-to-list 'package-archives '("org" . "https://orgmode.org/elpa/") t)

;; Set explicit priorities (gnu is the default package-archive)
(setq package-archive-priorities '(("org" . 4)
                                   ("melpa" . 3)
                                   ("melpa-stable" . 2)
                                   ("gnu" . 1)))

(unless (package-installed-p 'use-package) ; Bootstrap use-package
  (message "Use package needs to be installed")
  (package-refresh-contents)
  (package-install 'use-package))

;; Use-package can now be used for itself
(use-package use-package
  :init
  (setq use-package-always-ensure t)	; Try installing automatically
  (setq use-package-verbose nil)		; Set to true when interested in load times
  ;; If running as daemon, there's no reason to defer,just load shite
  ;; to minimize chance of lockup, but it still happens sometimes.
  (if (daemonp)
      (setq use-package-always-demand t))

  (use-package use-package-ensure-system-package :ensure t)	; Need this because we are in use-package config

  ;; Location where I keep custom packages
  (setq custom-package-directory (concat emacs-directory "lisp/")))

Now, there's an issue where a variable package-selected-packages gets written to custom.el everytime I try a package. I do not want that. The site from holger has an answer for that, although it needs a slight adaptation (context argument is not there anymore)

;; Override the function that write to selected packages
(defun package--save-selected-packages (&optional value) nil)

So, package-selected-packages will not stay empty and not written out. The first is a bit problematic though, because package-list will then show every installed package as dependency instead of installed. We can correct this by overriding the package ensure function to our own and fill the variable to correct the listings.

(defun mrb/use-package-ensure-elpa (name args state &optional no-refresh)
  ;; Args is a list, but only has 1 entry?
  (dolist (ensure args)
    (let ((package
           (or (and (eq ensure t) (use-package-as-symbol name))
               ensure)))
      (when package
        ;; It needs to be selected
        (add-to-list 'package-selected-packages package))))
  ;; Execute the default ensure elpa function
  (use-package-ensure-elpa name args state no-refresh))

(setq use-package-ensure-function #'mrb/use-package-ensure-elpa)

Personal information

I'd like to arrange 3 things related to personal information:

  1. register the proper identification to use;
  2. Make sure the proper authentication information is stored;
  3. Store this information privately.

So, identification, authorisation and encryption.

Seems like a good idea to start configuration with some details about me. The idea being that anything personal goes here and nowhere else. For one, this means my name only appears in this section in this document. Most of the variables I just made up, but some are used in other places too.

(setq user-full-name       "Marcel van der Boom"
      user-mail-address    "marcel@hsdev.com"
      user-domain          "hsdev.com"
      user-organisation    "HS-Development BV"
      user-gpg-encrypt-key "0x235E5C8CF5E8DFFB")

Authorization

Several things I use within Emacs need authorization, such as tramp, jabber, erc etc. The authorization can come from several sources; ideally as few as possible. Many packages in Emacs have support for a .netrc like mechanism, others want to use the keyring in GNOME. The variables auth-sources defines the sources available.

I want to use systems which are desktop independent, so things like the gnome keyring are out because they depend on the gnome environment being present, which I can not guarantee, nor want to related to authentication. The situation which I want to prevent is that if gnome is broken, I can't authenticate to services I need.

I have a gpg-agent daemon configured which manages gpg and ssh keys, protected by a hardware key. Let's make the system as simple as we can for now and just store passwords in the gpg protected store only, i.e. the password-store program.

;; Use only password-store
(use-package auth-source-pass
  :ensure password-store
  :init
  (auth-source-pass-enable)
  :config
  ;; Make sure it's the only mechanism
  (setq auth-sources '(password-store))
  (setq auth-source-gpg-encrypt-to (list user-mail-address)))

Encrypting information

I need a way to store some sensitive information without that being published, should I decide some day to push this config somewhere.

When needed, the gpg key is used to encrypt information.

;; Use my email-address for encryption
(setq-default epa-file-encrypt-to user-mail-address)
;; Make sure we always use this
(setq-default epa-file-select-keys nil)

For org-mode, there is a wayto encrypt sections separately. See 9.14 for the details on the settings for this.

Next to inline content in org that needs encryption, there is also content that needs encrypting which is more suitable to store in a separate file for several reasons.

Global Generic settings

Section contains global settings which are either generic or I haven't figured out how to classify it under a better section.

Let's begin with the setup of some fundamentals like if we want tabs or spaces, how to select stuff and what system facilities to use.

I'm of the spaces-no-tabs religion:

(setq-default indent-tabs-mode nil)     ; do not insert tabs when indenting by default
(setq tab-width 4)                      ; 4 spaces by default

A the same time, I don't want to be that spaces guy in every project, and I'm willing to use tabs if that is the overwhelming consensus in a file. https://www.emacswiki.org/emacs/NoTabs has a snippet which guess the style in a file. Call this at the appropriate time.

(defun mrb/infer-indentation-style ()
  ;; if our source file uses tabs, we use tabs, if spaces spaces, and if
  ;; neither, we use the current indent-tabs-mode
  (let ((space-count (how-many "^  " (point-min) (point-max)))
        (tab-count (how-many "^\t" (point-min) (point-max))))
    (if (> space-count tab-count) (setq indent-tabs-mode nil))
    (if (> tab-count space-count) (setq indent-tabs-mode t))))
(setq
 backup-inhibited t                   ; disable backup files like foo~ etc.
 delete-by-moving-to-trash t          ; move files to the trash instead of rm
 select-enable-clipboard t            ; use the clipboard in addition to kill-ring

 display-warning-minimum-level 'error
 large-file-warning-threshold nil
 find-file-use-truenames nil
 find-file-compare-truenames t

 minibuffer-max-depth nil
 minibuffer-confirm-incomplete t
 complex-buffers-menu-p t
 next-line-add-newlines nil
 kill-whole-line t

 auto-window-vscroll nil)

;; Map backspace to DEL and delete to C-d
(if window-system  (normal-erase-is-backspace-mode t))

;; Let marks be set when shift arrowing, everybody does this
(setq shift-select-mode t)
(delete-selection-mode 1)


;; Only require to type 'y' or 'n' instead of 'yes' or 'no' when prompted
(fset 'yes-or-no-p 'y-or-n-p)

;; Use auto revert mode globally
;; This is save because emacs tracks if the file is saved in the editting buffer
;; and if so, it will not revert to the saved file.
(use-package autorevert
  :diminish auto-revert-mode
  :ensure nil
  :config
  (global-auto-revert-mode t))

;; Also for dired
(setq global-auto-revert-non-file-buffers t)


;; Should this be here?
;; Try to have urls and mailto links clickable everywhere
(setq goto-address-url-mouse-face 'default)
(define-global-minor-mode global-goto-address-mode
  goto-address-mode
  (lambda ()
    (goto-address-mode 1)))
(global-goto-address-mode t)


;; When in orgmode, we can shorten our urls without a service
(defun mrb/shortenurl-at-point (arg)
  (interactive "P")
  (let ((url (thing-at-point 'url))
        (bounds (bounds-of-thing-at-point 'url)))
    (kill-region (car bounds) (cdr bounds))
    ;; TODO: this will work in orgmode, possibly markdown
    ;;       tst for major mode?
    (insert (format "[[%s][%s]]"
                    url
                    (truncate-string-to-width url (if arg (prefix-numeric-value arg) 40)
                                              nil nil "…")))))
(bind-key "C-c s" 'mrb/shortenurl-at-point)

Make life easier if we have sudo, so we can just edit the files and be done with them if possible

(use-package sudo-save
  :ensure nil
  :load-path (lambda () (concat custom-package-directory "sudo-save/")))

Using pdf-tools for PDF handling. This is a lot better than docview. I'm a bit annoyed that they are not one package, they are very similar.

(use-package pdf-tools
  :after (pdf-annot fullframe)
  :magic ("%PDF" . pdf-view-mode)
  :bind (:map pdf-view-mode-map
              ("h"   . 'pdf-annot-add-highlight-markup-annotation)
              ("t"   . 'pdf-annot-add-text-annotation)
              ("D"   . 'pdf-annot-delete)
              ("C-s" . 'isearch-forward)
              ("m"   . 'mrb/mailfile)
              :map pdf-annot-edit-contents-minor-mode-map
              ("<return>"   . 'pdf-annot-edit-contents-commit)
              ("<S-return>" .  'newline))

  :config
  ;; Some settings from http://pragmaticemacs.com/emacs/even-more-pdf-tools-tweaks/
  (fullframe pdf-view-mode quit-window)
  (setq-default pdf-view-display-size 'fit-page) ;scale to fit page by default
  (setq pdf-annot-activate-created-annotations t ; automatically annotate highlights
        pdf-view-resize-factor 1.1		  ; more fine-grained zooming
        pdf-misc-print-program "/usr/bin/lpr"
        pdf-view-midnight-colors '("#DCDCCC" . "#383838")) ; Not sure what this is


  (add-hook 'pdf-view-mode-hook (lambda () (cua-mode 0))) ; turn off cua so copy works
  (pdf-tools-install :no-query))						  ; no-query auto builds epfinfo when needed

I tend to compile Emacs with ImageMagick support, let's make sure we use it too.

(when (fboundp 'imagemagick-register-types)
  (imagemagick-register-types))

Internationalisation and multi-language features

If anything multi-language should work, UTF-8 encoding is a must, so let's make sure we try to use that everywhere

(prefer-coding-system 'utf-8)

For conveniently editting accented characters like 'é' and 'è' there are quite a few options to reach that result. I have dead characters configured as an option in the operating system, but that is far from ideal, especially when programming. As I hardly need those characters outside of emacs, i can leave that option as needed and optimize Emacs to my needs.

The fallback is C-x 8 C-h which gives specific shortcuts for special characters which are available. For the exotic characters that will do just fine. For the more common characters the C-x 8 prefix is to complex.

After evaluating some systems, the TeX input method suits me the best. I'd like to enable that globally by default, which needs two things:

  1. enable multi-language editting by default (input-method is only active in a multi-language environment)
  2. set the default input-method to tex

There is however a problem, the TeX input method assumes the first character / string produced will always be the choice I need, without allowing selecting an alternative. This turns out to be due to quail-define-package which determines the way the completions work. The problem is the DETERMINISTIC argument of the function, that is set to 't'. (8th argument). While I am at it, I also changed the FORGET-LAST-SELECTION (7th argument) to nil, so the last seleciton is remembered.

For this to work properly we have to define a whole new input-method based on a copy of latin-ltx.el

;; No input method will be active by default, so for each mode where
;; it needs to be active we need to activate it (by a hook for example)
(defun mrb/set-default-input-method()
  (interactive)
  (with-temp-buffer
    (activate-input-method "TeX")

    ;; Define a few omissions which I use regularly
    (let ((quail-current-package (assoc "TeX" quail-package-alist)))
      (quail-define-rules ((append . t))
                          ("\\bitcoin" ?฿)
                          ("\\cmd" ?⌘)
                          ("\\shift" ?⇧)
                          ("\\alt" ?⎇)
                          ("\\option" ?⌥)
                          ("\\return" ?⏎)
                          ("\\tab" ?↹)
                          ("\\backspace" ?⌫)
                          ("\\delete" ?⌦)
                          ("\\plusminus" ?±)
                          ("\\_1" ?₁)
                          ("\\_2" ?₂)))))


;; Set the default language environment and make sure the default
;; input method is activated
(add-hook 'set-language-environment-hook 'mrb/set-default-input-method)
;; And now we can set it
(set-language-environment "UTF-8")

;; Activate it for all text modes
(add-hook 'text-mode-hook 'mrb/set-default-input-method)

For even more esoteric characters we have to do some extra work. No font provides all Unicode characters. There are packages (like unicode-fonts) which aim to create a giant replacement table for characters. While I'm sure this works, the characters I tried either already worked or didn't change, i.e. still don't work. So, the solution I've come up with (with some borrowing here and there obviously) is to create a table of my own to which I can add characters to use certain fonts which do have the characters and be done with them.

The problem with applying this function is that we need to be 'done' with our visual initialisation or otherwise they'll do nothing (at least that I can see). So, let's group our corrections in a function and call that when we are done with our visual (X) init.

(defun mrb/unicode-font-corrections()
  (interactive)
  ;(emoji-fontset-enable "Symbola")
)

So, when characters do not show properly, the steps to take now are:

  1. Find a font which has the char
  2. Map the character(-range) to that font
  3. Optional: define a convenient way to type the character

Visual

Many settings have to do with how the screen looks and behaves in a visual way. Thing like colors, highlighting etc. go fall into this category.

Let's set up some basics first, background dark, some default frame and cursor properties:

(setq mrb/cursor-type '(bar . 3))		; Slightly wide bar
(setq mrb/cursor-color "DarkOrange")	; in fat orange color

(setq-default frame-background-mode 'dark)

;; Only show cursor in the active window.
(setq-default cursor-in-non-selected-windows nil)

;;Default frame properties frame position, color, etc
(setq default-frame-alist
`((cursor-type . ,mrb/cursor-type )
            (height . 60) (width . 100)
            (cursor-color . ,mrb/cursor-color)))

I'm using the Nord color palette. This palette defines 16 colors.

There is a base16 theme which uses exactly these colors and there are varieties which use the 16 colors and some derivatives of it.

Although I think the 16 color system is a bit simplistic and, for emacs at least, I will need to customize more later on, the main lure of this system is that I can use the same set for many of my programs I use (emacs, xterm, i3, dunst etc.) which sounds attractive. So, I'm starting with the base16-nord theme and see where this leaves me.

(setq custom--inhibit-theme-enable nil)	; This was needed for Emacs 27, but cant recally why
(use-package base16-theme
  :config
  ;; Do config here and finally load the theme
  (setq base16-distinct-fringe-background nil
        base16-highlight-mode-line 'contrast
        base16-theme-256-color-source "terminal")
  (load-theme 'base16-nord t)

  ;; Define our adjustments to faces
  ;; This should be the only place where we adjust our faces
  ;; For reference, these are the theme colors
  ;; base00 '#2e3440 base01 '#3b4252 base02 '#434c5e base03 '#4c566aq
  ;; base04 '#d8dee9 base05 '#e5e9f0 base06 '#eceff4
  ;; base07 '#8fbcbb base08 '#88c0d0 base09 '#81a1c1 base0A '#5e81ac
  ;; base0B '#bf616a base0C '#d08770 base0D '#ebcb8b base0E '#a3be8c base0F '#b48ead

  ;; FIXME: some of these adjustments come in too early and the package redefines it on loading
  ;; the only solution for this is to move the visual adjustments in the use-package clauses of
  ;; the package itself.
  (setq base16-adjustments
        `(
          ;; Adjustment for non-package faces
          ;; Comments should not be that dark as base03
          (font-lock-comment-face         :foreground "#666666")
          (button                         :foreground "#d08770" :weight semi-bold)

          (show-paren-match               :foreground "#eceff4" :background "#5e81ac"
                                          :inherit nil :weight normal)
          (show-paren-mismatch            :foreground "#2e3440" :background "#bf616a"
                                          :inherit nil :weight normal)

          (region                         :foreground nil :background "#3b4252"
                                          :inherit nil :weight normal)
          (fringe                         :foreground "#a3be8c")

          (hl-line                        :foreground nil :background "#5e81ac" )

          (line-number-current-line       :background "#4c566a")

          (lazy-highlight                 :foreground "#434c5e" :background "#5e81ac")
          (isearch                        :foreground "#434c5e" :background ,mrb/cursor-color)

          ;; Modeline faces
          (mode-line                      :foreground "#3b4252" :background "#ebcb8b")
          (header-line                    :foreground "#4c566a" :background "#ebcb8b")

          ;; Magit
          (magit-diff-added-highlight     :foreground "#a3be8c")
          (magit-diff-removed-highlight   :foreground "#bf616a")
          (magit-diff-added               :foreground "#a3be8c")
          (magit-diff-removed             :foreground "#bf616a")

          ;; Elfeed
          (elfeed-search-tag-face         :foreground "#a3be8c")
          (elfeed-search-feed-face        :foreground "#ebcb8b")
          (elfeed-search-date-face        :foreground "#88c0d0")

          ;; Flyspell
          ;; flyspell errors/dupes look the same as flycheck-errors/warnings
          (flyspell-incorrect                                   :inherit flycheck-error)
          (flyspell-duplicate             :foreground "#2e3440" :inherit flycheck-warning)

          ;; Orgmode
          (org-headline-done              :foreground "#4c566a")
          (org-date                       :foreground "#8fbcbb" :underline nil)
          (org-level-4                    :foreground "#b48ead")
          (org-verbatim                   :foreground "#8fbcbb")
          (org-list-dt                    :foreground "#b48ead")
          (org-drawer                     :height 0.8 :inherit font-lock-comment-face)
          (org-code                       :foreground "purple" :weight bold)

          ;; User defined faces for org-mode
          (mrb/org-todo-keyword-TODO      :foreground "#88c0d0" :weight bold)
          (mrb/org-todo-keyword-DONE      :foreground "#a3be8c" :weight bold)
          (mrb/org-todo-keyword-WAITING   :foreground "#bf616a" :weight bold)
          (mrb/org-todo-keyword-CANCELLED :foreground "#3b4252" :weight bold)
          (mrb/org-todo-keyword-BUY       :foreground "#d08770" :weight bold)
          (mrb/org-todo-keyword-HOWTO     :foreground "#5e81ac" :weight bold)
          (mrb/org-todo-keyword-INFO      :foreground "#ebcb8b" :weight bold)
          (mrb/org-todo-keyword-COLLECT   :foreground "#a3be8c" :weight bold)
          (mrb/org-todo-keyword-SOLVE     :foreground "#8fbcbb" :weight bold)
          (mrb/org-todo-keyword-READ      :foreground "#b48ead" :weight bold)
          (mrb/org-todo-keyword-PLAN      :foreground "#d8dee9" :weight bold)

          ;; Set space-line state colors
          (space-line-evil-normal         :background "#bf616a")
          (space-line-evil-insert         :background "#ebcb8b")
          (space-line-evil-emacs          :background "#a3be8c")
          (space-line-evil-motion         :background "#81a1c1")
          (space-line-evil-visual         :background "#8fbcbb")
          (space-line-evil-replace        :background "#b48ead")

          ;; Helm selection
          (helm-selection                 :foreground "black" :background "goldenrod2")

          ;; Mu4e
          (mu4e-header-highlight-face     :inherit hl-line :underline nil :foreground nil)

          ;; Man pages inside emacs
          (Man-overstrike                 :foreground "#bf616a" :weight bold)
          (Man-underline                  :foreground "#a3be8c" :weight bold)
          ))

  ;; Apply our adjustment using the theme function
  (base16-set-faces 'base16-nord base16-nord-colors base16-adjustments))

Miscelaneoous other visual settings follow.

;; no splash screen
(setq inhibit-startup-screen  t)
(setq inhibit-startup-message t)
(setq initial-scratch-message nil)

;; check speed consequences of this
(setq column-number-mode t)

;; Track changes to buffers
(use-package tracking
  :config
  (setq
   tracking-max-mode-line-entries 1
   tracking-shorten-buffer-names-p nil)
  (tracking-mode 1))

;; Modeline
;; TODO: probably simplify this package, now we do not need evil support
(use-package spaceline-config
  :after tracking
  :ensure spaceline
  :ensure eyebrowse
  :ensure persp-mode
  :ensure window-numbering
  :config

  ;; Define a segment for the tracking package
  (spaceline-define-segment tracking
    "Segment to show if buffers need attention, similar to
    erc-track, but using mcirc tracking library"
    tracking-mode-line-buffers)

  ;; We use the spacemacs theme, which gives us the evil state indicator
  (spaceline-spacemacs-theme 'tracking)

  ;; Enable the helm mode
  (spaceline-helm-mode))

(use-package mic-paren
  :config
  (setq paren-highlight-at-point nil)
  (paren-activate))

;; Defer fontification, but only if there is input pending
(setq jit-lock-defer-time 0)

;; Make underlining nicer
(setq  underline-minimum-offset 3)

;; Show color of '#RRBBGG texts
(use-package rainbow-mode
  :diminish)

;; Give commands the option to display fullscreen (so far, magit-status only)
(use-package fullframe  )

Lines

The most important element of an editor is probably how lines of text are displayed. This warrants its own section.

The problem is that 'line' needs clarification because there can be a difference between a logical line and a visual line. It's sometimes important that things stay on one logical line but are displayed over multiple lines. This mayb be different per mode.

I recently reversed my stance on this and am now enabling visual line mode everywhere (meaning one logical line can be smeared out over multiple display lines. This also enables word-wrap which breaks lines visually between words instead of at random characters.

To help me see the difference between visual lines and logical lines I let emacs display indicators in the fringe and define a function to help me unfill existing paragraphs.

(use-package visual-fill-column
  :commands (turn-on-visual-fill-column-mode))

(setq-default truncate-lines nil)

;; Similar to mail messages, use vertical bar for wrapped paragaphs
(setq visual-line-fringe-indicators
      '(vertical-bar nil))

;; For all text modes use visual-line-mode
(add-hook 'text-mode-hook 'visual-line-mode)

;; From:https://www.emacswiki.org/emacs/UnfillParagraph
(defun unfill-paragraph (&optional region)
  "Takes a multi-line paragraph and makes it into a single line of text."
  (interactive (progn (barf-if-buffer-read-only) '(t)))
  (let ((fill-column (point-max))
        ;; This would override `fill-column' if it's an integer.
        (emacs-lisp-docstring-fill-column t))
    (fill-paragraph nil region)))

 ;; Similar to M-q for fill, define M-Q for unfill
(bind-key "M-Q" 'unfill-paragraph)

Line-numbering is off by default, it tends to slow things down, but if I want it on, I almost always want them relative

(use-package display-line-numbers
  :ensure nil ;;builtin
  :config
  (setq
   display-line-numbers-type 'relative)
  (display-line-numbers-mode -1))

Client dependent settings

Because most of my usage involves having a long lasting emacs daemon, some settings only come into scope once a client connects to that daemon. Most of these settings have to do with appearance and are related to having X available. Anyways, some settings need to be moved to the event when a client visits the server, so we can still apply these settings transparently.

Note that if this code is evaluated any call to emacsclient (be that from external or, more importantly Magit) will try to run this code and magit will fail if there's an error in the next section. Take extra care here.

(defun mrb/run-client-settings(&optional args)
  (interactive)

  (tool-bar-mode -1)   ;; No tool-bar
  (scroll-bar-mode -1) ;; No scroll-bar
  (menu-bar-mode -1)   ;; No menu-bar
  (tooltip-mode -1)    ;; No tooltips
  (setq fringe-mode '(14 . 14)) ;; Fringe, left and right for the continuation characters
  (set-fringe-mode fringe-mode)
  (setq indicate-buffer-boundaries 'left)

  ;; Probably move this to somewhere else ?
  (use-package ws-butler
    :diminish
    :init
    (ws-butler-global-mode)             ; Enable by default
    ;; List exemptions here
    ;; FIXME: why is org-capture-mode in here?
    (add-to-list 'ws-butler-global-exempt-modes 'org-capture-mode)
    (add-to-list 'ws-butler-global-exempt-modes 'magit-mode))

  (mrb/unicode-font-corrections))

;; Run our client settings after we make a frame This is probably
;; overkill to do on every frame, but it covers the situation where
;; `server-visit-hook` won't work because we started emacs without a
;; file. This config covers all situations.
(add-hook 'after-make-frame-functions 'mrb/run-client-settings)

;; And we need to run those settings *now* too, when we are not in server mode
(mrb/run-client-settings)

Context dependent visualisation

Next to the information registered in the theme which controls the overall look and feel, there is additional visualisation which depend on context dependent factors, such as:

Flycheck

Flycheck is a syntax checking package which seems to have better support than flymake, which is built-in. I've no configuration for flymake specifically, but some packages enable it automatically (elpy for example). Where applicable, I gather the necessary disable actions here first.

Now we can start configuring flycheck

(use-package flycheck
  ;; Use popup to show flycheck message
  :ensure flycheck-pos-tip
  :config
  (with-eval-after-load 'flycheck
    (flycheck-pos-tip-mode)))

Currently actively configured are:

Flyspell is similar to flycheck but for text languages. I'm setting some of the flyspell faces to the flycheck faces so they are consistent

(use-package flyspell
  :ensure nil ;builtin
  :config

  ;; flyspell-duplicate  as warnings
  ;; Do I want this at all?, seems overkill
  (setq flyspell-mark-duplications-flag nil)  ;; This does not seem to help
  )

Still on the wish-list:

Math rendering

Mathetmatics requires some specific rendering. In simple cases, using the proper unicode symbols may suffice, but for anything other than the most basis equations or formulas, some help is needed.  $\LaTeX$ is the defacto standard for this and there's plenty of support for that in emacs.

For $\TeX$ and $\LaTeX$ fragments I use the texfrag package. This allows to insert math fragments inside documents.

Example:

$$e^{ix} = cos x + i sinx$$

(use-package texfrag
  :config
  (setq texfrag-prefix "")
  (texfrag-global-mode 1))

Buffers and files

How do I deal with all those buffers and files?

For starters, make sure that they have unique buffer names so I don't get confused:

;; nicer buffer names
(setq uniquify-buffer-name-style 'forward)

For every file-based buffer, I want auto-save to be on, but not in the same location as the file, as that clutters up everything. For that, I add to the list of file-name transforms to have (local) files autosaved in a designated folder)

(setq auto-save-default t)

(setq mrb/auto-save-folder "~/.emacs.d/auto-save-list/")

(add-to-list 'auto-save-file-name-transforms
             (list "\\(.+/\\)*\\(.*?\\)" (expand-file-name "\\2" mrb/auto-save-folder))
             t)

The autosave helps for the minor disasters, my backups help for the major disasters. What else is need is a 'normal save' but automatic.

What I am aiming for here is to not have to think about explicitly saving for certain files. Typically when typing stuff in org-mode I just want the stuff saved that I have types so far. For some files, each save is committed if I think the content warrants this (for example if I think going back to an earlier version is a likely event).

(defconst mrb/idle-timer 15
  "Time emacs needs to be idle to trigger the save idle timer")

;; This function does the actual useful thing
(defun mrb/save-timer-callback()
  "Callback function that runs when emacs is idle for
`mrb/idle-timer' seconds. Typically we save files here"
  (org-save-all-org-buffers))

;; Activate the timer
;; The # means: evaluate first ( I keep forgetting this stuff )
(run-with-idle-timer mrb/idle-timer 'always #'mrb/save-timer-callback)

Also, save the location in files between sessions.

;; Minibuffer prompt is a prompt, don't enter it as text.
(setq minibuffer-prompt-properties '(read-only t point-entered minibuffer-avoid-prompt face minibuffer-prompt))
;; Don't echo keystrokes in the minibuffer, prefix will be hinted on after 2 seconds or so.
(setq echo-keystrokes 0)

;; Save places in buffers between sessions
(use-package saveplace
  :ensure nil ;builtin
  :init
  (setq-default save-place-mode t))

The C-x-w shortcut write a file to a new location (and adjusts the buffer), but leaves the old file lingering. The next defun moves a file in a neater way.

(defun mrb/move-file (new-location)
  "Write this file to NEW-LOCATION, and delete the old one."
  (interactive (list (if buffer-file-name
                         (read-file-name "Move file to: ")
                       (read-file-name "Move file to: "
                                       default-directory
                                       (expand-file-name (file-name-nondirectory (buffer-name))
                                                         default-directory)))))
  (when (file-exists-p new-location)
    (delete-file new-location))
  (let ((old-location (buffer-file-name)))
    (write-file new-location t)
    (when (and old-location
               (file-exists-p new-location))
      (delete-file old-location))))

;; Bind it to the usual shortcut
(bind-key "C-x C-w" 'mrb/move-file)
(bind-key "C-S-s" 'write-file)

For file management itself, dired is the defacto standard It's builtin so use-package isn't actually needed but it's nice for consistency.

(use-package dired
  :ensure nil 				;builtin
  :init
  ;; auto-revert global is only for buffers with a file attached
    (add-hook 'dired-mode-hook #'auto-revert-mode)
    :config
    (setq dired-bind-jump nil)		; Dont bind to C-x C-j, we do jabber prefix there
)

Modes

Customisation setting for specific modes. Most of the modes I use have a separate section, so this section is only for other modes.

To determine the default major mode; the mode that is started with before all the magic starts is determined by buffer-file-name. If we have it, the normal routine can be followed. If there is no filename yet, the buffer-name is used to determine which mode is needed.

By looking at the code this may have a side-effect, because the buffer-file-name is given a value. Let's try this and see if it gives any issues.

(setq-default major-mode
              (lambda ()
                (if buffer-file-name
                    (fundamental-mode)
                  (let ((buffer-file-name (buffer-name)))
                    (set-auto-mode)))))

When emacs does not determine the right mode, I sometimes want a modestring somewhere in the file. Typically that string gets inserted on a line which is commented out in the proper syntax for that mode.

(defun mrb/buffer-majormode (&optional buffer-or-name)
  "Returns the major-mode of the specified buffer, or
    the current buffer if no buffer is specied"
  (buffer-local-value 'major-mode
                      (if buffer-or-name
                          (get-buffer buffer-or-name)
                        (current-buffer))))

(defun mrb/insert-mode-string()
  "Inserts a mode string for the current mode at beginning of the current buffer"
  (interactive)
  (let ((m (symbol-name (buffer-majormode))))
    (save-excursion
      (goto-char (point-min))
      (insert "-*- mode:"
              (substring m 0 (string-match "-mode" m))
              " -*-")
      (comment-region (point-min) (point)))))

First bring in a bunch of modes that are used regularly, but have no other config than a map to some extensions. :ensure is set to nil for those packages which aret built in.

(use-package adoc-mode             :mode ("\\.asciidoc\\'" "\\.adoc\\'"))
(use-package apache-mode           :mode "\\.htaccess\\'")
(use-package conf-mode :ensure nil)
(use-package css-mode  :ensure nil :mode ("\\.css\\'" "\\.mss\\'"))
(use-package csv-mode              :mode "\\.csv\\'")
(use-package diff-mode :ensure nil :mode "\\.patch\\'")
(use-package gnuplot-mode          :mode "\\.gp\\'")
(use-package js2-mode              :mode "\\.js\\'")
(use-package lua-mode              :mode "\\.lua\\'")
(use-package php-mode              :mode "\\.php\\'")

Now, do the other mode related packages which require a bit of configuration.

;; Extension mappings for several modes
(use-package markdown-mode
  :mode ("\\.markdown\\'" "\\.md\\'")
  :config
  (setq  markdown-css-path "/home/mrb/.markdown.css"))

(use-package eimp
  :config
  (add-hook 'image-mode-hook #'eimp-mode))

(use-package rainbow-mode
  :hook (conf-mode css-mode))

;; Open scratch buffer by default in the mode we are in at the moment
;; with C-u prefix a mode will be asked to use
(use-package scratch)
;; Don't throw away scratch buffer
(use-package persistent-scratch
  :ensure t
  :config
  (persistent-scratch-setup-default))

(use-package nxml-mode
  :ensure nil ;; builtin mode
  :config
  (setq nxml-heading-element-name-regexp "\\|.*"
        nxml-section-element-name-regexp "\\|file\\|.+")

  (add-hook 'nxml-mode-hook '(lambda () (set-input-method nil)))
  (add-hook 'nxml-mode-hook 'turn-off-auto-fill))

Org-mode

Orgmode configuraton is probably the largest part of my Emacs configuration, because most of the time I spent in Emacs, when not coding, is spent in org-mode.

Initialisation of Orgmode

Basically where to find stuff and what to load.

I loosely follow the GTD method for organising things and I want a quick shortcut to start my main file.

(defun mrb/gtd()
  "Start my GTD system"
  (interactive)
  (find-file org-default-notes-file))

We do not have to load the main orgmode location, because we already did that on the main initialisation to get org-babel started.

Also make sure we never load org from internal, this can happen when functions were defined in the included org version and not anymore in newer versions. We want an error, not a silent load of the older function.

(use-package org
  :ensure nil
  :pin org
  :mode ("\\.txt\\'" "\\.org\'")
  :after (org-capture visual-fill-column)
  :bind (("C-c g"   . 'mrb/gtd)
         ("C-c a"   . 'org-agenda)
         ("C-c b"   . 'org-switchb)
         ("C-s-s"   . 'org-save-all-org-buffers)
         ("C-x m"   . 'mrb/construct-mail)
         ("C-c l"   . 'org-store-link)	; Is this not bound to C-c l by default
        :map org-mode-map
         ("s-."     . 'org-todo)
         ("s->"     . 'mrb/org-cancel-todo)
         ("C-s-."   . 'mrb/force-org-todo)
         ("C-."     . 'mrb/org-schedule-for-today)
         ("s-t"     . 'org-schedule)
         ("M-p"     . 'org-set-property)
         :map org-agenda-mode-map
         ("s-."   . 'org-agenda-todo)
         ("s->"   . 'mrb/org-agenda-cancel-todo)
         ("C-s-." . 'mrb/force-org-agenda-todo)
         ("C-."   . 'mrb/org-agenda-schedule-for-today)
         ("s-t"   . 'org-agenda-schedule)
         ("M-p"   . 'org-set-property)
         ("C-x m" . 'mrb/construct-mail)
         :map org-capture-mode-map
         ("C-c C-t" . mrb/add-tags-in-capture))
  :config
  (add-hook 'org-mode-hook 'turn-on-visual-line-mode)
  (add-hook 'org-mode-hook 'turn-on-visual-fill-column-mode)
  (setq org-use-fast-todo-selection t
        org-use-fast-tag-selection t
        org-fast-tag-selection-single-key 'expert
        org-enforce-todo-dependencies t	; we do want task dependencies
        org-enforce-todo-checkbox-dependencies nil ; but relax checkbox contraints for it

        ;; We dont do priorities
        org-enable-priority-commands nil

        ;; Agenda settings
        org-agenda-include-diary t
        org-agenda-start-with-log-mode t
        org-agenda-todo-ignore-scheduled "future"
        org-agenda-ignore-properties '(effort appt category)
        org-agenda-todo-ignore-scheduled 'future

        ;; Habits
        org-habit-show-habits-only-for-today nil

        ;; Pressing enter on a link should activate it
        org-return-follows-link t
        org-support-shift-select 'always

        org-agenda-log-mode-items '(closed clock state)
        org-agenda-skip-deadline-prewarning-if-scheduled t
        ;; Auto detect blank line need, this is the default, but I explicitly set thi
        ;; because it needs to be this or my capturing breaks due to org-capture popup
        org-blank-before-new-entry '((heading . auto) (plain-list-item . auto))
        org-export-htmlize-output-type 'css

        org-file-apps
        '((auto-mode . emacs)
          ("\\.dia\\'" . "dia %s")
          ("\\.mm\\'" . default)
          ("\\.pdf\\'" . default))
        org-fontify-done-headline t
        org-goto-interface 'outline-path-completion
        ;; non nil is just direct children, what an ODD name!!!!
        org-hierarchical-todo-statistics nil
        org-provide-todo-statistics t
        org-log-into-drawer t
        org-log-redeadline 'note
        org-log-reschedule 'time
        org-modules '(org-info org-habit
                               org-inlinetask org-irc
                               org-toc org-mac-iCal org-mouse
                               org-tempo)
        org-remember-default-headline ""
        org-special-ctrl-a/e t
        org-stuck-projects '("-inactive/TODO" ("TODO" "WAITING") nil "")
        org-track-ordered-property-with-tag nil
        org-startup-indented t)

  ;; Allow for archiving and refiling in a date organised tree
  (use-package org-datetree :ensure nil)
  (use-package org-protocol :ensure nil
    :config
    ;; If nothing is specified, create a TODO item
    (setq org-protocol-default-template-key "t")))

Most of work originates in capturing some task item in orgmode. Set up the location and files for that.

(setq org-directory "~/dat/org/")
(setq org-agenda-files (concat org-directory ".agenda_files"))
(setq org-metadir (concat org-directory "_orgmeta/"))
(setq org-archive-location (concat org-metadir "archive.org::datetree/"))
(setq org-default-notes-file (concat org-directory "GTD.org"))
(setq diary-file (concat org-metadir "DIARY"))

;; Display org properties in the agenda view
(use-package org-agenda-property)

Capturing information

I guess 90% of the information I keep in the main orgmode files starts life originally as a captured item. I use it for:

  1. TODO items;
  2. BUY items;
  3. Journaling entries;
  4. Logbook entries.

The org-capture-pop-frame package makes sure all captures are in separate frames.

(use-package org-capture
  :diminish
  :ensure nil
  :after org-journal)

(use-package org-capture-pop-frame
  :config
  (setq ocpf-frame-parameters
   '((name . "*capture*")
     (width . 115) (height . 15)
     (tool-bar-lines . 0) (menu-bar-lines . 0))))

Here are the templates used for them.

(setq
 org-capture-templates
 '(
   ("b" "Buy"
    entry (function mrb/capture-location) "* BUY %? :buy:\n%(mrb/prop-or-nothing :annotation)\n" :prepend t :empty-lines 1)
   ("t" "Todo"
    entry (function mrb/capture-location) "* TODO %?\n%(mrb/prop-or-nothing :annotation)\n" :prepend t :empty-lines 1)))


(defun mrb/prop-or-nothing(prop)
  "Insert `[:type]: [:property]` when :property has a value, otherwise nothing.
Meant to be used inside org-capture-templates

Example: `%(mrb/prop-or-nothing :annotation)`"

  (let ((prop-value (plist-get org-store-link-plist prop))
        (type (plist-get org-store-link-plist :type)))
    (if (equal prop-value "")
        ""
      (concat (when type (concat type ": ")) prop-value))))

(defun mrb/capture-location()
  "This function is meant to be used inside org-capture-templates
to find a file and location where a captured ITEM should
be stored."

  ;; Open journal file without creating a journal entry This has the
  ;; side effect that it creates the file and moves TODO items over
  ;; on first call and leaves the cursor at the end of the file.
  (org-journal-new-entry 1)

  ;; Find the id in this file and go there for the capture
  (setq loc (org-find-entry-with-id "new-todo-receiver"))
  (when loc
    (goto-char loc)))

Define functions for each piece of information captured, so they can be easily bound to keys.

(defun mrb/capture-todo ()
  "Capture a TODO item"
  (interactive)
  (org-capture nil "t"))

(defun mrb/capture-buy ()
  "Capture a BUY item"
  (interactive)
  (org-capture nil "b"))

These capture functions are called from shell scripts in the operating system and have a shortcut key assigned to them. The scripts are produced directly from this document, in a similar way as the main edit script was produced in 1

edit-noframe --eval '(mrb/capture-todo)'
edit-noframe --eval '(mrb/capture-buy)'

By default C-c C-c ends the capture, but is normally the shortcut to enter tags, so I define a shortcut to define tags while capturing.

(defun mrb/add-tags-in-capture()
  (interactive)
  "Insert tags in a capture window without losing the point"
  (save-excursion
    (org-back-to-heading)
    (org-set-tags-command)))

Workflow

Orgmode used a couple of thing which enable you to steer the workflow for items. Item states are the most prominent ones. Org-mode uses keyword definitions to denote states on items. I keep an Orgmode configuration file (org-config.org) file which contains the description of the workflow in a formate suitable to include directly into orgmode files. The configuration of emacs itself is limited to dressing up this configuration with things less suitable to go into that config file. The configuration here and the org config file should be kept in sync.

Adapt the colors of the states I use a bit:

;; Define face specs for our keywords, so they can be used in the
;; theme adjustment like standard faces
(defface mrb/org-todo-keyword-TODO      nil "")
(defface mrb/org-todo-keyword-DONE      nil "")
(defface mrb/org-todo-keyword-WAITING   nil "")
(defface mrb/org-todo-keyword-CANCELLED nil "")
(defface mrb/org-todo-keyword-BUY       nil "")
(defface mrb/org-todo-keyword-HOWTO     nil "")
(defface mrb/org-todo-keyword-INFO      nil "")
(defface mrb/org-todo-keyword-COLLECT   nil "")
(defface mrb/org-todo-keyword-SOLVE     nil "")
(defface mrb/org-todo-keyword-READ      nil "")
(defface mrb/org-todo-keyword-PLAN      nil "")


(setq org-todo-keyword-faces `(
  ("TODO"      . mrb/org-todo-keyword-TODO)
  ("DONE"      . mrb/org-todo-keyword-DONE)
  ("WAITING"   . mrb/org-todo-keyword-WAITING)
  ("CANCELLED" . mrb/org-todo-keyword-CANCELLED)
  ("BUY"       . mrb/org-todo-keyword-BUY)
  ("HOWTO"     . mrb/org-todo-keyword-HOWTO)
  ("INFO"      . mrb/org-todo-keyword-INFO)
  ("COLLECT"   . mrb/org-todo-keyword-COLLECT)
  ("SOLVE"     . mrb/org-todo-keyword-SOLVE)
  ("READ"      . mrb/org-todo-keyword-READ)
  ("PLAN"      . mrb/org-todo-keyword-PLAN)))

Make sure we keep a clean tag slate when changing tag state. This means that when I move to an active state, remove inactive tags; if something is DONE, remove tags from it and automatically adding a 'buy' tag when a BUY item is created. Note: capturing does not honour this, i.e. when creating a new item.

(setq org-todo-state-tags-triggers
      '(('todo ("inactive"))          ; remove inactive tags if moved to any active state
        ('done ("inactive") ("fork")) ; remove tags from any inactive state
        ("BUY"  ("buy" . t))))        ; add buy tag when this is a buying action

To keep the TODO list clean we immediately archive the completed entry in the archive. The archiving only occurs when an item enters the 'DONE' state and the item is not marked as a habit.

I'm not sure if this works out in practice without having a confirmation (because we archive the whole subtree), so for now, I'm building in the confirmation.

(use-package org-habit :ensure nil)

;; I need a modified version of org-is-habit, which takes inheritance
;; in to account
(defun mrb/org-is-habit-p (&optional pom)
  "org-is-habit-p taking property inheritance into account"
  (equalp "habit" (org-with-point-at (or pom (point))
                    (org-entry-get-with-inheritance "STYLE"))))

(defun mrb/archive-done-item()
  ;; Determine if the item went to the DONE/CANCELLED state
  ;; if so, ask to archive it, but skip habits which have
  ;; their own logic.
  (when (not (mrb/org-is-habit-p))
    ;; No habit, so we have a candidate
    (progn
      ;; Try to use a dialog box to ask for confirmation
      (setq last-nonmenu-event nil)

      ;; When a note is going to be added, postpone that Otherwise just
      ;; run the archiving question
      ;; FIXME: org-add-note runs through post-command-hook,
      ;;        which is kinda weird, how to i get it to run
      ;;        before the archiving question?
      (when (equal org-state "DONE")
        (org-archive-subtree-default-with-confirmation)))))


;; Run archive for the item that changed state
(add-hook 'org-after-todo-state-change-hook
          'mrb/archive-done-item t)

Marking items as DONE

Marking work as completed should be a smooth process to stop getting in the way of doing the actual work. A shortcut is defined to mark items done in the standard way and have an additional shortcut to mark it done should it be blocked.

When an item changes to the DONE state, a question is asked if the item should be archived, to which the normal answer should be 'Yes' to keep the active file as clean as possible.

Thus, the normal sequence would be:

;; Normal flow, but ignore blocking
(defun mrb/force-org-todo()
  (interactive)
  ;; Disable blocking temporarily
  (let ((org-inhibit-blocking t))
    (org-todo)))

;; Normal flow, but ignore blocking, agenda scope
(defun mrb/force-org-agenda-todo()
  (interactive)
  ;; Disable blocking temporily
  (let ((org-inhibit-blocking t))
    (org-agenda-todo)))

;; Break flow, cancel directly
(defun mrb/org-cancel-todo ()
  (interactive)
  (org-todo "CANCELLED"))

;; Break flow, cancel directly, agenda scope
(defun mrb/org-agenda-cancel-todo ()
  (interactive)
  (org-agenda-todo "CANCELLED"))

Registering creation time of todo items

Over time it gets a bit messy in my orgmode files. I can not remember when something was created and thus, by judging the time I didn't do anything with the item, decide if it is still important or not.

So, to help with that I created a little glue to make sure each actionable item gets a CREATED property with the date in it on which that item was created. I use the contributed org-expiry for that and adjust it a bit.

I want the property to be name 'CREATED' (I don't remeber what the org-expiry default name is, but it is different) and the timestamps inserted must not be active, otherwise they'll appear all over the place in the agenda.

(use-package org-expiry
  :ensure nil
  :load-path (lambda () (concat custom-package-directory "org-expiry/"))
  :init
  (setq org-expiry-created-property-name "CREATED"
        org-expiry-inactive-timestamps  t))

So, to create the timestamp I need a little helper function which actually inserts it, using org-expiry. There is some additional cursor munging to make sure it is used comfortably during editing.

(defun mrb/insert-created-timestamp()
  "Insert a CREATED property using org-expiry.el for TODO entries"
  (org-expiry-insert-created)
  (org-back-to-heading)
  (org-end-of-line)
)

Now that function is used to insert the property when:

  1. creating a TODO heading, using an advice to insert-todo-heading
  2. capturing an item, but only when it is a TODO item (i.e. has a defined keyword)
(defadvice org-insert-todo-heading (after mrb/created-timestamp-advice activate)
  "Insert a CREATED property using org-expiry.el for TODO entries"
  (mrb/insert-created-timestamp))

(ad-activate 'org-insert-todo-heading)

(use-package org-capture :ensure nil)
(defadvice org-capture (after mrb/created-timestamp-advice activate)
  "Insert a CREATED property using org-expiry.el for TODO entries"
  (when (member (org-get-todo-state) org-todo-keywords-1)
    (mrb/insert-created-timestamp)))
(ad-activate 'org-capture)
;;(ad-deactivate 'org-capture)

Related to the above, with some regularity I want to record timestamps, for example for documenting longer during tasks and recording incremental progress or information on them. Orgmode provides the binding 'C-c !' for this which inserts an inactive datestamp, optionally including the time as well.

Lastly, there's a mechanism in emacs which can automatically insert time-stamps, based on a placeholder in the file and a format variable. I sometimes use that, so I add it to my before save hook. Typically, the format string is file specific and will be held in a file local variable.

(add-hook 'before-save-hook 'time-stamp)

Scheduling items

Orgmode has a number of provisions to schedule items, either explicitly by setting the SCHEDULE property, inferring a deadline by setting the DEADLINE property, thus scheduling the task in an interval before the deadline expires.

A routine task I am performing, often at the beginning of the day, is to make a list of what I want to do that day. This is often ad-hoc and thus still on paper most of the time. Over time, I want this to be done in orgmode, but that requires the process to be a lot simpler than it is now and one of the things is to make it easier to model a thing like: “yeah, do this today”

(defun mrb/org-schedule-for-today()
  "Schedule the current item for today"
  (interactive)
  (org-schedule nil
                (format-time-string "%Y-%m-%d")))

(defun mrb/org-agenda-schedule-for-today()
  "Schedule the current item in the agenda for today"
  (interactive)
  (org-agenda-schedule nil
                       (format-time-string "%Y-%m-%d")))

Visual settings

Having an attractive screen to look at becomes more important if you use the system all day long. Attractive is rather subjective here. For me it mainly consists of functional things. Anyways, this section groups settings for the visual characteristics of orgmode.

I want to hide the leading stars in the outliner, and do it exactly in the background color. This is redundant actually in my case, as it is also specified in the org config file that I include. Or rather, it is redundant there, because I want it always to be the case.

(setq org-hide-leading-stars t)

For the collapsed items in the outline orgmode uses the variable org-ellipsis to determine what character-sequence should be used to show that the item can be expanded. The variable can contain a string, which will then be used instead of the standard 3 dots, or a face which will then be used to render the standard 3 dots.

(setq org-ellipsis "…")

There are a couple of ways within org to emphasize text inline for bold, italics, underlined etc. These are set in the text by enclosing regions with delimiters. I do not want to see these delimiters, but rather render the text.

(setq org-hide-emphasis-markers t)

Similar to inline emphasis is the rewriting with pretty entity characters (like 'δ' for example). These characters can be added to the text by adding a '\' before a symbol name ('delta' in the example). I make an exception for the sub- and superscript characters. This happens a lot in variable names etc. and I a big annoyance if those get rendered to subscript all the time.

(setq org-pretty-entities 1)
(setq org-pretty-entities-include-sub-superscripts nil)

Related to that is the display of links. I want them to be explicit most of the time to avoid confusion, but the 'fancy' display is easier at first:

(setq org-descriptive-links t)

For most of the source blocks I want Emacs to render those blocks in their native mode. This had a serious performance problem in the past, but I think it has been solved recently.

(setq org-src-fontify-natively t)

For the headings at each level a * is normally used. As we're in unicode worls now, we can do a bit better.

(use-package org-bullets)
(add-hook 'org-mode-hook (lambda () (org-bullets-mode 1)))

The item lists can be made a whole lot more attractive by attaching some icons based on the category an items belongs to. The category assignment itself is done by setting the CATEGORY property explicitly on the item or on the file.

(setq org-agenda-category-icon-alist
      `(
        ("Afspraak"      ,(concat org-directory "images/stock_new-meeting.png") nil nil :ascent center)
        ("Blogging"      ,(concat org-directory "images/edit.png") nil nil :ascent center)
        ("Car"           ,(concat org-directory "images/car.png") nil nil :ascent center)
        ("Cobra"         ,(concat org-directory "images/car.png") nil nil :ascent center)
        ("DVD"           ,(concat org-directory "images/media-cdrom.png") nil nil :ascent center)
        ("Emacs"         ,(concat org-directory "images/emacs.png") nil nil :ascent center)
        ("Finance"       ,(concat org-directory "images/finance.png") nil nil :ascent center)
        ("Habitat"       ,(concat org-directory "images/house.png") nil nil :ascent center)
        ("Habit"         ,(concat org-directory "images/stock_task-recurring.png") nil nil :ascent center)
        ("Hobbies"       ,(concat org-directory "images/hobbies.png") nil nil :ascent center)
        ("Partners"      ,(concat org-directory "images/partners.png") nil nil :ascent center)
        ("Personal"      ,(concat org-directory "images/personal.png") nil nil :ascent center)
        ("Task"          ,(concat org-directory "images/stock_todo.png") nil nil :ascent center)
        ("Org"           ,(concat org-directory "images/org-mode-unicorn.png") nil nil :ascent center)
        ("Systeem"       ,(concat org-directory "images/systeembeheer.png") nil nil :ascent center)
        ("Wordpress"     ,(concat org-directory "images/wordpress.png") nil nil :ascent center)
))

Showing items in the agenda views reacts to a number of settings. In my setup I want blocked tasks hidden, that is the reason for blocking. Hide tasks which are DONE already and a deadline is coming up, no use showing those; the same goes for tasks which are DONE and are scheduled. In short, anything that does not need my attention needs to be hidden.

(setq
    org-agenda-dim-blocked-tasks t
    org-agenda-skip-deadline-if-done t
    org-agenda-skip-scheduled-if-done t
    org-agenda-skip-archived-trees nil
)

After experimenting with automatic aligning of tags I've come to the conclusion I want to have it manual. For now, I set the tags to align flush right with column 110 but ideally I'd want something more dynamic (in the sense that visual-line mode is also dynamic)

(setq org-tags-column -95)
(defun mrb/org-align-all-tags ()
  "Wrap org-align-tags to be interactive and appy to all"
  (interactive)
  (org-align-tags t))
(bind-key "M-\"" 'mrb/org-align-all-tags)

Agenda customization

Settings which are just applicable for the org-mode agenda view.

;; Show properties in agenda view
(use-package org-agenda-property
  :config
  (setq org-agenda-property-list '("LOCATION" "Responsible")))

;; But remove the mouse-face text property which makes the lines
;; highlight when hovering (it is either the cursor or the mouse, but not both)
(add-hook 'org-finalize-agenda-hook
          (lambda () (remove-text-properties
                 (point-min) (point-max) '(mouse-face t))))

A snippet which resized the agenda to its window whenever its rebuilt by orgmode The 'auto value of the org-agenda-tags-column variable right aligns the tags in the agenda view

(setq org-agenda-tags-column 'auto)
(defadvice org-agenda-redo (after fit-windows-for-agenda activate)
  "Fit the Org Agenda to its buffer."
  (fit-window-to-buffer))

Calendaring

Traditionally somewhat related to mail, although mostly through orgmode these days, I want a simple setup to pull in some calendering information. I run a radical calendar server somewhere, but having the same info in orgmode makes sense for me.

(use-package org-caldav
  :after org
  :config
  ;; Set values which are valid for all calendars
  ;; Authentication is done through auth-source automatically
  (setq org-caldav-url "https://calendars.hsdev.com/mrb"
        org-icalendar-timezone "Europe/Amsterdam"
        org-caldav-sync-direction 'cal->org ; Just mirroring for now
        org-caldav-delete-org-entries 'always
        org-caldav-save-directory "~/dat/org/_calendars/"
        org-caldav-files nil
          org-caldav-show-sync-results nil ; This should be off once I know it's stable
          org-caldav-backup-file nil	 ; saves a LOT of time when syncing

        ;; Calendars to sync
        org-caldav-calendars
        '((:calendar-id "e00ffed0-2fc3-4adb-ba08-95c93b2b6abc" ; My own
                        :inbox "~/dat/org/_calendars/marcel.org")
          (:calendar-id "4de0fb18-8548-61ce-57fc-b2319b87106d" ; Waste services
                        :inbox "~/dat/org/_calendars/afval.org")
          (:calendar-id "shared-sylvia"	                       ; SO
                        :inbox "~/dat/org/_calendars/sylvia.org"))))

For now, I can live with calling org-caldav-sync manually when I need up to date information.

Babel / Literate programming

Specific settings for babel and literate programming within org-mode

(setq org-babel-load-languages
      '((awk        . t)
        (calc       . t)
        (css        . t)
        (ditaa      . t)
        (emacs-lisp . t)
        (gnuplot    . t)
        (haskell    . t)
        (js         . t)
        (lisp       . t)
        (org        . t)
        (plantuml   . t)
        (python     . t)
        (scheme     . t)
        (shell      . t)
        (sql        . t)))

;; Activate Babel languages
(org-babel-do-load-languages
 'org-babel-load-languages
 org-babel-load-languages)

Refiling

A big part of organizing information and task is shuffling things around. The 'thing' to throw around is a heading and 'refiling' is the term org-mode uses for throwing.

When filing, or capturing we want the items at the bottom of what we are filing it into. The main reason for this is that a large part of the sections that contain items are ordered. Should we file the item at the top, in many cases that would mean it is the most imminent thing to do, which is not the case.

(setq org-reverse-note-order nil    ; File at the bottom of an entry
      org-refile-allow-creating-parent-nodes 'confirm
      org-refile-targets '((org-agenda-files :maxlevel . 10 ))
      org-refile-use-outline-path 'file
      org-outline-path-complete-in-steps nil
      org-refile-use-cache t)

The list of headers to refile to is, in the standard config, basically everything. I limit it above to a maximum depth of 10, but then still. By using org-refile-target-verify-function we can fine-tune the decision whether to use a header as target or not. The following conditions have been implemented:

  1. The header must not be a DONE item;
  2. The header needs to have children.

The latter is the most important one and will prevent creating 'gathering' items to tasks themselves.

(defun mrb/has-DONE-keyword()
  "Return t when the heading at point has a `DONE' like keyword"
  (member (nth 2 (org-heading-components)) org-done-keywords))

(defun mrb/verify-refile-target()
  "Decide if a target header can be used as a refile target
Conditions to return t:
- header must not have one of the DONE keywords
- it must be a parent of something already"
  ;; interactive during testing
  (interactive)

  (and
   ; exclude done keyword headers
   (not (mrb/has-DONE-keyword))
   ; must have a child
   (save-excursion (org-goto-first-child))))


(setq org-refile-target-verify-function 'mrb/verify-refile-target)

Exporting to other formats

Orgmode can export to a variety of formats, I mainly use LaTeX (PDF) and HTML as destination format

;; {% raw %}
(setq org-export-latex-hyperref-format "\\ref{%s}:{%s}"
      ;; old system
      org-export-latex-title-command " "
      ;; new system > 8.0
      org-latex-title-command " "

      org-export-docbook-xsl-fo-proc-command "fop %i %o"
      org-export-docbook-xslt-proc-command "xsltproc --output %o %s %i"
      org-export-htmlize-output-type 'css
      org-org-htmlized-css-url "orgmode.css"
      org-latex-pdf-process
      '("pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
        "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
        "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f")
      org-latex-to-pdf-process
      '("pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
        "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
        "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f")

      org-latex-listings 'minted
      ;; Define a default background color name, this needs to be set through
      ;; a latex header
      org-latex-minted-options '(("bgcolor" "codebg"))

      org-export-copy-to-kill-ring 'if-interactive

      org-export-htmlized-org-css-url "orgmode.css"
      org-export-latex-classes
      '(
              ("article" "\\documentclass[11pt,a4paper,twoside]{article}"
               ("\\section{%s}" . "\\section*{%s}")
               ("\\subsection{%s}" . "\\subsection*{%s}")
               ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
               ("\\paragraph{%s}" . "\\paragraph*{%s}")
               ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
              ("report" "\\documentclass[11pt]{report}"
               ("\\part{%s}" . "\\part*{%s}")
               ("\\chapter{%s}" . "\\chapter*{%s}")
               ("\\section{%s}" . "\\section*{%s}")
               ("\\subsection{%s}" . "\\subsection*{%s}")
               ("\\subsubsection{%s}" . "\\subsubsection*{%s}"))
              ("book" "\\documentclass[11pt]{book}"
               ("\\part{%s}" . "\\part*{%s}")
               ("\\chapter{%s}" . "\\chapter*{%s}")
               ("\\section{%s}" . "\\section*{%s}")
               ("\\subsection{%s}" . "\\subsection*{%s}")
               ("\\subsubsection{%s}" . "\\subsubsection*{%s}"))
              ("beamer" "\\documentclass{beamer}" org-beamer-sectioning))
      org-export-latex-hyperref-format "\\ref{%s}:{%s}"
      org-latex-title-command " "
      org-export-latex-title-command " "
      org-export-with-tags nil
      org-export-with-todo-keywords nil

      )
;; {% endraw %}

Journaling

While the standard capture method is useful I found myself not using it very much. Not sure why. It turns out that org-journal is a much better fit for my workflow, especially using the carry-over functionality.

(use-package org-journal
  :after org
  :bind
  (("C-c j"   . 'org-journal-new-entry))
  :init
  ;; The expand-file-name is needed, which is odd, because for single
  ;; files this is not needed.
  (setq org-journal-dir (expand-file-name (concat org-directory "journal/"))
        ;; Bring our config to every journal file
        org-journal-file-header "#+SETUPFILE: ~/.emacs.d/org-config.org"

        ;; Match the journal files (FIXME: make this independent of earlier assignments)
        org-agenda-file-regexp "^[0-9]+\\.org"
        org-journal-file-format  "%Y%m%d.org"

        ;; Put day on top of file, uses `org-journal-date-format`
        org-journal-date-format "%A, %e-%m-%Y"

        ;; Put day on top of file, uses `org-journal-date-format`
        org-journal-date-prefix "#+TITLE: "
        ;; New entries go at the bottom, make sure we are at top level
        org-journal-time-format "[%R] "
        org-journal-time-prefix "* "

        ;; Carry over TODO items and items explicitly marked
        org-journal-carryover-items "+carryover|+TODO=\"TODO\""
        ;; and for this to work, we need agenda integration
        org-journal-enable-agenda-integration t
        ;; Remove empty journals after carryover
        org-journal-carryover-delete-empty-journal 'always

        ;; I want to have encryption, but how do TODO items bubble up then in the agenda
        org-journal-enable-encryption nil
        org-crypt-disable-auto-save t)
  :config
      (add-hook 'org-journal-mode-hook 'turn-on-visual-line-mode)
      (add-hook 'org-journal-mode-hook 'turn-on-visual-fill-column-mode))

Particularly in journaling, but also in org-mode in general, I want to be able to quickly insert screenshots. Rather, images in general but 90% of my usecase is really screenshots.

There's a package org-attach-screenshot which matches my usecase 99% so let's use that and worry about extending it to images later on.

(use-package org-attach-screenshot
  :bind (("C-c i" . org-attach-screenshot)))

The 1% I was referring to above is that the original package exclusively supports org-mode. I patched it, which was trivial to support org-mode and all its derived modes. (org-journal in my case)

Publishing

There's plenty ways to publish orgmode content on the web. I use a couple of them sparingly. For most of them I don't need a permanent configuration. For the ones that I do need a config, there's this section.

Writefreely

Writefreely is a Write.as derivative and published as open source. This allows me to just kick out a quick orgmode file with a #+TITLE: and a #+DATE: header and throw it on a blog like site rather quickly.

;; Still needed:
;; - post as draft by default, this prevents automatic posting by accident for federated collections
;; - save augment -> publish / update automatically, probably a custom hook on minor mode?
;; - wf: have autorefresh GET parameter which monitors updates and
;;       refreshes automatically
;; - set local save dir for the files
;; Make sure the variables of THIS file are loaded before THIS file is loaded
(use-package writefreely
  :after org
  :config
  (setq
   ;; Set api endpoints for my own install
   writefreely-instance-url "https://qua.name"
   writefreely-instance-api-endpoint "https://qua.name/api"
   writefreely-maybe-publish-created-date t
   ;; FIXME: this is nice, but it triggers an unlock dialog for yubikey
   writefreely-auth-token (password-store-get "Qua.name/accesstoken")))

Encrypting information in org-mode

I use the encrypt tag for encrypting sections in org-mode (and sometimes my journal). The sections get encrypted and decrypted automatically on saving and opening. This uses the EasyPG library to get to my GPG key.

(use-package org-crypt
  :ensure nil
  :config
  (org-crypt-use-before-save-magic)
  (setq org-crypt-tag-matcher "encrypt")
  (setq org-crypt-key user-gpg-encrypt-key))

We do not want to inherit this tag automatically, as its behaviour is already subsection inclusive. When you encrypt a section, everything below it is considered content of that section and gets encrypted. I also add the value “crypt” as that is the org default, so it won't be inherited by mistake.

(add-to-list 'org-tags-exclude-from-inheritance '"encrypt")
(add-to-list 'org-tags-exclude-from-inheritance '"crypt")

Old configuration

Below is what was contained in the old configuration. I will slowly migrate this into more literal sections

;; Bit of a leftover from reorganising bits, do this later
(add-to-list 'org-tags-exclude-from-inheritance '"sell")



(defun mrb/is-project-p ()
  "This function returns true if the entry is considered a project.
    A project is defined to be:
    - having a TODO keyword itself (why was this again?);
    - having at least one todo entry, regardless of their state."
  (let ((has-todokeyword)
        (has-subtask)
        (subtree-end (save-excursion (org-end-of-subtree t)))
        (is-a-task (member (nth 2 (org-heading-components)) org-todo-keywords-1)))
    (save-excursion
      (forward-line 1)
      (while (and (not has-subtask)
                  (< (point) subtree-end)
                  (re-search-forward "^\*+ " subtree-end t))
        (when (member (org-get-todo-state) org-todo-keywords-1)
          (setq has-subtask t))))
    ;; both subtasks and a keyword on the container need to be present.
    (and is-a-task has-subtask)))

;; FIXME: testing for tag presence should be easier than a re-search forward
;; FIXME: are we not searching for all 'incomplete' type keywords here?,
;;        there must be an org function for that
(defun mrb/skip-non-stuck-projects ()
  "Skip trees that are not stuck projects"
  (let* ((subtree-end (save-excursion (org-end-of-subtree t)))
         (has-next (save-excursion
                     (forward-line 1)
                     (and (< (point) subtree-end)
                          (re-search-forward "^*+ \\(TODO\\|BUY\\|WAITING\\)" subtree-end t)))))
    (if (and (mrb/is-project-p) (not has-next))
        nil ; a stuck project, has subtasks but no next task
      subtree-end)))

(defun mrb/skip-non-projects ()
  "Skip trees that are not projects"
  (let* ((subtree-end (save-excursion (org-end-of-subtree t))))
    (if (mrb/is-project-p)
        nil
      subtree-end)))

(defun mrb/skip-projects ()
  "Skip trees that are projects"
  (let* ((subtree-end (save-excursion (org-end-of-subtree t))))
    (if (mrb/is-project-p)
        subtree-end
      nil)))

;; When in agenda mode, show the line we're working on.
(add-hook 'org-agenda-mode-hook (lambda () (hl-line-mode 1)))

;; Remove empty property drawers
(defun mrb/org-remove-empty-propert-drawers ()
  "*Remove all empty property drawers in current file."
  (interactive)
  (unless (eq major-mode 'org-mode)
    (error "You need to turn on Org mode for this function."))
  (save-excursion
    (goto-char (point-min))
    (while (re-search-forward ":PROPERTIES:" nil t)
      (save-excursion
        (org-remove-empty-drawer-at "PROPERTIES" (match-beginning 0))))))

(defun mrb/org-remove-redundant-tags ()
  "Remove redundant tags of headlines in current buffer.

 A tag is considered redundant if it is local to a headline and
 inherited by a parent headline."
  (interactive)
  (when (eq major-mode 'org-mode)
    (save-excursion
      (org-map-entries
       '(lambda ()
          (let ((alltags (split-string (or (org-entry-get (point) "ALLTAGS") "") ":"))
                local inherited tag)
            (dolist (tag alltags)
              (if (get-text-property 0 'inherited tag)
                  (push tag inherited) (push tag local)))
            (dolist (tag local)
              (if (member tag inherited) (org-toggle-tag tag 'off)))))
       t nil))))


(defvar org-agenda-group-by-property nil
  "Set this in org-mode agenda views to group tasks by property")

(defun mrb/org-group-bucket-items (prop items)
  (let ((buckets ()))
    (dolist (item items)
      (let* ((marker (get-text-property 0 'org-marker item))
             (pvalue (org-entry-get marker prop t))
             (cell (assoc pvalue buckets)))
        (if cell
            (setcdr cell (cons item (rest cell)))
          (setq buckets (cons (cons pvalue (list item))
                              buckets)))))
    (setq buckets (mapcar (lambda (bucket)
                            (cons (first bucket)
                                  (reverse (rest bucket))))
                          buckets))
    (sort buckets (lambda (i1 i2)
                    (string< (first i1) (first i2))))))

(defadvice org-agenda-finalize-entries (around org-group-agenda-finalize
                                               (list &optional nosort))
  "Prepare bucketed agenda entry lists"
  (if org-agenda-group-by-property
      ;; bucketed, handle appropriately
      (let ((text ""))
        (dolist (bucket (mrb/org-group-bucket-items
                         org-agenda-group-by-property
                         list))
          (let ((header (concat "Property "
                                org-agenda-group-by-property
                                " is "
                                (or (first bucket) "<nil>") ":\n")))
            (add-text-properties 0 (1- (length header))
                                 (list 'face 'org-agenda-structure)
                                 header)
            (setq text
                  (concat text header
                          ;; recursively process
                          (let ((org-agenda-group-by-property nil))
                            (org-agenda-finalize-entries
                             (rest bucket) nosort))
                          "\n\n"))))
        (setq ad-return-value text))
    ad-do-it))
(ad-activate 'org-agenda-finalize-entries)


(defvar mrb/org-my-archive-expiry-days 365
  "The number of days after which a completed task should be auto-archived.
 This can be 0 for immediate, or a floating point value.")

(defun mrb/org-my-archive-done-tasks ()
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (let ((done-regexp
           (concat "\\* \\(" (regexp-opt org-done-keywords) "\\) "))
          (state-regexp
           (concat "- State \"\\(" (regexp-opt org-done-keywords)
                   "\\)\"\\s-*\\[\\([^]\n]+\\)\\]")))
      (while (re-search-forward done-regexp nil t)
        (let ((end (save-excursion
                     (outline-next-heading)
                     (point)))
              begin)
          (goto-char (line-beginning-position))
          (setq begin (point))
          (when (re-search-forward state-regexp end t)
            (let* ((time-string (match-string 2))
                   (when-closed (org-parse-time-string time-string)))
              (if (>= (time-to-number-of-days
                       (time-subtract (current-time)
                                      (apply #'encode-time when-closed)))
                      mrb/org-my-archive-expiry-days)
                  (org-archive-subtree)))))))))

(defalias 'archive-done-tasks 'mrb/org-my-archive-done-tasks)

Key and mouse bindings

Keyboard binding are the primary way to interact for me. I have been struggling with consistent keyboard shorcuts and how to integrate them with the nother systems on my machine which capture shortcut keys. At this time the following applications capture shortcut keys:

  1. the awesome window manager captures keys;
  2. xbindkeys provides a number of key bindings for application dependent operations;
  3. emacs (and obviously all other applications, but those are largely irrelevant).
  4. the X-windows server has the kbd extension which has some keyboard related things to configure.
  5. The linux kernel provides key mapping, so I have to look at that place too (xmodmap)

Because I am daft, here is the notation for the modifiers:

To help me out with this when writing about key bindings, the lisp function key-description can help out, with a little bit of glue around it:

(defun mrb/insert-key-description ()
  "Insert a pretty printed representation of a key sequence"
  (interactive)
  (insert (key-description (read-key-sequence "Type a key seqence:"))))

I like the explicit notation where the name of the key is spelled out better, and I'll move all configured keybindings to that eventually.

The right alt and the right key should be the same as the left alt and the super key, but I haven't gotten around to configuring that yet.

Furthermore, because I still can't remember keys, after pressing a prefix key like C-c the package which-keys can show me a formatted menu with the combinations of keys that can follow it.

; which keys shows menu with completions of prefix-key
(use-package which-key
  :diminish
  :config
  (setq which-key-idle-delay 1.8
        which-key-show-operator-state-maps t)
  (which-key-mode))

First, unsetting the keys I don't want.

Let's begin with killing some bindings which are in my way, notably the standard right mouse click behaviour. This is because I want it to behave in org-mode, which apparently sets this. I should probably find out a better way for this.

(global-unset-key (kbd "<mouse-3>"))

Setting keys

Binding keys if fine and all, but remembering them later is rather hard, especially if they wander all over the configuration file. It seems that `bind-key' (now part of `use-package') solves this problem, so let's use that.

(use-package bind-key)

Bind-key can define both global keys as map-based key settings and accepts all kinds of key specifications, including strings.

; Font scaling, like in firefox
(bind-key "C-+" 'text-scale-increase)
(bind-key "C--" 'text-scale-decrease)

;; Line handling functions
(bind-key "s-`" 'toggle-truncate-lines)

;; Most of the time I want return to be newline and indent
;; Every mode can augment this at will obviously (org-mode does, for example)
(bind-key "RET" 'newline-and-indent)

;; Comment code lines, command reacts based on the major mode.
(bind-key "s-/" 'comment-dwim)

(bind-key "s-s" 'save-buffer)

;; Kill buffer
(bind-key "s-k" 'kill-buffer)

;; Resizing windows
;; Introduce a bit of intelligence so the shrink and enlarge know what window I'm in.
(defun mrb/xor (b1 b2)
  "Exclusive or between arguments"
  (or (and b1 b2)
      (and (not b1) (not b2))))

(defun mrb/move-border-left-or-right (arg dir)
  "General function covering move-border-left and move-border-right. If DIR is
  t, then move left, otherwise move right."
  (interactive)
  (if (null arg) (setq arg 5))
  (let ((left-edge (nth 0 (window-edges))))
    (if (mrb/xor (= left-edge 0) dir)
        (shrink-window arg t)
      (enlarge-window arg t)))
  )

(defun mrb/move-border-left (arg)
  (interactive "P")
  (mrb/move-border-left-or-right arg t))

(defun mrb/move-border-right (arg)
  (interactive "P")
  (mrb/move-border-left-or-right arg nil))

;; Same for up and down
(defun mrb/move-border-up-or-down (arg dir)
  "General function covering move-border-up and move-border-down. If DIR is
  t, then move up, otherwise move down."
  (interactive)
  (if (null arg) (setq arg 5))
  (let ((top-edge (nth 1 (window-edges))))
    (if (mrb/xor (= top-edge 0) dir)
        (shrink-window arg nil)
      (enlarge-window arg nil))))

(defun mrb/move-border-up (arg)
  (interactive "P")
  (mrb/move-border-up-or-down arg t))

(defun move-border-down (arg)
  (interactive "P")
  (mrb/move-border-up-or-down arg nil))


;; cut, copy and paste with cmd-key (like on osx).
;; this kinds sucks now, because the rest of the OS does not do this
;; SOLUTION: learn to work with standard emacs keybinding and adjust the OS  ?
(bind-key "s-z" 'undo)
(bind-key "s-x" 'clipboard-kill-region)
(bind-key "s-c" 'clipboard-kill-ring-save)
(bind-key "s-v" 'yank)
(bind-key "s-a" 'mark-whole-buffer)

;; Keypad delete
(bind-key [(kp-delete)] 'delete-char)

;; Make `C-x C-m' and `C-x RET' be different (since I tend
;; to type the latter by accident sometimes.)
;; Should this not be an unset?
(bind-key "C-x RET" nil)

Key bindings

Global

I am running the emacs daemon and sometime when I quit emacs, I want it to quit too. This sounds a bit counterintuitive, but as long as my emacs config is moving and I am not proficient enough in making sure I can apply the changed settings reliably from within emacs, restarting emacs is just easier. This saves me from having to kill the emacs daemon from the terminal.

(bind-key "C-x C-q" 'save-buffers-kill-emacs)

Probably the most important key is M-x (as set by default). That key gives access to other commands within emacs, so it better be effective. If I wasn't already used to it, I'd certainly not consider M-x as a first candidate. The main objection I have is that the two keys are close to eachother, making it hard to press in a typing flow.

I'm using helm which has a replacement for the M-x command, see the helm config for details.

What should have been in emacs

Sometimes there are logical gaps in emacs' keybinding. I put them here

(bind-keys :map help-map
          ("A" . describe-face))

Special keys

For some special keys I have defined some commands. Special keys are those keys that may not be on every keyboard, within reason. I consider the function keys also as special, although they do not fit the previous definition.

;; Menu key does M-x, if we have it.
;;(bind-key (kbd "<apps>") 'execute-extended-command)
(bind-key "<f1>" 'help-command)
(bind-key "<f2>" 'save-buffer)
(bind-key "<f4>" 'find-file)

;; Define the toggle-frame-fullscreen function in
;; the case that it is not already.

(if (not (fboundp 'toggle-frame-fullscreen))
    (defun toggle-frame-fullscreen()
      (interactive)
      (when (eq window-system 'x)
        (set-frame-parameter
         nil 'fullscreen
         (when (not (frame-parameter nil 'fullscreen)) 'fullboth)))))

;; Make gnome compliant, define a full-screen function and bind to F11
(defun mrb/switch-full-screen ()
  (interactive)
  (toggle-frame-fullscreen))
(bind-key "<f11>" 'mrb/switch-full-screen)

;; Not sure what this is? It is not the menu key.
(bind-key [XF86MenuKB] 'accelerate-menu)
(bind-key [XF86Battery] 'display-battery-mode)

Resizing and switching windows and frames

;; Moving back and forth in windows For now, I'm using the Fn Key +
;; Arrows, seems consistent with the other window movements
(bind-key [XF86AudioNext] 'next-multiframe-window)
(bind-key [XF86AudioPrev] 'previous-multiframe-window)

;; Alt-Cmd left-right arrows browse through buffers within the same frame
(bind-key "<M-s-left>"  'previous-buffer)
(bind-key "<M-s-right>" 'next-buffer)

;; These would conflict with awesome bindings, perhaps we should change those bindings
;; (bind-key "<C-S-left>"  'buf-move-left)
;; (bind-key "<C-S-right>" 'buf-move-right)

;; Awesome uses Super+Arrows to move between its 'frames'
;; Emacs   uses Shift-Super+Arrows to move between its windows
(bind-key "<S-s-right>" 'windmove-right)
(bind-key "<S-s-left>"  'windmove-left)
(bind-key "<S-s-up>"    'windmove-up)
(bind-key "<S-s-down>"  'windmove-down)

Emacs has, like for everything else, a peculiar idea on scrolling and moving from screen to screen.

These settings work better for me.

I have my keyboard repeat rate rather quick; this helps by moving the cursor fast. It also means that if I press a key like backspace things disappear quite quickly, so it's important that what happens on the screen is 'real-time'. The effect I want to prevent is that when releasing the backspace key, the cursor keeps on going and deletes way more than needed. I think, by default, this is properly configure, but I just want to make sure.

(setq redisplay-dont-pause t)

When scrolling, I don't tend to think in half-screens like emacs does, I just want the text in the window to move up or down without having to guess where it's going to be. Make sure we scroll 1 line and have a small, or none at all, scroll-margin. Having both at a value of 1 is intuitive.

(setq scroll-margin 1  scroll-step 1)

Make sure that the scroll wheel scrolls the window that the mouse pointer is over and that we are scrolling 1 line at a time. I don't use any modifiers with the scroll wheel.

(xterm-mouse-mode)
(setq mouse-wheel-follow-mouse 't)
(setq mouse-wheel-scroll-amount '(1 ((shift) . 1)))
(setq focus-follows-mouse t) ;; i3wm changes focus automatically

Terminals, Character encodings and emulation

My policy is to use Emacs for as many things and use as little programs as necessary. All this within reason obvously.

This sections describes how terminals and shells are used from within Emacs. In an ideal situation I won't be needing any other terminal emulator program, other than the ones used directly from Emacs.

At the moment I only use eshell; the all Emacs-Lisp based solution.

Eshell

Eshell is theoretically the ideal shell; part of emacs, completely implemented in lisp and thus available regardless of the underlying hardware and operating system. In practice, many things won't work as expected.

In the window manager (awesome-wm) 'Cmd Enter' launches a terminal, similarly within Emacs 'C-c Enter' launches Eshell:

(bind-key "C-c RET" 'eshell)

First thing I missed in eshell was a the clear function, as in bash. By defining a function in the “eshell namespace” it gets registered as a command automatically.

(defun eshell/clear ()
  "Clears the shell buffer as in bashes clear"
  (interactive)
  ;; clear read-only of prompts
  (let ((inhibit-read-only t))
        (delete-region (point-min) (point-max))))

We want to be able to open files from within eshell in the same way as we do from the command line shell in the os. For this I have define an alias which just uses find-file-other-window. To be able to open multiple files (so we can use $* as argument instead of just $1) we need to advice find-file-other-window to support this.

(defadvice find-file-other-window (around find-files activate)
  "Also find all files within a list of files. Thsi even works recursively."
  (if (listp filename)
      (loop for f in filename do (find-file-other-window f wildcards))
    ad-do-it))

Set the main directory for eshell, I do not want in in the default place but below the .emacs.d directory where all other configuration of emacs stuff is.

(setq eshell-directory-name (concat emacs-directory "eshell/"))

A list of functions / filters through which interactive output is passed, most of this was copied from the default for adjustment here. I have not actually changed anything myself.

(setq eshell-output-filter-functions
      '(eshell-handle-ansi-color
        eshell-handle-control-codes
        eshell-watch-for-password-prompt
        eshell-handle-control-codes
        eshell-handle-ansi-color
        eshell-watch-for-password-prompt))

As I am migrating from bash, I want eshell to behave as much like bash as possible. The next settings take care of some of the things to make that happen.

;; Do completions, but don't cycle
(setq eshell-cmpl-cycle-completions nil)

;; Completion ignores case
(setq eshell-cmpl-ignore-case t)

;; scroll to the bottom
(setq eshell-scroll-to-bottom-on-output t)
(setq eshell-scroll-show-maximum-output t)
(add-to-list 'eshell-output-filter-functions 'eshell-postoutput-scroll-to-bottom)

Another step closer to a full terminal replacement is to be able to replace lxterminal (my terminal emulator in use now). This requires a number of parts:

  1. something to call from the commandline instead of lxterminal
  2. a lisp function which takes care of the passthrough to eshell, creating the frame, etcetera.

The calling part is simpel: edit -nc -e '(mrb/server-eshell)' or vi -e '(mrb/server-eshell)' for a terminal variant

The last bit of that command line is the function to evaluate, which was taken from http://ur1.ca/cf1m4 and sligthly adapted.

(defun mrb/server-eshell ()
  "Command to be called by emacs-client to start a new shell.

A new eshell will be created. When the frame is closed, the buffer is
deleted or the shell exits, then hooks will take care that the other
actions happen. For example, when the frame is closed, then the buffer
will be deleted and the client disconnected."
  (lexical-let ((buf (eshell t))
                (client (first server-clients))
                (frame (selected-frame)))
    (cl-labels ((close (&optional arg)
                (when (not (boundp 'cve/recurse))
                  (let ((cve/recurse t))
                    (delete-frame frame)
                    (kill-buffer buf)
                    (server-delete-client client)))))
    (add-hook 'eshell-exit-hook #'close t t)
    (add-hook 'delete-frame-functions #'close t t))
    (delete-other-windows)
    nil))

Process handling

Sometimes processes get stuck and i want a way to delete those processes easily.

(defun mrb/delete-process-interactive ()
  "Based on an autocompleted list of process, choose one process to kill"
  (interactive)
  (let ((pname (completing-read "Process Name: "
                                    (mapcar 'process-name (process-list)))))
    (delete-process (get-process pname))))

Completion

There are 2 types of completion:

  1. Input completion in the minibuffer
  2. Inline completion in another buffer

I want completion to work as follows:

  1. completion functions are always bound to a keybinding involving the TAB-key, with as little modifiers as possible;
  2. completion should always produce something, even if emacs has no special semantic knowledge of the current mode, it should produce something which makes sense;
  3. completion should be inline whenever possible.
  4. for each mode, a specialization is ok, if that improves the situation; I expect to have many specialisations to improve the autocomplete quality;
  5. if a completion window must be openened, do this at the same place always and do not mess up other windows.
  6. Completion should behave somewhat like real-time systems. An answer must be produced within a certain amount of time. If a completion answer takes longer than the amount of type to type it in full, the system has collapsed, so the time needs to be in the order of one third of the typing time.

The next secions deal with the above requirments

Ad 1. Bind completion always involves TAB-key

The package smart-tab seems to fit this bill, but the thing that I care about can be achieved fine without it (I only found this out after considerable time using smart-tab).

So, tab tries to indent, which is the main expectation, and if it can't it tries to complete stuff.

(setq tab-always-indent 'complete)

In a standard emacs installation, TAB indents, depending on mode obviously. If indenting would not make sense, a TAB can be inserted or completion could start. The function completion-at-point is used in some situations. Ideally the company-complete function could take over in many cases. Here's a simplistic approach to get me started:

  1. if in minibuffer, do completion there like we are used to;
  2. if cursor is at the end of a symbol, try to complete it with company;
  3. else, indent according to mode.

This is probably incomplete or wrong even in some cases, but it's a start.

This way, TAB always does completion or indent, unless company-mode is not active.

Ad 2. Completion should always produce something

Not sure if there is anything to do here.

Ad 3. Inline completion when possible

With inline completion I mean without opening a whole new *Completions* window if not needed.

Content that I want:

Candidates:

I had auto-complete installed for a while and this worked fine. I am migrating to company-mode now, as it seems a lot faster and a lot easier to write backends for. Also, company mode gets more praise from emacs users, but I recall having problems with it. Anyway, let's enable company-mode everywhere for everything except for some modes which we know lead to problems.

The capf backend uses 'completion-at-point-functions' as source for candidates, which is what emacs by default does, so I want that in my backend-lists. This backend gets added automatically when emacs version is larger than 24.3.50. I'm not sure what the logic is behind that, as there is no mention of it not working before that version? Assuming that there is a good reason, I'm not going to do anything to the backends list for now.

The default colors of company-mode are horrendous, but I have put some corrections by executing a snippet, based on standard color-names. The snippet is here: http://www.emacswiki.org/CompanyMode#toc7

The default keybindings needs changing too; moving the selection up and down is mapped to C-p and C-n, just like moving the cursor in normal text.

Having the completion pop up automatically is annoying in most cases, so I disable that and when the popup is there, don't require a match.

(use-package company
  :diminish
  :bind
  (:map company-active-map
        ("C-n" . company-select-next)
        ("C-p" . company-select-previous)
   :map company-mode-map
        ;; Remap normal indent-for-tab-command
        ([remap indent-for-tab-command] . company-indent-for-tab-command))

  :config
  (setq company-idle-delay 0.0
        company-minimum-prefix-length 1
        company-require-match 'never

        company-global-modes '(not
                               org-mode org-journal-mode
                               text-mode))

  ;; And this turns it actually on, except for the modes above
  (add-hook 'after-init-hook 'global-company-mode)

  ;; Taken from: https://github.com/company-mode/company-mode/issues/94#issuecomment-40884387
  ;; Save the normal completion functions temporarily
  (defvar completion-at-point-functions-saved nil)

  (defun company-indent-for-tab-command (&optional arg)
    (interactive "P")
    (let ((completion-at-point-functions-saved completion-at-point-functions)
          (completion-at-point-functions '(company-complete-common-wrapper)))
      (indent-for-tab-command arg)))

  (defun company-complete-common-wrapper ()
    (let ((completion-at-point-functions completion-at-point-functions-saved))
      (company-complete-common))))

Ad 4. Mode specialisation

There's always exceptions to the rule; with Emacs doubly so. Here's the relevant exceptions for my configuration.

Ad 5. Open completion window predictably

If there is a need to open a *Completions* window, let's at least do it predictably, so I know what and where to expect it. The popwin package make this behaviour a bit more predictable by showing it in the same place and making sure I can get rid of it easily.

As popwin is a dependency of which-key, we already have it.

(use-package popwin
  :config
  (popwin-mode 1))

For most of the completion that is needed which is not inline (for which I am using the company package, helm seems to be the most powerful solution. I used to use ido, but helm seems a lot easier and more broad.

(use-package helm
  :diminish
  :bind
  (("C-c M-x" . 'execute-extended-command) ;; Save the default
   ("M-x"     . 'helm-M-x)                 ;; Replace M-x
   ("C-x C-f" . 'helm-find-files)          ;; Find files
   ("C-x b" .   'helm-buffers-list))       ;; Find buffers
  :config
  (helm-mode 1)                            ;; All commands using completing-read (and what else?)
)

Ad 6. Guaranteed response-time

I haven't found a solution for this yet, but also have not found it to be a problem that needs solving in practice so far.

Editing control

The multiple cursors package is often used for repetitive editting.

(use-package multiple-cursors
  ;; Binding mc/edit-lines because I can't call it from M-x with helm
  :bind (("C-c e" . 'mc/edit-lines)))

Navigating pieces of text effectively is probably the best optimization to make in the emacs configuration.

In my browser I use vimium to jump to links with the keyboard. In emacs avy does something similar. On pressing the hotkey, all matches of the first char typed are highlighted (frame wide) by a single character. By pressing that characator the cursor is place there directly. This makes within-frame navigation a 3-key operation, which is considerably faster than anything else.

In a more general sense, evil-mode, the VI emulation layer on top of emacs, is the ultimate navigational package. I have tried this for about 6 months, but it's not for me yet.

Remote editing

As my default shell is zsh tramp does not work out of the box for me. It gets confused by my prompt, so on top of my .zshrc file I have the following line:

# Immediately bail out if we are coming in with a dumb terminal
# which, in my case, mostly means TRAMP is asking for someting.
[[ $TERM == "dumb" ]] && unsetopt zle && PS1='$ ' && return

Once that is in place files can be opened with /ssh:/host:/path/to/file syntax. Surprisingly, the syntax I got used to in the past (i.e. /host:/path/to/file) does not work for me anymore.

(use-package tramp
  :config
  (setq default-tramp-method "ssh"))

Browser integration

My default browser, as set in the OS, should be started automatically on opening http: like stuff and html files. The only link to the outside world is specifying that xdg-open should figure out what program to use.

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

Messaging and chatting

Mail

This section describes a search for the mail setup I want in Emacs. There are a few mail related packages and I'm unsure at what I want to use; reading the feature list doesn't cut it. So, I'm having a setup where multiple mail handling packages may exist and hopefully one will float to the top as being the one

Sending mail

Sending mail through smtp, using the smtpmail package

(use-package smtpmail
   :init
   (setq smtpmail-default-smtp-server "localhost"
         smtpmail-service 25)
   :config
   (setq
    smtpmail-local-domain user-domain
    smtpmail-sendto-domain user-domain
    ;; User agent style is message mode by default, specific mail packages may override this
    mail-user-agent 'message-user-agent
    send-mail-function 'smtpmail-send-it ;This is for mail-mode
    message-send-mail-function 'message-smtpmail-send-it) ; This is for message-mode

   (setq password-cache t)            ; default is true, so no need to set this actually
   (setq password-cache-expiry 28800)) ; default is 16 seconds, which is ridiculously low

At times we want to send signed and/or encrypted mail

(use-package mml-sec
  :ensure nil 							; Built-in
  :config
  ; Use my key to sign
  (add-to-list 'mml-secure-openpgp-signers user-gpg-encrypt-key)

  ;; Add my bcc address to list of safe addresses in bcc for secure message
  (add-to-list 'mml-secure-safe-bcc-list "mrb+Sent@hsdev.com"))

Composing mail

Composing mail is often an out of band activity, like creating a tweet or a capture, so I would like to have roughly the same behaviour. This is by default provided by compose-mail-other-frame, which in turn calls the right mua to do the job. Oddly enough, it is still required to have mu4e-compose-in-new-frame set to actually have another frame, so I'm writing it with mu4e-compose directly.

(defun mrb/compose-mail (&optional mailto-url)
  "Run mail-compose, use mailto URI if it is given."
  (interactive)

  ;; If we have a mailto argument, parse and use it
  (if (and (stringp mailto-url)
           (string-match "\\`mailto:" mailto-url))
      (browse-url-mail mailto-url)
    ;; No mailto, argument, just run the mua
    (mu4e-compose-new)))
  (bind-key "C-x m" 'mrb/compose-mail)

(defun mrb/mailfile()
  "Compose mail message from current buffer file, typically a pdf is viewed for my usecase"
  (interactive)
  (let* ((file (buffer-file-name))
         (mimetype (mrb/mimetype file)))
    (mu4e-compose-new)
    (mml-attach-file file mimetype (concat mimetype " attachment") "attachment")))

;; We can either use file(1) in the OS or mailcap.
;; The latter is just extension based, which suits me fine
;; in 99% of the cases. (i.e. .pdf)
(defun mrb/mimetype(file)
  "Get a mimetype for a certain file extension, using mailcap functionality"
  (mailcap-extension-to-mime
   (file-name-extension file t)))

To be able to use the mrb/compose-mail function as a mailto handler we need to be able to call it from outside of emacs. Let's define a small shell script that does exactly this. The SRC attributes tangle it into my bin directory where is will be in the path. In the OS, this script will need to be set as the default mail handler.

# Emacs mailto URI handler

# Make sure mailto: is always prepended to $1
mailto="mailto:${1#mailto:}"
mailto=$(printf '%s\n' "$mailto" | sed -e 's/[\"]/\\&/g')

# Call the elisp function handling our mailto URI
elisp_expr="(mrb/compose-mail \"$mailto\")"

# Do not create new frame, because mu4e does that already
# which is a simpler solution in this case
edit-noframe --eval "$elisp_expr"

On top of that, I want a manual capture script which is basically the same as the mailto handler.

edit -e '(mrb/compose-mail)'

It's not entirely clear which package is exactly responsible for what, but there are a few settings which are related to the message package

(use-package message
  :ensure nil  ;; Emacs included, no need to go look for it
  :requires org-faces
  :config
  (setq
   ;; When citing, remove the original signature
   ;; FIXME: often I want to remove a lot more, like the disclaimer
   message-cite-function 'message-cite-original-without-signature

   ;; Citing/Quoting when replying
   message-yank-prefix "> "				; Add '> ' when quoting lines
   message-yank-cited-prefix ">"		; Add '>' for cited lines
   message-yank-empty-prefix ""			; Add nothing for empty lines

   ;; Always put one in the Sent folder on sending.
   message-default-mail-headers "Bcc: mrb+Sent@hsdev.com\n"

   ;; How to attribute
   message-citation-line-format "[%N]:"
   ;; Make sure we use it
   message-citation-line-function 'message-insert-formatted-citation-line

   message-kill-buffer-on-exit t))

(defun mrb/message-mode-hook ()
  "This configures message mode and thus other modes which inherit message mode."
  ;; We have to make sure that the messages we produce are
  ;; format=flowed compatible. This happens on encoding when sending
  ;; it. However, during compose we need to make sure that what we
  ;; offer to encode is suitable for it.

  ;; Let's do the compose part first No reflowing is possible without
  ;; having hard newlines, so lets always enable those
  (use-hard-newlines t 'always)

  ;; Use visual line mode and visually break at word boundaries which
  ;; is the same as the email encoding on send (see below)
  (visual-line-mode 1)
  (auto-fill-mode 0)                    ; Makes no sense in visual mode

  (setq visual-fill-column-width nil) 	; Make sure `fill-column gets used
  (setq fill-column 80)                 ; Not the same as encode column below!
  (visual-fill-column-mode 1)           ; Show it

  ;; Next, do the encoding and sending part
  ;; Set the fill column on encoding (send)
  ;; Q: where does the 66 come from, surely it could be a little bit more?
  (setq fill-flowed-encode-column 66)

  ;; This enables the f=f encoding
  (setq mml-enable-flowed t))

;; Enable it when entering message mode
(add-hook 'message-mode-hook 'mrb/message-mode-hook)

Mu4e

After a journey with many different MUAs I seem to be settling on mu4e.

(setq mrb/mu4e-path "/usr/local/share/emacs/site-lisp/mu4e")
;;(setq mrb/mu4e-path "/home/mrb/dat/src/mail/mu/mu4e")
(use-package mu4e
  :ensure nil
  :load-path mrb/mu4e-path
  :after message fullframe
  :commands mu4e
  :bind (:map mu4e-main-mode-map
              ("/" . mu4e-headers-search)
              :map mu4e-headers-mode-map
              ("/" . mu4e-headers-search)
              ("s" . mu4e-headers-mark-for-spam)
              ("r" . mu4e-compose-reply)
              ("R" . mu4e-headers-mark-for-refile)
              ("X" . (lambda () (interactive) (mu4e-mark-execute-all t)))
              :map mu4e-view-mode-map
              ("/" . mu4e-headers-search)
              ("s" . mu4e-view-mark-for-spam)
              ("r" . mu4e-compose-reply)
              ("R" . mu4e-view-mark-for-refile)
              ("X" . (lambda () (interactive) (mu4e~view-in-headers-context (mu4e-mark-execute-all t)))))
  ;; TODO: how are we editting a search? e?

  :init
  (fullframe mu4e mu4e-quit)			; Claim the whole screen
  :config
  ;; Support icalendar Yes/No/Maybe reactions to calendar invites
  (use-package mu4e-icalendar
    :load-path mrb/mu4e-path
    :config
    (mu4e-icalendar-setup))

  (use-package mu4e-query-fragments)	; Support reusable query pieces '%junk' etc.
  (use-package mu4e-jump-to-list)       ; Shortcut key 'l' to directly focus on list threads

  ;; Patch highlighting inside messages
  (use-package mu4e-patch
    :ensure nil
    :load-path "/home/mrb/dat/src/emacs/packages/mu4e-patch"
    :config
    (add-hook 'mu4e-view-mode-hook #'mu4e-patch-highlight))

  (setq
   mu4e-main-buffer-hide-personal-addresses t
   ;; Override the defaul mail user agent with the mu4e specific one
   mail-user-agent 'mu4e-user-agent
   mu4e-sent-folder     "/Sent"
   mu4e-drafts-folder   "/Drafts"
   mu4e-trash-folder    "/Trash"
   mu4e-attachment-dir  "~/Downloads"	; Absolute folder here, not relative to Maildir!
   mrb/mu4e-junk-folder "/Junk"         ; Added for my specific config

   ;; Refiling to /Archives/YYYY
   mu4e-refile-folder (lambda (msg)
                        (let ((archive_base "/Archives/")
                              (msgyear (format-time-string "%Y" (mu4e-message-field msg :date))))
                          (cond
                           ;; Order: most specific to general, end with the 't' case

                           ;; Archive messages per year if it has a date field (basically, always)
                           (msgyear	(concat archive_base msgyear))

                           ;; The default refile location
                           (t  archive_base))))


   mu4e-confirm-quit nil
   mu4e-use-fancy-chars t
   mu4e-get-mail-command "mbsync quick"	; Just get inbox and important stuff, not everything
   mu4e-decryption-policy 'ask
   mu4e-update-interval 120         ; we just index, no retrieve, so 2mins is fine
   mu4e-compose-complete-only-after nil

   ;; Set completing function to standard, so helm works
   mu4e-completing-read-function 'completing-read

   ;; Message view settings
   mu4e-view-show-images t              ; This creates connections, possible privacy issue?
   mu4e-view-image-max-width 800
   mu4e-view-image-max-height 600
   mu4e-view-use-gnus nil
   mu4e-view-show-addresses t			; Show actuall addresses instead of names
   mu4e-view-mode-hook '(turn-on-visual-line-mode)
   mu4e-save-multiple-attachments-without-asking t

   ;; Compose settings
   mu4e-compose-format-flowed t			; Compose in format=flowed
   mu4e-compose-in-new-frame t
   mu4e-compose-dont-reply-to-self t	; Do not include my addresses when replying

   mail-signature t			; use mail-signature file (move to message section?)
   mu4e-compose-signature '(with-current-buffer
                               (find-file-noselect message-signature-file)
                             (buffer-string))

   ;; Header view settings
   mu4e-headers-include-related nil ; Use 'W' to toggle interactively
   mu4e-headers-date-format "%F"  ; ISO format yyyy-mm-dd
   mu4e-headers-results-limit -1  ; For now, needed to have related search results
   mu4e-headers-visible-lines 20  ; Default of 10 is a bit small
   mu4e-headers-auto-update nil	  ; Prevent confusion


   ;; Jump to some folders directly with a shortcut
   mu4e-maildir-shortcuts '((:maildir "/Trash" :key ?t)
                            (:maildir "/Junk"  :key ?s)
                            (:maildir "/INBOX" :key ?i :hide t)) ; Just for refiling

   ;; Context definitions
   ;; Goal 1. use global config, unless private address is used
   ;; FIXME: Can we use the one that matched?
   mrb/private-addresses '("marcel@van-der-boom.nl" "m@rcel.van-der-boom.nl")
   mrb/private-address "marcel@van-der-boom.nl"
   mu4e-context-policy 'pick-first 		; When nothing matches only!
   mu4e-compose-context-policy 'nil     ; for compose, use current if nothing matches

   mu4e-contexts
   `(,(make-mu4e-context
       :name "Default"
       ;; Use global config, but restore what was changed in other contexts
       :vars `((user-mail-address . "marcel@hsdev.com")))
     ,(make-mu4e-context
       :name "Private"
       :match-func (lambda (msg)
                     (when msg
                       (mu4e-message-contact-field-matches
                        msg `(:to :cc :from) mrb/private-addresses)))
       :vars `((user-mail-address . ,mrb/private-address)))))


  ;; Define actions for the message view
  ;; Allow showing in browser when there is a HTML mess to deal with
  (add-to-list 'mu4e-view-actions
               '("B - view in external browser" . mu4e-action-view-in-browser) t)

  ;; Define marks
  (add-to-list 'mu4e-marks
               '(spam
                 :char ("S" . "💀")
                 :prompt "Mark as spam"
                 :show-target (lambda (dyn-target) "Spam")
                 :ask-target (lambda () mrb/mu4e-junk-folder)
                 :action (lambda (docid msg target)
                           (mu4e~proc-move
                            docid
                            (mu4e~mark-check-target target) "+S-N-u"))))

  ;; Let mu4e define functions for it
  (mu4e~headers-defun-mark-for spam)
  (mu4e~view-defun-mark-for spam)

  ;; Bookmarks
  (setq mu4e-bookmarks
        (list
         (make-mu4e-bookmark :name "Unread messages"
                             :query "flag:unread and not flag:trashed"
                             :key ?u)
         (make-mu4e-bookmark :name "INBOX"
                             :query "(flag:unread or flag:new or maildir:/INBOX) and not (flag:list or flag:draft or flag:flagged or flag:deleted or maildir:/Trash or flag:trashed)"
                             :key ?i)
         (make-mu4e-bookmark :name "TODO list"
                             :query "flag:flagged"
                             :key ?+)
         (make-mu4e-bookmark :name "Todays messages"
                             :query "date:today..now"
                             :key ?t)))

  ;; Rendering of html mail is done with shr
  ;; By default, it's inhibiting images, I dont want that, as I only look at the html if needed
  ;; FIXME: this does not work properly
  (defun mrb/mu4e-shr2text (msg)
    "Overridden mu4e-shr2text"
    (mu4e~html2text-wrapper
     (lambda ()
       (let ((shr-inhibit-images t)	; no images, unless I specifically ask for it
             (shr-width nil)            ; Use whole window width for rendering
             (shr-use-colors nil)       ; Don't use colors from html, they ugly
             (shr-bullet "• "))		; Original was "* "
         (shr-render-region (point-min) (point-max)))) msg))

  (setq mu4e-html2text-command 'mrb/mu4e-shr2text)

  ;; Make header configuration explicit
  (setq mu4e-headers-fields
        '((:flags . 6)(:human-date . 12)(:mailing-list . 10)(:from . 22)(:subject)))
  )

To enable integration with orgmode org-mu4e must be loaded

(use-package org-mu4e
        :ensure nil
        :load-path "/usr/local/share/emacs/site-lisp/mu4e/"
  :after org)

Sieve

Cyrus sieve is used on the server to filter our mail and this is accessed by the sieve-manage package. For some reason I can't get the 'M' characters out of the process, so here's a hack for that

(use-package sieve
  :commands sieve-manage
  :config
  (setq sieve-manage-authenticators '(plain digest-md5 cram-md5 scram-md5 ntlm login)))

  (defun dos2unix ()
    "Replace DOS eolns CR LF with Unix eolns CR"
    (interactive)
      (goto-char (point-min))
      (while (search-forward "\r" nil t) (replace-match "")))

  (add-hook 'sieve-mode-hook 'dos2unix)

which basically goes over the whole sieve script and removes the 'M' characters from the buffer

Elfeed

In an attempt to do even more in emacs, i've installed `elfeed' and imported my feeds from RSSyl

(use-package notifications)

(use-package elfeed
  :after notifications
  :commands elfeed
  :bind (("C-c f" . 'elfeed)
         :map elfeed-show-mode-map
         ("w" . 'mrb/elfeed-show-toggle-watchlater)
         ("v" . 'mrb/elfeed-play-with-mpv)
         :map elfeed-search-mode-map
         ("w" . 'mrb/elfeed-search-toggle-watchlater))
  :init
  (setf url-queue-timeout 30
        elfeed-db-directory "~/.elfeed")

  :config
  (defun mrb/elfeed-search-toggle-tag(tag)
    (let ((entries (elfeed-search-selected)))
      (cl-loop for entry in entries do
               (if (elfeed-tagged-p tag entry)
                   (elfeed-untag entry tag)
                 (elfeed-tag entry tag)))
      (mapc #'elfeed-search-update-entry entries)
      (unless (use-region-p) (forward-line))))

  (defun mrb/elfeed-search-togggle-watchlater()
    (mrb/elfeed-search-toggle-tag 'watchlater))

  (defun mrb/elfeed-show-toggle-tag(tag)
    (interactive)
    (if (elfeed-tagged-p tag elfeed-show-entry)
        (elfeed-show-untag tag)
      (elfeed-show-tag tag)))

  (defun mrb/elfeed-show-togggle-watchlater()
    (mrb/elfeed-show-toggle-tag 'watchlater))

  ;; TODO: mrb/elfeed-enqueue-video
  (defun mrb/elfeed-play-with-mpv ()
    "Play elfeed link in mpv"
    (interactive)
    (notifications-notify
     :title "Elfeed action"
     :body "Playing video with MPV"
     :app-name "Elfeed")

    (start-process "elfeed-mpv" nil
                   "umpv"
                   (elfeed-entry-link elfeed-show-entry)))


  ;; New entry hook allows meta information manipulation
  ;; without directly having to change elfeed-feeds
  (add-hook 'elfeed-new-entry-hook
            (elfeed-make-tagger :feed-url "youtube\\.com"
                                :add '(video youtube)))
  (add-hook 'elfeed-new-entry-hook
            (elfeed-make-tagger :feed-url "vimeo\\.com"
                                :add '(video vimeo))))

Elfeed allows to interactively subscribe to a feed (defaulting to what is in the clipboard. Managing elfeed-feeds as a variable is kinda clumsy, although very flexible. There is a middle ground which fits me even better. At the cost of the bare-bone flexibilith of having the feeds directly in lisp, I'm using an orgmode file to record the feeds i want.

(use-package elfeed-org
  :after elfeed
  :init
  (setq rmh-elfeed-org-files (list (concat emacs-directory "feeds.org")))
  (elfeed-org))

IRC

I'm experimenting with Weechat now, mostly because it has multiple clients and can make my IRC experience a bit more like Telegram, i.e. regardless of client, the chat state remains the same.

(use-package weechat
  ;; I now have a patch in place ignoring the problem until issue is resolved upstream
  ;; See: https://github.com/the-kenny/weechat.el/issues/80
  ;:if (version< emacs-version "27")
  :after tracking
  :config
  (use-package weechat-tracking :ensure nil)
  (use-package weechat-notifications :ensure nil)
  (use-package weechat-image :ensure nil)
  (setq weechat-host-default "chat.hsdev.com"
        weechat-port-default 8443
        weechat-mode-default 'ssl
        weechat-auto-monitor-buffers t   ;; Auto monitor all buffers for now
        weechat-tracking-types '(:message :highlight)
        weechat-image-max-height 300
        weechat-image-resize t
        weechat-color-list '(unspecified
                             "#2e3440" "#4c566a" "#d08770" "#bf616a"
                             "#d8dee9" "#a3be8c" "#e5e9f0"
                             "#ebcb8b" "#81a1c1" "#5e81ac"
                             "#eceff4" "#b48ead" "#88c0d0"
                             "#8fbcbb" "#3b4252"
                             "#434c5e"))

    (add-to-list 'weechat-modules 'weechat-notifications)
    (add-to-list 'weechat-modules 'weechat-tracking)
    (add-to-list 'weechat-modules 'weechat-image)
    (add-hook 'weechat-mode-hook 'visual-line-mode)

    ;; Make a convenient connect function
    (defun mrb/chat()
      (interactive)
      (weechat-connect
       weechat-host-default
       weechat-port-default
       nil
       weechat-mode-default)))

Mastodon

Not messaging or chatting perse, but a microblog social network. The 'capture a toot' is hotkeyed through xbindkeys like other capture commands.

;; Mastodon in emacs
(use-package mastodon
  :config
  (setq mastodon-instance-url "https://mastodon.nl"
        mastodon-auth-source-file "~/.authinfo.gpg")

  ;; Convenience function
  (defun mrb/capture-toot()
    (interactive)
    (mastodon-toot)))
edit-noframe --eval '(mrb/capture-toot)'

Development settings

Some settings which aid in development tasks.

Trailing whitespace can sneak into files without knowing it. I could enable the display of trailing whitespace, but I find that annoying to look at. Instead I just remove it just before saving a file. One solution is to enable a before-save-hook, which would make me the trailing-whitespace police in all projects.

Alternatively a package ws-butler exists on github, which solves this exact problem. It implements the removal of whitespace on top of the highlight-changes-mode which, at least in theory, would remove only the trailing whitespace on changes I have made.

However, this is problematic. It needs a special initialization because I run in server mode (doable), but its global mode is way to global, because it just applies to all buffers, which lead to errors on non-file buffers; for example because they are read-only.

Generic

Lets try to use ggtags everywhere.

(use-package ggtags)

(use-package cmake-mode)				; adjusts auto-mode-alist on load

For most of the languages I use (Bash, python, C, Go, HTML Haskell, Lua), the Language Server Protocol seems to be supported by lsp-mode, so I'm setting this up generally here and enable it for each language I'm using if it is supported.

;; LSP mode
(use-package lsp-mode
  :pin melpa
  :commands (lsp-deferred lsp)

  ;; Enable lsp for the languages that do not have their own section yet
  :hook (c++-mode    . lsp-deferred)
  :hook (c-mode      . lsp-deferred)
  :hook (css-mode    . lsp-deferred)
  :hook (html-mode   . lsp-deferred)
  :hook (js2-mode    . lsp-deferred)
  :hook (python-mode . lsp-deferred)
  :hook (sh-mode     . lsp-deferred)
  :hook (yaml-mode   . lsp-deferred)
  :init

  (use-package lsp-ui)
                                        ;(use-package helm-lsp    :commands helm-lsp-workspace-symbol)

  :config
  (setq lsp-enable-snippet nil
        lsp-prefer-flymake nil
        lsp-ui-doc-position 'bottom
        read-process-output-max (* 1024 1024) ; As per https://emacs-lsp.github.io/lsp-mode/page/performance/
        lsp-prefer-capf t
        lsp-idle-delay 0.5              ; Tuning know for frustration
        )
)

Make our source look a bit more attractive by enabling prettify-symbol-mode in all programming modes

;; List of replacements, this needs to be a function to eval in hook scope
(defun mrb/prettify-symbols()
  (interactive)
  ;; If you want to see the effect: enable prettify-symbols mode
  (setq prettify-symbols-alist
      '(("lambda" . ?λ)
        ("->"     . ?⟶)
        ("=>"     . ?⟹)
        ("#t"     . ?⟙)
        ("#f"     . ?⟘)))
  (prettify-symbols-mode))

;; Add hooks for all programming languages, for now
;; TODO: check for conflict with haskell, which has its own system.
(add-hook 'prog-mode-hook 'mrb/prettify-symbols)
(add-hook 'lisp-interaction-mode-hook 'mrb/prettify-symbols)

Reference & documentation

While emacs itself has a very nicely documentation system, I still don't understand why this is not interpolated into every other mode and or language.

Example: C-h-f in lisp-mode describes the function to me. If I am in a python file, I want it to do exactly the same, but for the python function. There's a page on emacswiki dealing with this a bit: https://www.emacswiki.org/emacs/Context_sensitive_help

Anyways, here's what I have configured related to reference information and documentation

Context sensitive

Regardless of (programming) language there's certain information that's needed on the spot. These are the function signatures, the specific syntaxes when typing Math symbols, orgmode oddities etc.

I want that type of information directly at hand. One part of the solution is ElDoc. This shows documentation in the echo area for a language. ElDoc is built into Emacs so that's a good start. Let's enable it for the modes that have support for it.

(dolist
    (the_mode
     (list 'emacs-lisp-mode-hook
           'lisp-interaction-mode-hook
           'ielm-mode-hook
           'python-mode))
  (add-hook the_mode 'eldoc-mode))

However, having the documentation in the echo area is, especially on big monitors, often far away from where I am typing. So, eldoc-overlay shows the same information, but right below the point, which is alreay a lot more useful.


(use-package eldoc-overlay
  :ensure t
  :init (eldoc-overlay-mode 1)
  :config
  (setq eldoc-overlay-backend 'quick-peek))

I've also looked at eldoc-box which seems a bit more modern, but it didn't work for me

Reference information

If the inline documentation presented by ElDoc is not sufficient, I want a way to spaw reference documentation based on the context.

The closest thing I found is zeal or dash which allow docsets to be searched. There is a gui and helm-dash is an interface to the docsets for emacs. Typically I install docsets through the GUI or manually (the folder names are sometimes not the same when I use helm-dash to install docsets). This solution gives me at least access to reference information for most of the programming languages I use. The direct accest to the sources is missing, or I don't know how to do this yet.

;; Unify reference documentation with dash
(use-package helm-dash
  :commands (helm-dash helm-dash-at-point)
  :config
  (setq helm-dash-browser-func 'eww		; Within dash, keep browser links within emacs
                ;; Use all installed docsets
                helm-dash-common-docsets (helm-dash-installed-docsets)))

Dash show documentation inside emacs, which is my preferred method. There are times however I need the desktop GUI Zeal. For one, it's a lot easier to see which docsets are installed and manage them.

;; Global keybinding ot open documentation
  ;; This should probably be somewhere else
  (use-package zeal-at-point
     :bind
     ("s-d" . zeal-at-point))

RFC document reader

I often need to consult RFC documents, so not having to leave emacs for that and be able to use all tools on RFC documents is very helpful.

;; Special mode to read rfc documents locally
(use-package rfc-mode
:init
(setq rfc-mode-directory (expand-file-name "~/dat/doc/rfc/")
        rfc-mode-index-path (concat rfc-mode-directory"rfc-index.txt")))

Man pages

There's a builtin package to read man pages, let's make it explicit.

(use-package man)

Epub documents

Not often, but increasingly so, reference books are in epub format. So, for that we need a reader package. nov seems to be the best option

(use-package nov)

Coding styles

Different projects use different coding styles. The ones I need I'll gather here for now.

My personal style will be called mrb and basing it on the linux style.


;; Basing it on k&r, no real reason, does it really matter?
(c-add-style "mrb"
             '("k&r"
               (indent-tabs-mode . nil)
               (c-basic-offset . 2)
               (c-cleanup-list . (scope-operator
                                  space-before-funcall))))

;; Make my style the default
(setq c-default-style
      '((java-mode . "java")
        (awk-mode  . "awk")
        (other     . "mrb")))

;; EditorConfig support, not used much by the looks of it
(use-package editorconfig
  :ensure t
  :diminish ""
  :config
  (editorconfig-mode 1))


Haskell

I am just starting out with haskell, but the two things that are probably needed in any case are a mode to use when editting Haskell source (*.hs files) and the ghc-mod package to help with completion and showing syntax errors.

(use-package haskell-mode
  :ensure ghc
  :init
  (progn
    (add-hook 'haskell-mode-hook 'interactive-haskell-mode)
    (add-hook 'haskell-mode-hook 'turn-on-haskell-doc-mode)
    (add-hook 'haskell-mode-hook 'turn-on-haskell-indent))

  :mode "\‌\.hs\\'"
  :config

  ;; Replace ⇒ with ⇉ q
  (delete '("=>" . "⇒") haskell-font-lock-symbols-alist)
  (add-to-list 'haskell-font-lock-symbols-alist '("=>" . "⇉"))
  (setq haskell-font-lock-symbols t
        haskell-interactive-popup-errors nil))

Because the haskell environment can be different for each project, we need a way to adjust to that for certain things. One such thing is flycheck which will give false-negatives when checking haskell files if the specific build environment is not taken into account. The package flycheck-haskell automatically adjusts flycheck.

(use-package flycheck-haskell
  :config
  (eval-after-load 'flycheck
    '(add-hook 'flycheck-mode-hook #'flycheck-haskell-setup)))

For creating web apps I use yesod, which is supported by a number of packages

;; Yesod's html like files
(use-package hamlet-mode)



Go

(use-package go-mode
  :ensure-system-package godef
  :config
  :bind (:map go-mode-map
              ("C-h f" . godoc-at-point)))

Lisp-like

There are a number of languages which are lisp-like, not in the least Emacs lisp itself. It makes sense to group the configuration for these languages together. In the first part there will be configuration for all lisp-like languages (like the paredit configuration for example). For each specific implementation there may be augmentations to the config, or even a completely separate.

For all of those languages, I want to use paredit which makes editting, longer term, a lot more productive.

(use-package paredit
  :diminish
  :config
  ;; For things like *scratch* buffer
  (add-hook 'lisp-interaction-mode-hook #'enable-paredit-mode)
  (add-hook 'emacs-lisp-mode-hook #'enable-paredit-mode)
  (add-hook 'eval-expression-mode-hook #'enable-paredit-mode)
  (add-hook 'scheme-mode-hook #'enable-paredit-mode)
  (add-hook 'lisp-mode-hook #'enable-paredit-mode))

When a mode wants to use paredit, it can use the same constructs as above to enable paredit on it modes.

While we're here, let's do paxedit as well; not sure whey these two package don't merge. It's the same concept. I'm just gonna treat them as one here and enable paxedit whenever paredit gets enabled, but I'll be a bit conservative with the keybindings first.

(use-package paxedit
  :ensure paredit
  :diminish
  :bind (("M-<right>" . 'paxedit-transpose-forward)
         ("M-<left>"  . 'paxedit-transpose-backward)
         ("M-<up>"    . 'paxedit-backward-up)
         ("M-<down>"  . 'paxedit-backward-end))
  :config
  (add-hook 'paredit-mode-hook 'paxedit-mode))

Scheme

Geiser is, among other things, a repl for schemes. I try to use one, mostly for educational purposes. I chose guile because it also claims to support emacs-lisp and might be the future vm on which emacs-lisp will run. So, let's have it and use paredit in its REPL.

(use-package geiser
  :after paredit
  :config
  ; Add implementations to support
  (setq geiser-active-implementations '(guile chez)
        geiser-repl-send-on-return-p nil)

  (add-hook 'geiser-repl-mode-hook #'enable-paredit-mode)
  (add-hook 'geiser-repl-mode-hook 'mrb/prettify-symbols))

Common lisp

The defacto standard for a development environment in Emacs for common-lisp is the the slime package.

(use-package slime
  :after paredit
  :config
  (setq
   ;; Choose an implementation
   inferior-lisp-program "/usr/bin/sbcl"
   slime-lisp-implementations
   '((sbcl ("sbcl" "--core" "/home/mrb/.sbcl/sbcl.core-for-slime")))
   slime-net-coding-system 'utf-8-unix)

  ;; Make it nice to work with
  (add-to-list 'slime-contribs 'slime-fancy)  ;; This loads a bunch, perhaps split?
  (add-to-list 'slime-contribs 'slime-banner) ;; Shows what I am connected to
  (setq slime-startup-animation nil)

  ;; In the REPL slime takes backspace, I want paredit to have it
  (defun override-slime-repl-bindings-with-paredit ()
    (define-key slime-repl-mode-map
      (read-kbd-macro paredit-backward-delete-key) nil))

  ;; Override the bindings and enable paredit
  (add-hook 'slime-repl-mode-hook 'override-slime-repl-bindings-with-paredit)
  (add-hook 'slime-repl-mode-hook #'enable-paredit-mode))


  ;; Trying to get font-locking in the lisp repl
  ;;
  ;; Copy the keywords from lisp mode
  (defvar slime-repl-font-lock-keywords lisp-font-lock-keywords-2)

  ;; Set them up
  (defun slime-repl-font-lock-setup ()
    (setq font-lock-defaults
          '(slime-repl-font-lock-keywords
            ;; From lisp-mode.el
            nil nil (("+-*/.<>=!?$%_&~^:@" . "w")) nil
            (font-lock-syntactic-face-function
             . lisp-font-lock-syntactic-face-function))))

  ;; Set up fontlocking
  (add-hook 'slime-repl-mode-hook 'slime-repl-font-lock-setup)

  ;; Correct the REPL prompt
  (defun slime-repl-font-lock-find-prompt (limit)
    ;; Rough: (re-search-forward "^\\w*>" limit t)
    (let (beg end)
      (when (setq beg (text-property-any
                       (point) limit 'slime-repl-prompt t))
        (setq end (or (text-property-any
                       beg limit 'slime-repl-prompt nil)
                      limit))
        (goto-char beg)
        (set-match-data (list beg end))
        t)))

  (setq slime-repl-font-lock-keywords
        (cons '(slime-repl-font-lock-find-prompt
                . 'slime-repl-prompt-face)
              slime-repl-font-lock-keywords))



On first glance, it looks like slime is a lot nicer than geiser + guile. I read that slime has support for MIT-Scheme as a backend so perhaps I should use that instead of guile.

Gcode

I'm not in the habit of editting gcode, but every now and then I need to look at it and I want it reasonably decent. My usecase is just sending gcode to my 3D-printer which runs Marlin firmware.

Using define-generic-mode is more than enough to define something useful for me.

(use-package generic-x :ensure nil)

(define-generic-mode marlin-gcode-mode
  '((";"))   ;; ; starts a comment
  (apply 'append
         (mapcar #'(lambda (s) (list (upcase s) (downcase s) (capitalize s)))
                 '("keywords?" "exist?")))
  '(("\\([GM]\\)[0-9]+" (1 font-lock-function-name-face))    ; code letters
    ("\\([ABCDEFHIJKLNOPQRSTUVWXYZ]\\)[-]?[0-9]+" (1 font-lock-string-face)) ; parameter letters
    ("\\([\-+]?[0-9]*\\.[0-9]+\\)" (1 font-lock-constant-face)) ; 9.9 type numbers
    ("\\([\-+]?[0-9]+\\)" (1 font-lock-constant-face)))         ; 999 type numbers
  '("\\.gcode\\'")
  nil
  "Mode for marlin g-code files.")

Openscad

Openscad is a script based 3d parametric drawing program. Obviously I'm editting those scripts in emacs.

(use-package scad-mode
  :config
  (setq scad-keywords '("return" "true" "false" "include")))

SQL

SQL has a number of different dialects I use, depending on the database software in use.

(setq sql-server "dbserver.hsdev.com"
      sql-postgres-options '("-P" "pager=off" "-p 5434"))

GIT integration

A common factor in all my development is the use of git. For emacs this automatically means Magit. I've used eshell in the past but that config didn't work out.

Magit

For most, if not all development work (and some other work too) I use git as the revision control system. In emacs that translates to using magit, so let's begin with bringing that in.

Magit forge is a submodule for magit which helps in managing repositories in forges (notably github and gitlab). This looks very promising, albeit slow.

Features I'm interested in: issue commenting, pull requests

(use-package magit
  :after (fullframe)
  :bind
  ("C-c m" . magit-status)

  :init
  (fullframe magit-status magit-mode-quit-window)

  :config
  (setq magit-last-seen-setup-instructions "1.4.0"
        magit-diff-refine-hunk 'all))


(use-package forge
  :after magit
  :config
  (setq forge-add-pullreq-refspec 'ask
        forge-pull-notifications t
        forge-topic-list-limit '(60 . 0))

  ;; Only show assigned stuff
  (magit-add-section-hook
   'magit-status-sections-hook
   'forge-insert-assigned-issues   nil t)
  (magit-add-section-hook
   'magit-status-sections-hook
   'forge-insert-assigned-pullreqs   nil t))

Commiting automatically

I have lost a number of changes in the past because I reverted a file, made a mistake or whatever. Some of these mistakes can be reverted easily if saves are automatically committed

Rather than using an after save hook, there is a minor git-auto-commit mode package which does just what I need.

There is not much to configure for this minor mode. There are a couple of ways to enable it:

  1. file-local variable (put it in the file to be autocommitted)
  2. directory-local variable (make a .dir-locals.el file); this enables it for all files in the directory
  3. as a hook

I'm using the first method on relevant files. The disadvantage of this method is that you have to think about it for each file, so perhaps a .dir-locals.el is a better solution.

I am considering using a generic hook again to enable the method and either using git commit --amend and commit squashing if working on more structured commits. For files that I really do not want autocommit to run I can use a file local variable to disable the hook (or the minor mode)

(use-package git-auto-commit-mode
  :diminish "Auto-committed")

Misc

(use-package gist
  :disabled)

Ansible

Not really a development setting, but closely related to it, as eventually the programs written need to be deployed. Most of the time, while deploying, ansible plays a role in this and it's now worth having a dedicated configuration for editting the YAML files for it.

For now, just the ansible package, and some access to its documentation will suffice

(use-package ansible
  :after yaml-mode
  :init
  (use-package ansible-doc
    :config
    (add-hook 'yaml-mode-hook #'ansible-doc-mode))
  :config
  (add-hook 'yaml-mode-hook '(lambda () (ansible 1))))

Finale

When we are all done with this, provide it.

(provide 'mrb)
;;; mrb ends here