config.phundrak.com/docs/emacs/packages/org.org
Lucien Cartier-Tilet 39d9900022
All checks were successful
deploy / build (push) Successful in 5m36s
docs(emacs/org): fix keybinding and add another
2024-09-21 16:08:30 +02:00

54 KiB
Raw Blame History

Emacs — Packages — Org Mode

Org-mode

(mapconcat (lambda (line)
             (let* ((key      (nth 0 line))
                    (function (nth 1 line))
                    (comment  (or (nth 2 line) ""))
                    (package  (or (nth 3 line) "")))
               (format "\"%s%s\" %s"
                       prefix
                       key
                       (if (string= "" comment)
                           (if (member function '("" "nil")) "nil" (concat "#'" function))
                         (format "'(%s :wk %s%s)"
                                 (if (member function '("" "nil")) ":ignore t" function)
                                 (if (member function '("none" "nil")) "t" (concat "\"" comment "\""))
                                 (if (string-blank-p package) "" (concat ":package " package)))))))
           table
           "\n")

Since recently, in order to make org-cite compile properly, we need the citeproc package, a citation processor.

(use-package citeproc
  :after (org)
  :defer t
  :straight (:build t))

Org is the main reason I am using Emacs. It is an extremely powerful tool when you want to write anything that is not necessarily primarily programming-related, though it absolutely can be! Org can be a replacement for anything similar to LibreOffice Writer, LibreOffice Calc, and LibreOffice Impress. It is a much more powerful (and older) version of Markdown which can be exported to LaTeX and HTML at least, rendering writing web pages and technical, scientific documents much simpler than writing manually HTML and LaTeX code, especially when a single document source is meant to be exported for both formats. And since org is an Emacs package, that also means it can be greatly extended however we like!

(use-package org
  :straight t
  :defer t
  :after engrave-faces
  :commands (orgtbl-mode)
  :hook ((org-mode . visual-line-mode)
         (org-mode . org-num-mode))
  :custom-face
  (org-macro ((t (:foreground "#b48ead"))))
  :init
  (auto-fill-mode -1)
  :config
  <<org-hydra-babel>>
  (require 'ox-beamer)
  (require 'org-protocol)
  (setq org-hide-leading-stars             nil
        org-hide-macro-markers             t
        org-ellipsis                       " ⤵"
        org-image-actual-width             600
        org-redisplay-inline-images        t
        org-display-inline-images          t
        org-startup-with-inline-images     "inlineimages"
        org-pretty-entities                t
        org-fontify-whole-heading-line     t
        org-fontify-done-headline          t
        org-fontify-quote-and-verse-blocks t
        org-startup-indented               t
        org-startup-align-all-tables       t
        org-use-property-inheritance       t
        org-list-allow-alphabetical        t
        org-M-RET-may-split-line           nil
        org-src-window-setup               'split-window-below
        org-src-fontify-natively           t
        org-src-tab-acts-natively          t
        org-src-preserve-indentation       t
        org-log-done                       'time
        org-directory                      "~/org"
        org-default-notes-file             (expand-file-name "notes.org" org-directory))
  (with-eval-after-load 'oc
   (setq org-cite-global-bibliography '("~/org/bibliography/references.bib")))
  <<org-agenda-files>>
  <<org-behavior-electric>>
  <<org-capture-target-files>>
  <<org-capture-templates>>
  <<org-create-emphasis-functions()>>
  <<org-babel-load-languages>>
  <<org-use-sub-superscripts>>
  <<org-latex-compiler>>
  <<org-latex-src-block-backend>>
  <<org-latex-default-packages>>
  <<org-export-latex-hyperref-format>>
  <<org-latex-pdf-process>>
  <<org-latex-logfiles-add-extensions>>
  <<org-re-reveal>>
  <<org-html-validation>>
  <<org-latex-classes>>
  <<org-publish-projects>>
  <<org-mode-visual-prettify-symbols>>
  :general
  (phundrak/evil
    :keymaps 'org-mode-map
    :packages 'org
    "RET" 'org-open-at-point)
  (phundrak/major-leader-key
    :keymaps 'org-mode-map
    :packages 'org
    <<general-keybindings-gen(table=org-keybinds-various)>>
    <<general-keybindings-gen(table=org-keybinds-babel)>>
    <<general-keybindings-gen(table=org-keybinds-dates)>>
    <<general-keybindings-gen(table=org-keybinds-insert)>>
    <<general-keybindings-gen(table=org-keybinds-jump)>>
    <<general-keybindings-gen(table=org-keybinds-tables)>>
    <<general-keybindings-gen(table=org-keybinds-toggles)>>)
  <<org-capture-keybinds>>
  (phundrak/major-leader-key
    :packages 'org
    :keymaps 'org-src-mode-map
    "'" #'org-edit-src-exit
    "k" #'org-edit-src-abort))

The main feature from evil-org that I love is how easy it is to modify some keybindings for keyboards layouts that do not have hjkl, such as the bépo layout (or Dvorak or Colemak if you are into that). But it also adds a ton of default keybindings which are just much more comfortable than the default ones you get with evil and org naked.

(use-package evil-org
  :straight (:build t)
  :after (org)
  :hook (org-mode . evil-org-mode)
  :config
  (setq-default evil-org-movement-bindings
                '((up    . "s")
                  (down  . "t")
                  (left  . "c")
                  (right . "r")))
  (evil-org-set-key-theme '(textobjects navigation calendar additional shift operators))
  (require 'evil-org-agenda)
  (evil-org-agenda-set-keys))

This package is a small package Ive written that helps me when writing conlanging documents, with features such as creating syntax trees, converting translitterated text to its native script, etc…

(use-package conlanging
  :straight (conlanging :build t
                        :type git
                        :repo "https://labs.phundrak.com/phundrak/conlanging.el")
  :after org
  :defer t)

Since very recently, the contrib/lisp/ directory of org moved out of the main repository to this repository. On the other hand, contrib/scripts/ moved to the worg repository, but I dont need it. The main reason I want org-contrib is due to ox-extra that allow the usage of the :ignore: tag in org.

(use-package org-contrib
  :after (org)
  :defer t
  :straight (:build t)
  :init
  (require 'ox-extra)
  (ox-extras-activate '(latex-header-blocks ignore-headlines)))

Agenda

(setq org-agenda-files (list "~/org/agenda" "~/org/notes.org"))
(use-package org-caldav
  :straight (:build t)
  :defer t
  :config
  (setq org-caldav-url "https://nextcloud.phundrak.com/remote.php/dav/calendars/phundrak"
        org-icalendar-timezone "Europe/Paris"
        org-caldav-calendars
        `((:calendar-id "personal" :files ("~/org/agenda/private.org")
           :inbox "~/org/agenda/private.org")
          (:calendar-id "contact_birthdays" :files ("~/org/agenda/birthdays.org")
           :inbox "~/org/agenda/birthdays.org"))))

Babel

One of the amazing features of org-mode is its literary programming capacities by running code blocks from within Org-mode itself. But for that, only a couple of languages are supported directly by Org-mode itself, and they need to be activated. Here are the languages I activated in my Org-mode configuration:

C
emacs-lisp
gnuplot
latex
makefile
plantuml
python
sass
shell
sql
'((C . t)
  (emacs-lisp . t)
  (gnuplot . t)
  (latex . t)
  (makefile . t)
  (plantuml . t)
  (python . t)
  (sass . t)
  (shell . t)
  (sql . t))

The corresponding code is as follows:

(org-babel-do-load-languages
 'org-babel-load-languages
 <<org-babel-languages-gen()>>)

Some languages can run asynchronously with the help of ob-async.

(use-package ob-async
  :straight (:build t)
  :defer t
  :after (org ob))

A package I use from time to time is ob-latex-as-png which allows me to easily convert a LaTeX snippet into a PNG, regardless of the exporter I use afterwards. Its installation is pretty simple:

(use-package ob-latex-as-png
  :after org
  :straight (:build t))

A nice thing to have when working with REST APIs is to have a REST client. Even better if it can work inside org-mode!

(use-package ob-restclient
  :straight (:build t)
  :defer t
  :after (org ob)
  :init
  (add-to-list 'org-babel-load-languages '(restclient . t)))

Behavior

A useful package I like is toc-org which creates automatically a table of contents. My main usage for this however is not just to create a table of content of my files to quickly jump around my file (I have counsel-org-goto for that), but it is for creating table of contents for org files that will be hosted and viewable on GitHub.

(use-package toc-org
  :after (org markdown-mode)
  :straight (:build t)
  :init
  (add-to-list 'org-tag-alist '("TOC" . ?T))
  :hook (org-mode . toc-org-enable)
  :hook (markdown-mode . toc-org-enable))

electric-mode also bothers me a lot when editing org files, so lets deactivate it:

(add-hook 'org-mode-hook (lambda ()
                           (interactive)
                           (electric-indent-local-mode -1)))

As explained in my blog post, org-mode is terrible with coming up with meaningful IDs for its headings. I actually wrote a package for this!

(use-package org-unique-id
  :straight (org-unique-id :build t
                           :type git
                           :host github
                           :repo "Phundrak/org-unique-id")
  :defer t
  :after org
  :init (add-hook 'before-save-hook #'org-unique-id-maybe))

Capture

Org capture is an amazing tool for taking quick notes, be it simple text, links, resources, or reminders. They are all organised is specified org files which are described below.

(defvar org-conlanging-file "~/org/conlanging.org")
(defvar org-notes-file "~/org/notes.org")
(defvar org-journal-file "~/org/journal.org")
(defvar org-linguistics-file "~/org/linguistics.org")
(defvar org-novel-file "~/org/novel.org")
(defvar org-agenda-file "~/org/agenda/private.org")
(defvar org-school-file "~/org/agenda/school.org")
(defvar org-worldbuilding-file "~/org/worldbuilding.org")

When org-capture is invoked, it will ask which template we wish to use. In the table /phundrak/config.phundrak.com/src/commit/39f8a4510b9ffa37b84d707da1a343c459f1e0dd/docs/emacs/packages/org-capture-shortcuts-table, the key column represents which keychord we need to hit, titled with name, we need to hit in order to use the template, inserted in the designated file in the manner described by insertion mode.

Shortcut Name Title Insertion mode file template
e Email
ew Write Email Emails file+headline org-default-notes-file email.orgcaptmpl
j Journal file+datetree org-journal-file journal.orgcaptmpl
l Link
ll General file+headline org-default-notes-file link.orgcaptmpl
ly YouTube file+headline org-default-notes-file youtube.orgcaptmpl
L Protocol Link Link file+headline org-default-notes-file protocol-link.orgcaptmpl
n Notes
nc Conlanging Note file+headline org-conlanging-file notes.orgcaptmpl
nn General file+headline org-default-notes-file notes.orgcaptmpl
nN Novel Note file+headline org-novel-notes-file notes.orgcaptmpl
nq Quote file+headline org-default-notes-file notes-quote.orgcaptmpl
nw Worldbuilding Note file+headline org-wordbuilding-file notes.orgcaptmpl
N Novel
Ni Ideas file+headline org-novel-notes-file notes.orgcaptmpl
p Protocol Link file+headline org-default-notes-file protocol.orgcaptmpl
r Resources
rc Conlanging Resources file+headline org-conlanging-file resource.orgcaptmpl
re Emacs file+headline org-default-notes-file resource.orgcaptmpl
ri Informatique file+headline org-default-notes-file resource.orgcaptmpl
rl Linguistics file+headline org-default-notes-file resource.orgcaptmpl
rL Linux file+headline org-default-notes-file resource.orgcaptmpl
rw Worldbuilding Resources file+headline org-wordbuilding-file resource.orgcaptmpl
t Tasks
tb Birthday file+headline org-private-agenda-file birthday.orgcaptmpl
te Event file+headline org-private-agenda-file event.orgcaptmpl
th Health file+headline org-private-agenda-file health.orgcaptmpl
ti Informatique file+headline org-private-agenda-file informatique.orgcaptmpl

All templates can be found in my dotfiles repository.

(mapconcat (lambda (entry)
             (let ((key      (nth 0 entry))
                   (name     (nth 1 entry))
                   (title    (nth 2 entry))
                   (ins-mode (nth 3 entry))
                   (file     (nth 4 entry))
                   (template (nth 5 entry)))
               (if (string= "" ins-mode)
                   (format "%S" `(,key ,name))
                 (format "(\"%s\" \"%s\" entry\n  %S\n  %S)"
                         key name
                         `(,(intern ins-mode) ,(intern file) ,(if (string= "file+datetree" ins-mode)
                                                                  (intern "")
                                                                (if (string= title "")
                                                                    name
                                                                  title)))
                         `(file ,(concat "~/org/capture/" template))))))
           entries
           "\n")
("e" "Email")
("ew" "Write Email" entry
  (file+headline org-default-notes-file "Emails")
  (file "~/org/capture/email.orgcaptmpl"))
("j" "Journal" entry
  (file+datetree org-journal-file ##)
  (file "~/org/capture/journal.orgcaptmpl"))
("l" "Link")
("ll" "General" entry
  (file+headline org-default-notes-file "General")
  (file "~/org/capture/link.orgcaptmpl"))
("ly" "YouTube" entry
  (file+headline org-default-notes-file "YouTube")
  (file "~/org/capture/youtube.orgcaptmpl"))
("L" "Protocol Link" entry
  (file+headline org-default-notes-file "Link")
  (file "~/org/capture/protocol-link.orgcaptmpl"))
("n" "Notes")
("nc" "Conlanging" entry
  (file+headline org-conlanging-file "Note")
  (file "~/org/capture/notes.orgcaptmpl"))
("nn" "General" entry
  (file+headline org-default-notes-file "General")
  (file "~/org/capture/notes.orgcaptmpl"))
("nN" "Novel" entry
  (file+headline org-novel-notes-file "Note")
  (file "~/org/capture/notes.orgcaptmpl"))
("nq" "Quote" entry
  (file+headline org-default-notes-file "Quote")
  (file "~/org/capture/notes-quote.orgcaptmpl"))
("nw" "Worldbuilding" entry
  (file+headline org-wordbuilding-file "Note")
  (file "~/org/capture/notes.orgcaptmpl"))
("N" "Novel")
("Ni" "Ideas" entry
  (file+headline org-novel-notes-file "Ideas")
  (file "~/org/capture/notes.orgcaptmpl"))
("p" "Protocol" entry
  (file+headline org-default-notes-file "Link")
  (file "~/org/capture/protocol.orgcaptmpl"))
("r" "Resources")
("rc" "Conlanging" entry
  (file+headline org-conlanging-file "Resources")
  (file "~/org/capture/resource.orgcaptmpl"))
("re" "Emacs" entry
  (file+headline org-default-notes-file "Emacs")
  (file "~/org/capture/resource.orgcaptmpl"))
("ri" "Informatique" entry
  (file+headline org-default-notes-file "Informatique")
  (file "~/org/capture/resource.orgcaptmpl"))
("rl" "Linguistics" entry
  (file+headline org-default-notes-file "Linguistics")
  (file "~/org/capture/resource.orgcaptmpl"))
("rL" "Linux" entry
  (file+headline org-default-notes-file "Linux")
  (file "~/org/capture/resource.orgcaptmpl"))
("rw" "Worldbuilding" entry
  (file+headline org-wordbuilding-file "Resources")
  (file "~/org/capture/resource.orgcaptmpl"))
("t" "Tasks")
("tb" "Birthday" entry
  (file+headline org-private-agenda-file "Birthday")
  (file "~/org/capture/birthday.orgcaptmpl"))
("te" "Event" entry
  (file+headline org-private-agenda-file "Event")
  (file "~/org/capture/event.orgcaptmpl"))
("th" "Health" entry
  (file+headline org-private-agenda-file "Health")
  (file "~/org/capture/health.orgcaptmpl"))
("ti" "Informatique" entry
  (file+headline org-private-agenda-file "Informatique")
  (file "~/org/capture/informatique.orgcaptmpl"))

The capture templates are set like so:

(setq org-capture-templates
      '(
        <<org-capture-shortcuts-gen()>>))

Custom functions

Emphasize text

Sometimes, I want to emphasize some text in my org-mode documents. Its very possible to just go to the beginning of the chosen text, add the marker, then go to the end of the text than needs emphasis and add another marker, and Im sure most people are fine with that. But I also like being able to select a region and hit a keybind to emphasize it that way. The table /phundrak/config.phundrak.com/src/commit/39f8a4510b9ffa37b84d707da1a343c459f1e0dd/docs/emacs/packages/org-emphasis-character lists the emphasis characters in org-mode, their role, and the character code of each emphasis character. From that, creating functions that emphasize a selected text is quite easy.

Emphasis Character Character code
bold * 42
italic / 47
underline _ 95
verbatim = 61
code ~ 126
strike-through + 43
(mapconcat (lambda (emphasis)
             (let ((type (car emphasis))
                   (code (nth 2 emphasis)))
               (format "(defun org-emphasize-%s ()
  \"Emphasize as %s the current region.\"
  (interactive)
  (org-emphasize %s))"
                       type
                       type
                       code)))
           emphasis-list
           "\n")
(defun org-emphasize-bold ()
  "Emphasize as bold the current region."
  (interactive)
  (org-emphasize 42))
(defun org-emphasize-italic ()
  "Emphasize as italic the current region."
  (interactive)
  (org-emphasize 47))
(defun org-emphasize-underline ()
  "Emphasize as underline the current region."
  (interactive)
  (org-emphasize 95))
(defun org-emphasize-verbatim ()
  "Emphasize as verbatim the current region."
  (interactive)
  (org-emphasize 61))
(defun org-emphasize-code ()
  "Emphasize as code the current region."
  (interactive)
  (org-emphasize 126))
(defun org-emphasize-strike-through ()
  "Emphasize as strike-through the current region."
  (interactive)
  (org-emphasize 43))

You can find the keybinds for these functions here.

phundrak/toggle-org-src-window-split

(defun phundrak/toggle-org-src-window-split ()
  "This function allows the user to toggle the behavior of
`org-edit-src-code'. If the variable `org-src-window-setup' has
the value `split-window-right', then it will be changed to
`split-window-below'. Otherwise, it will be set back to
`split-window-right'"
  (interactive)
  (if (equal org-src-window-setup 'split-window-right)
      (setq org-src-window-setup 'split-window-below)
    (setq org-src-window-setup 'split-window-right))
  (message "Org-src buffers will now split %s"
           (if (equal org-src-window-setup 'split-window-right)
               "vertically"
             "horizontally")))

Exporters

I want to disable by default behaviour of ^ and _ for only one character, making it compulsory to use instead ^{} and _{} respectively. This is due to my frequent usage of the underscore in my org files as a regular character and not a markup one, especially when describing phonetics evolution. So, lets disable it:

(setq org-use-sub-superscripts (quote {}))

Epub

A backend for exporting files through org I like is ox-epub which, as you can guess, exports org files to the Epub format.

(use-package ox-epub
  :after (org ox)
  :straight (:build t))

Gemini

Gemini is a lightweight protocol for creating lightweight websites that are basically text-only websites with maybe some images. Im currently maintaining my own fork of Justin Abrahms ox-gemini which fixes two issues I had with the original package.

(use-package ox-gemini
  :defer t
  :straight (ox-gemini :build t
                       :fork (:type git
                              :host nil
                              :repo "https://labs.phundrak.com/phundrak/ox-gemini"))
  :after (ox org))

HTML

On HTML exports, Org-mode tries to include a validation link for the exported HTML. Lets disable that since I never use it.

(setq org-html-validation-link nil)

This package allows for live-previewing the HTML export of an org buffer in an XWidget Webkit browser window. But when testing it, its not great for large org files, I should keep its usage for smaller org files.

(use-package preview-org-html-mode
  :defer t
  :after (org)
  :straight (preview-org-html-mode :build t
                                   :type git
                                   :host github
                                   :repo "jakebox/preview-org-html-mode")
  :general
  (phundrak/major-leader-key
   :keymaps 'org-mode-map
   :packages 'preview-org-html-mode
   :infix "P"
   ""  '(:ignore t :which-key "preview")
   "h" #'preview-org-html-mode
   "r" #'preview-org-html-refresh
   "p" #'preview-org-html-pop-window-to-frame)
  :config
  (setq preview-org-html-refresh-configuration 'save))

Hugo

I manage my blog with Hugo. Although it natively supports the org format, its not great compared to its markdown support. So, instead, lets directly export our org files as markdown files and let Hugo do the rest of the job for us!

(use-package ox-hugo
  :defer t
  :after ox
  :straight t)

I also have a function for publishing my blog once I exported my articles with ox-hugo. It will compile blog into a public/ directory and copy its content over to my remote server.

(defun phundrak/blog-publish ()
  "Publish my blog through Hugo to my remote server."
  (interactive)
  (let* ((default-directory (expand-file-name "~/org/blog"))
         (public-path       (concat default-directory "/public"))
         (target-path       "/rsync:Tilo:/home/phundrak/www/phundrak.com/blog"))
    (compile "hugo")
    (let ((files (mapcar (lambda (file)
                           (f-relative file public-path))
                         (f-files public-path nil t))))
      (dolist (file files)
        (copy-file (concat public-path "/" file)
                   (concat target-path "/" file)
                   t nil t)))))

LaTeX

When it comes to exports, I want the LaTeX and PDF exports to be done with XeTeX only. I also want LaTeX exports to use my labels rather than org-generated labels.

(setq org-latex-compiler "xelatex"
      org-latex-prefer-user-labels t)

A new backend that was introduced in org-mode for LaTeX source block coloring is engraved.

(use-package engrave-faces
  :straight (:build t))
(require 'engrave-faces)
(setq org-latex-src-block-backend 'engraved)

The default packages break my LaTeX exports: for some reason, images are not loaded and exported in PDFs, so I needed to redefine the default packages excluding the one that broke my exports; namely, I need to remove inputenc, fontenc and grffile. I also added some default packages:

  • cleveref for better references to various elements.
  • svg for inserting SVG files in PDF outputs
  • booktabs for nicer tables
  • and tabularx for tabulars with adjustable columns
(dolist (package '(("AUTO" "inputenc" t ("pdflatex"))
                   ("T1"   "fontenc"  t ("pdflatex"))
                   (""     "grffile"  t)))
  (delete package org-latex-default-packages-alist))

(dolist (package '(("AUTO" "babel" nil ("pdflatex"))
                   ("AUTO" "polyglossia" nil ("xelatex" "lualatex"))
                   ("capitalize" "cleveref")
                   (""           "booktabs")
                   (""           "tabularx")))
  (add-to-list 'org-latex-default-packages-alist package t))

(setq org-latex-reference-command "\\cref{%s}")

By the way, reference links in LaTeX should be written in this format, since we are using cleveref:

(setq org-export-latex-hyperref-format "\\ref{%s}")

Tectonic is awesome for processing LaTeX documents! Look how simple it is!

(setq org-latex-pdf-process
      '("tectonic -Z shell-escape --synctex --outdir=%o %f"))

Finally, org-mode is supposed to automatically clean logfiles after it exports an org file to LaTeX. However, it misses a few, so I need to add their extension like so:

(dolist (ext '("bbl" "lot"))
  (add-to-list 'org-latex-logfiles-extensions ext t))

Reveal.js

(use-package org-re-reveal
  :defer t
  :after org
  :straight (:build t)
  :init
  (add-hook 'org-mode-hook (lambda () (require 'org-re-reveal)))
  :config
  (setq org-re-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js"
        org-re-reveal-revealjs-version "4"))

SSH Config

Yet another exporter I enjoy is ox-ssh with which I manage my $HOME/.ssh/config file. You wont find my org file for managing my servers on my repos though.

(use-package ox-ssh
  :after (ox org)
  :straight (:build t))

Keybindings

Be prepared, I have a lot of keybindings for org-mode! They are all prefixed with a comma , in normal mode.

Key chord Function Description
RET org-ctrl-c-ret
* org-ctrl-c-star
, org-ctrl-c-ctrl-c
' org-edit-special
- org-ctrl-c-minus
a org-agenda
c org-capture
C org-columns
e org-export-dispatch
l org-store-link
p org-priority
r org-reload

I then have a couple of babel-related functions.

Key chord Function Description
b nil babel
b. org-babel-transient/body
bb org-babel-execute-buffer
bc org-babel-check-src-block
bC org-babel-tangle-clean
be org-babel-execute-maybe
bf org-babel-tangle-file
bn org-babel-next-src-block
bo org-babel-open-src-block-result
bp org-babel-previous-src-block
br org-babel-remove-result-one-or-many
bR org-babel-goto-named-result
bt org-babel-tangle
bi org-babel-view-src-block-info

The org-babel-transient hydra allows me to quickly navigate between code blocks and interact with them. This code block was inspired by one you can find in Spacemacs.

(defhydra org-babel-transient ()
  "
^Navigate^                    ^Interact
^^^^^^^^^^^------------------------------------------
[_t_/_s_] navigate src blocs  [_x_] execute src block
[_g_]^^   goto named block    [_'_] edit src block
[_z_]^^   recenter screen     [_q_] quit
"
  ("q" nil :exit t)
  ("t" org-babel-next-src-block)
  ("s" org-babel-previous-src-block)
  ("g" org-babel-goto-named-src-block)
  ("z" recenter-top-bottom)
  ("x" org-babel-execute-maybe)
  ("'" org-edit-special :exit t))

We next have keybindings related to org-modes agenda capabilities. We can schedule a todo header for some dates, or set a deadline.

Key chord Function Description
d nil dates
dd org-deadline
ds org-schedule
dt org-time-stamp
dT org-time-stamp-inactive

Lets now define some keybinds for inserting stuff in our org buffer:

Key chord Function Description Package
i nil insert
ib org-insert-structure-template
ic conlanging
ica conlanging-eittlandic-insert-adjective-declension adjective conlanging
icn conlanging-eittlandic-insert-noun-declensions noun conlanging
icv conlanging-eittlandic-insert-verb-declension verb conlanging
id org-insert-drawer
ie nil emphasis
ieb org-emphasize-bold
iec org-emphasize-code
iei org-emphasize-italic
ies org-emphasize-strike-through
ieu org-emphasize-underline
iev org-emphasize-verbatim
iE org-set-effort
if org-footnote-new
ih org-insert-heading
iH counsel-org-link
ii org-insert-item
il org-insert-link
in org-add-note
ip org-set-property
is org-insert-subheading
it org-set-tags-command
iV conlang-store-heading-vuepress store Vuepress link conlanging
iv conlanging-insert-heading-vuepress vuepress link conlanging

There isnt a lot of stuff I can jump to yet, but theres still some:

Key chord Function Description
j nil jump
ja counsel-org-goto-all
jh counsel-org-goto

Tables get a bit more love:

Key chord Function Description
t nil tables
tc org-table-move-column-left
tt org-table-move-row-down
ts org-table-move-row-up
tr org-table-move-column-right
ta org-table-align
te org-table-eval-formula
tf org-table-field-info
tF org-table-edit-formulas
th org-table-convert
tl org-table-recalculate
tp org-plot/gnuplot
tS org-table-sort-lines
tw org-table-wrap-region
tx org-table-shrink
tN org-table-create-with-table.el
td nil delete
tdc org-table-delete-column
tdr org-table-kill-row
ti nil insert
tic org-table-insert-column
tih org-table-insert-hline
tir org-table-insert-row
tiH org-table-hline-and-move
tt nil toggle
ttf org-table-toggle-formula-debugger
tto org-table-toggle-coordinate-overlays

Finally, lets make enabling and disabling stuff accessible:

Key chord Function Description
T nil toggle
Tc org-toggle-checkbox
Ti org-toggle-inline-images
Tl org-latex-preview
Tn org-num-mode
Ts phundrak/toggle-org-src-window-split
Tt org-show-todo-tree
TT org-todo

LaTeX formats

I currently have two custom formats for my Org-mode exports: one for general use (initially for my conlanging files, hence its conlang name), and one for beamer exports.

Below is the declaration of the conlang LaTeX class:

'("conlang"
  "\\documentclass{book}"
  ("\\chapter{%s}" . "\\chapter*{%s}")
  ("\\section{%s}" . "\\section*{%s}")
  ("\\subsection{%s}" . "\\subsection*{%s}")
  ("\\subsubsection{%s}" . "\\subsubsection*{%s}"))

And here is the declaration of the beamer class:

`("beamer"
  ,(concat "\\documentclass[presentation]{beamer}\n"
           "[DEFAULT-PACKAGES]"
           "[PACKAGES]"
           "[EXTRA]\n")
  ("\\section{%s}" . "\\section*{%s}")
  ("\\subsection{%s}" . "\\subsection*{%s}")
  ("\\subsubsection{%s}" . "\\subsubsection*{%s}"))

Both these classes have to be added to org-latex-classes like so:

(eval-after-load "ox-latex"
  '(progn
     (add-to-list 'org-latex-classes
                  <<org-latex-class-conlang>>)
     (add-to-list 'org-latex-classes
                  <<org-latex-class-beamer>>)))

Projects

Another great features of Org-mode is the Org projects that allow the user to easily publish a bunch of org files to a remote location. Here is the current declaration of my projects, which will be detailed later:

<<org-proj-config-setup>>
(setq org-publish-project-alist
      `(<<org-proj-config-html>>
        <<org-proj-config-static>>
        <<org-proj-config>>))

Configuration website

This is my configuration for exporting my dotfiles to my website in a web format only. No PDFs or anything, just HTML. Please note that I do not use that often anymore, I much prefer the automatic script that I have which deploys through my Drone instance my website on git pushes.

And before we get into the actual configuration, I would like to introduce a couple of variables. This is a bit more verbose than if I declared everything manually, but now I can change all three values at the same time without a hasle.

(defvar phundrak//projects-config-target
  "/ssh:Tilo:~/www/phundrak.com/config"
  "Points to where exported files for config.phundrak.com should be put.")
(defvar phundrak//projects-config-source
  "~/org/config/"
  "Points to where the sources for config.phundrak.com are.")
(defvar phundrak//projects-config-language
  "en"
  "Language of the website config.phundrak.com.")
(defvar phundrak//projects-config-recursive
  t
  "Defines whether subdirectories should be parsed for config.phundrak.com.")

Now, here is my configuration. In this snippet, my org files located in my source directory get exported in the HTML format and published to my target directory on my remote server through RSYNC via TRAMP. A sitemap is automatically generated, which comes in handy with the online sitemap that is available through the navigation bar.

("config-website-html"
 :base-directory ,phundrak//projects-config-source
 :base-extension "org"
 :publishing-directory ,phundrak//projects-config-target
 :recursive ,phundrak//projects-config-recursive
 :language ,phundrak//projects-config-language
 :publishing-function org-html-publish-to-html
 :headline-levels 5
 :auto-sitemap t
 :auto-preamble t)

We also have the component for all the static files needed to run the website (mostly images tbh).

("config-website-static"
 :base-directory ,phundrak//projects-config-source
 :base-extension "png\\|jpg\\|gif\\|webp\\|svg\\|jpeg\\|ttf\\|woff\\|txt\\|epub\\|md"
 :publishing-directory ,phundrak//projects-config-target
 :recursive ,phundrak//projects-config-recursive
 :language ,phundrak//projects-config-language
 :publishing-function org-publish-attachment)

The project is then defined like so:

("config-website"
 :components ("config-website-org"
              "config-website-static"))

Org-roam

After hearing about it for so many years and thinking I really should install it one day, 2023 is finally the year I installed org-roam! For those unaware of it, org-roam is a Zettelkasten-style knowledge management system based on org-mode.

(use-package org-roam
  :straight (:build t)
  :defer t
  :custom
  (org-roam-directory (expand-file-name "org/roam/" (getenv "HOME")))
  (org-roam-completion-everywhere t)
  :config
  (org-roam-db-autosync-mode 1)
  :general
  (phundrak/major-leader-key
    :keymaps 'org-mode-map
    :packages '(org org-roam)
    "h"   #'org-id-get-create
    "r"   '(:ignore t :which-key "roam")
    "ra"  '(:ignore t :which-key "alias")
    "raa" #'org-roam-alias-add
    "rar" #'org-roam-alias-remove))
(use-package org-roam-ui
  :straight (:build t)
  :defer t
  :after org-roam
  :config
  (setq org-roam-ui-sync-theme t
        org-roam-ui-follow t
        org-roam-ui-update-on-save t
        org-roam-ui-open-on-start t))

Org-ref and Bibtex configuration

(use-package reftex
  :commands turn-on-reftex
  :init (setq reftex-default-bibliography "~/org/bibliography/references.bib"
              reftex-plug-into-AUCTeX     t))
(use-package org-ref
  ;; :after (org ox-bibtex pdf-tools)
  :after org
  :defer t
  :straight (:build t)
  :custom-face
  (org-ref-cite-face ((t (:weight bold))))
  :init
  (setq org-ref-completion-library    'org-ref-ivy-cite
        org-latex-logfiles-extensions '("lof" "lot" "aux" "idx" "out" "log" "fbd_latexmk"
                                        "toc" "nav" "snm" "vrb" "dvi" "blg" "brf" "bflsb"
                                        "entoc" "ps" "spl" "bbl" "pygtex" "pygstyle"))
  (add-hook 'org-mode-hook (lambda () (require 'org-ref)))
  :config
  (setq bibtex-completion-pdf-field    "file"
        bibtex-completion-notes-path   "~/org/bibliography/notes/"
        bibtex-completion-bibliography "~/org/bibliography/references.bib"
        bibtex-completion-library-path "~/org/bibliography/bibtex-pdfs/"
        bibtex-completion-pdf-symbol   "⌘"
        bibtex-completion-notes-symbol "✎")
  :general
  (phundrak/evil
   :keymaps 'bibtex-mode-map
   :packages 'org-ref
   "C-t" #'org-ref-bibtex-next-entry
   "C-s" #'org-ref-bibtex-previous-entry
   "gt"  #'org-ref-bibtex-next-entry
   "gs"  #'org-ref-bibtex-previous-entry)
  (phundrak/major-leader-key
   :keymaps '(bibtex-mode-map)
   :packages 'org-ref
   ;; Navigation
   "t" #'org-ref-bibtex-next-entry
   "s" #'org-ref-bibtex-previous-entry

   ;; Open
   "b" #'org-ref-open-in-browser
   "n" #'org-ref-open-bibtex-notes
   "p" #'org-ref-open-bibtex-pdf

   ;; Misc
   "h" #'org-ref-bibtex-hydra/body
   "i" #'org-ref-bibtex-hydra/org-ref-bibtex-new-entry/body-and-exit
   "s" #'org-ref-sort-bibtex-entry

   "l" '(:ignore t :which-key "lookup")
   "la" #'arxiv-add-bibtex-entry
   "lA" #'arxiv-get-pdf-add-bibtex-entry
   "ld" #'doi-utils-add-bibtex-entry-from-doi
   "li" #'isbn-to-bibtex
   "lp" #'pubmed-insert-bibtex-from-pmid)
  (phundrak/major-leader-key
   :keymaps 'org-mode-map
   :pakages 'org-ref
   "iC" #'org-ref-insert-link
   "iL" #'org-ref-insert-ref-link
   "ir" #'org-ref-insert-link-hydra/body
   "iB" #'org-ref-bibtex-hydra/body))
(use-package ivy-bibtex
  :defer t
  :straight (:build t)
  :config
  (setq bibtex-completion-pdf-open-function #'find-file)
  :general
  (phundrak/leader-key
    :keymaps '(bibtex-mode-map)
    :packages 'ivy-bibtex
    "m" #'ivy-bibtex))

Org-present

org-present allows its user to create presentations through org-mode, which is really nice! However, most of my configuration will be stolen from Daviwils with minor changes.

(defun my/org-present-prepare-slide ()
  (org-overview)
  (org-show-entry)
  (org-show-children)
  (org-present-hide-cursor))

(defun my/org-present-init ()
  (setq header-line-format " ")
  (org-display-inline-images)
  (my/org-present-prepare-slide))

(defun my/org-present-quit ()
  (setq header-line-format nil)
  (org-present-small)
  (org-present-show-cursor))

(defun my/org-present-prev ()
  (interactive)
  (org-present-prev)
  (my/org-present-prepare-slide))

(defun my/org-present-next ()
  (interactive)
  (org-present-next)
  (my/org-present-prepare-slide))

(use-package org-present
  :after org
  :defer t
  :straight (:build t)
  :general
  (phundrak/major-leader-key
    :packages 'org-present
    :keymaps 'org-mode-map
    "P" #'org-present)
  (phundrak/evil
    :states 'normal
    :packages 'org-present
    :keymaps 'org-present-mode-keymap
    "+" #'org-present-big
    "-" #'org-present-small
    "<" #'org-present-beginning
    ">" #'org-present-end
    "«" #'org-present-beginning
    "»" #'org-present-end
    "c" #'org-present-hide-cursor
    "C" #'org-present-show-cursor
    "n" #'org-present-next
    "p" #'org-present-prev
    "r" #'org-present-read-only
    "w" #'org-present-read-write
    "q" #'org-present-quit)
  :hook ((org-present-mode      . my/org-present-init)
         (org-present-mode-quit . my/org-present-quit)))

Tangle config org files on save

Something really, really useful is tangling all my configuration files on save. For this, a dedicated function will do the trick.

(defun my/tangle-config-file ()
  (when (and (eq major-mode 'org-mode)
             (f-ancestor-of-p (f-full "~/.nosync/org/config") default-directory))
    (org-babel-tangle)))

(add-hook 'after-save-hook #'my/tangle-config-file)

Visual Configuration

While most modes of Emacs are dedicated to development, and therefore are much more comfortable with a fixed-pitch font, more literary modes such as org-mode are much more enjoyable if you have a variable pitch font enabled. BUT, these modes can also require some fixed-pitch fonts for some elements of the buffer, such as code blocks with org-mode. mixed-pitch comes to the rescue!

(use-package mixed-pitch
  :after org
  :straight (:build t)
  :hook
  (org-mode           . mixed-pitch-mode)
  (emms-browser-mode  . mixed-pitch-mode)
  (emms-playlist-mode . mixed-pitch-mode)
  :config
  (add-hook 'org-agenda-mode-hook (lambda () (mixed-pitch-mode -1))))

I have an issue with org-modes emphasis markers: I find them ugly. I can of course hide them if I simply set org-hide-emphasis-markers to t, but it makes editing hard since I never know whether I am before or after the emphasis marker when editing near the beginning/end of an emphasized region. org-appear fixes this issue so that it shows the emphasis markers only when the cursor is in the emphasized region, otherwise they will remain hidden! Very cool!

(use-package org-appear
  :after org
  :straight (:build t)
  :hook (org-mode . org-appear-mode)
  :config
  (setq org-appear-autoemphasis   t
        org-hide-emphasis-markers t
        org-appear-autolinks      t
        org-appear-autoentities   t
        org-appear-autosubmarkers t)
  (run-at-time nil nil #'org-appear--set-elements))

Similarly, LaTeX fragments previews are nice and all, but if I have my cursor on it, I want to see the LaTeX source code and modify it, not just the generated image!

(use-package org-fragtog
  :defer t
  :after org
  :straight (:build t)
  :hook (org-mode . org-fragtog-mode))

Org-modern modernizes a bit the appearance of org buffers, including tables, source blocks, and tags, and it applies settings similar to org-superstar which I used to use.

(use-package org-modern
  :straight (:build t)
  :after org
  :defer t
  :custom (org-modern-table nil)
  :hook (org-mode . org-modern-mode)
  :hook (org-agenda-finalize . org-modern-agenda))

org-fancy-priorities change the priority of an org element such as #A to anything user-defined. Lets all-the-iconify this!

(use-package org-fancy-priorities
  :after (org all-the-icons)
  :straight (:build t)
  :hook (org-mode        . org-fancy-priorities-mode)
  :hook (org-agenda-mode . org-fancy-priorities-mode)
  :config
  (setq org-fancy-priorities-list `(,(all-the-icons-faicon "flag"     :height 1.1 :v-adjust 0.0)
                                    ,(all-the-icons-faicon "arrow-up" :height 1.1 :v-adjust 0.0)
                                    ,(all-the-icons-faicon "square"   :height 1.1 :v-adjust 0.0))))

Org Outline Tree is a better way of managing my org files outline.

(use-package org-ol-tree
  :after (org avy)
  :defer t
  :straight (org-ol-tree :build t
                         :host github
                         :type git
                         :repo "Townk/org-ol-tree")
  :general
  (phundrak/major-leader-key
    :packages 'org-ol-tree
    :keymaps 'org-mode-map
    "O" #'org-ol-tree))
(add-hook 'org-mode-hook
          (lambda ()
            (dolist (pair '(("[ ]"         . ?☐)
                            ("[X]"         . ?☑)
                            ("[-]"         . ?❍)
                            ("#+title:"    . ?📕)
                            ("#+TITLE:"    . ?📕)
                            ("#+author:"   . ?✎)
                            ("#+AUTHOR:"   . ?✎)
                            ("#+email:"    . ?📧)
                            ("#+EMAIL:"    . ?📧)
                            ("#+include"   . ?⭳)
                            ("#+INCLUDE"   . ?⭳)
                            ("#+begin_src" . )
                            ("#+BEGIN_SRC" . )
                            ("#+end_src"   . )
                            ("#+END_SRC"   . )))
              (add-to-list 'prettify-symbols-alist pair))
            (prettify-symbols-mode)))

Misc

org-tree-slide is a presentation tool for org-mode.

(use-package org-tree-slide
  :defer t
  :after org
  :straight (:build t)
  :config
  (setq org-tree-slide-skip-done nil)
  :general
  (phundrak/evil
    :keymaps 'org-mode-map
    :packages 'org-tree-slide
    "<f8>" #'org-tree-slide-mode)
  (phundrak/major-leader-key
    :keymaps 'org-tree-slide-mode-map
    :packages 'org-tree-slide
    "d" (lambda () (interactive (setq org-tree-slide-skip-done (not org-tree-slide-skip-done))))
    "p" #'org-tree-slide-move-next-tree
    "n" #'org-tree-slide-move-previous-tree
    "t" #'org-tree-slide-move-next-tree
    "s" #'org-tree-slide-move-previous-tree
    "u" #'org-tree-slide-content))

org-roll is a simple package for tabletop RPGs for rolling dice.

(use-package org-roll
  :defer t
  :after org
  :straight (:build t :type git :host github :repo "zaeph/org-roll"))