Meine Konfiguration des GNU Emacs

Letzte Aktualisierung: 29. Januar 2017

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.

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/")))
(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

Ich mag meinen Emacs wie ich meinen Kaffee trinke: schwarz. Kleiner Scherz; ich bin Teetrinker.

(load-theme 'wombat)

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 Apaltennummer 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

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

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 . 50))
      (add-to-list 'default-frame-alist '(width . 150))
      (setq initial-frame-alist '((left . 250) (top . 120)))
      ;; Klick-Kram entfernen:
      (tool-bar-mode -1)
      (menu-bar-mode -1)
      (scroll-bar-mode -1)))

Magit

Magit ist eine Oberfläche für die Versionsverwaltung Git.

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

Flycheck

Flycheck is a modern on-the-fly syntax checking extension for GNU Emacs.

(use-package flycheck
  :ensure t
  :init
  (global-flycheck-mode t))

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.

Org-bullets

Ich verwende org-bullets, damit die Sternchen vor Org mode Tasks netter aussehen:

(use-package org-bullets
  :ensure t
  :config
  (add-hook 'org-mode-hook (lambda () (org-bullets-mode 1))))

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 möglichst gering gehalten werden.

(setq org-capture-templates
      '(("t" "Aufgabe in tasks.org" entry (file+headline "/media/hendrik/STICK/projects/org/tasks.org" "Inbox")
         "* TODO %?")
        ("w" "Waiting For Reply (Mail)" entry (file+headline "/media/hendrik/STICK/projects/org/tasks.org" "Inbox") 
         "* WAITING Antwort auf %a")
        ("m" "Aufgabe aus Mail" entry (file+headline "/media/hendrik/STICK/projects/org/tasks.org" "Inbox") 
         "* TODO %? , Link: %a")
        ("z" "Zeiteintrag in tasks.org" entry (file+headline "/media/hendrik/STICK/projects/org/tasks.org" "Inbox")
         "* ZKTO %? \n  %i" :clock-in t :clock-resume t)
        ("c" "Contacts" entry (file "/media/hendrik/STICK/projects/org/contacts.org")
         "* %(org-contacts-template-name) \n :PROPERTIES: %(org-contacts-template-email) \n :BIRTHDAY: \n :END:")
        ("j" "Journal" entry (file+datetree "/media/hendrik/STICK/projects/org/journal.org")
         "* %?\nEntered on %U\n  %i")
        ("p" "password" entry (file "/media/hendrik/STICK/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 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 
   ("/media/hendrik/STICK/projects/org/tasks.org"
    "/media/hendrik/STICK/projects/org/journal.org"
    "/media/hendrik/STICK/projects/bep/beptechdoc/beptechdoc.org"
    "/media/hendrik/STICK/projects/bep/bepdevdoc/bepdevdoc.org"
    "/media/hendrik/STICK/projects/bep/bepnotes.org"
    "/media/hendrik/STICK/projects/emacs-config/emacs-config.org"
    "/media/hendrik/STICK/projects/org/notes/")))

Daneben habe ich noch weitere Dateien in die Agenda einbezogen, in denen jeweils projektspezifische Inhalte untergebracht sind. Diese werden hier nicht mit ausgegeben.

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. Per SSH clocke ich nur in der Agenda. Dort kann I und O verwendet werden.

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

;; Clock out with F8:
(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 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:

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

Syntax highlighthing 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))

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)

Ich habe htmlize derzeit deaktiviert, 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;
 }

 .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

(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}")))

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 "/home/hendrik/bin/ditaa.jar")

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 '("/media/hendrik/STICK/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 „Notizen“ 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 "/media/hendrik/STICK/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 "/media/hendrik/STICK/projects/org-notes/"
         :base-extension "org"
         :publishing-directory "/media/hendrik/STICK/projects/website/website/apps/core/templates/core/notes/"
         :publishing-function org-twbs-publish-to-django-html
         )
        ("emacs-config"
         :base-directory "/media/hendrik/STICK/projects/emacs-config/"
         :base-extension "org"
         :publishing-directory "/media/hendrik/STICK/projects/website/website/apps/core/templates/core/notes/"
         :publishing-function org-twbs-publish-to-django-html
         )))

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)))

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)

Elpy

Folgende Pakete sind erforderlich:

