My Emacs Configuration

Table of Contents

1. Introduction

Some people have a Zen garden, I have my Emacs configuration.

I am far from being an Emacs or Elisp expert, but I love investing time into fiddling with the editor, note-taker and task manager of my choice.

I created this documentation in order to become more structured in my approach - and also learn bit about literate programming. I decided to invest even more time into this and move all my setup into .org files. These files serve as source of the actual Elisp configuration files as well as for this documentation and even generate some fancy illustrations.

Of course, this is a work in progress.

2. General Configuration

I use Spacemacs in evil mode and this configuration does not list all my packages yet.

These are my settings that are called in dotspacemacs/user-config. This function is called at the very end of Spacemacs initialization after layers configuration.

2.1. General

Setting the default directory to my user home.

(setq default-directory "~/")

Hitting TAB always just indents the current line.

(setq tab-always-indent t)

Only one auth source for magit & co.

(setq auth-sources '("~/.netrc"))

Utf-8

(prefer-coding-system 'utf-8)

Enable line wrapping

(setq truncate-lines nil)

Enable key frequency tracking

;; (setq truncate-lines nil)
;; (keyfreq-mode 1)
;; (keyfreq-autosave-mode 1)
;; (setq keyfreq-excluded-commands
;;       '(self-insert-command))

2.2. Appearance

Clean up spaceline contents.

(with-eval-after-load 'spaceline-segments
  (spaceline-toggle-minor-modes-off)
  (spaceline-toggle-buffer-size-off))

2.3. Major Mode Settings

2.3.1. Magit

Add ioki-github.com to browsable URLs, therefore also create custom formatters, based on the normal github formatter.

This allows to visit links via SPC g o

(with-eval-after-load 'browse-at-remote

  (add-to-list 'browse-at-remote-remote-type-regexps '("^ioki-github\\.com$" . "ioki-github"))

  (defun browse-at-remote--format-region-url-as-ioki-github (repo-url location filename &optional linestart lineend)
    "URL formatted for github."
    (setq repo-url (s-replace "ioki-github" "github" repo-url))

    (cond
     ((and linestart lineend)
      (format "%s/blob/%s/%s#L%d-L%d" repo-url location filename linestart lineend))
     (linestart (format "%s/blob/%s/%s#L%d" repo-url location filename linestart))
     (t (format "%s/tree/%s/%s" repo-url location filename))))

  (defun browse-at-remote--format-commit-url-as-ioki-github (repo-url commithash)
    "Commit URL formatted for github"
    (setq repo-url (s-replace "ioki-github" "github" repo-url))
    (format "%s/commit/%s" repo-url commithash))

  )

Enable private Gitlab instance in magit forge

(with-eval-after-load 'forge
  (add-to-list 'forge-alist '("gitlab.io.ki" "gitlab.io.ki/api/v4" "gitlab.io.ki" forge-gitlab-repository))
)
(with-eval-after-load 'browse-at-remote
  (add-to-list 'browse-at-remote-remote-type-regexps '("^gitlab\\.io\\.ki$" . "gitlab"))
  )

2.3.2. LSP

Disable documentation overlays, use , h h instead.

(setq lsp-ui-doc-enable nil)

Disable file watches, as many folders will slow Emacs down.

(setq lsp-enable-file-watchers nil)

2.3.3. Ruby

Don't automatically insert the magic encoding comment.

(setq ruby-insert-encoding-magic-comment nil)

Keybinding to toggle between new and old hash syntax.

(evil-leader/set-key
  "xrh" 'ruby-toggle-hash-syntax)

Underscore should be a word delimiter in slim.

(add-hook 'slim-mode-hook #'(lambda () (modify-syntax-entry ?_ "_")))

2.3.4. Elixir

Call elixir-format before save.

(add-hook 'elixir-mode-hook
          (lambda () (add-hook 'before-save-hook 'elixir-format nil t)))

Keybindings for Emacs ExUnit test runner.

(with-eval-after-load 'elixir-mode
  (spacemacs/declare-prefix-for-mode 'elixir-mode
    "mt" "tests" "testing related functionality")
  (spacemacs/set-leader-keys-for-major-mode 'elixir-mode
    "ta" 'exunit-verify-all
    "tb" 'exunit-verify
    "tr" 'exunit-rerun
    "tt" 'exunit-verify-single))

Pin the exunit window to the bottom.

(push '("*exunit-compilation*"
        :dedicated t
        :position bottom
        :stick t
        :height 0.4
        :noselect t)
      popwin:special-display-config)

2.3.5. Go

Set tab width to 4.

(setq-default tab-width 4)
(setq-default go-tab-width 4)

Call go-format before save.

(setq go-format-before-save t)

Enable flycheck for Go mode.

(add-hook 'go-mode-hook
          (lambda () (flycheck-mode 1)))

2.3.6. Web Mode

Intendation settings

(setq web-mode-css-indent-offset 2)
(setq js2-basic-offset 2)
(setq web-mode-markup-indent-offset 2)
(setq web-mode-code-indent-offset 2)
(setq css-indent-offset 2)
(setq-default js2-basic-offset 2
              js-indent-level 2)

2.3.7. JavaScript

Allow .dir-locals.el files to set prettier-related settings.

(put 'prettier-js-args 'safe-local-variable 'listp)
(put 'prettier-js-command 'safe-local-variable 'stringp)

Setup nodejs-repl.el keybindings.

(spacemacs/set-leader-keys-for-major-mode 'js2-mode "ne" 'nodejs-repl-send-last-expression)
(spacemacs/set-leader-keys-for-major-mode 'js2-mode "nl" 'nodejs-repl-send-line)
(spacemacs/set-leader-keys-for-major-mode 'js2-mode "nr" 'nodejs-repl-send-region)
(spacemacs/set-leader-keys-for-major-mode 'js2-mode "nb" 'nodejs-repl-send-buffer)
(spacemacs/set-leader-keys-for-major-mode 'js2-mode "nf" 'nodejs-repl-load-file)
(spacemacs/set-leader-keys-for-major-mode 'js2-mode "n'" 'nodejs-repl-switch-to-repl)
(spacemacs/set-leader-keys-for-major-mode 'js2-mode "ns" 'nodejs-repl-switch-to-repl)

2.3.8. TypeScript

Set indentation level to two spaces.

(setq typescript-indent-level 2)

2.3.9. SQL

The sqlfmt does not work very well with Postgres and is also outdated. This uses pgFormatter instead, installed with brew install pgformatter.

(setq sqlfmt-executable "pg_format")
(setq sqlfmt-options '())

2.4. Keybindings

2.4.1. Movements

Make evil-mode up/down operate in screen lines instead of logical lines.

(define-key evil-motion-state-map "j" 'evil-next-visual-line)
(define-key evil-motion-state-map "k" 'evil-previous-visual-line)

Also in visual mode…

(define-key evil-visual-state-map "j" 'evil-next-visual-line)
(define-key evil-visual-state-map "k" 'evil-previous-visual-line)

Move line under cursor with C-j/k.

(define-key evil-normal-state-map (kbd "C-j") 'drag-stuff-down)
(define-key evil-normal-state-map (kbd "C-k") 'drag-stuff-up)

Pressing H in any edit mode moves the cursor to the first non-blank character.

(evil-global-set-key 'normal "H" 'evil-first-non-blank)
(evil-global-set-key 'visual "H" 'evil-first-non-blank)
(evil-global-set-key 'motion "H" 'evil-first-non-blank)

Pressing L in any edit mode moves the cursor to the end of line.

(evil-global-set-key 'normal "L" (lambda () (interactive) (evil-end-of-line)))
(evil-global-set-key 'visual "L" (lambda () (interactive) (evil-end-of-line)))
(evil-global-set-key 'motion "L" (lambda () (interactive) (evil-end-of-line)))

Type g l to get a fast home row friendly jump menu to go to a visible line.

(define-key evil-motion-state-map "gl" 'evil-avy-goto-line)
(define-key evil-normal-state-map "gl" 'evil-avy-goto-line)

Type g o <char> <char> to get a fast home row friendly jump menu to go to a visible word that starts with these characters.

(define-key evil-motion-state-map "go" 'evil-avy-goto-char-2)
(define-key evil-normal-state-map "go" 'evil-avy-goto-char-2)

2.4.2. Macros

Type Q to execute the macro recorded to q.

(evil-global-set-key 'normal (kbd "Q") (lambda () (interactive) (evil-execute-macro 1 "@q")))

2.4.3. Dired

In dired, move to parent directory with h and open thing under cursor with l.

(with-eval-after-load 'dired
  (evil-define-key 'normal dired-mode-map
    "h" 'dired-up-directory
    "l" 'dired-find-file
    )
  )

2.4.4. Umlauts

Make Umlauts work like in the rest of MacOS.

(global-unset-key (kbd "M-s"))
(global-set-key (kbd "M-s")
                (lambda ()
                  "Insert ß."
                  (interactive)
                  (insert "ß")))
(global-unset-key (kbd "M-u"))
(global-set-key (kbd "M-u a")
                (lambda ()
                  "Insert ä."
                  (interactive)
                  (insert "ä")))
(global-set-key (kbd "M-u A")
                (lambda ()
                  "Insert Ä."
                  (interactive)
                  (insert "Ä")))
(global-set-key (kbd "M-u o")
                (lambda ()
                  "Insert ö."
                  (interactive)
                  (insert "ö")))
(global-set-key (kbd "M-u O")
                (lambda ()
                  "Insert Ö."
                  (interactive)
                  (insert "Ö")))
(global-set-key (kbd "M-u u")
                (lambda ()
                  "Insert ü."
                  (interactive)
                  (insert "ü")))
(global-set-key (kbd "M-u U")
                (lambda ()
                  "Insert Ü."
                  (interactive)
                  (insert "Ü")))

2.4.5. Misc

Set hippie expand from M-/ to ctrl-space.

(global-set-key (kbd "C-SPC") 'hippie-expand)

2.5. Emojis

Overwrite emoji settings.

(defun --set-emoji-font (frame)
  "Adjust the font settings of FRAME so Emacs can display emoji properly."
  (if (eq system-type 'darwin)
      ;; For NS/Cocoa
      (set-fontset-font t 'symbol (font-spec :family "Apple Color Emoji" :size 10) frame 'prepend)
    ;; For Linux
    (set-fontset-font t 'symbol (font-spec :family "Symbola") frame 'prepend)))

;; For when Emacs is started in GUI mode:
(--set-emoji-font nil)
;; Hook for when a frame is created with emacsclient
;; see https://www.gnu.org/software/emacs/manual/html_node/elisp/Creating-Frames.html
(add-hook 'after-make-frame-functions '--set-emoji-font)
(setq company-emoji-insert-unicode t)

2.6. Custom Functions

Helper functions that I call directly with SPC SPC.

2.6.1. Switch between Rails i18n files

This function switches between the German and the English translation file in a Rails project. Especially handy, if the project has a lot of files per language.

(defun switch-rails-i18n-file()
  "Switches to the i18n file in the other language"
  (interactive)
  (if (search "/de/" buffer-file-name)
    (find-file (replace-regexp-in-string "/de/" "/en/" (buffer-file-name)))
    (find-file (replace-regexp-in-string "/en/" "/de/" (buffer-file-name))))
  )

2.6.2. Exercism

Submits the current buffer to exercism and opens a temp buffer with the output and some additional information like the URL of the current practice.

(defun exercism-submit ()
  (interactive)
  (with-output-to-temp-buffer "*exercism*"
    (princ(shell-command-to-string (concat "exercism submit " (buffer-file-name))))
    )
  (pop-to-buffer "*exercism*"))

Fix the result buffer to the bottom and allow to close it with ctrl-g.

(push '("*exercism*"
        :dedicated t
        :position bottom
        :stick t
        :height 0.4
        :noselect t)
      popwin:special-display-config)

2.7. Finalization

In the end, satisfy the Spacemacs loading mechanism.

(provide 'my-general-config)

3. Org Mode

My personal Org mode configuration, as I am a daily user of Org mode and put quite some time into sharpening this tool.

This is an approach to put my whole Org mode configuration into one .org file.

org_screenshot_project.png

3.1. Task States

Right now, all my TODO-related files include the same task states, defined in a common setup file.

task_states.png

3.1.1. Logging Task State change

Changes to task states might get logged, especially for recurring routines. If so, log them in a drawer, not the content of the note.

(setq org-log-state-notes-into-drawer t)

3.2. Environment

The directory that contains the .org files is not only synced by a cloud service across devices, it is also a git repository that auto-commits on every save of a buffer. This is activated by a .dir_locals.el file with the following content:

((nil . ((eval git-auto-commit-mode 1))))

3.3. Initialization

This enables buffer face mode for the Org agenda views.

While Org mode needs a lot of custom styling to work in variable-pitch-mode I take the easy way out in the agenda view by choosing the monospaced version of the font: iA Writer Mono S. It is way easier to align the ASCII tables of agenda with a font with a fixed pitch.

(defun my-org-config/setup-buffer-face ()
  (setq buffer-face-mode-face '(:family "iA Writer Mono S"))
  (buffer-face-mode)
  )
(add-hook 'org-agenda-mode-hook 'my-org-config/setup-buffer-face)

Once Org mode loaded, turn on olivetti, hide tilde fringes and enable visual line mode.

(defun my-org-config/after-org-mode-load ()
  (visual-line-mode)
  (vi-tilde-fringe-mode -1)

  (require 'org-indent)
  (org-indent-mode)
  (set-face-attribute 'org-indent nil :inherit '(org-hide fixed-pitch))

  (variable-pitch-mode 1)

  (require 'olivetti)
  (olivetti-mode)
  )

(add-hook 'org-mode-hook 'my-org-config/after-org-mode-load)

Save all Org buffers after archiving, as this will trigger the auto-commit of the git repo the Org files live in.

(defun my-org-config/after-org-archive ()
  (org-save-all-org-buffers))

(add-hook 'org-archive-hook 'my-org-config/after-org-archive)

Within a recurring task, reset contained check boxes when task is done if RESET_CHECK_BOXES property is set to t.

(require 'org-checklist)

3.4. Quick Access

Quick access to my most important org functions is given by opening the menu SPC o.

It is not really needed/working to declare this prefix, as the prefix o is reserved for user bindings anyways.

(spacemacs/declare-prefix "o" "org mode")

You can view my daily agenda with SPC o d.

(defun my-org-daily-agenda ()
  (interactive)
  (org-agenda nil "d")
  )

(spacemacs/set-leader-keys "od" 'my-org-daily-agenda)

Add a new todo with preselected template with SPC o t.

(defun my-org-add-todo ()
  (interactive)
  (org-capture nil "t")
  )
(spacemacs/set-leader-keys "ot" 'my-org-add-todo)

Add a new feedback note entry with preselected template with SPC o f.

(defun my-org-add-feedback ()
  (interactive)
  (org-capture nil "f")
  )
(spacemacs/set-leader-keys "of" 'my-org-add-feedback)

Add a new notable change/fact entry with preselected template with SPC o n.

(defun my-org-add-to-changelog ()
  (interactive)
  (org-capture nil "n")
  )
(spacemacs/set-leader-keys "on" 'my-org-add-to-changelog)

Call org-capture with SPC o c.

(spacemacs/set-leader-keys "oc" 'org-capture)

Call org-agenda with SPC o a.

(spacemacs/set-leader-keys "oa" 'org-agenda)

Call org-store-link with SPC o l.

(spacemacs/set-leader-keys "ol" 'org-store-link)

Experimental: Open a mini buffer to search through all org file names.

Not sure how much sense this makes, as I want the selected file to be opened in the proper perspective. Also, maybe org-rifle makes more sense?

(defun my-org-helm-find-file ()
  (interactive)
  (helm-browse-project-find-files "/Users/fabrik42/org")
  )

(spacemacs/set-leader-keys "oj" 'my-org-helm-find-file)

3.5. Files

file_setup.png

I use a general inbox file to collect all new tasks on the run and will batch-schedule/refile them a couple times a day.

Inbox and mobile inbox co-exist to prevent sync conflicts when adding tasks while having no internet connection. This works pretty well and I treat them equally in the agenda views.

(defvar org-my-inbox-file "~/org/inbox.org")
(defvar org-my-mobile-inbox-file "~/org/inbox_mobile.org")

Default note file, that will also be used for capturing new notes.

(setq org-default-notes-file org-my-inbox-file)

Work-related tasks and notes.

(defvar org-my-general-files "~/org")

Private tasks and notes.

(defvar org-my-projects-dir "~/org/projects")

The files to be used for agenda display. This contains:

  • Task inbox file
  • Mobile task inbox file
  • Work tasks file

Note: Right now, I would like to have TODOs in my project files as well. However, this does not play well with beorg, as the app only allows one org directory without subfolders. :(

(add-to-list 'org-agenda-files org-my-general-files)
(add-to-list 'org-agenda-files org-my-projects-dir)

Refile targets are all agenda files, plus my project files. I fine-tune the considered headings to prevent human error when choosing the new location.

(setq org-refile-targets (quote (
                                 (org-agenda-files :maxlevel . 2)
                                 )))

3.6. Capture

Store new notes at the beginning of a file or entry.

(setq org-reverse-note-order t)

These are my custom capture templates.

(setq org-capture-templates '(("t" "Todo [inbox]"
                                entry
                                (file "~/org/inbox.org")
                                "* TODO %?\n:PROPERTIES:\n:CREATED: %U\n:END:\n  %i\n")
                              ("n" "Notable change/fact"
                                entry
                                (file "~/org/changelog.org")
                                "* %? \n:PROPERTIES:\n:CREATED: %U\n:END:\n  %i\n")
                              ("l" "Link currently stored [inbox]"
                                entry
                                (file "~/org/inbox.org")
                                "* TODO %i%?\n:PROPERTIES:\n:CREATED: %U\n:END:\n\%A\n%i\n")
                              ("f" "Feedback note"
                                entry
                                (file "~/org/feedback.org")
                                "* Feedback for: %^{prompt}\n:PROPERTIES:\n:CREATED: %U\n:END:\n- [ ] %?\n\n")))

3.7. Agenda

Enable org-super-agenda mode.

(org-super-agenda-mode)

Disable the super agenda header map.

(setq org-super-agenda-header-map nil)

Show warnings for deadlines 7 days in advance.

(setq org-deadline-warning-days 7)

Use a straight line as separator for between agenda blocks. See Unicode/UTF-8-character table.

(setq org-agenda-block-separator 9472)

Don't show scheduled items in agenda when they are in a DONE state.

(setq org-agenda-skip-scheduled-if-done t)

Agenda view starts today and +7 days.

(setq org-agenda-start-on-weekday nil)

3.7.1. Custom Agenda Views

Custom agenda commands used to generate my agenda views.

Experimental: New and better agenda views, powered by org-super-agenda.

((agenda "" ((org-agenda-span 'day)
             (org-agenda-compact-blocks t)
             ;; (org-agenda-deadline-leaders)
             ;; (org-agenda-scheduled-leaders)
             (org-agenda-prefix-format '(
                                         (agenda . "  %?-12t")
                                         ))
             (org-super-agenda-groups
              '(
                (:name "⏰ Calendar" :time-grid t)
                (:name "Optional" :priority "C" :order 90)
                (:name "📚 Tickets" :category "Tickets" :order 80)

                (:name "⚠ Overdue!" :deadline past)
                (:name "⚠ Overdue!" :scheduled past)

                ;; Discard full-day events from agenda
                (:discard (:category "Cal"))

                (:name "⭐ Next" :todo "NEXT")
                (:name "⭐ Important" :priority "A")
                (:name "📌 Routines" :category "Routines")

                (:auto-category t)
                ))
             ))
 (alltodo "" ((org-agenda-overriding-header "")
              (org-agenda-prefix-format '(
                                          (todo . "  ")
                                          ))
              (org-super-agenda-groups
               '(
                 (:name "🌆 End of day" :todo "ENDOFDAY")
                 (:name "Inbox" :tag "inbox")
                 (:name "Verify" :todo "VERIFY")
                 (:discard (:anything t))
                 )
               ))))

This used to be my main agenda view for my work at ioki.

I switched between:

  • Viewing only the current day for focus on today's tasks
  • Viewing all week for scheduling tasks
  • Enabling Log mode to view recently completed tasks (today and yesterday)
((agenda "" (
             (org-agenda-overriding-header "THIS WEEK")
             (org-agenda-span 'day)
             (org-agenda-scheduled-leaders '("   " "%2dx"))
             ))
 (tags "+inbox"
       ((org-agenda-overriding-header "INBOX: Entries to refile")))
 (todo "VERIFY"
       ((org-agenda-overriding-header "FINAL VERIFICATION PENDING")))
 )

This view is used by me whenever I clean up my Org files. It lists completed tasks that I can archive then, as well tasks then are uncomplete, but without a scheduled date.

I don't use it that often as I should, as I still have no satisfying way of batch-archiving entries.

(
 (todo "DONE"
       (
        (org-agenda-overriding-header "DONE!")
))
 (todo "CANCELLED"
       ((org-agenda-overriding-header "CANCELLED")))
 (todo "TODO"
       ((org-agenda-overriding-header "TODO Items (without time attached)")
        (org-agenda-skip-function '(org-agenda-skip-entry-if 'deadline 'scheduled 'timestamp))))
 (todo "WAIT"
       ((org-agenda-overriding-header "WAIT: Items on hold (without time attached)")
        (org-agenda-skip-function '(org-agenda-skip-entry-if 'deadline 'scheduled 'timestamp))))
 )

Map the custom agenda commands to keys.

(setq org-agenda-custom-commands '(
    ("h" "IOKI DASHBOARD"
     <<agenda-view-ioki>>
     )
    ("w" "WEEKLY REVIEW"
     <<agenda-view-weekly>>
     )
    ("d" "DAILY"
     <<agenda-view-daily>>
     )
    ))

3.8. Appearance

Show the filename and outline path in helm when refiling an entry. Also refile in one step (makes much more sense for helm).

(setq org-refile-use-outline-path 'file)
(setq org-outline-path-complete-in-steps nil)

The header line appears, optionally, at the top of a window, analogous to mode line. Hack to give some vertical space at the top of each buffer.

(setq header-line-format " ")

Folding symbol for the headings.

(setq org-ellipsis " … ")

Show actually italicized text instead of /italicized text/.

(setq org-hide-emphasis-markers t)

Fontify (aka "highlight, change appearance via font settings") the whole line for headings. This is useful when setting a background color for the org-level-* faces.

(setq org-fontify-whole-heading-line t)

Change the face of a headline if it is marked DONE. Normally, only the TODO/DONE keyword indicates the state of a headline. When this is non-nil, the headline after the keyword is set to the org-headline-done as an additional indication.

;; (setq org-fontify-done-headline t)

Add a special face to #+begin_quote and #+begin_verse block.

(setq org-fontify-quote-and-verse-blocks t)

Set bullet glyphs for Org headings via org-superstar-mode.

(setq org-superstar-headline-bullets-list '("⬢" "◆" "▲" "■"))

Show tags directly after headings (not on the right), which plays nicer with line-wrapping.

(setq org-tags-column 0)

Set a wider body witdh for olivetti-mode.

(setq olivetti-body-width 81)

3.9. Faces

Faces for TODO states. The colors are based on the Dracula theme for Emacs.

(let* (
       (comment      "#6272a4")
       (warning      "#ffb86c")
       (rainbow-1    "#f8f8f2")
       (rainbow-2    "#8be9fd")
       (rainbow-3    "#bd93f9")
       (rainbow-4    "#ff79c6")
       (rainbow-5    "#ffb86c")
       (rainbow-6    "#50fa7b")
       (rainbow-7    "#f1fa8c")
       (rainbow-8    "#0189cc")
       (rainbow-9    "#ff5555")
       (rainbow-10   "#a0522d")

       (variable-pitch-font `(:family "iA Writer Quattro S" ))
       (fixed-pitch-font    `(:family "Fira Mono" ))
       (fixed-pitch-font-alt `(:family "iA Writer Mono S" )))

  (setq org-todo-keyword-faces (list
                              `("TODO"
                                ,@fixed-pitch-font
                                :foreground ,comment
                                :weight bold
                                )
                              `("NEXT"
                                ,@fixed-pitch-font
                                :foreground ,warning
                                :weight bold)
                              `("WAIT"
                                ,@fixed-pitch-font
                                :foreground ,rainbow-2
                                :weight bold)
                              `("VERIFY"
                                ,@fixed-pitch-font
                                :foreground ,rainbow-7
                                :weight bold)
                              `("ENDOFDAY"
                                ,@fixed-pitch-font
                                :foreground ,rainbow-3
                                :weight bold)
                              `("LOWPRIO"
                                ,@fixed-pitch-font
                                :foreground ,comment
                                :weight bold)
                              `("DONE"
                                ,@fixed-pitch-font
                                :foreground ,rainbow-6
                                :weight bold)
                              `("CANCELLED"
                                ,@fixed-pitch-font
                                :foreground ,rainbow-9
                                :weight bold)
                              ))
)

3.10. Babel

Syntax highlightning in code blocks

(setq org-src-fontify-natively t)

Trying to fix indentation behaviour within code blocks.

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

Allow babel code execution without confirming it every time.

(setq org-confirm-babel-evaluate nil)

Available embedded languages for babel.

(org-babel-do-load-languages 'org-babel-load-languages
                              '((sql . t)
                                (shell . t)
                                (dot . t)
                                (emacs-lisp . t)
                                (ruby . t)
                                (js . t)
                                (jq . t)
                                (mermaid . t)
                                (plantuml . t)))

PlantUML settings for generating diagrams. It needs to know the path to the installed PlantUML jar, in this case installed via homebrew, so this path is available by calling brew info plantuml.

(setq org-plantuml-jar-path "/usr/local/Cellar/plantuml/1.2018.3/libexec/plantuml.jar")

Mermaid settings for generating diagrams. It needs to know the path to the installed mermaid cli, in this case installed via homebrew, so this path is available by calling brew info mermaid-cli.

(setq ob-mermaid-cli-path "/opt/homebrew/bin/mmdc")

3.11. Export

Do not inline CSS code when generating HTML exports. Instead, the CSS of the chosen theme will be used. This especially applies to fontified code blocks.

(setq org-html-htmlize-output-type 'css)

3.12. Attachments

Org download should use the attachment features to save the images.

(setq org-download-method 'attach)

3.13. Encryption

The package org-crypt allows to encrypt subtrees using GPG.

(require 'org-crypt)

Prevent the crypt tag from using inheritance so that there is no encrypted data inside encrypted data.

(setq org-tags-exclude-from-inheritance (quote ("crypt")))

Set GPG key to use for encryption

(setq org-crypt-key "821280F4")

Encrypt all entries before saving.

(org-crypt-use-before-save-magic)

Disable auto-save-mode for the current buffer prior to decrypting an entry.

(setq org-crypt-disable-auto-save t)

Set crypt as default tag available in Org files.

(setq org-tag-alist '(("crypt" . ?c)))

Add keybindings for encrypting and decrypting a subtree.

(spacemacs/set-leader-keys-for-major-mode
  'org-mode "se" 'org-encrypt-entry)

(spacemacs/set-leader-keys-for-major-mode
  'org-mode "sd" 'org-decrypt-entry)

3.14. Misc Keybindings

Allows to change the TODO state of a task via , k.

(spacemacs/set-leader-keys-for-major-mode
  'org-mode "k" 'org-todo)

Adds org-rich-yank.

(spacemacs/set-leader-keys-for-major-mode
  'org-mode "ir" 'org-rich-yank)

3.15. AI Robo Power

(with-eval-after-load 'org
  (setq org-ai-openai-api-token "OPEN-AI-TOKEN")
  (require 'org-ai)
    (add-hook 'org-mode-hook #'org-ai-mode)
    (org-ai-global-mode)
    (setq org-ai-default-chat-model "gpt-3.5-turbo")
    (org-ai-install-yasnippets)
    )

3.16. Finalization

In the end, satisfy the Spacemacs loading mechanism.

(provide 'my-org-config)

4. Custom Theme

I use a customized Dracula theme that I heavily modified for Org mode, especially the Org agenda views.

My favorite theme is Dracula which I use for a lot of different applications. Of course, there is also a port for Emacs. I use almost all of the given settings, but heavily customized the appearance of Org mode.

4.1. Color Palette

The original color palette from the Dracula theme.

ColorName
fg1
fg2
fg3
fg4
bg1
bg2
bg3
bg4
bg5
bg6
key2
key3
builtin
keyword
const
comment
func
str
type
var
warning
rainbow-1
rainbow-2
rainbow-3
rainbow-4
rainbow-5
rainbow-6
rainbow-7
rainbow-8
rainbow-9
rainbow-10
eph-verbatim
eph-code

4.2. Org Mode Faces

These face settings represent my constant struggle to make Org mode more visually pleasing.

;; org-agenda
(org-agenda-date :foreground ,fg1)
(org-agenda-date-today :foreground ,fg1)
(org-agenda-date-weekend :foreground ,fg3 :weight normal)
(org-agenda-dimmed-todo-face :foreground ,comment)
(org-agenda-done :foreground ,rainbow-6)
(org-agenda-structure :foreground  ,fg3 :background ,bg2 :weight bold)

;; important fix for variable pitch mode
;; otherwise indents like within tables won't work well
;; see https://lepisma.github.io/2017/10/28/ricing-org-mode/
(org-indent
(:inherit org-hide)
(:inherit (org-hide fixed-pitch)))

;; everything related to (code) blocks
(org-block :inherit fixed-pitch :foreground ,rainbow-5)
(org-block-begin-line :inherit fixed-pitch :foreground ,bg4)
(org-block-end-line :inherit fixed-pitch :foreground ,bg4)

(org-checkbox :inherit fixed-pitch :weight bold)
(org-code :foreground ,rainbow-7)
(org-date :foreground ,rainbow-2 :underline t)

(org-column :background ,bg4)
(org-column-title :inherit org-column :weight bold :underline t)

(org-document-info :foreground ,rainbow-8)
(org-document-info-keyword :inherit fixed-pitch :foreground ,bg4)
(org-special-keyword :inherit fixed-pitch :foreground ,bg4)
(org-meta-line :inherit fixed-pitch :foreground ,bg4)
(org-property-value :inherit fixed-pitch :foreground ,bg4)


(org-todo :inherit fixed-pitch :foreground ,rainbow-5 :bold t :background ,bg2)
(org-done :inherit fixed-pitch :foreground ,rainbow-6)
(org-headline-done :foreground ,comment :bold nil :strike-through t)

(org-ellipsis :foreground ,comment)
(org-footnote :foreground ,rainbow-8)
(org-formula :foreground ,rainbow-4)
(org-hide :foreground ,bg1 :background ,bg1)

(org-document-title :inherit bold :height 300)
(org-level-1 :inherit bold :height 1.25)
(org-level-2 :inherit bold :height 1.25)
(org-level-3 :inherit bold :height 1.0)
(org-level-4 :inherit bold)
(org-level-5 :inherit bold)
(org-level-6 :inherit bold)
(org-level-7 :inherit bold)
(org-level-8 :inherit bold)

(org-link :foreground ,rainbow-2 :underline t)
(org-priority :foreground ,rainbow-2)
(org-scheduled :foreground ,fg4)
(org-scheduled-previously :foreground ,rainbow-7)
(org-scheduled-today :foreground ,fg1)
(org-sexp-date :foreground ,fg4)
(org-table :inherit fixed-pitch :foreground ,rainbow-3)


(org-tag :inherit fixed-pitch :foreground ,bg4 :bold t :background ,bg2)
(org-upcoming-deadline :foreground ,rainbow-7)
(org-warning :weight bold :foreground ,rainbow-4)

4.3. Unchanged Faces

The settings directly used from the original Emacs Dracula theme, without any changes from my side.

4.4. Theme Code Generation

This is where it all comes together and gets tangled into a theme file.

5. Deployment of this site

This site is hosted on Netlify, but right now, without the automated git-deploys. I still use the local Netlify CLI to deploy files exported from Emacs.

It can be deployed to a staging environment following command:

netlify deploy

Once ready, it can overwrite the production site providing the prod flag.

netlify deploy --prod

6. Feedback

I am always happy to receive questions and tipps around this document. Feel free to send me a message on Twitter or just send me a mail to fabrik42 /at/ gmail.com.


View Org Source of index.org

Author: Christian Bäuerlein

Created: 2023-10-16 Mon 18:49

Validate