Meine Konfiguration des GNU Emacs

Letzte Aktualisierung: 1. Juli 2018

Dies ist meine persönliche, kommentierte Emacs-Konfiguration. Sie ist in der Syntax des Emacs Org-mode geschrieben. Der enthaltene Emacs Lisp Code wird beim Start des Emacs extrahiert und zur Konfiguration abgearbeitet; vgl. dazu meinen Text zur Systematik der Emacs-Konfiguration.

Versionsangaben:

  • Ubuntu Linux 18.04 LTS („Bionic“)
  • GNU Emacs 26.1
  • Org mode version 9.1.13

Emacs Core

Zunächst werden die Kernelemente meines Emacs konfiguriert.

Package Management

Heutzutage installiert man Emacs-Pakete nicht mehr manuell, sondern man verwendet das Emacs-eigene Paketmanagement.

(require 'package)
(setq package-enable-at-startup nil)
(setq package-archives '(("melpa"     . "https://melpa.org/packages/")
                         ("org"       . "http://orgmode.org/elpa/")
                         ("gnu"       . "http://elpa.gnu.org/packages/")
                         ("marmalade" . "https://marmalade-repo.org/packages/")))
(package-initialize)

Use package

use-package ist ein sehr praktisches Paket von John Wiegley, das die Strukturierung und Optimierung der Emacs-Konfiguration ermöglicht. Weitere Informationen finden sich auf der Seite des Github-Projekts.

;; Bootstrap `use-package'
(unless (package-installed-p 'use-package)
  (package-refresh-contents)
  (package-install 'use-package))

Theme und Schrift

(load-theme 'wombat)

Und die Schrift:

(add-to-list 'default-frame-alist '(font . "DejaVu Sans Mono-16"))

Keine Backupdateien

Mich stören die in der Grundeinstellung automatisch erstellten Backup- und Autosave-Dateien. Also, hinfort damit:

(setq make-backup-files nil) ;; stop creating those backup~ files
(setq auto-save-default nil) ;; stop creating those #autosave# files

Kleine Komfortfunktionen

;; ausgewählten Text bei Eingabe löschen
(delete-selection-mode 1) 

;; keine "yes-or-no"-Fragen - "y-or-n" reicht aus
(defalias 'yes-or-no-p 'y-or-n-p)

;; Zusammengehörende Klammern hervorheben
(show-paren-mode 1)
;; Text zwischen den Klammern nicht hervorheben
(setq show-paren-style 'parenthesis)

;; Aktuelle Spaltennummer in der Mode Line anzeigen
(column-number-mode nil)

Which Key

Auch sehr praktisch: Popups mit Erläuterungen zu Tastenkombinationen, Beispiel C-x und dann warten.

(use-package which-key
  :ensure t 
  :config
  (which-key-mode))

Buffer- und Window-Management

Schöneres Springen in andere Windows mit C-x o (Anzeige der Nummer zum Direktanspringen).

(use-package ace-window
  :ensure t
  :init
  (progn
    (global-set-key [remap other-window] 'ace-window)
    (custom-set-faces
     '(aw-leading-char-face
       ((t (:inherit ace-jump-face-foreground :height 3.0)))))))

Und:

;; Buffer Menu
(global-set-key (kbd "C-x C-b") 'buffer-menu)

Neotree auf <F1>

Die Breite des Neotree-Fensters ist mir in der Grundeinstellung zu schmal.

(use-package neotree
  :ensure t
  :config (setq neo-window-width 40))

(define-key global-map (kbd "<f1>") 'neotree)

Schönere mode-line mit powerline

(use-package powerline
  :ensure t)
(require 'powerline)
(powerline-default-theme)

Ido-Mode und Smex

Smex is a M-x enhancement for Emacs. Built on top of IDO, it provides a convenient interface to your recently and most frequently used commands. And to all the other commands, too.

(ido-mode t)
(setq ido-enable-flex-matching t) ;; fuzzy matching is a must have
(setq ido-enable-last-directory-history nil) ;; forget latest selected directory names

(use-package smex
  :ensure t
  :bind (("M-x" . smex))
  :config (smex-initialize))

;; SMEX
(global-set-key (kbd "M-x") 'smex)
(global-set-key (kbd "M-X") 'smex-major-mode-commands)
(global-set-key (kbd "C-c C-c M-x") 'execute-extended-command) ;; This is your old M-x:

Boxquote

(use-package boxquote
  :ensure t)

Mit boxquote-region Text so hübsch „wrappen“:

,----
| tls-cipher DHE-RSA-AES256-SHA
| cipher AES-256-CBC
`----

Graphische Elemente

Die folgenden Einstellungen sollen nur dann greifen, wenn die graphische Version des Emacs gestartet wird.

