However, this doesn't seem to be needed for latest MacOS builds of Emacs 28.
22 KiB
Org As A Word Processor
A literate programming file for making Org file more readable.
Introduction
I like having org-mode files look more like a word processor than having it look like programming code. But that is just me.
General Org Settings
Since I use ellipsis in my writing… to change how org renders a collapsed heading.
(setq org-pretty-entities t
org-ellipsis "⤵" ; …, ➡, ⚡, ▼, ↴, , ∞, ⬎, ⤷, ⤵
org-agenda-breadcrumbs-separator " ❱ "
org-catch-invisible-edits 'show-and-error
org-special-ctrl-a/e t ; Note: Need to get this working with Evil!
org-src-fontify-natively t ; Pretty code blocks
org-hide-emphasis-markers t)
Oh, and as I indent lists, they should change the bulleting in a particular sequence. If I begin with an *
asterisk, I walk down the chain, but with the dashed bullets (my default choice), I just stay with dashed bullets. Numeric bullets should cycle:
(setq org-list-demote-modify-bullet '(("*" . "+") ("+" . "-") ("-" . "-")
("1." . "a.") ("a." . "1.")))
The org-indent-indentation-per-level
, which defaults to 2
doesn’t really work well with variable-width fonts, so let’s make the spaces at the beginning of the line fixed:
(use-package org
:config
(set-face-attribute 'org-indent nil :inherit 'fixed-pitch))
Typographic Quotes
According to Artur Malabarba of Endless Parenthesis blog, I type a straight quote, ", Emacs actually inserts Unicode rounded quotes, like “this”. This idea isn’t how a file is displayed, but actually how the file is made. Time will tell if this idea works with my auxiliary functions on my phone, like Orgzly and Orgro.
Stealing his function so that “quotes” just work to insert rounded quotation marks:
(define-key org-mode-map "\"" #'endless/round-quotes)
(defun endless/round-quotes (italicize)
"Insert “” and leave point in the middle.
With prefix argument ITALICIZE, insert /“”/ instead
\(meant for org-mode).
Inside a code-block, just call `self-insert-command'."
(interactive "P")
(if (and (derived-mode-p 'org-mode)
(org-in-block-p '("src" "latex" "html")))
(call-interactively #'self-insert-command)
(if (looking-at "”[/=_\\*]?")
(goto-char (match-end 0))
(when italicize
(if (derived-mode-p 'markdown-mode)
(insert "__")
(insert "//"))
(forward-char -1))
(insert "“”")
(forward-char -1))))
Stealing function for automatically adding a single quote (not in pairs):
(define-key org-mode-map "'" #'endless/apostrophe)
(defun endless/apostrophe (opening)
"Insert ’ in prose or `self-insert-command' in code.
With prefix argument OPENING, insert ‘’ instead and
leave point in the middle.
Inside a code-block, just call `self-insert-command'."
(interactive "P")
(if (and (derived-mode-p 'org-mode)
(org-in-block-p '("src" "latex" "html")))
(call-interactively #'self-insert-command)
(if (looking-at "['’][=_/\\*]?")
(goto-char (match-end 0))
(if (null opening)
(insert "’")
(insert "‘’")
(forward-char -1)))))
Quote: “From this time forward, I shouldn’t have to worry about quotes.”
Note: I still need to worry about how quotes affect spell checking.
Org Beautify
I really want to use the Org Beautify package, but it overrides my darker themes (and all I really want is headlines to behave).
First step is to make all Org header levels to use the variable font, and be the same color as the default text:
(when window-system
(let ((default-color (face-attribute 'default :foreground)))
(dolist (face '(org-level-1 org-level-2 org-level-3 org-level-4 org-level-5 org-level-6 org-level-7 org-level-8))
(set-face-attribute face nil :height 1.1
:foreground default-color :weight 'bold :font ha-variable-font))))
Next, we just need to change the header sizes:
(when window-system
(set-face-attribute 'org-level-1 nil :height 2.2)
(set-face-attribute 'org-level-2 nil :height 1.8)
(set-face-attribute 'org-level-3 nil :height 1.4)
(set-face-attribute 'org-level-4 nil :height 1.2))
While we are at it, let’s make sure the code blocks are using my fixed with font:
(when window-system
(dolist (face '(org-block org-code org-verbatim org-table org-drawer org-table org-formula
org-special-keyword org-property-value org-document-info-keyword))
(set-face-attribute face nil :inherit 'fixed-pitch :height 0.9)))
(set-face-attribute 'org-table nil :height 1.0)
(set-face-attribute 'org-formula nil :height 1.0)
Not sure why the above code removes the color of org-verbatim
, so let’s make it stand out slightly:
(set-face-attribute 'org-verbatim nil :foreground "#aaaacc")
And some slight adjustments to the way blocks are displayed:
(set-face-attribute 'org-block-begin-line nil :background "#282828")
(set-face-attribute 'org-block nil :height 0.95)
(set-face-attribute 'org-block-end-line nil :background "#262626")
And decrease the prominence of the property drawers:
(set-face-attribute 'org-drawer nil :height 0.8)
(set-face-attribute 'org-property-value nil :height 0.85)
(set-face-attribute 'org-special-keyword nil :height 0.85)
This process allows us to use variable-pitch
mode for all org documents.
(use-package org
:hook (org-mode . variable-pitch-mode))
Superstar
Now that headers are noticeable, I have no reason to see a number of asterisks. I would think that this would work:
(setq org-hide-leading-stars t)
But since it doesn’t, I need to roll my own. I add a hook to standard Org, and since this is a Lisp-2, I can get away with using the same name as the variable:
(defun org-hide-leading-stars ()
(let* ((keyword
`(("^\\(\\*+ \\)\\s-*\\S-" ; Do not hide empty headings!
(1 (put-text-property (match-beginning 1) (match-end 1) 'invisible t)
nil)))))
(font-lock-add-keywords nil keyword)))
(add-hook 'org-mode-hook 'org-hide-leading-stars)
Once I used the org-bullets package, but we've replaced it with org-superstar-mode, so the following is an improvement, especially with the sub-bullets:
(use-package org-superstar
:after org
:straight (:type git :protocol ssh :host github :repo "integral-dw/org-superstar-mode")
:hook (org-mode . org-superstar-mode)
:init
(setq ; org-superstar-headline-bullets-list '("▶")
org-superstar-special-todo-items nil
org-superstar-todo-bullet-alist t
org-superstar-prettify-item-bullets t
org-superstar-item-bullet-alist '((42 . "●") ; *
(43 . "○") ; +
(45 . "•"))))
If this works, then we get the prettier bulleted list:
- Top bullets
-
Plus bullets:
- Apples
- Oranges
- Bananas
-
Minus bullets:
- Dogs
- Cats
- Birds
Checkboxes
According to an idea by Huy Trần, (and expanded by the org-modern project), we can prettify the list checkboxes as well:
(defun ha-org-prettify-checkboxes ()
"Beautify Org Checkbox Symbol"
(push '("[ ]" . "☐") prettify-symbols-alist)
(push '("[X]" . "☐✓") prettify-symbols-alist)
(push '("[-]" . "☐-") prettify-symbols-alist)
(prettify-symbols-mode))
And now we can attach it to a newly loaded org files:
(add-hook 'org-mode-hook 'ha-org-prettify-checkboxes)
To make it more distinguishable, he also changed the colors:
(defface org-checkbox-done-text
'((t (:foreground "#71696A" :strike-through t)))
"Face for the text part of a checked org-mode checkbox.")
(font-lock-add-keywords
'org-mode
`(("^[ \t]*\\(?:[-+*]\\|[0-9]+[).]\\)[ \t]+\\(\\(?:\\[@\\(?:start:\\)?[0-9]+\\][ \t]*\\)?\\[\\(?:X\\|\\([0-9]+\\)/\\2\\)\\][^\n]*\n\\)"
1 'org-checkbox-done-text prepend))
'append)
SVG-Based Prettiness
While I'm intrigued with Nicolas P. Rougier's notebook project, I really just want to steal their svg-lib project that allows me to create and display various SVG objects, namely tags, progress bars, progress pies and icons. Each object fits nicely in a text buffer ensuring width is an integer multiple of character width.
(when (image-type-available-p 'svg)
(use-package svg-lib
:straight (:type git :protocol ssh :host github :repo "rougier/svg-lib"))
(use-package svg-tag-mode
;; :straight (:type git :protocol ssh :host github :repo "rougier/svg-tag-mode")
:straight (:type git :protocol ssh :host github :repo "howardabrams/svg-tag-mode")
:hook ((org-mode . svg-tag-mode)
(emacs-lisp-mode . svg-tag-mode)
(python-mode . svg-tag-mode))
:config
(setq svg-tag-tags
`(("NOTE:" .
((lambda (tag)
(svg-tag-make "NOTE" :face 'org-done
:inverse nil :margin 0 :radius 2))))
;; ------------------------------------------------------------
;; Two definitions of TODO That include the single line message
;; ------------------------------------------------------------
(,(rx word-start "TODO" word-end) .
((lambda (tag)
(svg-tag-make "TODO" :face 'org-todo
:radius 2 :inverse t :margin 0 :padding 0 :crop-right t))))
(,(rx word-start "TODO"
(group (one-or-more space) (zero-or-more not-newline)) line-end) .
((lambda (tag)
(svg-tag-make tag :face 'org-todo
:radius 2 :margin 0 :padding 0 :crop-left t))))
;; ------------------------------------------------------------
;; Two definitions of tag combo, like :TAG:foobar:
;; ------------------------------------------------------------
;; Where the first is inversed:
;; (,(rx (group ":" (one-or-more (any alpha "-")) ":")
;; (one-or-more (any alphanumeric "-" "_")) ":") .
;; ((lambda (tag)
;; (svg-tag-make tag :beg 1 :end -1 :inverse t :margin 0 :crop-right t))))
;; And the second is not:
;; (,(rx ":" (one-or-more (any alpha "-"))
;; (group ":" (one-or-more (any alphanumeric "-" "_")) ":")) .
;; ((lambda (tag)
;; (svg-tag-make tag :beg 1 :end -1 :margin 0 :crop-left t))))
;; ------------------------------------------------------------
;; Org-mode :TAGS: shown as a box:
;; ------------------------------------------------------------
(,(rx (or line-start space)
(group ":" (one-or-more (any alpha "-")) ":")
(or space line-end)) .
((lambda (tag)
(svg-tag-make tag :face 'org-drawer :beg 1 :end -1 :margin 0))))
;; ------------------------------------------------------------
;; Org-mode blocks #+BEGIN_SRC language ... #+END_SRC
;; ------------------------------------------------------------
(,(rx line-start (zero-or-more space) "#+BEGIN_SRC") .
((lambda (tag)
(svg-tag-make "RUN" :face 'org-block-begin-line
:inverse t :margin 0 :crop-right t))
(lambda () (interactive) (org-ctrl-c-ctrl-c))
"Run this block of code"))
(,(rx line-start (zero-or-more space) "#+BEGIN_SRC"
(1+ space) (group (one-or-more (any alpha "-"))) word-end) .
((lambda (tag)
(svg-tag-make tag :face 'org-block-begin-line
:margin 0 :crop-left t))
(lambda () (interactive) (org-ctrl-c-ctrl-c))
"Run this block of code"))
(,(rx line-start (zero-or-more space) "#+END_SRC" word-end) .
((lambda (tag)
(svg-tag-make "END" :face 'org-block-end-line :inverse t))))
("^#\\+RESULTS:" .
((lambda (tag)
(svg-tag-make "RESULTS" :face 'org-block-end-line :inverse t))))
(,(rx line-start (zero-or-more space) "#+ATTR" (zero-or-more (any alpha "_")) ":") .
((lambda (tag)
(svg-tag-make "ATTR" :face 'org-block-begin-line :inverse t))))
("^#\\+NAME:" . ((lambda (tag) (svg-tag-make "NAME" :face 'org-meta-line))))
("^#\\+HEADER:" . ((lambda (tag) (svg-tag-make "HEADER" :face 'org-meta-line))))
("^#\\+BEGIN_EXAMPLE" . ((lambda (tag) (svg-tag-make "CODE" :face 'org-block-begin-line))))
("^#\\+END_EXAMPLE" . ((lambda (tag) (svg-tag-make "END" :face 'org-block-end-line))))
("^#\\+begin_quote" . ((lambda (tag) (svg-tag-make "“" :face 'org-block-begin-line))))
("^#\\+end_quote" . ((lambda (tag) (svg-tag-make "”" :face 'org-block-end-line))))
;; ------------------------------------------------------------
;; Things like #+OPTIONS: and property definitions
;; ------------------------------------------------------------
("#\\+PROPERTY:" . ((lambda (tag) (svg-tag-make "PROPERTY" :face 'org-meta-line))))
("#\\+OPTIONS:" . ((lambda (tag) (svg-tag-make "OPTIONS" :face 'org-meta-line))))))))
What does do? Here are some examples: TODO Marks comments for tasks (this can be in source files too). NOTE: Highlights comments and other notes. :PROP:tag: are highlighted as two parts of the same tag And :TAG: with colons are highlighted, which include :PROPERTY: drawers. Org-specific #+PROPERTY: entries are highlighted.
(use-package notebook
:straight (:type git :protocol ssh :host github :repo "rougier/notebook-mode")
:after org
:hook (org-mode . notebook-mode))
Padding
The org-padding project looks places extra space before and after headers and blocks (essentially leading), to create a more word-processor-y experience. Great idea, however, I have spent a lot of extra time entering blank lines before and after my headers and blocks:
(use-package org-padding
:straight (:type git :protocol ssh :host github :repo "TonCherAmi/org-padding")
:hook
(org-mode . org-padding-mode)
:config
(setq org-padding-block-begin-line-padding '(0.5 . 0.3)
org-padding-block-end-line-padding '(0.1 . 0.5)
org-padding-heading-padding-alist
'((4.0 . 1.5) (3.0 . 0.5) (3.0 . 0.5) (3.0 . 0.5) (2.5 . 0.5) (2.0 . 0.5) (1.5 . 0.5) (0.5 . 0.5))))
However, I'm just going to have to write a function to clean this.
(defun ha-remove-superfluous-org-padding ()
(interactive)
(goto-char (point-min))
(ha-remove-org-header-padding)
(goto-char (point-min))
(ha-remove-org-block-padding))
(defun ha-remove-org-header-padding ()
;; (goto-char (point-min))
(while (re-search-forward (rx (optional bol (zero-or-more space) eol "\n")
(group bol (one-or-more "*") (one-or-more space) (one-or-more any) "\n")
(optional bol (zero-or-more space) eol "\n")) nil t)
(replace-match (match-string 1) nil :no-error)))
(defun ha-remove-org-block-padding ()
;; (goto-char (point-min))
(while (re-search-forward (rx (optional bol (zero-or-more space) eol "\n")
(group bol (zero-or-more space) "#+BEGIN" (one-or-more any) eol "\n"
(zero-or-more (group bol (zero-or-more any) eol "\n"))
bol (zero-or-more space) "#+END" (zero-or-more any) eol "\n")
(optional bol (zero-or-more space) eol "\n")) nil t)
(replace-match (match-string 1) nil :no-error)))
Now that is some complicated regular expressions.
Pasting
I like the idea that I will paste HTML text from the clipboard and have it converted to org-formatted text:
(defun ha-org-paste ()
(interactive)
(if (eq system-type 'gnu/linux)
(shell-command "xclip -t text/html -o | pandoc -r html -w org" t)))
Presentations
Used to use org-tree-slide for showing org files as presentations. Converted to use org-present. I love the hooks as that makes it easier to pull out much of my demo-it
configuration. My concern with org-present
is that it only jumps from one top-level to another top-level header.
(use-package org-present
:init
(defvar ha-org-present-mode-line mode-line-format "Cache previous mode-line format state")
:config
(defun org-blocks-hide-headers ()
"Make the headers and other block metadata invisible. See `org-blocks-show-headers'."
(add-to-invisibility-spec 'ha-org-block-headers)
(defun hide-this (regexp)
(goto-char (point-min))
(while (re-search-forward regexp nil t)
(let ((start (match-beginning 0)) (end (1+ (match-end 0))))
(overlay-put (make-overlay start end) 'invisible 'ha-org-block-headers))))
(defun hide-these (patterns)
(when patterns
(hide-this (car patterns))
(hide-these (cdr patterns))))
(save-excursion
(hide-these (list (rx bol (zero-or-more space)
"#+" (or "begin" "end") "_"
(one-or-more any) eol)
(rx bol (zero-or-more space)
"#+" (or "name" "header" "results" "property" "options"
"filetags") ":"
(zero-or-more any) eol)
(rx bol (zero-or-more space)
":" (or "properties" "header-args" "end") ":"
(zero-or-more any) eol)))))
(defun org-blocks-show-headers ()
"Un-invisibilize the headers and other block metadata invisible.
In other words, this undoes what `org-blocks-hide-headers' did."
(remove-from-invisibility-spec 'ha-org-block-headers))
(defun org-present-start ()
(goto-char (point-min)) (re-search-forward (rx bol "*"))
(org-blocks-hide-headers)
(org-present-big)
(setq mode-line-format nil)
(org-display-inline-images)
(blink-cursor-mode -1)
(setq cursor-type nil))
(defun org-present-end ()
(org-present-small)
(org-blocks-show-headers)
(setq mode-line-format ha-org-present-mode-line)
(setq cursor-type t)
(blink-cursor-mode 1)
(org-present-read-write))
:hook
(org-present-mode . org-present-start)
(org-present-mode-quit . org-present-end))