Org-Mode

Org-mode might the emacs killer app. This file holds some extra settings which were not set in the use-package statement in init.el.

General settings

Some general settings for org. TODO: Clean up the use-package definition

(use-package org
  :init ; loaded before package is initialized
  ;; The background color of my org source code blocks needs to be defined before
  ;; org is loaded.
  (defface org-block-begin-line
    '((t (:foreground "#99968b" :background "#303030" :box (:style released-button) :extend t)))
    "Face used for the line delimiting the begin of source blocks.")
  (defface org-block-end-line
    '((t (:foreground "#99968b" :background "#303030" :box (:style released-button) :extend t)))
    "Face used for the line delimiting the end of source blocks.")
  :config
  (require 'org-tempo)
  (setq-default
   org-return-follows-link t
   org-image-actual-width '(800)
   org-highlight-latex-and-related '(latex script entities))
  (add-hook 'org-mode-hook #'org-indent-mode)
  (add-hook 'org-mode-hook #'visual-line-mode)
  (add-hook 'org-mode-hook #'flyspell-mode)
  (set-face-attribute 'org-drawer nil
    :inherit 'font-lock-keyword-face)

  ;; change some keys to be a little more like LaTeX
  (defun do.org/insert-math ()
    (interactive)
    (insert "\\(\\)")
    (backward-char 2))

  (defun do.org/insert-today-inactive ()
    "Insert today's date as inactive time stamp."
    (interactive)
    (org-insert-time-stamp (org-current-time) nil t))

  (defun do.org/time-stamp-inactive ()
    "Insert an inactive timestamp of the user's choosing."
    (interactive)
    (org-time-stamp nil t))

  (defalias 'ts #'do.org/time-stamp-inactive)

  ;; non-breaking spaces
  <<do.org/non-breaking-spaces>>

  ;; fix `electric-pair-mode'
  <<do.org/fix-electric-pair>>

  :bind
  (("C-c l" . #'org-store-link)
   ("C-c t" . #'do.org/insert-today-inactive)
   :map org-mode-map
   ("$" . do.org/insert-math)
   ("~" . do.org/insert-nbsp)))

(use-package org-contrib
  :ensure t)

fixing `electric-pair-mode'

(defun do.org/fix-electric-pair-fn ()
    (setq-local electric-pair-inhibit-predicate
                `(lambda (c)
                   (if (char-equal c ?<) t (,electric-pair-inhibit-predicate c)))))
(add-hook 'org-mode-hook #'do.org/fix-electric-pair-fn)

Syntax highlighting in Code Blocks

Very important if your config file is a .org document… Also, add native <tab> behavior in source blocks.

(setq
 org-src-fontify-natively t
 org-src-tab-acts-natively t)

We also want this in LaTeX output!

(setq org-latex-listings 'minted)

HTML Export

For HTML output, we want to be able to set a custom stylesheet.

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

We need the htmlize package for that

(use-package htmlize
  :ensure t
  :commands (htmlize-buffer
       htmlize-file
       htmlize-many-files
       htmlize-many-files-dired
       htmlize-region))

Static site generation for my website

The code to build my website is in a self-contained .el file in my website's git repo. This makes rebuilind the page using emacs --batch a little easier and does not require me to load the entire config. Whenever I edit the website, I use emacs-async to spawn an external emacs process to rebuild the site. A few commands and keybindings to make this easier follow.

First, I define the paths to my website's base directory and the static site generator script.

(defvar do.website/base-dir nil
  "Path to the base directory of the website.")
(defconst do.website/project-script
  (file-name-concat do.website/base-dir "lisp/project.el")
  "Path to the project.el script containing my static site generator.")

Next I define some helper functions to let me easily publish the website or republish only a single article asynchronously. Note that these commands never actually deploy the website to the web server, unless that was specifically requested

(defun do.website/generate-website (arg)
  "Rebuild the org-publish project only. If prefix ARG is set, force rebuild everything."
  (interactive "P")
  (async-start
   `(lambda ()
      (load-file ,do.website/project-script)
      (generate-website ,arg))
   (lambda (result)
     (beep)
     (message "generate-website: %s" (prin1-to-string result)))))

(defun do.website/make-website (arg)
  "Run the full \"make\" command for the website asynchronously. If prefix ARG is set, deploy the website"
  (interactive "P")
  (let ((buf-name "*Website Build Output*")
  (deploy (and arg (y-or-n-p "You requested to deploy the website after build. Are you sure?"))))
    (async-shell-command
     (concat "cd " do.website/base-dir " && " (if deploy "make deploy" "make"))
     buf-name)))

(defun do.website/deploy-website ()
  (interactive)
  (make-website t))

(defun do.website/republish-current-file ()
  "Re-publish the currently edited file asynchronously."
  (interactive)
  (save-buffer)
  (async-start
   `(lambda ()
      (load-file ,do.website/project-script)
      (org-publish-file ,(buffer-file-name)))
   (lambda (result)
     (beep)
     (message "republish-current-file: Done"))))

If the website actually exists, we add some local keybindings to org-mode to the above functions.

(when (file-exists-p do.website/project-script)
  (add-hook 'org-mode-hook (lambda ()
                             (local-set-key (kbd "C-c c") #'do.website/republish-current-file)
                             (local-set-key (kbd "C-c m") #'do.website/make-website)
                             (local-set-key (kbd "C-c d") #'do.website/deploy-website)
                             (local-set-key (kbd "C-c g") #'do.website/generate-website))))

For convenience, I define an org-tempo template for a new blog post.

(require 'org-tempo)
(tempo-define-template "blog-header" ; just some name for the template
     '("#+title: ?" n
       "#+AUTHOR: Dennis Ogbe" n
       "#+EMAIL: [email protected]" n
       "#+DATE:" n
       "#+STARTUP: showall" n
       "#+STARTUP: inlineimages" n
       "#+BEGIN_PREVIEW" n p n
       "#+END_PREVIEW")
     "<b"
     "Insert blog header" ; documentation
     'org-tempo-tags)

Finally, an org-tempo template for to insert a collapsible <details> block into a webpage.

(tempo-define-template "html-details" ; just some name for the template
                       '("#+HTML: <details><summary><b>" p
                         "</b></summary>" n p n
                         "#+HTML: </details>" n)
                       "<d"
                       "Insert collapsible block" ; documentation
                       'org-tempo-tags)

org-latex settings

For some reason, explained here, we need to run pdflatex with the -shell-escape flag. This is getting too complicated, so we're just going to run latexmk from org.

(setq org-latex-pdf-process (list "latexmk -pdflatex='pdflatex -interaction nonstopmode' -shell-escape -pdf -bibtex -f %f"))
(setq org-latex-with-hyperref
"\\hypersetup{
colorlinks=true,
citecolor=red,
filecolor=blue,
linkcolor=red,
urlcolor=blue,
pdfauthor={%a},
pdftitle={%t},
pdfkeywords={%k},
pdfsubject={%d},
pdfcreator={%c},
pdflang={%L}}\n")
(setq org-latex-prefer-user-labels t)

If we want to export to a documentclass other than article, we need to add its structure to the following list. To use that class, we can then put the following in the org-mode file header: #+LATEX_CLASS: <class>. To add options to the class, we add the following: #+LATEX_CLASS_OPTIONS: [op1, op2,...].

(require 'ox-latex)
(add-to-list 'org-latex-classes
             '("IEEEtran"
               "\\documentclass{IEEEtran}"
               ("\\section{%s}" . "\\section*{%s}")
               ("\\subsection{%s}" . "\\subsection*{%s}")
               ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
               ("\\paragraph{%s}" . "\\paragraph*{%s}")))

We add some templates for some common math environments

(add-to-list 'org-structure-template-alist
       '("prop"  "#+begin_proposition ?\n\n#+end_proposition"))
(add-to-list 'org-structure-template-alist
       '("thm"  "#+begin_theorem ?\n\n#+end_theorem"))
(add-to-list 'org-structure-template-alist
       '("lem"  "#+begin_lemma ?\n\n#+end_lemma"))
(add-to-list 'org-structure-template-alist
       '("pf"  "#+begin_proof ?\n\n#+end_proof"))

Non-breaking spaces

Entering "~" in org-mode buffers enters a literal tilde character and not the non-breaking space that I usually want. To get around this, we can insert "non-breaking-space" org entity. Unfortunately, having \nbsp{} littered around org-mode buffers is super ugly, so we use prettify-symbols-mode to correct this eyesore.

This is straightforward, but the default behavior of prettify-symbols-mode does not match occurrences like as\nbsp{}cite:citation, where the space is butting up to other strings on both sides. So we have to fix that too.

(defun do.org/prettify-symbols-compose-predicate (start end _match)
  "Explicitly allow any occurrence of the non-breaking space to be composed."
  (let ((result (prettify-symbols-default-compose-p start end _match)))
    (or result (string-equal (buffer-substring start end) "\\nbsp{}"))))

(defun do.org/prettify-symbols-setup ()
  "Set up `prettify-symbols-mode' for `org-mode' buffers."
  (make-variable-buffer-local 'prettify-symbols-unprettify-at-point)
  (setq prettify-symbols-unprettify-at-point 'right-edge)
  (setq prettify-symbols-compose-predicate #'do.org/prettify-symbols-compose-predicate)
  (setq prettify-symbols-alist `(("\\nbsp{}" . ,(string-to-char "~"))))
  (prettify-symbols-mode 1))

(add-hook 'org-mode-hook #'do.org/prettify-symbols-setup)

(defun do.org/insert-nbsp ()
  "Bind this to the ~ (tilde) character to insert non-breaking
spaces on `org-mode' for LaTeX export."
  (interactive)
  (insert "\\nbsp{}"))

Default Applications

Org opens PDFs by default in gv… change that to evince. Also open HTML in Chrome.

(let ((pdf-reader (if (boundp 'do.minimal/pdf-reader)
    do.minimal/pdf-reader
        "evince")))
(setq org-file-apps `((auto-mode . emacs)
    ("\\.x?html?\\'" . "firefox %s")
    ("\\.pdf\\'" . ,(concat pdf-reader " \"%s\""))
    ("\\.pdf::\\([0-9]+\\)\\'" . ,(concat pdf-reader " \"%s\" -p %1"))
    ("\\.pdf.xoj" . "xournal %s"))))

org-babel

Babel lets the user run code inside an org-mode document. I feel like this can come in handy one day.

Active Babel languages

(org-babel-do-load-languages
 'org-babel-load-languages
 '((python . t)
   (emacs-lisp . t)
   (matlab . t)
   (octave . t)
   (latex . t)
   (js . t)
   (shell . t)
   (C . t)
   (ditaa . t)))

org-babel settings

(setq org-confirm-babel-evaluate nil)

ditaa

Ditaa is a small tool that takes ASCII art and renders it as neat diagrams. Combined with org, I can sketch out ideas directly in an org buffer using the emacs Artist mode.

(setq org-ditaa-jar-path (concat initel-directory "random/ditaa/ditaa0_9.jar"))

Windmove and org-mode clashes

Make windmove (and framemove) play nice with org.

(add-hook 'org-shiftup-final-hook 'windmove-up)
(add-hook 'org-shiftleft-final-hook 'windmove-left)
(add-hook 'org-shiftdown-final-hook 'windmove-down)
(add-hook 'org-shiftright-final-hook 'windmove-right)

YaSnippet and org

backtab is already used in org-mode. Let's use C-<tab> instead.

(define-key org-mode-map (kbd "C-<tab>") 'yas-expand)

org-download

Incredibly useful for inserting images and screenshots from the clipboard into my org-mode notes files.

(use-package org-download
  :ensure t
  :init
  (setq org-download-screenshot-method "xclip -selection clipboard -t image/png -o > %s")
  (setq org-download-method 'directory)
  (defalias 'insert-screenshot 'org-download-screenshot)
  :config
  (add-hook 'dired-mode-hook 'org-download-enable))

Execute a code block on startup

Sometimes I want to execute a code block in an org file as soon as it is started. This can be done as follows. We first need the following function.

(defun do.org/execute-startup-block ()
  "Go to the startup block and execute it. It must be named 'do-org-startup-code'."
  (interactive)
  ;; only run this once. some modes (looking at you, org-ref) run
  ;; `hack-local-variables' again. we want our code to be executed only once on
  ;; buffer creation.
  (unless (bound-and-true-p do.org/startup-was-run-p)
    (org-babel-goto-named-src-block "do-org-startup-code")
    (let ((org-babel-after-execute-hook nil))
      (org-babel-execute-src-block))
    (org-global-cycle)
    (setq-local do.org/startup-was-run-p t)))

We can identify the code block to execute on startup by giving it the name do-org-startup-code, i.e., we add a #+NAME: do-org-startup-code in the line before #+begin_src.

Then to execute, we need to set the :eval local variable to call our function, which visits the block and runs it. This might look like this:

FILENAME -*- mode: org; eval: (do.org/execute-startup-block) -*-

or the corresponding multiline comment form at the end of a file.