(if window-system
    (progn
      ;; Frame Size and Position at Startup
      (add-to-list 'default-frame-alist '(height . 36))
      (add-to-list 'default-frame-alist '(width . 110))
      (setq initial-frame-alist '((left . 220) (top . 65)))
      ;; Klick-Kram entfernen:
      (tool-bar-mode -1)
      (scroll-bar-mode -1)))

;; Das Menü soll immer ausgeblendet sein
(menu-bar-mode -1)

Magit

Magit ist eine Oberfläche für die Versionsverwaltung Git. Eine Killer-App des Emacs.

(use-package magit
  :ensure t
  :config (setq magit-display-buffer-function  ;; Make Magit Fullscreen
                (lambda (buffer)
                  (if magit-display-buffer-noselect
                    ;; the code that called `magit-display-buffer-function'
                    ;; expects the original window to stay alive, we can't go
                    ;; fullscreen
                    (magit-display-buffer-traditional buffer)
                    (delete-other-windows)
                    ;; make sure the window isn't dedicated, otherwise
                    ;; `set-window-buffer' throws an error
                    (set-window-dedicated-p nil nil)
                    (set-window-buffer nil buffer)
                    ;; return buffer's window
                    (get-buffer-window buffer)))))

(global-set-key "\C-xg" 'magit-status)

Org-mode

Grundkonfiguration

(use-package org
  :mode (("\\.\\(org\\|org_archive\\)$" . org-mode))
  :ensure org-plus-contrib
  :config 
  (progn
    (setq org-hide-leading-stars 'hidestars)
    (setq org-return-follows-link t)
    (setq org-drawers (quote ("PROPERTIES" "CLOCKTABLE" "LOGBOOK" "CLOCK")))
    (setq org-completion-use-ido t)
    (setq org-tags-exclude-from-inheritance '("review"))))

Um meine Einstellungen ausführlich kommentieren zu können, definiere ich die weiteren Konfigurationen außerhalb der use-package-Definition.

HTMLize

(use-package htmlize
  :ensure t)

Org-download

org-download - Drag and drop images to Emacs org-mode.

(use-package org-download
  :ensure t)

Capture

Der Org-mode verfügt über eine sehr einfache Funktion, um schnell neue Aufgaben, Notizen und ähnliche Dinge zu erfassen, ohne dass man erst viel in seinen Dateien navigieren müsste oder in seiner anderen Arbeit im Emacs unterbrochen würde. Wenn man beispielsweise gerade im Emacs einen Text schreibt, ein Anruf kommt und eine Notiz erfassen möchte, so kann man durch Eingabe von C-c c einen Buffer aufrufen, in dem man diese erfasst und wenn man dann C-c C-c betätigt, wird die Notiz an der vorgesehenen Stelle gespeichert und man befindet sich wieder im ursprünglichen Textdokument, welches man bearbeitet. Auf diese Weise können Unterbrechnungen des Arbeitsflusses durch Anrufe, eigene spontane Ideen und anderes gering gehalten werden.

(setq org-capture-templates
      '(("t" "Aufgabe in tasks.org" entry (file+headline "~/projects/org/tasks.org" "Inbox")
         "* TODO %?")
        ("w" "Waiting For Reply (Mail)" entry (file+headline "~/projects/org/tasks.org" "Inbox") 
         "* WAITING Antwort auf %a")
        ("m" "Aufgabe aus Mail" entry (file+headline "~/projects/org/tasks.org" "Inbox") 
         "* TODO %? , Link: %a")
        ("z" "Zeiteintrag in tasks.org" entry (file+headline "~/projects/org/tasks.org" "Inbox")
         "* ZKTO %? \n  %i" :clock-in t :clock-resume t)
        ("c" "Contacts" entry (file "~/projects/org/contacts.org")
         "* %(org-contacts-template-name) \n :PROPERTIES: %(org-contacts-template-email) \n :BIRTHDAY: \n :END:")
        ("j" "Journal" entry (file+datetree "~/projects/org/journal.org")
         "* %?\nEntered on %U\n  %i")
        ("p" "password" entry (file "~/projects/org/passwords.gpg")
         "* %^{Title}\n  %^{PASSWORD}p %^{USERNAME}p")))

;; Automatische Anpassung des Links zu einer E-Mail.
;; Alle Mails werden im Folder 'Archive' gespeichert.
(add-hook 'org-capture-prepare-finalize-hook 'hs/search)
(defun hs/search ()
  (interactive)
  (goto-char 1)
  (replace-string "INBOX" "Archive"))

Häufig erreichen mich neue Aufgaben per E-Mail. Dann kann es praktisch sein, einen Link zu dieser E-Mail mit der Aufgabe zu speichern. Es gibt mittlerweile Lösungen für die verschiedensten E-Mail-Programme. Als Emacs-Geek verwendet man natürlich ohnehin ein E-Mail-Programm direkt im Emacs; es gibt aber auch Erweiterungen für externe Programme. Der Org mode unterstützt nativ eine Reihe dieser Programme wie VM, Wanderlust, Gnus, notmuch oder MH-E. Siehe dazu die Sektion External Links in der Org mode Dokumentation. Die nötigen Befehle sind zu finden im Kapitel Handling Links.

  • Mit C-c l (org-store-link) wird der Link zu der aktuellen Nachricht in die Zwischenablage kopiert.
  • Nach Eingabe von C-c c kann dann mit C-c C-l (org-insert-link) der Link eingefügt werden.

Stadien von Aufgaben

In der Grundeinstellung verfügt der Org-mode lediglich über die Todo-Stadien TODO und DONE. Ich verwende ein paar mehr:

Eine „normale“ Aufgabe kann die offenen Stadien TODO, STARTED, WAITING, DELEGATED und die geschlossenen Stadien DONE oder CANCELED haben. Daneben habe ich für Termine APPT, für Projekte PROJ und für Zeitkonten ZKTO vorgesehen.

;; Ein "!" bedeutet Zeitstempel
;; Ein "@" bedeutet Notiz
(setq org-todo-keywords
 '((sequence "TODO(t)" "STARTED(s!)" "WAITING(w@/!)" "APPT(a)" "PROJ(p)" "NOTIZ(n)" "BESPROCHEN(b)"
             "DELEGATED(g@/!)" "|" "DONE(d!)" "ZKTO(z)" "CANCELED(c@)")))

;; Farben anpassen
(setq org-todo-keyword-faces
      '(("TODO"  . (:foreground "#b70101" :weight bold))
        ("STARTED"  . (:foreground "#b70101" :weight bold))
        ("APPT"  . (:foreground "sienna" :weight bold))
        ("PROJ"  . (:foreground "lightblue" :weight bold))
        ("NOTIZ"  . (:foreground "yellow" :weight bold))
        ("ZKTO"  . (:foreground "orange" :weight bold))
        ("WAITING"  . (:foreground "orange" :weight bold))
        ("BESPROCHEN"  . (:foreground "darkorange" :weight bold))
        ("REVIEWED"  . (:foreground "forestgreen" :weight bold))
        ("DONE"  . (:foreground "forestgreen" :weight bold))
        ("DELEGATED"  . (:foreground "forestgreen" :weight bold))
        ("CANCELED"  . shadow)))

;; Fast TODO Selection
(setq org-use-fast-todo-selection t)

Logging

Einen Zeitstempel eintragen, wenn eine Aufgabe als erledigt markiert wird.

(setq org-log-done 'time)

Einen eigenen Drawer benutzen:

(setq org-log-into-drawer t)

Agenda

Allgemeines

;; Aktuelle Zeile in der Agenda hervorheben
(add-hook 'org-agenda-mode-hook '(lambda () (hl-line-mode 1 )))

(setq org-agenda-format-date
 "%Y-%m-%d ---------------------------------------------------------------------")

;; Tasks mit Prioriäten unterschiedlich darstellen:
(setq org-agenda-fontify-priorities
   (quote ((65 (:foreground "Red")) (66 (:foreground "Blue")) (67 (:foreground "Darkgreen")))))

(setq org-agenda-date-weekend (quote (:foreground "Yellow" :weight bold)))

;; Tasks mit Datum in der Agenda ausblenden, wenn sie bereits erledigt sind:
(setq org-agenda-skip-deadline-if-done t)
(setq org-agenda-skip-scheduled-if-done t)

Nach der Methode Getting Things Done gibt es Aufgaben, die man zwar erfasst, die man aber nicht zu einem bestimmten Zeitpunkt oder in absehbarer Zukunft erledigen möchte. Diese Aufgaben tagge ich mit „someday“. Sie werden in der Standardansicht meiner Agenda ausgeblendet und ich kann sie nur (im Rahmen des Weekly-Review) mit einer speziellen Agenda-Ansicht ausdrücklich anzeigen lassen.

(setq org-agenda-filter-preset '("-someday"))

Die Agenda nicht in der Wochenansicht, sondern Tagesansicht starten:

(setq org-agenda-span 1)

Diary einbeziehen

(setq org-agenda-include-diary t)

Eigene Agenda-Ansichten

(setq org-agenda-custom-commands
      (quote (
              ("g" . "GTD-Workflow")
              ("gn" "Next Actions" tags-todo "next" ((org-use-tag-inheritance nil)))
              ("gd" "DONE" tags-todo "done" ((org-use-tag-inheritance nil)))
              ("gw" "Waiting" todo "WAITING")
              ("gs" "SOMEDAY" tags "someday" ((org-agenda-filter-preset 
                                               '("+someday"))(org-agenda-todo-ignore-with-date nil)))
              ("gz" "ZKTO" tags "zkto")
              ("gf" "Agenda + flagged + Zeitkonten"
               ((tags "zkto")
                (todo "WAITING")
                (todo "DELEGATED")
                (tags "flagged")
                (agenda "")))
              ("p" . "People")
              ("pb" "Stefan" tags-todo "stefan" ((org-use-tag-inheritance nil)))
              ("w" . "Privates")
              ("wo" "Org-Mode-Konfiguration" tags-todo "orgmode" ((org-use-tag-inheritance nil))))))

Agenda-Files

(setq org-agenda-files (quote 
   ("~/projects/org/tasks.org"
    "~/projects/org/journal.org"
    "~/projects/emacs-config/emacs-config.org"
    "~/projects/org/notes/")))

Agenda soll in ganzem Frame starten

(setq org-agenda-window-setup 'current-window)

Agenda log-mode

In der Agenda kann man mit l den log-mode aktivieren. Dann werden die geloggten Aktivitäten des jeweiligen Tages angezeigt.

  • Einfach Eingabe von l startet den log-mode in der Grundeinstellung: Closed und Clocked Tasks
  • C-u l zeigt alles: Closed, Clocked und State-Änderungen (z.B. auf Waiting gesetzt)
  • C-u C-u l blendet die offenen Tasks aus und zeigt nur die Logging-Informationen
;;(setq org-agenda-log-mode-items '(closed state))
(setq org-agenda-log-mode-items '(closed clock))

Clocking

Eine sehr interessante und praktische Funktion ist die Möglichkeiten, Zeiten für die einzelnen Aufgaben zu erfassen und beliebige Auswertungen der aufgezeichneten Zeiten zu generieren.

Die Zeiteinträge sollen in einem eigenen Drawer abgelegt werden:

(setq org-clock-into-drawer "CLOCK")

Das Ein- und Ausclocken erfolgt über F5 und F8 oder I und O direkt in der Agenda.

(fset 'my-clock-in "\C-c\C-x\C-i")
(global-set-key (kbd "<f5>") 'my-clock-in)

(fset 'my-clock-out "\C-c\C-x\C-o")
(global-set-key (kbd "<f8>") 'my-clock-out)

Die folgende Einstellung sorgt dafür, dass der Org-mode dann, wenn ein Task mit „clocknote“ getaggt ist, beim Ausclocken eine Notiz abfragt. Ich nutze dieses dafür, um eine nähere Beschreibung zu Zeiten, die ich auf Zeitkonten wie „Allgemeine Verwaltung“ buche, einzugeben. Auf diese Weise muss ich nicht immer dann, wenn ich eine Kleinigkeit erledige, einen neuen Zeiteintrag mit Hilfe der Capture-Funktion erstellen. Ich habe also die folgenden Varianten der Generierung von Zeiteinträgen zur Verfügung:

  1. Es besteht bereits eine Aufgabe in meiner Aufgabenverwaltung, die ausreichend konkret ist und auf die ich daher „clocken“ kann.
  2. Es besteht ein Zeitkonto wie „Allgemeine Verwaltung“, auf das ich clocken kann, bei dem ich jedoch besser eine Notiz eingeben sollte, was ich genau gemacht habe.
  3. Es handelt sich um eine nicht zuordnenbare Aufgabe. Diese kann ich über die Capture-Funktion mit dem Zeiteintrags-Template erstellen.
(defun hs/check-for-clock-out-note()
      (interactive)
      (save-excursion
        (org-back-to-heading)
        (let ((tags (org-get-tags)))
          (and tags (message "tags: %s " tags)
               (when (member "clocknote" tags)
                 (org-add-note))))))

(add-hook 'org-clock-out-hook 'hs/check-for-clock-out-note)

Weitere Einstellungen:

;; Resume clocking tasks when emacs is restarted
;;(org-clock-persistence-insinuate)

;; Yes it's long... but more is better ;)
(setq org-clock-history-length 35)

;; Resume clocking task on clock-in if the clock is open
(setq org-clock-in-resume t)

;; Change task state to STARTED when clocking in
;(setq org-clock-in-switch-to-state "STARTED")

;; Sometimes I change tasks I'm clocking quickly
;; this removes clocked tasks with 0:00 duration
;;(setq org-clock-out-remove-zero-time-clocks t)

;; Don't clock out when moving task to a done state
(setq org-clock-out-when-done nil)

;; Save the running clock and all clock history when exiting Emacs,
;; load it on startup
(setq org-clock-persist t)

;; Disable auto clock resolution
(setq org-clock-auto-clock-resolution nil)

(setq org-global-properties (quote (("Effort_ALL" .
                                "0:10 0:30 1:00 2:00 3:00 4:00 5:00 6:00 8:00"))))

Nachdem ich lange Zeit mit Clocktables gearbeitet hatte, habe ich dann doch noch herausgefunden, dass man sich die aufgezeichneten Zeiten auch in der Agenda anzeigen lassen kann. Darüber hatte ich zwar schon häufiger nachgedacht, dass das eine nette Sache wäre, aber mir ist nie aufgefallen, dass es auch diese tolle Funktion bereits gibt.

;; Agenda direkt mit dem jeweiligen Clock-Report starten:
(setq org-agenda-start-with-clockreport-mode t)

;; Auch die Zeit einer aktuell laufenden Uhr einbeziehen:
(setq org-clock-report-include-clocking-task t)

;; Keine Links, maximal bis Level 3 herunter:
;;(setq org-agenda-clockreport-parameter-plist (quote (:link nil :maxlevel 3)))
(setq org-agenda-clockreport-parameter-plist
      (quote (:link t :maxlevel 4 :fileskip0 t :compact t :narrow 80)))

Die Clocking-Tabelle in der Agenda kann mit "R" aus- und wieder eingeblendet werden.

Source Code Blocks und Babel

Folgende Sprachen in Babel aktivieren:

(org-babel-do-load-languages
   'org-babel-load-languages
   '((emacs-lisp . t)
     (python . t)
     (shell . t)
     (js . t)
     (latex . t)
     (org . t)
     (dot . t)
     (gnuplot . t)
     (ruby . t)
     (screen . nil)
     (ledger . t)
     (C . t)
     (sql . t)
     (ditaa . t)))

Syntax Highlighting in Source Code Blocks:

(setq org-src-fontify-natively nil)

Wenn man mit Sprachen wie Python arbeitet, bei denen die Einrückung des Codes „Erklärungswert“ hat, sind folgende Einstellungen sinnvoll:

(setq org-edit-src-content-indentation 0)
(setq org-src-tab-acts-natively t)
(setq org-src-preserve-indentation t)

Column-View

Der Org-mode verfügt mit der Column-View über eine spezielle Ansicht, die meines Wissens nach einer Funktion der Aufgabenverwaltung Omnifocus for Mac nachgebildet wurde.

(setq org-columns-default-format
      "%80ITEM(Task) %10Effort(Effort){:} %10CLOCKSUM")

Inline Tasks

Führt man einfache Aufgabenlisten, dann eignen sich die „normalen“ Org mode Tasks sehr gut. Verwendet man den Org mode allerdings dazu, umfangreichere Notizen oder Texte zu schreiben, die man mit dem Org mode als Outliner strukturiert, kollidiert dies mit der Definition von Aufgaben in diesen Dokumenten. Was man dann benötigt, ist eine Möglichkeit, Aufgaben zu definieren, die keinen Einfluss haben auf die Strukturelemente. Und dies sind die sog. Inline Tasks.

;;(use-package org-inlinetask
;;  :bind (:map org-mode-map
;;              ("C-c C-x t" . org-inlinetask-insert-task))
;;  :after (org)
;;  :commands (org-inlinetask-insert-task)
;;  :config (setq org-inlinetask-export t)) 

(require 'org-inlinetask)

Ein Beispiel:

TODO Eine Beispielaufgabe

Dies ist ein Inline Task. Er wird grundsätzlich genauso behandelt wie ein „normaler“ Tasks, allerdings wird er nicht mit in das Visibility Cycling mit einbezogen.

Export

HTML

Damit die Source Code Blocks im Export mit Syntax Highlighting versehen werden:

;;(use-package htmlize
;;  :ensure t
;;  :config (setq org-html-htmlize-output-type nil)) ;; standard: inline-css

Ich deaktiviere htmlize zwischendurch, da ich org-notes für meine Website ohne Syntax Highlighthing exportieren möchte. Mein Custom Exporter versieht die Source Code Blocks statt dessen mit einer der Sprache entsprechenden Markierung, die von highlight.js erkannt wird. Das Syntax Highlighthing auf meiner Website wird dann durch highlight.js im Webbrowser des Betrachters vorgenommen.

Und nun Custom CSS:

(setq org-html-head "<style type='text/css'>
 body {
     font-family: Helvetica, Arial, sans-serif;
     text-align: left;
     width: 920px;
     padding: 10px;
     padding-top: 10px;
     line-height: 1.5em;
     margin: 0 auto;
     font-size: 18px;
     line-height: 1.6em;
     color: #505050;
     hyphens: auto;
 }

 .title {
     padding-bottom: 20px;
 }

pre {
     color: #c6c6c6; 
     background-color: #292929;
     padding: 0.5em;
     font-size: 16px;
 }

 code {
     padding: 2px 4px;
     font-size: 90%;
     color: #c7254e;
     background-color: #f9f2f4;
     border-radius: 4px;
 }

 pre.src {
     padding: 0.5em;
 }

 table, th, td {
     border: 1px solid #b1b1b1;
 }

 th {
     background: #dadada;
 }

 .PROJ { color: blue; }

 .WAITING { color: orange; }

 .org-ul p { color: grey; margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; }

 #postamble {
      margin-top: 10px;       
      margin-left: -10px;       
      font-size: 10pt;
      line-height: 13px; 
      background-color: #eeeeee;
      border: 1px solid #cccccc;
      padding: 5px;
 }

  #postamble a {
          display: none; 
  }

  #postamble .creator a {
          display: inline; 
          color: black;
          text-decoration: none;
  }

  </style>")

org-reveal

Reveal.js ist eine gute Möglichkeit, auf einfache Weise ansprechende Präsentationen zu erstellen. Mit org-reveal wird es noch besser.

(use-package ox-reveal
  :ensure t)

LaTeX => PDF

Es folgt eine angepasste LaTeX-Vorlage. Verwendet werden kann sie so:

#+TITLE: Titel
#+LaTeX_CLASS: myscrartcl

(add-to-list 'org-latex-classes
  '("myscrartcl"
    "\\documentclass{scrartcl}
     \\usepackage[utf8]{inputenc}
     \\usepackage[T1]{fontenc}
     \\usepackage{listings,xcolor}
     \\usepackage{hyperref}
     \\usepackage[scaled=.90]{helvet}  % Helvetica für Überschriften
     \\usepackage{mathptmx}            % Times für den Fließtext
     \\renewcommand{\\contentsname}{Inhaltsverzeichnis}
     \\hypersetup{unicode=false,colorlinks=true,linkcolor=blue,filecolor=cyan,citecolor=green,urlcolor=magenta}
     [NO-DEFAULT-PACKAGES][NO-PACKAGES]"

    ("\\section{%s}" . "\\section*{%s}")
    ("\\subsection{%s}" . "\\subsection*{%s}")
    ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
    ("\\paragraph{%s}" . "\\paragraph*{%s}")
    ("\\subparagraph{%s}" . "\\subparagraph*{%s}")))

;; PDFs visited in Org-mode are opened in Evince (and not in the default choice) https://stackoverflow.com/a/8836108/789593
(add-hook 'org-mode-hook
      '(lambda ()
         (delete '("\\.pdf\\'" . default) org-file-apps)
         (add-to-list 'org-file-apps '("\\.pdf\\'" . "evince %s"))))

Open Document Text (ODT)

(require 'ox-odt)
(setq org-odt-preferred-output-format "docx")

Ditaa

Org-Support für Ditaa.

(setq org-ditaa-jar-path "/usr/bin/ditaa")

GnuPG-Verschlüsselung

Die folgenden Zeilen sorgen dafür, dass der Emacs alle Dateien mit der Endung .gpg verschlüsselt speichert. Beim Öffnen werden sie direkt wieder entschlüsselt.

(require 'epa-file)
(epa-file-enable)

Wenn man nur einzelne Bereiche einer Org mode Datei verschlüsseln möchte, braucht man zusätzlich org-crypt. Diese Erweiterung verschlüsselt den Text unter einer Überschrift beim Speichern, wenn der Tag crypt vergeben wurde. Erst durch den Befehl org-decrypt-entries bzw. org-decrypt-entry werden die entsprechenden Inhalte wieder entschlüsselt.

;(require 'org-crypt)
;(org-crypt-use-before-save-magic)
;(setq org-tags-exclude-from-inheritance (quote ("crypt")))
     
;(setq org-crypt-key nil)
;; GPG key to use for encryption
;; Either the Key ID or set to nil to use symmetric encryption.
     
;(setq auto-save-default nil)
;; Auto-saving does not cooperate with org-crypt.el: so you need
;; to turn it off if you plan to use org-crypt.el quite often.
;; Otherwise, you'll get an (annoying) message each time you
;; start Org.
     
;; To turn it off only locally, you can insert this:
;;
;; # -*- buffer-auto-save-file-name: nil; -*-

Fußnoten

Fußnoten definiere ich direkt im Text.

(setq org-footnote-define-inline t)

org-contacts

(use-package org-contacts
  :config
  (setq org-contacts-files '("~/projects/org/contacts.org")))

Refiling Tasks

;; Targets include this file and any file contributing to the agenda - up to 5 levels deep
;;(setq org-refile-targets (quote ((org-agenda-files :maxlevel . 5) (nil :maxlevel . 5))))

;; Targets start with the file name - allows creating level 1 tasks
(setq org-refile-use-outline-path (quote file))

;; Targets complete in steps so we start with filename, TAB shows the next level of targets etc
(setq org-outline-path-complete-in-steps t)

Private Website mit Org und Django

Meine private Website basiert auf Python und Django. Texte, die ich in der Rubrik „GNU Emacs“ pflege, schreibe ich im Emacs org-mode – das finde ich praktischer als sie in HTML oder einem WYSIWYG-Editor zu schreiben. Für den Export verwende ich einen angepassten Exporter, der die org-mode-Dateien in Django-Templates schreibt.

(add-to-list 'load-path "~/projects/emacs-config/")
(require 'ox-django-website)

Um nicht jede einzelne Datei manuell in HTML exportieren zu müssen, verwende ich ox-publish.

(require 'ox-publish)
(setq org-publish-project-alist
      '(
        ("org-notes"
         :base-directory "~/projects/org-notes/"
         :base-extension "org"
         :publishing-directory "~/projects/website/website/apps/core/templates/core/emacs/"
         :publishing-function org-twbs-publish-to-django-html
         )
        ("emacs-config"
         :base-directory "~/projects/emacs-config/"
         :base-extension "org"
         :publishing-directory "~/projects/website/website/apps/core/templates/core/emacs/"
         :publishing-function org-twbs-publish-to-django-html
         )))

;;; Switches off use of time-stamps when publishing. I would prefer to publish
;;; everything every time
(setq org-publish-use-timestamps-flag nil)

Softwareentwicklung

Allgemeines

(use-package auto-complete
  :ensure t)
(use-package projectile
  :ensure t)
(use-package paredit
  :ensure t)

Webentwicklung mit Web-mode

(use-package web-mode
  :ensure t
  :config
  (progn
    (add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode))
    (setq web-mode-engines-alist
          '(("django"    . "\\.html\\'")))
    (setq web-mode-ac-sources-alist
          '(("css" . (ac-source-css-property))
            ("html" . (ac-source-words-in-buffer ac-source-abbrev))))
    (setq web-mode-markup-indent-offset 2)
    (setq web-mode-code-indent-offset 2)
    (setq web-mode-css-indent-offset 2)
    (setq web-mode-enable-current-element-highlight t)
    (setq web-mode-enable-auto-closing t)
    (setq web-mode-enable-auto-quoting t)
    (setq web-mode-enable-auto-pairing t)
    (setq web-mode-enable-auto-expanding t)
    (setq web-mode-enable-css-colorization t)))

Golang

(use-package go-mode
  :ensure t)

Python- und Django-Entwicklung

Es gibt viele unterschiedliche Modi und Pakete für die Arbeit mit Python-Dateien. Ein einfacher Python-Modus ist bereits im Standardumfang des Emacs enthalten.

Grundsätzliches

(use-package epc
  :ensure t)
(use-package jedi
  :ensure t)
(use-package anaconda-mode
  :ensure t)
(use-package company
  :ensure t)
(use-package company-anaconda
  :ensure t)

(global-font-lock-mode t)
(setq font-lock-maximum-decoration t)

;; no tabs but four spaces for indentation
(setq-default indent-tabs-mode nil)
(setq default-tab-width 4)

Anaconda Mode

Derzeit verwende ich den anaconda-mode.

(add-hook 'python-mode-hook 'anaconda-mode)
(add-hook 'python-mode-hook 'eldoc-mode)

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

;; company-anaconda
(eval-after-load "company"
 '(progn
   (add-to-list 'company-backends 'company-anaconda)))

;; start in python mode
(add-hook 'python-mode-hook 'anaconda-mode)

E-Mails

Mit den Jahren sind eine Reihe von MUAs für den Emacs entwickelt worden. Ich verwende notmuch.

Absenderinformationen

(setq user-full-name "Hendrik Sünkler"
      user-mail-address "mailbox@suenkler.info")

Weitere Header-Zeilen:

(setq message-default-headers 
   (concat "X-OS: Debian GNU/Linux"))

Synchronisation

Ich halte meine E-Mails zentral auf dem IMAP-Server meines Providers und synchronisiere den Account mit einem lokalen Maildir-Verzeichnis. Auf dieses Verzeichnis lasse ich dann das Programm notmuch los. Als MUA verwende ich den mit notmuch mitgelieferten MUA notmuch.el.

Meine ~/.mbsyncrc:

IMAPAccount suenkler-info
Host imap.example.com
User XXXXXXXXX
Pass XXXXXXXXX
CertificateFile /etc/ssl/certs/ca-certificates.crt

IMAPStore suenkler-info-remote
Account suenkler-info

MaildirStore suenkler-info-local
Path ~/.mail/suenkler-info/
Inbox ~/.mail/suenkler-info/INBOX
Trash trash

Channel suenkler-info-folders
Master :suenkler-info-remote:
Slave :suenkler-info-local:
Patterns *
Create Both
Expunge Both
SyncState *

Group suenkler-info
Channel suenkler-info-folders

Mit folgenem Skript stoßte ich die Synchronisation an:

#!/bin/bash

# Sync mail when online
if ping -c1 8.8.8.8 &>/dev/null; then

    # Sync Maildir
    /usr/bin/mbsync suenkler-info

    # Index Mail
    /usr/local/bin/notmuch new
fi

Und dieses Skript soll alle fünf Minuten ausgeführt werden:

$ crontab -l
*/5 * * * * /home/hendrik/bin/sync_mail.sh > /dev/null

Notmuch

Die Distributionspakete von notmuch sind in der Regel recht alt, daher habe ich das Programm aus den Quellen übersetzt. Auch der Emacs-MUA ist im Source-Paket enthalten. Achtung: Nicht die Version aus dem Emacs-Paketmanagement installieren; die MUA-Version muss mit der CLI-Version übereinstimmen; ab besten scheint daher die Installation direkt aus den Quellen.

Bei Aufruf von ./configure zeigt sich ggf., dass nicht alle Abhängigkeiten erfüllt sind. Die Fehlermeldung hat aber eine schöne Auflistung der Abhängigkeiten samt apt-Befehl zur Installation unter Debian-artigen Betriebssystemen geliefert. Anschließend ließ sich notmuch kompilieren.

Nun den MUA im Emacs einbinden:

(add-to-list 'load-path "~/bin/notmuch-0.24.2/emacs/")
(require 'notmuch)

(global-set-key "\C-xm" 'notmuch)

SMTP

E-Mails versende ich über das Emacs-eigene SMTP-Modul. Bei mir hat das bisher immer sehr gut funktioniert. Wenn man etwas mehr möchte, bietet sich beispielsweise msmtp an.

(use-package smtpmail
   :config
   (setq
    user-full-name "Hendrik Sünkler"
    smtpmail-local-domain "suenkler.info"
    user-mail-address (concat "mailbox@" smtpmail-local-domain)
    send-mail-function 'smtpmail-send-it
    smtpmail-smtp-server "smtp.suenkler.info"
    smtpmail-stream-type 'starttls
    smtpmail-smtp-service 587))

Notizen mit Deft

Deft manuell laden

Die Version von deft in den ELPA-Repositories funktionierte bei mir nicht korrekt. Daher lade ich deft manuell:

(add-to-list 'load-path "~/projects/emacs-config/lib/")
(require 'deft)

Konfiguration

(use-package deft
;;  :ensure t
  :config (setq deft-directory "~/projects/org/notes/")
  (setq deft-strip-title-regexp "\\(?:^%+\\|^#\\+TITLE: *\\|^#\\+COMMENT: *\\|^[#* ]+\\|-\\*-[[:alpha:]]+-\\*-\\|^Title:[  ]*\\|#+$\\)")
  (setq deft-directory "~/projects/org/notes/")
  (setq deft-text-mode 'org-mode)
  (setq deft-extension "org")
  (setq deft-extensions '("org"))
  (setq deft-default-extension "org")
  (setq deft-use-filename-as-title nil)
  (setq deft-use-filename-as-title nil)
  (setq deft-use-filter-string-for-filename nil)
  (setq deft-use-filter-string-for-filename nil))

(defun show-notes ()
  (interactive)
  (hs-kill-buffers "*Deft*")
  (when deft-directory "~/projects/bep/org/"
        (setq-default deft-directory "~/projects/org/notes/"))
  (deft))

(defun show-website ()
  (interactive)
  (hs-kill-buffers "*Deft*")
  (setq-default deft-directory "~/projects/org-website/")
  (deft))

(defun show-itsec-notes ()
  (interactive)
  (hs-kill-buffers "*Deft*")
  (setq-default deft-directory "~/projects/org/itsec-notes/")
  (deft))

(defun show-programming-notes ()
  (interactive)
  (hs-kill-buffers "*Deft*")
  (setq-default deft-directory "~/projects/org/programming-notes/")
  (deft))

;; Deft mit Notizen auf C-c n
(global-set-key "\C-cn" 'show-notes)
(global-set-key "\C-cw" 'show-website)

;; Deft mit programming notes auf C-c p
(global-set-key "\C-cp" 'show-programming-notes)
(global-set-key "\C-ci" 'show-itsec-notes)

;; Start Deft in insert-mode
;;(evil-set-initial-state 'deft-mode 'insert)

Wenn man Deft mit C-c C-q beendet, besteht ein Buffer mit dem Namen „Deft“ fort. Solange dieser Buffer existent ist, greift die Neudefinition insbesondere des Verzeichnisses (deft-directory) nicht. Daher wird kill-matiching-buffers verwendet. Die Emacs-Funktion kill-matching-buffers erwartet allerdings eine Bestätigung durch den Anwender (yes or no), wenn der betreffende Buffer geändert wurde. Dies ist beim Deft-Buffer stets der Fall. Die folgende Funktion ermöglicht das Schließen von Buffern ohne eine solche Abfrage – und wird in den oben definierten Funktionen verwendet.

(require 'cl)
(defun hs-kill-buffers (regexp)
  "Kill buffers matching REGEXP without asking for confirmation."
  (interactive "sKill buffers matching this regular expression: ")
  (flet ((kill-buffer-ask (buffer) (kill-buffer buffer)))
    (kill-matching-buffers regexp)))

Unused files can be archived by pressing C-c C-a. Files will be moved to deft-archive-directory, which is a directory named `archive` within your deft-directory by default.

Es ergibt sich dann jedoch das Problem, wie man bei Bedarf die archivierten Notizen durchsuchen kann. Die Lösung scheint eine rekursive Suche:

By default, Deft only searches for files in deft-directory but not in any subdirectories. Set deft-recursive to a non-nil value to enable recursive searching for files in subdirectories:

(setq deft-recursive t)

Finanzbuchhaltung mit Ledger

(use-package ledger-mode
  :ensure t
  :mode "\\.ledger\\'"
  :config
  (define-key ledger-mode-map (kbd "C-c c") 'ledger-mode-clean-buffer)
  (setq ledger-post-amount-alignment-at :decimal
        ledger-post-amount-alignment-column 49
        ledger-clear-whole-transactions t)
  (use-package flycheck-ledger))

Weitere Keyboard Shortcuts

;; Emacs-Konfigurationsdatei auf C-c e
(global-set-key "\C-ce" '(lambda ()
                           (interactive)
                           (find-file "~/projects/emacs-config/emacs-config.org")))

;; Tasks-Datei auf C-c g
(global-set-key "\C-cg" '(lambda ()
                           (interactive)
                           (find-file "~/projects/org/tasks.org")))

;; Übersichtsseite auf C-c s
(global-set-key "\C-cs" '(lambda ()
                           (interactive)
                           (find-file "~/projects/org/start.org")))

;; Org Store Link
(global-set-key "\C-cl" 'org-store-link)

;; Agenda
(global-set-key "\C-ca" 'org-agenda)

;; Meine Hauptagenda auf F12
(defun org-agenda-show-myagenda (&optional arg)
  (interactive "P")
  (org-agenda arg "gf"))
(global-set-key (kbd "<f12>") 'org-agenda-show-myagenda)

;; Org Capture
(define-key global-map "\C-cc" 'org-capture)
(global-set-key (kbd "<f9>") 'org-capture)

Automatische stündliche Commits

Damit ich nicht bei jeder Kleinigkeit manuell „committen“ muss, habe ich automatische stündliche Commits konfiguriert.

Zunächst sollen alle orgmode-Buffer immer eine Minute vor jeder vollen Stunde gespeichert werden, damit sie in die Versionsverwaltung eingecheckt werden können:

(run-at-time "00:59" 3600 'org-save-all-org-buffers)

Und hier das Shellscript:

#!/bin/sh
# Add org file changes to the repository
REPOS="org emacs-config"

for REPO in $REPOS
do
    echo "Repository: $REPO"
    cd ~/projects/$REPO
    # Remove deleted files
    git ls-files --deleted -z | xargs -0 git rm >/dev/null 2>&1
    # Add new files
    git add . >/dev/null 2>&1
    git commit -m "$(date)"
done

Über den folgenden Crojob wird das Script zu jeder vollen Stunde ausgeführt:

0 * * * * ~/bin/org-git-sync.sh >/dev/null

Secrets

Ich habe ein paar zusätzliche, vertrauliche Einstellungen, die ich nicht in dieser öffentlich geteilten Fassung wiedergeben möchte. Sie finden sich in der Datei secret.el, die nicht mit in die Versionsverwaltung eingecheckt wird.

(require 'secret)

Finale

Beim Start des Emacs soll die Datei tasks.org geladen werden.

;; Keinen Splash-Screen
(setq inhibit-splash-screen t)

(find-file "~/projects/org/tasks.org")