$ pip install rope
$ pip install jedi
$ pip install flake8
$ pip install importmagic
(use-package elpy
  :ensure t
  :defer 2
  :config
  (progn
    ;; Use Flycheck instead of Flymake
    (when (require 'flycheck nil t)
      (remove-hook 'elpy-modules 'elpy-module-flymake)
      (remove-hook 'elpy-modules 'elpy-module-yasnippet)
      (remove-hook 'elpy-mode-hook 'elpy-module-highlight-indentation)
      (add-hook 'elpy-mode-hook 'flycheck-mode))
    (elpy-enable)
    ;; jedi is great
    (setq elpy-rpc-backend "jedi")))

E-Mails

Absenderinformationen

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

Bei Bedarf weitere Header-Zeilen:

;;(setq message-default-headers 
;;   (concat "X-Whatever: "))

E-Mail Client: mu4e

Initialisierung

Nach der Installation des aktuellen Tarballs steht der Client unter dem folgenden Verzeichnis zur Verfügung:

(add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu4e")
(require 'mu4e)

Konfiguration

  • Allgemeines

    Mein Shortcut für den Aufruf des E-Mail-Clients lautet C-x m.

    (global-set-key "\C-xm" 'mu4e)
    
  • Maildir
    ;; Only needed if your maildir is _not_ ~/Maildir
    ;; Must be a real dir, not a symlink
    ;(setq mu4e-maildir "~/Maildir")
    
    ;; these must start with a "/", and must exist
    ;; (i.e.. /home/user/Maildir/sent must exist)
    ;; you use e.g. 'mu mkdir' to make the Maildirs if they don't
    ;; already exist
    
    ;; below are the defaults; if they do not exist yet, mu4e offers to
    ;; create them. they can also functions; see their docstrings.
    (setq mu4e-sent-folder   "/sent")
    (setq mu4e-drafts-folder "/drafts")
    (setq mu4e-trash-folder  "/trash")
    
  • Orgmode-Integration
    ;; org integration
    (require 'org-mu4e)
    
  • HTML-Mails

    Einfache HTML-Mails können auch im Emacs direkt betrachtet werden. Komplexere HTML-Mails sind aber sehr unschön im Emacs zu betrachten. Daher möchte ich diese HTML-Mails auch in einem externen, graphischen, Webbrowser öffnen können:

    (add-to-list 'mu4e-view-actions
      '("ViewInBrowser" . mu4e-action-view-in-browser) t)
    

    Wichtig: Hier ist daran zu denken, dass in der Nachricht enthaltene Bilder und JavaScript-Code im Browser geladen bzw. ausgeführt werden! Das kann ein Sicherheitsrisiko sein. Es ist daher eine gute Idee, ein gesondertes Browser-Profil zu nutzen, welches problematische Inhalte ignoriert. Ich habe dazu im Firefox ein entsprechendes Profil secure angelegt. Mit den folgenden Einstellungen bringe ich den Emacs dazu, dieses Profil zu nutzen.

    ;;(setq browse-url-browser-function 'browse-url-generic
    ;;      browse-url-generic-program "/usr/bin/firefox"
    ;;      browse-url-generic-args '("-P" "secure"))
    

SMTP

E-Mails werden direkt dem SMTP-Server meines Providers übergeben. Diese Einstellungen sind unabhängig von der Wahl des Clients (gnus oder mu4e).

(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 Repositories funktionierte bei mir nicht korrekt. Daher lade ich deft manuell:

(add-to-list 'load-path "/media/hendrik/STICK/projects/emacs-config/lib/")  ;; Pfad, in dem oben die Datei deft.el gespeichert wurde
(require 'deft)

Konfiguration

(use-package deft
  :ensure t
  :config (setq deft-directory "/media/hendrik/STICK/projects/org/notes/")
  (setq deft-strip-title-regexp "\\(?:^%+\\|^#\\+TITLE: *\\|^#\\+COMMENT: *\\|^[#* ]+\\|-\\*-[[:alpha:]]+-\\*-\\|^Title:[  ]*\\|#+$\\)")
  (setq deft-directory "/media/hendrik/STICK/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 "/media/hendrik/STICK/projects/bep/org/"
        (setq-default deft-directory "/media/hendrik/STICK/projects/org/notes/"))
  (deft))

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

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

(defun show-bep-documentation ()
  (interactive)
  (hs-kill-buffers "*Deft*")
  (setq-default deft-directory "/media/hendrik/STICK/projects/bep/docs/")
  (deft))

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

(defun show-programming-notes ()
  (interactive)
  (hs-kill-buffers "*Deft*")
  (setq-default deft-directory "/media/hendrik/STICK/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 Weblog-Posts auf C-c w
(global-set-key "\C-cb" 'show-bep-projects)
(global-set-key "\C-cd" 'show-bep-documentation)

;; 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)))

Bei der IT-Dokumentation verwende ich eine zentrale Datei mit Includes vieler kleiner einzelner Dateien, in denen ich die Inhalte stehen habe. In der Einzeldateien steht dann kein Titel mehr am Anfang, sondern lediglich der mit „#+COMMENT:“ auskommentierte Titel. Damit dieses Keyword nicht in der Deft-Übersicht angezeigt wird, habe ich in der Variable deft-strig-title-regexp dieses Schlüsselwort ergänzt:

(setq deft-strip-title-regexp "\\(?:^%+\\|^#\\+TITLE: *\\|^#\\+COMMENT: *\\|^[#* ]+\\|-\\*-[[:alpha:]]+-\\*-\\|^Title:[	 ]*\\|#+$\\)")

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 "/media/hendrik/STICK/projects/emacs-config/emacs-config.org")))

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

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

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

;; Agenda
(global-set-key "\C-ca" 'org-agenda)
(global-set-key (kbd "<f12>") 'org-agenda)

;; 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 /media/hendrik/STICK/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 "/media/hendrik/STICK/projects/org/tasks.org")