hamacs/ha-org-word-processor.org
Howard Abrams dd49acbfd2 Turn on colored Emojis
However, this doesn't seem to be needed for latest MacOS builds of
Emacs 28.
2022-04-09 09:10:55 -07:00

471 lines
22 KiB
Org Mode
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#+TITLE: Org As A Word Processor
#+AUTHOR: Howard X. Abrams
#+DATE: 2020-09-10
#+FILETAGS: :emacs:
A literate programming file for making Org file more readable.
#+BEGIN_SRC emacs-lisp :exports none
;;; ha-org-word-processor --- Making Org file more readable. -*- lexical-binding: t; -*-
;;
;; © 2020-2022 Howard X. Abrams
;; This work is licensed under a Creative Commons Attribution 4.0 International License.
;; See http://creativecommons.org/licenses/by/4.0/
;;
;; Author: Howard X. Abrams <http://gitlab.com/howardabrams>
;; Maintainer: Howard X. Abrams
;; Created: September 10, 2020
;;
;; This file is not part of GNU Emacs.
;;
;; *NB:* Do not edit this file. Instead, edit the original literate file at:
;; ~/other/hamacs/ha-org-word-processor.org
;; Using `find-file-at-point', and tangle the file to recreate this one .
;;
;;; Code:
#+END_SRC
* 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.
#+BEGIN_SRC emacs-lisp
(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)
#+END_SRC
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:
#+BEGIN_SRC emacs-lisp
(setq org-list-demote-modify-bullet '(("*" . "+") ("+" . "-") ("-" . "-")
("1." . "a.") ("a." . "1.")))
#+END_SRC
The =org-indent-indentation-per-level=, which defaults to =2= doesnt really work well with variable-width fonts, so lets make the spaces at the beginning of the line fixed:
#+BEGIN_SRC emacs-lisp
(use-package org
:config
(set-face-attribute 'org-indent nil :inherit 'fixed-pitch))
#+END_SRC
** Typographic Quotes
According to [[http://endlessparentheses.com/prettify-your-quotation-marks.html][Artur Malabarba]] of [[http://endlessparentheses.com/prettify-you-apostrophes.html][Endless Parenthesis]] blog, I type a /straight quote/, ", Emacs actually inserts Unicode rounded quotes, like “this”. This idea isnt 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 [[https://play.google.com/store/apps/details?id=com.orgzly&hl=en_US&gl=US][Orgzly]] and [[https://github.com/amake/orgro][Orgro]].
Stealing his function so that “quotes” just work to insert /rounded/ quotation marks:
#+BEGIN_SRC emacs-lisp
(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))))
#+END_SRC
Stealing function for automatically adding a single quote (not in pairs):
#+BEGIN_SRC emacs-lisp
(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)))))
#+END_SRC
Quote: “From this time forward, I shouldnt have to worry about quotes.”
*Note:* I still need to worry about how quotes affect [[file:ha-org.org::*Spell Checking][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:
#+BEGIN_SRC emacs-lisp
(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))))
#+END_SRC
Next, we just need to change the header sizes:
#+BEGIN_SRC emacs-lisp
(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))
#+END_SRC
While we are at it, lets make sure the code blocks are using my fixed with font:
#+BEGIN_SRC emacs-lisp
(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)
#+END_SRC
Not sure why the above code removes the color of =org-verbatim=, so lets make it stand out slightly:
#+BEGIN_SRC emacs-lisp
(set-face-attribute 'org-verbatim nil :foreground "#aaaacc")
#+END_SRC
And some slight adjustments to the way blocks are displayed:
#+BEGIN_SRC emacs-lisp
(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")
#+END_SRC
And decrease the prominence of the property drawers:
#+BEGIN_SRC emacs-lisp
(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)
#+END_SRC
This process allows us to use =variable-pitch= mode for all org documents.
#+BEGIN_SRC emacs-lisp
(use-package org
:hook (org-mode . variable-pitch-mode))
#+END_SRC
* Superstar
Now that headers are noticeable, I have no reason to see a number of asterisks. I would think that this would work:
#+BEGIN_SRC emacs-lisp :tangle no
(setq org-hide-leading-stars t)
#+END_SRC
But since it doesnt, 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:
#+BEGIN_SRC emacs-lisp :tangle no
(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)
#+END_SRC
Once I used the [[https://github.com/sabof/org-bullets][org-bullets]] package, but we've replaced it with [[https://github.com/integral-dw/org-superstar-mode][org-superstar-mode]], so the following is an improvement, especially with the sub-bullets:
#+BEGIN_SRC emacs-lisp
(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 . ""))))
#+END_SRC
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 [[https://jft.home.blog/2019/07/17/use-unicode-symbol-to-display-org-mode-checkboxes/][Huy Trần]], (and expanded by the [[https://github.com/minad/org-modern][org-modern]] project), we can prettify the list checkboxes as well:
#+BEGIN_SRC emacs-lisp
(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))
#+END_SRC
And now we can attach it to a newly loaded org files:
#+BEGIN_SRC emacs-lisp
(add-hook 'org-mode-hook 'ha-org-prettify-checkboxes)
#+END_SRC
To make it more distinguishable, he also changed the colors:
#+BEGIN_SRC emacs-lisp
(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)
#+END_SRC
* SVG-Based Prettiness
While I'm intrigued with [[https://github.com/rougier][Nicolas P. Rougier]]'s [[https://github.com/rougier/notebook-mode][notebook project]], I really just want to steal their [[https://github.com/rougier/svg-lib][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.
#+BEGIN_SRC emacs-lisp
(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))))))))
#+END_SRC
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.
#+BEGIN_SRC emacs-lisp :tangle no
(use-package notebook
:straight (:type git :protocol ssh :host github :repo "rougier/notebook-mode")
:after org
:hook (org-mode . notebook-mode))
#+END_SRC
* Padding
The [[https://github.com/TonCherAmi/org-padding][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:
#+BEGIN_SRC emacs-lisp
(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))))
#+END_SRC
However, I'm just going to have to write a function to clean this.
#+BEGIN_SRC emacs-lisp :tangle no
(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)))
#+END_SRC
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:
#+BEGIN_SRC emacs-lisp
(defun ha-org-paste ()
(interactive)
(if (eq system-type 'gnu/linux)
(shell-command "xclip -t text/html -o | pandoc -r html -w org" t)))
#+END_SRC
* Presentations
Used to use [[https://github.com/takaxp/org-tree-slide][org-tree-slide]] for showing org files as presentations. Converted to use [[https://github.com/rlister/org-present][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.
#+BEGIN_SRC emacs-lisp
(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))
#+END_SRC
* Technical Artifacts :noexport:
Let's provide a name so that the file can be required:
#+BEGIN_SRC emacs-lisp :exports none
(provide 'ha-org-word-processor)
;;; ha-org-word-processor.el ends here
#+END_SRC
Before you can build this on a new system, make sure that you put the cursor over any of these properties, and hit: ~C-c C-c~
#+DESCRIPTION: A literate programming file for making Org file more readable.
#+PROPERTY: header-args:sh :tangle no
#+PROPERTY: header-args:emacs-lisp :tangle yes
#+PROPERTY: header-args :results none :eval no-export :comments no
#+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil date:nil
#+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil
#+INFOJS_OPT: view:nil toc:nil ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js