1

org-special-block-extras

 2 years ago
source link: https://alhassy.github.io/org-special-block-extras/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

org-special-block-extras

Processing math: 93%

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 and quote, 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!”

minimal-working-example-multiforms.png

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/, italics Emphasise
    *Strong*, bold Strong
    */very strongly/*, bold italics very strongly
    =verbatim=, monospaced typewriter verbatim
    +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 doit 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

sin2x+cos2x=∫π+1π1dx=33

  • 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

eiπ+1=0

See equation (1).


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:

literate-programming.png


  • ⇒ A brief reference of Emacs keybindings; 2 pages
  • ⇒ A compact Emacs Lisp Reference; 7 pages

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.

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's funcall.
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's funcall. 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 and example 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

This is some editable text! Click me & type!

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.

What would I use links for?

Buttons

[[elisp:(find-file user-init-file)][Go to my init!]] is a nice clickable-actionable button within Emacs. The elisp link is part of Emacs.

Textual expansions

show:user-full-name expands, upon export, to Musa Al-hassy on my machine. Or maybe we want a splash of colour in our lives: *<pink: super neato stuff>* becomes super neato stuff.

Similarly, link-here:usefulness-of-links becomes: . When you click on it, the URL of the webpage changes: It is a local anchor to a possibly interesting location in your article. Similarly, octoicon:home becomes ; o-link/octoicon.

More aesthetically, <badge: Emacs | is awesome | blue | https://www.google.com/search?q=hello%20world | gnu-emacs> exports to .

These link types are all defined below.

Definitions / Personal Glossary

doc:thread-first, doc:family, doc:Hussain export as the link labels but with tooltips for the Lisp function thread-first, for the English definition of family, and my personal documentation for the phrase Hussain.

Similarly, [[kbd:M-s h .]] becomes M-s h ., which has a tooltip for the Lisp function associated with the keybinding.

Example Mini-Article Using The Above Link Types

( ) C-x ESC ESC invokes repeat-complex-command, you will see a Lisp form of the previous interactive action you performed. You can press RET to repeat that command, or, more likely, to convert your actions to Lisp code for a personal function.

This is a super neato way to get Lisp from your actions. Conversely, use M-: to evaluate any Lisp you have in-hand 😉

Emacs-is_so_cool-informational?logo=gnu-emacs.

Just as there is defun for “def”ining “fun”ctions and defmacro for “def”ining “mac”ros, there is also o-deflink for “def”ining “link”s for “O”rg, and it behaves similar to them.

However, there are usually multiple ways to define things —e.g., macros are code-valued functions defined with cl-defun. Indeed, Org links are popularly defined using org-link-set-parameters —which, unlike o-deflink, is not consistent with defun— and so let's take a look at it first.

1.2.1 A comprehensive example of org-link-set-parameters and org-link-parameters

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 example:salam and descriptive, ^_^ example_link.png

 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:

  1. label ⇒ the path of the link, the text after the link type prefix
  2. description ⇒ the description of the link, if any
  3. backend ⇒ the export format, a symbol like html or latex or ascii.

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 :displayShould 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.

MoreSee org-link-parameters for documentation on more parameters.

1.2.2 Define links as you define functions: o-deflink

For export purposes, an Org link can be thought of as a string-valued function whose inputs are the link's label, description, and the export backend. Since the input arguments are fixed, the o-deflink interface is similar to cl-defun in that it takes a name, (no input arguments), an optional docstring, and a body which may make use of (the unchanging input) names o-label, o-description, o-backend. Moreover, after the documentation string, it may take an optional vector of display settings: Org links can be declared to look aesthetically pleasing within Emacs and to behave (via clicks/hovers/key-bindings) in meaningful ways.

o-deflink.png

Here is a minimal working example. After evaluating this, in an Org buffer, phrases such as shout:hello will (1) be shown in bold red, (2) have a hover tooltip that shows the documentation of the link and an HTML export preview, and (3) the HTML export of the Org buffer will render the label hello as capitalised red text.

(o-deflink shout
  "Capitalise the link description, if any, otherwise capitalise the label.

The link text appears as red bold in both Emacs and in HTML export."
  [:face '(:foreground "red" :weight bold)]
  (format "<span style=\"color:red\"> %s </span>"
          (upcase (or o-description o-label))))

Implementation of o-deflink

(cl-defmacro o-deflink
    (name &optional docstring display &rest body)
  "Make a new Org-link NAME that exports using form BODY.

Since Org links are essentially string-valued functions,
a function ‘o-link/NAME’ is created.

DOCSTRING is optional; it is visible with
   (documentation 'o-link/NAME)

BODY is a string-valued expression, that may make use of the names
o-label, o-description, o-backend. The final one refers to the export
backend, such as 'html or 'latex. The first two are obtained from uses:

   [[name:o-label][o-description]]

In particular, the use case “name:o-label” means that o-description is nil.

Example use:

   ;; In a Lisp buffer, press “C-x C-e” to load this definition
   (o-deflink shout (upcase (or o-description o-label)))

   ;; In an Org-buffer, press “C-c C-e h o” to see how this exports
   <shout: hello world!>

   ;; Or using the bracket format
   [[shout:][hello world!]]
   [[shout: hello world!]]

   ;; or using the plain format
   shout:hello_world

Here is a more complex, involved, example that makes use of
‘:let’ for local declarations. For instance, “define:hello”
renders as the word “hello” with a tooltip defining the word; the
definition is obtained from the command line tool ‘wn’.

  (o-deflink define
    \"Define the given word using WordNet, along with synonyms and coordinate terms.\"
    [:let (definition (shell-command-to-string (format \"wn %s -over -synsn -coorn\" o-label)))
     :help-echo definition]
    (--> definition
      (s-replace-regexp \"\\\\\\\"\" \"''\" it) ;; The presence of ‘\\\"’ in tooltips breaks things, so omit them.
      (s-replace-regexp \"\\n\" \"<br>\" it)
      (format \"<abbr class=\\\"tooltip\\\" title=\\\"%s\\\">%s</abbr>\" it o-label)))

For HTML tooltips, see `o-html-export-preserving-whitespace'.

More generally, org-special-block-extra's “doc” link type
supports, in order of precedence: User definitions, Emacs Lisp
documentation of functions & variables, and definitions of
English words. For example, “doc:existential_angst” for an entry
‘existential_angst’ whose associated documentation-glossary is
user-defined in a ‘#+documentation’ Org-block, or
“doc:thread-first” for the Emacs Lisp documentation of the
function `thread-first', or “doc:user-mail-address” for the Emacs
Lisp documentation of the variable `user-mail-address', or
“doc:hello” for the definition of the English word ‘hello’.

DISPLAY is a vector consisting of key-value pairs that affects how the link
is displayed in Emacs Org buffers. The keys are as follows.

+ :help-echo is a string-valued expression of the tooltip that should accompany
  the new link in Org buffers. It has access to o-format being one of ‘plain’,
  ‘angle’, ‘bracket’ which indicates the format of the link, as shown above.
  It also has access to o-label and o-description.

  By default, the tooltip is the link name followed by the documentation
  of the link, and, finally, the HTML export of the link.
  That way, upon hover, users can visually see the link contents,
  know what/how the link exports, and actually see the HTML export.

  That is to say, for the ‘shout’ example aboce, the default display is essentially:
  [:help-echo (o-link/shout o-label o-description 'html)]

  You may want to add the following to your Emacs init file:

    ;; Nearly instantaneous display of tooltips.
    (setq tooltip-delay 0)
    ;; Give user 30 seconds before tooltip automatically disappears.
    (setq tooltip-hide-delay 300)

+ :face specifies how should these links be displayed within Emacs.
   It is a list-valued expression.
   As usual, it may make use of O-LABEL (but O-DESCRIPTION has value nil).

+ [:display full] if you do not want bracket links to be
  folded away in Org buffers; i.e., “[[X][Y]]” does not render as just “Y”.

+ :follow is a form that is executed when you click on such links; e.g., to open
   another buffer, browser, or other action. It makes use of (an implicit argument) ‘o-label’.
   Be aware that ‘o-label’ is a string that may contain spaces; e.g., when the action is to open
   a URL in a browser.

   If you are in need of providing similar, related, actions on a single link
   then your :follow can condition on the current prefix argument via ‘o-prefix’
   (which is essentially `current-prefix-arg').
   For instance, a user presses “C-u RET” on your link to do one thing
   but “C-u 72 RET” to do another action.

+ :keymap is an alternating list of keys and actions to be
  performed when those keys are pressed while point is on the link.
  For example:
      [:keymap (C-h (message-box \"hola\"))]

  By default, C-n and C-p are for moving to next and previous occruances of the same link type.

+ :let is a list of alternating variable symbol name and value, which are then used to form
  a concrete `let*' clause. This is useful for introducing local variables for use in the DISPLAY
  as well as in the CONTENTS. Such local declarations may make use of O-LABEL and O-DESCRIPTION, as usual."
  (cl-destructuring-bind (docstring display body)
      (lf-extract-optionals-from-rest docstring #'stringp
                                      display   #'vectorp
                                      body)
    (setq display (seq--into-list display))
    (let ((o-link/NAME (intern (format "o-link/%s" name)))
          (navigation "Press “C-h” to see possible actions on this link type.")
          (lets (cl-loop for (variable value)
                      on (cl-getf display :let)
                      by #'cddr
                      collect (list variable value))))
      `(progn
       ;; Declare the underlying function and documentation
       (cl-defun ,o-link/NAME ;; function name
           (o-label o-description o-backend)         ;; function args
           ;; new function documentation
           ,docstring
           ;; function body
           (let* ,lets ,@body))
       ;; Construct the Org-link
       (org-link-set-parameters
        ,(format "%s" name)
        :export (quote ,o-link/NAME)
        ;; How should these links be displayed?
        ;; (We augment the namespace with the missing o-description that local variables may be using.)
        :face (lambda (o-label)  (let (o-description) (let* ,lets ,(cl-getf display :face))))
        ;; When you click on such links, what should happen?
        ;; (We augment the namespace with the missing o-description that local variables may be using.)
        :follow (lambda (o-label o-prefix) (let (o-description) (let* ,lets ,(cl-getf display :follow))))
         ;; These links should *never* be folded in descriptive display;
        ;; i.e., “[[example:lable][description]]” will always appear verbatim
        ;; and not hide the first pair […].
        :display (quote ,(cl-getf display :display)) ;; e.g.,: 'full
        ;; Any special keybindings when cursour is on this link type?
        ;; On ‘NAME:’ links, C-n/p to go to the next/previous such links.
        :keymap (let ((o-keymap (copy-keymap org-mouse-map))
                      (pattern (format "%s:" (quote ,name)))
                      (msg  (concat
                             (documentation (quote ,o-link/NAME))
                             "\nKEY BINDINGS:\n"
                             "\nUnless indicated below otherwise..."
                             "\n\tC-h: Shows this helpful message buffer"
                             "\n\tC-n/C-p on the link to jump to next/previous links of this type;"
                             "\n\tC-c C-x C-n/p for moving between arbitrary link types.\n\n"
                             (pp-to-string (quote ,(cl-getf display :keymap))))))
                  ;; Populate the keymap
                  (cl-loop for (key action)
                        on (-cons* 'C-p `(re-search-backward ,pattern nil t)
                                   'C-n `(re-search-forward ,pattern nil t)
                                   'C-h `(-let [max-mini-window-height 0] ;; i.e., insist on displaying in a dedicated buffer
                                           (display-message-or-buffer ,msg))
                                   (quote ,(cl-getf display :keymap)))
                        by #'cddr
                        do (define-key o-keymap (kbd (format "%s" key))
                             `(lambda () (interactive) ,action)))
                  ;; Return the keymap
                  o-keymap)
        ;; The tooltip alongside a link
        :help-echo (lambda (window object position)
                     (save-excursion
                       (goto-char position)
                       (-let* (((&plist :path :format :contents-begin :contents-end)
                                (cadr (org-element-context)))
                               (o-format format)
                               (o-label path)
                               (o-description
                                (when (equal format 'bracket)
                                  (copy-region-as-kill contents-begin contents-end)
                                  (substring-no-properties (car kill-ring)))))
                         (or (let* ,lets ,(cl-getf display :help-echo))
                             (format "%s:%s\n\n%s\nHTML Export:\n\n%s"
                                     (quote ,name)
                                     (or o-description o-label)
                                     ,(concat (or docstring "") "\n\n" navigation "\n")
                                     (,o-link/NAME o-label o-description 'html)))))))
        ;; Return value is the name of the underlying function.
        ;; We do this to be consistent with `defun'.
        (quote ,o-link/NAME)))))

Let's have some sanity tests…

(o-deflink shout
  "Capitalise the link description, if any, otherwise capitalise the label.

The link text appears as red bold in both Emacs and in HTML export."
  [:face '(:foreground "red" :weight bold)
   ;; :help-echo (o-link/shout o-label o-description 'html)
   :display full
   :keymap (C-m (message-box "hola"))
   :follow (message-box "%s and %s" pre current-prefix-arg)
   ]
  (format "<span style=\"color:red\"> %s </span>"
          (upcase (or o-description o-label))))

(deftest "o-deflink makes documented functions"
  [o-deflink]
  (⇝ (documentation #'o-link/shout)
     "Capitalise the link description, if any, otherwise capitalise the label.

     The link text appears as red bold in both Emacs and in HTML export."))

(deftest "o-deflink works as expected, plain links"
  [o-deflink]
  (should (not (null (symbol-function 'o-link/shout))))
  (⇝ (⟰ "shout:hello")
     "<p> <span style=\"color:red\"> HELLO </span></p>"))

(deftest "o-deflink works as expected, bracket links"
  [o-deflink]
  (⇝ (⟰ "[[shout:hello]]")
     "<p> <span style=\"color:red\"> HELLO </span></p>")
  (⇝ (⟰ "[[shout:hello][world!]]")
     "<p> <span style=\"color:red\"> WORLD! </span></p>"))

(deftest "o-deflink works as expected, angle links"
  [o-deflink]
  (⇝ (⟰ "<shout: hello world!>")
     "<p> <span style=\"color:red\"> HELLO WORLD! </span></p>"))

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:

  1. ( Fuse the process of link generation and special block support into one macro, defblock which is like defun. )
  2. The user writes as string-valued function named 𝒳, possibly with arguments, that has access to a contents and backend variables.

    ‘defblock’ Implementation

    (defvar o--supported-blocks nil
      "Which special blocks, defined with DEFBLOCK, are supported.")
    
    (cl-defmacro o-defblock
      (name main-arg kwds &optional experimental docstring &rest 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 and DOCSTRING and BODY, the first two parts are
    optional; they're a symbol, a string, then the main body.  The
    symbol, O-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)O-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 'o--supported-blocks name) ;; global var
    
      ;; Identify which of the optional features is present...
      (cl-destructuring-bind (o-respect-newlines? docstring body)
          (lf-extract-optionals-from-rest experimental #'keywordp
                                          docstring    #'stringp
                                          body)
        `(progn
           ;; Produce an associated Lisp function
           ,(o-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.
             o-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 …)’
               (o-deflink ,name
                 [:help-echo (format "%s:%s\n\n%s" (quote ,name) o-label ,docstring)
                 ,@(cddr main-arg) ;; verbatim link extras
                 ]
                 ;; s-replace-all `(("#+end_export" . "") (,(format "#+begin_export %s" backend) . ""))
                 (s-replace-all `(("@@" . "")) ;; (,(format "@@%s:" backend) . "")
                                (,(intern (format "o--%s" name))
                                 o-backend (or o-description o-label) o-label :o-link? t))))))
    
    ;; WHERE ...
    
    (cl-defmethod o-defblock---support-block-type
        (name docstring main-arg-name main-arg-value kwds body o-respect-newlines?)
      "Helper method for o-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 "o--%s" name))
           (backend raw-contents
                    &optional ;; ,(car main-arg)
                    ,main-arg-name
                    &rest _
                    &key (o-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 o--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 o-link?
                    x
                ;; o-respect-newlines? is super experimental: It's a bit ugly on the LaTeX side.
                (cond ((and ,o-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 o-link?
                          x
                        (cond ((and ,o-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 o--header-args))
                                                      ,(intern (format ":%s" k)))
                                 (when (s-blank-p ,k)
                                   (setq ,k it))))
    
           (org-export
            (let ((contents (org-parse raw-contents))) ,@body)))))
    

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.

  1. 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ₙ :o-contents contents)
    

    Implementing the hooking mechanism

The mechanism that rewrites your source…

(defun o--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 o--current-backend nil
  "A message-passing channel updated by
o--support-special-blocks-with-args
and used by DEFBLOCK.")

(defun o--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 o--current-backend backend)
  (let (blk-start        ;; The point at which the user's block begins.
        header-start ;; The point at which the user's block header & args begin.
        kwdargs          ;; The actual key-value arguments for the header.
        main-arg         ;; The first (non-keyed) value to the block.
        blk-column       ;; The column at which the user's block begins.
        body-start       ;; The starting line of the user's block.
        blk-contents         ;; The actual body string.
        ;; ⟨blk-start/column⟩#+begin_⟨header-start⟩blk main-arg :key₀ val ₀ … :keyₙ valₙ  ;; ⟵ ⟨kwdargs⟩
        ;; ⟨body-start⟩ body
        ;; #+end_blk
        )
  (cl-loop for blk in o--supported-blocks
        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!
          (setq header-start (point))
          ;; Save indentation
          (re-search-backward (format "\\#\\+begin_%s" blk))
          (setq blk-start (point))
          (setq blk-column (current-column))
          ;; actually process body
          (goto-char header-start)
          (setq body-start (1+ (line-end-position)))
          (thread-last
              (buffer-substring-no-properties header-start (line-end-position))
            (format "(%s)")
            read
            (--split-with (not (keywordp it)))
            (setq kwdargs))
          (setq main-arg (o--pp-list (car kwdargs)))
          (setq kwdargs (cadr kwdargs))
          (forward-line -1)
          (re-search-forward (format "^\s*\\#\\+end_%s" blk))
          (setq blk-contents (buffer-substring-no-properties body-start (line-beginning-position)))
          (kill-region blk-start (point))
          (insert (eval `(,(intern (format "o--%s" blk))
                          (quote ,backend)
                          ,blk-contents
                          ,main-arg
                          ,@(--map (list 'quote it) kwdargs))))
          ;; See: https://github.com/alhassy/org-special-block-extras/issues/8
          ;; (indent-region blk-start (point) blk-column) ;; Actually, this may be needed...
          ;; (indent-line-to blk-column) ;; #+end...
          ;; (goto-char blk-start) (indent-line-to blk-column) ;; #+begin...
          ;; the --map is so that arguments may be passed
          ;; as "this" or just ‘this’ (raw symbols)
      ))))

Let's have some sanity tests…

(deftest "pp-list works as desired"
  (should (equal "1 2 3 4 5"
                 (o--pp-list '(1 2 3 4 5))))
  (should (equal "1"
                 (o--pp-list '(1))))
  (should (equal ""
                 (o--pp-list nil))))

;; Using propcheck, we run this test on /arbitrary/ buffer contents.
(deftest "No supported blocks means buffer is unchanged"
  :tags '(core)
  (let* (o--supported-blocks
         (propcheck-seed (propcheck-seed))
         (buf (propcheck-generate-string nil)))
    (should (equal buf
                   (with-temp-buffer
                     (insert buf)
                     (o--support-special-blocks-with-args 'html)
                     (buffer-string))))))

(deftest "Constant blocks preserve indentation/enumeration"
  :expected-result :failed
  :tags '(core)
  (defblock go nil nil "doc" "hello") ;; Constantly “hello”
  (should (equal
"
  1. item one
  2. item two
     #+begin_export html
hello
#+end_export
  3. item three"
    (with-temp-buffer
      (insert
  "
  1. item one
  2. item two
     #+begin_go
  world
  #+end_go
  3. item three")
      (o--support-special-blocks-with-args 'html)
      (buffer-string)))))

(deftest "Constant blocks export to LaTex preserves indentation/enumeration"
(should (equal
"\\begin{enumerate}
\\item item one
\\item item two
hello
\\item item three
\\end{enumerate}
"
(org-export-string-as
  "
  1. item one
  2. item two
     #+begin_go
  world
  #+end_go
  3. item three"
  'latex :body-only-please))))


(deftest "Constant blocks export to HTML preserves indentation/enumeration"
(should (equal
"<ol class=\"org-ol\">
<li>item one</li>
<li><p>
item two
</p>
hello</li>
<li>item three</li>
</ol>
"
(org-export-string-as
  "
  1. item one
  2. item two
     #+begin_go
  world
  #+end_go
  3. item three"
  'html :body-only-please))))


(deftest "Identity blocks preserve indentation/enumeration"
    :expected-result :failed
  :tags '(core)
  (defblock id nil nil "doc" contents)
  (should (equal
"
  1. item one
  2. item two
     #+begin_export html

     #+end_export
     world

     #+begin_export html

     #+end_export
  3. item three"
    (with-temp-buffer
      (insert
  "
  1. item one
  2. item two
     #+begin_id
  world
  #+end_id
  3. item three")
      (o--support-special-blocks-with-args 'html)
      (buffer-string)))))

(deftest "Identity blocks export to LaTex preserves indentation/enumeration"
    :expected-result :failed
(defblock id nil nil "doc" contents)
(should (equal
"\\begin{enumerate}
\\item item one
\\item item two

world
\\item item three
\\end{enumerate}
"
(org-export-string-as
  "
  1. item one
  2. item two
     #+begin_id
  world
  #+end_id
  3. item three"
 'latex :body-only-please))))

(deftest "Identity blocks export to HTML preserves indentation/enumeration"
    :expected-result :failed
(defblock id nil nil "doc" contents)
(should (equal

"<ol class=\"org-ol\">
<li>item one</li>
<li><p>
item two
</p>

<p>
world
</p></li>
<li>item three</li>
</ol>
"
(org-export-string-as
  "
  1. item one
  2. item two
     #+begin_id
  world
  #+end_id
  3. item three"
 'html :body-only-please))))

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 'o--support-special-blocks-with-args)

When you disable the org-special-block-extras mode, it is deactivated…

(remove-hook 'org-export-before-parsing-hook 'o--support-special-blocks-with-args)

‘header-args’ Implementation

Then:

(defvar o--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 o-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 'o--header-args (list (quote ,blk) ,@kvs)))

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))

;; 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 "".
(o-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.
(o-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 ∑ni=0i=n×(n+1)2. Note that n×(n+1) is even. ⟦Jasim Jameson: Why are you taking about “even” here?( Aim for success! )⟧

⟦Bobbert Barakallah:

I was trying, uh …

Yeah, to explain that n×(n+1)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 o--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
o-thread-blockcall.
"
  `(concat "#+end_export\n" (,(intern (format "o--%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 o-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:

    (o-defblock nesting (name) nil
      \"Show text in a box, within details, which contains a box.\"

      (o-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 (o--blockcall ,@(car forms) ,body)]
    ,@(cl-loop for b in (cdr forms)
          collect `(setq result (o--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.

(o-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."
  (o-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

(o-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
                 (o--blockcall box source :background-color source-color)
                 org-export)
               ;; Separator
               sep
               ;; Result
               (thread-last raw-contents
                 (o--blockcall box result :background-color result-color)
                 org-export))]

   (if (equal style "parallel")
       (o--blockcall parallel "2" :bar nil text)
       (concat "#+end_export\n" text "\n#+begin_export"))))

Example

#+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

ei×π+1=0

(Sequential) Example

#+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

ei×π+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 html

There 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

Box-as-link! Boxception!

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

  1. You need at-most 5 lines of Lisp.
  2. 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

(o-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

  1. It can be done in less than 10 lines of Lisp.
  2. First, try to s-replace-all the substitution '(("Allah" . "God") ("Yacoub". "Jacob") ("Yusuf" . "Joseph")) only.
  3. 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

(o-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

  1. It can be done in less than 10 lines of Lisp.
  2. 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>
    
  3. Escape HTML snippets by enclosing them in @@html: … @@ —as discussed above in the introduction to special blocks.
  4. 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.”


(o-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

a:Af:A→Bf(a):B[Function Application]pp⇒qq[Modus Ponens]

Solution

Did you actually try? Maybe see the ‘hints’ above!

Solution, for real

(defun o--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)) (o--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 #'o--list-to-math children))
                conclusion
                (if named? (format "[\\text{%s}]" name) "")))))))

(o-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\\]"
                                (o--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 ;-)

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

 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 o-hide-editor-comments nil
  "Should editor comments be shown in the output or not.")

(o-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 o-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.

Let's have some sanity tests…

(deftest "The user's remark is enclosed in the default delimiters"
  [remark]
  (⇝ (⟰ "#+begin_remark
                   Here is some meta-commentary...
                  #+end_remark")
     (* anything) "[Editor Remark:"
     (* anything) "Here is some meta-commentary"
     (* anything)  "]"))

;; The other features of remark blocks should be tested;
;; but this is not a pressing, nor interesting, concern.

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., ∑ni=0i2

With:

A linear one-dimensional notation; e.g., (Σi:0..n∙i2)

]

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., (Σi:0..n∙i2)

]

Forgot the new replacement thing…

[Editor Remark: Replace:

The two-dimensional notation; e.g., ∑ni=0i2

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: o-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

(o-defblock details (title "Details") (title-color "green")
  "Enclose contents in a folded up box, for HTML.

For LaTeX, this is just a boring, but centered, box.

By default, the TITLE of such blocks is “Details”
and its TITLE-COLOR is green.

In HTML, we show folded, details, regions with a nice greenish colour.

In the future ---i.e., when I have time---
it may be prudent to expose more aspects as arguments,
such as ‘background-color’.
"
  (format
   (pcase backend
     (`latex "\\begin{quote}
                  \\begin{tcolorbox}[colback=%s,title={%s},sharp corners,boxrule=0.4pt]
                    %s
                  \\end{tcolorbox}
                \\end{quote}")
     (_ "<details class=\"code-details\"
                 style =\"padding: 1em;
                          background-color: #e5f5e5;
                          /* background-color: pink; */
                          border-radius: 15px;
                          color: hsl(157 75% 20%);
                          font-size: 0.9em;
                          box-shadow: 0.05em 0.1em 5px 0.01em  #00000057;\">
                  <summary>
                    <strong>
                      <font face=\"Courier\" size=\"3\" color=\"%s\">
                         %s
                      </font>
                    </strong>
                  </summary>
                  %s
               </details>"))
   title-color title contents))

