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.