org-special-block-extras
A unified interface for special blocks and links: defblock
Abstract
The aim is to write something once using Org-mode markup then generate the markup for multiple backends. That is, write once, generate many!
In particular, we are concerned with ‘custom’, or ‘special’, blocks which delimit how a particular region of text is supposed to be formatted according to the possible export backends. In some sense, special blocks are meta-blocks. Rather than writing text in, say, LaTeX environments using LaTeX commands or in HTML
div
's using HTML tags, we promote using Org-mode markup in special blocks —Org markup cannot be used explicitly within HTML or LaTeX environments.Special blocks, like
centre
andquote
, allow us to use Org-mode as the primary interface regardless of whether the final result is an HTML or PDF article; sometime we need to make our own special blocks to avoid a duplication of effort. However, this can be difficult and may require familiarity with relatively advanced ELisp concepts, such as macros and hooks; as such, users may not be willing to put in the time and instead use ad-hoc solutions.We present a new macro, defblock, which is similar in-spirit to Lisp's standard defun except that where the latter defines functions, ours defines new special blocks for Emacs' Org-mode —as well as, simultaneously, defining new Org link types. Besides the macro, the primary contribution of this effort is an interface for special blocks that admits arguments and is familar to Org users —namely, we ‘try to reuse’ the familiar
src
-block interface, including header-args, but for special blocks.It is hoped that the ease of creating custom special blocks will be a gateway for many Emacs users to start using Lisp.
A 5-page PDF covering ELisp fundamentals can be found here.
This article is featured in EmacsConf2020, with slides here: No pictures, instead we use this system to make the slides have a variety of styling information; i.e., we write Org and the result looks nice. “Look ma, no HTML required!”
Figure 1: Write in Emacs using Org-mode, export beautifully to HTML or LaTeX
Super Simple Intro to Emacs’ Org-mode
Emacs’ Org-mode is an outliner, a rich markup language, spreadsheet tool, literate programming system, and so much more. It is an impressive reason to use Emacs (•̀ᴗ•́)و
Org-mode syntax is very natural; e.g., the following is Org-mode!
+ Numbered and bulleted lists are as expected. - Do the things: 1. This first 2. This second 44. [@44] This forty-fourth - =[@𝓃]= at the beginning of an iterm forces list numbering to start at 𝓃 - =[ ]= or =[X]= at the beginning for checkbox lists - Use =Alt ↑, ↓= to move items up and down lists; renumbering happens automatically. + Definitions lists: ~- term :: def~ + Use a comment, such as =# separator=, between two lists to communicate that these are two lists that /happen/ to be one after the other. Or use any /non-indented/ text to split a list into two. * My top heading, section words ** Child heading, subsection more words *** Grandchild heading, subsubsection even more!
Export In Emacs, press C-c C-e h o to obtain an HTML webpage ---like this one!— of the Org-mode markup; use C-c C-e l o to obtain a PDF rendition.
You can try Org-mode notation and see how it renders live at: http://mooz.github.io/org-js/
You make a heading by writing * heading
at the start of a line, then you can
TAB to fold/unfold its contents. A table of contents, figures, tables can be
requested as follows:
# figures not implemented in the HTML backend # The 𝓃 is optional and denotes headline depth #+toc: headlines 𝓃 #+toc: figures #+toc: tables
Markup elements can be nested.
Syntax Result /Emphasise/
, italicsEmphasise *Strong*
, boldStrong */very strongly/*
, bold italicsvery strongly =verbatim=
, monospaced typewriterverbatim
+deleted+
deleted_inserted_
inserted super^{script}ed
superscripted sub_{scripted}ed
subscripteded - Markup can span across multiple lines, by default no more than 2.
- In general, markup cannot be ‘in the middle’ of a word.
- New lines demarcate paragraphs
- Use
\\
to force line breaks without starting a new paragraph - Use at least 5 dashes,
-----
, to form a horizontal rule
provides support for numerous other kinds of markup elements, such as
red:hello
which becomes “ hello ”.
Working with tables
#+ATTR_HTML: :width 100% #+name: my-tbl #+caption: Example table | Who? | What? | |------+-------| | me | Emacs | | you | Org |
Note the horizontal rule makes a header row and is formed by typing | - then pressing TAB . You can TAB between cells.
Working with links
Link syntax is [[source url][description]]
; e.g., we can refer to the above
table with [[my-tbl][woah]]
.
Likewise for images: file:path-to-image.
Mathematics
Source
\[ \sin^2 x + \cos^2 x = \int_\pi^{\pi + 1} 1 dx = {3 \over 3} \]
Result
\[ \sin^2 x + \cos^2 x = \int_\pi^{\pi + 1} 1 dx = {3 \over 3} \]
- Instead of
\[...\]
, which displays a formula on its own line, centred, use$...$
to show a formula inline. - Captioned equations are numbered and can be referenced via links, as shown below.
Source
#+name: euler \begin{equation} e ^ {i \pi} + 1 = 0 \end{equation} See equation [[euler]].
Result
\begin{equation} \label{org44b9165} e ^ {i \pi} + 1 = 0 \end{equation}See equation \eqref{org44b9165}.
Source code
Source
#+BEGIN_SRC C -n int tot = 1; (ref:start) for (int i = 0; i != 10; i++) (ref:loop) tot *= i; (ref:next) printf("The factorial of 10 is %d", tot); #+END_SRC
Result
1: int tot = 1; (start) 2: for (int i = 0; i != 10; i++) (loop) 3: tot *= i; (next) 4: printf("The factorial of 10 is %d", tot);
The labels (ref:name)
refer to the lines in the source code and can be
referenced with link syntax: [[(name)]]
. Hovering over the link, in the HTML
export, will dynamically highlight the corresponding line of code. To strip-out
the labels from the displayed block, use -r -n
in the header so it becomes
#+begin_src C -r -n
, now the references become line numbers.
Another reason to use Org:
If you use :results raw
, you obtain dynamic templates that may use Org-markup:
Source
#+BEGIN_SRC C printf("*bold* +%d+ (strikethrough) /slanted/", 12345); #+END_SRC ♯+RESULTS: *bold* +12345+ (strikethrough) /slanted/
Result
printf("*bold* +%d+ (strikethrough) /slanted/", 12345);
♯+RESULTS:
bold 12345 (strikethrough) slanted
The #+RESULTS:
is obtained by pressing C-c C-c on the src
block, to execute
it and obtain its result.
Also: Notice that a C program can be run without a main
;-)
That is, we can write code in between prose that is intended to be read like an essay:
Single source of truth: This mini-tutorial can be included into other Org files by declaring
#+include: ~/.emacs.d/init.org::#Mini-tutorial-on-Org-mode |
For more, see https://orgmode.org/features.html.
Table of Contents
- 1. A unified interface for special blocks and links:
defblock
- 2. Editor Comments
- 3. Folded Details —As well as boxed text and subtle colours
- 4. Parallel
- 5. Colours
- 6. Nice Keystroke Renditions: kbd:C-h_h
- 7. “Link Here!” & OctoIcons
- 8. Badge Links
- 9. Tooltips for Glossaries, Dictionaries, and Documentation
- 10. Marginal, “one-off”, remarks
- 11. Equational Proofs
- 12. Emacs faces for links
- 13. Summary
The full article may be read as a PDF or as HTML —or visit the repo. Installation instructions are below.
1 A unified interface for special blocks and links: defblock
An Org-mode block of type 𝒳 is a bunch of text enclosed in #+begin_𝒳
and
#+end_𝒳
, upon export it modifies the text —e.g., to center it, or to display
it as code. We show how to make new blocks using a simple interface
---defblock— that lets users treat 𝒳 as a string-valued function in the style
of defun.
The notable features of the system are as follows.
- Familiar
defun
syntax for making block ---defblock
- Familiar
src
syntax for passing arguments —e.g.,:key value
- Modular: New blocks can be made out of existing blocks really quickly using
blockcall
—similar to Lisp'sfuncall
.
EmacsConf 2020 Abstract
Users will generally only make use of a few predefined special blocks, such as
example, centre, quote
, and will not bother with the effort required to make new
ones. When new encapsulating notions are required, users will either fallback on
HTML or LaTeX specific solutions, usually littered with #+ATTR
clauses to pass
around configurations or parameters.
Efforts have been exerted to mitigate the trouble of producing new special blocks. However, the issue of passing parameters is still handled in a clumsy fashion; e.g., by having parameters be expressed in a special block's content using specific keywords.
We present a novel approach to making special blocks in a familiar fashion and
their use also in a familiar fashion. We achieve the former by presenting
defblock
, an anaphoric macro exceedingly similar to defun
, and for the latter we
mimic the usual src
-block syntax for argument passing to support special blocks.
For instance, here is a sample declaration.
(defblock stutter () (reps 2) "Output the CONTENTS of the block REPS many times" (org-parse (s-repeat reps contents)))
Here is an invocation that passes an optional argument; which defaults to 2 when not given.
#+begin_stutter 5 Emacs for the win :-) #+end_stutter 5
Upon export, to HTML or LaTeX for instance, the contents of this block are
repeated (stuttered) 5 times. The use of src
-like invocation may lead to a
decrease in #+ATTR
clauses.
In the presentation, we aim to show a few practical special blocks that users may want: A block that …
- translates some selected text —useful for multilingual blogs
- hides some selected text —useful for learning, quizzes
- folds/boxes text —useful in blogs for folding away details
In particular, all of these examples will be around ~5 lines long!
We also have a larger collection of more useful block types, already implemented.
The notable features of the system are as follows.
- Familiar
defun
syntax for making block ---defblock
- Familiar
src
syntax for passing arguments —e.g.,:key value
- Fine-grained control over export translation phases —c.f.,
org-parse
above - Modular: New blocks can be made out of existing blocks really quickly using
blockcall
—similar to Lisp'sfuncall
. We will show how to fuse two blocks to make a new one, also within ~5 lines.
It is hoped that the ease of creating custom special blocks will be a gateway for many Emacs users to start using Lisp.
1.1 What is a special block?
An Org mode block is a region of text surrounded by #+BEGIN_𝒳 … #+END_𝒳
; they
serve various purposes as summarised in the table below. However, we shall
use such blocks to execute arbitrary code on their contents.
𝒳 | Description |
---|---|
example |
Format text verbatim, leaving markup as is |
src |
Format source code |
center |
Centre text |
quote |
Format text as a quotation —ignore line breaks |
verse |
Every line is appended with a line break |
tiny |
Render text in a small font; likewise footnotesize |
comment |
Completely omit the text from export |
- They can be folded and unfolded in Emacs by pressing TAB in the
#+BEGIN
line. - The contents of blocks can be highlighted as if they were of language ℒ such
as
org, html, latex, haskell, lisp, python, …
by writing#+BEGIN_𝒳 ℒ
on the starting line, where𝒳
is the name of the block type. - Verbatim environments
src
andexample
may be followed by switch-n
to display line numbers for their contents.
I use snippets in my init to quickly insert special blocks (•̀ᴗ•́)و
You can ‘zoom in temporarily’, narrowing your focus to only on a particular
block, with org-narrow-to-element, C-x n e
, to make your window only show
the block. Then use C-x n w
to widen your vision of the buffer's contents.
Warning! Special blocks of the same kind do not nest!
By their very nature, special blocks of the same name cannot be nested —e.g.,
try to put one quote
block within another and see what (does not) happen.
Moreover, special blocks cannot contain unicode in their names and no
underscore, ‘_’, in their names; e.g., a special block named quote₀
will
actually refer to quote
.
Our goal is to turn Org blocks into LaTeX environments and HTML divs.
Why not use LaTeX or HTML environments directly?
- Can no longer use Org markup in such settings.
- Committed to one specific export type.
[Aside:
The export syntax @@backend: 𝒳@@
inserts text 𝒳 literally as-is precisely when
the current backend being exported to is backend
. This is useful for inserting
html
snippets or latex
commands. We can use @@comment: 𝒳@@
to mimic inline
comments ;-) —Since there is [hopefully] no backend named comment
.
In general, a “special block” such as
#+begin_𝒳 I /love/ Emacs! #+end_𝒳
Exports to LaTeX as:
\begin{𝒳} I \emph{love} Emacs! \end{𝒳}
Exports to HTML as:
<div class="𝒳"> I <em>love</em> Emacs! </div>
Notice that the standard org markup is also translated according to the export type.
If the 𝒳
environment exists in a backend —e.g., by some \usepackage{⋯}
or
manually with
\newenvironment{𝒳}{⋯}{⋯}
in LaTeX— then the file will compile
without error. Otherwise, you need to ensure it exists —e.g., by defining the
backend formatting manually yourself.
[Aside:
LaTeX packages that a user needs consistently are declared in the
list org-latex-packages-alist
. See its documentation, with C-h o
,
to learn more. To export to your own LaTeX classes, C-h o org-latex-classes
.
What is an HTML ‘div’?
A div
tag defines a division or a section in an HTML document that is styled in
a particular fashion or has JavaScript code applied to it. For example
—placing the following in an #+begin_export html ⋯ #+end_export
— results in
a section of text that is editable by the user —i.e., one can just alter text
in-place— and its foreground colour is red, while its background colour is
light blue, and it has an uninformative tooltip.
Source
<div contenteditable="true" title="woah, a tool tip!" style="color:red; background-color:lightblue"> This is some editable text! Click me & type! </div>
Result
What is a CSS ‘class’?
To use a collection of style settings repeatedly, we may declare them in a class
—which is just an alias for the ;-separated list of attribute:value
pairs. Then our div
's refer to that particular class
name.
For example, in an HTML export block, we may declare the following style class
named red
.
#+begin_export html <style> .red { color:red; } </style> #+end_export
Now, the above syntax with 𝒳
replaced by red
works as desired in HTML export:
HTML now knows of a class named red
.
For instance, now this
#+begin_red
I /love/ Emacs!
#+end_red
Results in:
I love Emacs!
This approach, however, will not work if we want to produce LaTeX and so requires a duplication of efforts. We would need to declare such formatting once for each backend.
1.2 How do I make a new link type?
Sometimes using a block is too verbose and it'd be better to ‘inline’ it; for this, we use Org's link mechanism.
Use (org-link-set-parameters params)
to add a
new link type —an older obsolete method is org-add-link-type
. The list of all
supported link types is org-link-parameters
; its documentation identifies the
possibilities for params
.
Let's produce an example link type, then discuss its code.
Intended usage:
Raw use salam and descriptive, using ‘example’ link type ^_^
1: (org-link-set-parameters 2: ;; The name of the new link type, usage: “example:label” 3: "example" 4: 5: ;; When you click on such links, “let me google that for you” happens 6: :follow (lambda (label) (browse-url (concat "https://lmgtfy.com/?q=" label))) 7: 8: ;; Upon export, make it a “let me google that for you” link 9: :export (lambda (label description backend) 10: (format (pcase backend 11: ('html "<a href=\"%s\">%s</a>") 12: ('latex "\\href{%s}{%s}") 13: (_ "I don’t know how to export that!")) 14: (concat "https://lmgtfy.com/?q=" label) 15: (or description label))) 16: 17: ;; These links should *never* be folded in descriptive display; 18: ;; i.e., “[[example:lable][description]]” will always appear verbatim 19: ;; and not hide the first pair […]. 20: ;; :display 'full 21: 22: ;; The tooltip alongside a link 23: :help-echo (lambda (window object position) 24: (save-excursion 25: (goto-char position) 26: (-let* (((&plist :path :format :raw-link :contents-begin :contents-end) 27: (cadr (org-element-context))) 28: ;; (org-element-property :path (org-element-context)) 29: (description 30: (when (equal format 'bracket) 31: (copy-region-as-kill contents-begin contents-end) 32: (substring-no-properties (car kill-ring))))) 33: (format "“%s” :: Let me google “%s” for you -__- %s and %s" 34: raw-link (or description raw-link))))) 35: 36: ;; How should these links be displayed 37: :face '(:foreground "red" :weight bold 38: :underline "orange" :overline "orange") 39: 40: ;; Any special keybindings when cursour is on this link type? 41: ;; On ‘example:’ links, C-n/p to go to the next/previous such links. 42: :keymap (let ((map (copy-keymap org-mouse-map))) 43: ;; Populate the keymap 44: (define-key map (kbd "C-p") 45: (lambda () (interactive) (re-search-backward "example:" nil t))) 46: (define-key map (kbd "C-n") 47: (lambda () (interactive) (re-search-forward "example:" nil t))) 48: ;; Return the keymap 49: map))
- Line 3
"example"
Add a new
example
link type.- If the type already exists, update it with the given arguments.
The syntax for a raw link is
example:path
and for the bracketed descriptive form[[example:path][description]]
.- Some of my intended uses for links including colouring text and doing nothing else, as such the terminology ‘path’ is not sufficiently generic and so I use the designation ‘label’ instead.
- Line 6
:follow
What should happen when a user clicks on such links?
This is a function taking the link path as the single argument and does whatever is necessary to “follow the link”, for example find a file or display a message. In our case, we open the user's browser and go to a particular URL.
- Line 9
:export
How should this link type be exported to HTML, LaTeX, etc?
This is a three-argument function that formats the link according to the given backend, the resulting string value os placed literally into the exported file. Its arguments are:
label
⇒ the path of the link, the text after the link type prefixdescription
⇒ the description of the link, if anybackend
⇒ the export format, a symbol likehtml
orlatex
orascii
.
In our example above, we return different values depending on the
backend
value.- If
:export
is not provided, default Org-link exportation happens.
- Line 20
:display
- Should links be prettily folded away when a description is provided?
- Line 23
:help-echo
What should happen when the user's mouse is over the link?
This is either a string or a string-valued function that takes the current window, the current buffer object, and its position in the current window.
In our example link, we go to the position of the object, destructure the Org link's properties using
-let
, find the description of the link, if any, then provide a string based on the link's path and description.
The general textual property ‘help-echo’
We may use help-echo
to attach tooltips to arbitrary text in a file, as
follows. I have found this to be useful in metaprogramming to have
elaborated, generated, code shown as a tooltip attached to its named
specification.
;; Nearly instantaneous display of tooltips. (setq tooltip-delay 0) ;; Give user 30 seconds before tooltip automatically disappears. (setq tooltip-hide-delay 300) (defun tooltipify (phrase notification &optional underline) "Add a tooltip to every instance of PHRASE to show NOTIFICATION. We only add tooltips to PHRASE as a standalone word, not as a subword. If UNDERLINE is provided, we underline the given PHRASE so as to provide a visual clue that it has a tooltip attched to it. The PHRASE is taken literally; no regexp operators are recognised." (assert (stringp phrase)) (assert (stringp notification)) (save-excursion ;; Return cursour to current-point afterwards. (goto-char 1) ;; The \b are for empty-string at the start or end of a word. (while (search-forward-regexp (format "\\b%s\\b" (regexp-quote phrase)) (point-max) t) ;; (add-text-properties x y ps) ;; ⇒ Override properties ps for all text between x and y. (add-text-properties (match-beginning 0) (match-end 0) (list 'help-echo (s-trim notification))))) ;; Example use (tooltipify "Line" "A sequential formatation of entities or the trace of a particle in linear motion")
We will use the tooltip documentation later on ^_^
Useful info on tooltips:
- Line 37
:face
What textual properties do these links possess?
This is either a face or a face-valued function that takes the current link's path label as the only argument. That is, we could change the face according to the link's label —which is what we will do for the
color
link type as in[[color:brown][hello]]
will be rendered in brown text.- If
:face
is not provided, the default underlined blue face for Org links is used. - Learn more about faces!
- If
- More
- See
org-link-parameters
for documentation on more parameters.
1.3 The Core Utility: defblock
and friends
To have a unified, and pleasant, interface for declaring new blocks and links, we take the following approach:
- ( Fuse the process of link generation and special block support into one macro, defblock which is like defun. )
- The user writes as string-valued function named 𝒳, possibly with arguments,
that has access to a
contents
andbackend
variables.
‘defblock’ Implementation
(defvar org-special-block-extras--supported-blocks nil "Which special blocks, defined with DEFBLOCK, are supported.") (cl-defmacro org-special-block-extras-defblock (name main-arg kwds &rest experimental&&docstring&&body) "Declare a new special block, and link, in the style of DEFUN. A full featured example is at the end of this documentation string. This is an anaphoric macro that provides export support for special blocks *and* links named NAME. Just as an Org-mode src-block consumes as main argument the language for the src block, our special blocks too consume a MAIN-ARG; it may be a symbol or a cons-list consisting of a symbolic name (with which to refer to the main argument in the definition of the block) followed by a default value, then, optionally, any information for a one-time setup of the associated link type. The main arg may be a sequence of symbols separated by spaces, and a few punctuation with the exception of comma ‘,’ since it is a special Lisp operator. In doubt, enclose the main arg in quotes. Then, just as Org-mode src blocks consume key-value pairs, our special blocks consume a number of KWDS, which is a list of the form (key₀ value₀ … keyₙ valueₙ). After that is an optional DOCSTRING, a familar feature of DEFUN. The docstring is displayed as part of the tooltip for the produced link type. Finally, the BODY is a (sequence of) Lisp forms ---no progn needed--- that may refer to the names BACKEND and CONTENTS which refer to the current export backend and the contents of the special block ---or the description clause of a link. CONTENTS refers to an Org-mode parsed string; i.e., Org-markup is acknowledged. In, hopefully, rare circumstances, one may refer to RAW-CONTENTS to look at the fully unparsed contents. Finally, this macro exposes two functions: + ORG-EXPORT: Wrap the argument in an export block for the current backend. + ORG-PARSE: This should ONLY be called within an ORG-EXPORT call, to escape text to Org, and out of the export block. ---------------------------------------------------------------------- TLDR for EXPERIMENTAL&&DOCSTRING&&BODY, the first two parts are optional; they're a symbol, a string, then the main body. The symbol, OSPE-RESPECT-NEWLINES?, when present enables a highly experimental [i.e., do *not* use it!] feature: No new lines for blocks in HTML export. Its need rose from developing the MARGIN block type. ---------------------------------------------------------------------- The relationship between links and special blocks: [ [type:label][description]] ≈ #+begin_type label description #+end_type ---------------------------------------------------------------------- Example declaration, with all possible features shown: ;; We can use variable values when defining new blocks (setq angry-red '(:foreground \"red\" :weight bold)) (defblock remark (editor \"Editor Remark\" :face angry-red) (color \"red\" signoff \"\") \"Top level (HTML & LaTeX)OSPE-RESPECT-NEWLINES? editorial remarks; in Emacs they're angry red.\" (format (if (equal backend 'html) \"<strong style=\\\"color: %s;\\\">⟦%s: %s%s⟧</strong>\" \"{\\color{%s}\\bfseries %s: %s%s}\") color editor contents signoff)) ;; I don't want to change the definition, but I'd like to have ;; the following as personalised defaults for the “remark” block. ;; OR, I'd like to set this for links, which do not have argument options. (defblock-header-args remark :main-arg \"Jasim Jameson\" :signoff \"( Aim for success! )\") Three example uses: ;; ⟨0⟩ As a special blocks with arguments given. #+begin_remark Bobbert Barakallah :signoff \"Thank-you for pointing this out!\" :color green I was trying to explain that ${\large (n × (n + 1) \over 2}$ is always an integer. #+end_remark ;; ⟨1⟩ As a terse link, using default values for the args. ;; Notice that Org-mode formatting is recoqgnised even in links. [ [remark:Jasim Jameson][Why are you taking about “$\mathsf{even}$” here?]] ;; ⟨2⟩ So terse that no editor name is provided. [ [remark:][Please improve your transition sentences.]] ;; ⟨★⟩ Unlike 0, examples 1 and 2 will have the default SIGNOFF ;; catenated as well as the default red color. " ;; ⇨ The special block support ;; (add-to-list 'org-special-block-extras--supported-blocks name) ;; global var ;; Identify which of the optional features is present... (let (ospe-respect-newlines? docstring body) (if (symbolp (first experimental&&docstring&&body)) ;; okay we have a newline declaration, but do we ALSO have a doc-string? (if (stringp (second experimental&&docstring&&body)) (setq ospe-respect-newlines? t docstring (second experimental&&docstring&&body) body (cddr experimental&&docstring&&body)) (setq ospe-respect-newlines? t body (rest experimental&&docstring&&body))) ;; no newline declaration... ;; maybe we have a docstring? (if (stringp (first experimental&&docstring&&body)) (setq docstring (first experimental&&docstring&&body) body (rest experimental&&docstring&&body)) ;; else neither newline-declaration now docstring (setq body experimental&&docstring&&body))) `(progn ;; Produce an associated Lisp function ,(org-special-block-extras-defblock---support-block-type name docstring (if (consp `,main-arg) (car main-arg) 'main-arg) ;; main argument's name (cadr main-arg) ;; main argument's value kwds body ;; MA: I'd like it to be always ‘true’, but it's experimental and breaks so much stuff. ospe-respect-newlines? ) ;; ⇨ The link type support ;; The ‘main-arg’ may contain a special key ‘:link-type’ whose contents ;; are dumped here verbatim. ;; ‘(main-arg-name main-arg-val :face … :follow …)’ ,(org-special-block-extras-defblock---support-link-type name (cddr main-arg) docstring)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; WHERE ... ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (cl-defmethod org-special-block-extras-defblock---support-block-type (name docstring main-arg-name main-arg-value kwds body ospe-respect-newlines?) "Helper method for org-special-block-extras-defblock. This method creates an Org block type's associated Lisp function. NAME, string: The name of the block type. DOCSTRING, string: Documentation of block. MAIN-ARG-NAME: Essentially main-arg's name MAIN-ARG-VALUE: Essentially main-arg's value KWDS, plist: Keyword-value pairs BODY, list: Code to be executed" `(cl-defun ,(intern (format "org-special-block-extras--%s" name)) (backend raw-contents &optional ;; ,(car main-arg) ,main-arg-name &rest _ &key (ospe-link? nil) ,@(-partition 2 kwds)) ,docstring ;; Use default for main argument (when (and ',main-arg-name (s-blank-p ,main-arg-name)) (--if-let (plist-get (cdr (assoc ',name org-special-block-extras--header-args)) :main-arg) (setq ,main-arg-name it) (setq ,main-arg-name ,main-arg-value))) (cl-letf (((symbol-function 'org-export) (lambda (x) "Wrap the given X in an export block for the current backend. One can think of this function as replacing the #+begin_𝒳⋯#+end_𝒳 in-place in your Org document; but really that's done by the ⋯-support-blocks function. " (if ospe-link? x ;; ospe-respect-newlines? is super experimental: It's a bit ugly on the LaTeX side. (cond ((and ,ospe-respect-newlines? (member backend '(html reveal))) (format "@@%s:%s@@" backend (s-replace "\n" (format "@@\n@@%s:" backend) x) backend)) (:else (format "#+begin_export %s \n%s\n#+end_export" backend x)))))) ((symbol-function 'org-parse) (lambda (x) "This should ONLY be called within an ORG-EXPORT call." (if ospe-link? x (cond ((and ,ospe-respect-newlines? (member backend '(html reveal))) (format "@@%s@@%s:" x backend)) (:else (format "\n#+end_export\n%s\n#+begin_export %s\n" x backend))))))) ;; Use any headers for this block type, if no local value is passed ,@(cl-loop for k in (mapcar #'car (-partition 2 kwds)) collect `(--when-let (plist-get (cdr (assoc ',name org-special-block-extras--header-args)) ,(intern (format ":%s" k))) (when (s-blank-p ,k) (setq ,k it)))) (org-export (let ((contents (org-parse raw-contents))) ,@body))))) (cl-defmethod org-special-block-extras-defblock---support-link-type (name verbatim tooltip) "Helper method for org-special-block-extras-defblock. This method creates an Org link type. NAME, string: The name of the link type. VERBATIM, list: Code dumped into the org-link-set-parameters. TOOLTIP, string: Tooltip text alongside link, for use in Emacs." `(org-link-set-parameters ,(format "%s" name) ,@verbatim :export (lambda (label description backend) ; s-replace-all `(("#+end_export" . "") (,(format "#+begin_export %s" backend) . "")) (s-replace-all `(("@@" . "")) ; (,(format "@@%s:" backend) . "") (,(intern (format "org-special-block-extras--%s" name)) backend (or description label) label :ospe-link? t))) ;; The tooltip alongside a link :help-echo (lambda (window object position) (save-excursion (goto-char position) (-let* (((&plist :path :format :raw-link :contents-begin :contents-end) (cadr (org-element-context))) (description (when (equal format 'bracket) (copy-region-as-kill contents-begin contents-end) (substring-no-properties (car kill-ring))))) (format "%s\n\n%s" raw-link ,tooltip))))))
Going forward, it would be nice to have a set of switches that apply to all
special blocks. For instance, :ignore
to simply bypass the user-defined
behaviour of a block type, and :noexport
to zero-out a block upon export. These
are super easy to do —just need a few minutes to breath. It may also be
desirable to provide support for drawers —just as we did to ‘fuse’ the
block-type and link-type approaches used here into one macro.
We tell Org to please look at all special blocks
#+begin_𝒳 main-arg :key₀ value₀ … :keyₙ valueₙ contents #+end_𝒳
Then, before export happens, to replace all such blocks with the result of calling the user's 𝒳 function; i.e., replace them by, essentially,
(𝒳 main-arg :key₀ value₀ … :keyₙ valueₙ)
Implementing the hooking mechanism
The mechanism that rewrites your source…
(defun org-special-block-extras--pp-list (xs) "Given XS as (x₁ x₂ … xₙ), yield the string “x₁ x₂ … xₙ”, no parens. When n = 0, yield the empty string “”." (s-chop-suffix ")" (s-chop-prefix "(" (format "%s" (or xs ""))))) (defvar org-special-block-extras--current-backend nil "A message-passing channel updated by org-special-block-extras--support-special-blocks-with-args and used by DEFBLOCK.") (defun org-special-block-extras--support-special-blocks-with-args (backend) "Remove all headlines in the current buffer. BACKEND is the export back-end being used, as a symbol." (setq org-special-block-extras--current-backend backend) (cl-loop for blk in org-special-block-extras--supported-blocks for kwdargs = nil for blk-start = nil do (goto-char (point-min)) (while (ignore-errors (re-search-forward (format "^\s*\\#\\+begin_%s" blk))) ;; MA: HACK: Instead of a space, it should be any non-whitespace, optionally; ;; otherwise it may accidentlly rewrite blocks with one being a prefix of the other! ; (kill-line) ; (error (format "(%s)" (substring-no-properties (car kill-ring)))) (setq blk-start (line-beginning-position)) (setq header-start (point)) (setq body-start (1+ (line-end-position))) (setq kwdargs (read (format "(%s)" (buffer-substring-no-properties header-start (line-end-position))))) (setq kwdargs (--split-with (not (keywordp it)) kwdargs)) (setq main-arg (org-special-block-extras--pp-list (car kwdargs))) (setq kwdargs (cadr kwdargs)) ; (beginning-of-line) (kill-line) (forward-line -1) (re-search-forward (format "^\s*\\#\\+end_%s" blk)) (setq contents (buffer-substring-no-properties body-start (line-beginning-position))) ; (beginning-of-line)(kill-line) ;; Hack! (kill-region blk-start (point)) (insert (eval `(,(intern (format "org-special-block-extras--%s" blk)) (quote ,backend) contents main-arg ,@(--map (list 'quote it) kwdargs))) ) ;; the --map is so that arguments may be passed ;; as "this" or just ‘this’ (raw symbols) )))
When you enable the org-special-block-extras
mode, it is activated…
;; https://orgmode.org/manual/Advanced-Export-Configuration.html (add-hook 'org-export-before-parsing-hook 'org-special-block-extras--support-special-blocks-with-args)
When you disable the org-special-block-extras
mode, it is deactivated…
(remove-hook 'org-export-before-parsing-hook 'org-special-block-extras--support-special-blocks-with-args)
‘header-args’ Implementation
Then:
(defvar org-special-block-extras--header-args nil "Alist (name plist) where “:main-arg” is a special plist key. It serves a similar role to that of Org's src ‘header-args’. See doc of SET-BLOCK-HEADER-ARGS for more information.") (defmacro org-special-block-extras-set-block-header-args (blk &rest kvs) "Set default valuts for special block arguments. This is similar to, and inspired by, Org-src block header-args. Example src use: #+PROPERTY: header-args:Language :key value Example block use: (set-block-header-args Block :main-arg mainvalue :key value) A full, working, example can be seen by “C-h o RET defblock”. " `(add-to-list 'org-special-block-extras--header-args (list (quote ,blk) ,@kvs))) (defun org-special-block-extras-short-names () "Expose shorter names to the user. Namely, org-special-block-extras-set-block-header-args ↦ set-block-header-args org-special-block-extras-defblock ↦ defblock org-special-block-extras-subtle-colors ↦ subtle-colors " (defalias 'defblock 'org-special-block-extras-defblock) (defalias 'set-block-header-args 'org-special-block-extras-set-block-header-args) (defalias 'thread-blockcall 'org-special-block-extras-thread-blockcall) (defalias 'subtle-colors 'org-special-block-extras-subtle-colors))
This interface is essentially that of Org's src
blocks, with the main-arg
being the first argument to 𝒳 and the only argument not needing to be
preceded by a key name —it is done this way to remain somewhat consistent
with the Org src
interface. The user definition of 𝒳 decides on how optional
the arguments actually are.
Perhaps an example will clarify things …
1.3.1 Example: Jasim providing in-place feedback to Bobbert
Suppose we want to devise a simple special block for editors to provide constructive feedback to authors so that the feedback appears as top-level elements of the resulting exported file —instead of comments that may accidentally not be handled by the author.
In order to showcase the multiple bells and whistles of the system, the snippet below is twice as long than it needs to be, but it is still reasonably small and accessible.
;; We can use variable values when defining new blocks (setq angry-red '(:foreground "red" :weight bold)) ;; Enable short names; e.g., ‘defblock’ instead of ‘org-special-block-extras-defblock’. (org-special-block-extras-short-names) ;; This is our 𝒳, “remark”. ;; As a link, it should be shown angry-red; ;; it takes two arguments: “color” and “signoff” ;; with default values being "red" and "". ;; (Assuming we already called org-special-block-extras-short-names. ) (defblock rremark (editor "Editor Remark" :face angry-red) (color "red" signoff "") "Top level (HTML & LaTeX) editorial remarks; in Emacs they're angry red." (format (if (equal backend 'html) "<strong style=\"color: %s;\">⟦%s: %s%s⟧</strong>" "{\\color{%s}\\bfseries %s: %s%s}") color editor contents signoff)) ;; I don't want to change the definition, but I'd like to have ;; the following as personalised defaults for the “remark” block. ;; OR, I'd like to set this for links, which do not have argument options. (set-block-header-args rremark :main-arg "Jasim Jameson" :signoff "( Aim for success! )")
Example use
The sum of the first $n$ natural numbers is $\sum_{i = 0}^n i = {n × (n + 1) \over 2}$. Note that $n × (n + 1)$ is even. [[rremark:Jasim Jameson][Why are you taking about “$\mathsf{even}$” here?]] #+begin_rremark Bobbert Barakallah :signoff "Thank-you for pointing this out!" :color green I was trying, uh ... Yeah, to explain that ${\large n × (n + 1) \over 2}$ is always an integer. #+end_rremark Hence, we only need to speak about whole numbers. [[rremark:][Then please improve your transition sentences.]]
Resulting rendition
The sum of the first \(n\) natural numbers is \(\sum_{i = 0}^n i = {n × (n + 1) \over 2}\). Note that \(n × (n + 1)\) is even. ⟦Jasim Jameson: Why are you taking about “\(\mathsf{even}\)” here?( Aim for success! )⟧
⟦Bobbert Barakallah:I was trying, uh …
Yeah, to explain that \({\large n × (n + 1) \over 2}\) is always an integer.
Thank-you for pointing this out!⟧Hence, we only need to speak about whole numbers. ⟦Jasim Jameson: Then please improve your transition sentences.( Aim for success! )⟧
Notice that the result contains text —the signoff message— that the user Jasim did not write explicitly.
… Why the stuttered rremark
? Because this package comes with a remark
block that
has more bells and whistles … keep reading ;-)
1.4 Modularity with thread-blockcall
Since defblock let's us pretend block —and link— types are string-valued functions, then one would expect that we can compose blocks modularly as functions compose. Somewhat analogously to funcall and thread-last, we provide a macro thread-blockcall.
Example
(thread-blockcall raw-contents (box name) (details (upcase name) :title-color "green")
=
#+begin_details NAME :title-color "green"
#+begin_box name
contents
#+end_box
#+end_details
Implementation
First, we need to handle the case of one block…
(cl-defmacro org-special-block-extras--blockcall (blk &optional main-arg &rest keyword-args-then-contents) "An anaologue to `funcall` but for blocks. Usage: (blockcall blk-name main-arg even-many:key-values raw-contents) One should rarely use this directly; instead use org-special-block-extras-thread-blockcall. " `(concat "#+end_export\n" (,(intern (format "org-special-block-extras--%s" blk)) backend ;; defblock internal ; (format "\n#+begin_export html\n\n%s\n#+end_export\n" ,(car (last keyword-args-then-contents))) ;; contents ,@(last keyword-args-then-contents) ;; contents ,main-arg ,@(-drop-last 1 keyword-args-then-contents)) "\n#+begin_export"))
Using the above sequentially does not work due to the plumbing of
defblock
, so we handle that plumbing below …
(defmacro org-special-block-extras-thread-blockcall (body &rest forms) "Thread text through a number of blocks. BODY is likely to be ‘raw-contents’, possibly with user manipulations. Each FORMS is of the shape “(block-name main-argument :key-value-pairs)” (thread-blockcall x) = x (thread-blockcall x (f a)) = (blockcall f a x) (thread-blockcall x f₁ f₂) ≈ (f₂ (f₁ x)) The third is a ‘≈’, and not ‘=’, because the RHS contains ‘blockcall’s as well as massages the export matter between conseqeuctive blockcalls. A full example: (org-special-block-extras-defblock nesting (name) nil \"Show text in a box, within details, which contains a box.\" (org-special-block-extras-thread-blockcall raw-contents (box name) (details (upcase name) :title-color \"green\") (box (format \"⇨ %s ⇦\" name) :background-color \"blue\") )) " (if (not forms) body `(-let [result (org-special-block-extras--blockcall ,@(car forms) ,body)] ,@(cl-loop for b in (cdr forms) collect `(setq result (org-special-block-extras--blockcall ,@b (concat "#+begin_export\n" result "\n#+end_export" )))) result)))
1.4.1 Short Example: An opportunity to learn!
The following tiny block is composed from two details blocks and a box block —defined elsewhere in this article. It is intended to give the reader another opportunity to make sure they have tried to solve the puzzle posed in the main text before seeing the answer -—this works well in HTML, not so in LaTeX.
(org-special-block-extras-defblock solution (title "Solution") (reprimand "Did you actually try? Maybe see the ‘hints’ above!" really "Solution, for real") "Show the answers to a problem, but with a reprimand in case no attempt was made." (org-special-block-extras-thread-blockcall raw-contents (details really :title-color "red") (box reprimand :background-color "blue") (details title)))
E.g., what is 1 + 1?
Useless Hint: What is a number?
Solution
Did you actually try? Maybe see the ‘hints’ above!
Solution, for real
The answer is 2.
If you're interested in such ‘fundamental’ questions, consider reading Russel and Whitehead's Principa Mathematica ;-)
The above box was created from:
#+begin_solution The answer is 2. If you're interested in such ‘fundamental’ questions, consider reading Russel and Whitehead's /Principa Mathematica/ ;-) #+end_solution
We will make use of this block below when we get to guided problems ;-)
1.4.2 Longer Example: Demonstrating Org-markup with org-demo
Sometimes, we want to show verbatim source and its resulting rendition —which is a major part of this article! So, let's make a block to mitigate such an error-prone tedium.
Implementation
(org-special-block-extras-defblock org-demo nil (source "Source" result "Result" source-color "cyan" result-color "cyan" style "parallel" sep (if (equal backend 'html) "@@html:<p><br>@@" "\n\n\n\n") ) "Output the CONTENTS of the block as both parsed Org and unparsed. Label the source text by SOURCE and the result text by RESULT finally, the source-result fragments can be shown in a STYLE that is either “parallel” (default) or “sequential”. SEP is the separator; e.g., a rule ‘<hr>'. " (-let [text (concat ;; Source (thread-last raw-contents (format (if (equal backend 'html) "<div ><pre class=\"src src-org\">%s</pre></div>" "\n\\begin{verbatim}\n%s\n\\end{verbatim}")) org-export (org-special-block-extras--blockcall box source :background-color source-color) org-export) ;; Separator sep ;; Result (thread-last raw-contents (org-special-block-extras--blockcall box result :background-color result-color) org-export))] (if (equal style "parallel") (org-special-block-extras--blockcall parallel "2" :bar nil text) (concat "#+end_export\n" text "\n#+begin_export"))))
Example
This
#+begin_org-demo /italics/ and _underline_ $e^{i \times \pi} + 1 = 0$ #+end_org-demo
Yields
Source
/italics/ and _underline_ $e^{i \times \pi} + 1 = 0$
Result
italics and underline
\(e^{i \times \pi} + 1 = 0\)
(Sequential) Example
This
#+begin_org-demo :style seq /italics/ and _underline_ $e^{i \times \pi} + 1 = 0$ #+end_org-demo
Yields
Source
/italics/ and _underline_ $e^{i \times \pi} + 1 = 0$
Result
italics and underline
\(e^{i \times \pi} + 1 = 0\)
However, since our implementation scheme relies on a preprocessing step before
export, we cannot use org-demo
to show the results of special blocks: They
disappear in the preprocessing step!
E.g., this
#+begin_org-demo #+begin_box There is no special block ‘box’ to touch! #+end_box #+end_org-demo
yields the mess
Source
#+begin_export htmlThere is no special block ‘box’ to touch!
</pre></div> #+endexport
Result
There is no special block ‘box’ to touch!
However, it does work with links!
Source
[[box:][Box-as-link! Boxception!]]
Result
1.5 Practice Problems: Now you try!
A 5-page PDF covering ELisp fundamentals can be found here.
The first problem is to get you going with Lisp, the next two are actually useful blocks. The rename is useful for when you want to change some names or translate some words; spoiler is useful when we want to test a student's understanding, or to subtly hide the answer to a puzzle so the reader has the opportunity to attempt solving it.
1.5.1 Sttutttterrr
Define a block stutter so that the following examples behave as shown.
Hints
- You need at-most 5 lines of Lisp.
- These functions may be useful: s-repeat, numberp, string-to-number
Examples
The following outputs, well, nothing, since we asked for zero repetitions.
#+begin_stutter 0 words more words #+end_stutter
In contrast …
Source
[[stutter:5][woah, I'm repeated 5 times!]]
Result
woah, I'm repeated 5 times!woah, I'm repeated 5 times!woah, I'm repeated 5 times!woah, I'm repeated 5 times!woah, I'm repeated 5 times!
Solution
Did you actually try? Maybe see the ‘hints’ above!
Solution, for real
(org-special-block-extras-defblock stutter (reps 2) nil "Output the CONTENTS of the block REPS many times" (-let [num (if (numberp reps) reps (string-to-number reps))] (s-repeat num contents)))
1.5.2 Textual Substitution —A translation tool
Define a block rename so that the following examples behave as shown.
Hints
- It can be done in less than 10 lines of Lisp.
- First, try to s-replace-all the substitution
'(("Allah" . "God") ("Yacoub". "Jacob") ("Yusuf" . "Joseph"))
only. - Then take out such hard-coded substitutions … these functions may be helpful: --map / -map, s-split, s-trim
Examples
#+begin_rename "Allah to God, Yacoub to Jacob, Yusuf to Joseph" Quran 12-4: *_Yusuf_* said to his father ( _*Yacoub*_ ), /“O my father, indeed I have seen (in a dream) eleven stars and the sun and the moon; I saw them prostrating to me.”/ #+end_rename
Yields…
Quran 12-4: Joseph said to his father ( Jacob ), “O my father, indeed I have seen (in a dream) eleven stars and the sun and the moon; I saw them prostrating to me.”
Source
[[rename:Pharaoh to Firaun, Joseph to Yusuf][Genesis 41-17: Pharaoh said unto Joseph, /In my dream, behold, I stood upon the bank of the river/ …]]
Result
Genesis 41-17: Firaun said unto Yusuf, In my dream, behold, I stood upon the bank of the river …
Solution
Did you actually try? Maybe see the ‘hints’ above!
Solution, for real
(org-special-block-extras-defblock rename (list "") nil "Perform the given LIST of substitutions on the text. The LIST is a comma separated list of ‘to’ separated symbols. In a link, no quotes are needed." (s-replace-all (--map (cons (car it) (cadr it)) (--map (s-split " to " (s-trim it)) (s-split "," list))) contents))
1.5.3 Spoilers! —“fill in the blanks”
Define a block spoiler so that the following examples behave as shown.
Hints
- It can be done in less than 10 lines of Lisp.
You will need the following style setup …
#+html_head: <style> #+html_head: .spoiler {color: grey; background-color:grey;} #+html_head: .spoiler:hover {color: black; background-color:white;} #+html_head: <style> # Example use: <span class="spoiler"> test </span>
- Escape HTML snippets by enclosing them in
@@html: … @@
—as discussed above in the introduction to special blocks. - The functions s-replace-regexp and regexp-quote may be useful.
Examples
#+begin_spoiler ((Yusuf)) said to his father ((Yacoub)), /“O my father, indeed I have seen ((eleven stars)) and ((the sun and the moon)); I saw them prostrating to me.”/ #+end_spoiler
Yusuf said to his father Yacoub , “O my father, indeed I have seen eleven stars and the sun and the moon ; I saw them prostrating to me.”
#+begin_spoiler :left "[" :right "]" [Yusuf] said to his father [Yacoub], /“O my father, indeed I have seen [eleven stars] and [the sun and the moon]; I saw them prostrating to me.”/ #+end_spoiler
Yusuf said to his father Yacoub , “O my father, indeed I have seen eleven stars and the sun and the moon ; I saw them prostrating to me.”
Solution
Did you actually try? Maybe see the ‘hints’ above!
Solution, for real
Rather than having auxiliary #+html_head:
styling settings, we have moved the
styling information to the defblock
declaration and are using the main argument
to colour the spoiler —which defaults to grey ;-)
For example, the next segment of text is in a block #+begin_spoiler pink
…
Yusuf said to his father Yacoub , “O my father, indeed I have seen eleven stars and the sun and the moon ; I saw them prostrating to me.”
Whereas, the following begins with #+begin_spoiler orange
…
Yusuf said to his father Yacoub , “O my father, indeed I have seen eleven stars and the sun and the moon ; I saw them prostrating to me.”
(org-special-block-extras-defblock spoiler (color "grey") (left "((" right "))") "Hide text enclosed in double parens ((like this)) as if it were spoilers. LEFT and RIGHT may be other kinds of delimiters. The main argument, COLOR, indicates which color to use. For LaTeX, this becomes “fill in the blanks”, with the answers in the footnotes." (if (equal backend 'latex) (s-replace-regexp (concat (regexp-quote left) "\\(.*?\\)" (regexp-quote right)) "@@latex:\\\\fbox{\\\\phantom{\\1}}\\\\footnote{\\1}@@" contents) (-let [id (gensym)] (concat ;; In HTML, a ‘style’ can be, technically, almost anywhere... (format "<style> #%s {color: %s; background-color:%s;} #%s:hover {color: black; background-color:white;} </style> " id color color id) (s-replace-regexp (concat (regexp-quote left) "\\(.*?\\)" (regexp-quote right)) (format "@@html:<span id=\"%s\"> \\1 </span>@@" id) contents)))))
There's actually a problem with this ‘solution’; can you find it?
Hint: Try the link form and see how it breaks!
1.5.4 Judgements: Inference rules and proof trees
Using a mixture of \frac
and \displaystyle
, define a block tree so that the
following examples behave as shown. Hint: org-list-to-lisp and
with-temp-buffer may be useful ;-)
Programming ≈ Proving
Source
#+begin_tree + Function Application :: f(a) : B - a : A - f : A → B + Modus Ponens :: q - p - p ⇒ q #+end_tree
Result
\[\frac{\displaystyle \qquad a : A \qquad f : A → B }{f(a) : B}[\text{Function Application}]\]\[\frac{\displaystyle \qquad p \qquad p ⇒ q}{q}[\text{Modus Ponens}]\]Solution
Did you actually try? Maybe see the ‘hints’ above!
Solution, for real
(defun org-special-block-extras--list-to-math (lst) "Get a result LST from ORG-LIST-TO-LISP and render it as a proof tree." (cond ((symbolp lst) "") ((symbolp (car lst)) (org-special-block-extras--list-to-math (cadr lst))) (t (-let* (((conclusion₀ children) lst) ((name named?) (s-split " :: " conclusion₀)) (conclusion (or named? conclusion₀))) (if (not children) (if named? (format "\\frac{}{%s}[%s]" conclusion name) conclusion) (format "\\frac{\\displaystyle %s}{%s}%s" (s-join " \\qquad " (mapcar #'org-special-block-extras--list-to-math children)) conclusion (if named? (format "[\\text{%s}]" name) ""))))))) (org-special-block-extras-defblock tree (main-arg) nil "Write a proof tree using Org-lists. To get premises₀ … premisesₙ ────────────────────────────[ reason ] conclusion You type #+begin_tree + reason :: conclusion - premises₀ - premises₁ ⋮ - premisesₙ #+end_tree Where each premisesᵢ may, recursively, also have named reasons and (indented) child premises of its own. If there are multiple trees, they are shown one after the other. The text in this block should be considered LaTeX; as such, Org markup is not recognised. A proof tree, derivation, is then just a deeply nested itemisation. For instance, assuming P = Q(X), X = Y, Q(Y) = R, the following proves P = R. #+begin_tree + Trans :: P = R - P = Q(X) + ✓ - Trans :: Q(X) = R + Trans :: Q(X) = Q(Y) - Refl :: Q(X) = Q(X) + ✓ - Leibniz :: Q(X) = Q(Y) + X = Y - ✓ + Sym :: Q(Y) = R - R = Q(Y) - ✓ #+end_tree" (s-join "" (--map (format "\\[%s\\]" (org-special-block-extras--list-to-math it)) (cdr (with-temp-buffer (insert raw-contents) (goto-char (point-min)) (org-list-to-lisp))))))
For more on these ‘proof trees’, see ‘Natural Logic’ by Neil Tennant.
\[\] (Warning! For MathJax to activate, you should have some math $...$
somewhere besides the tree
blocks; just \[\]
suffices. )
1.6 What's the rest of this article about?
The rest of the article showcases the special blocks declared with defblock
to
allow the above presentation —with folded regions, coloured boxes, tooltips,
parallel columns of text, etc.
Enjoy ;-)
1.7 The Older org-special-block-extras--𝒳
Utility
For posterity, below is the original route taken to solve the same problem. In particular, the route outlined below may be faster.
Why is defblock
better?
- The approach below requires an awkward way to handle arguments, key-values.
- It requires the user to learn a new interface.
- Even if it's slower,
defblock
uses a very familiar interface and requires less Lisp mastery on the user's part.
The simplest route is to ‘advise’ —i.e., function patch or overload— the Org
export utility for special blocks to consider calling a method
org-special-block-extras--𝒳
whenever it encounters a special block named 𝒳
.
(advice-add #'org-html-special-block :before-until (apply-partially #'org-special-block-extras--advice 'html)) (advice-add #'org-latex-special-block :before-until (apply-partially #'org-special-block-extras--advice 'latex))
Here is the actual advice:
(defun org-special-block-extras--advice (backend blk contents _) "Invoke the appropriate custom block handler, if any. A given custom block BLK has a TYPE extracted from it, then we send the block CONTENTS along with the current export BACKEND to the formatting function ORG-SPECIAL-BLOCK-EXTRAS--TYPE if it is defined, otherwise, we leave the CONTENTS of the block as is. We also support the seemingly useless blocks that have no contents at all, not even an empty new line." (let* ((type (nth 1 (nth 1 blk))) (handler (intern (format "org-special-block-extras--%s" type)))) (ignore-errors (apply handler backend (or contents "") nil))))
To support a new block named 𝒳:
- Define a function
org-special-block-extras--𝒳
. - It must take two arguments:
backend
⇒ A symbol such as'html
or'latex
,content
⇒ The string contents of the special block.
- The function must return a string, possibly depending on the backend being exported to. The resulting string is inserted literally in the exported file.
- Test out your function as in
(org-special-block-extras--𝒳 'html "some input")
—this is a quick way to find errors. - Enjoy ^_^
If no such function is defined, we export 𝒳
blocks using the default
mechanism, as discussed earlier, as a LaTeX environment or an HTML div
.
An example is provided at the end of this section.
Of-course, when the user disables our mode, then we remove such advice.
(advice-remove #'org-html-special-block (apply-partially #'org-special-block-extras--advice 'html)) (advice-remove #'org-latex-special-block (apply-partially #'org-special-block-extras--advice 'latex))
1.7.1 :argument:
Extraction
As far as I can tell, there is no way to provide arguments to special blocks.
As such, the following utility looks for lines of the form :argument: value
within the contents of a block and returns an updated contents string that no
longer has such lines followed by an association list of such argument-value
pairs.
(defun org-special-block-extras--extract-arguments (contents &rest args) "Get list of CONTENTS string with ARGS lines stripped out and values of ARGS. Example usage: (-let [(contents′ . (&alist 'k₀ … 'kₙ)) (…extract-arguments contents 'k₀ … 'kₙ)] body) Within ‘body’, each ‘kᵢ’ refers to the ‘value’ of argument ‘:kᵢ:’ in the CONTENTS text and ‘contents′’ is CONTENTS with all ‘:kᵢ:’ lines stripped out. + If ‘:k:’ is not an argument in CONTENTS, then it is assigned value NIL. + If ‘:k:’ is an argument in CONTENTS but is not given a value in CONTENTS, then it has value the empty string." (let ((ctnts contents) (values (cl-loop for a in args for regex = (format ":%s:\\(.*\\)" a) for v = (cadr (s-match regex contents)) collect (cons a v)))) (cl-loop for a in args for regex = (format ":%s:\\(.*\\)" a) do (setq ctnts (s-replace-regexp regex "" ctnts))) (cons ctnts values)))
For example, we use this feature to indicate when a column break should happen
in a parallel
block and which person is making editorial remarks in an
remark
block.
Why the :𝒳:
notation? At the start of a line, a string of this form is coloured
—I don't recall why that is— and that's a good enough reason to make use of
such an existing support.
[Aside:
In org-mode, ‘drawers’ are pieces of text that begin with
:my_drawer_name:
on a line by itself and end with :end:
on a line by itself, and
these delimiters allow us to fold away such regions and possibly exclude them
from export. That is, drawers act as a light-weight form of blocks. Anyhow, Org
colours drawer delimiters,
1.7.2 An Example Special Block ---foo
Herein we show an example function org-special-block-extras--𝒳
that makes use of
arguments. In a so-called foo
block, all occurrences of the word foo
are
replaced by bar
unless the argument :replacement:
is given a value.
(defun org-special-block-extras--foo (backend contents) "The FOO block type replaces all occurances of ‘foo’ with ‘bar’, unless a ‘:replacement:’ is provided." (-let [(contents′ . (&alist 'replacement)) (org-special-block-extras--extract-arguments contents 'replacement)] (s-replace "foo" (or replacement "bar") contents′)))
2 Editor Comments
“Editor Comments” are intended to be top-level first-class comments in an article that are inline with the surrounding text and are delimited in such a way that they are visible but drawing attention. I first learned about this idea from Wolfram Kahl —who introduced me to Emacs many years ago. We
Example
This
In LaTeX, a =remark= appears inline with the text surrounding it. #+begin_remark Bobert org-mode is dope, yo! #+replacewith: Org-mode is essentially a path toward enlightenment. #+end_remark Unfortunately, in the HTML rendition, the =remark= is its own paragraph and thus separated by new lines from its surrounding text.
Yields
In LaTeX, an remark
appears inline with the text surrounding it.
[Bobert: Replace:
org-mode is dope, yo!
With:Org-mode is essentially a path toward enlightenment.
]
Unfortunately, in the HTML rendition, the remark
is its own paragraph and thus
separated by new lines from its surrounding text.
Implementing ‘remark’, from §ection 1, with more bells and whistles
(defvar org-special-block-extras-hide-editor-comments nil "Should editor comments be shown in the output or not.") (org-special-block-extras-defblock remark (editor "Editor Remark" :face '(:foreground "red" :weight bold)) (color "black" signoff "" strong nil) ; :inline-please__see_margin_block_for_a_similar_incantation ; ⇒ crashes! "Format CONTENTS as an first-class editor comment according to BACKEND. The CONTENTS string has an optional switch: If it contains a line with having only ‘#+replacewith:’, then the text preceding this clause should be replaced by the text after it; i.e., this is what the EDITOR (the person editing) intends and so we fromat the replacement instruction (to the authour) as such. In Emacs, as links, editor remarks are shown with a bold red; but the exported COLOR of a remark is black by default and it is not STRONG ---i.e., bold---. There is an optional SIGNOFF message that is appended to the remark. " (-let* (;; Are we in the html backend? (tex? (equal backend 'latex)) ;; fancy display style (boxed (lambda (x) (if tex? (concat "\\fbox{\\bf " x "}") (concat "<span style=\"border-width:1px" ";border-style:solid;padding:5px\">" "<strong>" x "</strong></span>")))) ;; Is this a replacement clause? ((this that) (s-split "\\#\\+replacewith:" contents)) (replacement-clause? that) ;; There is a ‘that’ (replace-keyword (if tex? "\\underline{Replace:}" " <u>Replace:</u>")) (with-keyword (if tex? "\\underline{With:}" "<u>With:</u>" )) (editor (format "[%s:%s" editor (if replacement-clause? replace-keyword ""))) (contents′ (if replacement-clause? (format "%s %s %s" this (org-export (funcall boxed with-keyword)) that) contents)) ;; “[Editor Comment:” (edcomm-begin (funcall boxed editor)) ;; “]” (edcomm-end (funcall boxed "]"))) (setq org-export-allow-bind-keywords t) ;; So users can use “#+bind” immediately (if org-special-block-extras-hide-editor-comments "" (format (pcase backend ('latex (format "{\\color{%%s}%s %%s %%s %%s %%s}" (if strong "\\bfseries" ""))) (_ (format "<%s style=\"color: %%s;\">%%s %%s %%s %%s</%s>" (if strong "strong" "p") (if strong "strong" "p")))) color edcomm-begin contents′ signoff edcomm-end))))
In the HTML export, the edcomm
special block is not in-line with the text
surrounding it —ideally, it would be inline so that existing paragraphs are
not split into multiple paragraphs but instead have an editor's comment
indicating suggested alterations.
Example: No optional arguments
[Editor Remark:
Please change this section to be more, ya know, professional.
]Source:
#+begin_remark /Please/ *change* _this_ section to be more, ya know, professional. #+end_remark
Example: Only providing a main argument ---i.e., the remark author, the editor
[Bobert:
Please change this section to be more, ya know, professional.
]Source:
#+begin_remark Bobert /Please/ *change* _this_ section to be more, ya know, professional. #+end_remark
Example: Possibly with no contents:
[Bobert: ]
Source:
#+begin_remark Bobert #+end_remark
Example: Empty contents, no authour, nothing
[Editor Remark: ]
Source:
#+begin_remark #+end_remark
Example: Possibly with an empty new line
[Editor Remark: ]
Source:
#+begin_remark #+end_remark
Example: With a “#+replacewith:” clause
[Editor Remark: Replace:
The two-dimensional notation; e.g., \(\sum_{i = 0}^n i^2\)
With:A linear one-dimensional notation; e.g., \((\Sigma i : 0..n \;\bullet\; i^2)\)
]Source:
#+begin_remark The two-dimensional notation; e.g., $\sum_{i = 0}^n i^2$ #+replacewith: A linear one-dimensional notation; e.g., $(\Sigma i : 0..n \;\bullet\; i^2)$ #+end_remark
Example: Possibly “malformed” replacement clauses
Forgot the thing to be replaced…
[Editor Remark: Replace: With:
A linear one-dimensional notation; e.g., \((\Sigma i : 0..n \;\bullet\; i^2)\)
]Forgot the new replacement thing…
[Editor Remark: Replace:
The two-dimensional notation; e.g., \(\sum_{i = 0}^n i^2\)
With: ]Completely lost one's train of thought…
[Editor Remark: Replace: With: ]
Source:
#+begin_remark #+replacewith: #+end_remark
A block to make an editorial comment could be overkill in some cases; luckily defblock automatically provides an associated link type for the declared special blocks.
- Syntax:
[[remark:person_name][editorial remark]]
. - This link type exports the same as the
remark
block type; however, in Emacs it is shown with an ‘angry’ —bold— red face.
Example: Terse remarks via links
[[edcomm:Jasim][Hello, where are you?]]
[Jasim: Hello, where are you? ]
The #+replacewith:
switch —and usual Org markup— also works with these
links:
[[remark:Qasim][/‘j’/ #+replacewith: /‘q’/]]
[Qasim: Replace: ‘j’ With: ‘q’ ]
All editor comments, remarks, are disabled by declaring, in your Org file:
#+bind: org-special-block-extras-hide-editor-comments t
The #+bind:
keyword makes Emacs variables buffer-local during export
—it is evaluated after any src
blocks. To use it, one must declare in
their Emacs init file the following line, which our mode
ensures is true.
(setq org-export-allow-bind-keywords t)
( Remember to C-c C-c the #+bind to activate it, the first time it is written. ) |
3 Folded Details —As well as boxed text and subtle colours
How did we fold away those implementations?
Sometimes there is a remark or a code snippet that is useful to have, but not relevant to the discussion at hand and so we want to fold away such details.
- ‘Conversation-style’ articles, where the author asks the reader questions whose answers are “folded away” so the reader can think about the exercise before seeing the answer.
- Hiding boring but important code snippets, such as a list of import declarations or a tedious implementation.
Requires: ,,#+latex_header: \usepackage{tcolorbox}
Implementation
1: (org-special-block-extras-defblock details (title "Details") (title-color "green") 2: "Enclose contents in a folded up box, for HTML. 3: 4: For LaTeX, this is just a boring, but centered, box. 5: 6: By default, the TITLE of such blocks is “Details” 7: and its TITLE-COLOR is green. 8: 9: In HTML, we show folded, details, regions with a nice greenish colour. 10: 11: In the future ---i.e., when I have time--- 12: it may be prudent to expose more aspects as arguments, 13: such as ‘background-color’. 14: " 15: (format 16: (pcase backend 17: (`latex "\\begin{quote} 18: \\begin{tcolorbox}[colback=%s,title={%s},sharp corners,boxrule=0.4pt] 19: %s 20: \\end{tcolorbox} 21: \\end{quote}") 22: (_ "<details class=\"code-details\" 23: style =\"padding: 1em; 24: background-color: #e5f5e5; 25: /* background-color: pink; */ 26: border-radius: 15px; 27: color: hsl(157 75% 20%); 28: font-size: 0.9em; 29: box-shadow: 0.05em 0.1em 5px 0.01em #00000057;\"> 30: <summary> 31: <strong> 32: <font face=\"Courier\" size=\"3\" color=\"%s\"> 33: %s 34: </font> 35: </strong> 36: </summary> 37: %s 38: </details>")) 39: title-color title contents))
Above is a lower-case ‘d’etails block, we now make a captial-case ‘D’etails block, for the not-too-odd situation we want to have, right away, one details block within another.
1: (org-special-block-extras-defblock Details (title "Details") (title-color "green") 2: "Enclose contents in a folded up box, for HTML. 3: 4: For LaTeX, this is just a boring, but centered, box. 5: 6: By default, the TITLE of such blocks is “Details” 7: and its TITLE-COLOR is green. 8: 9: In HTML, we show folded, details, regions with a nice greenish colour. 10: 11: In the future ---i.e., when I have time--- 12: it may be prudent to expose more aspects as arguments, 13: such as ‘background-color’. 14: " 15: (format 16: (pcase backend 17: (`latex "\\begin{quote} 18: \\begin{tcolorbox}[colback=%s,title={%s},sharp corners,boxrule=0.4pt] 19: %s 20: \\end{tcolorbox} 21: \\end{quote}") 22: (_ "<details class=\"code-details\" 23: style =\"padding: 1em; 24: background-color: #e5f5e5; 25: /* background-color: pink; */ 26: border-radius: 15px; 27: color: hsl(157 75% 20%); 28: font-size: 0.9em; 29: box-shadow: 0.05em 0.1em 5px 0.01em #00000057;\"> 30: <summary> 31: <strong> 32: <font face=\"Courier\" size=\"3\" color=\"%s\"> 33: %s 34: </font> 35: </strong> 36: </summary> 37: %s 38: </details>")) 39: title-color title contents))
We could use
\begin{quote}\fbox{\parbox{\linewidth}{\textbf{Details:} ...}}\end{quote}
; however, this does not work well with minted for coloured
source blocks. Instead, we use tcolorbox
.
If you fear that your readers may not click on details boxes in the HTML export,
you could, say, have the details heading “flash pink” whenever the user hovers
over it. This can be accomplished by inserting the following incantation into
your Org file or place it into your org-html-head
variable.
#+html: <style> summary:hover {background:pink;} </style>
3.1 Example: Here's a nifty puzzle, can you figure it out?
Reductions —incidentally also called ‘folds’1— embody primitive recursion and thus computability. For example, what does the following compute when given a whole number 𝓃?
(-reduce #'/ (number-sequence 1.0 𝓃))
Solution
Rather than guess-then-check, let's calculate!
(-reduce #'/ (number-sequence 1.0 𝓃)) = ;; Lisp is strict: Evaluate inner-most expression (-reduce #'/ '(1.0 2.0 3.0 … 𝓃)) = ;; Evaluate left-associating reduction (/ (/ (/ 1.0 2.0) ⋯) 𝓃) =;; Arithmetic: (/ (/ a b) c) = (* (/ a b) (/ 1 c)) = (/ a (* b c)) (/ 1.0 (* 2.0 3.0 … 𝓃))
We have thus found the above Lisp program to compute the inverse factorial of 𝓃; i.e., \(\large \frac{1}{𝓃!}\).
Neato, let's do more super cool stuff ^_^
( In the Emacs Web Wowser, folded regions are displayed unfolded similar to LaTeX. )
3.2 Boxed Text
Folded regions, as implemented above, are displayed in a super neat text box which may be useful to enclose text to make it standout —without having it folded away. As such, we provide the special block box to enclosing text in boxes.
Requires: ,#+latex_header: \usepackage{tcolorbox}
Implementation
1: (org-special-block-extras-defblock box (title "") (background-color nil) 2: "Enclose text in a box, possibly with a title. 3: 4: By default, the box's COLOR is green for HTML and red for LaTeX, 5: and it has no TITLE. 6: 7: The HTML export uses a padded div, whereas the LaTeX export 8: requires the tcolorbox pacakge. 9: 10: In the future, I will likely expose more arguments. 11: " 12: (apply #'concat 13: (pcase backend 14: (`latex `("\\begin{tcolorbox}[title={" ,title "}" 15: ",colback=" ,(pp-to-string (or background-color 'red!5!white)) 16: ",colframe=red!75!black, colbacktitle=yellow!50!red" 17: ",coltitle=red!25!black, fonttitle=\\bfseries," 18: "subtitle style={boxrule=0.4pt, colback=yellow!50!red!25!white}]" 19: ,contents 20: "\\end{tcolorbox}")) 21: (_ `("<div style=\"padding: 1em; background-color: " 22: ,(org-special-block-extras-subtle-colors (format "%s" (or background-color "green"))) 23: ";border-radius: 15px; font-size: 0.9em" 24: "; box-shadow: 0.05em 0.1em 5px 0.01em #00000057;\">" 25: "<h3>" ,title "</h3>" 26: ,contents "</div>")))))
Example: A super boring observation presented obscurely
If you start walking —say, counterclockwise— along the unit circle from its right-most point and walk 180ᵒ, then you will be at its left-most point. That is, \[ e^{i · \pi} \;=\; - 1 \]
How did we get that nice light blue? What is its HTML code?
That's not something I care to remember, so let's make a handy
dandy utility … Now when users request a colour to box
their text,
it will be a ‘subtle colour’ ;-)
Implementation for Subtle Colours
1: (defun org-special-block-extras-subtle-colors (c) 2: "HTML codes for common colours. 3: 4: Names are very rough approximates. 5: 6: Translations from: https://www.december.com/html/spec/softhues.html" 7: (pcase c 8: ("teal" "#99FFCC") ;; close to aqua 9: ("brown" "#CCCC99") ;; close to moss 10: ("gray" "#CCCCCC") 11: ("purple" "#CCCCFF") 12: ("lime" "#CCFF99") ;; brighter than ‘green’ 13: ("green" "#CCFFCC") 14: ("blue" "#CCFFFF") 15: ("orange" "#FFCC99") 16: ("peach" "#FFCCCC") 17: ("pink" "#FFCCFF") 18: ("yellow" "#FFFF99") 19: ("custard" "#FFFFCC") ;; paler than ‘yellow’ 20: (c c) 21: ))
To use these colour names, you will need the following incantations in your Org file.
Source
#+latex_header: \usepackage{xcolor} #+latex_header: \definecolor{teal} {HTML}{99FFCC} #+latex_header: \definecolor{brown} {HTML}{CCCC99} #+latex_header: \definecolor{gray} {HTML}{CCCCCC} #+latex_header: \definecolor{purple} {HTML}{CCCCFF} #+latex_header: \definecolor{lime} {HTML}{CCFF99} #+latex_header: \definecolor{green} {HTML}{CCFFCC} #+latex_header: \definecolor{blue} {HTML}{CCFFFF} #+latex_header: \definecolor{orange} {HTML}{FFCC99} #+latex_header: \definecolor{peach} {HTML}{FFCCCC} #+latex_header: \definecolor{pink}{HTML}{FFCCFF} #+latex_header: \definecolor{yellow} {HTML}{FFFF99} #+latex_header: \definecolor{custard}{HTML}{FFFFCC} #+latex_header: \definecolor{cyan}{HTML}{00FFFF}
Result
In the future, it'd be nice to account for colours for LaTeX as well. ( E.g.,
\color{blue}
is a nightmare. )
It may be useful to fuse the box
and details
concepts into one. Something for
future me —or another contributor— to think about ;-)
4 Parallel
Articles can get lengthy when vertical whitespace is wasted on thin lines; instead, one could save space by using parallel columns of text.
Requires: ,#+latex_header: \usepackage{multicol}
Implementation
1: (org-special-block-extras-defblock parallel (cols 2) (bar nil) 2: "Place ideas side-by-side, possibly with a seperator. 3: 4: There are COLS many columns, and they may be seperated by black 5: solid vertical rules if BAR is a non-nil value. 6: 7: Writing “#+begin_parallel 𝓃 :bar (any text except ‘nil’)” 8: will produce a parallel of 𝓃 many columns, possibly 9: seperated by solid rules, or a “bar”. 10: 11: The contents of the block may contain ‘#+columnbreak:’ to request 12: a columnbreak. This has no effect on HTML export since HTML 13: describes how text should be formatted on a browser, which can 14: dynamically shrink and grow and thus it makes no sense to have 15: hard columnbreaks. We do replace such declarations by ‘<p><br>’, 16: which sometimes accomplishes the desired goal. 17: " 18: (let ((rule (pcase backend 19: (`latex (if bar 2 0)) 20: (_ (if bar "solid" "none")))) 21: (contents′ (s-replace "#+columnbreak:" 22: (if (equal 'latex backend) "\\columnbreak" "@@html:<p><br>@@") 23: contents))) 24: (format (pcase backend 25: (`latex "\\par \\setlength{\\columnseprule}{%s pt} 26: \\begin{minipage}[t]{\\linewidth} 27: \\begin{multicols}{%s} 28: %s 29: \\end{multicols}\\end{minipage}") 30: (_ "<div style=\"column-rule-style: %s;column-count: %s;\">%s</div>")) 31: rule cols contents′)))
Going forward, it would be desirable to have the columns take a specified percentage of the available width —whereas currently it splits it uniformly. Such a feature would be useful in cases where one column is wide and the others are not.
Example
This
#+begin_parallel 2 :bar yes-or-any-other-text X #+columnbreak: Y Z #+end_parallel
Yields
X
Y
Z
( The Emacs Web Wowser, M-x eww
, does not display parallel
environments as
desired. )
5 Colours
Let's develop blocks for colouring text and link types for inline colouring; e.g., color and teal.
- E.g.,
[[brown: Hello, World!]]
⇒ Hello, World! - E.g.,
brown:Friends!
⇒ Friends! ( Note the ‘!’ is not coloured; use[[...]]
! )
Use M-x list-colors-display to see a list of defined colour names in Emacs —see xcolor for the LaTeX side and htmlcolorcodes.com for the HTML side, or just visit http://latexcolor.com/ for both.
A Picture and Block Examples
This text is black!
This text is blue!
This text is brown!
This text is darkgray!
This text is gray!
This text is green!
This text is lightgray!
This text is lime!
This text is magenta!
This text is olive!
This text is orange!
This text is pink!
This text is purple!
This text is red!
This text is teal!
This text is violet!
This text is white!
This text is yellow!
Implementation of numerous colour blocks/links
We declare a list of colors that should be available on most systems. Then using this list, we evaluate the code necessary to produce the necessary functions that format special blocks.
By default, Org uses the graphicx
LaTeX package which let's us colour text
—see its documentation here. For example, in an #+begin_export latex
block,
the following produces blue coloured text.
{ \color{blue} This is a sample text in blue. }
(defvar org-special-block-extras--colors '(black blue brown cyan darkgray gray green lightgray lime magenta olive orange pink purple red teal violet white yellow) "Colours that should be available on all systems.") (cl-loop for colour in org-special-block-extras--colors do (eval `(org-special-block-extras-defblock ,colour (the-color "black" :face `(:foreground ,(format "%s" (quote ,colour)))) nil ,(format "Show text in %s color." colour) (let () (format (pcase backend (`latex "\\begingroup\\color{%s}%s\\endgroup\\,") (_ "<span style=\"color:%s;\">%s</span>")) (quote ,colour) contents)))))
Implementation of ‘color’
For faster experimentation between colours, we provide a generic color
block
that consumes a color argument.
(org-special-block-extras-defblock color (color black :face (lambda (colour) `(:foreground ,(format "%s" colour)))) nil "Format text according to a given COLOR, which is black by default." (format (pcase backend (`latex "\\begingroup\\color{%s}%s\\endgroup\\,") (`html "<span style=\"color:%s;\">%s</span>")) color contents))
[Posterity] Old version which did not allow wombo
colour:
(org-special-block-extras-defblock color (color black :face (lambda (colour) (if (member (intern colour) org-special-block-extras--colors) `(:foreground ,(format "%s" colour)) `(:height 300 :underline (:color "red" :style wave) :overline "red" :strike-through "red")))) nil "Format text according to a given COLOR, which is black by default." (format (pcase backend (`latex "\\begingroup\\color{%s}%s\\endgroup\\,") (`html "<span style=\"color:%s;\">%s</span>")) color contents))
Moreover, we have that the syntax red:text
renders ‘text’ with the colour red
in both the Emacs interface and in exported backends.
Observe: this is super neato, amigos! and this is brown ‘color’ link and this one is an orange ‘color’ link!
Also: If we try to use an unsupported colour ‘wombo’, we render the
descriptive text larger in Emacs along with a tooltip explaining why this is
the case; e.g.,
[[color:wombo][hi]]
.
[[color:#fad][Using Hex colour code!]]
⇒ Using Hex colour code! ;-)
( Markdown does not support colour; go look at the HTML or PDF! )
5.1 latex-definitions
for hiding LaTeX declarations in HTML
Before indicating desirable next steps, let us produce an incidentally useful special block type.
We may use LaTeX-style commands such as {\color{red} x}
by enclosing them in
$
-symbols to obtain \({\color{red}x}\) and other commands to present mathematical
formulae in HTML. This is known as the MathJax tool —Emacs' default HTML
export includes it.
It is common to declare LaTeX definitions for convenience, but such
declarations occur within $
-delimiters and thereby produce undesirable extra
whitespace. We declare the latex_definitions
block type which avoids
displaying such extra whitespace in the resulting HTML.
‘latex-definitions’ Implementation
(org-special-block-extras-defblock latex-definitions nil nil "Declare but do not display the CONTENTS according to the BACKEND." (format (pcase backend ('html "<p style=\"display:none\">\\[%s\\]</p>") (_ "%s")) raw-contents))
Here —which you cannot see, as desired—is an example usage, where we
declare \LL
to produce a violet left parenthesis. We then use these to produce
an example of quantification notation.
\[ {\color{teal}\bigoplus} _{ {\color{violet} x} = {\color{red} a}} ^{\color{cyan} b} {\color{brown}{\,f\, x}} \quad=\quad {\color{brown}{f\,\LL {\color{red} a} \RR}} \;{\color{teal}\oplus}\; {\color{brown}{f \, \LL a + 1 \RR }} \;{\color{teal}\oplus}\; {\color{brown}{f \, \LL a + 2 \RR }} \;{\color{teal}\oplus}\; \cdots \;{\color{teal}\oplus}\; {\color{brown}{f \, \LL {\color{cyan} b} \RR}} \]
⊕ | Loop sequentially with loop-bodies fused using ⊕ |
x | Use x as the name of the current element |
a | Start with x being a |
b | End with x being b |
f x | At each x value, compute f x |
( Markdown does not support MathJax; go look at the HTML or PDF! )
Unfortunately, MathJax does not easily support arbitrary HTML elements to occur
within the $
-delimiters —see this and this for ‘workarounds’. As such, the
MathJax producing the above example is rather ugly whereas its subsequent
explanatory table is prettier on the writer's side.
Going forward, it would be nice to easily have our colour links work within a mathematical special block.
Moreover, it would be nice to extend the color
block type to take multiple
arguments, say, c₁ c₂ … cₙ
such that:
n | Behaviour |
---|---|
0 | No colouring; likewise if no arguments altogether |
1 | Colour all entries using the given colour c₁ |
n | Paragraph –region separated by a new line– i is coloured by cₖ where k = i mod n |
Besides having a colourful article, another usage I envision for this generalisation would be when rendering text in multiple languages; e.g., use red and blue to interleave Arabic poetry with its English translation.
6 Nice Keystroke Renditions: C-h h
Anyone who writes about Emacs will likely want to mention keystrokes in an aesthetically pleasing way, such as C-u 80 - to insert 80 dashes or C-c C-e h o to export an Org-mode file to HTML.
- If there is a description, we render it in keystroke style.
- If there is only a label, we translate underscores into spaces.
Implementation
(org-link-set-parameters "kbd" :follow (lambda (_)) :export (lambda (label description backend) (format (pcase backend ('latex "\\texttt{%s}") (_ "<kbd> %s </kbd>") ) (or description (s-replace "_" " " label)))))
The following styling rule is used to make the keystrokes displayed nicely.
(defvar org-special-block-extras--kbd-html-setup nil "Has the necessary keyboard styling HTML beeen added?") (unless org-special-block-extras--kbd-html-setup (setq org-special-block-extras--kbd-html-setup t) (setq org-html-head-extra (concat org-html-head-extra " <style> /* From: https://endlessparentheses.com/public/css/endless.css */ /* See also: https://meta.superuser.com/questions/4788/css-for-the-new-kbd-style */ kbd { -moz-border-radius: 6px; -moz-box-shadow: 0 1px 0 rgba(0,0,0,0.2),0 0 0 2px #fff inset; -webkit-border-radius: 6px; -webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.2),0 0 0 2px #fff inset; background-color: #f7f7f7; border: 1px solid #ccc; border-radius: 6px; box-shadow: 0 1px 0 rgba(0,0,0,0.2),0 0 0 2px #fff inset; color: #333; display: inline-block; font-family: 'Droid Sans Mono', monospace; font-size: 80%; font-weight: normal; line-height: inherit; margin: 0 .1em; padding: .08em .4em; text-shadow: 0 1px 0 #fff; word-spacing: -4px; box-shadow: 2px 2px 2px #222; /* MA: An extra I've added. */ } </style>")))
Using the org-special-block-extras-fancy-links
variable, defined below, we can
now render kbd:𝒳
links in a pretty way within Emacs itself.
7 “Link Here!” & OctoIcons
Use the syntax link-here:name
to create an anchor link that alters the URL with
#name
as in “ ”
—it looks and behaves like the Github generated links for a heading.
Use case: Sometimes you want to explicitly point to a particular location in an
article, such as within a #+begin_details
block, this is a possible way to do so.
Likewise, get OctoIcons with the syntax octoicon:𝒳
where 𝒳
is one of home,
link, mail, report, tag, clock
: , , ,
, , .
- Within
octoicon:𝒳
andlink-here:𝒳
the label𝒳
determines the OctoIcon shown and the name of the local link to be created, respectively.- Descriptions, as in
[[link:label][description]]
, are ignored.
- Descriptions, as in
- Besides the HTML backend, such links are silently omitted.
Six OctoIcons and Implementation
The following SVGs are obtained from: https://primer.style/octicons/
(defvar org-special-block-extras--supported-octoicons (-partition 2 '( home "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M16 9l-3-3V2h-2v2L8 1 0 9h2l1 5c0 .55.45 1 1 1h8c.55 0 1-.45 1-1l1-5h2zm-4 5H9v-4H7v4H4L2.81 7.69 8 2.5l5.19 5.19L12 14z\"></path></svg>" link "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg>" mail "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 14 16\" width=\"14\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M0 4v8c0 .55.45 1 1 1h12c.55 0 1-.45 1-1V4c0-.55-.45-1-1-1H1c-.55 0-1 .45-1 1zm13 0L7 9 1 4h12zM1 5.5l4 3-4 3v-6zM2 12l3.5-3L7 10.5 8.5 9l3.5 3H2zm11-.5l-4-3 4-3v6z\"></path></svg>" report "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M0 2a1 1 0 011-1h14a1 1 0 011 1v9a1 1 0 01-1 1H7l-4 4v-4H1a1 1 0 01-1-1V2zm1 0h14v9H6.5L4 13.5V11H1V2zm6 6h2v2H7V8zm0-5h2v4H7V3z\"></path></svg>" tag "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 15 16\" width=\"15\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.73 1.73C7.26 1.26 6.62 1 5.96 1H3.5C2.13 1 1 2.13 1 3.5v2.47c0 .66.27 1.3.73 1.77l6.06 6.06c.39.39 1.02.39 1.41 0l4.59-4.59a.996.996 0 000-1.41L7.73 1.73zM2.38 7.09c-.31-.3-.47-.7-.47-1.13V3.5c0-.88.72-1.59 1.59-1.59h2.47c.42 0 .83.16 1.13.47l6.14 6.13-4.73 4.73-6.13-6.15zM3.01 3h2v2H3V3h.01z\"></path></svg>" clock "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 14 16\" width=\"14\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M8 8h3v2H7c-.55 0-1-.45-1-1V4h2v4zM7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 011.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7z\"></path></svg>")) "An association list of supported OctoIcons. Usage: (cadr (assoc 'ICON org-special-block-extras--supported-octoicons))")
;; Show an OctoIcon: home, link, mail, report, tag, clock (org-link-set-parameters "octoicon" :follow (lambda (_)) :export (lambda (icon _ backend) (pcase backend (`html (format (s-collapse-whitespace (cadr (assoc (intern icon) org-special-block-extras--supported-octoicons))))) (_ "")))) ;; Export a link to the current location in an Org file. (org-link-set-parameters "link-here" :follow (lambda (path) (message "This is a local anchor link named “%s”" path)) :export (lambda (label _ backend) (pcase backend (`html (format (s-collapse-whitespace "<a class=\"anchor\" aria-hidden=\"true\" id=\"%s\" href=\"#%s\">%s</a>") label label (cadr (assoc 'link org-special-block-extras--supported-octoicons)))) (_ ""))))
Going forward, it would be desirable to provide a non-whitespace alternative for the LaTeX rendition. More usefully, before the HTML export hook, we could place such ‘link-here’ links before every org-title produce clickable org-headings, similar to Github's —the necessary ingredients are likely here.
Moreover, it may be useful to have octoicon:𝒳|url
so the resulting
OctoIcon is optionally a link to the given url
. Another direction
would be to support more, if not all, the possible OctoIcons.
Source
link-here:example-location (Click the icon and see the URL has changed!)
Result
(Click the icon and see the URL has changed!)
8 Badge Links
Badges provide a quick and colourful summary of key features of a project, such as whether it's maintained, its license, and if it's documented.
Figure 7: An Emacs interface to https://shields.io/
As people who are passionate about writing great code we display "badges" in our code repositories to signal to fellow developers that we set ourselves high standards for the code we write, think of them as the software-equivalent of the brand on your jeans or other reliable product. — repo-badges
Source
What are badges? badge:Let_me_google_that|for_you!|orange|https://lmgtfy.app/?q=badge+shields.io&iie=1|Elixir or [[badge: Let me google that | for you! |orange| https://lmgtfy.app/?q=badge+shields.io&iie=1 |Elixir]] or badge:Let_me_*not*_google_that|for_you
Notice that the orange badge points to a URL and so is clickable, whereas the green one does not point to a URL and so is not clickable.
8.1 Example Badges
The general syntax is as follows, with only the first 2 are mandatory,
with the colour defaulting to green, and the url and logo both to nil.
We can thus have badge:label|message
⇐
badge:key|value|informational|here|Elixir
- Standard template; with URL pointing to current location which is named
#key
—click on it and look at your browser's URL ;-)
- Standard template; with URL pointing to current location which is named
⇐
badge:example_with_spaces,_-,_and_%|points_right_here|orange|here
⇐
badge:key|value
.⇐
badge:empty_value||informational
⇐
badge:|value
⇐
badge:||green
( No key; nor value! )Also:
⇐
[[badge:]]
.
General Syntax
badge:your_key|its_neato_value|some_url|a_logo_as_shown_below [[badge: your key | its neato value | some url | a logo as shown below]] reddit-subscribe-to:exact-name-of-a-subreddit github-stars:user-name/repository-name github-watchers:user-name/repository-name github-forks:user-name/repository-name github-followers:user-name twitter-follow:user-name tweet:url
For now, only badge:
badges redirect to their URL, if any, upon a user's click.
Within Emacs, hovering over a badge displays a tooltip indicating which values
corresponds to which badge fields.
You can make your own badge types with org-special-block-extras-make-badge.
org-special-block-extras-make-badge
The implementation is a bit lengthy since it attempts to capture a useful portion of the shilelds.io badge interface.
(cl-defmacro org-special-block-extras-make-badge (name &optional social-shields-name social-url social-shields-url ) "Make a link NAME whose export is presented as an SVG badge. If the link is intend to be a social badge, then, adhering to shield.io conventions, the appropriate SOCIAL-SHIELDS-NAME must be given. The SOCIAL-URL is a URL that the badge points to and it should have a single ‘%s’ where the link label argument will go. Some social badges have a uniform URL, but otherwise, such as twitter, deviate and so need their own SOCIAL-SHIELDS-URL, which has a single ‘%s’ for the link's label argument. ---------------------------------------------------------------------- E.g., to create a badge named “go”: (org-special-block-extras-make-badge \"go\") Then the following exports nicely from an Org file: go:key|value|blue|here|gnu-emacs -------------------------------------------------------------------------------- The LABEL should be of the shape ‘key|value|color|url|logo’ resulting in a badge “|key|value|” where the ‘key’ is coloured grey and the ‘value’ is coloured ‘color’. + Only the syntax ‘badge:key|value|color|url’ is supported. - ‘key’ and ‘value’ have their underscores interpreted as spaces. ⇒ Underscores are interpreted as spaces; ⇒ ‘__’ is interpreted as an underscore; ⇒ ‘|’ is not a valid substring, but ‘-, %, ?’ are okay. - ‘|color|url|logo’ are optional; if ‘url’ is ‘here’ then the resulting badge behaves like ‘link-here:key’. - ‘color’ may be: ‘brightgreen’ or ‘success’, ‘red’ or ‘important’, ‘orange’ or ‘critical’, ‘lightgrey’ or ‘inactive’, ‘blue’ or ‘informational’, or ‘green’, ‘yellowgreen’, ‘yellow’, ‘blueviolet’, ‘ff69b4’, etc. + Of course, you can write ‘⟦badge: ⋯⟧’, then ‘⋯’ may have multiword, spaced, content. + Such links are displayed using a SVG badges and so do not support the DESCRIPTION syntax ‘⟦link:label][description⟧’. + Besides the HTML BACKEND, such links are silently omitted. " `(org-link-set-parameters ,name :follow (lambda (path) (--> (s-split "|" path) (or (nth 3 it) path) (browse-url it))) ;; :export #'org-special-block-extras--link--badge :export (lambda (label description backend) (if (equal backend 'latex) "" (-let [ (key value color url logo) (s-split "|" label) ] (format (pcase ,(if social-shields-name `(format ,social-url label) 'url) ("here" (format "<a id=\"%s\" href=\"#%s\">%%s</a>" (s-replace "%" "%%" key) (s-replace "%" "%%" key))) ("" "%s") ;; e.g., badge:key|value|color||logo ('nil "%s") ;; e.g., badge:key|value|color (t (format "<a href=\"%s\">%%s</a>" (s-replace "%" "%%" ,(if social-shields-name `(format ,social-url label) 'url)))) ) ,(if social-shields-name (if social-shields-url `(format ,social-shields-url label) `(format "<img src=\"https://img.shields.io/%s/%s?style=social\">" ,social-shields-name label)) '(format "<img src=\"https://img.shields.io/badge/%s-%s-%s?logo=%s\">" (url-hexify-string (s-replace "-" "--" key)) (url-hexify-string (s-replace "-" "--" (or value ""))) color logo))) ))) ;; The tooltip alongside a link :help-echo (lambda (window object position) (save-excursion (goto-char position) (-let* (((&plist :path :format :raw-link :contents-begin :contents-end) (cadr (org-element-context))) (description (when (equal format 'bracket) (copy-region-as-kill contents-begin contents-end) (substring-no-properties (car kill-ring))))) (format "%s\n\n General Syntax:\n\t badge:key|value|colour|url|logo" raw-link))))))
Notice that we can have special %
-HTML characters in URLs; e.g.,
[[badge:my key|my value| my
colour|https://www.google.com/search?q=hello%20world|gnu-emacs]]
⇒
.
Example: Derived Reddit/Github/Twitter social badges
For example, a super simple ‘badge’ link type
(org-special-block-extras-make-badge "badge")
A more involved example.
;; Since we're invoking a macro, the twitter-excitement is used lazily; i.e., ;; consulted when needed instead of being evaluated once. (defvar org-special-block-extras-link-twitter-excitement "This looks super neat (•̀ᴗ•́)و:" "The string prefixing the URL being shared.") (org-special-block-extras-make-badge "tweet" "twitter/url?=url=" (format "https://twitter.com/intent/tweet?text=%s:&url=%%s" org-special-block-extras-link-twitter-excitement) "<img src=\"https://img.shields.io/twitter/url?url=%s\">" )
A few more in one, convoluted, go.
;; MA: I don't think this is ideal for long-term maintainability, see ‘:OLD’ below. (cl-loop for (social url name) in '(("reddit/subreddit-subscribers" "https://www.reddit.com/r/%s" "reddit") ("github" "https://www.github.com/%s") ("github/stars" "https://www.github.com/%s/stars") ("github/watchers" "https://www.github.com/%s/watchers") ("github/followers" "https://www.github.com/%s?tab=followers") ("github/forks" "https://www.github.com/%s/fork") ("twitter/follow" "https://twitter.com/intent/follow?screen_name=%s")) for name′ = (or name (s-replace "/" "-" social)) do (eval `(org-special-block-extras-make-badge ,name′ ,social ,url)))
Here are some examples.
8.2 Example Colours
Source
+ badge:|red|red badge:|critical|critical + badge:|blue|blue badge:|informational|informational + badge:|brightgreen|brightgreen badge:|success|success + badge:|orange|orange badge:|important|important + badge:|lightgrey|lightgrey badge:|inactive|inactive + badge:|green|green + badge:|yellowgreen|yellowgreen + badge:|yellow|yellow + badge:|blueviolet|blueviolet + badge:|ff69b4|ff69b4 + badge:|9cf|9cf + ... Consult https://htmlcolorcodes.com/ to see the HEX code of any other colour you wish to use; e.g., badge:|1d8348|1d8348
Result
- …
Consult https://htmlcolorcodes.com/ to see the HEX code of any other colour you
wish to use; e.g.,
8.3 Example Badge Icons
Here are a few free SVG icons for popular brands from https://simpleicons.org/.
To use any of the names below, just write, say,
badge:||grey||SVG-NAME-HERE
.
- “Fire”
Elixir
tinder
codeigniter
prometheus
sparkpost
- “Messaging”
quip
WeChat
google-hangouts
hackhands
google-messages
Slack
- “Emacs”
gnu-emacs
spacemacs
vim
neovim
gnu
github
acm
wikipedia
microsoft-excel
microsoft-word
dropbox
google-scholar
google
google-translate
ghost
helm
apache-openoffice
buffer
adobe-fonts
google-calendar
- “Social”
google-cast
youtube
discord
facebook
google-hangouts
whatsapp
skype
reddit
stack-overflow
stack-exchange
linkedin
twitter
jabber
- “Lightbulb”
lighthouse
google-keep
minds
- “Programming”
git
ruby
scala
OCaml
javascript
gnu-bash
powershell
LaTeX
java
kotlin
haskell
coffeescript
purescript
rust
typescript
css3
python
c
clojure
lua
adobe-acrobat-reader
perl
- “Miscellaneous”
read-the-docs
buy-me-a-coffee
gimp
mega
nintendo-3ds
paypal
pinboard
mocha
Gitea
instacart
openStreetMap
amazon
svg
rss
swagger
pastebin
skyliner
iTunes
gulp
leaflet
youtube-gaming
GIMP
atom
8.4 Common Project Badges —with copyable source
src
badge:Emacs|23/26/28|green|https://www.gnu.org/software/emacs|gnu-emacs
src
badge:Org|9.3.6|blue|https://orgmode.org|gnu
src
[[badge:org-special-block-extras|1.0|informational|https://alhassy.github.io/org-special-block-extras/README.html|Gnu-Emacs][org-special-block-extras badge]]
src
[[badge:melpa|pending|critical|https://github.com/alhassy/emacs.d#use-package-the-start-of-initel|github][melpa badge]]
src
[[badge:docs|literate|success|https://github.com/alhassy/emacs.d#what-does-literate-programming-look-like|read-the-docs][read-the-docs badge]]
src
badge:wiki|github|informational|here|wikipedia
src
badge:code_coverage|88%|green|here|codecov
src
badge:build|passing|success|here|azure-pipelines
src
badge:author|musa_al-hassy|purple|https://alhassy.github.io/|nintendo-3ds
src
badge:author|musa_al-hassy|purple|https://alhassy.github.io/|gimp
src
[[badge:license|GNU_3|informational|https://www.gnu.org/licenses/gpl-3.0.en.html|read-the-docs][gnu 3 license badge]]
src
badge:issue_tracking|github|informational|here|github
src
badge:help_forum|discourse|informational|here|discourse
src
badge:social_chat|gitter|informational|https://gitter.im/explore|gitter
src
badge:Maintained?|yes|success
src
badge:Maintained?|no|critical
src
badge:No_Maintenance_Intended|×|critical|http://unmaintained.tech/
src
badge:website|up|success
src
badge:website|down|critical
src
badge:Ask_me|anything|1abc9c
src
badge:contributions|welcome|green|https://github.com/alhassy/org-special-block-extras/issues
src
badge:Made_with|Python,_LaTeX,_MathJax,_and_Emacs_Org-mode|1f425
8.5 Next Steps
Going forward, it would be desirable to provide non-whitespace alternatives for the LaTeX backend.
[Author: That is why no examples are shown in the PDF ]
It may be useful to colour the|
-separated fields of a badge link.
9 Tooltips for Glossaries, Dictionaries, and Documentation
Let's make a link type doc
that shows a tooltip documentation —e.g., glossary
or abbreviation— for a given label.
E.g., user-declared Category Theory and Emacs-retrieved loop and thread-last ^_^
Eye-candy ---A gallery of images
Figure 8: Tooltips for documentation and glossary items –in the browser!
Figure 9: Tooltips for documentation and glossary items –in Emacs!
Figure 10: Tooltips for documentation and glossary items –in the PDF!
Full Example: …
User enters …
#+begin_documentation Existential Angst :label "ex-angst" A negative feeling arising from freedom and responsibility. Also known as 1. /Existential Dread/, and 2. *Existential Anxiety*. Perhaps a distraction, such as [[https://www.w3schools.com][visiting W3Schools]], may help ;-) Then again, ~coding~ can be frustrating at times, maybe have a slice of pie with maths by reading “$e^{i×π} + 1 = 0$” as a poem ;-) #+end_documentation
Then… doc:ex-angst
gives us Existential Angst,
or using a description: “existence is pain”?
( [[doc:ex-angst][“existence is pain”?]]
)
As it stands, Emacs tooltips only appear after an export has happened: The export updates the dictionary variable which is used for the tooltips utility.
The
:label
is optional; when not provided it defaults to the name of the entry with spaces replaced by ‘_’. For more, see org-special-block-extras--documentation.Entry names do not need to be unique, but labels do! ( The labels are like the addresses used to look up an entry. )
More Examples
Supported | Example | Source |
---|---|---|
No description | Category Theory | doc:cat |
TODO: FIXME
Fallback; e.g., arbitrary ELisp Docs | thread-first | doc:thread-first |
Notice how hovering over items makes them appear, but to make them disappear you should click on them or scroll away. This is ideal when one wants to have multiple ‘definitions’ visible ;-)
Below are the entries in my personal glossary ;-)
Category Theory Category Theory Category Theory Category Theory Existential Angst Existential Angst Existential Angst Existential Angsty Existential Angsty Existential Angsty Existential Angsty Hussain Hussain Hussain Hussain Hello Hello Existential Angsty Graph Graph Existential Angsty Expression Expression Existential Angsty Induction Induction Existential Angsty Textual_Substitution Textual_Substitution Existential Angsty Inference_Rule Inference_Rule Existential Angsty Logic Logic Existential Angsty Theorem Theorem Existential Angsty Metatheorem Metatheorem Existential Angsty Calculus Calculus Calculus Semantics Semantics Semantics Semantics Calculational Proof Calculational Proof Existential Angsty Programming Programming Existential Angsty Specification Specification Existential Angsty Proving_is_Programming Proving_is_Programming Existential Angsty Algorithmic Problem Solving Algorithmic Problem Solving Existential Angsty Natural Transformation Natural Transformation Natural Transformation Natural Transformation Category Theory Category Theory Associative Associative Existential Angsty Identity Identity Existential Angsty Distributive Distributive Existential Angsty Commutative Commutative Existential Angsty Reflexive Reflexive Existential Angsty Transitive Transitive Existential Angsty Symmetric Symmetric Existential Angsty Antisymmetric Antisymmetric Existential Angsty Asymmetric Asymmetric Existential Angsty Preorder Preorder Existential Angsty Equivalence Equivalence Existential Angsty Linear Linear Existential Angsty Semilinear Semilinear Existential Angsty Univalent Univalent Existential Angsty Total Total Existential Angsty Map Map Existential Angsty Surjective Surjective Existential Angsty Injective Injective Existential Angsty Bijective Bijective Existential Angsty Iso Iso Existential Angsty Difunctional Difunctional Existential Angsty Existential Angsty Hussain Hussain Hussain Hussain Hello Hello Existential Angsty Graph Graph Existential Angsty Expression Expression Existential Angsty Induction Induction Existential Angsty Textual_Substitution Textual_Substitution Existential Angsty Inference_Rule Inference_Rule Existential Angsty Logic Logic Existential Angsty Theorem Theorem Existential Angsty Metatheorem Metatheorem Existential Angsty Calculus Calculus Calculus Semantics Semantics Semantics Semantics Calculational Proof Calculational Proof Existential Angsty Programming Programming Existential Angsty Specification Specification Existential Angsty Proving_is_Programming Proving_is_Programming Existential Angsty Algorithmic Problem Solving Algorithmic Problem Solving Existential Angsty Natural Transformation Natural Transformation Natural Transformation Natural Transformation Category Theory Category Theory Category Theory Associative Associative Existential Angsty Identity Identity Existential Angsty Distributive Distributive Existential Angsty Commutative Commutative Existential Angsty Reflexive Reflexive Existential Angsty Transitive Transitive Existential Angsty Symmetric Symmetric Existential Angsty Antisymmetric Antisymmetric Existential Angsty Asymmetric Asymmetric Existential Angsty Preorder Preorder Existential Angsty Equivalence Equivalence Existential Angsty Linear Linear Existential Angsty Semilinear Semilinear Existential Angsty Univalent Univalent Existential Angsty Total Total Existential Angsty Map Map Existential Angsty Surjective Surjective Existential Angsty Injective Injective Existential Angsty Bijective Bijective Existential Angsty Iso Iso Existential Angsty Difunctional Difunctional Existential Angsty Existential Angsty Hussain Hussain Hussain Hussain Hello Hello Existential Angsty Graph Graph Existential Angsty Expression Expression Existential Angsty Induction Induction Existential Angsty Textual_Substitution Textual_Substitution Existential Angsty Inference_Rule Inference_Rule Existential Angsty Logic Logic Existential Angsty Theorem Theorem Existential Angsty Metatheorem Metatheorem Existential Angsty Calculus Calculus Calculus Semantics Semantics Semantics Semantics Calculational Proof Calculational Proof Existential Angsty Programming Programming Existential Angsty Specification Specification Existential Angsty Proving_is_Programming Proving_is_Programming Existential Angsty Algorithmic Problem Solving Algorithmic Problem Solving Existential Angsty Natural Transformation Natural Transformation Natural Transformation Natural Transformation Category Theory Category Theory Category Theory Associative Associative Existential Angsty Identity Identity Existential Angsty Distributive Distributive Existential Angsty Commutative Commutative Existential Angsty Reflexive Reflexive Existential Angsty Transitive Transitive Existential Angsty Symmetric Symmetric Existential Angsty Antisymmetric Antisymmetric Existential Angsty Asymmetric Asymmetric Existential Angsty Preorder Preorder Existential Angsty Equivalence Equivalence Existential Angsty Linear Linear Existential Angsty Semilinear Semilinear Existential Angsty Univalent Univalent Existential Angsty Total Total Existential Angsty Map Map Existential Angsty Surjective Surjective Existential Angsty Injective Injective Existential Angsty Bijective Bijective Existential Angsty Iso Iso Existential Angsty Difunctional Difunctional Existential Angsty Category Theory Category Theory Category Theory Existential Angst Existential Angst Existential Angst Existential Angsty Existential Angsty Existential Angsty Existential Angsty Hussain Hussain Hussain Hussain Hello Hello Existential Angsty Graph Graph Existential Angsty Expression Expression Existential Angsty Induction Induction Existential Angsty Textual_Substitution Textual_Substitution Existential Angsty Inference_Rule Inference_Rule Existential Angsty Logic Logic Existential Angsty Theorem Theorem Existential Angsty Metatheorem Metatheorem Existential Angsty Calculus Calculus Calculus Semantics Semantics Semantics Semantics Calculational Proof Calculational Proof Existential Angsty Programming Programming Existential Angsty Specification Specification Existential Angsty Proving_is_Programming Proving_is_Programming Existential Angsty Algorithmic Problem Solving Algorithmic Problem Solving Existential Angsty Natural Transformation Natural Transformation Natural Transformation Natural Transformation Category Theory Category Theory Category Theory Associative Associative Existential Angsty Identity Identity Existential Angsty Distributive Distributive Existential Angsty Commutative Commutative Existential Angsty Reflexive Reflexive Existential Angsty Transitive Transitive Existential Angsty Symmetric Symmetric Existential Angsty Antisymmetric Antisymmetric Existential Angsty Asymmetric Asymmetric Existential Angsty Preorder Preorder Existential Angsty Equivalence Equivalence Existential Angsty Linear Linear Existential Angsty Semilinear Semilinear Existential Angsty Univalent Univalent Existential Angsty Total Total Existential Angsty Map Map Existential Angsty Surjective Surjective Existential Angsty Injective Injective Existential Angsty Bijective Bijective Existential Angsty Iso Iso Existential Angsty Difunctional Difunctional Hussain Hussain Hussain Hussain Hello Hello Existential Angsty Graph Graph Existential Angsty Expression Expression Existential Angsty Induction Induction Existential Angsty Textual_Substitution Textual_Substitution Existential Angsty Inference_Rule