Orgmode configuration

Emacs configuration

This page is a rendering of my Emacs configuration. I use org-babel to specify my configuration in an orgmode file that dynamically gets converted, or tangled as it is called, to an elisp file loaded during emacs startup.

As my jekyll setup is capable of rendering org-mode syntax files directly I have put this original config file in place and have jekyll produce a rendering of it. The original of this file lives on github at the following location:

https://raw.githubusercontent.com/mrvdb/emacs-config/master/mrb.org

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. On top of that I have an auto commit hook on save running at the moment which does not produce the best hitory records. All that will change over time.

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.

1  (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.

1  (setq custom-file (concat emacs-directory "custom.el"))
2  (load custom-file)

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

1  (defun mrb/compile-config ()
2    (interactive)
3    (org-babel-load-file (concat emacs-directory "mrb.org"))
4    (byte-recompile-directory emacs-directory 0)
5  )

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.

 1  #set -x
 2
 3  # This is an improved version of my edit script. The original one is
 4  # still available as edit.old. This one does not use any window
 5  # manager trickery to see if the daemon is already there. This one
 6  # just starts it if it is not already there.
 7  #
 8  # This script should provide the following functionality:
 9  # - start emacs if it hasn't already started
10  # - open a frame if emacs is there, but no frames are visible yet
11  # - be suitable as 'open with...' target for linux file managers
12  # - allow additional command line arguments
13  #   (like -e to just run a piece of elisp)
14  # - change the focus to the emacs frame
15  #
16  # The script is basically a wrapper around emacsclient and we would
17  # like to be compatible with it.
18  #
19  # - TODO: see if we can have some output when the daemon is not there,
20  #         it is now very silent
21  # - TODO: there is a mention of server-visit-hook not running when no
22  #         file is passed in in edit.old. I have not seen any ill effects,
23  #         so left that out
24  # - TODO: the way we open files and treat options is inconsistent, we now
25  #         optimize to opening files and adapt scripts
26  #
27
28  # Save how we are called
29  WEARE=$$(basename $$0)
30
31  # Base invocation of emacsclient
32  EMACS_SOCKET=/tmp/emacs`id -u`/server
33
34  # Check how we are called and adapt
35  # This is for things like commit messages
36  XARGS="--quiet --socket-name=$$EMACS_SOCKET"
37  if [ $$WEARE == "edit" ]; then
38      XARGS=$$XARGS' --no-wait'
39  fi
40  EMACSCLIENT="`which emacsclient` $$XARGS"
41
42  # Number of current visible frames,
43  # Emacs daemon always has a visible frame called F1 which is erm, not visible.
44  visible_frames() {
45      $$EMACSCLIENT --alternate-editor="" --eval '(length (visible-frame-list))' 2>/dev/null
46  }
47
48  # Change the focus to emacs
49  change_focus() {
50      $$EMACSCLIENT --eval "(select-frame-set-input-focus (selected-frame))" > /dev/null
51  }
52
53  # Editting stdin, part 1: save the input to a file
54  save_stdin() {
55      TMPSTDIN="$$(mktemp /tmp/emacsstdinXXX)";
56      cat >"$$TMPSTDIN";
57  }
58
59  # if the file argument is '-', treat this as stdin
60  # TODO: pass $$@ further down?
61  if [ "X$$1" == "X-" ]; then
62      save_stdin
63      # Recursive call!
64      edit $$TMPSTDIN;
65      rm $$TMPSTDIN;
66      exit 0;
67  fi
68
69  # try switching to the frame incase it is just minimized
70  # This will start a server if not running
71  test "$$(visible_frames)" -eq "1" && change_focus
72
73  XARGS=$$@
74  if [ "X$$XARGS" == "X" ]; then
75      XARGS='~/untitled'
76  fi
77  if [ "$$(visible_frames)" -lt  "2" ]; then
78      # need to create a frame
79      # -c $$@ with no args just opens the scratch buffer
80      $$EMACSCLIENT --create-frame "$$XARGS" && change_focus
81  else
82      # there is already a visible frame besides the daemon, change focus
83      # -n $$@ errors if there are no args
84      test  "$$#" -ne "0" && $$EMACSCLIENT "$$XARGS"
85  fi

Package Management

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

This has gotten a bit of attention in the last years and there are now lots of options, of which package.el seems to be the choice if not anything else explicitly preferred. I have always used el-get instead of package.el, although I have no idea why I made that decision at that time, but I think it was because it allowed me to inlcude git recipes in an easy way. ([2014-03-03 ma] Not sure if this is stil the case now)

There are several options:

  1. Just use plain package.el

  2. Use el-get

  3. Use use-package (piggybacks on package.el)

  4. Homebrew

  5. Others…?

The important bit here is that this section manages ALL my packages, including the ones gotten from apt-get, git and elpa. The reason for this is that it provides one neat umbrella (in Emacs) to manage its packages.

We manage everything with el-get so we start by loading that if we don't have it already.

First of all, define the package archives I need; for now, just the default gnu augmented with the melpa repository. I use setq because I want this entry to be the definitive one, not adding to a list.

1  (setq package-archives
2        '(("melpa" . "http://melpa.milkbox.net/packages/")
3          ("gnu"   . "http://elpa.gnu.org/packages/")))
4  (package-initialize)
1  (add-to-list 'load-path (concat emacs-directory "/el-get/el-get"))
2
3  (unless  (require 'el-get nil 'noerror)
4    (with-current-buffer
5        (url-retrieve-synchronously
6         "https://raw.github.com/dimitri/el-get/master/el-get-install.el")
7      (goto-char (point-max))
8      (eval-print-last-sexp)))

Now, el-get is either require’d, and thus available, or loaded by the el-get installer, in which case it is also available.

The rest is basically a long list of sources for el-get where to get the packages from and how to install and update them. When installing a new package, add a recipe in this list, eval it, and run el-get-install with the package name.

 1    ;; Personal or adapted recipes
 2    ;; Sounds like this could use its own file?
 3    (setq el-get-sources
 4          '(
 5            ;; Obviously we need el-get itself, so we can run a self-update
 6            ;; There is a recipe, but we want the master branch of it
 7            (:name el-get :branch "master")
 8            (:name zenburn-emacs          :type github :pkgname "bbatsov/zenburn-emacs")
 9            (:name org-mode               :type git    :url "/home/mrb/dat/src/emacs/packages/org-mode/" :branch "mrb/release_8.2.9")
10            (:name cursor-chg             :type github :pkgname  "emacsmirror/cursor-chg"
11                   :features cursor-chg)
12            (:name newlua-mode            :type github :pkgname "immerrr/lua-mode"
13                   :features lua-mode)
14            (:name oauth                  :type github :pkgname "psanford/emacs-oauth")
15            (:name expand-region          :type github :pkgname "magnars/expand-region.el")
16            (:name mark-multiple          :type github :pkgname "magnars/mark-multiple.el")
17            (:name fixed-point-completion :type github :pkgname "smithzvk/fixed-point-completion")
18            (:name xlicense               :type github :pkgname "emacsmirror/xlicense")
19            (:name autosmiley             :type github :pkgname "emacsmirror/autosmiley")
20            (:name async                  :type github :pkgname "jwiegley/emacs-async")
21            (:name pcache                 :type github :pkgname "sigma/pcache")
22            (:name logito                 :type github :pkgname "sigma/logito")
23            (:name gh                     :type github :pkgname "sigma/gh.el"
24                   :depends (pcache logito))
25            (:name gisthub                :type github :pkgname  "defunkt/gist.el"
26                   :depends (gh))
27            (:name stripe-buffer          :type github :pkgname "sabof/stripe-buffer")
28            (:name git-auto-commit-mode   :type github :pkgname "ryuslash/git-auto-commit-mode")
29            (:name ujelly-theme           :type github :pkgname "marktran/color-theme-ujelly")
30            (:name org-present            :type github :pkgname "rlister/org-present")
31            ;; Dependency? I dont use this directly
32            (:name dash                   :type github :pkgname "magnars/dash.el")
33            (:name gitmessenger           :type github :pkgname "syohex/emacs-git-messenger")
34            (:name org-agenda-property    :type github :pkgname "Bruce-Connor/org-agenda-property")
35            (:name pump.io                :type github :pkgname "cnngimenez/pumpio-el")
36            (:name elfeed                 :type github :pkgname "skeeto/elfeed")
37            (:name guide-key              :type github :pkgname "kbkbkbkb1/guide-key")
38            (:name org-screenshot         :type github :pkgname "dfeich/org-screenshot")
39            (:name json-rpc               :type github :pkgname "skeeto/elisp-json-rpc")
40            (:name btcticker              :type github :pkgname "niedbalski/emacs-btc-ticker"
41                   :depends request)
42            (:name ws-butler              :type github :pkgname "lewang/ws-butler")
43            (:name wrap-region            :type github :pkgname "rejeep/wrap-region.el")
44            (:name vlc                    :type github :pkgname "mrvdb/vlc.el")
45            (:name tsupport               :type github :pkgname "mrvdb/tsupport")
46            (:name elfeed-org             :type github :pkgname "remyhonig/elfeed-org")
47            (:name jabber                 :type git    :url     "/home/mrb/dat/src/emacs/packages/jabber.el" :branch "mrb/master")
48          )
49    )
50
51  (setq mrb/packages
52        (append
53         ;; list of packages we use from standard recipes
54         '(smex ace-jump-mode apache-mode gnuplot-mode sudo-save
55                edit-server magit markdown-mode rainbow-mode rainbow-delimiters scratch popwin
56                highlight-parentheses multiple-cursors git-modes cl-lib keywiz
57                smart-tab yaml-mode org-jekyll hideshow-org oauth flim haskell-mode ghc-mod s f
58                flycheck pkg-info perspective ido-vertical-mode jekyll-el org2blog
59                identica-mode php-mode company-mode csv-mode notmuch use-package
60                git-timemachine eimp mu4e litable diminish paredit fullframe)
61
62         ;; the sources we have configured above
63         (mapcar 'el-get-as-symbol (mapcar 'el-get-source-name el-get-sources))))
64
65    (el-get 'sync mrb/packages)
66    (require 'use-package)

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.

1  (setq user-full-name    "Marcel van der Boom")
2  (setq user-mail-address "marcel@hsdev.com")
3  (setq user-domain "hsdev.com")
4  (setq user-organisation "HS-Development BV")
5  (setq user-xmpp-account (concat user-mail-address "/" system-name))
6  (setq user-gpg-encrypt-key "FEAA7ADD")

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 one password. Let's make the system as simple as we can for now and just store passwords in the gpg agent store only, i.e. authinfo.gpg using public key encryption aimed at my key.

1;; Use only authinfo.gpg for password storage
2(setq auth-sources '("~/.authinfo.gpg"))
3(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.

One solution is to have all these in a separate file, and not publish that file. I’m also encrypting the file, as I’m sure I will publish it by accident some day.

For settings that are made in elisp, load secrets.el.gpg which will be encrypted automatically.

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

Apart from this there is some inline content that is not suitable for storing in the secrets file. For org-mode for example, there is a way to encrypt sections separately. See Encrypting information in org-mode 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

 1  (setq
 2
 3   ; disable backup files (foo~)
 4   backup-inhibited t
 5
 6
 7   ; move files to the trash instead of rm
 8   delete-by-moving-to-trash t
 9
10   ; use clipboard
11   x-select-enable-clipboard t
12
13   display-warning-minimum-level 'error
14   large-file-warning-threshold nil
15   tab-width 4
16   find-file-use-truenames nil
17   find-file-compare-truenames t
18
19   minibuffer-max-depth nil
20   minibuffer-confirm-incomplete t
21   complex-buffers-menu-p t
22   next-line-add-newlines nil
23   kill-whole-line t
24  )
25
26  ;; What I want: one line is one line unless I explicitly tell to wrap
27  ;; FIXME: This does not seem to work.
28  (setq-default truncate-lines t)
29  (global-visual-line-mode 1)
30
31  ;; Only require to type 'y' or 'n' instead of 'yes' or 'no' when prompted
32  (fset 'yes-or-no-p 'y-or-n-p)
33
34  ; Use auto revert mode globally
35  ; This is save because emacs tracks if the file is saved in the editting buffer
36  ; and if so, it will not revert to the saved file.
37  (global-auto-revert-mode t)
38  ;; Also for dired
39  (setq global-auto-revert-non-file-buffers t)
40
41  ;; Turn on auto-fill minor mode for all text buffers
42  (add-hook 'text-mode-hook 'turn-on-auto-fill)
43
44  ;; Should this be here?
45  ;; Try to have urls and mailto links clickable everywhere
46  (define-global-minor-mode global-goto-address-mode
47    goto-address-mode
48    (lambda ()
49      (goto-address-mode 1)))
50  (global-goto-address-mode t)

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

1  (use-package sudo-save)

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

1  (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

 1  ;; Register a new input method
 2  (register-input-method
 3   "MyTeX" "UTF-8" 'quail-use-package
 4   "\\" "Personalized TeX input method"
 5   "~/.emacs.d/mytex-inputmethod")
 6
 7  (defun mrb/set-input-method()
 8    (interactive)
 9    (setq default-input-method "MyTeX")
10    ;; Toggle only if it not active yet
11    (if (not current-input-method)
12        (toggle-input-method)))
13
14  (add-hook 'set-language-environment-hook 'mrb/set-input-method)
15  ;; And now we can set it
16  (set-language-environment "UTF-8")
17
18  ;; So we have it active at this point, but for new buffers it's still
19  ;; not active, let's do that for the most common modes I use
20  ;; FIXME: this works ok-ish, but is not a very clean solution I think.
21  (add-hook 'text-mode-hook 'mrb/set-input-method)
22  ;; Orgmode is derived from outline-mode -> text-mode so this should already work.

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.

1  (defun mrb/fix-fontset-font ( family from &optional to)
2    "Make characters FROM to TO come from FAMILY. The TO parameter
3  defaults to the same value as the FROM parameter. The FAMILY
4  parameter, when specified as nil defaults to 'FreeSerif'"
5    (set-fontset-font
6     t
7     (cons from (or to from))
8     (font-spec :family (or family "FreeSerif"))))

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.

1  (defun mrb/unicode-font-corrections()
2    ;; Add a line like the following  for each char displaying improperly
3    (dolist
4        (the_char
5         (list '#x1f3d8 '#x1f3e0 '#x1f3d8   ;; 🏘 🏠 🏡
6               '#x1f435 '#x1f44d '#x1f4a9   ;; 🐵 👍 💩
7               '#x1f4ac '#x1f4de '#x1f385   ;; 💬 📞 🎅
8               '#x1f60e '#x1f60a '#x1f596)) ;; 😎 😊 🖖
9      (mrb/fix-fontset-font "Symbola" the_char)))

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.

I generally use dark backgrounds, so let's tell Emacs that.

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

Set the zenburn theme, slightly corrected for a darker background color. The default one is too light in daylight conditions.

1  (use-package zenburn-theme)
2  (load-theme 'zenburn)

The zenburn theme does not fit for everything so I have a number of corrections. I do it like this because zenburn is more complete than ujelly and otherwise I'd have a large set of faces defined in a wrong color.

First, the default background of zenburn is to wooshy for me and I like the purple background that Ubuntu uses for the terminal. This happens to mix rather well with the other Zenburn colors, so I'm using that. The actual config for this is in the custom-set-faces section of my custom.el file which I found to be the most reliable for the default settings.

 1  ;; Zenburn underlines date face in org, no go
 2  (set-face-attribute 'org-date nil :underline nil)
 3
 4  ;; Adjust the faces for the modeline
 5  (set-face-attribute 'mode-line nil :foreground "white" :background "grey40" :box nil)
 6  (set-face-attribute 'mode-line-inactive nil :box nil :underline nil :overline "grey40")
 7
 8  (set-face-attribute 'isearch nil :background "sea green" :foreground "black")
 9  (set-face-attribute 'ido-first-match nil :background "sea green" :foreground "black")
10
11  (set-face-attribute 'lazy-highlight nil :inherit isearch-lazy-highlight
12                      :background "black" :foreground "tomato")
13
14  ;; Set the face for the fring to have the normal backbround.
15  (set-face-attribute 'fringe nil :background (face-attribute 'default :background))

I enable hightlighting matching parentheses in many levels globally. I do not know of a situation where not knowing which bracket or parenthesis goes with which is not a useful thing.

Define the colors for the parentheses at different levels. One catch here is that the configuration needs one more color than is actually used. The last entry here does not get highlighted, so my config is valid for 5 levels, not 6.

1(setq hl-paren-colors (quote ("firebrick" "lightgreen" "orange" "cyan" "yellow" "blue")))

And enable the minor mode globally.

 1  (define-globalized-minor-mode global-highlight-parentheses-mode
 2    highlight-parentheses-mode
 3    (lambda ()
 4      (highlight-parentheses-mode t)))
 5  (global-highlight-parentheses-mode t)
 6
 7  ;; no splash screen
 8  (setq inhibit-startup-screen  t)
 9  (setq inhibit-startup-message t)
10  (setq initial-scratch-message nil)
11
12  ;; check speed consequences of this
13  (setq column-number-mode t)
14
15  ;; When I am not typing, the cursor should become more visible, so I
16  ;; don't lose it.
17  ;;(makunbound 'mrb-cursor-color)
18  (setq mrb-cursor-color "springgreen")
19
20  ;; Default frame properties frame position, color, etc
21  (setq default-frame-alist
22        '((cursor-type . (bar . 1))
23          (cursor-color . "springgreen") ;; FIXME: variable use yields error here, why?
24          (height . 60)
25          (width . 100)
26  ))
27
28  (use-package cursor-chg
29    :init
30    (progn
31      (change-cursor-mode 0)        ; On for overwrite/read-only/input mode
32      (toggle-cursor-type-when-idle 1)   ; On when idle
33      (setq curchg-default-cursor-color mrb-cursor-color)
34        (setq curchg-default-cursor-type (quote bar\ \.\ 1))))
35
36
37  ;; Parenthesis matching
38  (show-paren-mode 1)
39  (setq show-paren-style (quote expression))
40  (setq show-paren-delay 0)
41
42  ;; variables for the hightlighting of regions
43  (defvar mrb-bg-paren-match "gray22")
44  (defvar mrb-bg-region "gray35")
45
46
47  ;; Defer fontification a little bit, so we could gain a bit in scroll speed
48  (setq jit-lock-defer-time 0.02)
49
50  ;; Make colorful balanced parentheses etc. in different modes
51  (add-hook 'lisp-mode 'rainbow-delimiters-mode)

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.

 1  (defun mrb/run-client-settings()
 2    (interactive)
 3
 4    (message "Running client settings which server may not have applied")
 5    (tool-bar-mode -1)   ;; No tool-bar
 6    (scroll-bar-mode -1) ;; No scroll-bar
 7    (menu-bar-mode -1)   ;; No menu-bar
 8    (tooltip-mode -1)    ;; No tooltips
 9    (setq fringe-mode '(8 . 0)) ;; Fringe, 8 pixels left and 0 pixels right
10    (set-fringe-mode fringe-mode)
11
12    ;; When making a selection or in case of highlighting a region
13    ;; automatically, keep all font-locking in the region instead of
14    ;; just overwriting the whole section. The trick here is to specify
15    ;; nil in the foreground attribute
16
17    (set-face-attribute 'show-paren-match nil
18                        :inherit nil :foreground nil
19                        :weight 'normal :background mrb-bg-paren-match)
20
21
22    ;; Set regions so we keep the normal font locking
23    (set-face-attribute 'region nil
24                        :inherit nil :foreground nil
25                        :weight 'normal :background mrb-bg-region)
26
27    ;; Probably move this to somewhere else ?
28    (use-package ws-butler
29      :init
30      (progn
31        (add-hook 'lisp-mode-hook         'ws-butler-mode)
32        (add-hook 'emacs-lisp-mode-hook   'ws-butler-mode)
33        (add-hook 'python-mode-hook       'ws-butler-mode)
34        (add-hook 'org-mode-hook          'ws-butler-mode)))
35
36    (mrb/unicode-font-corrections))
37
38
39  ;; This seems to work if we start up emacs using filename on cli and
40  ;; server was not running yet. It does however not work if we just
41  ;; startup emacs (through my edit script) without a filename on the cli
42  (add-hook 'server-visit-hook 'mrb/run-client-settings)

Buffers and files

How do I deal with all those buffers?

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

1  ;; nicer buffer names
2  (use-package uniquify
3    :init
4    (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)

1  (setq auto-save-default t)
2
3  (setq mrb/auto-save-folder "~/.emacs.d/auto-save-list/")
4
5  (add-to-list 'auto-save-file-name-transforms
6               (list "\\(.+/\\)*\\(.*?\\)" (expand-file-name "\\2" mrb/auto-save-folder))
7               t)
1  ;; Minibuffer prompt is a prompt, don't enter it as text.
2  (setq minibuffer-prompt-properties (quote (read-only t point-entered minibuffer-avoid-prompt face minibuffer-prompt)))
3
4  ;; Save places in buffers between sessions
5  (use-package saveplace
6    :init
7    (setq-default save-place t))

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.

1  (setq-default major-mode
2                (lambda ()
3                  (if buffer-file-name
4                      (fundamental-mode)
5                    (let ((buffer-file-name (buffer-name)))
6                      (set-auto-mode)))))
 1  (add-hook 'after-init-hook #'global-flycheck-mode)
 2
 3  ;; Extension mappings
 4  ;; First, specify which files to load when functions are called
 5  (autoload 'markdown-mode "markdown-mode" "Markdown." t)
 6  (autoload 'gnuplot-mode  "gnuplot"       "GNU-Plot" t)
 7  (autoload 'php-mode      "php-mode"      "PHP" t)
 8  (autoload 'css-mode      "css-mode"      "Mode for editing CSS file" t)
 9  (autoload 'apache-mode   "apache-mode"   "Apache config files" t)
10  (autoload 'rainbow-mode  "rainbow-mode"  "Coloring of color codes" t)
11  (autoload 'yaml-mode     "yaml-mode"     "YAML files" t)
12
13  (add-hook 'javascript-mode-hook 'mrb/javascript-custom-setup)
14  (add-hook 'css-mode-hook 'rainbow-mode)
15  (add-hook 'image-mode-hook 'eimp-mode)
16
17  (defun mrb/javascript-custom-setup ()
18    (moz-minor-mode 1))
19
20  ;; Second, specify the extension to function mappings
21  (add-to-list 'auto-mode-alist '("\\.txt\\'"      . org-mode))
22  (add-to-list 'auto-mode-alist '("\\.markdown\\'" . markdown-mode))
23  (add-to-list 'auto-mode-alist '("\\.gp\\'"       . gnuplot-mode))
24  (add-to-list 'auto-mode-alist '("\\.php\\'"      . php-mode))
25  (add-to-list 'auto-mode-alist '("\\.css\\'"      . css-mode))
26  (add-to-list 'auto-mode-alist '("\\.js\\'"       . javascript-mode))
27  (add-to-list 'auto-mode-alist '("\\.htaccess\\'" . apache-mode))
28  (add-to-list 'auto-mode-alist '("\\.patch\\'"    . diff-mode))
29  (add-to-list 'auto-mode-alist '("\\.zsh\\'"      . sh-mode))
30  (add-to-list 'auto-mode-alist '("\\.yml\\'"      . yaml-mode))
31
32  ;; Open scratch buffer by default in the mode we are in at the moment
33  ;; with C-u prefix a mode will be asked to use
34  (use-package scratch)
35
36  ;; Turn on eldoc for modes which support it
37  (dolist
38      (the_mode
39      (list 'emacs-lisp-mode-hook 'lisp-interaction-mode-hook 'ielm-mode-hook 'python-mode))
40    (add-hook the_mode 'turn-on-eldoc-mode))
41
42  ;; Autofill should be off for a couple of modes, but mostly on
43  (auto-fill-mode 1)
44  (dolist
45      (the_mode
46       (list 'nxml-mode))
47    (add-hook the_mode 'turn-off-auto-fill))
48
49  ;; Truncate lines in some modes
50  (dolist
51      (the_mode
52       (list 'python-mode 'org-mode))
53    (add-hook the_mode (toggle-truncate-lines 1)))

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.

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

1  (add-to-list 'load-path "~/dat/src/emacs/packages/org-mode/contrib/lisp/")
2  (add-to-list 'load-path "~/dat/src/emacs/packages/org-mode/contrib/babel/langs/")

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.

1  (delete  "/usr/local/share/emacs/24.3/lisp/org" load-path)
2  ;; CHECKME: is this still the proper installation method, it has changed a lot lately
3  (use-package org)                   ;; This is required, see installation docs
4  ;;(require 'org-special-blocks)  ;; Generalizes the #+begin_foo and #+end_foo blocks,
5                                   ;; useful on latex (export)
6  (use-package org-datetree)          ;; Allows for archiving and refiling in a date organised tree
7  (use-package org-mobile)            ;; Only org-mobile-push and pull get autoloaded and we need
8                                   ;; the file list before that.

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

1(setq org-directory "~/dat/org/")
2(setq org-metadir (concat org-directory "_orgmeta/"))
3(setq org-archive-location (concat org-metadir "archive.org::date-tree"))
4(setq org-default-notes-file (concat org-directory "GTD.org"))
5(setq diary-file (concat org-metadir "DIARY"))

Finally, run some things when entering orgmode.

1(add-hook 'org-mode-hook 'turn-on-auto-fill)

I want to activate org-mode automatically on *.org files, this is probably already the case, but making it explicit can't hurt.

1  (add-to-list 'auto-mode-alist '("\\.org\\'"      . org-mode))

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. Statusnet Updates;

Make sure org-capture is available:

1(use-package org-capture)

Here are the templates used for them.

 1  (setq
 2   org-capture-templates
 3   (quote (
 4           ("b" "Buy"
 5            entry (id "new-todo-receiver") "* BUY %? :buy:\n" :prepend t)
 6           ("t" "Todo"
 7            entry (id "new-todo-receiver") "* TODO %?\n%i" :prepend t)
 8           ("j" "Journal"
 9            entry (file+datetree (concat org-directory "journal.org"))
10            "* ___________________________________________________________ *%U* ___\n\n%?\n")
11           ("w" "Weigh-In"
12            entry (file+headline (concat org-directory "diet.org") "Daily Logs")
13            "* CAL-IN Diet for day %t
14  %^{Weight}p
15  | Food / Exercise | Calories | Quantity | Total |
16  |-----------------+----------+----------+-------|
17  | %?              |          |          |       |
18  |-----------------+----------+----------+-------|
19  | Total           |          |          |       |
20  |-----------------+----------+----------+-------|
21  #+TBLFM: $$4=$$2*$$3;%.0f::$$LR4=vsum(@2$$4..@-I$$4)
22
23  "
24            :prepend t :empty-lines 1)
25           )
26          )
27   )

The concept I use for capturing is that a window is opened in the center of the screen, on top of all windows, on all workspaces, so it shows up whenever I press the shortcut key for it. The exception to this rule is journaling entries, which go into a regular Emacs frame.

So, the first thing I need is a way to create a capturing frame and proper arrangement for finalising the capturing; the frame should be deleted. The frame should also be deleted if the capture is destroyed for some reason. The two defadvice snippets take care of this. Also, by default org-capture splits the screen; we do not need that as we have a dedicated frame for it, so on entering capture mode we just delete the other windows which may be in the frame.

 1  (defun mrb/make-capture-frame ()
 2    "Create a new frame for org-capture to use."
 3    (select-frame (make-frame '((name . "capture")
 4                                (width . 115) (height . 15)
 5                                (menu-bar-lines . 0) (tool-bar-lines . 0))))
 6    )
 7
 8  (defadvice org-capture-finalize (after delete-capture-frame activate)
 9    "Advise org-capture-finalize to close the frame if it is the capture frame"
10    (if (equal "capture" (frame-parameter nil 'name))
11        (delete-frame)))
12
13  (defadvice org-capture-destroy (after delete-capture-frame activate)
14    "Advise org-capture-destroy to close the frame if it is the capture frame"
15    (if (equal "capture" (frame-parameter nil 'name))
16        (delete-frame)))
17
18  (add-hook 'org-capture-mode-hook 'delete-other-windows)
19  (add-hook 'org-capture-mode-hook '(lambda () (setq mode-line-format nil)))

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

 1  (defun mrb/capture-todo ()
 2    "Capture a TODO item"
 3    (interactive)
 4    (mrb/make-capture-frame)
 5    (org-capture nil "t")
 6    )
 7
 8  (defun mrb/capture-buy ()
 9    "Capture a BUY item"
10    (interactive)
11    (mrb/make-capture-frame)
12    (org-capture nil "b")
13    )
14  (defun mrb/make-journal-entry ()
15    "Create a journal entry"
16    (interactive)
17    (setq org-descriptive-links t)
18    (org-capture nil "j")
19  )

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 Preparing for lift-off

1emacsclient --eval '(mrb/capture-todo)'
1emacsclient --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.

1  (defun mrb/add-tags-in-capture()
2    (interactive)
3    "Insert tags in a capture window without losing the point"
4    (save-excursion
5      (org-back-to-heading)
6      (org-set-tags)))
7
8  (bind-key "C-c C-t" 'mrb/add-tags-in-capture org-capture-mode-map)

Capturing dents for status updates in statusnet are done the same. Their config is in the section Statusnet

Capturing of screenshots is provided by the org-screenshot library.

 1  (use-package org-screenshot)
 2
 3  (defun mrb/capture-screenshot()
 4    "Capture a screenshot"
 5    (interactive)
 6    ;; Always create the standard attach dir if there is none
 7    (org-attach-dir t)
 8
 9    ;; Don't hide the emacs frame, it's silly
10    (org-screenshot t ""))

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:

 1  (setq org-todo-keyword-faces '(
 2    ("DONE"      . (:foreground "#afd8af"     :weight bold))
 3    ("WAITING"   . (:foreground "dark salmon" :weight bold))
 4    ("CANCELLED" . (:foreground "dim gray"    :weight bold))
 5    ("BUY"       . (:foreground "goldenrod"   :weight bold))
 6    ("HOWTO"     . (:foreground "SkyBlue3"    :weight bold))
 7    ("INFO"      . (:foreground "khaki1"      :weight bold))
 8    ("COLLECT"   . (:foreground "MediumSeaGreen"   :weight bold))
 9    ("SOLVE"     . (:foreground "orange red"    :weight bold))
10  ))

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.

1  (setq org-todo-state-tags-triggers
2        (quote (
3                ('todo ("inactive"))          ; remove inactive tags if moved to any active state
4                ('done ("inactive") ("fork")) ; remove tags from any inactive state
5                ("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.

 1  (use-package org-habit)
 2
 3  ;; I need a modified version of org-is-habit, which takes inheritance
 4  ;; in to account
 5  (defun mrb/org-is-habit-test (&optional pom)
 6    "Is the task at POM or point a habit, taking property
 7  inheritance into account?"
 8    (message "Testing for habit")
 9    (if (org-is-habit-p)
10        (message "Org-direct: Seems to be a habit")
11      (message "Org-direct: Nope, no habit"))
12
13    (if (equalp "habit" (org-entry-get (or pom (point)) "STYLE" t))
14        (message "My test: seems to be a habit")
15      (progn
16        (message "My test: Nope, no habit")
17        (message (org-entry-get (or pom (point)) "STYLE" t))
18        (message "Still here")
19        )))
20
21  (defun mrb/org-is-habit-p (&optional pom)
22    "org-is-habit-p taking property inheritance into account"
23    (equalp "habit" (org-entry-get (or pom (point)) "STYLE" t)))
24
25  (defun mrb/archive-done-item()
26    ;; Determine if the item went to the DONE/CANCELLED state
27    ;; if so, ask to archive it, but skip habits which have
28    ;; their own logic.
29    (when (not (mrb/org-is-habit-p))
30      ;; No habit, so we have a candidate
31      (progn
32       ;; Try to use a dialog box to ask for confirmation
33       (setq last-nonmenu-event nil)
34
35       ;; When a note is going to be added, postpone that Otherwise just
36       ;; run the archiving question
37       ;; FIXME: org-add-note runs through post-command-hook,
38       ;;        which is kinda weird, how to i get it to run
39       ;;        before the archiving question?
40       (when (equal org-state "DONE")
41         (org-archive-subtree-default-with-confirmation)))))
42
43
44  ;; Run archive for the item that changed state
45  (add-hook 'org-after-todo-state-change-hook
46            '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:

 1  (bind-key "s-." 'org-todo org-mode-map)
 2
 3  (defun mrb/force-org-todo()
 4    (interactive)
 5    ;; Disable blocking temporarily
 6    (let ((org-inhibit-blocking t))
 7      (org-todo)))
 8
 9  (defun mrb/force-org-agenda-todo()
10    (interactive)
11    ;; Disable blocking temporily
12    (let ((org-inhibit-blocking t))
13      (org-agenda-todo)))
14
15  (bind-key "C-s-." 'mrb/force-org-todo org-mode-map)
16  (bind-key "C-s-." 'mrb/force-org-agenda-todo org-agenda-mode-map)
17  (bind-key "s-."   'org-agenda-todo org-agenda-mode-map)

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-expirty 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.

1  (use-package org-expiry)
2
3  (setq org-expiry-created-property-name "CREATED")
4  (setq 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.

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

Now that function is used to insert the proprty 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)

 1  (defadvice org-insert-todo-heading (after mrb/created-timestamp-advice activate)
 2    "Insert a CREATED property using org-expiry.el for TODO entries"
 3    (mrb/insert-created-timestamp)
 4  )
 5  (ad-activate 'org-insert-todo-heading)
 6
 7  (use-package org-capture)
 8  (defadvice org-capture (after mrb/created-timestamp-advice activate)
 9    "Insert a CREATED property using org-expiry.el for TODO entries"
10    (when (member (org-get-todo-state) org-todo-keywords-1)
11      (mrb/insert-created-timestamp)))
12  (ad-activate 'org-capture)

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"

 1  (defun mrb/org-schedule-for-today()
 2    "Schedule the current item for today"
 3    (interactive)
 4    (org-schedule nil
 5                  (format-time-string "%Y-%m-%d")))
 6  (bind-key "C-." 'mrb/org-schedule-for-today org-mode-map)
 7
 8  (defun mrb/org-agenda-schedule-for-today()
 9    "Schedule the current item in the agenda for today"
10    (interactive)
11    (org-agenda-schedule nil
12                         (format-time-string "%Y-%m-%d")))
13  (bind-key "C-." 'mrb/org-agenda-schedule-for-today org-agenda-mode-map)

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.

1(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.

1(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.

1(setq org-hide-emphasis-markers t)

A similar thing can be done 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.

1(setq org-pretty-entities 1)
2(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

1  (setq org-descriptive-links nil)

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.

1(setq org-src-fontify-natively t)

A couple of settings to steer the column in which the tags and habits appear in the frame:

1(setq
2   org-tags-column -110
3   org-agenda-tags-column -110
4   org-habit-graph-column 100
5)

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.

 1  (setq org-agenda-category-icon-alist
 2        '(
 3          ("Afspraak"      "~/dat/org/images/stock_new-meeting.png" nil nil :ascent center)
 4          ("Blogging"      "~/dat/org/images/edit.png" nil nil :ascent center)
 5          ("Cobra"         "~/dat/org/images/car.png" nil nil :ascent center)
 6          ("DVD"           "~/dat/org/images/media-cdrom.png" nil nil :ascent center)
 7          ("Emacs"         "~/dat/org/images/emacs.png" nil nil :ascent center)
 8          ("Finance"       "~/dat/org/images/finance.png" nil nil :ascent center)
 9          ("Habitat"       "~/dat/org/images/house.png" nil nil :ascent center)
10          ("Habit"         "~/dat/org/images/stock_task-recurring.png" nil nil :ascent center)
11          ("Hobbies"       "~/dat/org/images/hobbies.png" nil nil :ascent center)
12          ("Partners"      "~/dat/org/images/partners.png" nil nil :ascent center)
13          ("Personal"      "~/dat/org/images/personal.png" nil nil :ascent center)
14          ("Task"          "~/dat/org/images/stock_todo.png" nil nil :ascent center)
15          ("Org"           "~/dat/org/images/org-mode-unicorn.png" nil nil :ascent center)
16          ("Statusnet"     "~/dat/org/images/statusnet.png" nil nil :ascent center)
17          ("Systeem"       "~/dat/org/images/systeembeheer.png" nil nil :ascent center)
18          ("Wordpress"     "~/dat/org/images/wordpress.png" nil nil :ascent center)
19  ))

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.

1  (setq
2      org-agenda-dim-blocked-tasks t
3      org-agenda-skip-deadline-if-done t
4      org-agenda-skip-scheduled-if-done t
5      org-agenda-skip-archived-trees nil
6  )

Agenda customization

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

 1  (use-package keywiz)
 2  (use-package cl)
 3
 4  ;; Helper function to build a set of functions and their
 5  (defun mrb/load-keybindings ()
 6    "Since we don't want to have to pass through a keywiz game each time..."
 7    (setq keywiz-cached-commands nil)
 8    (do-all-symbols (sym)
 9      (when (and (commandp sym)
10                 (not (memq sym '(self-insert-command
11                                  digit-argument undefined))))
12        (let ((keys (apply 'nconc (mapcar
13                                   (lambda (key)
14                                     (when (keywiz-key-press-event-p key)
15                                       (list key)))
16                                   (where-is-internal sym)))))
17          ;;  Politically incorrect, but clearer version of the above:
18          ;;    (let ((keys (delete-if-not 'keywiz-key-press-event-p
19          ;;                               (where-is-internal sym))))
20          (and keys
21               (push (list sym keys) keywiz-cached-commands))))))
22
23  ;; Load em up
24  (mrb/load-keybindings)
25
26  ;; Might be good to use this in org-agenda...
27  (defun mrb/random-keybinding ()
28    "Describe a random keybinding."
29    (interactive)
30    (let* ((command (keywiz-random keywiz-cached-commands))
31           (doc (and command (documentation (first command)))))
32        (if command
33            (concat (symbol-name (first command)) " "
34                    "(" (mapconcat 'key-description (cadr command) ", ") ")"
35                    (if doc
36                        (concat ": " (substring doc 0 (string-match "\n" doc)))
37                      ""))
38          "")))
39
40
41  ;; Call this function in a multipart agenda command.
42  (defun mrb/org-agenda-show-tip (arg)
43    (interactive)
44    (let ((inhibit-read-only t))
45      (insert "A random keybinding for inspiration:\n  ")
46      (insert (mrb/random-keybinding) "\n")))
47
48  ;; Show properties in agenda view
49  (use-package org-agenda-property)
50  (setq org-agenda-property-list '("LOCATION" "Responsible"))

Babel / Literate programming

Specific settings for babel and literate programming within org-mode

 1  (setq
 2     org-babel-interpreters (quote ("emacs-lisp" "python" "ditaa" "sql" "sh" "R" "haskell" "js" "calc" "mathomatic"))
 3  )
 4  ;; Activate Babel languages
 5  (use-package ob-gnuplot)
 6  (org-babel-do-load-languages
 7   'org-babel-load-languages
 8   '( (ditaa . t) (sql . t) (sh . t) (emacs-lisp t) (lisp t)
 9     (css t) (awk t) (js t) (lisp t) (org t) (plantuml t) (gnuplot . t)
10     (haskell t) (js t) (calc t) (mathomatic t)))

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.

1(setq
2   org-reverse-note-order nil    ; File at the bottom of an entry
3   org-refile-allow-creating-parent-nodes (quote confirm)
4   org-refile-targets (quote ((org-agenda-files :maxlevel . 10 )))
5   org-refile-use-outline-path 'file
6)

Exporting to other formats

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

 1  ;; {% raw %}
 2  (setq
 3   org-export-latex-hyperref-format "\\ref{%s}:{%s}"
 4   ;; old system
 5   org-export-latex-title-command " "
 6   ;; new system > 8.0
 7   org-latex-title-command " "
 8
 9   org-export-docbook-xsl-fo-proc-command "fop %i %o"
10   org-export-docbook-xslt-proc-command "xsltproc --output %o %s %i"
11   org-export-htmlize-output-type (quote css)
12   org-export-htmlized-org-css-url "orgmode.css"
13   org-latex-pdf-process
14   (quote
15    ("pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
16     "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
17     "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"))
18   org-latex-to-pdf-process
19   (quote
20    ("pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
21     "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
22     "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"))
23
24    org-latex-listings (quote minted)
25    ;; Define a default background color name, this needs to be set through
26    ;; a latex header
27    org-latex-minted-options (quote (("bgcolor" "codebg")))
28
29   org-export-copy-to-kill-ring (quote if-interactive)
30   org-export-docbook-xsl-fo-proc-command "fop %i %o"
31   org-export-docbook-xslt-proc-command "xsltproc --output %o %s %i"
32   org-export-htmlize-output-type (quote css)
33   org-export-htmlized-org-css-url "orgmode.css"
34   org-export-latex-classes
35   (quote (
36           ("article" "\\documentclass[11pt,a4paper,twoside]{article}"
37            ("\\section{%s}" . "\\section*{%s}")
38            ("\\subsection{%s}" . "\\subsection*{%s}")
39            ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
40            ("\\paragraph{%s}" . "\\paragraph*{%s}")
41            ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))
42           ("report" "\\documentclass[11pt]{report}"
43            ("\\part{%s}" . "\\part*{%s}")
44            ("\\chapter{%s}" . "\\chapter*{%s}")
45            ("\\section{%s}" . "\\section*{%s}")
46            ("\\subsection{%s}" . "\\subsection*{%s}")
47            ("\\subsubsection{%s}" . "\\subsubsection*{%s}"))
48           ("book" "\\documentclass[11pt]{book}"
49            ("\\part{%s}" . "\\part*{%s}")
50            ("\\chapter{%s}" . "\\chapter*{%s}")
51            ("\\section{%s}" . "\\section*{%s}")
52            ("\\subsection{%s}" . "\\subsection*{%s}")
53            ("\\subsubsection{%s}" . "\\subsubsection*{%s}"))
54           ("beamer" "\\documentclass{beamer}" org-beamer-sectioning)))
55   org-export-latex-hyperref-format "\\ref{%s}:{%s}"
56   org-latex-title-command " "
57   org-export-latex-title-command " "
58   org-export-with-tags nil
59   org-export-with-todo-keywords nil
60
61  )
62  ;; {% endraw %}
 1  ; Exporting and Publishing related settings
 2
 3  ; Apart from the normal export menu, I need something that exports
 4  ; just the body, so the resulting html is suitable to be used inside a
 5  ; blogging system like wordpress for example
 6
 7  ; This puts the body inside a buffer called  blog-entry
 8  (defun mrb/org-export-body-as-html ()
 9    (interactive)
10    (org-export-as-html 3 nil nil "blog-entry" t))
11
12  ; Make sure we export in css mode, meaning no inline css crap
13  (setq org-export-htmlize-output-type 'css)
14  (bind-key "C-c C-e" 'mrb/org-export-body-as-html org-mode-map)
15
16  (defun mrb/org-export-body-as-html-batch ()
17    "Call `org-export-as-html', may be used in batch processing as
18  emacs   --batch
19          --load=$$HOME/lib/emacs/org.el
20          --eval \"(setq org-export-headline-levels 2)\"
21          --visit=MyFile --funcall org-export-body as-html-batch"
22    (interactive)
23    (org-export-as-html 3 nil nil nil t))

Blogging with org-mode

Having most of my writings in org-mode it makes sense to add just a little bit more to be able to push that content to my blogging platform; wordpress. Fortunately, there's some elisp for that in the form of org2blog, which publishes orgmode files as wordpress articles or pages.

Org2Blog configuration for Wordpress

I use my own repositories mostly

1  (use-package org2blog-autoloads)

The configuration of org2blog is a matter of setting some variables. First let get some of the basics out of the way.

1  (setq
2   org2blog/wp-server-weblog-id ""
3   org2blog/wp-default-title "<Untitled>"
4   org2blog/wp-default-categories ""
5   org2blog/wp-confirm-post t)

Each blog-entry has it's own file, but is really nothing different from a normal org-mode file. To be able to have wordpress and orgmode work together, org2blog could insert some properties into the file, but I choose to have these in a so called tracking-file which is a convenient outline of all blog entries. Bij making the header a link to the file, it makes a convenient system.

1  (setq
2   org2blog/wp-track-posts (list "~/dat/blogs/blogs.org" "To be filed properly")
3  )

This leaves use with the blogs to configure; the name, url and username are probably the required entries for each blog. I added the tracker file, which is the same as the global value. Authentication to the blogs is done interactively by default.

 1  (setq org2blog/wp-blog-alist
 2        '(("cobra"
 3           :url "http://cobra.mrblog.nl/xmlrpc.php"
 4           :username "mrb"
 5           :track-posts ("~/dat/blogs/blogs.org" "cobra.mrblog.nl")
 6           )
 7          ("hsd"
 8           :url "http://test.hsdev.com/xmlrpc.php"
 9           :username "mrb"
10           :track-posts ("~/dat/blogs/blogs.org" "hsdev.com")
11           )))
Using org-mode for Jekyll based sites

Using jekyll and orgmode together is a bit more low level, because jekyll as such is not a blogging solution, it can just parse files from one format to html.

As there are quite a few different setups possible, it's probably smart to write a few paragraphs on how to set things up.

As usual, I want it to be as simple as possible at first. One thing that is sort of fixed is the directory structure that jekyll expects and I that as a starting point. Typically there is a _posts subdirectory there which will hold the sourse files that jekyll will need to convert into content for the website.

Typically in org-mode, after writing a subtree, which is one blog post in this case, a call to org-jekyll-export-current-entry is called. This will try to deduce which project org-mode must use to publish the entry, based on the filename, i.e. is the file in or below the ':base-directory' of one the publishing projects and has the proper extension and so on.

I chose to simplify this and use the org-mode files directly as source files in the jekyll directory structure and use org-ruby to convert the files with a jekyll plugin. This dramatically simplifies the process.

To support this process, I'm using the jekyll-el package as a base. Given the unmodified package, just setting the variables defined in there gets it configured for my situation:

 1  (use-package jekyll
 2    :config
 3    (progn
 4      (setq jekyll-directory "~/dat/blogs/mrblog/sites/main/")
 5      (setq jekyll-post-ext ".org")
 6
 7      (defun file-string (file)
 8        "Read the contents of a file and return as a string."
 9        (with-temp-buffer
10          (insert-file-contents file)
11          (buffer-string)))
12
13      (setq jekyll-post-template
14            (file-string
15             (concat jekyll-directory jekyll-posts-dir "_posttemplate.org")))))

The only special thing here is that I save the template in a file, so it's easier to change. On a call to jekyll-draft-post a title is asked and a buffer is filled with this title properly filled in, plus the filename is attached to the the buffer in the proper location. A simple save creates the draft as a file.

Old configuration

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

  1  (setq
  2   org-use-fast-todo-selection t
  3
  4   ; We support task dependencies
  5   org-enforce-todo-dependencies t
  6   ; but relax checkbox constraints
  7   org-enforce-todo-checkbox-dependencies nil
  8
  9   ; We dont do priorities
 10   org-enable-priority-commands nil
 11
 12   ; Agenda settings
 13   org-agenda-include-diary t
 14   org-agenda-start-with-log-mode t
 15   org-agenda-todo-ignore-scheduled "future"
 16
 17   ; Habits
 18   org-habit-show-habits-only-for-today nil
 19
 20   ; Pressing enter on a link should activate it
 21   org-return-follows-link t
 22   org-support-shift-select (quote always)
 23
 24   org-agenda-log-mode-items (quote (closed clock state))
 25   org-agenda-skip-deadline-prewarning-if-scheduled t
 26   org-blank-before-new-entry (quote ((heading) (plain-list-item)))
 27   org-export-htmlize-output-type (quote css)
 28   org-fast-tag-selection-single-key (quote expert)
 29   org-file-apps
 30   (quote
 31    ((auto-mode . emacs)
 32    ("\\.dia\\'" . "dia %s")
 33    ("\\.mm\\'" . default)
 34    ("\\.pdf\\'" . default)))
 35   org-fontify-done-headline t
 36   org-goto-interface (quote outline-path-completion)
 37   ;; non nil is just direct children, what an ODD name!!!!
 38   org-hierarchical-todo-statistics nil
 39   org-provide-todo-statistics t
 40   org-log-into-drawer t
 41   org-log-redeadline (quote note)
 42   org-log-reschedule (quote time)
 43   org-modules (quote
 44                (org-info org-jsinfo org-habit
 45                          org-inlinetask org-irc
 46                          org-toc org-mac-iCal org-mouse))
 47   org-remember-default-headline ""
 48   org-special-ctrl-a/e t
 49   org-stuck-projects (quote ("-inactive/TODO" ("TODO" "WAITING") nil ""))
 50   org-track-ordered-property-with-tag nil
 51  )
 52
 53  ;; Bit of a leftover from reorganising bits, do this later
 54  (add-to-list 'org-tags-exclude-from-inheritance (quote "fix"))
 55  (add-to-list 'org-tags-exclude-from-inheritance (quote "sell"))
 56  (add-to-list 'org-tags-exclude-from-inheritance (quote "build"))
 57
 58
 59  ; Keybindings which only make sense when having an orgmode file
 60  (bind-key "C-c t" 'org-set-tags org-mode-map)
 61  (bind-key "C-c e" 'org-export-dispatch org-mode-map)
 62
 63  ; Map \cmd{}t to schedule in both task and agenda-view
 64  (bind-key "s-t" 'org-schedule org-mode-map)
 65  (bind-key "s-t" 'org-agenda-schedule org-agenda-mode-map)
 66  (bind-key "M-p" 'org-set-property org-mode-map)
 67  (bind-key "M-p" 'org-set-property org-agenda-mode-map)
 68  (bind-key "C-s-s" 'org-save-all-org-buffers org-mode-map)
 69
 70  ; Dynamic behaviour
 71  (defun mrb/gtd()
 72    "Start my GTD system"
 73    (interactive)
 74    (find-file org-default-notes-file)
 75  )
 76
 77
 78  (defun mrb/is-project-p ()
 79    "This function returns true if the entry is considered a project.
 80     A project is defined to be:
 81     - having a TODO keyword itself (why was this again?);
 82     - having at least one todo entry, regardless of their state."
 83    (let ((has-todokeyword)
 84          (has-subtask)
 85          (subtree-end (save-excursion (org-end-of-subtree t)))
 86          (is-a-task (member (nth 2 (org-heading-components)) org-todo-keywords-1)))
 87      (save-excursion
 88        (forward-line 1)
 89        (while (and (not has-subtask)
 90                    (< (point) subtree-end)
 91                    (re-search-forward "^\*+ " subtree-end t))
 92          (when (member (org-get-todo-state) org-todo-keywords-1)
 93            (setq has-subtask t))))
 94      ;; both subtasks and a keyword on the container need to be present.
 95      (and is-a-task has-subtask)
 96      )
 97    )
 98
 99  ; FIXME: testing for tag presence should be easier than a re-search forward
100  ; FIXME: are we not searching for all 'incomplete' type keywords here?,
101  ;        there must be an org function for that
102  (defun mrb/skip-non-stuck-projects ()
103    "Skip trees that are not stuck projects"
104    (let* ((subtree-end (save-excursion (org-end-of-subtree t)))
105           (has-next (save-excursion
106                       (forward-line 1)
107                       (and (< (point) subtree-end)
108                            (re-search-forward "^*+ \\(TODO\\|BUY\\|WAITING\\)" subtree-end t)))))
109      (if (and (mrb/is-project-p) (not has-next))
110          nil ; a stuck project, has subtasks but no next task
111        subtree-end)))
112
113  (defun mrb/skip-non-projects ()
114    "Skip trees that are not projects"
115    (let* ((subtree-end (save-excursion (org-end-of-subtree t))))
116      (if (mrb/is-project-p)
117          nil
118        subtree-end)))
119
120  (defun mrb/skip-projects ()
121    "Skip trees that are projects"
122    (let* ((subtree-end (save-excursion (org-end-of-subtree t))))
123      (if (mrb/is-project-p)
124          subtree-end
125        nil)))
126
127  (defun mrb/save-containing-org-file()
128    ;; FIXME: this is a bit over the top, especially as I am auto
129    ;; commiting to a git repository.
130    (org-save-all-org-buffers)
131  )
132  (add-hook 'org-after-todo-state-change-hook 'mrb/save-containing-org-file)
133
134  ; When in agenda mode, show the line we're working on.
135  (add-hook 'org-agenda-mode-hook '(lambda () (hl-line-mode 1)))
136
137  ; When a tag change adds the waiting tag, make sure it gets scheduled
138  ; 1 week from now if it is not already.
139  (defun mrb/autoschedule-waiting()
140    ; variable 'tags' contains the values of the tag-string
141    ; If tags has the tag :waiting:, schedule this
142    ;(if includes tags "waiting")
143    (message "Running my own hook")
144    ;(message tags)
145    (org-schedule nil (org-timestring-to-seconds "+1w"))
146  )
147  ; Activate it
148  ;(add-hook 'org-after-tags-change-hook 'mrb/autoschedule-waiting)
149
150
151
152  ; Remove empty property drawers
153  (defun mrb/org-remove-empty-propert-drawers ()
154    "*Remove all empty property drawers in current file."
155    (interactive)
156    (unless (eq major-mode 'org-mode)
157      (error "You need to turn on Org mode for this function."))
158    (save-excursion
159      (goto-char (point-min))
160      (while (re-search-forward ":PROPERTIES:" nil t)
161        (save-excursion
162          (org-remove-empty-drawer-at "PROPERTIES" (match-beginning 0))))))
163
164  (defun mrb/org-remove-redundant-tags ()
165    "Remove redundant tags of headlines in current buffer.
166
167  A tag is considered redundant if it is local to a headline and
168  inherited by a parent headline."
169    (interactive)
170    (when (eq major-mode 'org-mode)
171      (save-excursion
172        (org-map-entries
173         '(lambda ()
174            (let ((alltags (split-string (or (org-entry-get (point) "ALLTAGS") "") ":"))
175                  local inherited tag)
176              (dolist (tag alltags)
177                (if (get-text-property 0 'inherited tag)
178                    (push tag inherited) (push tag local)))
179              (dolist (tag local)
180                (if (member tag inherited) (org-toggle-tag tag 'off)))))
181         t nil))))
182
183
184  (defvar org-agenda-group-by-property nil
185    "Set this in org-mode agenda views to group tasks by property")
186
187  (defun mrb/org-group-bucket-items (prop items)
188    (let ((buckets ()))
189      (dolist (item items)
190        (let* ((marker (get-text-property 0 'org-marker item))
191               (pvalue (org-entry-get marker prop t))
192               (cell (assoc pvalue buckets)))
193          (if cell
194              (setcdr cell (cons item (rest cell)))
195            (setq buckets (cons (cons pvalue (list item))
196                                buckets)))))
197      (setq buckets (mapcar (lambda (bucket)
198                              (cons (first bucket)
199                                    (reverse (rest bucket))))
200                            buckets))
201      (sort buckets (lambda (i1 i2)
202                      (string< (first i1) (first i2))))))
203
204  (defadvice org-agenda-finalize-entries (around org-group-agenda-finalize
205                                                 (list &optional nosort))
206    "Prepare bucketed agenda entry lists"
207    (if org-agenda-group-by-property
208        ;; bucketed, handle appropriately
209        (let ((text ""))
210          (dolist (bucket (mrb/org-group-bucket-items
211                           org-agenda-group-by-property
212                           list))
213            (let ((header (concat "Property "
214                                  org-agenda-group-by-property
215                                  " is "
216                                  (or (first bucket) "<nil>") ":\n")))
217              (add-text-properties 0 (1- (length header))
218                                   (list 'face 'org-agenda-structure)
219                                   header)
220              (setq text
221                    (concat text header
222                            ;; recursively process
223                            (let ((org-agenda-group-by-property nil))
224                              (org-agenda-finalize-entries
225                               (rest bucket) nosort))
226                            "\n\n"))))
227          (setq ad-return-value text))
228      ad-do-it))
229  (ad-activate 'org-agenda-finalize-entries)
230
231
232  ;; Shorten url at point
233  ;; This is a stripped down version of the code in identica-mode
234  (defun mrb/ur1ca-get (api longurl)
235    "Shortens url through ur1.ca free service 'as in freedom'"
236    (let* ((url-request-method "POST")
237          (url-request-extra-headers
238           '(("Content-Type" . "application/x-www-form-urlencoded")))
239          (url-request-data (concat "longurl=" (url-hexify-string longurl)))
240          (buffer (url-retrieve-synchronously api)))
241      (with-current-buffer buffer
242        (goto-char (point-min))
243        (prog1
244            (if (search-forward-regexp "Your .* is: .*>\\(http://ur1.ca/[0-9A-Za-z].*\\)</a>" nil t)
245                (match-string-no-properties 1)
246              (error "URL shortening service failed: %s" longurl))
247        (kill-buffer buffer)))))
248
249  (defun mrb/shortenurl-replace-at-point ()
250    "Replace the url at point with a tiny version."
251    (interactive)
252    (let ((url-bounds (bounds-of-thing-at-point 'url)))
253      (when url-bounds
254        (let ((url (mrb/ur1ca-get "http://ur1.ca" (thing-at-point 'url))))
255          (when url
256            (save-restriction
257              (narrow-to-region (first url-bounds) (rest url-bounds))
258              (delete-region (point-min) (point-max))
259              (insert url)))))))
260
261
262  (defvar mrb/org-my-archive-expiry-days 365
263    "The number of days after which a completed task should be auto-archived.
264  This can be 0 for immediate, or a floating point value.")
265
266  (defun mrb/org-my-archive-done-tasks ()
267    (interactive)
268    (save-excursion
269      (goto-char (point-min))
270      (let ((done-regexp
271             (concat "\\* \\(" (regexp-opt org-done-keywords) "\\) "))
272            (state-regexp
273             (concat "- State \"\\(" (regexp-opt org-done-keywords)
274                     "\\)\"\\s-*\\[\\([^]\n]+\\)\\]")))
275        (while (re-search-forward done-regexp nil t)
276          (let ((end (save-excursion
277                       (outline-next-heading)
278                       (point)))
279                begin)
280            (goto-char (line-beginning-position))
281            (setq begin (point))
282            (when (re-search-forward state-regexp end t)
283              (let* ((time-string (match-string 2))
284                     (when-closed (org-parse-time-string time-string)))
285                (if (>= (time-to-number-of-days
286                         (time-subtract (current-time)
287                                        (apply #'encode-time when-closed)))
288                        mrb/org-my-archive-expiry-days)
289                    (org-archive-subtree)))))))))
290
291  (defalias 'archive-done-tasks 'mrb/org-my-archive-done-tasks)
292
293  ;; Map it to Ctrl-C S in orgmode (consider a global key assignment?
294  (bind-key "C-c s" 'mrb/shortenurl-replace-at-point org-mode-map)
295  ;; END shorten url functionality
296
297  ;;(add-to-list 'load-path "~/dat/src/emacs/packages/org-bom/")
298  ;;(use-package org-bom)
299
300  ;; archive entries into a date-tree
301  ;; (setq org-archive-location "%s_archive::date-tree")
302  (defadvice org-archive-subtree
303    (around org-archive-subtree-to-data-tree activate)
304    "org-archive-subtree to date-tree"
305    (if
306        (string= "date-tree"
307                 (org-extract-archive-heading
308                  (org-get-local-archive-location)))
309        (let* ((dct (decode-time (org-current-time)))
310               (y (nth 5 dct))
311               (m (nth 4 dct))
312               (d (nth 3 dct))
313               (this-buffer (current-buffer))
314               (location (org-get-local-archive-location))
315               (afile (org-extract-archive-file location))
316               (org-archive-location
317                (format "%s::*** %04d-%02d-%02d %s" afile y m d
318                        (format-time-string "%A" (encode-time 0 0 0 d m y)))))
319          (message "afile=%s" afile)
320          (unless afile
321            (error "Invalid `org-archive-location'"))
322          (save-excursion
323            (switch-to-buffer (find-file-noselect afile))
324            (org-datetree-find-year-create y)
325            (org-datetree-find-month-create y m)
326            (org-datetree-find-day-create y m d)
327            (widen)
328            (switch-to-buffer this-buffer))
329          ad-do-it)
330      ad-do-it))
331
332  ;;
333  ;; Org-mobile configuration
334  (setq
335   ; Remote org dir
336   ;;org-mobile-directory "/plato.hsdev.com:/home/mrb/data/mobileorg"
337   ; Where to place items which need resolving
338   org-mobile-inbox-for-pull (concat org-metadir "from-mobile.org")
339   ; No id yet, don't see the advantage yet
340   org-mobile-force-id-on-agenda-items nil
341   ; No encryption (yet)
342   org-mobile-use-encryption nil
343   org-mobile-agendas (quote all)
344   org-mobile-files
345   (quote (
346           "~/dat/src/GTD.org"
347           "~/dat/src/habits.org"
348           "~/dat/src/_calendars/meetings.org"))
349   org-mobile-use-encryption nil
350  )
351
352  ;; Define timer variables for pull and push operations
353  (defvar org-mobile-push-timer nil)
354  (defvar org-mobile-pull-timer nil)
355
356  ;; Define notificaters
357  (use-package notifications)
358
359  (defun mrb/org-mobile-notify (type result)
360    (notifications-notify
361     :title (concat type " complete:")
362     :body  (format (concat "Org-mobile-" type ": %s") result)))
363
364  (defun mrb/notify-push (result) (mrb/org-mobile-notify "Push" result))
365  (defun mrb/notify-pull (result) (mrb/org-mobile-notify "Pull" result))
366
367  ;; Fork the work of pushing to mobile
368  (defun mrb/fork-org-mobile-push()
369    (async-start
370     ;; What to do in the child process
371     `(lambda ()
372        ,(async-inject-variables "org-\\(mobile-\\|directory\\)")
373        (org-mobile-push))
374
375     ; What to do when it finishes
376     (lambda (result)
377       (mrb/notify-push result)
378       (message "Push of mobile org complete"))))
379
380  ;; Push to mobile when the idle timer runs out
381  (defun mrb/org-mobile-push-with-delay (secs)
382    (when org-mobile-push-timer
383      (cancel-timer org-mobile-push-timer))
384    (setq org-mobile-push-timer
385          (run-with-idle-timer
386           (* 1 secs) nil 'mrb/fork-org-mobile-push)))
387
388  ;; After saving files, start a 30 seconds idle timer after which we
389  ;; are going to push
390  ;; (add-hook
391  ;;  'after-save-hook
392  ;;  (lambda ()
393  ;;    (when (eq major-mode 'org-mode)
394  ;;      (dolist (file (org-mobile-files-alist))
395  ;;        (if (string= (expand-file-name (first file)) (buffer-file-name))
396  ;;         (mrb/org-mobile-push-with-delay 30))))))
397
398  ;; Fork the work of pushing to mobile
399  (defun mrb/fork-org-mobile-pull ()
400    (async-start
401     ;; What to do in the child process
402     `(lambda ()
403        ,(async-inject-variables "org-\\(mobile-\\|directory\\)")
404        (org-mobile-pull))
405
406     ; What to do when it finishes
407     (lambda (result)
408       (mrb/notify-pull result)
409       (message "Pull of mobile org complete"))))
410
411  ;; Construct the name of the remote file
412  (setq remote-org-mobile-file
413        (file-truename
414         (concat
415          (file-name-as-directory org-mobile-directory)
416          "mobileorg.org")))
417
418  ;; Pull by monitoring the file mobile-org writes to
419  (defun mrb/install-monitor (file secs)
420    ;; Cancel an existing timer, if any
421    (when org-mobile-pull-timer
422      (cancel-timer org-mobile-pull-timer))
423    ;; And set up a new one
424    (setq org-mobile-pull-timer
425          (run-with-timer
426           0 secs
427           (lambda (f p)
428             ;; If the remote file has been changed within out repeat
429             ;; period, we need a new copy
430             (unless (< p (second (time-since (elt (file-attributes f) 5))))
431               (mrb/fork-org-mobile-pull)))
432           file
433           secs)))
434
435  ;; Install a monitor on the remote org file. Don't make the time too
436  ;; short, otherwise the file might nog get pulled in.
437  ;; (mrb/install-monitor remote-org-mobile-file 30)
438
439  ;; Mail facilities related to org-mode
440  (use-package org-mime)
441
442  ;; Small bit to mail an org subtree
443  (defun mrb/mail-subtree-from-org ()
444    (interactive)
445    (if (equal major-mode 'org-agenda-mode)
446        (org-agenda-goto)
447      )
448    ;;TODO: make this also use the mailcompose frame
449    (org-mime-subtree))
450
451  ;; The entry point
452  (defun mrb/construct-mail (useOrgTree)
453    (interactive "P")
454    (if useOrgTree
455        (mrb/mail-subtree-from-org)
456      (mrb/make-mailcompose-frame)))
457  ;; Bind it to \C-xm in org-mode only
458  (bind-key "C-x m" 'mrb/construct-mail org-mode-map)
459  (bind-key "C-x m" 'mrb/construct-mail org-agenda-mode-map)
460  ;; But not for all others, because the org subtree will never be there
461  (bind-key "C-x m" 'mrb/make-mailcompose-frame)
462
463
464  (use-package stripe-buffer)
465  (defun mrb/enable-org-table-striping ()
466    (interactive)
467    (stripe-org-tables-enable))
468  ;; TODO I want to have this in the agenda mode to

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.

1(use-package org-crypt)
2(org-crypt-use-before-save-magic)
3(setq org-crypt-tag-matcher "encrypt")
4(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.

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

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)

OpenSCAD

OpenSCAD mode, it’s a mode for files that describe 3D-models. I use it for part design for the 3D-print and for the lathe.

1  ; OpenSCAD has git repository in my dev tree
2  ;; Find a way to get this with el-get, say from github
3  ;; It is part of the openscad package and in the contrib folder
4  ;; there. Until that time, we're disabling
5  ;; (add-to-list 'load-path "~/dat/src/reprapping/openscad/contrib")
6  ;; (load "scad-mode")
7
8  ;;(provide 'openscad)

Python

Python is a indent aware language, wich saves quite a bit of fiddling around with parenthes and what have you.

Having lines truncated is the preferred way in python, usually lines will be short enough anyway, but I can't have line automatically folding because that disturbs the way I think when looking at python code.

1  (add-hook 'python-mode-hook
2            (lambda ()
3              (toggle-truncate-lines t)))

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.

1    (use-package haskell-mode
2
3      (add-to-list 'auto-mode-alist '("\\.hs\\'". haskell-mode))
4      (add-hook 'haskell-mode-hook 'interactive-haskell-mode)
5      (add-hook 'haskell-mode-hook 'turn-on-haskell-doc-mode)
6      (add-hook 'haskell-mode-hook 'turn-on-haskell-indent)
7      )

LDAP

LDAP integration, mostly for address book lookups

 1  ; LDAP integration
 2
 3  (setq ldap-host-parameters-alist
 4        (quote (("ldap.hsdev.com" base "ou=addressbook,dc=hsdev,dc=com"))))
 5
 6  (use-package ldap)
 7  (use-package eudc)
 8
 9  (setq eudc-default-return-attributes nil
10        eudc-strict-return-matches nil)
11
12  (setq ldap-ldapsearch-args (quote ("-tt" "-LLL" "-x")))
13  (setq eudc-inline-query-format '((name)
14                                   (firstname)
15                                   (firstname name)
16                                   (email)
17                                   ))
18
19
20  (eudc-set-server "ldap.hsdev.com" 'ldap t)
21  (setq eudc-server-hotlist '(("ldap.hsdev.com" . ldap)))
22  (setq eudc-inline-expansion-servers 'hotlist)
23
24  (defun mrb/enz-eudc-expand-inline()
25    (interactive)
26    (move-end-of-line 1)
27    (insert "*")
28    (unless (condition-case nil
29                (eudc-expand-inline)
30              (error nil))
31      (backward-delete-char-untabify 1))
32    )
33
34  ;; Adds some hooks
35
36  ;; (eval-after-load "message"
37  ;;   '(bind-key "TAB" 'mrb/enz-eudc-expand-inline message-mode-map))
38  ;; (eval-after-load "sendmail"
39  ;;   '(bind-key "TAB" 'mrb/enz-eudc-expand-inline mail-mode-map))
40  ;; (eval-after-load "post"
41  ;;   '(bind-key "TAB" 'mrb/enz-eudc-expand-inline post-mode-map))

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:

C -

control

s -

super, meaning the (left) windows key in my configuration

M -

meta, meaning the (left) alt key in my configuration

S -

shift

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:

1  (defun mrb/insert-key-description ()
2    "Insert a pretty printed representation of a key sequence"
3    (interactive)
4    (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 <menu> key should be the same as the left alt and the super key, but I haven't gotten around to configuring that yet.

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.

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

Because I use the spacebar in a bit of a special way; an extra control key, this complicates things a bit. To be able to do this I use xcape, which takes control keys and allows them to insert normal characters when they are not used with other keys. For this, I have defined the space bar in Xmodmap to be a control key with keycode 0x1234. Without xcape, the behaviour would be that there is no more spacebar on the keyboard. The configuration of xcape says: 'when the control key 0x1234 is pressed by itself, insert a space'

The problem is that emacs then still thinks the key is undefined, so we let it do nothing in emacs, just to prevent an annoying message in the echo area each time we push the spacebar.

1;; We want the spacebar itself to do nothing
2(global-set-key [key-4660] 'ignore)
3
4;; Put more cases here when we encounter them

This small change requires quite a bit of tweaking because you don't want this to be in way, ever. The only customization I had to make was the shortening of the timeout from 50ms to 35ms to make it work for me.

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.

1(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.

  1  ; Let marks be set when shift arrowing, everybody does this
  2  ;; FIXME: this does not belong in keybindings, but somwhere global
  3  (setq shift-select-mode t)
  4  (delete-selection-mode 1)
  5
  6  ; Font scaling, like in firefox
  7  (bind-key "C-+" 'text-scale-increase)
  8  (bind-key "C--" 'text-scale-decrease)
  9
 10  ;; Line handling functions
 11  (bind-key "s-`" 'toggle-truncate-lines)
 12
 13  ;; Most of the time I want return to be newline and indent
 14  ;; Every mode can augment this at will obviously (org-mode does, for example)
 15  (bind-key "RET" 'newline-and-indent)
 16
 17  ;; Comment code lines, command reacts based on the major mode.
 18  (bind-key "s-/" 'comment-or-uncomment-region)
 19
 20  (bind-key "s-s" 'save-buffer)
 21
 22  ;; Kill buffer, FIXME: asks for name, which I dont need
 23  (bind-key "s-k" 'ido-kill-buffer)
 24
 25  ;; Resizing windows
 26  ;; Introduce a bit of intelligence so the shrink and enlarge know what window I'm in.
 27  (defun mrb/xor (b1 b2)
 28    "Exclusive or between arguments"
 29    (or (and b1 b2)
 30        (and (not b1) (not b2))))
 31
 32  (defun mrb/move-border-left-or-right (arg dir)
 33    "General function covering move-border-left and move-border-right. If DIR is
 34    t, then move left, otherwise move right."
 35    (interactive)
 36    (if (null arg) (setq arg 5))
 37    (let ((left-edge (nth 0 (window-edges))))
 38      (if (mrb/xor (= left-edge 0) dir)
 39          (shrink-window arg t)
 40        (enlarge-window arg t)))
 41    )
 42
 43  (defun mrb/move-border-left (arg)
 44    (interactive "P")
 45    (mrb/move-border-left-or-right arg t))
 46
 47  (defun mrb/move-border-right (arg)
 48    (interactive "P")
 49    (mrb/move-border-left-or-right arg nil))
 50
 51  ;; Same for up and down
 52  (defun mrb/move-border-up-or-down (arg dir)
 53    "General function covering move-border-up and move-border-down. If DIR is
 54    t, then move up, otherwise move down."
 55    (interactive)
 56    (if (null arg) (setq arg 5))
 57    (let ((top-edge (nth 1 (window-edges))))
 58      (if (mrb/xor (= top-edge 0) dir)
 59          (shrink-window arg nil)
 60        (enlarge-window arg nil))))
 61
 62  (defun mrb/move-border-up (arg)
 63    (interactive "P")
 64    (mrb/move-border-up-or-down arg t))
 65
 66  (defun move-border-down (arg)
 67    (interactive "P")
 68    (mrb/move-border-up-or-down arg nil))
 69
 70
 71  ;; cut, copy and paste with cmd-key (like on osx).
 72  ;; this kinds sucks now, because the rest of the OS does not do this
 73  ;; SOLUTION: learn to work with standard emacs keybinding and adjust the OS  ?
 74  (bind-key "s-z" 'undo)
 75  (bind-key "s-x" 'clipboard-kill-region)
 76  (bind-key "s-c" 'clipboard-kill-ring-save)
 77  (bind-key "s-v" 'yank)
 78  (bind-key "s-a" 'mark-whole-buffer)
 79
 80  ;; Keypad delete
 81  (bind-key [(kp-delete)] 'delete-char)
 82
 83  ;; Make `C-x C-m' and `C-x RET' be different (since I tend
 84  ;; to type the latter by accident sometimes.)
 85  ;; Should this not be an unset?
 86  (bind-key "C-x RET" nil)
 87
 88  (defun mrb/new-empty-buffer ()
 89    "Opens a new empty buffer."
 90    (interactive)
 91    (let ((buf (generate-new-buffer "untitled")))
 92      (switch-to-buffer buf)
 93      (make-frame)
 94      (funcall (and initial-major-mode))
 95      (setq buffer-offer-save t)))
 96  ;; note: emacs won't offer to save a buffer that's
 97  ;; not associated with a file,
 98  ;; even if buffer-modified-p is true.
 99  ;; One work around is to define your own my-kill-buffer function
100  ;; that wraps around kill-buffer, and check on the buffer modification
101  ;; status to offer save
102  ;; This custome kill buffer is close-current-buffer.
103  (bind-key "s-n" 'mrb/new-empty-buffer)

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.

1  (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 like the incremental matching that smex does, so I am going to rebind the M-x keybinding to that, and rebind the original to have C-c as a prefix. Using the shift modifier with the M-x command also runs smex, but limits the commands to make sense for the current major mode only.

1  (bind-key "C-c M-x" 'execute-extended-command)
2  (bind-key "M-x" 'smex)
3  (bind-key "M-X" 'smex-major-mode-commands)

I want to be able to call up magit from anywhere

1  (bind-key "C-c m" 'magit-status)
COMMENT 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.

 1  ;; Menu key does M-x, if we have it.
 2  ;;(bind-key (kbd "<apps>") 'execute-extended-command)
 3  (bind-key "<f1>" 'help-command)
 4  (bind-key "<f2>" 'save-buffer)
 5  (bind-key "<f4>" 'find-file)
 6
 7  ;; Define the toggle-frame-fullscreen function in
 8  ;; the case that it is not already.
 9
10  (if (not (fboundp 'toggle-frame-fullscreen))
11      (defun toggle-frame-fullscreen()
12        (interactive)
13        (when (eq window-system 'x)
14          (set-frame-parameter
15           nil 'fullscreen
16           (when (not (frame-parameter nil 'fullscreen)) 'fullboth)))))
17
18  ;; Make gnome compliant, define a full-screen function and bind to F11
19  (defun mrb/switch-full-screen ()
20    (interactive)
21    (toggle-frame-fullscreen))
22  (bind-key "<f11>" 'mrb/switch-full-screen)
23
24  ;; Not sure what this is? It is not the menu key.
25  (bind-key [XF86MenuKB] 'accelerate-menu)
26  (bind-key [XF86Battery] 'display-battery-mode)
Resizing and switching windows and frames
 1  ;; Moving back and forth in windows For now, I'm using the Fn Key +
 2  ;; Arrows, seems consistent with the other window movements
 3  (bind-key [XF86AudioNext] 'next-multiframe-window)
 4  (bind-key [XF86AudioPrev] 'previous-multiframe-window)
 5
 6  ;; Alt-Cmd left-right arrows browse through buffers within the same frame
 7  (bind-key "<M-s-left>"  'previous-buffer)
 8  (bind-key "<M-s-right>" 'next-buffer)
 9
10  ;; These would conflict with awesome bindings, perhaps we should change those bindings
11  ;; (bind-key "<C-S-left>"  'buf-move-left)
12  ;; (bind-key "<C-S-right>" 'buf-move-right)
13
14  ;; Awesome uses Super+Arrows to move between its 'frames'
15  ;; Emacs   uses Shift-Super+Arrows to move between its windows
16  (bind-key "<S-s-right>" 'windmove-right)
17  (bind-key "<S-s-left>"  'windmove-left)
18  (bind-key "<S-s-up>"    'windmove-up)
19  (bind-key "<S-s-down>"  'windmove-down)

Org-mode

Orgmode specific keybindings.

1  (bind-key "C-c g" 'mrb/gtd)
2  (bind-key "C-c a" 'org-agenda)
3  (bind-key "C-c b" 'org-iswitchb)
4  (bind-key "C-c l" 'org-store-link)
5  (bind-key "C-c j" 'mrb/make-journal-entry)
6  (bind-key "C-c p" 'org-plot/gnuplot)

Other key and mouse related settings

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.

1(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.

1(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.

1(setq mouse-wheel-follow-mouse 't)
2(setq mouse-wheel-scroll-amount '(1 ((shift) . 1)))

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 momen I use two varieties:

  1. Ansi terminal; basically a front-end to /bin/bash

  2. Eshell; the all Emacs-Lisp based solution.

Ansi term

 1  (defun mrb/use-utf8 ()
 2    (set-buffer-process-coding-system 'utf-8-unix 'utf-8-unix))
 3  (add-hook 'term-exec-hook 'mrb/use-utf8)
 4
 5  ;; Define terminal config, I prefer ansi-term
 6  (use-package term)
 7  (defun mrb/ansi-term (&optional new-buffer-name)
 8    "Start a terminal-emulator in a new buffer."
 9    (interactive)
10
11    (setq program "/bin/bash")
12
13    ;; Pick the name of the new buffer.
14    (setq term-ansi-buffer-name
15          (if new-buffer-name
16              new-buffer-name
17            (if term-ansi-buffer-base-name
18                (if (eq term-ansi-buffer-base-name t)
19                    (file-name-nondirectory program)
20                  term-ansi-buffer-base-name)
21              "ansi-term")))
22
23    (setq term-ansi-buffer-name (concat "*" term-ansi-buffer-name "*"))
24
25    ;; In order to have more than one term active at a time
26    ;; I'd like to have the term names have the *term-ansi-term<?>* form,
27    ;; for now they have the *term-ansi-term*<?> form but we'll see...
28
29    (setq term-ansi-buffer-name (generate-new-buffer-name term-ansi-buffer-name))
30    (setq term-ansi-buffer-name (term-ansi-make-term term-ansi-buffer-name program))
31
32    (set-buffer term-ansi-buffer-name)
33    (term-mode)
34    (term-char-mode)
35
36    ;; Go there
37    (switch-to-buffer term-ansi-buffer-name))
38
39  ; Aliases
40  (defalias 'at 'mrb/ansi-term)
41
42  ;; When closing the ansi term window, close the useless buffer too
43  (defadvice term-sentinel (around mrb/advice-term-sentinel (proc msg))
44    (if (memq (process-status proc) '(signal exit))
45        (let ((buffer (process-buffer proc)))
46          ad-do-it
47          (kill-buffer buffer))
48      ad-do-it))
49  (ad-activate 'term-sentinel)

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:

1  (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.

1(defun eshell/clear ()
2  "Clears the shell buffer as in bashes clear"
3  (interactive)
4  ;; clear read-only of prompts
5  (let ((inhibit-read-only t))
6        (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.

1  (defadvice find-file-other-window (around find-files activate)
2    "Also find all files within a list of files. Thsi even works recursively."
3    (if (listp filename)
4        (loop for f in filename do (find-file-other-window f wildcards))
5      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.

1  (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.

1  (setq eshell-output-filter-functions
2        (quote
3         (eshell-handle-ansi-color
4          eshell-handle-control-codes
5          eshell-watch-for-password-prompt
6          eshell-handle-control-codes
7          eshell-handle-ansi-color
8          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.

 1;; Do completions, but don't cycle
 2(setq eshell-cmpl-cycle-completions nil)
 3
 4;; Completion ignores case
 5(setq eshell-cmpl-ignore-case t)
 6
 7;; scroll to the bottom
 8(setq eshell-scroll-to-bottom-on-output t)
 9(setq eshell-scroll-show-maximum-output t)
10(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:

1emacsclient -a "" -n -c -e '(mrb/server-eshell)'

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

 1  (use-package cl)
 2
 3  (defun mrb/server-eshell ()
 4    "Command to be called by emacs-client to start a new shell.
 5
 6  A new eshell will be created. When the frame is closed, the buffer is deleted or the shell exits,
 7  then hooks will take care that the other actions happen. For example, when the frame is closed,
 8  then the buffer will be deleted and the client disconnected.
 9  "
10    (lexical-let ((buf (eshell t))
11                  (client (first server-clients))
12                  (frame (selected-frame)))
13      (cl-labels ((close (&optional arg)
14                  (when (not (boundp 'cve/recurse))
15                    (let ((cve/recurse t))
16                      (delete-frame frame)
17                      (kill-buffer buf)
18                      (server-delete-client client)))))
19      (add-hook 'eshell-exit-hook #'close t t)
20      (add-hook 'delete-frame-functions #'close t t))
21      (delete-other-windows)
22      nil))

Completion

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.

1(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 funciton completion-at-point is used in some situations. Ideally the company-complet 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 complet it with company;

  3. else, indent according to mode.

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

 1  (use-package company)
 2
 3  ;; Remap normal indent-for-tab-command
 4  (bind-key [remap indent-for-tab-command]
 5    'company-indent-for-tab-command company-mode-map)
 6
 7  ;; Save the normal completion functions temporarily
 8  (defvar completion-at-point-functions-saved nil)
 9
10  (defun company-indent-for-tab-command (&optional arg)
11    (interactive "P")
12    (let ((completion-at-point-functions-saved completion-at-point-functions)
13          (completion-at-point-functions '(company-complete-common-wrapper)))
14      (indent-for-tab-command arg)))
15
16  (defun company-complete-common-wrapper ()
17    (let ((completion-at-point-functions completion-at-point-functions-saved))
18      (company-complete-common)))

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:

  • languages: lisp, python, ruby, bash, C/C++ roughtly in that order (function and argument completion)

  • for all languages, function/method signature shorthands

  • speed, slowness is killing here

  • prevent minibuffer distractions, put info where my eyes are and that is the cursor in most cases.

  • maybe: spelling suggestions

  • nick completion in irc channels

Candidates:

auto-complete

http://cx4a.org/software/auto-complete/

company-mode

http://company-mode.github.io

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 mode which we know lead to problems:

1  (setq company-global-modes
2        '(not magit-status-mode
3              help-mode))
4  (add-hook 'after-init-hook 'global-company-mode)

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 logis 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.

1(bind-key  "C-n" 'company-select-next company-active-map)
2(bind-key  "C-p" 'company-select-previous company-active-map)

Having the completion pop up automatically is annoying in most cases, so I disable that.

1(setq company-idle-delay nil)

Ad 4. Mode specialisation

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

1
2  ;; This does not work for some reason?
3  ;; (use-package eshell
4  ;;   :init
5  ;;   (bind-key "<tab>"  'company-complete  eshell-mode-map))

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.

1(use-package popwin)
2(setq display-buffer-function 'popwin:display-buffer)

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

I like to do things interactively where I can. The package ido is part of emacs and is more or less a defacto standard within the emacs community. The basic promis is that when finding files, switching buffers or otherwise completing things in the command area, ido takes over and make that completion a better experience. One of these this is so called flex matching, which makes the completion match strings which contain the entered characters if it doesn't match directly.

1(use-package ido)
2(ido-mode t)
3(setq ido-enable-flex-matching t) ;; enable fuzzy matching
4(ido-everywhere)

Normally, ido presents choices as a horizontal list which is kind of messy, especially when working in projects which have standard files in many directories.

1(use-package ido-vertical-mode)
2(ido-vertical-mode)

The (ido-everywhere) call above does not really enable ido everywhere. ido-ubiquitous does, so let's enable that.

1;;(use-package ido-ubiquitous)
2;;(ido-ubiquitous-mode)

By selecting a piece of text, wrap-region can quote or otherwise delimit that region automatically. I enable this globally. There is an option to configure in exception, which I have commented out as I've found none applicable yet.

1(use-package wrap-region)
2(wrap-region-global-mode 1)
3;; (add-to-list 'wrap-region-except-modes 'conflicting-mode)

Creating regions can be done in many different ways and expand region which expands a selection by semantic analysis can not be left out

1  (use-package expand-region
2    :bind
3    (("s-\\" . er/expand-region)
4    ("s-|"  . er/contract-region)))

Navigation

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 ace-jump-mode 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.

The mechanisme is similar to isearch, so I want the faces to be the same. I don't think there is a risk of confusion as to which mechanism is in use, but we'll see.

 1  (use-package ace-jump-mode
 2    :config
 3    (set-face-attribute
 4     'ace-jump-face-foreground nil
 5     :inherit isearch-lazy-highlight
 6     :background "black"
 7     :foreground "tomato")
 8
 9    (set-face-attribute
10     'ace-jump-face-background nil
11     :foreground nil
12     :background mrb-bg-paren-match))

The effectiveness is also dependent on a quick and easy keyboard shortcut. Ace jump mode has 3 modes:

  1. char mode

  2. word mode

  3. line mode

which respectively search for the existence of a character, the beginning of a word and the beginning of a line. The mode keeps highlighting jump locations until there is a unique match before it jumps. The word mode is typically 90% of the usage, so that should get the main keyboard shortcut, while the two others can get the usual C-u treatment. For now, we take the standard keybinding C-c SPC. This key needs to be undefined in org-mode (clear table cell). I never use this.

1  (bind-key "C-c SPC" nil org-mode-map)
2  (bind-key "s-j" 'ace-jump-mode)
3  ;; Pop back to mark is special for ace-jump? C-x space is the key i think.

Recall that once jumped, jumping back is just a pop the mark (C-u C-SPC) away, but there is a little catch. If the scope of ace-jumping is global, popping the mark is not working properly because the mark is by current window only. I'm not sure hwo this works out for me; estimation is that the normal pop mark is enough. I'm just taking the advised configuration from the author at this point.

1  (eval-after-load "ace-jump-mode"
2    '(ace-jump-mode-enable-mark-sync))
3  (bind-key "C-x SPC" 'ace-jump-mode-pop-mark)

Remote editing

1(setq tramp-default-method "sshx")

Browser integration

My default browser, as set in the OS, is chromium. Yet, emacs needs an explicit mention, otherwise it will start firefox. Not sure why that is.

1(setq browse-url-browser-function (quote browse-url-generic))
2(setq browse-url-generic-program "x-www-browser")

Entering information in text areas in the browsert, provided it is of the chrome family, can be delegated to emacs with the 'edit with emacs' extension. This requires the 'edit-server' package in emacs.

1(use-package edit-server)
2(edit-server-start)

Messaging and chatting

Mail

Mail configuration.

 1  (setq
 2   ; User agent style is message mode (gnus, but independent of it)
 3    mail-user-agent 'message-user-agent
 4
 5    ; Sending it
 6    smtpmail-default-smtp-server "localhost"
 7    smtpmail-smtp-service 24 ;; I use anubis for pre-processing, 24 is private mail port
 8    smtpmail-local-domain user-domain
 9    smtpmail-sendto-domain user-domain
10    send-mail-function 'smtpmail-send-it                 ; This is for mail
11    message-send-mail-function 'message-smtpmail-send-it ; This is for gnus
12
13    ; Always put one in the Sent folder on sending
14    message-default-mail-headers "Bcc: mrb+Sent@hsdev.com\n"
15    mail-yank-prefix ">> "
16  )
17  ;; Automatically sign outgoing messages, be part of the solution here,
18  ;; not the problem
19  ;; TODO: where can I toggle this on/off while composing?
20  ;; TODO: I want to do this in anubis, so this can probably go
21  ;;(setq smime-keys (quote (( user-mail-address "~/keys/comodo-2012-06-12.pem" nil))))
22  ;;(add-hook 'message-send-hook 'mml-secure-message-sign-smime)
23  (setq password-cache t)            ; default is true, so no need to set this actually
24  (setq password-cache-expiry 28800) ; default is 16 seconds, which is ridiculously low
25  (use-package smtpmail)
26
27  ;;(add-hook 'message-mode-hook 'orgstruct++-mode 'append)
28  (add-hook 'message-mode-hook 'turn-on-auto-fill 'append)

I think I might like mu4e use once in a while, so let's set up a configuration for that as well.

 1    (use-package mu4e)
 2
 3    (setq
 4     mu4e-maildir "~/Maildir"
 5     mu4e-sent-folder "/.Sent"
 6     mu4e-drafts-folder "/.Drafts"
 7     mu4e-trash-folder "/.Trash"
 8     mu4e-get-mail-command t
 9     mu4e-update-interval nil
10     mu4e-view-show-images t
11     mu4e-view-image-max-width 800
12     mu4e-reply-to-address user-mail-address)

Composing mail is often an out of band activity, like creating a dent or a capture, so I would like to have roughly the same behaviour. This is by default provided by compose-mail-other-frame, but I could not get awesome to properly place the windows because it's hard to match on the varying name that xprop delivers, so. we make our own window.

 1  (defun mrb/make-mailcompose-frame (&optional mailto-url)
 2    "Create a new frame and run mail-compose, use mailto URI if it is given."
 3    (interactive)
 4
 5    ;; Create and select the frame, awesome wm takes care of the
 6    ;; placement and floating it on top of all other windows.
 7    (select-frame (make-frame '((name . "mailcompose")
 8                                (width . 100) (height . 50)
 9                                (menu-bar-lines . 0) (tool-bar-lines . 0))))
10
11    ;; If we have a mailto argument, parse and use it
12    (if (and (stringp mailto-url)
13             (string-match "\\`mailto:" mailto-url))
14        (progn
15          (use-package rfc2368)
16          (use-package rfc2047)
17          (use-package mailheader)
18
19          (let ((hdr-alist (rfc2368-parse-mailto-url mailto-url))
20                (body "")
21                to subject
22                ;; In addtion to To, Subject and body these headers are
23                ;; allowed:
24                (allowed-xtra-hdrs '(cc bcc in-reply-to)))
25            (with-temp-buffer
26              ;; Extract body if it is defined
27              (when (assoc "Body" hdr-alist)
28                (dolist (hdr hdr-alist)
29                  (when (equal "Body" (first hdr))
30                    (insert (format "%s\n:" (rest hdr)))))
31                (rfc2047-decode-region (point-min) (point-max))
32                (setq body (buffer-substring-no-properties
33                            (point-min) (point-max)))
34                (erase-buffer))
35
36              ;; Extract headers
37              (dolist (hdr hdr-alist)
38                (unless (equal "Body" (first hdr))
39                  (insert (format "%s: %s\n" (first hdr) (rest hdr)))))
40              (rfc2047-decode-region (point-min) (point-max))
41              (goto-char (point-min))
42              (setq hdr-alist (mail-header-extract-no-properties)))
43
44            (setq to (or (rest (assq 'to hdr-alist)) "")
45                  subject (or (rest (assq 'subject hdr-alist)) "")
46                  hdr-alist
47                  (remove nil (mapcar
48                               #'(lambda (item)
49                                   (when (memq (first item) allowed-xtra-hdrs)
50                                     (cons (capitalize (symbol-name (first item)))
51                                           (rest item))))
52                               hdr-alist)))
53
54
55            ;; Fill it
56            (compose-mail to subject hdr-alist nil nil
57                          (list (lambda (string)
58                                  (insert string))
59                                body))))
60      ;; No mailto, argument, just run compose mail
61      (compose-mail))
62
63      ;; Delete other windows from the frame we are composing it in.
64    (delete-other-windows))
65
66    (defadvice message-kill-buffer (after delete-mailcompose-frame activate)
67      "Advise message-kill-buffer to close the frame if it is the capture frame"
68      (if (equal "mailcompose" (frame-parameter nil 'name))
69          (delete-frame)))
70    (defadvice message-send-and-exit (after delete-mailcompose-frame activate)
71      "Advise message-send-and-exit to close the frame if it is the capture frame"
72      (if (equal "mailcompose" (frame-parameter nil 'name))
73          (delete-frame)))

To be able to use the mailcompose-frame 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.

 1# Emacs mailto URI handler
 2
 3mailto=$$1
 4mailto="mailto:$${mailto#mailto:}"
 5
 6mailto=$$(printf '%s\n' "$$mailto" | sed -e 's/[\"]/\\&/g')
 7
 8# Call the elisp function handling our mailto URI
 9elisp_expr="(mrb/make-mailcompose-frame \"$$mailto\")"
10
11# Go
12edit --eval "$$elisp_expr"

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

1edit -e '(mrb/make-mailcompose-frame)'

GNUS

GNUS warrants its own chapter for configuration, as it is somewhat different from other clients. This is my story of gnus.

It's probably going to be an iterative effort to get things working the way I want them to, so I'm going to configure things from a working situation to a new working situation where the latte does not necessarily represent a wanted situation, but takes me close to it.

So, let's begin by enabling imap as the selection method.

1    (setq gnus-select-method
2      '(nnimap "hsd"
3	       (nnimap-address "imap.hsdev.com")
4	       (nnimap-server-port 993)
5	       (nnimap-stream ssl)))

This, in combination with a line in .authinfo.gpg which holds the authentication info is enough to get things rolling. The initial connect subscribes to all my mail folders and shows them in a group window.

Notmuch

Not having used GNUS enough to be comfortable, notmuch seems a route which can be taken somewhat easier. The compose part of emails is, with the capture like frame, basically done. The one thing which was a bit messy was the address completion which was supposed to go to an LDAP directory tied to an ODOO instance.

The cop-out intermediate solution is address completion by a small utility which searches the notmuch database for email-addresses used before, which is likely to be a match in 90% of the cases.

https://github.com/aperezdc/notmuch-addrlookup-c

Installation would be quite easy:

1   (use-package notmuch-address
2     :config
3     (progn
4       (setq notmuch-address-command "/usr/local/bin/notmuch-addrlookup"
5             notmuch-fcc-dirs nil
6             notmuch-poll-script nil)
7       (notmuch-address-message-insinuate)
8       (message "Notmuch address completion loaded...")))

but this only activates the completion after notmuch has loaded, so it seems we need to put a similar piece of code on a lower level. This can work because notmuch is a database on the OS level.

Ideally, i'd want to use inline completion with company mode, but I haven't figured out yet how to do that. So, for the time being, I'll use the ido completion I use in many other places as the selection function.

1  (defun mrb/notmuch-address-selection-function (prompt addressess first)
2    "Use `ido-completing-read' to select one of the addresses."
3    (ido-completing-read prompt (cons first addressess)
4                         nil nil nil 'notmuch-address-history))
5
6  (setq notmuch-address-selection-function
7        'mrb/notmuch-address-selection-function)

Statusnet

Statusnet, or perhaps microblog in general settings

 1  ; Identica comes directly from its git repository
 2  (use-package longlines)
 3  (setq
 4   statusnet-server "o.mrblog.nl"
 5   statusnet-access-url "http://o.mrblog.nl/api/oauth/access_token"
 6   statusnet-authorize-url "http://o.mrblog.nl/api/oauth/authorize"
 7   statusnet-request-url "http://o.mrblog.nl/api/oauth/request_token"
 8   identica-username "mrb"
 9   identica-display-success-messages nil
10   identica-soft-wrap-status t
11   identica-status-format "%i %s %r: %t"
12   identica-timer-interval 120
13   identica-update-status-method (quote minibuffer)
14   identica-oldest-first nil
15   identica-soft-wrap-status nil
16   identica-urlshortening-service (quote isgd)
17   identica-enable-striping t
18   identica-enable-highlighting t
19  )
20
21
22  (bind-key "C-c ip" 'identica-update-status-interactive)
23  (bind-key "C-c id" 'identica-direct-message-interactive)
24  (bind-key "C-c is" 'identica-shortenurl-replace-at-point)
25  (bind-key "C-c d"  'mrb/make-dent-frame)
26
27
28  ;; I'm using a floating frame in awesome WM for dents
29  (defun mrb/make-dent-frame ()
30    "Create a new frame and run identica-update status."
31    (interactive)
32    ;; Create and select the frame
33    (select-frame (make-frame '((name . "dent")
34                  (width . 80) (height . 15)
35                  (menu-bar-lines . 0) (tool-bar-lines . 0))))
36    ;; Capture a Todo entry, force edit-window method
37    (identica-update-status 'edit-buffer)
38
39    ;; Once there, make sure we're the only one
40    (delete-other-windows)
41  )
42
43  ;; Make sure we remove our frame too
44  (defadvice identica-update-status-from-edit-buffer-send
45    (after delete-dent-frame-on-send activate)
46    "Advise to close the frame"
47    (if (equal "dent" (frame-parameter nil 'name))
48        (delete-frame))
49  )
50  (defadvice identica-update-status-from-edit-buffer-cancel
51    (after delete-dent-frame-on-cancel activate)
52    "Advise to close the frame"
53    (if (equal "dent" (frame-parameter nil 'name))
54        (delete-frame))
55  )

Denting interface from the commandline goes to here:

1edit -e '(mrb/make-dent-frame)'

Pump.io

Similar to capturing TODO and BUY entries, there's a way to captur pump.io entries too.

The only pump.io elisp implementation does almost everything like I want it to be, including key bindings.

 1  ;;(use-package pumpio-interface)
 2
 3  ;; Configure for qua.name
 4  (setq pumpio-pod "http://qua.name")
 5
 6
 7  ;; I'm using a floating frame in awesome WM for dents
 8  (defun mrb/make-pump-frame ()
 9    "Create a new frame and run pump postnote"
10    (interactive)
11    ;; Create and select the frame
12    (select-frame (make-frame '((name . "pump")
13                                (width . 80) (height . 15)
14                                (menu-bar-lines . 0) (tool-bar-lines . 0)))))
15
16  (defun mrb/capture-pump ()
17    "Capture a pump note"
18    (interactive)
19
20    (mrb/make-pump-frame)
21    (pmpio-ctrl-post-note)
22
23    ;; Once there, make sure we're the only one
24    (delete-other-windows)
25  )
26
27  ;; Make sure we close our frame
28  (defadvice pmpio-ctrl-close-new-note (after delete-pump-frame activate)
29    "Advise to close the frame"
30    (if (equal "pump" (frame-parameter nil 'name))
31        (delete-frame)))

Commandline interface for posting a pump.io notices goes here:

1edit -e '(mrb/capture-pump)'

XMPP

XMPP related settings, including interfacing it to other systems

 1  (use-package jabber
 2    :init
 3    ;; My accounts
 4    ;; Make sure the user-xmpp-account gets evaluated
 5    (setq jabber-account-list
 6          `((,user-xmpp-account (:connection-type . starttls))))
 7
 8    :config
 9    (progn
10      ;; Show some info in the modeline
11      (jabber-mode-line-mode 1)
12
13      ;; Configuration variables
14      (setq
15       jabber-show-offline-contacts nil
16       jabber-default-priority 30
17       jabber-alert-message-hooks (quote
18                                   (jabber-message-libnotify
19                                    jabber-message-echo
20                                    jabber-message-awesome
21                                    jabber-message-wave
22                                    jabber-message-scroll))
23       jabber-message-alert-same-buffer nil
24       jabber-roster-show-bindings nil
25       jabber-auto-reconnect t
26       jabber-chat-buffer-format "*-chat-%n-*"
27       jabber-groupchat-buffer-format "*-groupchat-%n-*"
28       jabber-muc-colorize-foreign t
29       jabber-muc-colorize-local t
30       jabber-muc-disable-disco-check t
31       jabber-muc-private-buffer-format "*-muc-priv-%g-%n-*"
32       jit-lock-stealth-time 16
33       jabber-show-resources 'sometimes
34       jabber-resource-line-format "         %j/%r%S [%p]"
35       jabber-roster-buffer "*-roster-*"
36       jabber-roster-line-format "  %u %a %-25n - %S"
37       jabber-roster-show-title nil
38       jabber-roster-subscription-display (quote
39                                           (("none" . "   ")
40                                            ("from" . "← ")
41                                            ("to" . " →")
42                                            ("both" . "←→")))
43       jabber-socks5-proxies (quote ("proxy.hsdev.com"))
44       jabber-vcard-avatars-retrieve nil
45       jabber-muc-disable-disco-check t
46       jabber-muc-colorize-foreign t
47       jabber-muc-colorize-local t
48       jabber-muc-nick-saturation 0.35 ;; empirical value, suitable for my theme
49       jabber-muc-nick-value 0.75
50
51       ;; Make the MUCs
52       jabber-muc-show-affiliation-changes nil
53       jabber-muc-show-enter-presence nil
54       jabber-muc-show-leave-messages nil
55       jabber-muc-show-role-changes nil
56       )
57
58
59      ;; C-j is the prefix for all jabber command in the C-x map (so, C-x C-j precede all commands for jabber)
60      ;; The default C-x map for emacs has a C-j entry which binds it to
61      ;; dired-jump. This gets in the way of all the keyboard shortcuts for
62      ;; jabber, so lets re-call the definition here, so we are sure we get
63      ;; them.
64
65      (bind-key "C-j" jabber-global-keymap ctl-x-map)
66
67      ;; Do not steal my focus in the mini buffer
68      ;; Message alert hooks
69      (define-jabber-alert echo "Show a message in the echo area"
70        (lambda (msg)
71          (unless (minibuffer-prompt)
72            (message "%s" msg))))
73
74      ;; Some face adjustments
75      (add-hook 'jabber-chat-mode-hook
76                (lambda ()
77                  (set-face-attribute 'jabber-chat-prompt-system nil :foreground "dark gray" :weight 'bold))))
78
79    :bind
80    ("C-c C-SPC" . jabber-activity-switch-to))

Twister

Twister is a p2p microblogging application based on

  1. Bitcoin - for user registration and authentication

  2. Distributed Hash Tables - for KV storage and tracker location for:

  3. Bittorrent - manage followers(leechers/seeders) for notification

http://twister.net.co has all the info

I want this to behave the same as my other microblog postings, hopefully one day consolidating all these entry points into a consistent interface to rule them all.

 1  (add-to-list 'load-path "/home/mrb/dat/src/twister/twister.el")
 2  (use-package twister)
 3  (setq twister-user "mrb")
 4
 5  (defun mrb/capture-twist()
 6    "Create a new frame an run twister postnote"
 7    (interactive)
 8
 9    (select-frame (make-frame '((name . "twister")
10                                (width . 80) (height . 15)
11                                (menu-bar-lines .0)
12                                (tool-bar-lines .0))))
13
14    (twister-create-post)
15
16    ;; And once we're there, make sure we are alone
17    (delete-other-windows))
18
19  ;; Make sure we close our frame after we close the post
20  (defadvice twister-close-post (after delete-twister-frame activate)
21    "Advise to close the frame"
22    (if (equal "twister" (frame-parameter nil 'name))
23        (delete-frame)))
24
25  ;; I normally want to close the post after posting Note: this is not
26  ;; at all a duh! Another advise could be to just clear the window and
27  ;; have it ready for another twist
28  (defadvice twister-post-region (after close-twister-post activate)
29    "Advise to close the post"
30    (twister-close-post))
31
32  ;; Change the default keybinding to company mode completion
33  ;; C-i is the same as TAB
34  (bind-key "C-i" 'company-complete twister-post-mode-map)

And, like the others, a commandline interface

1edit -e '(mrb/capture-twist)'

The keybinding for the separate posting frame is configured with bindkeys on my system.

Elfeed

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

 1  (use-package vlc)  ;; Needs my little hack from https://github.com/mrvrb/vlc.el
 2  (use-package elfeed
 3    :init
 4    (bind-key "C-c f" 'elfeed)
 5    :config
 6    (progn
 7      (defun mrb/elfeed-search-toggle-tag(tag)
 8        (let ((entries (elfeed-search-selected)))
 9          (cl-loop for entry in entries do
10                   (if (elfeed-tagged-p tag entry)
11                       (elfeed-untag entry tag)
12                     (elfeed-tag entry tag)))
13          (mapc #'elfeed-search-update-entry entries)
14          (unless (use-region-p) (forward-line))))
15
16      (defun mrb/elfeed-show-toggle-tag(tag)
17        (interactive)
18        (if (elfeed-tagged-p tag elfeed-show-entry)
19            (elfeed-show-untag tag)
20          (elfeed-show-tag tag)))
21
22      (defun mrb/enqueue-video()
23        "Enqueue the current entry, assuming it is playable, to vlc"
24        (interactive)
25        (vlc/enqueue (elfeed-entry-link elfeed-show-entry)))
26
27      (defun mrb/play-video()
28        "Play the video in the current entry, bypassing the queeu it first"
29        (interactive)
30        (vlc/add (elfeed-entry-link elfeed-show-entry)))
31
32      (defun youtube-get-id (url)
33        "Get the YouTube video ID from URL. Returns nil for invalid URLs."
34        (let* ((obj (url-generic-parse-url url))
35               (host (url-host obj))
36               (path (url-filename obj))
37               (match (string-match-p "[-_a-zA-Z0-9]\\{11\\}" path)))
38          (when (and match (member host youtube-hosts))
39            (substring path match (+ match 11)))))
40
41      (defcustom youtube-pl-arguments '("--title" "--no-mtime" )
42        "Arguments to be send to youtube-pl."
43        :group 'external)
44
45      (defvar youtube-hosts '("www.youtube.com" "youtube.com" "youtu.be")
46        "Domain names for YouTube.")
47
48      (defun youtube-pl-sentinel (process event)
49        "Responds to completed youtube-dl processes."
50        (let ((buffer (process-buffer process)))
51          (with-current-buffer buffer
52            (when (string-match-p "finished" event)
53              (message "youtube-pl %s completed." (youtube-get-id youtube-pl-url))
54              (kill-buffer buffer))
55            (when (string-match-p "abnormal" event)
56              (message "youtube-pl %s failed." (youtube-get-id youtube-pl-url))))))
57
58      (defun mrb/play-youtube-video (url)
59        "Play the video at URL with youtube-pl. Returns the buffer
60         that will show progress output. The buffer is killed if the
61         play completes successfully."
62        (interactive (list (read-from-minibuffer "URL: " (x-get-selection-value))))
63        (let ((id (youtube-get-id url)))
64          (when id
65            (let* ((process-name (format "youtube-pl-%s" id))
66                   (buffer-name (format "*youtube-pl %s*" id))
67                   (buffer (get-buffer-create buffer-name)))
68              (unless (get-buffer-process buffer)
69                (with-current-buffer buffer
70                  (erase-buffer)
71                  ;(youtube-dl-mode)
72                  (setq youtube-pl-url url)
73                  ;(setq default-directory
74                  ;      (concat (directory-file-name youtube-dl-directory) "/"))
75                  (set-process-sentinel
76                   (apply #'start-process process-name buffer "youtube-pl"
77                          (append youtube-pl-arguments (list "--" id)))
78                   'youtube-pl-sentinel)))
79              buffer))))
80
81      (bind-key "w" '(lambda () (interactive) (mrb/elfeed-show-toggle-tag 'watchlater)) elfeed-show-mode-map)
82      (bind-key "w" '(lambda () (interactive) (mrb/elfeed-search-toggle-tag 'watchlater)) elfeed-search-mode-map)
83      (bind-key "e" '(lambda () (interactive) (mrb/enqueue-video)) elfeed-show-mode-map)
84      (bind-key "v" '(lambda () (interactive) (mrb/play-video)) elfeed-show-mode-map)
85
86
87      ;; New entry hook allows meta information manipulation
88      ;; without directly having to change elfeed-feeds
89      (add-hook 'elfeed-new-entry-hook
90                (elfeed-make-tagger :feed-url "youtube\\.com"
91                                    :add '(video youtube)))
92      (add-hook 'elfeed-new-entry-hook
93                (elfeed-make-tagger :feed-url "vimeo\\.com"
94                                    :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.

1(use-package elfeed-org
2      :init
3      (setq rmh-elfeed-org-files (list (concat emacs-directory "feeds.org")))
4
5      (defadvice elfeed (before configure-elfeed activate)
6        "Load all feed settings before elfeed is started"
7        (rmh-elfeed-org-configure)))
1  ;; Automatically update the feeds every now and then
2  (run-at-time 180 1800 (lambda () (unless elfeed-waiting (elfeed-update))))

Telegram

There's no telegram client in emacs lisp (yet), but I do have a little mode I use when giving telegram support. It's in my own repository on github.

1  (use-package tsupport)

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.

A better solution seems to be ethan-space or ws-trim.el. The first seems to be the least intrusive, as it keeps files which do not have trailing spaces clean and highlights 'dirty' ones in the hope that I notice and remove them. ws-trim.el can have different levels of intrusiveness. I'm installing ethan-space for now.

[2014-05-20 di] worked with ethan-wspace for a while and it's pretty annoying. 80% of the space it is displaying as being wrong is harmless. I have uninstalled it for now, I may reconsider installing it only for some modes and not globally.

[2014-06-20 vr] Retrying ws-butler mode (see Visual ) [2014-12-23 di] Retrying ws-butler mode (see Visual )

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 useing magit, so let's begin with bringing that in.

 1  (use-package magit
 2    ;; the 'MRev' text in the modeline is useless
 3    :diminish magit-autorevert-mode
 4
 5    :init
 6     (fullframe magit-status magit-mode-quit-window)
 7
 8    :config
 9    (progn
10      ;; Make sure that highlighted regions in magit keep their syntax
11      (set-face-attribute 'magit-item-highlight nil
12                          :inherit nil
13                          :foreground nil :background mrb-bg-paren-match)))

GIT integration

This section is a temporary place to store some git related settings. I need to reorganize this document a bit so I have a section for source code control or something similar to group these settings.

This particular setting is for eshell git completion:

 1(defun pcmpl-git-commands ()
 2  "Return the most common git commands by parsing the git output."
 3  (with-temp-buffer
 4    (call-process-shell-command "git" nil (current-buffer) nil "help" "--all")
 5    (goto-char 0)
 6    (search-forward "available git commands in")
 7    (let (commands)
 8      (while (re-search-forward
 9              "^[[:blank:]]+\\([[:word:]-.]+\\)[[:blank:]]*\\([[:word:]-.]+\\)?"
10              nil t)
11        (push (match-string 1) commands)
12        (when (match-string 2)
13          (push (match-string 2) commands)))
14      (sort commands #'string<))))
15
16(defconst pcmpl-git-commands (pcmpl-git-commands)
17  "List of `git' commands.")
18
19(defvar pcmpl-git-ref-list-cmd "git for-each-ref refs/ --format='%(refname)'"
20  "The `git' command to run to get a list of refs.")
21
22(defun pcmpl-git-get-refs (type)
23  "Return a list of `git' refs filtered by TYPE."
24  (with-temp-buffer
25    (insert (shell-command-to-string pcmpl-git-ref-list-cmd))
26    (goto-char (point-min))
27    (let (refs)
28      (while (re-search-forward (concat "^refs/" type "/\\(.+\\)$$") nil t)
29        (push (match-string 1) refs))
30      (nreverse refs))))
31
32(defun pcmpl-git-remotes ()
33  "Return a list of remote repositories."
34  (split-string (shell-command-to-string "git remote")))
35
36(defun pcomplete/git ()
37  "Completion for `git'."
38  ;; Completion for the command argument.
39  (pcomplete-here* pcmpl-git-commands)
40  (cond
41   ((pcomplete-match "help" 1)
42    (pcomplete-here* pcmpl-git-commands))
43   ((pcomplete-match (regexp-opt '("pull" "push")) 1)
44    (pcomplete-here (pcmpl-git-remotes)))
45   ;; provide branch completion for the command `checkout'.
46   ((pcomplete-match "checkout" 1)
47    (pcomplete-here* (append (pcmpl-git-get-refs "heads")
48                             (pcmpl-git-get-refs "tags"))))
49   (t
50    (while (pcomplete-here (pcomplete-entries))))))

To organize and properly configure

Multiple cursors sounds interesting

1(use-package multiple-cursors)

License templates

1(use-package xlicense)
2(setq license-directory (concat emacs-directory "licenses"))
3(add-to-list 'license-types '(agpl . "AGPL"))
4(setenv "ORGANIZATION" user-organisation)

Make sure erase works properly, even though I don't understand this, apparently this is what I need.

1(if window-system  (normal-erase-is-backspace-mode t))

Having regex based query replace options is quite a large usecase, but regexps in emacs are, well, odd. Luckily there is re-build which lets you interactively build a regular expression on a target buffer and see the matches.

Once such an expression is made, 90% of the time I want to copy the regexp to the input of query-replace-regexp to do the actual replacing. Somehow the copy of the regexp is mangled in that process (apparently suitable for programming) so the operation won't work.

Below is a function which fixes this. I want to have this in my config, bound to a key in the re-builder keymap.

1(defun reb-query-replace (to-string)
2      "Replace current RE from point with `query-replace-regexp'."
3      (interactive
4       (progn (barf-if-buffer-read-only)
5              (list (query-replace-read-to (reb-target-binding reb-regexp)
6                                           "Query replace"  t))))
7      (with-current-buffer reb-target-buffer
8        (query-replace-regexp (reb-target-binding reb-regexp)
9to-string)))

Usecase: copying a rectangle of text and re-using it somewhere else.

1(defun my-copy-rectangle (start end)
2   "Copy the region-rectangle instead of `kill-rectangle'."
3   (interactive "r")
4   (setq killed-rectangle (extract-rectangle start end)))

Google-map

Google map integration for Emacs

1  ; Directly tie into the GIT repository on this machine
2  ;(add-to-list 'load-path "~/.emacs.d/el-get/google-maps/")
3
4  ;(use-package google-maps)
5
6  ; org-mode integration
7  ;(use-package org-location-google-maps)
8
9  (provide 'google-map-settings)

Machine specific configurations

The aim is to have the exact same configuration on each machine, but there may be occasions where is pays to have a slight difference in configuration on different machines.

 1  (when (string= system-name "x200s")
 2    (progn
 3      ;; Stuff that needs to happen for machine named "x200s"
 4      ;; TODO: deviate from the standard font, make it Ubuntu
 5      (set-frame-font "DejaVu Sans Mono")
 6      (set-face-attribute 'default nil :height 110)
 7      ))
 8
 9  (when (string= system-name "t510")
10    (progn
11      ;; Stuff for the "t510" configuration
12
13      ))

Wishlist

There's always more to want, here a quick list of things:

  • epub export from org-mode

  • a working fill-column indicator which does not kill performance

  • specific changes to increase emacs performance, it can be slow at times

  • doc-view window fitting

  • if i had a browser in emacs i could do away with all other programs ;-)

  • threading or reliable async operation

  • much better integration of emacs into social systems There is proper support for xmpp, irc, possibly twitter, but the rest sucks.

Finale

When we are all done with this, provide it.

1(provide 'mrb)
2;;; mrb ends here

Self configuration

I have the same config file for orgmode itself

If there is anything that needs to be configured specifically for this file, here is the place to do it. Other than those, nothing else should come below this point