:HACK:Disabled: 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. MA: TODO: The above should not be present, it should instead be factored out into a defblock-alias function.

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.

Let's have some sanity tests…

(deftest "The result is a <details> tag containing the user's title & text."
  [details]
  (⇝ (⟰ "#+begin_details TITLE-RIGHT-HERE
          My aside...
          #+end_details")
     "<details"
     (* anything)
     "TITLE-RIGHT-HERE"
     (* anything)
     "My aside"
     (* anything)
     "</details>"))

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., 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

(o-defblock box (title "") (background-color nil)
  "Enclose text in a box, possibly with a title.

By default, the box's COLOR is green for HTML and red for LaTeX,
and it has no TITLE.

The HTML export uses a padded div, whereas the LaTeX export
requires the tcolorbox pacakge.

In the future, I will likely expose more arguments.
"
  (apply #'concat
  (pcase backend
   (`latex `("\\begin{tcolorbox}[title={" ,title "}"
             ",colback=" ,(pp-to-string (or background-color 'red!5!white))
             ",colframe=red!75!black, colbacktitle=yellow!50!red"
             ",coltitle=red!25!black, fonttitle=\\bfseries,"
             "subtitle style={boxrule=0.4pt, colback=yellow!50!red!25!white}]"
             ,contents
             "\\end{tcolorbox}"))
    (_ `("<div style=\"padding: 1em; background-color: "
             ,(o-subtle-colors (format "%s" (or background-color "green")))
             ";border-radius: 15px; font-size: 0.9em"
             "; box-shadow: 0.05em 0.1em 5px 0.01em #00000057;\">"
             "<h3>" ,title "</h3>"
            ,contents "</div>")))))

Let's have some sanity tests…

(deftest "We have an HTML box enclosing the user's title (in <h3) and text"
  [box]
  (⇝ (⟰ "#+begin_box Pay Attention!
                 This is the key insight...
                 #+end_box")
     "<div style=\"padding: 1em; background-color: #CCFFCC;border-radius: 15px;"
     (* anything)
     "<h3>Pay Attention!</h3>"
     (* anything)
     "This is the key insight"
     (* anything)))

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, ei·π=−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 o-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: (o-defblock parallel (cols 2) (bar nil)
 2:   "Place ideas side-by-side, possibly with a separator.
 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.

Let's have some sanity tests…

(deftest "Parallel blocks work as expected"
  [parallel block]
  (⇝ (⟰ "#+begin_parallel 2 :bar yes-or-any-other-text
                  X

                  #+columnbreak:

                  Y

                  Z
                  #+end_parallel")

    ;; The result is 2 columns with a solid rule between them
  ;; and it contains the user's text along with the “#+columnbreak”.

     "<div style=\"column-rule-style: solid;column-count: 2;\">"
     (* anything)
     "X"
     (* anything)
     "<p><br>" ;; “#+columnbreak” above
     (* anything)
     "Y"
     (* anything)
     "Z"
     (* anything)))

Example

#+begin_parallel 2 :bar yes-or-any-other-text
X

#+columnbreak:

Y

Z
#+end_parallel

Yields

( 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

colours.jpg

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 o--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 o--colors
         do (eval `(o-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)))))

Let's have some sanity tests…

(deftest "It is an HTML span styled red that contains the user's text"
  [color red block]
  (⇝ (⟰ "#+begin_red
          My cool thoughts...
          #+end_red")
     "<span style=\"color:red;\">"
     (* anything)
     "My cool thoughts"
     (* anything)
     "</span>"))

;; We have an HTML span styled with the user's color and it contains the user's text
(deftest "It works as expected"
  [color pink block]
  (⇝ (⟰ "#+begin_color pink
          My cool thoughts...
          #+end_color")
     "<span style=\"color:pink;\">"
     (* anything)
     "My cool thoughts"
     (* anything)
     "</span>"))

Implementation of ‘color’

For faster experimentation between colours, we provide a generic color block that consumes a color argument.

(o-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:

(o-defblock color (color black    :face (lambda (colour)
           (if (member (intern colour) o--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.

colour_links.png

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 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

(o-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.

⨁bx=afx=f(a)⊕f(a+1)⊕f(a+2)⊕⋯⊕f(b)

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, or the useful M-s h ..

  • kbd:𝒳 will show a tooltip defining 𝒳, as an Emacs Lisp function, if possible. For example, kbd:C-h_h is C-h h; and likewise <kbd:M-s h .> is M-s h. (we need to use <...> since punctuation is not picked up as part of link labels). In contrast, kbd:nope renders as nope without a tooltip (nor a red border).

    You can also supply explicit tooltip description: [[kbd:key sequence][description]] will show as key sequence; i.e., the key sequence in nice key font along with a tooltip explaining it.

kbd.png

Implementation

(o-deflink kbd
  "Show keysequence O-LABEL in a nice grey button-like font, along with a tooltip of its documentation, if any.

Such links do not get folded in [[bracket]] style, and are rendered as buttons within Emacs.

Moreover, O-LABEL may use ‘_’ in-lieu of spaces or [[bracket]] link notation.

Examples:
    [[kbd:C-x C-s]]
  ≈ <kbd: C-x C-s>
  ≈ kbd:C-x_C-s"
  [:display 'full
   :let (the-label  (s-trim (s-replace "_" " " o-label))
         lisp-func  (ignore-errors (cl-second (help--analyze-key (kbd the-label) the-label)))
         tooltip    (or o-description (ignore-errors (documentation lisp-func)) "")
         tooltip?   (not (equal tooltip ""))
         style      (if tooltip? "border-color: red" "")
         keystrokes (format "<kbd style=\"%s\">%s</kbd>" style the-label))
   ;; o-description is always nil when it comes to deciding the :face.
   :face (list :inherit 'custom-button :box (if tooltip? "red" t))
   :help-echo (format "%s ∷ %s\n%s" the-label (or lisp-func "") tooltip)]
  (if (equal o-backend 'latex)
      (format "\\texttt{%s}" the-label)
    (if tooltip?
        ;; The style=⋯ is to remove the underlying caused by <abbr>.
        (format "<abbr class=\"tooltip\" style=\"border: none; text-decoration: none;\" title=\"%s ∷ %s<br>%s\">%s</abbr>"
                the-label (or lisp-func "") (o-html-export-preserving-whitespace tooltip)
                keystrokes)
      keystrokes)))

The following styling rule is used to make the keystrokes displayed nicely.

(defvar o--kbd-html-setup nil
  "Has the necessary keyboard styling HTML beeen added?")

(unless o--kbd-html-setup
  (setq o--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>")))

Let's have some sanity tests…

(deftest "It becomes <kbd> tags, but final symbol non-ascii *may* be ignored"
  [kbd direct-org-links]
  (⇝ (⟰ "kbd:C-u_80_-∀") "<p>\n<kbd style=\"\">C-u 80</kbd>_-∀</p>"))

(deftest "[[It]] becomes <kbd> tags"
  [kbd square-org-links]
  (⇝ (⟰ "[[kbd:C-u_80_-]]") "<p>\n<kbd style=\"\">C-u 80 -</kbd></p>"))

(deftest "<It> becomes <kbd> tags, and surrounding space is trimmed"
  [kbd angle-org-links]
  (⇝ (⟰ "<kbd: C-u 80 - >")  "<p>\n<kbd style=\"\">C-u 80 -</kbd></p>"))

(deftest "It has a tooltip documenting the underlying Lisp function, when possible"
  [kbd tooltip]
  (⇝ (⟰ "<kbd: M-s h .>")

     "<abbr class=\"tooltip\""
     (* anything)
     "Highlight each instance of the symbol at point.<br>Uses the
     next face from ‘hi-lock-face-defaults’ without
     prompting,<br>unless you use a prefix argument.<br>Uses
     ‘find-tag-default-as-symbol-regexp’ to retrieve the symbol
     at point.<br><br>This uses Font lock mode if it is enabled;
     otherwise it uses overlays,<br>in which case the
     highlighting will not update as you type. The
     Font<br>Lock mode is considered ''enabled'' in a buffer if
     its ‘major-mode’<br>causes ‘font-lock-specified-p’ to return
     non-nil, which means<br>the major mode specifies support for
     Font Lock."
     (* anything)
     "<kbd style=\"border-color: red\">M-s h .</kbd></abbr>"))

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:𝒳 and link-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.
  • Besides the HTML backend, such links are silently omitted.
Six OctoIcons and Implementation

The following SVGs are obtained from: https://primer.style/octicons/

(defvar
 o--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 o--supported-octoicons))")
(o-deflink octoicon
  "Show an OctoIcon: home, link, mail, report, tag, clock"
  [:help-echo "Show an OctoIcon: home, link, mail, report, tag, clock"]
  (unless (member (intern o-label) '(home link mail report tag clock))
    (error "octoicon:%s ⇒ This label is not supported!" o-label))
  (if (not (equal o-backend 'html))
      ""
    (s-collapse-whitespace
     (cadr (assoc (intern o-label)
                  o--supported-octoicons)))))

(o-deflink link-here
  "Export a link to the current location in an Org file."
  [:help-echo (format "This is a local anchor link named “%s”" path)]
  (if (not (equal o-backend 'html))
      ""
    (format (s-collapse-whitespace
     "<a class=\"anchor\" aria-hidden=\"true\" id=\"%s\"
          href=\"#%s\">%s</a>")
    o-label o-label (cadr (assoc 'link
                             o--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!)

Tests

(deftest "It works as expected: We have an anchor with the given ID, and the default SVG chain icon."
  [link:here]
  (⇝ (⟰ "link-here:example-location (Click the icon and see the URL has changed!)")
     "<a class=\"anchor\" aria-hidden=\"true\" id=\"example-location\" href=\"#example-location\"><svg"
     (* anything)
     "</svg></a> (Click the icon and see the URL has changed!)"
     (* anything)))

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.

badges.png

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

Result

What are badges?

Let_me_%2Anot%2A_google_that-for_you-nil?logo=nil

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.

Automated Tests

Let's have some sanity tests…

(deftest "It works when all 5 arguments are provided"
  [badge]
  (⇝ (⟰ "badge:Let_me_google_that|for_you!|orange|https://lmgtfy.app/?q=badge+shields.io&iie=1|Elixir")
     "<a href=\"https://lmgtfy.app/?q=badge+shields.io&iie=1\">"
     "<img src=\"https://img.shields.io/badge/Let_me_google_that-for_you%21-orange?logo=Elixir\"></a>"))

(deftest "It works when we use [[link]] syntax with generous spaces and newlines"
  [badge]
  (⇝ (⟰ "[[badge: Let me google that | for you! | orange |
         https://lmgtfy.app/?q=badge+shields.io&iie=1|Elixir]]")

     "<a href=\"https://lmgtfy.app/?q=badge+shields.io&iie=1\">"
     (* anything)
     "<img src=\"https://img.shields.io/badge/Let%20me%20google%20that-for%20you%21-orange?logo=Elixir\">"))

(deftest "It works when only the first 2 arguments are provided; asterisks are passed unaltered into the first argument"
  [badge]
  (⇝ (⟰ "badge:Let_me_*not*_google_that|for_you")
     "<img src=\"https://img.shields.io/badge/Let_me_%2Anot%2A_google_that-for_you-nil?logo=nil\">"))

(deftest "It works when all 5 arguments are provided - URL ‘here’ makes it a local link"
  [badge]
  (⇝ (⟰ "badge:key|value|informational|here|Elixir")
     "<a id=\"key\" href=\"#key\">"
     "<img src=\"https://img.shields.io/badge/key-value-informational?logo=Elixir\"></a>"))

(deftest "We can use spaces, commas, dashes, and percentage symbols in the first argument"
  [badge]
  (⇝ (⟰ "badge:example_with_spaces,_-,_and_%|points_right_here|orange|here")
     "<a id=\"example_with_spaces,_-,_and_%\" href=\"#example_with_spaces,_-,_and_%\">"
     "<img src=\"https://img.shields.io/badge/example_with_spaces%2C_--%2C_and_%25-points_right_here-orange?logo=nil\"></a>"))

(deftest "It works when only first 2 arguments are given: Default colour & logo are green & no logo shown"
  [badge]
  (⇝ (⟰ "badge:key|value")
     "<img src=\"https://img.shields.io/badge/key-value-nil?logo=nil\">"))

(deftest "When only a key is provided, the value slot is shown as an empty green stub"
  [badge]
  (⇝ (⟰ "badge:key")
     "<img src=\"https://img.shields.io/badge/key--nil?logo=nil\">"))

(deftest "When only a value is provided, only the value is shown in a default green ---no stub for the missing key, yay"
  [badge]
  (⇝ (⟰ "badge:|value")
        "<img src=\"https://img.shields.io/badge/-value-nil?logo=nil\">"))


(deftest "It's only a green stub when provided with an empty key and empty value"
  [badge]
  (⇝ (⟰ "badge:||green")
     "<img src=\"https://img.shields.io/badge/--green?logo=nil\">"))

(deftest "It's only a green stub when we use a totally [[badge:]]"
  [badge]
  (⇝ (⟰ "[[badge:]]")
     "<img src=\"https://img.shields.io/badge/--nil?logo=nil\">"))

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 ;-)
  • badge:example_with_spaces,_-,_and_%|points_right_here|orange|here
  • no_colour-given-nil?logo=nilbadge:key|value.
  • empty_value--informational?logo=nilbadge:empty_value||informational
  • -value-nil?logo=nilbadge:|value
  • --green?logo=nilbadge:||green ( No key; nor value! )

    Also: --nil?logo=nil[[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 o-make-badge.

o-make-badge

The implementation is a bit lengthy since it attempts to capture a useful portion of the shilelds.io badge interface.

(cl-defmacro o-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”:

     (o-make-badge \"go\")

Then the following exports nicely from an Org file:

     go:key|value|blue|here|gnu-emacs"
  `(o-deflink ,(intern name)
    "Export a Shields.io badge, with general Syntax:  badge:key|value|colour|url|logo.
Precise details for each argument are shown in the Emacs tooltip for this badge."
    [:display 'full
     :follow (--> (s-split "|" path)
               (or (nth 3 it) o-label)
               (browse-url it))
    :help-echo
      (-let [ (key value color url logo)  (mapcar #'s-trim (s-split "|" o-label)) ]
        (lf-string "${o-label}
                    \nGeneral Syntax:  badge:key|value|colour|url|logo
                           Key    : ${key}
                           Value  : ${(or value \"\")}
                           Colour : ${(or color \"green\")}
                           URL    : ${(or url \"\")}
                           Logo   : ${(or logo \"\")}

                    This results in an SVG badge “[key∣value]”, where the ‘key’
                    is coloured grey and the ‘value’ is coloured ‘color’;
                    for the HTML backend, and otherwise are silently omitted.
                    Descriptions are ignored; i.e., ‘[[badge:label][description]]’
                    is the same as ‘[[badge:label]]’.

                    ► ‘key’ and ‘value’ have their underscores interpreted as spaces.
                       ⇒ ‘__’ is interpreted as an underscore;
                         Of course, you can write ‘<badge: ⋯>’, then ‘⋯’ may have multiword, spaced, content.
                       ⇒ ‘|’ is not a valid substring, but ‘-, %, ?’ are okay.

                    ► ‘|color|url|logo’ are optional;
                       ⇒ If ‘url’ is not present, the resulting badge is not a hyperlink.
                       ⇒ if ‘url’ is ‘here’ then we have a local link;
                          i.e., 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.
                         Consult https://htmlcolorcodes.com/ to see the HEX codes of other colours.
                       ⇒ ‘logo’ examples and how they look can be found at
                          https://alhassy.github.io/org-special-block-extras/#Example-Badge-Icons

                   See also: https://alhassy.github.io/org-special-block-extras/#Common-Project-Badges"))]

    ;; :export #'o--link--badge
    (if (equal o-backend 'latex) ""
      (-let [ (key value color url logo)  (mapcar #'s-trim (s-split "|" o-label)) ]
        (format
         (pcase ,(if social-shields-name `(format ,social-url o-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 o-label)
                                    'url)))))
         ,(if social-shields-name
              (if social-shields-url
                  `(format ,social-shields-url o-label)
                `(format "<img src=\"https://img.shields.io/%s/%s?style=social\">"
                         ,social-shields-name o-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)))))))

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

(o-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 o-link-twitter-excitement
  "This looks super neat (•̀ᴗ•́)و:"
  "The string prefixing the URL being shared.")

(o-make-badge
 "tweet"
 "twitter/url?=url="
 (format
   "https://twitter.com/intent/tweet?text=%s:&url=%%s"
   o-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 `(o-make-badge ,name′ ,social ,url)))

Here are some examples.

  • Things I like:

  • Info about my cheatsheets:

  • My profile:

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

  • -red-red?logo=nil-critical-critical?logo=nil
  • -blue-blue?logo=nil-informational-informational?logo=nil
  • -brightgreen-brightgreen?logo=nil-success-success?logo=nil
  • -orange-orange?logo=nil-important-important?logo=nil
  • -lightgrey-lightgrey?logo=nil-inactive-inactive?logo=nil
  • -green-green?logo=nil
  • -yellowgreen-yellowgreen?logo=nil
  • -yellow-yellow?logo=nil
  • -blueviolet-blueviolet?logo=nil
  • -ff69b4-ff69b4?logo=nil
  • -9cf-9cf?logo=nil

Consult https://htmlcolorcodes.com/ to see the HEX code of any other colour you wish to use; e.g., -1d8348-1d8348?logo=nil

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”
  • --grey?logo=Elixir Elixir
  • --grey?logo=tinder tinder
  • --grey?logo=codeigniter codeigniter
  • --grey?logo=prometheus prometheus
  • --grey?logo=sparkpost sparkpost

“Messaging”
  • --grey?logo=quip quip
  • --grey?logo=WeChat WeChat
  • --grey?logo=google-hangouts google-hangouts
  • --grey?logo=hackhands hackhands
  • --grey?logo=google-messages google-messages
  • --grey?logo=Slack Slack

“Emacs”
  • --grey?logo=gnu-emacs gnu-emacs
  • --grey?logo=spacemacs spacemacs
  • --grey?logo=vim vim
  • --grey?logo=neovim neovim
  • --grey?logo=gnu gnu
  • --grey?logo=github github
  • --grey?logo=acm acm
  • --grey?logo=wikipedia wikipedia
  • --grey?logo=microsoft-excel microsoft-excel
  • --grey?logo=microsoft-word microsoft-word
  • --grey?logo=dropbox dropbox
  • --grey?logo=google-scholar google-scholar
  • --grey?logo=google google
  • --grey?logo=google-translate google-translate
  • --grey?logo=ghost ghost
  • --grey?logo=helm helm
  • --grey?logo=apache-openoffice apache-openoffice
  • --grey?logo=buffer buffer
  • --grey?logo=adobe-fonts adobe-fonts
  • --grey?logo=google-calendar google-calendar

“Social”
  • --grey?logo=google-cast google-cast
  • --grey?logo=youtube youtube
  • --grey?logo=discord discord
  • --grey?logo=facebook facebook
  • --grey?logo=google-hangouts google-hangouts
  • --grey?logo=whatsapp whatsapp
  • --grey?logo=skype skype
  • --grey?logo=reddit reddit
  • --grey?logo=stack-overflow stack-overflow
  • --grey?logo=stack-exchange stack-exchange
  • --grey?logo=linkedin linkedin
  • --grey?logo=twitter twitter
  • --grey?logo=jabber jabber

“Lightbulb”
  • --grey?logo=lighthouse lighthouse
  • --grey?logo=google-keep google-keep
  • --grey?logo=minds minds

“Programming”
  • --grey?logo=git git
  • --grey?logo=ruby ruby
  • --grey?logo=scala scala
  • --grey?logo=OCaml OCaml
  • --grey?logo=javascript javascript
  • --grey?logo=gnu-bash gnu-bash
  • --grey?logo=powershell powershell
  • --grey?logo=LaTeX LaTeX
  • --grey?logo=java java
  • --grey?logo=kotlin kotlin
  • --grey?logo=haskell haskell
  • --grey?logo=coffeescript coffeescript
  • --grey?logo=purescript purescript
  • --grey?logo=rust rust
  • --grey?logo=typescript typescript
  • --grey?logo=css3 css3
  • --grey?logo=python python
  • --grey?logo=c c
  • --grey?logo=clojure clojure
  • --grey?logo=lua lua
  • --grey?logo=adobe-acrobat-reader adobe-acrobat-reader
  • --grey?logo=perl perl

“Miscellaneous”
  • --grey?logo=read-the-docs read-the-docs
  • --grey?logo=buy-me-a-coffee buy-me-a-coffee
  • --grey?logo=gimp gimp
  • --grey?logo=mega mega
  • --grey?logo=nintendo-3ds nintendo-3ds
  • --grey?logo=paypal paypal
  • --grey?logo=pinboard pinboard
  • --grey?logo=mocha mocha
  • --grey?logo=Gitea Gitea
  • --grey?logo=instacart instacart
  • --grey?logo=openStreetMap openStreetMap
  • --grey?logo=amazon amazon
  • --grey?logo=svg svg
  • --grey?logo=rss rss
  • --grey?logo=swagger swagger
  • --grey?logo=pastebin pastebin
  • --grey?logo=skyliner skyliner
  • --grey?logo=iTunes iTunes
  • --grey?logo=gulp gulp
  • --grey?logo=leaflet leaflet
  • --grey?logo=youtube-gaming youtube-gaming
  • --grey?logo=GIMP GIMP
  • --grey?logo=atom atom

8.4 Common Project Badges —with copyable source

  • srcbadge:Emacs|23/26/28|green|https://www.gnu.org/software/emacs|gnu-emacs
  • srcbadge: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]] srcbadge:wiki|github|informational|here|wikipedia
  • srcbadge:code_coverage|88%|green|here|codecov srcbadge:build|passing|success|here|azure-pipelines
  • srcbadge:author|musa_al-hassy|purple|https://alhassy.github.io/|nintendo-3ds
  • srcbadge: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]]
  • srcbadge:issue_tracking|github|informational|here|github
  • srcbadge:help_forum|discourse|informational|here|discourse
  • srcbadge:social_chat|gitter|informational|https://gitter.im/explore|gitter
  • Maintained%3F-yes-success?logo=nilsrcbadge:Maintained?|yes|success Maintained%3F-no-critical?logo=nilsrcbadge:Maintained?|no|critical srcbadge:No_Maintenance_Intended|×|critical|http://unmaintained.tech/
  • website-up-success?logo=nilsrcbadge:website|up|success website-down-critical?logo=nilsrcbadge:website|down|critical
  • Ask_me-anything-1abc9c?logo=nilsrcbadge:Ask_me|anything|1abc9c srcbadge:contributions|welcome|green|https://github.com/alhassy/org-special-block-extras/issues
  • Made_with-Python%2C_LaTeX%2C_MathJax%2C_and_Emacs_Org--mode-1f425?logo=nilsrcbadge: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

tooltips_browser.png

Figure 8: Tooltips for documentation and glossary items –in the browser!

tooltips_emacs.png

Figure 9: Tooltips for documentation and glossary items –in Emacs!

tooltips_pdf.png

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 o--documentation.

Entry names do not need to be unique, but labels do! ( The labels are like the addresses used to look up a person's home. )

‼TODO More Examples

Supported Example Source
No description Category Theory doc:cat
With description and code font polymorphism [[doc:natural-transformation][=polymorphism=]]
Fallback: Arbitrary ELisp Docs (function) thread-first doc:thread-first
Fallback: Arbitrary ELisp Docs (variable) user-mail-address doc:user-mail-address
Fallback: Arbitrary English word user doc:user

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 ;-)

‼ TODO

In the export, it looks like some names are repeated, but in the source one would notice that we have labels in both upper and lower cases and with underscores ;-) They just happen to be referring to the same named entry.


My personal documentation library can be seen here:

or .

Tests

(deftest "It gives a tooltip whose title is the Lisp docs of APPLY"
  [doc]
  (⇝ (⟰ "doc:apply")
     "<abbr class=\"tooltip\" title=\""
     (literal (s-replace "\n" "<br>" (documentation #'apply)))
     "\">apply</abbr>"))

(setq angst
      (⟰ "#+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

                       We now use this as doc:ex-angst."))

(deftest "Documentation blocks are not exported; they produce a new osbe--docs entry"
  [doc documentation o--name&doc]
    (should (o-docs-get "ex-angst")))

;; Upon export, the #+begin_documentation is /not/ present.
;; We have the text outside that block only.
;; Along, with a htmlified tooltip of the new entry.
(deftest "The osbe--docs entry of the documentation block appears within a tooltip"
    [doc documentation o--name&doc]
    (⇝ angst " <p> We now use this as <abbr class=\"tooltip\" title=\""
             (literal (o-html-export-preserving-whitespace
                       (cl-second (o-docs-get "ex-angst"))))
                    "\">Existential Angst</abbr>.</p> "))

9.1 Implementation Details: doc link, documentation block, and tooltipster

We begin by making use of a list of documentation-glossary entries —a lightweight database of information, if you will.

Overview of relevant Lisp

  • o--docs is the global variable that holds the glossary/database for the current session. Entries may be inserted with o-docs-set and retrieved with o-docs-get.
  • o-docs-fallback is a fallback method for retriving documentation-glossary entries from elsewhere besides the current buffer; e.g., using Emacs' documentation function or the wn WordNet command for definitions of English words. We use o-docs-load-libraries to load documentation-glossary entries from external files.
  • o-docs-insert for interactively inserting links for documented-glossary words.
(defvar o--docs nil
  "An alist of (LABEL NAME DESCRIPTION) entries; our glossary.

Example setter:
0. (o-docs-set \"os\" \"Emacs\" \"A place wherein I do all of my computing.\")

Example getters:
0. (o-docs-get LABEL)
1. (-let [(name description) (cdr (assoc LABEL o--docs))] ⋯)

See also `o--docs-from-libraries' and `o-docs-load-libraries'.")

(cl-defun o-docs-set (label name description)
  "Add a new documentation-glossary entry, if it is not already present.

We associate LABEL to have title NAME and glossary value DESCRIPTION.

Example usage:
  (o-docs-set \"cat\"
              \"Category Theory\"
              \"A theory of typed  composition; e.g., typed monoids.\")"
  (add-to-list 'o--docs (list label name description)))

(cl-defun o-docs-get (label)
  "Return the name and documentation-glossary values associated with LABEL.

It returns a list of length 2.

Example uses:

  ;; Get the Lisp documentation of `thread-last'
  (o-docs-get \"thread-last\")

  ;; Get the English definition of ‘computing’
  (o-docs-get \"computing\")

We look for LABEL from within the current buffer first, using `o--docs',
and otherwise look among the loaded libraries, using `o--docs-from-libraries',
and, finally, look for the documentation entry using `o-docs-fallback'."
  (cdr (or (assoc label o--docs)
           (assoc label o--docs-from-libraries)
           (funcall o-docs-fallback label)
           (error "Error: No documentation-glossary entry for “%s”!" label))))

(cl-defun o-docs-insert ()
  "Insert a “doc:𝒳” link from user's documentation-glossary database.

It can be tricky to remember what you have, or what documentation entries mention, and so
this command gives a searchable way to insert doc links."
  (interactive)
  (thread-last
      (cl-remove-duplicates (-concat o--docs o--docs-from-libraries)
                            :test (lambda (x y) (cl-equalp (car x) (car y))))
    (--map (format "%s ∷ %s" (car it) (cl-third it)))
    (completing-read "Insert doc link ∷ ")
    (s-split "∷")
    car
    (concat "doc:")
    (insert)))

We may wish to use Emacs' documentation (or lf-documentation) command to retrieve entries —this is useful for an online article that refers to unfamiliar Emacs terms ;-) To avoid copy-pasting documentation entries from one location to another, users may declare a fallback method. Besides Emacs' documentation, the fallback can be refer to a user's personal ‘global glossary’ variable —which may live in their Emacs' init file—, for more see: o-docs-load-libraries

(defvar o-docs-fallback
  (lambda (label) (list
              ;; label
              label
              ;; name
              label
              ;; documentation
              (or
               (ignore-errors (documentation (intern label)))
               (ignore-errors (documentation-property
                               (intern label)
                               'variable-documentation))
               (-let [it (shell-command-to-string
                          (format "wn %s -over -synsn" label))]
                 (if (s-blank-p it)
                     (error "Error: No documentation-glossary entry for “%s”!" label)
                   it)))))

  "The fallback method to retriving documentation or glossary entries.

We try to retrive the Emacs Lisp function documentation of the
given LABEL, if possible, otherwise we try to retrive the Emacs
Lisp variable documentation, and if that fails then we look up
the word in the English dictionary.

The English definition is obtained from the command line tool ‘wn’, WordNet.")

Implementation matter for user libraries

(defvar o-docs-libraries nil
  "List of Org files that have ‘#+begin_documentation’ blocks that should be loaded
   for use with the ‘doc:𝒳’ link type.")

In your init, you could do the following. ( See the installation instructions below, for more! )

(setq o-docs-libraries
  '("~/org-special-block-extras/documentation.org"))

My personal documentation library can be seen here:

or .

(cl-defun o-docs-load-libraries
    (&optional (libs o-docs-libraries))
"Load documentation-glossary libraries LIBS.

If no LIBS are provided, simply use those declared
o-docs-libraries.

See `o-docs-from-libraries'."
(interactive)
(cl-loop for lib in libs
      do (with-temp-buffer
           (insert-file-contents lib)
           ;; doc only activates after an export
           (-let [org-export-with-broken-links t] (org-html-export-as-html))
           (kill-buffer)
           (delete-window)
           (setq o--docs-from-libraries (-concat o--docs o--docs-from-libraries))
           (setq o--docs nil))))

(defvar o--docs-from-libraries nil

  "The alist of (label name description) entries loaded from the libraries.

The ‘doc:𝒳’ link will load the libraries, possibly setting this variable to ‘nil’,
then make use of this variable when looking for documentation strings.

Interactively call `o-docs-load-libraries'
to force your documentation libraries to be reloaded.

See also `o-docs-libraries'.")

When the mode is actually enabled, then we load the user's libraries.

;; Ensure user's documentation libraries have loaded
(unless o--docs-from-libraries
  (o-docs-load-libraries))

Let's keep track of where documentation comes from —either the current article or from the fallback— so that we may process it later on.

(defvar o--docs-actually-used nil
  "Which words are actually cited in the current article.

We use this listing to actually print a glossary using
‘show:GLOSSARY’.")

Now HTML exporting such links as tooltips and displaying them in Emacs as tooltips happens in two stages: First we check the documentation, if there is no entry, we try the fallback —if that falls, an error is reported at export time. E.g., upon export doc:wombo will produce a no-entry error.

‘doc’ link implementation

(o-deflink doc
 "Export O-LABEL as itself, or as the provided O-DESCRIPTION,
 along with a tooltip that shows the user's
 documentation-glossary for o-LABEL and using that entry's name
 when no O-DESCRIPTION is provided."
 [:let (entry (o-docs-get o-label)
        name (cl-first entry)
        docs (cl-second entry)
        display-name (or o-description name))
  :help-echo (format "[%s] %s :: %s" o-label name docs)
  :face '(custom-button)]
   (add-to-list 'o--docs-actually-used (list o-label name docs))
   (pcase o-backend
     (`html  (format "<abbr class=\"tooltip\" title=\"%s\">%s</abbr>"
                     (o-html-export-preserving-whitespace docs)
                     display-name))
     ;; Make the current word refer to its glosary entry;
     ;; also declare the location that the glossary should refer back to.
     (`latex (format (concat "\\hyperref"
                             "[o-glossary-%s]{%s}"
                             "\\label{o-glossary"
                             "-declaration-site-%s}")
                     label display-name label))))

;; WHERE ...

(defun o-html-export-preserving-whitespace (s)
  "Make Org-markup'd string S ready for HTML presentation, preserving whitespace.

This is orthogonal to the  `org-export-string-as' command; i.e.,
(org-export-string-as s 'html :body-only-please) does something
else!  In particular, what this yields is an HTML rendition that
does not account for whitespace, such as indentation and
newlines.
"
  ;; Make it look pretty!
  (thread-last s
    (s-replace-regexp "\\#\\+begin_src [^\n]*\n" "<pre class='tooltip'>")
    (s-replace-regexp "\\( \\)*\\#\\+end_src\n" "</pre>")
    (s-replace-regexp "\\#\\+begin_export [^\n]*\n" "")
    (s-replace-regexp "\\( \\)*\\#\\+end_export" "")
    (s-replace "  " " ") ; Preserve newlines
    (s-replace "\n" "<br>")   ; Preserve whitespace
    (s-replace-regexp "\\#\\+begin_example<br>" "")
    (s-replace-regexp "\\#\\+end_example<br>" "")
    ; (s-replace-regexp "\\#\\+begin_src \\(.\\)*\\#\\+end_src)" "<pre>\1</pre>")
    ; (s-replace-regexp "\\#\\+begin_src [^<]*<br>" "<pre>")
    ; (s-replace-regexp "<br>\\( \\)*\\#\\+end_src<br>" "<br>\1</pre>")
    ;; Translate Org markup
    ;; Only replace /.*/ by <em>.*<em> when it does not have an alphanum,:,/,< before it.
    (s-replace-regexp "\\([^a-z0-9A-Z:/<]\\)/\\(.+?\\)/" "\\1<em>\\2</em>")
    (s-replace-regexp "\\*\\(.+?\\)\\*" "<strong>\\1</strong>")
    (s-replace-regexp "\\~\\([^ ].*?\\)\\~" "<code>\\1</code>")
    ;; No, sometimes we want equalities.
    ;; (s-replace-regexp "=\\([^ \n].*?\\)=" "<code>\\1</code>")
    (s-replace-regexp "\\$\\(.+?\\)\\$" "<em>\\1</em>")
    (s-replace-regexp "\\[\\[\\(.*\\)\\]\\[\\(.*\\)\\]\\]" "\\2 (\\1)")
    ;; 5+ dashes result in a horizontal line
    (s-replace-regexp "-----+" "<hr>")
    ;; Spacing in math mode
    (s-replace-regexp "\\\\quad" " ")
    (s-replace-regexp "\\\\," " ") ;; en space
    (s-replace-regexp "\\\\;" " ") ;; em space
    ;; The presence of ‘\"’ in tooltips breaks things, so omit them.
    (s-replace-regexp "\\\"" "''")))

Since tooltips are not clickable and do not support MathJax, we have manually transpiled what are perhaps the most common instances of Org-markup: Bold, emphasises, code/verbatim, math, and links.

Things look great at the HTML side and on the Emacs side for consuming documented text. Besides being inconvenient, we cannot with good conscious force the average user to write Lisp as we did for the doc:cat entry. We turn to the problem of producing documentation entries with a block type interface…

‘documentation’ Implementation: An Example of Mandatory Arguments and Using ‘raw-contents’

(o-defblock documentation
  (name (error "Documentation block: Name must be provided"))
  (label nil show nil color "green")
  "Register the dictionary entries in CONTENTS to the dictionary variable.

The dictionary variable is ‘o--docs’.

A documentation entry may have its LABEL, its primary identifier,
be:
1. Omitted
2. Given as a single symbol
3. Given as a many aliases '(lbl₀ lbl₁ … lblₙ)

The third case is for when there is no canonical label to refer to
an entry, or it is convenient to use multiple labels for the same
entry.

In all of the above cases, two additional labels are included:
The entry name with spaces replaced by underscores, and again but
all lower case.

Documentation blocks are not shown upon export;
unless SHOW is non-nil, in which case they are shown
using the ‘box’ block, with the provided COLOR passed to it.

In the futture, it may be nice to have an option to render tooltips.
That'd require the ‘doc:𝒳’ link construction be refactored via a ‘defun’."
  (unless (consp label) (setq label (list label)))
  (push (s-replace " " "_" name) label)
  (push (downcase (s-replace " " "_" name)) label)
  (cl-loop for l in label
        do  (add-to-list 'o--docs
                         (mapcar #'s-trim (list (format "%s" l) name (substring-no-properties raw-contents)))))
  ;; Should the special block show something upon export?
  (if show (o--blockcall box name :background-color color raw-contents) ""))

Thus far, Org entities are converted into HTML tags such as <i> for italicised text. However, HTML's default tooltip utility —using title="⋯" in a div— does not render arbitrary HTML elements. Moreover, the default tooltip utility is rather slow. As such, we move to using tooltipster. The incantation below sets up the required magic to make this happen.

Fast, Sleek, and Beautiful Tooltips: Touching ‘org-html-head-extra’

(defvar o--tooltip-html-setup nil
  "Has the necessary HTML beeen added?")

(unless o--tooltip-html-setup
  (setq o--tooltip-html-setup t)
(setq org-html-head-extra
 (concat org-html-head-extra
"
<link rel=\"stylesheet\" type=\"text/css\" href=\"https://alhassy.github.io/org-special-block-extras/tooltipster/dist/css/tooltipster.bundle.min.css\"/>

<link rel=\"stylesheet\" type=\"text/css\" href=\"https://alhassy.github.io/org-special-block-extras/tooltipster/dist/css/plugins/tooltipster/sideTip/themes/tooltipster-sideTip-punk.min.css\" />

<script type=\"text/javascript\">
    if (typeof jQuery == 'undefined') {
        document.write(unescape('%3Cscript src=\"https://code.jquery.com/jquery-1.10.0.min.js\"%3E%3C/script%3E'));
    }
</script>

 <script type=\"text/javascript\"            src=\"https://alhassy.github.io/org-special-block-extras/tooltipster/dist/js/tooltipster.bundle.min.js\"></script>

  <script>
         $(document).ready(function() {
             $('.tooltip').tooltipster({
                 theme: 'tooltipster-punk',
                 contentAsHTML: true,
                 animation: 'grow',
                 delay: [100,500],
                 // trigger: 'click'
                 trigger: 'custom',
                 triggerOpen: {
                     mouseenter: true
                 },
                 triggerClose: {
                     originClick: true,
                     scroll: true
                 }
 });
         });
     </script>

<style>
   abbr {color: red;}

   .tooltip { border-bottom: 1px dotted #000;
              color:red;
              text-decoration: none;}
</style>
")))

Note that we have a conditional in the middle to avoid loading jQuery multiple times —e.g., when one uses the read-the-org them in combination with this library. Multiple imports lead to broken features.

9.2 Wait, what about the LaTeX?

A PDF is essentially a fancy piece of paper, so tooltips will take on the form of glossary entries: Using doc:𝒳 will result in the word 𝒳 being printed as a hyperlink to a glossary entry, which you the user will eventually declare using show:GLOSSARY; moreover, the glossary entry will also have a link back to where the doc:𝒳 was declared. E.g., defmacro and lambda.

We make a show:𝒳 link type to print the value of the variable 𝒳 as follows, with GLOSSARY being a reserved name.

Implementation of ‘show’

(o-deflink show
  "Yield the value of the expression O-LABEL, with =GLOSSARY= being a reserved name.

Example uses:

    show:user-full-name

    <show: (* 2 (+ 3 4 (- pi))) >


Note that there is `elisp' links with Emacs, out of the box.
However, they only serve to evaluate Lisp expressions; for example,
to make “link buttons” that do useful things, as follows.

   [[elisp:(find-file user-init-file)][Init]]

In particular, `elisp' links do not export the value of their expression.
That is what we accomplish with this new `show' link type."
  [:face '(:underline "green")
   :let (o-value (if (equal o-label "GLOSSARY")
                     (pp-to-string (mapcar #'cl-second o--docs-actually-used))
                   (pp-to-string (eval (car (read-from-string o-label)))))
         o-expr (if (equal o-label "GLOSSARY")
                    (concat "GLOSSARY ---i.e., o--docs-actually-used"
                            "\n\nWe erase the glossary not on the first export, but on the second export."
                            "\nThe first export collects all citations, which are used in the second export.")
                  o-label))
  :help-echo (format
              (concat "Upon export, the following will be placed literally"
                      "\n\t%s"
                      "\nWhich is the value of the expression:\n\t%s")
              o-value
              o-expr)]
  (cond ((not (equal o-label "GLOSSARY")) o-value)

       ;; Otherwise O-LABEL is glossary, which we print in HTML & LaTeX
       ((equal 'html o-backend)
          (s-join " "
                  (--map
                   (format "<abbr class=\"tooltip\" title=\"%s\">%s</abbr>"
                           (o-html-export-preserving-whitespace (cl-third it))
                           (cl-second it))
                   ;; Ignore duplicates; i.e., entries with the same name/title.
                   (cl-remove-duplicates o--docs-actually-used
                                         :test (lambda (x y) (cl-equalp (cl-second x) (cl-second y)))))))
       (t (s-join "\n\n"
                  (cl-loop for (label name doc)
                           in o--docs-actually-used
                           collect
                           (format
                           (concat "\\vspace{1em}\\phantomsection"
                                   "\\textbf{%s}\\quad"
                                   "\\label{o-glossary-%s}"
                                   "%s See page "
                                   "\\pageref{org-special-block-extras"
                                   "-glossary-declaration-site-%s}")
                           name
                           o-label
                           (when doc
                             (thread-last doc ;; preserve whitespace
                               (s-replace "&" "\\&") ;; Hack!
                               (s-replace "  " " \\quad ")
                               (s-replace "\n" " \\newline{\\color{white}.}")))
                           o-label))))))

As an example, we know have generic sentences:

My name is show:user-full-name and I am using Emacs show:emacs-version ^_^
My name is "Musa Al-hassy" and I am using Emacs "27.2" ^_^

For example, here is a word whose documentation is obtained from Emacs rather than me writing it: thread-last. If you click on it, in the LaTeX output, you will be directed to the glossary at the end of this article —glossaries are not printed in HTML rendering.

Neato! The whitespace in the documentation is preserved in the LaTeX output as is the case for HTML.

If we decide to erase a doc:𝒳, then it should not longer appear in the printed glossary listing. Likewise, a documentation block has its Org markup exported according to the backend being exported to, hence if we decide to switch between different backends then only the first rendition will be used unless we erase the database each time after export.

(defvar o--docs-empty! (list nil t)
  "An indicator of when glossary entries should be erased.

We erase the glossary not on the first export, but on the second export.
The first export collects all citations, which are used in the second export.")
(setcdr (last o--docs-empty!) o--docs-empty!) ;; It's an infinite cyclic list.

;; Actual used glossary entries depends on the buffer; so clean up after each export
(advice-add #'org-export-dispatch
  :after (lambda (&rest _)
  (when (pop o--docs-empty!)
      (setq o--docs-actually-used nil ;; The 𝒳 of each “doc:𝒳” that appears in the current buffer.
            o--docs nil))))           ;; The “#+begin_documentation ⋯ :label 𝒳” of the current buffer.

9.3 Next Steps

Going forward, it'd be nice to have URLs work well upon export for documentation block types; whereas they currently break the HTML export.

  • If an entry is referenced multiple times, such as Category Theory, then it would be nice if the glossary referred to the pages of all such locations rather than just the final one.
  • The glossary current prints in order of appearance; we may want to have the option to print it in a sorted fashion.
  • Perhaps use the line activation feature to provide link tooltips immediately rather than rely on exportation.
  • The show link type could accept an arbitrary Lisp expression as a bracketed link. It does ;-)
  • When one clicks on a doc documentation link, it would be nice to ‘jump’ to its associated #+begin_documentation declaration block in the current buffer, if possible.

10 Marginal, “one-off”, remarks

(Scroll to make tooltips disappear!)

The use of doc:𝒳 — and documentation — are for explanatory remarks, personal documentation, or glossary, that is intended to be used in multiple places. However, sometimes we want a “one-off” remark containing, say, references or additional explanation; for that we provide margin. For example:

Allah°  does not burden a soul beyond what it can bear. — Quran 2:286


Source:

/Allah[[margin:][The God of Abraham; known as Elohim in the Bible]] does not
burden a soul beyond what it can bear./ --- Quran 2:286

In HTML, such “marginal remarks” appear as tooltips; in LaTeX, they appear in the margin.

Tests

(setq margin (⟰ "/Allah[[margin:][The God of Abraham; known as Elohim
               in the Bible]] does not burden a soul beyond what it can bear./
               --- Quran 2:286"))

(deftest "It exports into an <abbr> tooltip"
  [margin]
  (⇝ margin "<abbr class=\"tooltip\""))

(deftest "It mentions the margin link's key before the tooltip"
  [margin]
  (⇝ margin "Allah<abbr class=\"tooltip\""))

(deftest "Our personalised marginal remark appears in an <abbr> title"
  [margin]
  (⇝ margin "title=\"The God of Abraham; known as Elohim"
            "<br>        in the Bible\">"))

(deftest "The marginal remark appears in a tiny circle"
  [margin]
  (⇝ margin "<abbr" (* anything) "°</abbr>"))

Implementation
(o-defblock margin
  (marker nil
          :display 'full
          :face '(:foreground "grey" :weight bold
          :underline "orange" :overline "orange"))
  (color "gray!80"
          counter "footnote"
          width "\\paperwidth - \\textwidth - \\oddsidemargin - 1in - 3ex")
          ;; Width: https://tex.stackexchange.com/a/101861/69371
  :please-inline__no-extra-newlines__k-thx-bye!
  "Produce an HTML tooltip or a LaTeX margin note.

The ‘margin’ block is intended for “one-off” (mostly optional) remarks.

For notes that you want to use repeatedly, in multiple articles
or in multiple locations in the same article, consider using
‘documentation’ to declare them and ‘doc’ to invoke them.

For LaTeX, place ‘#+resize:’ to have the remainder of a block be
resized, for now 1.3 the margin width ---requires \\usepackage{adjustbox}.

----------------------------------------------------------------------

WIDTH, COUNTER, and COLOR are LaTeX specfic.

When no label, marker, is used for a marginal note, we rely
on a COUNTER, such as ‘footnote’ (default) or ‘sidenote.’
Since HTML has no margin per se, we use “∘” as default marker:
Users hover over it to read the marginal note.

Marginal notes have their labels, markers, in black
and the notes themselves have COLOR being grey!80.
In Emacs, margin links appear grey with an orange tinted boarder.

Regarding LaTeX, since verbatim environments do not in general work well
as arguments to other commands, such as ‘\\marginpar’, we save the contents
of the special block in a ‘minipage’ within a LaTeX ‘box’; then we can unfold such
a box in the margin. Hence, ‘src’ blocks can appear within ‘margin’ blocks (•̀ᴗ•́)و

The WIDTH argument is the width of the margin; i.e., the width of the underlying
minipage.

One could use \\maxsizebox{.25\\textwidth}{\\textheight}{ ... }
which only resizes the content if its natural size is larger than
the given 〈width〉 or 〈height〉.  We don't use this, since
maxsizebox does not natively allow linebreaks
(e.g., one would wrap contents in a tabular environment then use
‘\\\\’, two backslashes, to request a line break; but this
crashes if one wants to also use verbatim environments.)

In LaTeX, it may be useful to invoke ‘\\dotfill’."
  (-let [stepcounter (if marker "" (format "\\stepcounter{%s}" counter))]
    (pcase backend
      (`latex
       (setq marker (or marker (format "{\\the%s}" counter))) ;; "\\circ"
       (format "\\!\\!${}^{\\textnormal{%s}}$
               \\newsavebox{\\OrgSpecialBlockExtrasMarginBox}
               \\begin{lrbox}{\\OrgSpecialBlockExtrasMarginBox}
               \\begin{minipage}{%s}
               \\raggedright \\iffalse Otherwise default alignment is fully justified. \\fi
               \\footnotesize
               \\setminted{fontsize=\\footnotesize, breaklines} \\iffalse HACK! \\fi
               \\color{%s}
               {\\color{black}${}^{\\textnormal{%s}}$}\n\\normalfont\n %s
               \\end{minipage}
               \\end{lrbox}
               \\marginpar{\\usebox{\\OrgSpecialBlockExtrasMarginBox}%s}
               \\hspace{-1.9ex}
               \\global\\let\\OrgSpecialBlockExtrasMarginBox\\relax"
               marker
               width
               color
               marker
               (if (s-contains? "#+resize:" contents)
                   (s-concat
                    (s-replace "#+resize:"
                               "#+latex: \\maxsizebox{1.3\\textwidth}{\\textheight}{\\begin{tabular}{l}\n"
                               (s-trim contents))
                    "\n\\end{tabular}}")
                 (s-trim contents))
               stepcounter))
      (_ (setq marker (or marker "°"))
         (format "<abbr class=\"tooltip\" title=\"%s\">%s</abbr>&emsp13;"
                 (o-html-export-preserving-whitespace contents)
                 ; MA: FIXME: (org-export-string-as contents 'html :body-only-please)
                 marker)))))

Example: Links, without and with explict lables

This °  works!

This look!  works!


Source:

 This [[margin:][hola and *woah* ]] works!

 This [[margin:look!][hola]] works!

Example: Middle of a sentence, no explicit label

Hello this °  is super neat!


Source:

 Hello this
 #+begin_margin
 Extra details about whatever it is I'm talking about.
 #+end_margin
 is super neat!

Example: Middle of a sentence, explicit (full word) label

Hello this  is super neat!


Source:

 Hello
 #+begin_margin this
 Extra details about whatever it is I'm talking about.
 #+end_margin
 is super neat!

★ Example: A ‘margin’ with an ‘export’ in the middle

An HTML “marquee” °  is a scrolling piece of text.


Source:

 An HTML “marquee”
 #+begin_margin
 An example is best:
 #+begin_export html
 <marquee>Catch me if you can!</marquee>
 #+end_export
 It is pronounced “mar key”.
 #+end_margin
 is a scrolling piece of text.

Example: Brief code snippet in tooltip/margin

It's a simple  exercise to show that ℕ is fixedpoint of Maybe.


Source:

It's a
#+begin_margin simple
/Proof Sketch:/ “Squint” your eyes to see ~zero : ℕ~ and ~nothing : Maybe ℕ~ as
essentially the same, and “squint” your eyes to see that ~suc~ is essentially the
same as ~just~.

More formally, here's one direction:
#+begin_src haskell :tangle no
to : Maybe ℕ → ℕ
to nothing  = zero
to (just n) = suc n
#+end_src

The rest is an arduous exercise if you don't know what's going on.
#+end_margin
exercise to show that ℕ is fixedpoint of ~Maybe~.

★ Example: Lengthy ‘margin’ with multiple code blocks!

It's a simple  exercise to show that ℕ is fixedpoint of Maybe.


Source:

It's a
#+begin_margin simple
We show that there is an /isomorphism/; i.e., a non-lossy protocol between ~Maybe ℕ~
and ~ℕ~ in stages.

First, let's recall the definitions ... in Agda ...
#+begin_src haskell :tangle no :tangle no
data Maybe (A : Set) : Set₁ where
  nothing : Maybe A
  just    : A → Maybe A

data ℕ : Set where
  zero : ℕ
  suc  : ℕ → ℕ
#+end_src

We can show ~Maybe ℕ ≅ ℕ~ by writing two functions...
#+begin_src haskell :tangle no
to : Maybe ℕ → ℕ
to nothing = zero
to (just n) = suc n

from : ℕ → Maybe ℕ
from zero = nothing
from (suc n) = just n
#+end_src

...and, finally, checking that the two functions undo each other...
#+begin_src emacs-lisp
to∘from : ∀ {n} →  to (from n)  ≡  n
to∘from n = {! try it! }

from∘to : ∀ {m} →  from (to m)  ≡  m
from∘to m = {! try it! }
#+end_src

This is “simple”, but involved!
#+end_margin
exercise to show that ℕ is fixedpoint of ~Maybe~.

For the tooltips containing code snippets, I've declared the following to make the code look nice.

#+html:      <style> pre.tooltip {color: black; background-color:Snow;} </style>

Going forward, it would be nice to get full code colouring of source blocks in the HTML tooltips —which can be done using org-export-string-as, just need the time.

11 Equational Proofs

Sometimes you just want to have a quick, yet detailed, explanation and so we can use org-lists as equational proofs —thanks to the calc block!

Implementation

(defun o--list-to-calc (lst rel hint-format NL-length color)
  "Get a result from org-list-to-lisp and render it as a calculational proof.

LST is an expression, possibly with a hint and dedicated relation.
The expression may contain multiple lines, as may the hints.

REL is the default relation in the left-most column.

HINT_FORMAT is the formatting string for hints; e.g.,
\"\\color{maroon}{\\langle\\large\\substack{\\text{ %s }}⟩}\"

NL-length is how long the explicit vertical space, \\, should be.
The number refers to the vspace occupied nearly by the height of
a single normal sized letter.

COLOR is the colour of the hints."
  (cond
   ((symbolp lst) "")
   ((symbolp (car lst)) (o--list-to-calc (cadr lst)))
   (t (-let* (((conclusion₀ children) lst)
              ((expr₀ hint) (s-split "--" conclusion₀))
              ((op₀ expr₁) (cdr (s-match "^\\[\\(.*\\)\\]\\(.*\\)" expr₀)))
              (op (or op₀ rel))
              (expr (or expr₁ expr₀)))
        (if (not children)
            (if hint
                (format
                 "\n %s \\;\\; & \\qquad \\color{%s}{%s} \n \\\\ & \\begin{split}%s\\end{split}"
                 op
                 color
                 ;; the hfill is so that we do not use substack's default
                 ;; centering, but instead left-align justificatiion
                 ;; hints.
                 (format (s-replace "%s" "{\\large\\substack{\\text{ %s } \\hfill\\\\\n}}" hint-format)
                         (s-replace "\n" " } \\hfill\\\\\n\\text{ "
                                    (s-replace "\n\n" (s-repeat (* 6 NL-length) "\n $\\,$") (s-trim hint))))
                 expr)
              (format "\\begin{split}%s\\end{split} \n" expr))
       ;; MA: The following could be improved.
          (format "\n %s \\;\\; & \\qquad \\color{%s}{%s} \n \\\\ & \\begin{split}%s\\end{split}"
                  op color
                  ;; BEGIN similar as above
                  (format (s-replace
                           "%s"
                           "{\\large\\substack{\\text{ %s } \\hfill\\\\ \\begin{split} & %s \n\\end{split}\\hfill\\\\\n}}"
                           hint-format)
                          (s-replace "\n" " } \\hfill\\\\\n\\text{ "
                                     (s-replace "\n\n" (s-repeat (* 6 NL-length) "\n $\\,$") (s-trim hint)))
                          ;; END similar
                          (s-chop-prefix
                           "\\\\" (s-join
                                   "\\\\"
                                   (--map (format "%s" (o--list-to-calc it rel hint-format NL-length color))
                                          children))))
                  expr))))))

(o-defblock calc
  (main-arg)
  (rel "=" hint-format "\\left[ %s \\right." explicit-vspace 2 color "maroon")
  "Render an Org-list as an equational proof.

Sometimes the notation delimiting justification hints may clash
with the domain topic, so we can change the hint format, e.g., to
\"\\left\\langle %s \\right\\rangle\".  Set HINT-FORMAT to the
empty string, \"\", to comment-out all hints in the exported
version.

The hint is the text immediately after a “--”, if there are
multiple such delimiters only the first is shown; this can be
useful if we want to have multiple alternatives, say for extra
details in the source but not so much in the export.

Line breaks are taken literally in the hints, but not in the
math; where one uses \\\\.  For math with multiple lines, use ‘&’
as an alignment marker; otherwise math is right-justified.

For HTML, to use an TeX it must be enclosed in $, since that is
what is required by MathJaX."
  (thread-last (with-temp-buffer
                 (insert raw-contents)
                 (goto-char (point-min))
                 (org-list-to-lisp))
    cdr
    (--map (format "%s" (o--list-to-calc it rel hint-format explicit-vspace color)))
    (s-join "\\\\")
    (format "$$\\begin{align*} & %s \n\\end{align*}$$")))

“Programming Comments  ≈  Proof Hints”

Let's prove that x≤z…

Source:

#+begin_calc :hint-format "\\left\\{ %s\\right."
+     x
+     y -- Explanation of why $x \;=\; y$
  Actually, let me explain:
  * x
  * x′ -- hint 1
  * y  -- hint 2

  No words can appear (in the export) *after* a nested calculation, for now.
+ [≤] z
  --
  Explanation of why $y \;\leq\; z$

  -- explain it more, this is ignored from export ;-)
#+end_calc

Result:

x={ Explanation of why x=y  Actually, let me explain: x={ hint 1 x′={ hint 2 yy≤{ Explanation of why y≤z z

Benefits of Org-lists for proofs:

  • Nested proofs can be folded-away with TAB.
    • This provides a nice proof outline!
  • All Org support for lists active; e.g., M-↑,↓ for moving proof steps (list items) and M-RET for new proof steps.
  • Intentional explanations can make the proof argument more convincing, or accurate.

Examples below: Rational root equivales perfect power, Handshaking lemma, calculating train length, and last, but not least, “1 + 1 = 2”.


Align the proof hints with M-x align-regexp RET -- RET …let's calculate that a⊆e…

Source:

#+begin_calc
+     a
+     b  --  reason for the equality
+ [⊆] c  --  reason for the inclusion
+     d  --  reason for the equality
+     e  --  final justification
#+end_calc

Result:

a=[ reason for the equality b⊆[ reason for the inclusion c=[ reason for the equality d=[ final justification e

Tests

(setq calc (⟰ "#+begin_calc :hint-format \"\\\\left\\{ %s\\\\right.\"
                  +     x
                  +     y -- Explanation of why $x \\;=\\; y$
                    Actually, let me explain:
                    * x
                    * x′ -- hint 1
                    * y  -- hint 2

                    No words can appear (in the export) *after* a nested calculation, for now.
                  + [≤] z
                    --
                    Explanation of why $y \\;\\leq\\; z$

                    -- explain it more, this is ignored from export ;-)
                  #+end_calc"))

(deftest "It's an align environment, in displayed math mode"
  [calc]
  (⇝ calc "$$\\begin{align*}" (* anything) "\\end{align*}$$"))

(deftest "The calculation has 4 proof steps"
  [calc]
  ;; The number of steps in a calculation is the number of items in each nesting, minus 1 (at each nesting).
  ;; Above we have depth 2 with 3 items in each depth, for a total of (3-1) + (3-1) = 2 + 2 = 4.
  (should (= 4 (s-count-matches (rx (seq "\\" (* whitespace) (any "=" "≤"))) calc))))


(deftest "Of our 4 steps, 3 of them are equalities and one is an inclusion."
  [calc]
  (should (= 3 (s-count-matches (rx "= \\;\\;") calc)))
  (should (= 1 (s-count-matches (rx "≤ \\;\\;") calc))))

(deftest "All of the hints actually appear in the calculational proof"
  [calc]
  (mapc (lambda (hint) (should (s-contains? hint calc)))
        '("Explanation of why $x \\;=\\; y$"
          "Actually, let me explain:"
          "hint 1"
          "hint 2"
          "Explanation of why $y \\;\\leq\\; z$")))

Examples…

★ Proof of “1 + 1 = 2” with source

1+1=[   “1” abbreviates “suczero” suczero+suczero=[ Addition is defined by moving the suc-s from the left argument to the  right.                          That is, we define x+y by considering the possible shape of x,  which could only be a zero or a suc-cessor.  When x is zero, there are no suc-s to move to the right, so we yield the right.  zero+n=n  When x is a suc, we move the suc to the right, and continue to try adding.  sucm+n=m+sucn  Since we're moving a suc at each step, eventually we'll hit the  base case and be done adding.                        For now, we apply the (Inductive Step). zero+suc(suczero)=[ Using the (Base Case) definition of addition. suc(suczero)=[ Switching to ‘conventional’ notation 2

Source:

#+begin_calc
+ 1 + 1
+ \suc \zero + \suc \zero
  --
  $\def\suc{\mathsf{suc}\,} \def\zero{\mathsf{zero}}$
  “1” abbreviates “$\suc \zero$”
+ \zero + \suc (\suc \zero) --
  Addition is defined by moving the $\suc$-s from the left argument to the
  right.
  $\def\eqn#1#2{\begin{equation} #2 \tag{#1} \label{#1} \end{equation}}$

  That is, we define $x + y$ by considering the possible $shape$ of $x$,
  which could only be a $\zero$ or a $\suc$-cessor.
  When $x$ is zero, there are no $\suc$-s to move to the right, so we yield the  right.
  $\eqn{Base Case}{\zero + n \; = \; n}$
  When $x$ is a $\suc$, we move the $\suc$ to the right, and continue to try adding.
  $\eqn{Inductive Step}{\suc m \,+\, n \; = \; m \,+\, \suc n}$
  Since we're moving a $\suc$ at each step, eventually we'll hit the
  base case and be done adding.

  For now, we apply the \eqref{Inductive Step}.

+ \suc (\suc \zero)
  -- Using the \eqref{Base Case} definition of addition.
+ 2 -- Switching to ‘conventional’ notation
#+end_calc

“Calculating” the length of a train

Let us calculate the length of a train, that is travelling at a speed of 60 km/hr and, on its way, it crosses a pole in 9 seconds.

 the length of the train =[ Express length as a difference of distance  the distance from the end of the train to its front =[ distance = time × speed ( difference in time from when the train's front crosses the pole until its back crosses the pole )×speed of the train=[ Rephrase  how long it takes the train to cross the pole ×speed of the train=[   The train crosses the pole in 9 seconds;  and the train is travelling at the speed of 60km/hr. 9 seconds ×60 km / hr=[ Express things in common units, such as seconds; i.e.,: 1 km / hr=[ Be more explicit 1 km /1 hr =[ A kilometre is 1000 meters 1000meters/1 hr =[ Express hours in terms of seconds: 1 hr =[ Express in minutes 60minutes=[ Be more explicit 60×1minutes=[ A minute has 60 seconds 60×60seconds1000meters/(60×60)seconds=[ Collecting like-terms (1000/60×60)meters/seconds9seconds×60×(1000/60×60)meters/seconds=[ Division cancels multiplication: b×(a/b)=a 9seconds×(1000/60)meters/seconds=[ Collect like-terms; i.e., commutativity of × (1000/60)×9seconds×(meters/seconds)=[ Division cancels multiplication: b×(a/b)=a (1000/60)×9meters=[ Arithmetic 150meters

Euler's Handshaking Lemma

At a party of 7 people, is it possible that everybody knows exactly 3 other people?

We could consider all possible cases, which is not particularly enlightening; or we could calculate

“Obviously”, the sum of everyone's friendship count is twice the total number of friendships=[ Formalise...                        Let f(p) denote the number of friends that person p has,  which is 0 if p has no friends.                        Let F be the total number of friendships (between any 2 people).                        Since friendships are always between two people, we have  that ∑pf(p)=2×F: Counting-up the number of friends  that everyone has will always be twice the total number of relationships sum of everyone's friendship count=[ Formalise, where p ranges over people ]∑pf(p)=[ Definition of f:  Let 𝑭𝒓p,r be 1 exactly when p participates in  friendship/relationship r,  and 0 otherwise, then f(p)=∑r𝑭𝒓p,r. ]∑p∑r𝑭𝒓p,r=[ Quantifier interchange; i.e., Fubini's Principle ]∑r∑p𝑭𝒓p,r=[ Every relationship r is between 2 people;  i.e., ∑p𝑭𝒓p,r=2 ]∑r2=[ Factoring ]2×∑r1=[ The total number of friendships is F ]2×F]∑pf(p)=2×F⇒[ Equals for equals;  i.e., if those numbers are equal then their parities are equal     ]even(∑pf(p))≡even(2×F)=[ The right side is clearly true;  (‘true’ is the identity of ‘≡’) ]even(∑pf(p))=[ even(−) distributes over sums turning them into equivalences ‘≡’ ]≡peven(f(p))=[ By definition, a chain of equivalences is true exactly when  it has an even number of false values ]even(♯{p∣¬even(f(p))})=[ A non-even number is precisely an odd number ]even(♯{p∣odd(f(p))})=[ Un-formalise ]an even number of people have an odd number of friends

Back to the problem: At a party of 7 people, is it possible that everybody knows exactly 3 other people?

If such a party were possible, the number of people with an odd friend count must be even, but 7 is not even, and so no such party is possible. ( Alternatively, if such a party were possible, then by the Handshaking Lemma, F = {∑_p f(p) \over 2} = {7 × 3 \over 2} = 10.5: This is absurd since there cannot be a ‘.5’ relationship! Hence, no such party is possible. )

  • Likewise, if exactly 5 people in a room each claim to have shaken 3 hands, then someone is lying.

( Rephrased: The sum of the degrees of the vertices of a (total) Graph is twice the number of edges of the graph; moreover, an even number of vertices have an odd degree.

The party problem is then: Is there a graph of 7 nodes with each node being connected to exactly 3 other nodes? Apparently not, and we didn't need to draw any graphs at all to prove it.

For more on Graph Theory, here are some lecture notes. )

★ Rational root equivales perfect power



\begin{align*} & \begin{split}\sqrt[n]{k} \text{ is a rational number } \end{split} \\ ⇔ \;\; & \qquad \color{maroon}{\left[ {\large\substack{\text{ A rational number is the fraction of two integers. } \hfill\\ \text{ Let variables $a,\, b$ range over integer numbers. } \hfill\\ }} \right.} \\ & \begin{split}∃\, a, b •\; \sqrt[n]{k} = {a \over b} \end{split}\\ ⇔ \;\; & \qquad \color{maroon}{\left[ {\large\substack{\text{ Use arithmetic to eliminate the $n$-th root operator. } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$$\def\exp#1#2{\mathsf{exp}_#1\,#2}$ } \hfill\\ \text{ $\def\eq{\;=\;}$ } \hfill\\ }} \right.} \\ & \begin{split}∃\, a, b •\; k · a ^n = b ^n \end{split}\\ ⇔ \;\; & \qquad \color{maroon}{\left[ {\large\substack{\text{ Let $\exp{m} x$ be the number of times that $m$ divides $x$. } \hfill\\ \text{ For example, $\exp{2} 48 \eq 4$ and $\exp{2} 49 \eq 0$. } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$The numbers $p$ with $∀ m : ℤ⁺ \;•\; \exp{m}p \,≠\, 0 \,≡\, m \,=\, p$ } \hfill\\ \text{ are called $prime$ numbers. Let variable $p$ range over prime numbers. } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$ } \hfill\\ \text{ $\,$Fundamental theorem of arithmetic: Numbers are determined by their prime powers. } \hfill\\ \text{ That is, $\big(∀ \,p\, •\;\; \exp{p} x \eq f(p)\big) \;\;≡\;\; x \eq \big(Π\, p\, •\; p^{f(p)}\big)$ for any $f$. } \hfill\\ \text{ As such, every number is the product of its prime powers: } \hfill\\ \text{ $\qquad x \eq \big(Π \,p\, •\; p^{\exp{p} x}\big)$. } \hfill\\ \text{ And so, any two numbers are the same precisely when they have the same primes: } \hfill\\ \text{ $\qquad x \eq y \;\;≡\;\; \big(∀ p \,•\, \exp{p} x \eq \exp{p} y\big)$. } \hfill\\ }} \right.} \\ & \begin{split}∃\, a, b •\; ∀\, p •\; \exp{p}(k · a ^n) \eq \exp{p}(b ^n ) \end{split}\\ ⇔ \;\; & \qquad \color{maroon}{\left[ {\large\substack{\text{ When $p$ is prime, $\exp{p}(x · y) \eq \exp{p} x \,+\, \exp{p} y$. } \hfill\\ \text{ Aside: In general, $\exp{p}(Π \,i\, \,•\, x_i) \eq (Σ \,i\, \,•\, \exp{p} x_i)$. } \hfill\\ }} \right.} \\ & \begin{split}∃\, a, b •\; ∀\, p •\; \exp{p} k + n · \exp{p} a \eq n · \exp{p} b \end{split}\\ ⇔ \;\; & \qquad \color{maroon}{\left[ {\large\substack{\text{ Use arithmetic to collect similar terms. } \hfill\\ }} \right.} \\ & \begin{split}∃\, a, b •\; ∀\, p •\; \exp{p} k \eq n · \Big(\exp{p} b - \exp{p} a\Big) \end{split}\\ ⇔ \;\; & \qquad \color{maroon}{\left[ {\large\substack{\text{ (⇒) is the definition of multiplicity; } \hfill\\ \text{ (⇐) take $a \,≔\, 1$ and define $b$ by its prime powers: } \hfill\\ \text{ $\qquad ∀\, p \,•\, \exp{p} b \,≔\, {\exp{p} k \,/\, n}$ } \hfill\\ }} \right.} \\ & \begin{split}∀\, p •\; \exp{p} k \text{ is a multiple of } n \end{split}\\ ⇔ \;\; & \qquad \color{maroon}{\left[ {\large\substack{\text{ Fundamental theorem of arithmetic and definition of ‘perfect’ } \hfill\\ }} \right.} \\ & \begin{split}k \text{ is a perfect $n$-th power; i.e., of the shape } x^n \end{split} \end{align*}


Possibly Useful links:

★ What if I want to do this in LaTeX (e.g., for math.stackexchange.com)?

Copy-paste the following…

$
% set up \step command
\def\BEGINstep{ \left\langle }
\def\ENDstep{ \right\rangle }
\newcommand{\step}[2][=]{ \\ #1 \;\; & \qquad \color{maroon}{\BEGINstep\text{ #2 } \ENDstep} \\ & }
% multi-line step with many lines of text
\newcommand{\line}[1]{ \text{#1}\hfill\\ }
\newcommand{\stepmany}[2][=]{ \\ #1 \;\; & \qquad \color{maroon}{\BEGINstep \large\substack{ #2 } \ENDstep} \\ & }
% multi-line step with 4 lines of text
\newcommand{\stepfour}[5][=]{ \stepmany[#1]{\line{#2} \line{#3} \line{#4}
\line{#5}} }
\newenvironment{calc}{\begin{align*} & }{\end{align*}}
$

Then, use it as follows…

\begin{calc}
x
\step{reason for equality}
y
\step[\leq]{reason for inclusion}
z
\end{calc}

12 Summary

The full article may be read as a PDF or as HTML —or visit the repo.

Let 𝒞 be any of the following: black, blue, brown, cyan, darkgray, gray, green, lightgray, lime, magenta, olive orange, pink, purple, red, teal, violet, white, yellow.

Idea Documentation Link only?
Colours 𝒞, latex-definitions, color  
Parallel parallel  
Editorial Comments remark  
Folded Details details , box  
Keystrokes   kbd
OctoIcons & Link Here   octoicon, link-here
Documentation-Glossary documentation doc
Marginal remarks margin  
Badges badge  
Equational proofs calc  

Other fun stuff: solution, org-demo, stutter, rename, spoiler, tree :grin:

There are also the social badge links: reddit-subscribe-to, github-followers, github-forks, github-stars, github-watchers, twitter-follow, and tweet.

12.1 Installation Instructions

Manually or using quelpa:

;; ⟨0⟩ Download the org-special-block-extras.el file manually or using quelpa
(quelpa '(org-special-block-extras :fetcher github :repo
"alhassy/org-special-block-extras"))

;; ⟨1⟩ Have this always active in Org buffers
(add-hook #'org-mode-hook #'org-special-block-extras-mode)

;; ⟨1′⟩ Or use: “M-x org-special-block-extras-mode” to turn it on/off

Or with use-package:

(use-package org-special-block-extras
  :ensure t
  :hook (org-mode . org-special-block-extras-mode)
  :custom
    ;; The places where I keep my ‘#+documentation’
    (o-docs-libraries
     '("~/org-special-block-extras/documentation.org"))
    ;; Details heading “flash pink” whenever the user hovers over them?
    (org-html-head-extra (concat org-html-head-extra "<style>  summary:hover {background:pink;} </style>"))
    ;; The message prefixing a ‘tweet:url’ badge
    (o-link-twitter-excitement
     "This looks super neat (•̀ᴗ•́)و:"))

Then, provide support for a new type of special block, say re-using the src blocks that, say, folds up all such blocks in HTML export, by declaring the following.

(o-defblock src (lang nil) (title nil exports nil file nil)
  "Fold-away all ‘src’ blocks as ‘<details>’ HTML export.
If a block has a ‘:title’, use that to title the ‘<details>’."
  (format "<details> <summary> %s </summary> <pre> %s </pre></details>"
          (or title (concat "Details; " lang))
          raw-contents))

12.2 Minimal working example

The following example showcases the prominent features of this library.

#+begin_parallel
[[color:orange][Are you excited to learn some Lisp?]] [[blue:Yes!]]

Pop-quiz: How does doc:apply work?
#+end_parallel

#+begin_details Answer
link-here:solution
Syntactically, ~(apply f '(x0 ... xN)) = (f x0 ... xN)~.

[[remark:Musa][Ain't that cool?]]

#+begin_spoiler aqua
That is, [[color:magenta][we can ((apply)) a function to a list of arguments!]]
#+end_spoiler

#+end_details

#+html: <br>
#+begin_box
octoicon:report Note that kbd:C-x_C-e evaluates a Lisp form!
#+end_box

/Allah[[margin:][The God of Abraham; known as Elohim in the Bible]] does
not burden a soul beyond what it can bear./ --- Quran 2:286

#+LATEX_HEADER: \usepackage{multicol}
#+LATEX_HEADER: \usepackage{tcolorbox}
#+latex: In the LaTeX output, we have a glossary.

show:GLOSSARY

badge:Thanks|for_reading
tweet:https://github.com/alhassy/org-special-block-extras
badge:|buy_me_a coffee|gray|https://www.buymeacoffee.com/alhassy|buy-me-a-coffee

Here is what it looks like as HTML (left) and LaTeX (right):

minimal-working-example.png

Tests

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Run all MWE tests
;; (ert "mwe")

(setq mwe (⟰
 "
#+begin_parallel
[[color:orange][Are you excited to learn some Lisp?]] [[blue:Yes!]]

Pop-quiz: How does doc:apply work?
#+end_parallel

#+begin_details Answer
link-here:solution
Syntactically, ~(apply f '(x0 ... xN)) = (f x0 ... xN)~.

[[remark:Musa][Ain't that cool?]]

#+begin_spoiler aqua
That is, [[color:magenta][we can ((apply)) a function to a list of arguments!]]
#+end_spoiler

#+end_details

#+html: <br>
#+begin_box
octoicon:report Note that kbd:C-x_C-e evaluates a Lisp form!
#+end_box

/Allah [[margin:][The God of Abraham; known as Elohim in the Bible]] does not burden a soul
beyond what it can bear./ --- Quran 2:286

#+LATEX_HEADER: \\usepackage{multicol}
#+LATEX_HEADER: \\usepackage{tcolorbox}
#+latex: In the LaTeX output, we have a glossary.

show:GLOSSARY

badge:Thanks|for_reading
tweet:https://github.com/alhassy/org-special-block-extras
badge:|buy_me_a coffee|gray|https://www.buymeacoffee.com/alhassy|buy-me-a-coffee
"))

(deftest "It exports to HTML without any problems"
  [mwe html-export]
  (find-file "mwe.org")
  (should (org-html-export-to-html)))

(deftest "It starts with a 2-column div for ‘parallel’"
  [mwe parallel]
  (⇝ mwe "<div style=\"column-rule-style: none;column-count: 2;\">"
         (* anything)
         "</div>"))

(deftest "Its initial question is in ‘orange’, with answer in ‘blue’"
  [mwe color orange blue]
  (⇝ mwe "<span style=\"color:orange;\">Are you excited to learn some Lisp?</span>"
         (* anything)
         "<span style=\"color:blue;\">Yes!</span>"))

(deftest "Its second question, about ‘apply’, has a tooltip"
  [mwe doc]
  (⇝ mwe "Pop-quiz: How does "
         "<abbr class=\"tooltip\" title=\"Call FUNCTION with our remaining args, "
         "using our last arg as list of args.<br>Then return the value FUNCTION returns."
         "<br>Thus, (apply '+ 1 2 '(3 4)) returns 10.<br><br>(fn FUNCTION &rest ARGUMENTS)\""
         ">apply</abbr> work?"))

(deftest "Its ‘details’ block is titled “Answer”, in green"
  [mwe details]
  (⇝ mwe
     "<details"
     (* anything) ;; styling
     "<summary>"
     (* anything) ;; styling
     "<font face=\"Courier\" size=\"3\" color=\"green\">"
     (* anything)
     "Answer"))

(deftest "Its details block begins with an SVG anchor identified as ‘solution’"
  [mwe link-here]
  (⇝ mwe "<details"
         (* anything) ;; styling
         "<a class=\"anchor\""
         (* anything)
         "href=\"#solution\">" ;; link-here:solution
         "<svg"
         (* anything)
         "</svg></a>"
         (* anything)
         "Syntactically, <code>(apply f '(x0 ... xN)) = (f x0 ... xN)</code>."))

(deftest "Its top-level remark is my name in a box, then the text, then a closing box delimiter"
  [mwe remark]
  (⇝ mwe "<details"
         (* anything)
         "<p style=\"color: black;\">"
         "<span style=\"border-width:1px;border-style:solid;padding:5px\">"
         "<strong>[Musa:</strong>"
         "</span>"
         " Ain't that cool?  "
         "<span style=\"border-width:1px;border-style:solid;padding:5px\"><strong>]</strong></span>"
         ))

(deftest "The aqua-coloured ‘spoiler’ appears within a magenta coloured piece of text"
  [mwe spoiler color magenta gensym]

  (⇝ mwe "<details"
         (* anything)
         ;; The local spoiler style is declared
         "<style>"
         (* anything) ;; A random id; e.g., #g289
         "{color: aqua; background-color:aqua;}"
         (* anything)
         ":hover {color: black; background-color:white;} "
         "</style>"
         (* anything)
         ;; Then it is used
         "That is, <span style=\"color:magenta;\">"
         "we can <span id="
         (* anything) ;; our random id is used here
         "> apply </span>" ;; Here is the spoiler!
         " a function to a list of arguments!</span>"
         (* anything)
         "</details>"))

(deftest "It has a title-less green box starting with an octoicon"
  [mwe box octoicon kbd]
  :expected-result :failed ;; FIXME The MWE has been updated, and more tests need to be written.
  (⇝ mwe
     "<div style=\"padding: 1em;"
     (* anything)
     "<h3></h3>"
     (* anything)
     "<svg" ;; octoicon:report
     (* anything)
     "Note that <kbd> C-x C-e </kbd> evaluates a Lisp form!"
     (* anything)
     "</div>"))

(deftest "Its Quranic quote has the user requested tooltip indicated by a small raised circle"
  [mwe margin]
  (⇝ mwe
     "Allah <abbr class=\"tooltip\""
     " title=\"The God of Abraham; known as Elohim in the Bible\">°</abbr>&emsp13;" ;; ∘ !
     " does not burden a soul beyond what it can bear."
     (* anything)
     "Quran 2:286"))

(deftest "It concludes with three beautiful badges"
  [mwe badge]
  ;; badge:Thanks|for_reading
  ;; tweet:https://github.com/alhassy/org-special-block-extras
  ;; badge:|buy_me_a coffee|gray|https://www.buymeacoffee.com/alhassy|buy-me-a-coffee
  (⇝ mwe
     "<img src=\"https://img.shields.io/badge/Thanks-for_reading-nil?logo=nil\">"
     (* anything)
     "<img src=\"https://img.shields.io/twitter/url?url=https://github.com/alhassy/org-special-block-extras\">"
     (* anything)
     "<img src=\"https://img.shields.io/badge/-buy_me_a%C2%A0coffee-gray?logo=buy-me-a-coffee\">"))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

The above section, ‘practice problems’, presents a few puzzles to get you comfortable with defblock ;-)

Other cool stuff…

inference-rules.png
calculational_proofs.png
marginal_remarks.png

o--org-demo o--solution o--remark o--latex-definitions Graph o--calc org-export-string-as o--margin lambda lf-documentation o-docs-insert o-docs-load-libraries o-docs-fallback o-docs-get o-docs-set o--docs user user-mail-address Natural Transformation o--documentation Existential Angst loop Category Theory o-make-badge o--teal o--color o--parallel with-temp-buffer org-list-to-lisp o--tree regexp-quote s-replace-regexp s-trim s-split -map --map s-replace-all string-to-number numberp s-repeat o--stutter o--spoiler o--rename o--box o--details o-thread-blockcall thread-last funcall documentation org-link-parameters org-add-link-type org-link-set-parameters cl-defun o-deflink defmacro repeat-complex-command Hussain family thread-first o-link/octoicon org-narrow-to-element defun o-defblock

Author: Musa Al-hassy

Created: 2021-09-09 Thu 16:25

Validate


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK