A Life Configuring Emacs

Table of Contents

Abstract

Hello! Herein I document the configurations I utilise with Emacs.

As a literate program file with Org-mode, I am ensured optimal navigation through my ever growing configuration files, ease of usability and reference for peers, and, most importantly, better maintainability for myself!

Dear reader, when encountering a foregin command X I encourage you to execute (describe-symbol 'X), or press C-h o with the cursor on X. An elementary Elisp Cheat Sheet can be found at and is a 2-page 3-column PDF of the bindings in this configuration.

Finally, C-h d asks nicely what ‘d’ocumentation you're interested in. After providing a few keywords, the apropos tool yields possible functions and variables that may accomplish my goal.

This article is about how I like to do things ---I'm not insisting others should do things this way.

Always remember that to argue, and win, is to break down the reality of the person you are arguing against. It is painful to lose your reality, so be kind, even if you are right. - Haruki Murakami

Life is too short to not read the very best book you know of right now. - Patrick Collison

Inspiration is for amateurs. The rest of us just show up and get to work. - Chuck Close

“Personal instructions for a new machine”

These steps must be performed at the terminal since they are required to get my Emacs, which then installs everything else when it's first opened.

  1. Install a package manager: https://brew.sh/ :

    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
    

    Also: Change to the conventional scrolling direction: If I pull my scroll down, I want to go down.

    • Apple menu → System Preferences → Mouse → Tick the scroll direction option.
  2. Use brew to get Emacs:

    brew install --cask emacs
    

    If that fails, try this to install Emacs:

    brew tap daviderestivo/emacs-head
    brew install emacs-head
    
  3. Then    make   the command emacs available via the terminal —required if doing any melpa development.

    ln -s /usr/local/opt/emacs-head@27/Emacs.app /Applications
    
    sudo ln -s /usr/local/opt/emacs-head@27/Emacs.app/Contents/MacOS/Emacs /usr/local/bin/emacs
    
  4. Install git: brew install git
  5. Get my Emacs setup: rm -rf ~/.emacs.d; git clone https://github.com/alhassy/emacs.d.git ~/.emacs.d

    Open Emacs and watch download and set up many other things … ^_^

    This may take ~15 minutes —we install a massive LaTeX setup.

We get: Spell checker, dictionary, LaTeX + pygements, Dropbox, AG (for fast system-wide searching of a string with helm-do-grep-ag, useful for finding definitions), Amethyst window manager.

Amethyst requires some more setup: Open its preferences, then…

For convenience, on a Mac, add the home (~/) directory to the default file navigator: Finder → Preferences → Sidebar, then select home 🏠.

If you notice any “file system access” concerns, give Emacs permissions to read your files: General Settings → Security & Privacy → Full Disk Access → ⌘-M-g (to search) then enter /usr/bin/ruby —Emacs is launched via a Ruby script in MacOS.

1. Why Emacs?

A raw code file is difficult to maintain, especially for a large system such as Emacs. Instead, we're going with a ‘literate programming’ approach: The intialisation configuration is presented in an essay format, along with headings and subheadings, intended for consumption by humans such as myself, that, incidentally, can be ‘tangled’ into a raw code file that is comprehensible by a machine. We achieve this goal using org-mode —which is Emacs' killer app.

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! ( Org Mode Is One of the Most Reasonable Markup Languages to Use for Text )

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

  • You can make an empty table with C-c |, which is just org-table-create-or-convert-from-region, then give it row×column dimensions.
  • Any lines with comma-separated-values (CSV) can be turned into an Org table by selecting the region and pressing C-u C-c |. (Any CSV file can thus be visualised nicely as an Org table).
  • Use C-u C-u C-u C-c | to make a table from values that are speared by a certain regular expression.

Working with links

Link syntax is [[source url][description]]; e.g., we can refer to the above table with [[my-tbl][woah]]. Likewise for images: file:path-to-image.


Mathematics

Source

\[ \sin^2 x + \cos^2 x = \int_\pi^{\pi + 1} 1 dx = {3 \over 3} \]

Result

\[ \sin^2 x + \cos^2 x = \int_\pi^{\pi + 1} 1 dx = {3 \over 3} \]

  • Instead of \[...\], which displays a formula on its own line, centred, use $...$ to show a formula inline.
  • Captioned equations are numbered and can be referenced via links, as shown below.

Source

#+name: euler
\begin{equation}
e ^ {i \pi} + 1 = 0
\end{equation}

See equation [[euler]].

Result

\begin{equation} \label{orgbefd1a4} e ^ {i \pi} + 1 = 0 \end{equation}

See equation \eqref{orgbefd1a4}.


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.

Emacs is a flexible platform for developing end-user applications —unfortunately it is generally perceived as merely a text editor. Some people use it specifically for one or two applications.

For example, writers use it as an interface for Org-mode and others use it as an interface for version control with Magit. Org is an organisation tool that can be used for typesetting which subsumes LaTeX, generating many different formats –html, latex, pdf, etc– from a single source, keeping track of schedules & task management, blogging, habit tracking, personal information management tool, and much more. Moreover, its syntax is so natural that most people use it without even knowing! For me, Org allows me to do literate programming: I can program and document at the same time, with no need to seperate the two tasks and with the ability to generate multiple formats and files from a single file.

A list of programs that can be replaced by Emacs

Pieces of (disparate) software can generally be replaced by (applications written on the) Emacs (text processing Lisp platform).

From the table below, of non-editing things you can do with Emacs, it's reasonable to think of Emacs as an operating system —and Vim/Evil is one of its text editors.

Application Emacs Package
Habit Tracker / TODO-list   Org mode
Agenda / Calendar / Time Tracker   Org mode
Literate Programming (like Jupyter)   Org mode
Blogging Software   Org mode
Reference Information Platform   Org mode with refile and my/reference
Word Processing / PDFs / Slidedeck tool   Org mode
Spell checker & dictionary & grammar checker   ispell & langtool
Reference and citation manager   org-ref
PDF Viewer   PDF View mode
Powerful Calculator   Calc-mode (Nice article on literate calc mode)
Fillable Forms / Data Entry   Widgets
Ebook Reader   nov.el and calibredb.el
Git / Version control   Magit or vc-mode
Shells   eshell or shell
Terminal emulators   term, ansi-term, and vterm
Package Manager   helm-system-packages
File Manager   dired
IDE / debugger   LSP / Dap
Scripting Language   Emacs Lisp
[Neo]Vim / Modal text editor   EVIL mode
RSS Newsreader   ElFeed
Email   Gnus / Mu4e [very pretty!] / notmuch
Spredsheet tool   Org Table / Simple Emacs Spreadsheet / spreadsheet-mode / csv-mode
Automatic file backups   ⟨Built-in⟩ & backup-walker
seemless GPG tool   ⟨Built-in⟩
Lisp interpreter   Anywhere press C-x C-e to run a Lisp expression
Documentation viewer   tldr-mode; C-h o / describe-symbol
Diff / Merge tool   ediff, diff
Games   tetris, pacman, mario, etc
Psychologist   doctor
Typing tutor   typing-of-emacs
Modern Internet Browser   xwidget-webkit-browse-url
everything else   EAF

I’m down to essentially Emacs and Chrome for almost all my work —I like using Chrome; I like the integration of all things Google.

Were I “only coding”, then I'd use a popular Integrated Development Environment that requires minimal setup and Just Worksᵀᴹ; but I blog, make cheat sheets, run background services, etc, and so I need an Integrated Computing Environment: Emacs.

If you are a professional writer…Emacs outshines all other editing software in approximately the same way that the noonday sun does the stars. It is not just bigger and brighter; it simply makes everything else vanish. —Neal Stephenson, In the beginning was the command line

  • Extensible ⇒ IDEs are generally optimised for one framework, unlike Emacs!

    • You can program Emacs to automate anything you want.
    • Hence, it's an environment, not just an editor.
    • ⇒ Unified keybinding across all tools in your environment.

    Users are given a high-level full-featured programming language, not just a small configuration language. For the non-programmers, there is Custom, a friendly point-and-click customisation interface.

  • Self Documented ⇒ Simply M-x info-apropos or C-h d to search all manuals or look up any function provided by Emacs!
  • Mature ⇒ tool with over 40 years of user created features
    • Plugins for nearly everything!
    • No distinction between built-ins and user-defined features! (Lisp!)
    • You can alter others' code without even touching the source.
      • Advising functions and ‘hooking’ functionality onto events.
  • Free software ⇒ It will never die!
    • Emacs is one of the oldest open source projects still under developement.

Of course Emacs comes with the basic features of a text editor, but it is much more; for example, it comes with a powerful notion of ‘undo’: Basic text editors have a single stream of undo, yet in Emacs, we have a tree –when we undo and make new edits, we branch off in our editing stream as if our text was being version controlled as we type! –We can even switch between such branches!

;; Allow tree-semantics for undo operations.
(use-package undo-tree
  :diminish                       ;; Don't show an icon in the modeline
  :bind ("C-x u" . undo-tree-visualize)
  :hook (org-mode . undo-tree-mode) ;; For some reason, I need this. FIXME.
  :config
    ;; Always have it on
    (global-undo-tree-mode)

    ;; Each node in the undo tree should have a timestamp.
    (setq undo-tree-visualizer-timestamps t)

    ;; Show a diff window displaying changes between undo nodes.
    (setq undo-tree-visualizer-diff t))

;; Execute (undo-tree-visualize) then navigate along the tree to witness
;; changes being made to your file live!

( The above snippet has a noweb-ref: It is presented here in a natural position, but is only executable once use-package is setup and so it is weaved there! We can present code in any order and tangle it to the order the compilers need it to be! )

Emacs is an extensible editor: You can make it into the editor of your dreams! You can make it suited to your personal needs. If there's a feature you would like, a behaviour your desire, you can simply code that into Emacs with a bit of Lisp. As a programming language enthusiast, for me Emacs is my default Lisp interpreter and a customisable IDE that I use for other programming languages –such as C, Haskell, Agda, Lisp, and Prolog. Moreover, being a Lisp interpreter, we can alter the look and feel of Emacs live, without having to restart it –e.g., press C-x C-e after the final parenthesis of (scroll-bar-mode 0) to run the code that removes the scroll-bar.

I use Emacs every day. I rarely notice it. But when I do, it usually brings me joy.Norman Walsh

I have used Emacs as an interface for developing cheat sheets, for making my blog, and as an application for ‘interactively learning C’. If anything Emacs is more like an OS than just a text editor –“living within Emacs” provides an abstraction over whatever operating system my machine has: It's so easy to take everything with me. Moreover, the desire to mould Emacs to my needs has made me a better programmer: I am now a more literate programmer and, due to Elisp's documentation-oriented nature, I actually take the time and effort to make meaningful documentation –even when the project is private and will likely only be seen by me.

Seeing Emacs as an editor is like seeing a car as a seating-accommodation.Karl Voit

1.1.   Emacs is a flexible platform for developing end-user applications

Just as a web browser is utilised as a platform for deploying applications, or ‘extensions’, written in JavaScript that act on HTML documents, Emacs is a platform for deploying applications written in Emacs Lisp that act on buffers of text. In the same vein, people who say Emacs having Tetris is bloat are akin to non-coders who think their browser has bloat since it has a “view page source” feature —which nearly all browsers have, yet it's only useful to web developers. Unlike a web browser in which the user must get accustomed to its features, Emacs is customised to meet the needs of its user. ( Incidentally, Emacs comes bundled with a web browser. )

In the case of Emacs the boundary between user and programmer is blurred as adapting the environment to one’s needs is already an act of programming with a very low barrier to entry. ---rekado

Don't just get used to your tool, make it get used to you!

Emacs is not just an editor, but a host for running Lisp applications!

For example, Emacs is shipped as a language-specific IDE to a number of communities —e.g., Oz, Common Lisp, and, most notably, Agda. Emacs is a great IDE for a language —one just needs to provide a ‘major mode’ and will then have syntax highlighting, code compleition, jumping to definitions, etc. There is no need to make an IDE from scratch.

1.2. The Power of Text Manipulation

Emacs has ways to represent all kinds of information as text.

E.g., if want to make a regular expression rename of files in a directory, there's no need to learn about a batch renaming tool: M-x dired ⟨RET⟩ M-x wdired-change-to-wdired-mode now simply perform a usual find-and-replace, then save with the usual C-x C-s to effect the changes!

Likewise for other system utilities and services (•̀ᴗ•́)و

Moreover, as will be shown below, you can literally use Emacs anywhere for textually input in your operating system –no copy-paste required.

1.3. Keyboard Navigation and Alteration

Suppose you wrote a paragraph of text, and wanted to ‘border’ it up for emphasies in hypens. Using the mouse to navigate along with a copy-paste of the hypens is vastely inferior to the incantation M-{ C-u 80 - RET M-} C-u 80 - RET. If we want to border up the previous 𝓃-many paragraphs, we simply prefix kbd:M-{,} above with C-u 𝓃 —a manual approach would have us count 𝓃 and slowly scroll. ( Exercise: What incantation of keys ‘underlines’ the current line with only the necessary amount of dashes? —Solution in the source file. )

MacOS supports many Emacs shortcuts, system-wide, such as C-a/e, C-d, C-k/y, C-o, C-p/n and even C-t for transposing two characters. ⇐

1.4. Emacs Proverbs as Koan

Below is an extract from William Cobb's “Reflections on the Game of Go”, with minor personalised adjustements for Emacs. Enjoy!

The Japanese term satori refers to the experience of enlightenment, the realisation of how things really are that is the primary aim of practice and meditation. However, the Zen tradition is famous for claiming that one cannot say what it is that one realises, that is, one cannot articulate the content of the enlightenment experience. Although it makes everything clear, it is an experience beyond words. Instead of being given an explanation of how things are, the student of Zen hears sayings called koan, often somewhat paradoxical in character, that come from those who are enlightened:

  • “There are no CTRL and META.”
  • “If you meet an Emacs you dislike, kill it.”
  • “No one knows Emacs.”
  • “One can only learn Emacs by living within it.”
  • “To know Org mode is to know oneself.”

It is important to realise that koan are intended to move you off of one path and onto another. They are not just attempts to mystify you. For example, the first proverb is in regards to newcomers complaining about too many keybinings —eventually it's muscle memory—, whereas the second is about using the right tool for the right task —Emacs is not for everyone. The fourth is, well, Emacs is an operating system.

1.5. Possibly Interesting Reads

—If eye-candy, a sleek and beautiful GUI, would entice you then consider starting with spacemacs. Here's a helpful installation video, after which you may want to watch Org-mode in Spacemacs tutorial—

Remember: Emacs is a flexible platform for developing end-user applications; e.g., this configuration file is at its core an Emacs Lisp program that yields the editor of my dreams –it encourages me to grow and to be creative, and I hope the same for all who use it; moreover, it reflects my personality such as what I value and what I neglect in my workflow.

I’m stunned that you, as a professional software engineer, would eschew inferior computer languages that hinder your ability to craft code, but you put up with editors that bind your fingers to someone else’s accepted practice. ---Howard Abrams

1.6. Fun commands to try out

Finally, here's some fun commands to try out:

  • M-x doctor —generalising the idea of rubber ducks
  • M-x tetris or M-x gomoku or M-x snake—a break with a classic
    • C-u 𝓃 M-x hanoi for the 𝓃-towers of Hanoi
  • M-x butterfly —in reference to “real programmers”

A neat way to get started with Emacs is to solve a problem you have, such as taking notes or maintaining an agenda —both with Org-mode.

Before we get started…

1.7. What Does Literate Programming Look Like?

Briefly put, literate programming in Emacs allows us to evaluate source code within our text files, then using the results as values in other source blocks. When presenting an algorithm, we can talk it out, with a full commentary thereby providing ‘reproducible research’: Explorations and resulting algorithms are presented together in a natural style.

literate-programming.png

⟨ This image was created in org-mode; details below or by looking at the source file 😉 ⟩

Here's an example of showing code in a natural style, but having the resulting code appear in a style amicable to a machine. Here's what you type:

It's natural to decompose large problems,
#+begin_src haskell :noweb-ref defn-of-f :tangle no
f = h ∘ g
#+end_src

But we need to define $g$ and $h$ /beforehand/ before we can use them. Yet it's
natural to “motivate” their definitions ---rather than pull a rabbit out of
hat. Org lets us do that!

Here's one definition,
#+begin_src haskell :noweb-ref code-from-other-places :tangle no
g = ⋯
#+end_src

then the other.
#+begin_src haskell :noweb-ref code-from-other-places :tangle no
h = ⋯
#+end_src

Of-course, we might also want a preamble:
#+BEGIN_SRC haskell :tangle myprogram.hs
import ⋯
#+END_SRC

We can now tangle together the tagged code blocks in the order we want.
#+BEGIN_SRC haskell :tangle myprogram.hs :comments none :noweb yes
<<code-from-other-places>>
<<defn-of-f>>
#+END_SRC
( You can press “C-c C-v C-v” to see what this block expands into! )

Now C-c C-v C-t (org-babel-tangle) yields a file named myprogram.hs with contents in an order amicable to a machine.

import 

g = 
h = 
f = h  g

Interestingly, unlike certain languages, Haskell doesn't care too much about declaration order.

Warning! If we have different language blocks tangled to the same file, then they are tangled alphabetically —e.g., if one of the blocks is marked emacs-lisp then its contents will be the very first one in the resulting source file, since emacs-lisp begins with e which is alphabetically before h of haskell.

1.8. Why a monolithic configuration?

Why am I keeping my entire configuration —from those involving cosmetics & prose to those of agendas & programming— in one file? Being monolithic —“a large, mountain-sized, indivisible block of stone”— is generally not ideal in nearly any project: E.g., a book is split into chapters and a piece of software is partitioned into modules. Using Org-mode, we can still partition our setup while remaining in one file. An Emacs configuration is a personal, leisurely project, and one file is a simple architecture: I don't have to worry about many files and the troubles of moving content between them; instead, I have headings and move content almost instantaneously —org-refile by pressing w at the start of the reader. Moreover, being one file, it is easy to distribute and to extract artefacts from it —such as the README for Github, the HTML for my blog, the colourful PDF rendition, and the all-important Emacs Lisp raw code file. Moreover, with a single # I can quickly comment out whole sections, thereby momentarily disabling features.

There's no point in being modular if there's nothing explaining what's going on, so I document.

The section of this read further argues the benefits of maintaining literate, and monolithic, configuration files. As a convention, I will try to motivate the features I set up and I will prefix my local functions with, well, my/ —this way it's easy to see all my defined functions, and this way I cannot accidentally shadow existing utilities. Moreover, besides browsing the web, I do nearly everything in Emacs and so the start-up time is unimportant to me: Once begun, I have no intention of spawning another instance nor closing the current one. ( Upon an initial startup using this configuration, it takes a total of 121 seconds to install all the packages featured here. )

Enjoy!

2. Booting Up

Let's decide on where we want to setup our declarations for personalising Emacs to our needs. Then, let's bootstrap Emacs' primintive packaging mechanism with a slick interface —which not only installs Emacs packages but also programs at the operating system level, all from inside Emacs! Finally, let's declare who we are and use that to setup Emacs email service.

2.1.   ~/.emacs vs. init.org

Emacs is extensible: When Emacs is started, it tries to load a user's Lisp program known as an initialisation (‘init’) file which specifies how Emacs should look and behave for you. Emacs looks for the init file using the filenames ~/.emacs.el, ~/.emacs, or ~/.emacs.d/init.el —it looks for the first one that exists, in that order; at least it does so on my machine. Below we'll avoid any confusion by ensuring that only one of them is in our system. Regardless, execute C-h o user-init-file to see the name of the init file loaded. Having no init file is tantamount to have an empty init file.

  • One can read about the various Emacs initialisation files online or within Emacs by the sequence C-h i m emacs RET i init file RET.
  • A friendly tutorial on ‘beginning a .emacs file’ can be read online or within Emacs by C-h i m emacs lisp intro RET i .emacs RET.
  • After inserting some lisp code, such as (set-background-color "salmon"), and saving, one can load the changes with M-x eval-buffer, eval-buffer.
  • In a terminal, use emacs -Q to open emacs without any initialisation files.

Besides writing Lisp in an init file, one may use Emacs' customisation interface, M-x customize: Point and click to change Emacs to your needs. The resulting customisations are, by default, automatically thrown into your init file ---~/.emacs is created for you if you have no init file. This interface is great for beginners.

We shall use ~/.emacs.d/init.el as the initialisation file so that all of our Emacs related files live in the same directory: ~/.emacs.d/.

A raw code file is difficult to maintain, especially for a large system such as Emacs. Instead, we're going with a ‘literate programming’ approach: The intialisation configuration is presented in an essay format, along with headings and subheadings, intended for consumption by humans such as myself, that, incidentally, can be ‘tangled’ into a raw code file that is comprehensible by a machine. We achieve this goal using org-mode ---Emacs' killer app— which is discussed in great detail later on.

/Adventure time!/ “Honey, where's my init?”

Let's use the three possible locations for the initialisation files to explore how Emacs finds them. Make the following three files.

~/.emacs.el

;; Emacs looks for this first;
(set-background-color "chocolate3")
(message-box ".emacs.el says hello")

~/.emacs

;; else; looks for this one;
(set-background-color "plum4")
(message-box ".emacs says hello")

~/.emacs.d/init.el

;; Finally, if neither are found; it looks for this one.
(set-background-color "salmon")
(message-box ".emacs.d/init.el says hello")

Now restart your Emacs to see how there super tiny initilaisation files affect your editor. Delete some of these files in-order for others to take effect!

Adventure time! Using Emacs’ Easy Customisation Interface

We have chosen not to keep configurations in ~/.emacs since Emacs may explicitly add, or alter, code in it.

Let's see this in action!

Execute the following to see additions to the ~/.emacs have been added by ‘custom’.

  1. M-x customize-variable RET line-number-mode RET
  2. Then press: toggle, state, then 1.
  3. Now take a look: C-x C-f ~/.emacs
Support for ‘Custom’

Let the Emacs customisation GUI insert configurations into its own file, not touching or altering my initialisation file. For example, I tend to have local variables to produce README.org's and other matters, so Emacs' Custom utility will remember to not prompt me each time for the safety of such local variables.

(setq custom-file "~/.emacs.d/custom.el")
(ignore-errors (load custom-file)) ;; It may not yet exist.

2.2. Who am I?

Let's set the following personal Emacs-wide variables —to be used locations such as email.

(setq user-full-name    "Musa Al-hassy"
      user-mail-address "alhassy@gmail.com")

For some fun, run this cute method.

(animate-birthday-present user-full-name)

2.3. Emacs Package Manager

There are a few ways to install packages —run C-h C-e for a short overview. The easiest, for a beginner, is to use the command package-list-packages then find the desired package, press i to mark it for installation, then install all marked packages by pressing x.

  • Interactively: M-x list-packages to see all melpa packages that can install
    • Press Enter on a package to see its description.
  • Or more quickly, to install, say, unicode fonts: M-x package-install RET unicode-fonts RET.

“From rags to riches”: Recently I switched to Mac —first time trying the OS. I had to do a few package-install's and it was annoying. I'm looking for the best way to package my Emacs installation —including my installed packages and configuration— so that I can quickly install it anywhere, say if I go to another machine. It seems use-package allows me to configure and auto install packages. On a new machine, when I clone my .emacs.d and start Emacs, on the first start it should automatically install and compile all of my packages through use-package when it detects they're missing. ♥‿♥

First we load package, the built-in package manager. It is by default only connected to the GNU ELPA (Emacs Lisp Package Archive) repository, so we extended it with other popular repositories; such as the much larger MELPA (Milkypostman's ELPA) —it builds packages directly from the source-code repositories of developers rather than having all packages in one repository.

;; Make all commands of the “package” module present.
(require 'package)

;; Internet repositories for new packages.
(setq package-archives '(("gnu"    . "http://elpa.gnu.org/packages/")
                         ("nongnu" . "https://elpa.nongnu.org/nongnu/")
                         ("melpa"  . "http://melpa.org/packages/")))

;; Update local list of available packages:
;; Get descriptions of all configured ELPA packages,
;; and make them available for download.
(package-refresh-contents)
  • All installed packages are placed, by default, in ~/.emacs.d/elpa.
  • Neato: If one module requires others to run, they will be installed automatically.

The declarative configuration tool use-package is a macro/interface that manages our packages and the way they interact.

(unless (package-installed-p 'use-package)
  (package-install 'use-package))
(require 'use-package)

We can now invoke (use-package XYZ :ensure t) which should check for the XYZ package and makes sure it is accessible. If the file is not on our system, the :ensure t part tells use-package to download it —using the built-in package manager— and place it somewhere accessible, in ~/.emacs.d/elpa/ by default. By default we would like to download packages, since I do not plan on installing them manually by downloading Lisp files and placing them in the correct places on my system.

(setq use-package-always-ensure t)

Notice that use-package allows us to tersely organise a package's configuration —and that it is not a package manger, but we can make it one by having it automatically install modules, when needed, using :ensure t.

Super Simple ‘use-package’ Mini-tutorial

Here are common keywords we will use, in super simplified terms.

  • :init f₁ … fₙ Always executes code forms fᵢ before loading a package.
  • :diminish str Uses optional string str in the modeline to indicate this module is active. Things we use often needn't take real-estate down there and so no we provide no str.
  • :config f₁ … fₙ Only executes code forms fᵢ after loading a package.

    The remaining keywords only take affect after a module loads.

  • :bind ((k₁ . f₁) … (kₙ . fₙ) Lets us bind keys kᵢ, such as "M-s o", to functions, such as occur.
    • When n = 1, the extra outer parenthesis are not necessary.
  • :hook ((m₁ … mₙ) . f) Enables functionality f whenever we're in one of the modes mᵢ, such as org-mode. The . f, along with the outermost parenthesis, is optional and defaults to the name of the package —Warning: Erroneous behaviour happens if the package's name is not a function provided by the package; a common case is when package's name does not end in -mode, leading to the invocation ((m₁ … mₙ) . <whatever-the-name-is>-mode) instead.

    Additionally, when n = 1, the extra outer parenthesis are not necessary.

    Outside of use-package, one normally uses a add-hook clause. Likewise, an ‘advice’ can be given to a function to make it behave differently —this is known as ‘decoration’ or an ‘attribute’ in other languages.

  • :custom (k₁ v₁ d₁) … (kₙ vₙ dₙ) Sets a package's custom variables kᵢ to have values vᵢ, along with optional user documentation dᵢ to explain to yourself, in the future, why you've made this decision.

    This is essentially setq within :config.

  • Use the standalone keyword :disabled to turn off loading a module that, say, you're not using anymore.

We now bootstrap use-package.

The use of :ensure t only installs absent modules, but it does no updating. Let's set up an auto-update mechanism.

(use-package auto-package-update
  :config
  ;; Delete residual old versions
  (setq auto-package-update-delete-old-versions t)
  ;; Do not bother me when updates have taken place.
  (setq auto-package-update-hide-results t)
  ;; Update installed packages at startup if there is an update pending.
  (auto-package-update-maybe))

Here's another example use of use-package. Later on, I have a “show recent files pop-up” command set to C-x C-r; but what if I forget? This mode shows me all key completions when I type C-x, for example. Moreover, I will be shown other commands I did not know about! Neato :-)

;; Making it easier to discover Emacs key presses.
(use-package which-key
  :diminish
  :config (which-key-mode)
          (which-key-setup-side-window-bottom)
          (setq which-key-idle-delay 0.05))

⟨ Honestly, I seldom even acknowledge this pop-up; but it's always nice to show to people when I'm promoting Emacs. ⟩

Above, the :diminish keyword indicates that we do not want the mode's name to be shown to us in the modeline —the area near the bottom of Emacs. It does so by using the diminish package, so let's install that.

(use-package diminish)

Here are other packages that I want to be installed onto my machine.

;; Haskell's cool
(use-package haskell-mode :defer t)

;; Lisp libraries with Haskell-like naming.
(use-package dash)    ;; “A modern list library for Emacs”
(use-package s   )    ;; “The long lost Emacs string manipulation library”.

;; Let's use the “s” library.
(defvar my/personal-machine?
  (not (s-contains? "work-machine" (shell-command-to-string "scutil --get ComputerName")))
  "Is this my personal machine, or my work machine?

 At one point, on my work machine I run the following command to give the machine a sensible name.

     sudo scutil --set ComputerName work-machine
     dscacheutil -flushcache")

(defvar my/work-machine? (not my/personal-machine?))

;; Library for working with system files;
;; e.g., f-delete, f-mkdir, f-move, f-exists?, f-hidden?
(use-package f)

Note:

  • dash: “A modern list library for Emacs”
    • E.g., (--filter (> it 10) (list 8 9 10 11 12))
  • s: “The long lost Emacs string manipulation library”.
    • E.g., s-trim, s-replace, s-join.

Remember that snippet for undo-tree in the introductory section? Let's activate it now, after use-package has been setup.

  ;; Allow tree-semantics for undo operations.
  (use-package undo-tree
    :diminish                       ;; Don't show an icon in the modeline
    :bind ("C-x u" . undo-tree-visualize)
    :hook (org-mode . undo-tree-mode) ;; For some reason, I need this. FIXME.
    :config
      ;; Always have it on
      (global-undo-tree-mode)

      ;; Each node in the undo tree should have a timestamp.
      (setq undo-tree-visualizer-timestamps t)

      ;; Show a diff window displaying changes between undo nodes.
      (setq undo-tree-visualizer-diff t))

  ;; Execute (undo-tree-visualize) then navigate along the tree to witness
  ;; changes being made to your file live!

DRY: Don 't Repeat Yourself!

In the HTML export, above it looks like I just copy-pasted the undo tree setup from earlier, but that is not the case! All I did was declare to Org that I'd like that named snippet to be tangled now, here in the resulting code file.

#+begin_src emacs-lisp :noweb yes
  <<undo-tree-setup>>
#+end_src

You can press C-c C-v C-v, org-babel-expand-src-block, to see what this block expands into —which is what was shown above.


Quelpa allows us to build Emacs packages directly from source repositories. It derives its name from the German word Quelle, for souce [code], adjoined to ELPA. Its use-package interface allows us to use use-package like normal but when we want to install a file from souce we use the keyword :quelpa.

(use-package quelpa
  :custom (quelpa-upgrade-p t "Always try to update packages")
  :config
  ;; Get ‘quelpa-use-package’ via ‘quelpa
  (quelpa
   '(quelpa-use-package
     :fetcher git
     :url "https://github.com/quelpa/quelpa-use-package.git"))
  (require 'quelpa-use-package))

Let's use this to obtain an improved info-mode from the EmacsWiki. [Disabled for now]

(use-package info+
  :disabled
  :quelpa (info+ :fetcher wiki :url "https://www.emacswiki.org/emacs/info%2b.el"))

2.4. Installing OS packages, and automatically keeping my system up to data, from within Emacs

Sometimes Emacs packages depend on existing system binaries, use-package let's us ensure these exist using the :ensure-system-package keyword extension.

  • This is like :ensure t but operates at the OS level and uses your default OS package manager.
  • It has multiple features.

Let's obtain the extension.

;; Auto installing OS system packages
(use-package use-package-ensure-system-package
  :config (system-packages-update))

;; Please don't bother me when shell buffer names are in use, just make a new
;; buffer.
(setq async-shell-command-buffer 'new-buffer)

;; Display the output buffer for asynchronous shell commands only when the
;; command generates output.
(setq async-shell-command-display-buffer nil)

;; Don't ask me if I want to kill a buffer with a live process attached to it;
;; just kill it please.
(setq kill-buffer-query-functions
  (remq 'process-kill-buffer-query-function
         kill-buffer-query-functions))

;; Ensure our operating system is always up to date.
;; This is run whenever we open Emacs & so wont take long if we're up to date.
;; It happens in the background ^_^
;;
;; After 5 seconds of being idle, after starting up.


(defvar my/installed-packages
  (shell-command-to-string "brew list")
   "What is on my machine already?

Sometimes when I install a GUI based application and do not have access to it everywhere in my path,
it may seem that I do not have that application installed. For instance,
   (system-packages-package-installed-p \"google-chrome\")
returns nil, even though Google Chrome is on my machine.

As such, we advise the `system-packages-ensure' installtion method to only do
installs of pacakges that are not in our `my/installed-packages' listing.
")
(advice-add 'system-packages-ensure   :before-until (lambda (pkg) (s-contains-p pkg my/installed-packages)))

After an update to Mac OS, one may need to restore file system access privileges to Emacs.

Here's an example use for Emacs packages that require OS packages:

(shell-command-to-string "type rg") ;; ⇒ rg not found
(use-package rg
  :ensure-system-package rg) ;; ⇒ There's a buffer *system-packages*
                             ;;   installing this tool at the OS level!

If you look at the *Messages* buffer, via C-h e, on my machine it says brew install rg: finished —it uses brew which is my OS package manager!

The extension makes use of system-packages; see its documentation to learn more about managing installed OS packages from within Emacs. This is itself a powerful tool, however it's interface M-x system-packages-install leaves much to be desired —namely, tab-compleition listing all available packages, seeing their descriptions, and visiting their webpages. This is remedied by M-x helm-system-packages then RET to see a system package's description, or TAB for the other features! This is so cool!

;; An Emacs-based interface to the package manager of your operating system.
(use-package helm-system-packages)

The Helm counterpart is great for discovarability, whereas the plain system-packages is great for programmability.

(setq system-packages-noconfirm :do-not-prompt-me-about-confirms)

;; After 1 minute after startup, kill all buffers created by ensuring system
;; packages are present.
(run-with-timer 60 nil
 (lambda () (kill-matching-buffers ".*system-packages.*" t :kill-without-confirmation)))

It is tedious to arrange my program windows manually, and as such I love tiling window managers, which automatically arrange them. I had been using xmonad until recently when I obtained a Mac machine and now use Amethyst —“Tiling window manager for macOS along the lines of xmonad.”

;; Unlike the Helm variant, we need to specify our OS pacman.
(when (eq system-type 'darwin)
  (setq system-packages-package-manager 'brew))

;; If the given system package doesn't exist; install it.
(when (eq system-type 'darwin)
  (system-packages-ensure "amethyst")) ;; This is a MacOS specific package.

(ignore-errors (system-packages-ensure "google-chrome")) ;; My choice of web browser
(system-packages-ensure "microsoft-teams") ;; For remote work meetings

;; Gif maker; needs privileges to capture screen.
;;
;; ⇒ Move the screen capture frame while recording.
;; ⇒ Pause and restart recording, with optional inserted text messages.
;; ⇒ Global hotkey (shift+space) to toggle pausing while recording
(system-packages-ensure "licecap") ;; Use: ⌘-SPACE licecap

;; Pack, ship and run any application as a lightweight container
(system-packages-ensure "docker")
;; Free universal database tool and SQL client
(system-packages-ensure "dbeaver-community")
;; Kubernetes IDE
(system-packages-ensure "lens")
;; Platform built on V8 to build network applications
;; Also known as: node.js, node@16, nodejs, npm
(system-packages-ensure "node") ;; https://nodejs.org/
;; Nice: https://nodesource.com/blog/an-absolute-beginners-guide-to-using-npm/
;; Manage multiple Node.js versions
(shell-command "curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash")
;; According to https://github.com/nvm-sh/nvm, nvm shouldn't be installed via brew.

#+ENDSRC

Amethyst requires some more setup: Open its preferences, then…

  • Then select: Mouse: Focus follows mouse.
  • Also: Shortcuts, then disable ‘increase/decrease main pane count’ bindings since they override the beloved Emacs M-<,> keys.

Neato! Now I can live in Emacs even more ^_^


(Open Scripting Architecture (OSA) Scripts) Amethyst is great, but it has a problem of randomly not working. Unfortunatley it has no command line interface, so let's make one in Emacs: Now ⌘-a r relaunches Amethyst.

Details
(defun ⌘-quit (app)
  "Kill application APP; e.g., “amethyst” or “Safari”"
  (shell-command (format "osascript -e 'quit app \"%s\"'" app)))

(defun ⌘-open (app)
 "Open application APP; e.g., “amethyst” or “Safari”"
  (async-shell-command (format "osascript -e 'launch app \"%s\"'" app)))

;; (bind-key "???-a r" #'my/relaunch-amethyst)
(defun my/relaunch-amethyst () (interactive)
       (⌘-quit "amethyst")
       (⌘-open "amethyst"))

We use the osascript command to tell the System Events application to issue keystrokes to other applications. I found out about by Googling “How to send keystrokes from terminal”.

;; (bind-key "???-a c" #'amethyst/cycle-layout)
(defun amethyst/cycle-layout ()
  (interactive)
  (shell-command "osascript -e 'tell application \"System Events\" to keystroke space using {shift down, option down}'"))

If you get:

36:51: execution error: System Events got an error: osascript is not allowed to send keystrokes. (1002)

Then: Go to Security & Privacy -> Privacy tab -> Accessibility -> Add osascript (/usr/bin/osascript)

You may need to restart Emacs.

Reads:

I enter “⌘” using a TeX input method setup below (called “Agda”).

2.5. Syncing to the System's $PATH

For one reason or another, on OS X it seems that an Emacs instance begun from the terminal may not inherit the terminal's environment variables, thus making it difficult to use utilities like pdflatex when Org-mode attempts to produce a PDF.

(use-package exec-path-from-shell
  :init
  (when (memq window-system '(mac ns x))
    (exec-path-from-shell-initialize)))

See the exec-path-from-shell documentation for setting other environment variables.

2.6. Restarting Emacs —Keeping buffers open across sessions?

Sometimes I wish to close then reopen Emacs; unsurprisingly someone's thought of implementing that.

;; Provides only the command “restart-emacs”.
(use-package restart-emacs
  ;; If I ever close Emacs, it's likely because I want to restart it.
  :bind ("C-x C-c" . restart-emacs)
  ;; Let's define an alias so there's no need to remember the order.
  :config (defalias 'emacs-restart #'restart-emacs))

The following is disabled. I found it a nuisance to have my files open across sessions —If I'm closing Emacs, it's for a good reason.

;; Keep open files open across sessions.
(desktop-save-mode 1)
(setq desktop-restore-eager 10)

Instead, let's try the following: When you visit a file, point goes to the last place where it was when you previously visited the same file.

(setq-default save-place  t)
(setq save-place-file "~/.emacs.d/etc/saveplace")

2.7.  “Being at the Helm” —Completion & Narrowing Framework

Whenever we have a choice to make from a list, Helm provides possible completions and narrows the list of choices as we type. This is extremely helpful for when switching between buffers, C-x b, and discovering & learning about other commands! E.g., press M-x to see recently executed commands and other possible commands! “Fuzzy finding”: Press M-x and just start typing, methods mentioning what you've typed are suddenly listed! Moreover, C-c i (helm-imenu) will show you the headers in an Org file or the top-level variables/functions/types when programming. Finally, whenever a Helm session has started, toggle follow-mode with C-c C-f to obtain contextual-awareness; e.g., C-c i RET C-c C-f will change your screen as you scroll through the menu. (A killer feature is helm-do-grep-ag which will do a search in your whole project, file tree).

Remembrance comes with time, until then ask Emacs!

Try and be grateful!

(use-package helm
 :diminish
 :init (helm-mode t)
 :bind (("M-x"     . helm-M-x)
        ("C-x C-f" . helm-find-files)
        ("C-x b"   . helm-mini)     ;; See buffers & recent files; more useful.
        ("C-x r b" . helm-filtered-bookmarks)
        ("C-x C-r" . helm-recentf)  ;; Search for recently edited files
        ("C-c i"   . helm-imenu) ;; C.f. “C-x t m” (imenu-list)
        ;; ("C-u C-c i" . imenu-list)  ;; TODO FIXME  Key sequence C-u C-c i starts with non-prefix key C-u
        ("C-h a"   . helm-apropos)
        ;; Look at what was cut recently & paste it in.
        ("M-y" . helm-show-kill-ring)
        ("C-x C-x" . helm-all-mark-rings)
        :map helm-map
        ;; We can list ‘actions’ on the currently selected item by C-z.
        ("C-z" . helm-select-action)
        ;; Let's keep tab-completetion anyhow.
        ("TAB"   . helm-execute-persistent-action)
        ("<tab>" . helm-execute-persistent-action)))

;; Show me nice file icons when using, say, “C-x C-f” or “C-x b”
(use-package helm-icons
  :custom (helm-icons-provider 'all-the-icons)
  :config (helm-icons-enable))

Helm provides generic functions for completions to replace tab-completion in Emacs with no loss of functionality.

  • The execute-extended-command, the default “M-x”, is replaced with helm-M-x which shows possible command completions.

    Likewise with apropos, which is helpful for looking up commands. It shows all meaningful Lisp symbols whose names match a given pattern.

  • The ‘Helm-mini’, C-x b, shows all buffers, recently opened files, bookmarks, and allows us to create new bookmarks and buffers!
  • The ‘Helm-imenu’, C-c i, yields a a menu of all “top-level items” in a file; e.g., functions and constants in source code or headers in an org-mode file.

    ⟳ Nifty way to familarise yourself with a new code base, or one from a while ago.

  • When Helm is active, C-x lists possible course of actions on the currently selected item.

When helm-mode is enabled, even help commands make use of it. E.g., C-h o runs describe-symbol for the symbol at point, and C-h w runs where-is to find the key binding of the symbol at point. Both show a pop-up of other possible commands.

Here's a nifty tutorial: A package in a league of its own: Helm

Let's ensure C-x b shows us: Current buffers, recent files, and bookmarks —as well as the ability to create bookmarks, which is via C-x r b manually. For example, I press C-x b then type any string and will have the option of making that a bookmark referring to the current location I'm working in, or jump to it if it's an existing bookmark, or make a buffer with that name, or find a file with that name.

(setq helm-mini-default-sources '(helm-source-buffers-list
                                    helm-source-recentf
                                    helm-source-bookmarks
                                    helm-source-bookmark-set
                                    helm-source-buffer-not-found))

Incidentally, Helm even provides an interface for the top program via helm-top. It also serves as an interface to popular search engines and over 100 websites such as google, stackoverflow, ctan, and arxiv.

(system-packages-ensure "surfraw")
; ⇒  “M-x helm-surfraw” or “C-x c s”

If we want to perform a google search, with interactive suggestions, then invoke helm-google-suggest —which can be acted for other serves, such as Wikipedia or Youtube by C-z. For more google specific options, there is the google-this package.

Let's switch to a powerful searching mechanism – helm-swoop. It allows us to not only search the current buffer but also the other buffers and to make live edits by pressing C-c C-e when a search buffer exists. Incidentally, executing C-s on a word, region, will search for that particular word, region; then make changes with C-c C-e and apply them by C-x C-s.

(use-package helm-swoop
  :bind  (("C-s"     . 'helm-swoop)           ;; search current buffer
          ("C-M-s"   . 'helm-multi-swoop-all) ;; Search all buffer
          ;; Go back to last position where ‘helm-swoop’ was called
          ("C-S-s" . 'helm-swoop-back-to-last-point)
          ;; swoop doesn't work with PDFs, use Emacs' default isearch instead.
          ; :map pdf-view-mode-map ("C-s" . isearch-forward)
          )
  :custom (helm-swoop-speed-or-color nil "Give up colour for speed.")
          (helm-swoop-split-with-multiple-windows nil "Do not split window inside the current window."))
  • C-u 𝓃 C-s does a search but showing 𝓃 contextual lines!
  • helm-multi-swoop-all, C-M-s, lets us grep files anywhere!

Lets also use helm-do-grep-ag (C-x c M-g a) search all files in the current directory for a particular (regexp) string

  • Shows matches live as you type
  • Very helpful when looking for a definition of something
(system-packages-ensure "ag")

Marking my place when I jump around Let's use M-m to get a nice menu of places we've been recently.

;; Save/mark a location with “C-u M-m”, jump back to it with “M-m”.
(bind-key* "M-m"
           (lambda ()
             (interactive)
             (if (not current-prefix-arg)
                 (helm-mark-ring)
               (push-mark)
               (message "[To return to this location, press M-m] ∷ %s"
                        (s-trim (substring-no-properties (thing-at-point 'line)))))))

Finally, note that there is now a M-x helm-info command to show documentation, possibly with examples, of the packages installed. For example, M-x helm-info RET dash RET -parition RET to see how the parition function from the dash library works via examples ;-)

;; Make `links' from elisp symbols (quoted functions, variables and fonts) in Gnu-Emacs Info viewer to their help documentation.
(use-package inform
  :config (require 'inform))

2.8. Org-Mode Administrivia

Let's conclude this ‘boot-up’ by getting Emacs' killer app, Org-mode, setup; along with the extras that allow us to ignore heading names, but still utilise their contents —e.g., such as a heading named ‘preamble’ that contains org-mode setup for a file.

(use-package emacs
    :ensure org-contrib
    :diminish org-indent-mode
    :config (require 'ox-extra)
            (ox-extras-activate '(ignore-headlines)))

org-plus-contrib contain the files that are included with Emacs plus all contributions from the org-mode repository.

  • Use the :ignore: tag on headlines you'd like to have ignored, while not ignoring their content.
  • Use the :noexport: tag to omit a headline and its contents.
;; Replace the content marker, “⋯”, with a nice unicode arrow.
(setq org-ellipsis " ⤵")

;; Fold all source blocks on startup.
(setq org-hide-block-startup t)

;; Lists may be labelled with letters.
(setq org-list-allow-alphabetical t)

;; Avoid accidentally editing folded regions, say by adding text after an Org “⋯”.
(setq org-catch-invisible-edits 'show)

;; I use indentation-sensitive programming languages.
;; Tangling should preserve my indentation.
(setq org-src-preserve-indentation t)

;; Tab should do indent in code blocks
(setq org-src-tab-acts-natively t)

;; Give quote and verse blocks a nice look.
(setq org-fontify-quote-and-verse-blocks t)

;; Pressing ENTER on a link should follow it.
(setq org-return-follows-link t)

I rarely use tables, but here is a useful Org-Mode Table Editing Cheatsheet and a friendly tutorial.

Moreover, since I end up using org-mode most of the time, let's make that the default mode.

(setq initial-major-mode 'org-mode)

Finally, let's get some extra Org-mode mark-up goodies, such as kbd:C-c_C-e which renders as C-c C-e. Documentations and screenshots at: https://alhassy.github.io/org-special-block-extras/

;; TODO org-special-block-extras.el:681:1:Error: Symbol’s value as variable is void: o--supported-blocks
;;
(when nil use-package org-special-block-extras
  :hook (org-mode . org-special-block-extras-mode)
  :custom
    ;; The places where I keep my ‘#+documentation’
    (org-special-block-extras--docs-libraries
     '("~/org-special-block-extras/documentation.org"))
    ;; Disable the in-Emacs fancy-links feature?
    (org-special-block-extras-fancy-links
     '(elisp badge kbd link-here doc tweet))
    ;; 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
    (org-special-block-extras-link-twitter-excitement
     "This looks super neat (•̀ᴗ•́)و:")
  :config
  ;; Use short names like ‘defblock’ instead of the fully qualified name
  ;; org-special-block-extras--defblock
    (org-special-block-extras-short-names))

;; Let's execute Lisp code with links, as in “elisp:view-hello-file”.
(setq org-confirm-elisp-link-function nil)

2.9. Password-locking files —“encryption”

With the following incantation, we name our files 𝒳.𝒴.gpg where 𝒳 is the file name and 𝒴 is the usual extension, then upon save we will be prompted for an encryption method, we can press Enter on OK to just provide a password for that file. You can open that file without the passphrase for a limited amount of time —i.e., it's cached, saved, for your current computing session until logout— or force authentication by invoking gpgconf --kill gpg-agent.

(system-packages-ensure "gnupg") ;; i.e.,  brew install gnupg

;; “epa” ≈ EasyPG Assistant

;; Need the following in init to have gpg working fine:
;; force Emacs to use its own internal password prompt instead of an external pin entry program.
(setq epa-pinentry-mode 'loopback)

;; https://emacs.stackexchange.com/questions/12212/how-to-type-the-password-of-a-gpg-file-only-when-opening-it
(setq epa-file-cache-passphrase-for-symmetric-encryption t)
;; No more needing to enter passphrase at each save ^_^
;;
;; Caches passphrase for the current emacs session?

The purpose of encrypting a file is so that an adversary —e.g., an immoral computer administrator or a thief who stole your computer— will have to spend so much decrypting the data than the data is actually worth. As such, one uses GPG keys…!

GPG Details

Trivia: “gpg” stands for GnuPG, which abbreviates GNU Privacy Guard.

To obtain encrypted messages from others, you will need a “GPG key”: They use your “public key” (which others can see) to encrypt a file, which only you can open since you have the associated “private key” (which only you see).

Possibly interesting reads:

2.10. all-the-icons

 (use-package all-the-icons
    :config (all-the-icons-install-fonts 'install-without-asking))
;; (cl-defun all-the-icons-faicon (icon &rest _)
;;  #("" 0 1 (rear-nonsticky t display (raise -0.24) font-lock-face (:family "FontAwesome" :height 1.2) face (:family "FontAwesome" :height 1.2))))

2.11. Hydra: Supply a prefix only once

Hydras let us do “super temporary modal editing”

Sometimes we have keybindings that share a common prefix, say C-c j and C-c k, and we invoke them in an arbitrary sequence, it would be nice to invoke the shared prefix only once thereby having:

C-c j C-c j C-c k C-c k M-3 C-c j M-5 C-c k C-c jjkk3j5k
  • The “hydra-zoom” example from the documentation really showcases this utility.
  • After the prefix is supplied, all extensions are shown in a minibuffer.
;; Invoke all possible key extensions having a common prefix by
;; supplying the prefix only once.
(use-package hydra)

From the Hydra repository is a ‘description for poets’:

Once you summon the Hydra through the prefixed binding (the body + any one head), all heads can be called in succession with only a short extension.

The Hydra is vanquished once Hercules, any binding that isn't the Hydra's head, arrives. Note that Hercules, besides vanquishing the Hydra, will still serve his original purpose, calling his proper command. This makes the Hydra very seamless, it's like a minor mode that disables itself auto-magically.

⇒ The Hydra Wiki has many example hydras for common uses cases ⇐

Below are two examples; one to simplify textual navigation and another for window navigation. Yet another possible hydra would be to avoid remembering word operations, such as copying a word, upcasing it, killing a word from anywhere within it —in contrast kill-word kills to the end of the word—, etc. Likewise for line operations, such as copying a line from anywhere in it. See below for another small and useful example.

When there are multiple actions, it's nice to see such a menu displayed in the middle of the frame; so we use hydra-posframe. Moreover, it can be useful to group related actions under a common heading —e.g., textual navigation may occur at the line level or word level or screen level— we obtain a nice interface by declaraing hydras using pretty-hydra-define —this saves us the trouble of formating docstrings using classic hydra.

;; TODO Fix me, breaking Github Actions test setup
;; Show hydras overlyaed in the middle of the frame
;; (use-package hydra-posframe
;;   :quelpa (hydra-posframe :fetcher git :url
;;                           "https://github.com/Ladicle/hydra-posframe.git")
;;   :hook (after-init . hydra-posframe-mode)
;;   :custom (hydra-posframe-border-width 5))

;; Neato doc strings for hydras
(use-package pretty-hydra)

To actually define hydras, we make a helper function: my/defhydra —which combines defhydra and pretty-hydra-define.

Implementation
;; TODO convert my existing defhydras to my/defhydra.
(defmacro my/defhydra (key title icon-name &rest body)
"Make a hydra whose heads appear in a pretty pop-up window.
Heads are signalled by keywords and the hydra has an icon in its title.

KEY [String]: Global keybinding for the new hydra.

TITLE [String]: Either a string or a plist, as specified for pretty-hydra-define.
       The underlying Lisp function's name is derived from the TITLE;
       which is intentional since hydra's are for interactive, pretty, use.

       One uses a plist TITLE to specify what a hydra should do *before*
       any options, or to specify an alternate quit key (:q by default).

ICON-NAME [Symbol]: Possible FontAwesome icon-types: C-h v `all-the-icons-data/fa-icon-alist'.

BODY: A list of columns and entries. Keywords indicate the title
      of a column; 3-lists (triples) indicate an entry key and
      the associated operation to perform and, optionally, a name
      to be shown in the pop-up. See DEFHYDRA for more details.


For instance, the verbose mess:

    ;; Use ijkl to denote ↑←↓→ arrows.
    (global-set-key
     (kbd \"C-c w\")
     (pretty-hydra-define my/hydra/\\t\\tWindow\\ Adjustment
       ;; Omitting extra work to get an icon into the title.
       (:title \"\t\tWindow Adjustment\" :quit-key \"q\")
       (\"Both\"
        ((\"b\" balance-windows                 \"balance\")
         (\"s\" switch-window-then-swap-buffer  \"swap\"))
        \"Vertical adjustment\"
        ((\"h\" enlarge-window                  \"heighten\")
         (\"l\" shrink-window                   \"lower\"))
        \"Horizontal adjustment\"
        ((\"n\" shrink-window-horizontally      \"narrow\")
         (\"w\" enlarge-window-horizontally \"widen\" )))))

Is replaced by:

    ;; Use ijkl to denote ↑←↓→ arrows.
    (my/defhydra \"C-c w\" \"\t\tWindow Adjustment\" windows
       :Both
       (\"b\" balance-windows                 \"balance\")
       (\"s\" switch-window-then-swap-buffer  \"swap\")
       :Vertical_adjustment
       (\"h\" enlarge-window                  \"heighten\")
       (\"l\" shrink-window                   \"lower\")
       :Horizontal_adjustment
       (\"n\" shrink-window-horizontally      \"narrow\")
       (\"w\" enlarge-window-horizontally     \"widen\"))"
  (let* ((name (intern (concat "my/hydra/"
                              (if (stringp title)
                                  title
                                (plist-get title :title)))))
         (icon-face `(:foreground ,(face-background 'highlight)))
         (iconised-title
          (concat
           (when icon-name
             (concat
              (all-the-icons-faicon (format "%s" icon-name) :face icon-face :height 1.0 :v-adjust -0.1)
              " "))
           (propertize title 'face icon-face))))
    `(global-set-key
      (kbd ,key)
      (pretty-hydra-define ,name
        ,(if (stringp title)
             (list :title iconised-title
                   :quit-key "q")
           title)
        ,(thread-last body
           (-partition-by-header #'keywordp)
           (--map (cons (s-replace "_" " " (s-chop-prefix ":" (symbol-name (car it)))) (list (cdr it))))
           (-flatten-n 1))))))

2.11.1. Textual Navigation —“Look Ma, no CTRL key!”

Basic movement commands —without having to hold the control key!

C-n moves us to the next line, as it should; but it now also let's us press n,p,f,… for other movement commands. Unlisted keys insert themselves, whereas q close the pop-up menu.

(my/defhydra "C-n" "\t\t\t\t\tTextual Navigation" arrows
   :Line
   ("n" next-line)
   ("p" previous-line)
   ("a" beginning-of-line)
   ("e" move-end-of-line)
   ("g" goto-line)
   :Word
   ("f" forward-word "Next")
   ("b" backward-word "Previous")
   ("{" org-backward-element "Next Element")
   ("}" org-forward-element "Previous Element")
   :Screen
   ("v" scroll-up-command "Scroll Down")
   ("V" scroll-down-command "Scroll Up")
   ("l" recenter-top-bottom "Center Page")
   ("r" move-to-window-line-top-bottom "Relocate Point")
   ("m" helm-imenu "Textual Menu"))

Along with the “pop-up window”, this is a useful way to (re)learn about Emacs' features.

For “key-based navigation”, consider ‘EVIL-mode’ or ‘Spacemacs’, or ace-jump-mode (below).

Also …

;; C-n, next line, inserts newlines when at the end of the buffer
(setq next-line-add-newlines t)

2.11.2. Window Navigation

It can be difficult to remember the incantations to adjust windows, so we can make a hydra to alleviate the trouble.

;; Use ijkl to denote ↑←↓→ arrows.
(my/defhydra "C-c w" "\t\tWindow Adjustment" windows
   :Both
   ("b" balance-windows                 "balance")
   ("s" switch-window-then-swap-buffer  "swap")
   :Vertical_adjustment
   ("h" enlarge-window                  "heighten")
   ("l" shrink-window                   "lower")
   :Horizontal_adjustment
   ("n" shrink-window-horizontally      "narrow")
   ("w" enlarge-window-horizontally     "widen"))

;; Provides a *visual* way to choose a window to switch to.
;; (use-package switch-window :defer t)
;; :bind (("C-x o" . switch-window)
;;        ("C-x w" . switch-window-then-swap-buffer))

;; Have a thick ruler between vertical windows
(window-divider-mode)

2.12. Helpful Utilities & Shortcuts

Let's save a few precious seconds,

;; change all prompts to y or n
(fset 'yes-or-no-p 'y-or-n-p)

;; Make RETURN key act the same way as “y” key for “y-or-n” prompts.
;; E.g., (y-or-n-p "Happy?") accepts RETURN as “yes”.
(define-key y-or-n-p-map [return] 'act)

;; Enable all ‘possibly confusing commands’ such as helpful but
;; initially-worrisome “narrow-to-region”, C-x n n.
(setq-default disabled-command-function nil)

3. Staying Sane

3.1. Undo-tree: Very Local Version Control

undo-tree-visualize, C-x u, gives a visual representation of the current buffer's edit history.

  ;; Allow tree-semantics for undo operations.
  (use-package undo-tree
    :diminish                       ;; Don't show an icon in the modeline
    :bind ("C-x u" . undo-tree-visualize)
    :hook (org-mode . undo-tree-mode) ;; For some reason, I need this. FIXME.
    :config
      ;; Always have it on
      (global-undo-tree-mode)

      ;; Each node in the undo tree should have a timestamp.
      (setq undo-tree-visualizer-timestamps t)

      ;; Show a diff window displaying changes between undo nodes.
      (setq undo-tree-visualizer-diff t))

  ;; Execute (undo-tree-visualize) then navigate along the tree to witness
  ;; changes being made to your file live!

( We're just showing the <<undo-tree-setup>> from earlier since this is a good place for such a setup. More importantly, we are not copy-pasting the setup: It is written only once; in a single source of truth! )

;; By default C-z is suspend-frame, i.e., minimise, which I seldom use.
(global-set-key (kbd "C-z")
  (lambda () (interactive)
   (undo-tree-mode) ;; Ensure the mode is on
   (undo-tree-visualize)))

3.2. Automatic Backups

By default, Emacs saves backup files —those ending in ~— in the current directory, thereby cluttering it up. Let's place them in ~/.emacs.d/backups, in case we need to look for a backup; moreover, let's keep old versions since there's disk space to go around —what am I going to do with 500gigs when nearly all my ‘software’ is textfiles interpreted within Emacs 😼

;; New location for backups.
(setq backup-directory-alist '(("." . "~/.emacs.d/backups")))

;; Silently delete execess backup versions
(setq delete-old-versions t)

;; Only keep the last 1000 backups of a file.
(setq kept-old-versions 1000)

;; Even version controlled files get to be backed up.
(setq vc-make-backup-files t)

;; Use version numbers for backup files.
(setq version-control t)

Why backups? Sometimes I may forget to submit a file, or edit, to my version control system, and it'd be nice to be able to see a local automatic backup. Whenever ‘I need space,’ then I simply empty the backup directory, if ever. That the backups are numbered is so sweet ^_^

Like package installations, my backups are not kept in any version control system, like git; only locally.

Finally, let's not create .# files (crashes 'npm') nor bother confirming killing processes.

(setq confirm-kill-processes nil
      create-lockfiles nil)

3.2.1. What changed? —Walking through backups

Let's use an elementary diff system for backups: backup-walker essentially makes all our backups behave as if they were (implicitly) version controlled.

(use-package backup-walker
  :commands backup-walker-start)

In a buffer that corresponds to a file, invoke backup-walker-start to see a visual diff of changes between versions; then n and p to move between diffs. By default, you see the changes ‘backwards’: Red means delete these things to get to the older version; i.e., the red ‘-’ are newer items.

There is also diff-backup for comparing a file with its backup.

3.2.2. Save ≈ Backup

Emacs only makes a backup the very first time a buffer is saved; I'd prefer Emacs makes backups everytime I save! —If I saved, that means I'm at an important checkpoint, so please check what I have so far as a backup!

;; Make Emacs backup everytime I save

(defun my/force-backup-of-buffer ()
  "Lie to Emacs, telling it the curent buffer has yet to be backed up."
  (setq buffer-backed-up nil))

(add-hook 'before-save-hook  'my/force-backup-of-buffer)

;; [Default settings]
;; Autosave when idle for 30sec or 300 input events performed
(setq auto-save-timeout 30
      auto-save-interval 300)

It is intestesting to note that the above snippet could be modified to make our own backup system, were Emacs lacked one, by having our function simply save copies of our file —on each save— where the filename is augmented with a timestamp.

3.3.   magit —Emacs' porcelain interface to git

Let's setup an Emacs ‘porcelain’ interface to git —it makes working with version control tremendously convenient.

(Personal reminder: If using 2FA [two factor authentication], then when you do git operations, such as git push, you must use your PAT [personal access token] instead of your password! Also: Install refined-github: Browser extension that simplifies the GitHub interface and adds useful features!)

;; Bottom of Emacs will show what branch you're on
;; and whether the local file is modified or not.
(use-package magit
  :init (require 'magit-files)
  :bind (("C-c M-g" . magit-file-dispatch))
  :custom ;; Do not ask about this variable when cloning.
    (magit-clone-set-remote.pushDefault t))

Why use magit as the interface to the git version control system? In a magit buffer nearly everything can be acted upon: Press return, or space, to see details and tab to see children items, usually.

  • C-x g, magit-status, gives you a nice buffer with an overview of the Git repo that you're buffer is currently visiting.
  • C-c M-g, magit-file-dispatch, lets you invoke Git actions on the current file directly; e.g., following up with blame, log, diff, stage, or commit the current file.

    For ease, above, we have also bound this to C-c g —reminiscent of C-x g :smile:

    ;; When we invoke magit-status, show green/red the altered lines, with extra
    ;; green/red on the subparts of a line that got alerted.
    (system-packages-ensure "git-delta")
    (use-package magit-delta
      :hook (magit-mode . magit-delta-mode))
    
    ;; Don't forget to copy/paste the delta config into the global ~/.gitconfig file.
    ;; Copy/paste this: https://github.com/dandavison/delta#get-started
    
  • Blame, magit-blame, is super nice: The buffer gets annotations for each chunk of text, regarding who authoured it, when, and their commit title. Then q to quit the blame.

    Likewise, magit-log-buffer-file is super neat!

Super Simple ‘magit’ Mini-tutorial

Below is my personal quick guide to working with magit —for a full tutorial see jr0cket's blog.

dired
See the contents of a particular directory.
magit-init
Put a project under version control. The mini-buffer will prompt you for the top level folder version. A .git folder will be created there.
magit-status , C-x g
See status in another buffer. Press ? to see options, including:
g
Refresh the status buffer.
TAB
See collapsed items, such as what text has been changed.
q
Quit magit, or go to previous magit screen.
s

Stage, i.e., add, a file to version control. Add all untracked files by selecting the Untracked files title.

The staging area is akin to a pet store; commiting is taking the pet home.

k
Kill, i.e., delete a file locally.
K
This' (magit-file-untrack) which does git rm --cached.
i
Add a file to the project .gitignore file. Nice stuff =)
u
Unstage a specfif staged change highlighed by cursor. C-u s stages everything –tracked or not.
c
Commit a change.
  • A new buffer for the commit message appears, you write it then commit with C-c C-c or otherwise cancel with C-c C-k. These commands are mentioned to you in the minibuffer when you go to commit.
  • You can provide a commit to each altered chunk of text! This is super neat, you make a series of local such commits rather than one nebulous global commit for the file. The magit interface makes this far more accessible than a standard terminal approach!
  • You can look at the unstaged changes, select a region, using C-SPC as usual, and commit only that if you want!
  • When looking over a commit, M-p/n to efficiently go to previous or next altered sections.
  • Amend a commit by pressing a on HEAD.
d
Show differences, another d or another option.
  • This is magit! Each hunk can be acted upon; e.g., s or c or k ;-)
v
Revert a commit.
x
Undo last commit. Tantamount to git reset HEAD~ when cursor is on most recent commit; otherwise resets to whatever commit is under the cursor.
l
Show the log, another l for current branch; other options will be displayed.
  • Here space shows details in another buffer while cursour remains in current buffer and, moreover, continuing to press space scrolls through the other buffer! Neato.
P
Push.
F
Pull.
:
Execute a raw git command; e.g., enter whatchanged.

Notice that every time you press one of these commands, a ‘pop-up’ of realted git options appears! Thus not only is there no need to memorise many of them, but this approach makes discovering other commands easier.

[Disabled] Homemade ‘uncomitted changes’ Notification

Let's always notify ourselves of a file that has uncommited changes —we might have had to step away from the computer and forgotten to commit.

(require 'magit-git)

(defun my/magit-check-file-and-popup ()
  "If the file is version controlled with git
  and has uncommitted changes, open the magit status popup."
  (let ((file (buffer-file-name)))
    (when (and file (magit-anything-modified-p t file))
      (message "This file has uncommited changes!")
      (when nil ;; Became annyoying after some time.
      (split-window-below)
      (other-window 1)
      (magit-status)))))

;; I usually have local variables, so I want the message to show
;; after the locals have been loaded.
(add-hook 'find-file-hook
  '(lambda ()
      (add-hook 'hack-local-variables-hook 'my/magit-check-file-and-popup)))

3.3.1. Credentials: I am who I am

First, let's setup our git credentials.

;; See here for a short & useful tutorial:
;; https://alvinalexander.com/git/git-show-change-username-email-address
(when (equal "" (shell-command-to-string "git config user.email "))
  (shell-command (format "git config --global user.name \"%s\"" user-full-name))
  (shell-command (format "git config --global user.email \"%s\"" user-mail-address)))

;; Also need to customise email routes per organization
;; https://docs.github.com/en/github/managing-subscriptions-and-notifications-on-github/configuring-notifications#customizing-email-routes-per-organization
(ignore-error (unless my/personal-machine?
  (shell-command (format "git config --global user.email \"%s\"" work/email))))

;; If we ever need to use Git in the terminal, it should be done with Emacs as
;; the underlying editor
(shell-command "git config --global core.editor emacs")

3.3.2. Encouraging useful commit messages

Let's try our best to have a useful & consistent commit log:

(defun my/git-commit-reminder ()
  (insert "\n\n# The commit subject line ought to finish the phrase:
# “If applied, this commit will ⟪your subject line here⟫.” ")
  (beginning-of-buffer))

(add-hook 'git-commit-setup-hook 'my/git-commit-reminder)

Super neat stuff!

3.3.3. Maybe clone … everything?

Below are the git repos I'd like to clone —along with a function to do so quickly.

;; Clone git repo from clipboard
(cl-defun maybe-clone (remote &optional local)
  "Clone a REMOTE repository [from clipboard] if the LOCAL directory does not exist.

If called interactively, clone URL in clipboard into ~/Downloads then open in dired.

Yields ‘repo-already-exists’ when no cloning transpires, otherwise yields ‘cloned-repo’.

LOCAL is optional and defaults to the base name; e.g.,
if REMOTE is https://github.com/X/Y then LOCAL becomes ∼/Y."
  (interactive "P")

  (when (interactive-p)
    (setq remote (substring-no-properties (current-kill 0)))
    (cl-assert (string-match-p "^\\(http\\|https\\|ssh\\)://" remote) nil "No URL in clipboard"))

  (unless local
    (setq local (concat "~/" (if (interactive-p) "Downloads/" "") (file-name-base remote))))

  (require 'magit-repos) ;; Gets us the magit-repository-directories variable.
  (add-to-list 'magit-repository-directories `(,local . 0))

  (if (file-directory-p local)
      'repo-already-exists
    (shell-command (concat "git clone " remote " " local))
    (dired local)
    'cloned-repo))


(maybe-clone "https://github.com/alhassy/emacs.d" "~/.emacs.d")
(maybe-clone "https://github.com/alhassy/alhassy.github.io" "~/blog")
(maybe-clone "https://github.com/alhassy/holy-books")
Many more repos to clone
(maybe-clone "https://github.com/alhassy/melpa")
(maybe-clone "https://github.com/alhassy/org-special-block-extras")


;; (maybe-clone "https://github.com/alhassy/next-700-module-systems-proposal.git" "~/thesis-proposal")
;; (maybe-clone "https://github.com/JacquesCarette/MathScheme")
;; (maybe-clone "https://github.com/alhassy/gentle-intro-to-reflection" "~/reflection/")
;; (maybe-clone "https://github.com/alhassy/org-agda-mode")
;; (maybe-clone "https://github.com/JacquesCarette/TheoriesAndDataStructures")
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/RATH/RATH-Agda"     "~/RATH-Agda")
;; (maybe-clone "https://github.com/alhassy/MyUnicodeSymbols") ;; Deleted?

(maybe-clone "https://github.com/alhassy/islam")
(maybe-clone "https://github.com/alhassy/CheatSheet")
(maybe-clone "https://github.com/alhassy/ElispCheatSheet")
;; (maybe-clone "https://github.com/alhassy/CatsCheatSheet")
;; (maybe-clone "https://github.com/alhassy/OCamlCheatSheet")
;; (maybe-clone "https://github.com/alhassy/AgdaCheatSheet")
(maybe-clone "https://github.com/alhassy/RubyCheatSheet")
;; (maybe-clone "https://github.com/alhassy/PrologCheatSheet")
;; (maybe-clone "https://github.com/alhassy/FSharpCheatSheet")


;; (maybe-clone "https://gitlab.cas.mcmaster.ca/armstmp/cs3mi3.git" "~/3mi3")
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/alhassm/CAS781" "~/cas781") ;; cat adventures
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/carette/cs3fp3.git" "~/3fp3")
;; (maybe-clone "https://github.com/alhassy/interactive-way-to-c")
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/assignment-distribution.git" "~/3ea3/assignment-distribution")
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/notes.git" "~/3ea3/notes")
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/assignment-development.git" "~/3ea3/assignment-development")
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/kandeeps.git" "~/3ea3/sujan")
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/horsmane.git" "~/3ea3/emily")
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/anderj12.git" "~/3ea3/jacob")
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/alhassm/3EA3.git" "~/3ea3/_2018")
;; (maybe-clone "https://gitlab.cas.mcmaster.ca/2DM3/LectureNotes.git" "~/2dm3")

This maybe-clone utility has genuinely made it easier for me to learn about new projects and codebases from Github: I type it in with the repo's address, then C-x C-e ---eval-last-sexp— and then I can view it in my beloved Emacs (─‿‿─).

Moreover, this handy tool makes it so that you can list your Git repositories with magit-list-repositories: It marks modified repos as “dirty”.

It may be useful to know that (magit-anything-modified-p t file) can be used to check if file has been modified (magit-anything-modified-p), whereas (magit-status repo) checks the status of a repository (magit-status).

3.3.4. Gotta love that time machine

Finally, one of the main points for using version control is to have access to historic versions of a file. The following utility allows us to M-x git-timemachine on a file and use p/n/g/q to look at previous, next, goto arbitrary historic versions, or quit.

(use-package git-timemachine :defer t)

If we want to roll back to a previous version, we just write-file or C-x C-s as usual! The power of text!


vc-annotate is also very useful to go through history and work out when things went wrong.

3.3.5. Jump to a (ma)git repository with C-u C-x g

;; Jump to a (ma)git repository with C-u C-x g.
;;
;; To get a selection of repositories (that have been visited at least once),
;; call with “C-u M-x magit-status” or “C-u C-x g”; use “C-u C-u C-x g” to
;; manually enter a path to a repository.
;;
;; We use projectile's record of known projects, and keep only projects with
;; .git directory.
(with-eval-after-load 'projectile
  (setq magit-repository-directories
        (thread-last (projectile-relevant-known-projects)
          (--filter (unless (file-remote-p it)
                      (file-directory-p (concat it "/.git/"))))
          (--map (list (substring it 0 -1) 0)))))

;; Follow-up utility
(defun my/update-repos ()
  "Update (git checkout main & pull) recently visited repositories."
  (interactive)
  (cl-loop for (repo _depth) in magit-repository-directories
        ;; Is it “main” or “master”
        for trunk = (s-trim (shell-command-to-string (format "cd %s; git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@'" repo)))
        do (message (format "🤖 %s ∷ Checking out & pulling main" repo))
           (shell-command (format "cd %s; git checkout %s; git pull" repo trunk)))
  (message "🥳 Happy coding!"))

3.4. Pretty Magit Commit Leaders

⟨ Following Pretty Magit - Integrating commit leaders | Modern Emacs ⨾⨾ Code comes from there as well. Notable alteration: Helm compleition shows description of leaders. ⟩

Add faces to Magit to achieve icon and colored commit leaders. I also integrate Helm to prompt a leader when committing so there's no need to remember or type out completely every leader we choose.

  • It's not just aesthetics. It's about visual clarity.
  • Here is an alternate approach: Add icons based on words mentioned in commit titles —no leaders required.
Boring details ~ See linked article instead
(cl-defmacro pretty-magit (WORD ICON PROPS &optional (description "") NO-PROMPT?)
  "Replace sanitized WORD with ICON, PROPS and by default add to prompts."
  `(prog1
     (add-to-list 'pretty-magit-alist
                  (list (rx bow (group ,WORD (eval (if ,NO-PROMPT? "" ":"))))
                        ,ICON ',PROPS))
     (unless ,NO-PROMPT?
       (add-to-list 'pretty-magit-prompt (cons (concat ,WORD ": ") ,description)))))

(setq pretty-magit-alist nil)
(setq pretty-magit-prompt nil)

My personal choices for leaders are:

(pretty-magit "Add"      ?➕ (:foreground "#375E97" :height 1.2) "✅ Create a capability e.g. feature, test, dependency.")
(pretty-magit "Delete"   ?❌ (:foreground "#375E97" :height 1.2) "❌ Remove a capability e.g. feature, test, dependency.")
(pretty-magit "Fix"      ?🔨 (:foreground "#FB6542" :height 1.2) "🐛 Fix an issue e.g. bug, typo, accident, misstatement.")
(pretty-magit "Clean"    ?🧹 (:foreground "#FFBB00" :height 1.2) "✂ Refactor code; reformat say by altering whitespace; refactor performance.")
(pretty-magit "Document" ?📚 (:foreground "#3F681C" :height 1.2) "ℹ Refactor of documentation, e.g. help files.")
(pretty-magit "Feature"  ?⛲ (:foreground "slate gray" :height 1.2) "⛳ 🇮🇶🇨🇦 A milestone commit - flagpost")
(pretty-magit "Generate"  ?🔭 (:foreground "slate gray" :height 1.2) "Export PDF/HTML or tangle raw code from a literate program") ;; Generating artefacts
(pretty-magit "master"   ? (:box t :height 1.2) "" t)
(pretty-magit "origin"   ?🐙 (:box t :height 1.2) "" t)
;; Commit leader examples: https://news.ycombinator.com/item?id=13889155.
;;
;; Cut ~ Remove a capability e.g. feature, test, dependency.
;; Bump ~ Increase the version of something e.g. dependency.
;; Make ~ Change the build process, or tooling, or infra.
;; Start ~ Begin doing something; e.g. create a feature flag.
;; Stop ~ End doing something; e.g. remove a feature flag.
Boring details ~ See linked article instead
(defun add-magit-faces ()
  "Add face properties and compose symbols for buffer from pretty-magit."
  (interactive)
  (with-silent-modifications
    (--each pretty-magit-alist
      (-let (((rgx icon props) it))
        (save-excursion
          (goto-char (point-min))
          (while (search-forward-regexp rgx nil t)
            (compose-region
             (match-beginning 1) (match-end 1) icon)
            (when props
              (add-face-text-property
               (match-beginning 1) (match-end 1) props))))))))

(advice-add 'magit-status :after 'add-magit-faces)
(advice-add 'magit-refresh-buffer :after 'add-magit-faces)


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

(setq use-magit-commit-prompt-p nil)
(defun use-magit-commit-prompt (&rest args)
  (setq use-magit-commit-prompt-p t))

(defun magit-commit-prompt ()
  "Magit prompt and insert commit header with faces."
  (interactive)
  (when use-magit-commit-prompt-p
    (setq use-magit-commit-prompt-p nil)
    (thread-last (--map (format "%s %s" (car it) (cdr it)) pretty-magit-prompt)
      (completing-read "Insert commit leader ∷ ")
      ;; My “Generate:” commit type has one use case, for now; so let's insert it filled-in.
      (funcall (lambda (it) (if (s-starts-with? "Generate:" it) it (car (s-split " " it)))))
      (insert)
      (end-of-line))
    (add-magit-faces)))


(remove-hook 'git-commit-setup-hook 'with-editor-usage-message)
(add-hook 'git-commit-setup-hook 'magit-commit-prompt)
(advice-add 'magit-commit :after 'use-magit-commit-prompt)

3.5. Version Control with SVN —Using Magit!   Disabled

Disabled: I seldom work with SVN anymore.

Let's use git as an interface to subversion repositories so that we can continue to use magit as our version control interface. The utility to do so is called git svn —note git 𝒳 on a MacOS is the same as git-𝒳 on other systems.

(use-package magit-svn
  :hook (magit-mode . magit-svn-mode))

Here's an example. The following command checksout an SVN repo; afterwhich we may open a file there and do M-x magit-status to get the expected porcelain git interface ^_^

(async-shell-command "mkdir ~/2fa3; git svn clone --username alhassm https://websvn.mcmaster.ca/csse2fa3/2019-2020_Term2 ~/2fa3/")

In the magit buffer, we may now use the N key which wraps the git svn subcommands fetch, rebase, dcommit, branch, tag. For example:

  1. Make changes to a file.
  2. ‘Stage’ them with s and ‘commit’ them with c.
  3. ‘Push’ changes with N c.

We get to pretend we're using git even though the underlying mechanism is svn!

For move on git svn, see A simple guide to git-svn or Effectively using Git with Subversion.

(If I need to work with svn repos often enough, I'd extend my maybe-clone utility above to account for them.)

3.6. Highlighting TODO-s & Showing them in Magit

Sometimes it's nice to flag a chunk of text by its author, such as ‘ MA ’ for ‘M’usa ‘A’l-hassy, or ‘ HACK ’ for text that needs to be improved. Such flags stand out from other text by being coloured and bold.

;; NOTE that the highlighting works even in comments.
(use-package hl-todo
  ;; I want todo-words highlighted in prose, not just in code fragements.
  :hook (org-mode . hl-todo-mode)
  :config
    ;; Adding new keywords
    (cl-loop for kw in '("TEST" "MA" "WK" "JC")
             do (add-to-list 'hl-todo-keyword-faces (cons kw "#dc8cc3")))
    ;; Enable it everywhere.
    (global-hl-todo-mode))

We've added few to the default flag keywords so that in total we have the following flags —where any sequence of at least 3 XXX are considered flags.

(JC WK MA TEST HOLD TODO NEXT THEM PROG OKAY DONT FAIL DONE NOTE KLUDGE HACK TEMP FIXME XXX+)

Lest these get buried in mountains of text, let's have them become mentioned in a magit status buffer —which uses the keywords from hl-todo.

;; MA: The todo keywords work in code too!
(use-package magit-todos
  :after magit
  :after hl-todo
  ;; :hook (org-mode . magit-todos-mode)
  :config
  ;; For some reason cannot use :custom with this package.
  (custom-set-variables
    '(magit-todos-keywords (list "TODO" "FIXME" "MA" "WK" "JC")))
  ;; Ignore TODOs mentioned in exported HTML files; they're duplicated from org src.
  (setq magit-todos-exclude-globs '("*.html"))
  (magit-todos-mode))
  • Note that such TODO keywords are not propagated from sections that are COMMENT-ed out in org-mode.
  • Ensure you exclude generated files, such as the Emacs backups directory, from being consulted. Using magit, press i to mark items to be ignored.
  • This feature also works outside of git repos.

Open a Magit status buffer, or run magit-todos-list to show a dedicated to-do list buffer. You can then peek at items with space, or jump to them with enter.

Seeing the TODO list with each commit is an incentive to actually tackle the items there (•̀ᴗ•́)و

3.7. Silently show me when a line was modified and by whom

Quickly & automatically glimpse who, why, and when a line or code block was changed, using blamer.el. Jump back through history to gain further insights as to how and why the code evolved with C-x g l l (magit-log-head) or git-timemachine.

(unless noninteractive

  (use-package blamer
    :quelpa (blamer :fetcher github :repo "artawower/blamer.el")
    :custom
    (blamer-idle-time 0.3)
    (blamer-min-offset 70)
    (blamer-max-commit-message-length 80) ;; Show me a lot of the commit title
    :custom-face
    (blamer-face ((t :foreground "#7a88cf"
                      :background nil
                      :height 140
                      :italic t)))
    :config
    (global-blamer-mode 1)))

This is so nice!. I've enabled it once and then it just “works in the background, silently”.


When reading a line and wondering “who changed/wrote this and why”, blamer.el answers that seamlessly —almost as if a transient comment 😁

  • The “why” is answered by commit messages; which may lead to improved messages: “Hey, I was just browsing through the code, and landed on this commit, but the message does not tell me why the change was introduced. Would you please write more detailed commit messages.”
  • The “when” gives a useful context in time about the change. For example, when looking for a regression/recent-bug, we can immediately see “Did this line even change in the past week? No? That must not be it then” and we move on.

If we want to see commit messages alongside all code, then we can invoke magit-blame-addition or vc-annotate. If we want to “walk-along changes” then we use git-timemachine.

3.8. delete-by-moving-to-trash t

;; Move to OS’ trash can when deleting stuff
;; instead of deleting things outright!
(setq delete-by-moving-to-trash t
      trash-directory "~/.Trash/")

3.9. Jumping to extreme semantic units

Sometimes it's unreasonable for M-< to take us to the actual start of a buffer; instead it'd be preferable to go to the first “semantic unit” in the buffer. For example, when directory editing with dired we should jump to the first file, with version control with magit we should jump to the first section, when composing mail we should jump to the first body line, and in the agenda we should jump to the first entry.

;; M-< and M-> jump to first and final semantic units.
;; If pressed twice, they go to physical first and last positions.
(use-package beginend
  :diminish 'beginend-global-mode
  :config (beginend-global-mode)
    (cl-loop for (_ . m) in beginend-modes do (diminish m)))

3.10. Get CheatSheets and view them easily

(defvar my/cheatsheet/cached-topics nil)
(cl-defun my/cheatsheet (&optional topic)
  "Clone Al-hassy's ⟨TOPIC⟩CheatSheet repository when called from Lisp; visit the pretty HTML page when called interactively.

- Example usage: (my/cheatsheet \"Vue\")
- Example usage: M-x my/cheatsheet RET Vue RET."
  (interactive)
  (if (not topic)
      (browse-url (format "https://alhassy.github.io/%sCheatSheet" (completing-read "Topic: " my/cheatsheet/cached-topics)))
    (push topic my/cheatsheet/cached-topics)
    (maybe-clone (format "https://github.com/alhassy/%sCheatSheet" topic))))

Let's actually get some repos locally, and use: M-x my/cheatsheet to view the pretty HTML (or PDF) sheets.

(mapcar #'my/cheatsheet '("ELisp" "GojuRyu" "Rust")) ; Python Prolog Vue Agda JavaScript
                                              ; Clojure Ruby Oz Coq Cats Haskell FSharp OCaml

4. Literate Programming

Org-mode lets us run chunks of code anywhere, then feed their outputs to other chunks of code in possibly different programming languages: Org is a meta-(programming language).

Importantly, this means we can write text and whenever we need the result of some computation, we can place it there and then and only request its result appear in PDF/HTML export. The result is a single document.

( There is the org-modern package, which provides a modern look-and-feel: It makes Org look less like a markup and more like a word editor. Nice stuff. )

4.1. High Speed Literate Programming

4.1.1. Manipulating Sections

(setq org-use-speed-commands t)

This enables the Org Speed Keys so that when the cursor is at the beginning of a headline, we can perform fast manipulation & navigation using the standard Emacs movement controls, such as:

  • # toggle COMMENT-ing for an org-header.
  • s toggles “narrowing” to a subtree; i.e., hide the rest of the document.

    If you narrow to a subtree then any export, C-c C-e, will joyously only consider the narrowed detail.

  • u for going to upwards to parent heading
    • i insert a new same-level heading below current heading.
  • c for cycling structure below current heading, or C for cycling global structure.
  • w refile current heading; options list pops-up to select which heading to move it to. Neato!
    • g to go to another heading, without refiling anything.

      ;; When refiling, only show me top level headings [Default]. Sometimes 2 is useful.
      ;; When I'm refiling my TODOS, then give me all the freedom.
      (setq org-refile-targets '((nil :maxlevel . 1)
                                 (org-agenda-files :maxlevel . 9)))
      
      ;; Maybe I want to refile into a new heading; confirm with me.
      (setq org-refile-allow-creating-parent-nodes 'confirm)
      
      ;; Use full outline paths for refile targets
      ;; When refiling, using Helm, show me the hierarchy paths
      (setq org-outline-path-complete-in-steps nil)
      (setq org-refile-use-outline-path 'file-path)
      
  • n/p for next/previous visible heading.
  • f/b for jumping forward/backward to the next/previous same-level heading.
  • D/U move a heading down/up.
  • L/R recursively promote (move leftwards) or demote (more rightwards) a heading.
  • I/O clock In/Out to the task defined by the current heading.
    • Keep track of your work times!
    • v view agenda.
  • t/,/:/e to add a TODO state, priority level, tag, or effort estimate
    • 1/2/3 to mark a heading with priority, highest to lowest.
  • ^ sort children of current subtree; brings up a list of sorting options.
  • k/@/a to kill or mark or archive the current subtree
  • o to open a link mentioned in the subtree then go to the link; a pop-up of links appears.

We can add our own speed keys by altering the org-speed-commands association list variable; e.g.,

;; TODO FIXME Crashes upon startup.
(when nil (add-to-list 'org-speed-commands (cons "P" #'org-set-property)))
;; Use ‘:’ and ‘e’ to set tags and effort, respectively.
⇒ Moreover, ? to see a complete list of keys available. ⇐

4.1.2. Seamless Navigation Between Source Blocks

The “super key” —aka the command or windows key— can be used to jump to the previous, next, or toggle editing org-mode source blocks.

;; Overriding keys for printing buffer, duplicating gui frame, and isearch-yank-kill.
;;
(require 'org)
(use-package emacs
  :bind (:map org-mode-map
              ("s-p" . org-babel-previous-src-block)
              ("s-n" . org-babel-next-src-block)
              ("s-e" . org-edit-special)
              :map org-src-mode-map
              ("s-e" . org-edit-src-exit)))

Note that we could have bound ⌘+e to org-edit-src-code / org-edit-src-exit, but instead chose the more general org-edit-special since, well, look at the tooltip documentation: This allows us to use ⌘+e to ‘e’dit all kinds of Org entities —including footnotes and export blocks. ( Footnotes can be quickly produced with org-footnote-new. )

4.1.3. Modifying ⟨return⟩

  • C-⟨return⟩ , C-S-⟨return⟩ make a new heading where the latter marks it as a TODO.
  • By default M-⟨return⟩ makes it easy to work with existing list items, headings, tables, etc by creating a new item, heading, etc.

Usually we want a newline then we indent, let's make that the default.

(add-hook 'org-mode-hook '(lambda ()
   (local-set-key (kbd "<return>") 'org-return-indent))
   (local-set-key (kbd "C-M-<return>") 'electric-indent-just-newline))

Notice that I've also added another kind of return, for when I want to break-out of the indentation approach and start working at the beginning of the line.

In summary:

key method behaviour
⟨return⟩ org-return-indent Newline with indentation
M-⟨return⟩ org-meta-return Newline with new org item
C-M-⟨return⟩ electric-indent-just-newline Newline, cursor at start
C-⟨return⟩ org-insert-heading-respect-content New heading after current content
C-S-⟨return⟩ org-insert-todo-heading-respect-content Ditto, but with a TODO marker

4.2. Executing code from src blocks

For example, to execute a shell command in Emacs, write a src with a shell command, then C-c c-c to see the results. Emacs will generally query you to ensure you're confident about executing the (possibly dangerous) code block; let's stop that:

;; Seamless use of babel: No confirmation upon execution.
;; Downside: Could accidentally evaluate harmful code.
(setq org-confirm-babel-evaluate nil)

;; Never evaluate code blocks upon export and replace results when evaluation does occur.
;; For a particular language 𝑳, alter ‘org-babel-default-header-args:𝑳’.
(setq org-babel-default-header-args
      '((:results . "replace")
        (:session . "none")
        (:exports . "both")
        (:cache .   "no")
        (:noweb . "no")
        (:hlines . "no")
        (:tangle . "no")
        (:eval . "never-export")))

Some initial languages we want org-babel to support:

(defvar my/programming-languages
  '(emacs-lisp shell python haskell rust ruby ocaml dot latex org js css
               sqlite C) ;; Captial “C” gives access to C, C++, D
  "List of languages I have used in Org-mode, for literate programming.")

;; Load all the languagues
(cl-loop for lang in my/programming-languages
         do (require (intern (format "ob-%s" lang))))
;;
(org-babel-do-load-languages
 'org-babel-load-languages
 (--map (cons it t) my/programming-languages))

;; Preserve my indentation for source code during export.
(setq org-src-preserve-indentation t)

;; The export process hangs Emacs, let's avoid this.
;; MA: For one reason or another, this crashes more than I'd like.
;; (setq org-export-in-background t)

More languages can be added using add-to-list.

4.3. Executing all #+name: startup-code for local configurations

Sometimes my Org-files contain configurations that are local to the file, so I name all such src blocks #+name: startup-code and place # -*- eval: (my/execute-startup-blocks) -*- at the top of the file so that such blocks are evaluated when the file opens up.

  • The -*- ... -*- notation is for making local configurations.
  • Use M-x add-file-local-variable-prop-line to have them inserted interactively.
(defun my/execute-startup-blocks ()
  "Execute all startup blocks, those named ‘startup-code’.

I could not use ORG-BABEL-GOTO-NAMED-SRC-BLOCK since it only goes
to the first source block with the given name, whereas I'd like to
visit all blocks with such a name."
  (interactive)
  (save-excursion
    (goto-char 0)
    (while (ignore-errors (re-search-forward "^\\#\\+name: startup-code"))
      (org-babel-execute-src-block))))

The following setup enables this feature in a safe fashion —e.g., we do not want to avoid evaluating a random person's potentially dangerous code when we only want to look at it.

;; Please ask me on a file by file basis whether its local variables are ‘safe
;; or not. Use ‘!’ to mark them as permanently ‘safe’ to avoid being queried
;; again for the same file.
(setq enable-local-variables t)

I have been using a combination of (org-babel-goto-named-src-block ⋯) in multi-line local-variable declarations ---M-x add-file-local-variable-prop— for a while in many files using a dedicated * footer :noexport: section, but this new approach frees from having such sections and instead to having a single line at the top of the file. Moreover, being at the top of the file, such a line is a nice ‘in your face’ reminder that there is local configuration that should have been loaded.

4.4. Quickly pop-up a terminal, run a command, close it —and zsh

Pop up a terminal, do some work, then close it using the same command.

Shell-pop uses only one key action to work: If the buffer exists, and we're in it, then hide it; else jump to it; otherwise create it if it doesn't exit. Use universal arguments, e.g., C-u 5 C-t, to have multiple shells and the same universal arguments to pop those shells up, but C-t to pop them away.

(use-package shell-pop
  :custom
    ;; This binding toggles popping up a shell, or moving cursour to the shell pop-up.
    (shell-pop-universal-key "C-t")

    ;; Percentage for shell-buffer window size.
    (shell-pop-window-size 30)

    ;; Position of the popped buffer: top, bottom, left, right, full.
    (shell-pop-window-position "bottom")

    ;; Please use an awesome shell.
    (shell-pop-term-shell "/bin/zsh"))
Oh My Zsh

Now that we have access to quick pop-up for a shell, let's get a pretty and practical shell: zsh along with the Oh My Zsh community configurations give us:

  1. brew install zsh
  2. sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"

    This installs everything ^_^

;; Be default, Emacs please use zsh
;; E.g., M-x shell
(unless noninteractive (setq shell-file-name "/bin/zsh"))

Out of the box, zsh comes with

  • git support; the left side indicates which branch we're on and whether the repo is dirty, ✗.
  • Recursive path expansion; e.g., /u/lo/b TAB expands to /usr/local/bin/
  • Over 250+ Plugins and 125+ Themes that are enabled by simply mentioning their name in the .zshrc file.

The defaults have been good enough for me, for now —as all else is achieved via Emacs ;-)

Also, there's the tldr tool which aims to be like terse manuals for commandline-tools in the style of practical example uses cases: tldr 𝒳 yields a number of ways you'd actually use 𝒳. ( In Emacs, C-t tldr 𝒳 ⟨return⟩. )

(system-packages-ensure "tldr")

4.5. Snippets —Template Expansion

It is common that there is a sequence of text that we tend to repeat often, possibly with a name or some other parameter altered. Such a ‘snippet’ could be written once then provided by a simple Lisp insert command with the parameters being queried. Luckily, others have written such pleasant utilities.

Besides snippets, there are words that we may want to repeat often but it can be tedious to write them out in full. As such, we employ word completion; which we also use to expand our snippets.

4.5.1. Word Completion

Let's enable “complete anything” mode —it ought to start in half a second and only need two characters to get going, which means word suggestions are provided and so I need only type partial words then tab to get the full word!

(use-package company
  :diminish
  :config
  (global-company-mode 1)
  (setq ;; Only 2 letters required for completion to activate.
   company-minimum-prefix-length 2

   ;; Search other buffers for compleition candidates
   company-dabbrev-other-buffers t
   company-dabbrev-code-other-buffers t

   ;; Show candidates according to importance, then case, then in-buffer frequency
   company-transformers '(company-sort-by-backend-importance
                          company-sort-prefer-same-case-prefix
                          company-sort-by-occurrence)

   ;; Flushright any annotations for a compleition;
   ;; e.g., the description of what a snippet template word expands into.
   company-tooltip-align-annotations t

   ;; Allow (lengthy) numbers to be eligible for completion.
   company-complete-number t

   ;; M-⟪num⟫ to select an option according to its number.
   company-show-numbers t

   ;; Show 10 items in a tooltip; scrollbar otherwise or C-s ^_^
   company-tooltip-limit 10

   ;; Edge of the completion list cycles around.
   company-selection-wrap-around t

   ;; Do not downcase completions by default.
   company-dabbrev-downcase nil

   ;; Even if I write something with the ‘wrong’ case,
   ;; provide the ‘correct’ casing.
   company-dabbrev-ignore-case nil

   ;; Immediately activate completion.
   company-idle-delay 0)

  ;; Use C-/ to manually start company mode at point. C-/ is used by undo-tree.
  ;; Override all minor modes that use C-/; bind-key* is discussed below.
  (bind-key* "C-/" #'company-manual-begin)

  ;; Bindings when the company list is active.
  :bind (:map company-active-map
              ("C-d" . company-show-doc-buffer) ;; In new temp buffer
              ("<tab>" . company-complete-selection)
              ;; Use C-n,p for navigation in addition to M-n,p
              ("C-n" . (lambda () (interactive) (company-complete-common-or-cycle 1)))
              ("C-p" . (lambda () (interactive) (company-complete-common-or-cycle -1)))))

;; It's so fast that we don't need a key-binding to start it!

Note that M-/ goes through a sequence of completions —and C-/ manually begins company mode at point. Besides the arrow keys, we can also use M- with n, p to navigate the options or use C-s to search the list of suggestions.

  • Company backends are available as separate packages.
  • Note that by default company mode does not support completion for phrases containing hyphens —this can be altered, if desired.

Besides boring word completion, let's add support for emojis.

(use-package company-emoji
  :config (add-to-list 'company-backends 'company-emoji))

For example: 🥞 💻 🐵 ✉️😉 🐬 🌵.

➡️On a new line, write : then any letter to have a tool-tip appear. All emoji names are lowercase. ◀

  • On MacOS, C-⌘-SPC brings up an emoji picker, where one drags desired emojis to textual areas.
  • Here is a list of emoji —all supported by Github.

The libraries emojify and emojify-logos provides cool items like :haskell: :emacs: :org: :ruby: :python:. Unfortunately they do not easily export to html with org-mode, so I'm not using them.

4.5.2. Intro to Snippets

A snippet, template, mechanism is a tool that when you press some keystrokes inserts some text, possibly with some fields (‘blanks’) to fill in. Possibly interesting read:


Yasnippet is a pleasant utility for template expansion with the alluring feature to allow arbitrary Lisp code to be executed during expansion. The declaration of templates is verbose, requiring a particular file hierarchy, as such I utilise Yankpad which allows me to employ an Org-mode approach: Each template corresponds to an org heading of the form Key:Words:For:Expansion:Here: name of snippet here and the template body is then the body of the org heading. Any of Key, Words, For, Expansion, Here will rewrite into the body of the org tree. This is much more terse, and I even don't bother with that; instead preferring to tangle my templates using yankpad as a mere interface. It is important to note that Yankpad also provides features that are not in Yassnippet, such as allowing arbitrary language code to be executed —one simply uses an org-src block!

Here is a nice self-contained tutorial.

There can only be one major completion backend for any mode, but other backends can serve as secondary ones. Here's a function to make company-yankpad a secondary of all existing backends.

;; Add yasnippet support for all company backends
;;
(cl-defun my/company-backend-with-yankpad (backend)
  "There can only be one main completition backend, so let's
   enable yasnippet/yankpad as a secondary for all completion
   backends.

   Src: https://emacs.stackexchange.com/a/10520/10352"

  (if (and (listp backend) (member 'company-yankpad backend))
      backend
    (append (if (consp backend) backend (list backend))
            '(:with company-yankpad))))
;; Yet another snippet extension program
(use-package yasnippet
  :diminish yas-minor-mode
  :config
    (yas-global-mode 1) ;; Always have this on for when using yasnippet syntax within yankpad
    ;; respect the spacing in my snippet declarations
    (setq yas-indent-line 'fixed))

;; Alternative, Org-based extension program
(use-package yankpad
  :diminish
  :config
    ;; Location of templates
    (setq yankpad-file "~/.emacs.d/yankpad.org")

    ;; Ignore major mode, always use defaults.
    ;; Yankpad will freeze if no org heading has the name of the given category.
    (setq yankpad-category "Default")

    ;; Load the snippet templates ---useful after yankpad is altered
    (yankpad-reload)

    ;; Set company-backend as a secondary completion backend to all existing backends.
    (setq company-backends (mapcar #'my/company-backend-with-yankpad company-backends)))

With these settings, along with the company backend, I may type a keyword then TAB it into expansion.

Yankpad requires we have an org file that contains our templates, so we tangle such a file ~/.emacs.d/yankpad.org, and have all of our templates be globally accessible. Here is the start of my file:

#+Description: This is file is generated from my init.org; do not edit.

* Default                                           :global:
Fully discussed example: Using the clipboard for Org-links

Here's an example of a common template I perform by hand —no more! I have the expected habit of copying (to clipboard) a URL from someplace then forming a link to it by writing [[URL] [description]], since the URL & syntax are already known, let's expand those and place the cursour at the only unknown —the description.

** my_org_insert_link: cleverly insert a link copied to clipboard
 [[${1:`(clipboard-yank)`}][$2]] $0

What's going on here? ( The above, verbatim: [[${1:`(clipboard-yank)`}][$2]] $0. )

  1. This template is expanded with the keyword my-org-insert-link, then TAB.
  2. The cursour lands at position $1, which has default text being the result of evaluating (clipboard-yank).

    We may evaluate Lisp code anywhere by enclosing it in backticks.

  3. If we're satisfied with the current field, we simply tab to the next field. Otherwise, we simply write text —which overwrites the default text.
  4. After enough tabbing we complete the template and the cursour lands at position $0.

⟪ Having default or mirrored text for $2 would not allow me to see the URL field, lest I wish to change it or at least confirm it's what I want. Hence, the $2 field has no default. ⟫

Let's overwrite the usual way to insert such links, via C-c C-l.

(cl-defun org-insert-link ()
  "Makes an org link by inserting the URL copied to clipboard and
  prompting for the link description only.

  Type over the shown link to change it, or tab to move to the
  description field.

  This overrides Org-mode's built-in ‘org-insert-link’ utility;
  whence C-c C-l uses the snippet."
  (interactive)
  (insert "my_org_insert_link")
  (yankpad-expand))

Warning! Snippet names cannot have hypens in them —in this setup at least.

The Yasnippet manual is an accessible read, as is the Yankpad manual, and showcases many other utilities; such as having certain snippets being enabled only in particular modes or on demand. Of note is that field $n can be accessed in code with the invocation (yas-field-value n).

Incidentally, I used this snippet setup to demo the idea of repetitious code in grouping constructs within dependently-typed languages, which was accepted and led to my doctoral research on a ‘do it yourself module system’.

The rest of this section is other templates, not much for now, concluding with actually loading this snippet mechanism globally.

The remaining subsections discuss contents of my yankpad file.

4.5.3. Org-mode Templates —A reason I “generate” templates ;)

This produces a pop-up list of org-mode block types, if src is selected, then a list of my commonly used languages pops-up. Alternatively, ignore the pop-up menu and write any block or language name.

** begin: produce an org-mode block
#+begin_${1:environment$(let*
    ((block '("src" "example" "quote" "verse" "center" "latex" "html" "ascii"))
     (langs '("c" "emacs-lisp" "lisp" "latex" "python" "sh" "haskell" "plantuml" "prolog"))
     (type (yas-choose-value block)))
     (concat type (when (equal type "src") (concat " " (yas-choose-value langs)))))}
 $0
#+end_${1:$(car (split-string yas-text))}

In this case, yas-text is equivalent to (yas-field-value 1); it generally refers to the value of the field being mirrored with ${n: ⋯yas-text⋯}.

However, going through pop-ups takes precious time —besides being slightly annyoing. Let's introduce a template for my most utilised kind of language blocks.

** s_org: src block for org
#+begin_src org
$0
#+end_src

However, doing this for each language I want is a waste of time and textual space. Why? The purpose of templates is to reduce repetition, yet the above block would be repeated with only 3 parts ‘unknown’: The expansion keyword, the description, and the org-mode source block name. Whence, the template text is generated by the following basic loop —whose source block is named my-org-lang-templates.

;; We make an org BLOCK snippet template for each LANG the user has declared.
;;
(cl-loop for (shortcut block takes-language-argument? default-text)
      in '(("s_" "src" t)
           ("is_" "inline source" t)  ;; Treated specially below
           ("e_" "example" t)
           ("q_" "quote")
           ("v_" "verse")
           ("c_" "center")
           ("ex_" "export") ;; only HTML and LATEX
           ;; https://alhassy.github.io/org-special-block-extras/#Summary
           ("p_"  "parallel" nil "\n$0\n#+columnbreak:\n")
           ("d_"  "details"  nil "${1:title}\n$0")
           ("ed_" "edcomm"   nil  "${1:editor}\n$0")
           ("doc_" "documentation" nil "${1: mandatory entry name}\n$0")
           ("def_" "latex-definitions"))
      for languages = (if takes-language-argument?
                          (-cons* "org" "agda2" "any" ;; Extra ‘languages
                                  ;; Also include whatever languages we've loaded for literate programming.
                                  (--map (symbol-name (car it)) org-babel-load-languages))
                        '("")) ;; The “empty language”
      concat (cl-loop for lang in languages
                   for key         = (concat shortcut
                                       (if (s-blank? lang) block lang))
                   for description =  (if (s-blank? lang)
                                          block
                                        (concat
                                         block " for " lang))
                   concat (if (equal "is_" shortcut)
                              (concat "\n** " key ": " description
                                      "\nsrc_" lang "[:exports code]{$1} $0")
                            (concat "\n** " key ": " description
                                    "\n#+begin_" block " " lang
                                    (or default-text "\n$0")
                                    "\n#+end_" block "\n"))))

The resulting text of this block, generated below, is tangled to our yankpad by utilising a noweb source block invocation. An example of the resulting text is the above s_org block. The result is (last I checked) 83 template expansions —that would have been a bit much to write by hand.

#+begin_src org :tangle "~/.emacs.d/yankpad.org" :noweb yes
<<my-org-lang-templates()>>
#+end_src
nil

Now s_, due to company mode, brings up a list of languages that I can then scroll down through, then “enter” upon to expand. Moreover, the prefix s_ means that the key is mostly irrelevant, since I needn't remember it because company-mode immediately lists possible completions along with the descriptions for the snippets. Likewise for examples with e_ or quotes with q_. Super neat stuff :-)

Ain't this reminiscent of meta-programming ;-)

Using noweb invocations, any time the tangling is performed, the yankpad is kept up to date —no personal intervention from myself.

With the advent of org-special-block-extras, I've made increased usage of links –such as green:hello which yields hello and [[kbd:][green]] which yields .

** ll_make_a_link: insert a link template
${1:`(let* ((τ (read-string "Link type: "))
            (δ (read-string "Link Description: "))
            (⊤ (if (s-contains? ":" τ) τ (s-concat τ ":"))))
       (format "[[%s][%s]]" ⊤ δ))`} $0

4.5.4. Operating System Keyboard Symbols

Write os_ then see a bunch of completions ;-)

(loop for (name expansion)
      in '((os-command )
           (os_option ⌥)
           (os_alt ⌥)
           (os_control ⌃)
           (os_shift ⇧)
           (os_backspace ⌫)
           (os_delete ⌫)
           (os_delete_forward ⌦)
           (os_enter ⏎)
           (os_return ⏎)
           (os_escape ⎋)
           (os_tab_right ⇥)
           (os_tab_left ⇤)
           (os_caps_lock ⇪)
           (os_eject ⏏))
      concat
      (format "** %s: %s Operating System Keyboard Symbol\n%s\n" name expansion expansion))
nil

4.5.5. Work Templates

** ll_console_log: Log some JS variables

console.log("%c ******* LOOK HERE *******", "color: green; font-weight: bold;");
console.log({ ${1:List the variables here whose values you want to log} });
$0

** uuidgen: Insert the result of “uuidgen” and copy it to the clipboard

${1:`(-let [it (shell-command-to-string "uuidgen | tr '[:upper:]' '[:lower:]' |
pbcopy; pbpaste")] (message "Copied to clipboard, uuid: %s" it) it)`}

4.5.6. Elisp Templates

The following snippets were rather useful as I began learning Lisp to construct my editor of choice —I love Emacs so much. Admittedly, I still need the first one below and usually beat around the bush by using (cl-loop for ⋯ do ⋯), (cl-loop), which is ‘noisier’ but easier to remember and to read for non-Lispers.

** loop:  Elisp's for each loop
(dolist (${1:var} ${2:list-form})
        ${3:body})

** defun: Lisp functions
(cl-defun ${1:fun-name} (${2:arguments})
  "${3:documentation}"
  $0)

** cond: Elisp conditionals
(cond (${1:scenario₁} ${2:response₁})
      (${3:scenario₂} ${4:response₂}))

4.5.7. Equational Templates

To show ℒ = ℛ, one starts at the complicated side, say , then, with the aim of simplification, tries to end at the simpler side, 𝓡. Along the way, one justifies each step of the calculation. This approach is popular in the proof assistant Agda; Examples. Read more about informal calculational proofs.

** fun: Function declaration with type signature

${1:fun-name} : ${2:arguments}
$1 ${3:args} = ?$0

** eqn_begin: Start a ≡-Reasoning block in Agda

begin
  ${1:complicated-side}
$0≡⟨ ${3:reason-for-the-equality} ⟩
 ${2:simpler-side}
∎

** eqn_step: Insert a step in a ≡-Reasoning block in Agda
≡⟨ ${2:reason-for-the-equality} ⟩
  ${1:new-expression}
$0

One expands eqn_begin, tabs to fill in the three main locations, then immediately types eqn_step to produce a new step in a calculational proof.

4.5.8. Fixed replies

Here are some replies that I sometimes need to produce; e.g., to people who insist their way is the right way.

** reply_opinionated_pantomath: What to say to, e.g., an arrogant academic

Your certainty inspires me to continuing exploring, and I may arrive at your
point of view, but I'm going to need more evidence first.

** reply_em_dashes: Why use em dashes for parenthetical remarks?

According to the “Canadian Style Guide” (CSG):

   The em is an expansive, attention-seeking dash. It supplies much stronger
   emphasis than the comma, colon or semicolon it often replaces. Positioned
   around interrupting elements, em dashes have the opposite effect of
   parentheses—em dashes emphasize; parentheses minimize.

From “A Logical Approach to Discrete Math” (LADM), page ix:

   We place a space on one side of an em dash ---here are examples--- in
   order to help the reader determine whether the em dash begins or ends
   a parenthetical remark. In effect, we are creating two symbols from one.
   In longer sentences---and we do write long sentences from time to time---the
   lack of space can make it difficult to see the sentence structure---especially
   if the em dash is used too often in one sentence. Parenthetical remarks
   delimited by parentheses (like this one) have a space on one side of each
   parenthesis, so why not parenthetical remarks delimited by em dashes?

Interestingly, according to the CSG, there should be no space before or after an
em dash.  As such, it appears that the spacing is mostly stylistic; e.g., some
people surround em-s with spaces on both sides.  In particular, when em-s are
unmatched, I make no use of additional space ---indeed this form of one-sided
parentheses without a space is how LADM is written, as can be seen at the top of
page 3.

4.5.9. Emojis

;;
;; https://emojipedia.org/people/
(cl-loop for (emoji name description)
         in '((😀 "Grinning Face"
                  "Often conveys general pleasure and good cheer or humor.")

              (😃 "Grinning Face with Big Eyes"
                  "Often conveys general happiness and good-natured amusement.
                   Similar to 😀 Grinning Face but with taller,
                   more excited eyes.")

              (😄 "Grinning Face with Smiling Eyes"
                  "Often conveys general happiness and good-natured amusement.
                   Similar to 😀 Grinning Face and 😃 Grinning
                   Face With Big Eyes, but with warmer, less
                   excited eyes.")

              (😁 "Beaming Face with Smiling Eyes"
                  "Often expresses a radiant, gratified
                  happiness. Tone varies, including warm, silly,
                  amused, or proud.")

              (😆 "Grinning Squinting Face"
                  "Often conveys excitement or hearty laughter.
                   Similar to 😀 Grinning Face but with eyes that
                   might say ‘Squee!’ or ‘Awesome!’ An emoji form of
                   the >< or xD emoticons.")

              (😅 "Grinning Face with Sweat"
                  "Intended to depict nerves or discomfort but
                  commonly used to express a close call, as if
                  saying ‘Whew!’ and wiping sweat from the
                  forehead. ")

              (🤣 "Rolling on the Floor Laughing"
                  "Often conveys hysterical laughter more intense
                  than 😂 Face With Tears of Joy.")

              (😂 "Face with Tears of Joy")
              (🙂 "Slightly Smiling Face")
              (🙃 "Upside-Down Face")
              (😉 "Winking Face")
              (😊 "Smiling Face with Smiling Eyes")
              (😇 "Smiling Face with Halo")
              (🥰 "Smiling Face with Hearts")
              (😍 "Smiling Face with Heart-Eyes")
              (🤩 "Star-Struck")
              (😘 "Face Blowing a Kiss")
              (😗 "Kissing Face")
              (☺️ "Smiling Face")
              (😚 "Kissing Face with Closed Eyes")
              (😙 "Kissing Face with Smiling Eyes")
              (🥲 "Smiling Face with Tear")
              (😋 "Face Savoring Food")
              (😛 "Face with Tongue")
              (😜 "Winking Face with Tongue")
              (🤪 "Zany Face")
              (😝 "Squinting Face with Tongue")
              (🤑 "Money-Mouth Face")
              (🤗 "Hugging Face")
              (🤭 "Face with Hand Over Mouth")
              (🤫 "Shushing Face")
              (🤔 "Thinking Face")
              (🤐 "Zipper-Mouth Face")
              (🤨 "Face with Raised Eyebrow")
              (😐 "Neutral Face")
              (😑 "Expressionless Face")
              (😶 "Face Without Mouth")
              (😏 "Smirking Face")
              (😒 "Unamused Face")
              (🙄 "Face with Rolling Eyes")
              (😬 "Grimacing Face")
              (🤥 "Lying Face")
              (😌 "Relieved Face")
              (😔 "Pensive Face")
              (😪 "Sleepy Face")
              (🤤 "Drooling Face")
              (😴 "Sleeping Face")
              (😷 "Face with Medical Mask")
              (🤒 "Face with Thermometer")
              (🤕 "Face with Head-Bandage")
              (🤢 "Nauseated Face")
              (🤮 "Face Vomiting")
              (🤧 "Sneezing Face")
              (🥵 "Hot Face")
              (🥶 "Cold Face")
              (🥴 "Woozy Face")
              (😵 "Dizzy Face")
              (🤯 "Exploding Head")
              (🤠 "Cowboy Hat Face")
              (🥳 "Partying Face")
              (🥸 "Disguised Face")
              (😎 "Smiling Face with Sunglasses")
              (🤓 "Nerd Face")
              (🧐 "Face with Monocle")
              (😕 "Confused Face")
              (😟 "Worried Face")
              (🙁 "Slightly Frowning Face")
              (☹️ "Frowning Face")
              (😮 "Face with Open Mouth")
              (😯 "Hushed Face")
              (😲 "Astonished Face")
              (😳 "Flushed Face")
              (🥺 "Pleading Face")
              (😦 "Frowning Face with Open Mouth")
              (😧 "Anguished Face")
              (😨 "Fearful Face")
              (😰 "Anxious Face with Sweat")
              (😥 "Sad but Relieved Face")
              (😢 "Crying Face")
              (😭 "Loudly Crying Face")
              (😱 "Face Screaming in Fear")
              (😖 "Confounded Face")
              (😣 "Persevering Face")
              (😞 "Disappointed Face")
              (😓 "Downcast Face with Sweat")
              (😩 "Weary Face")
              (😫 "Tired Face")
              (🥱 "Yawning Face")
              (😤 "Face with Steam From Nose")
              (😡 "Pouting Face")
              (😠 "Angry Face")
              (🤬 "Face with Symbols on Mouth")
              )
         for nom  = (s-replace " " "_" name)
         for desc = (s-collapse-whitespace (or description ""))
         concat (concat
                 ;; f_… ⇒ get emoji from company menu showing only name & emoji
                 (format "\n** f_%s: %s %s \n%s" nom emoji "" emoji)
                 ;; fd_… ⇒ get emoji from company menu showing name, emoji, & ‘d’escription
                 (format "\n** fd_%s: %s %s \n%s" nom emoji desc emoji)))
;; Get all unicode emojis to appear within Emacs
;; See also: https://emacs.stackexchange.com/questions/5689/force-a-single-font-for-all-unicode-glyphs?rq=1
(unless noninteractive (set-fontset-font t nil "Apple Color Emoji"))

4.5.10.   my_⋯ Templates to obtain User Information

Let's add templates for links to common user information ^_^

** my_name: User's name
`user-full-name`

** my_email: User's email address
`user-mail-address`

** my_github: User's Github repoistory link
https://github.com/alhassy/

** my_emacsdrepo: User's version controlled Emacs init file
https://github.com/alhassy/emacs.d

** my_blog: User's blog website
https://alhassy.github.io/

** my_webpage: User's organisation website
http://www.cas.mcmaster.ca/~alhassm/

** my_twitter: User's Twitter profile
https://twitter.com/musa314

** my_masters_thesis
A Mechanisation of Internal Galois Connections In Order Theory Formalised Without Meets
https://macsphere.mcmaster.ca/bitstream/11375/17276/2/thesis.pdf

It may be useful to also have Org-link variants of these …

4.5.11. Templates from other places in my init

In this setup, I have some templates appear elsewhere, tagged with :noweb-ref templates-from-other-places-in-my-init. They are presented in natural positions, but can only occur to the machine after template expansion is setup. Using org-mode, we are able to present code in any order and tangle it to the order the compilers need it to be!

Let's activate all such templates, now after template expansion has been setup.

#+begin_src org :noweb yes :tangle "~/.emacs.d/yankpad.org" :comments none
<<templates-from-other-places-in-my-init>>
#+end_src

You can press C-c C-v C-v, org-babel-expand-src-block, to see what this block expands into…

Expansion
** journal_guided: Introspection & Growth
I'm writing from ${1:location}.

Gut answer, today I feel ${2:scale}/10.
⇒ ${3:Few words or paragraphs to explain what's on your mind.}

${4: All things which cause us to groan or recoil are part of the tax of
life. These things you should never hope or seek to escape.  Life is a battle,
and to live is to fight.

⟨ Press TAB once you've read this mantra. ⟩
$(when yas-moving-away-p "")
}
`(progn
  (eww "https://www.dailyinspirationalquotes.in/")
  (sit-for 2) (when nil let eww load)
  (read-only-mode -1)
  (goto-line 52)
  (kill-line)
  (kill-buffer)
  (yank))`
${7:
Self Beliefs:
+ I am working on a healthier lifestyle, including a low-carb diet.

  - I’m also investing in a healthy, long-lasting relationship.

  ➩ These are what I want and are important to me. ⇦

+ I will not use any substances to avoid real issues in my life. I must own them.

+ Everything I’m searching for is already inside of me.

+ Progress is more important than perfection.

⟨ Press TAB once you've read these beliefs. ⟩
$(when yas-moving-away-p "")
}

*Three things I'm grateful for:*
1. ${8:??? … e.g., old relationship, something great yesterday, an opportunity I
   have today, something simple near me within sight}
2. ${9:??? … e.g., old relationship, something great yesterday, an opportunity I
   have today, something simple near me within sight}
3. ${10:??? … e.g., old relationship, something great yesterday, an opportunity I
   have today, something simple near me within sight}

*Three things that would make today great:*
1. ${11:???}
2. ${12:???}
3. ${13:???}

*What one thing is top of mind today?*
${14:???}

*What’s one opportunity I want to go after?*
${15:???}

*What’s one thing I’m really proud of OR I’m amazed and in awe of?*
${16:???}

$0

Note: Since I've insisted that Org blocks are space sensative, any whitespace before the <<⋯>> will propogate to the resulting extracted code.

Warning!

This section had

:PROPERTIES:
:CUSTOM_ID: Templates-from-other-places-in-my-init
:END:

Which, as of Org 9.4, led to the entire section being tangled: This is what the above incantation requested, but I thought it only worked on src blocks, having the specified :noweb-ref, not on :CUSTOM_ID: incidentally having the same name.

4.6. Prettify inline source code

;; Show “ src_emacs-lisp[:exports results]{ 𝒳 } ” as “ ℰ𝓁𝒾𝓈𝓅﴾ 𝒳 ﴿ ”.
;;
(font-lock-add-keywords 'org-mode
  '(("\\(src_emacs-lisp\\[.*]{\\)\\([^}]*\\)\\(}\\)"
  (1 '(face (:inherit (bold) :foreground "gray65") display "ℰ𝓁𝒾𝓈𝓅﴾"))
  (2 '(face (:foreground "blue")))
  (3 '(face (:inherit (bold) :foreground "gray65") display "﴿"))
    )))
;;
;; Let's do this for all my languages:
;; Show “ src_LANGUAGE[…]{ ⋯ } ” as “ ﴾ ⋯ ﴿ ”.
(cl-loop for lang in my/programming-languages
         do (font-lock-add-keywords 'org-mode
               `(( ,(format "\\(src_%s\\[.*]{\\)\\([^}]*\\)\\(}\\)" lang)
                  (1 '(face (:inherit (bold) :foreground "gray65") display "﴾"))
                  (2 '(face (:foreground "blue")))
                  (3 '(face (:inherit (bold) :foreground "gray65") display "﴿"))
                  ))))

;;
(defun my/toggle-line-fontification ()
  "Toggle the fontification of the current line"
  (interactive)
  (defvar my/toggle-fontify/current-line -1)
  (defvar my/toggle-fontify/on? nil)
  (add-to-list 'font-lock-extra-managed-props 'display)
  (let ((start (line-beginning-position)) (end (line-end-position)))
    (cond
     ;; Are we toggling the current line?
     ((= (line-number-at-pos) my/toggle-fontify/current-line)
      (if my/toggle-fontify/on?
          (font-lock-fontify-region start end)
        (font-lock-unfontify-region start end))
      (setq my/toggle-fontify/on? (not my/toggle-fontify/on?)))
     ;; Nope, we've moved on to another line.
     (:otherwise
      (setq my/toggle-fontify/current-line (line-number-at-pos)
            my/toggle-fontify/on? :yes_please_fontify)
      (font-lock-unfontify-region  start end)))))

  ;; TODO FIXME; maybe ignore: Wasted too much time here already.
;; (add-hook 'post-command-hook #'my/toggle-line-fontification nil t)
;; (font-lock-add-keywords nil '((my/toggle-line-fontification)) t)

5. Life within Org-mode

It's hard to estimate how long a task takes if you don't keep track of time spent by ‘clocking-in and clocking-out’ of tasks. We can ‘capture’ todos right in the middle of a task without context-switching; e.g., no opening a todos file! After some reflection on the relative importance of the tasks, we can schedule them into our ‘agenda’.

Let's do this!

5.1. Using Org-Mode as a Day Planner

⟪ This section is based on a dated, yet delightful, tutorial of the same title by John Wiegley. ⟫

We want a day-planner with the following use:

  1. “Mindlessly” & rapidly create new tasks.
  2. Schedule and archive tasks at the end, or start, of the work day.
  3. Glance at a week's tasks, shuffle if need be.
  4. Prioritise the day's tasks. Aim for ≤15 tasks.
  5. Progress towards completion of A tasks by documenting work completed.
  6. Repeat! During the day, if anything comes up, capture it and intentionally forget about it.

A 1-page, 3-column, PDF summarising the ideas and keybindings of this section (งಠ_ಠ)ง

5.1.1. Capturing ideas & notes without interrupting the current workflow

Capture lets me quickly make notes & capture ideas, with associated reference material, without any interruption to the current work flow. Without losing focus on what you're doing, quickly jot down a note of something important that just came up.

‘my/org-capture’ Implementation
(cl-defun my/org-capture-buffer (&optional keys no-additional-remarks
                                           (heading-regexp "Subject: \\(.*\\)"))
  "Capture the current [narrowed] buffer as a todo/note.

This is mostly intended for capturing mail as todo tasks ^_^

When NO-ADDITIONAL-REMARKS is provided, and a heading is found,
then make and store the note without showing a pop-up.
This is useful for when we capture self-contained mail.

The HEADING-REGEXP must have a regexp parenthesis construction
which is used to obtain a suitable heading for the resulting todo/note."
  (interactive "P")
  (let* ((current-content (substring-no-properties (buffer-string)))
         (heading         (progn (string-match heading-regexp current-content)
                                 (or (match-string 1 current-content) ""))))
    (org-capture keys)
    (insert heading "\n\n\n\n" (s-repeat 80 "-") "\n\n\n" current-content)

    ;; The overtly verbose conditions are for the sake of clarity.
    ;; Moreover, even though the final could have “t”, being explicit
    ;; communicates exactly the necessary conditions.
    ;; Being so verbose leads to mutual exclusive clauses, whence order is irrelevant.
    (cond
     ((s-blank? heading)
        (beginning-of-buffer) (end-of-line))
     ((and no-additional-remarks (not (s-blank? heading)))
        (org-capture-finalize))
     ((not (or no-additional-remarks (s-blank? heading)))
        (beginning-of-buffer) (forward-line 2) (indent-for-tab-command)))))

With that in-hand, we use a wrapper to org-capture to make use of it.

(defun my/org-capture (&optional prefix keys)
  "Capture something!

      C-c c   ⇒ Capture something
C-u   C-c c   ⇒ Capture current [narrowed] buffer.
C-u 5 C-c c   ⇒ Capture current [narrowed] buffer without adding additional remarks.
C-u C-u C-c c ⇒ Goto last note stored.

At work, ‘C-c c’ just captures notes under ‘Tasks’; no menu used."
  (interactive "p")
  (pcase prefix
    (4     (my/org-capture-buffer keys))
    (5     (my/org-capture-buffer keys :no-additional-remarks))
    (t     (if my/personal-machine?
               (org-capture prefix keys)
             (org-capture prefix "t")))))

  • C-c c Capture something
  • C-u C-c c Capture current [narrowed] buffer.
  • C-u 5 C-c c Capture current [narrowed] buffer without adding additional remarks.
  • C-u C-u C-c c Goto last note stored.

E.g., I have a task, or something I wish to note down, rather than opening some file, then making a heading, then writing it; instead, I press C-c c t and a pop-up appears, I make my note, and it disappears —with my notes file(s) now being altered! Moreover, by default it provides a timestamp and a link to the file location where I made the note —helpful for tasks, tickets, to be tackled later on.

;; Location of my todos / captured notes file
(unless noninteractive
  (setq org-default-notes-file
        (if my/personal-machine?
            "~/Dropbox/todo.org"
          "~/Desktop/Work-2022-01-01.org")))

;; “C-c c” to quickly capture a task/note
(define-key global-map "\C-cc" #'my/org-capture) ;; See above.
By default we only get a ‘tasks’ form of capture, let's add some more.
(cl-defun my/make-org-capture-template
   (shortcut heading &optional (no-todo nil) (description heading) (scheduled nil))
  "Quickly produce an org-capture-template.

  After adding the result of this function to ‘org-capture-templates’,
  we will be able perform a capture with “C-c c ‘shortcut’”
  which will have description ‘description’.
  It will be added to the tasks file under heading ‘heading’.

no-todo’ omits the ‘TODO’ tag from the resulting item; e.g.,
  when it's merely an interesting note that needn't be acted upon.

  Default for ‘description’ is ‘heading’. Default for ‘no-todo’ is ‘nil’.

  Scheduled items appear in the agenda; true by default.

  The target is ‘file+headline’ and the type is ‘entry’; to see
  other possibilities invoke: C-h o RET org-capture-templates.
  The “%?” indicates the location of the Cursor, in the template,
  when forming the entry.
  "
  `(,shortcut ,description entry
      (file+headline org-default-notes-file ,heading)
         ,(concat "*" (unless no-todo " TODO") " %?\n"
                (when nil ;; this turned out to be a teribble idea.
                  ":PROPERTIES:\n:"
                (if scheduled
                    "SCHEDULED: %^{Any time ≈ no time! Please schedule this task!}t"
                  "CREATED: %U")
                "\n:END:") "\n\n ")
      :empty-lines 1 :time-prompt t))
(setq org-capture-templates
      (cl-loop for (shortcut heading)
            in (-partition 2 '("t" "Tasks, Getting Things Done"
                               "r" "Reference Material"
                               "m" "Email"
                               "e" "Emacs (•̀ᴗ•́)و"
                               "i" "Islam"
                               "b" "Blog"
                               "a" "Arbitrary Reading and Learning"
                               "l" "Programming Languages"
                               "p" "Personal Matters"))
            collect  (my/make-org-capture-template shortcut heading)))

Rather than adding notes to particular Org headings in my todo.org file, I could defer such a choice by having only one template and have C-c a automatically use it. Then I could ‘refile’ tasks to their appropriate parent headings with w. This allows us to seperate the concerns of capturing ideas from doing any form of processing. Something to consider.

;; Update: Let's schedule tasks during the GTD processing phase.
;;
;; For now, let's automatically schedule items a week in advance.
;; TODO: FIXME: This overwrites any scheduling I may have performed.
;; (defun my/org-capture-schedule ()
;;   (org-schedule nil "+7d"))
;;
;; (add-hook 'org-capture-before-finalize-hook 'my/org-capture-schedule)

For now I capture everything into a single file. One would ideally keep separate client, project, information in its own org file.

  • ⇒ Org capture actually lets us add any type of entry, ‘programmable template’, to any type of file! ⇐
    • Look at my/make-org-capture-template, above, to notice that capture actually lets you add any type of item to any file.
  • ( For now, I'm only using it to add entries to my tasks lists. )
  • Org-protocol is a way to create capture notes in org-mode from other applications.

Let's also ensure TODO-s respect hierarchical structure.

;; Cannot mark an item DONE if it has a  TODO child.
;; Conversely, all children must be DONE in-order for a parent to be DONE.
(setq org-enforce-todo-dependencies t)

Where am I currently capturing?

  • During meetings, when a nifty idea pops into my mind, I quickly capture it.
    • I've found taking my laptop to meetings makes me an active listener and I get much more out of my meetings since I'm taking notes.
  • Through out the day, as I browse the web, read, and work; random ideas pop-up, and I capture them indiscriminately.
  • I envision that for a phone call, I would open up a capture to make note of what the call entailed so I can review it later.
  • Yet another place to capture content is from mail, such as for reference material, or self-contained tasks.
  • Anywhere you simply want to make a note, for the current heading, just press C-c C-z. The notes are just your remarks along with a timestamp; they are collected at the top of the tree, under the heading.

      ;; Ensure notes are stored at the top of a tree.
      (setq org-reverse-note-order nil)
    

Quickly look up some reference material…

(cl-defun my/reference (&optional (file org-default-notes-file))
  "Look up some reference material super quick.

By default we look in user's Org TODOs file.

FILE should be an ORG file with a top-level heading that starts with ‘Reference’.
We show its subheadings in a completing-read menu, then narrow to that entry."
  (interactive)
  (find-file file)
  (widen)
  (goto-char (point-min))
  (re-search-forward "^* Reference") ;; Start of line.
  (org-narrow-to-subtree)
  (org-cycle) (org-cycle)
  (let* ((headings (org-map-entries (lambda () (org-element-property :title (org-element-at-point)) ) "LEVEL=2"))
         (topic (completing-read "What to review? " headings)))
    (search-forward (concat "** " topic))
    (org-narrow-to-subtree)
    (org-cycle)))

(defalias 'my/review-reference-notes 'my/reference)
(defalias 'w-reference 'my/reference) ;; “w”ork

5.1.2. Step 1: When new tasks come up

Isn't it great that we can squirrel away info into some default location then immediately return to what we were doing before —with speed & minimal distraction! ♥‿♥ Indeed, if our system for task management were slow then we may not produce tasks and so forget them altogether! щ(゜ロ゜щ)

  • Entering tasks is a desirably impulsive act; do not make any further scheduling considerations.

    The next step, the review stage occurring at the end or the start of the workday, is for processing.

The reason for this is that entering new tasks should be impulsive, not reasoned. Your reasoning skills are required for the task at hand, not every new tidbit. You may even find that during the few hours that transpire between creating a task and categorizing it, you’ve either already done it or discovered it doesn’t need to be done at all! ---John Wiegley

When my computer isn't handy, I'll make a note on my phone then transfer it later.

5.1.3. Step 2: Filing your tasks

At a later time, a time of reflection, we go to our tasks list and actually schedule time to get them done by C-c C-s, org-schedule, then pick a date by entering a number in the form +𝓃 to mean that task is due 𝓃 days from now.

  • Tasks with no due date are ones that “could happen anytime”, most likely no time at all.
  • At least schedule tasks reasonably far off in the future, then reassess when the time comes.
  • An uncompleted task is by default rescheduled to the current day, each day, along with how overdue it is.

    • Aim to consciously reschedule such tasks!

    Let's keep track of how many times, and when, we have pushed events to other dates.

    ;; Add a note whenever a task's deadline or scheduled date is changed.
    (setq org-log-redeadline 'time)
    (setq org-log-reschedule 'time)
    

custard

With time, it will become clear what is an unreasonable day verses what is an achievable day.

Repeat tasks by a repeater such as ‘+1m’ or ‘+7d’ in their timestamps; e.g., :SCHEDULED: <2005-10-01 Sat +1m>. Likewise, to schedule an event (to repeat) for multiple days, just use a bunch of timestamps (with repeaters): :SCHEDULED: <2022-01-04 Tues 9:00 +1w><2022-01-06 Thu +1w><2022-01-07 Fri 9:00 +1w>. (Notice that we have : on each side of the keyword; and we have a time of day for the event.)


A ‘project’ is a task that has multiple steps, each as a checkbox item. It can be given a percentage marker to show progress: Place [%] after its name, then press C-c # ---org-update-statistics-cookies— on the name to see a completion percentage —press C-c C-c on a checkbox item to toggle its completion state.

5.1.4. Step 3: Quickly review the upcoming week

The next day we begin our work, we press C-c a a to see the scheduled tasks for this week ---C-c C-s to re-schedule the task under the cursor and r to refresh the agenda.

(define-key global-map "\C-ca" 'org-agenda)
  • Show the next 𝓃 days schedule ⇐ C-u 𝓃 C-c a a.

Org agenda is an interactive tool for generating summary reports from Org data —e.g., commonly, the weekly task list is generated from todo tasks.

The agenda dispatch menu, C-c a, has options for displaying tasks —e.g., C-c a m generates a list of entries having the same tags. new ways to view tasks by altering the org-agenda-custom-commands variable —e.g., above we added two, one for completed tasks and one for unscheduled tasks.

Let's setup the basics of our agenda.
;; List of all the files & directories where todo items can be found. Only one
;; for now: My default notes file.
(setq org-agenda-files (list org-default-notes-file))

;; Display tags really close to their tasks.
(setq org-agenda-tags-column -10)

;; How many days ahead the default agenda view should look
(setq org-agenda-span 'day)
;; May be any number; the larger the slower it takes to generate the view.
;; One day is thus the fastest ^_^

;; How many days early a deadline item will begin showing up in your agenda list.
(setq org-deadline-warning-days 14)

;; In the agenda view, days that have no associated tasks will still have a line showing the date.
(setq org-agenda-show-all-dates t)

;; Scheduled items marked as complete will not show up in your agenda view.
(setq org-agenda-skip-scheduled-if-done t)
(setq org-agenda-skip-deadline-if-done  t)
Super Simple ‘agenda’ Mini-tutorial

The agenda view, like nearly all Emacs entities, is interactive:

  • 𝓃 f,b ⇒ Look forward at next week's agenda, or backward to a previous week.
    • The optional \(𝓃\) means do the action 𝓃-many times; it defaults to 1.
  • w, d ⇒ toggle week view, or day view; use v to see possible views.
    • E.g., C-u 2017 v y shows us the specific year 2017.
  • 𝓃 n,p to navigate to next and previous entries.
  • t ⇒ cycle TODO state of the current entry.
  • ± ⇒ cycle priority state.
  • 𝓃 S-⇆ ⇒ Shift date time by \(𝓃\) days; 1 day by default.
  • C-c C-s ⇒ Reschedule an entry; prefix it with C-u to remove a scheduled entry.

    in their timestamps; e.g., DEADLINE: <2005-10-01 Sat +1m>.

  • s ⇒ save all agenda buffers; i.e., save the org-files where the agenda items live.
  • g ⇒ Rebuild agenda according to any changes made thus far.
  • F ⇒ Toggle ‘follow mode’: As you go up/down entries, you can see their details in an adjacent window.
    • SPC ⇒ Show details of a single entry in other window; stay in Agenda.
  • RET, TAB ⇒ Go to the current entry in the current window or in a new adjacent window, so as to alter task details.

The agenda view ––even in the 7-days-at-a-time view–– will always begin on the current day. This is important, since while using org-mode as a day planner, you never want to think of days gone past. That’s something you do in other ways, such as when reviewing completed tasks.

(setq org-agenda-start-on-weekday nil)

;; Start each agenda item with ‘’, then show me it's %timestamp and how many
;; times it's been re-%scheduled.
(setq org-agenda-prefix-format " ○ %t%s")
Grouping agenda entries together

Instead of having the day's tasks all in one field, org-super-agenda allows us to use predicates to group entries together; e.g., by considering an entry's :tags: or its priority level. Since I'm placing all my tasks in a single file, under appropriate parent headings, I want entries to be shown according to their parent heading. Of-course, the top-most grouping, the important tasks, should be pulled out of their group and placed at the top.

(use-package origami)
(use-package org-super-agenda
  :hook (org-agenda-mode . origami-mode) ;; Easily fold groups via TAB.
  :bind (:map org-super-agenda-header-map ("<tab>" . origami-toggle-node))
  :config
  (org-super-agenda-mode)
  (setq org-super-agenda-groups
        '((:name "Important" :priority "A")
          (:name "Personal" :habit t)
          ;; For everything else, nicely display their heading hierarchy list.
          (:auto-map (lambda (e) (org-format-outline-path (org-get-outline-path)))))))

;; MA: No noticable effect when using org-super-agenda :/
;;
;; Leave new line at the end of an entry.
;; (setq org-blank-before-new-entry '((heading . t) (plain-list-item . t)))

The org-super-agenda homepage shows complex configurations and pleasant screenshots contrasting with and without the system. E.g., you can change how entries in particular headings are displayed and coloured.

5.1.5. Step 4: Getting ready for the day

After having seen our tasks for the week, we press d to enter daily view for the current day. Now we decide whether the items for today are A: of high urgency & important; B: of moderate urgency & importance; or C: Pretty much optional, or very quick or fun to do.

  • A tasks should be both important and urgently done on the day they were scheduled.
    • Such tasks should be relatively rare!
    • If you have too many, you're anxious about priorities and rendering priorities useless.
  • C tasks can always be scheduled for another day without much worry.
    • Act! If the thought of rescheduling causes you to worry, upgrade it to a B or A.
  • As such, most tasks will generally be priority B: Tasks that need to be done, but the exact day isn't as critical as with an A task. These are the “bread and butter” tasks that make up your day to day life.

On a task item, or any org-heading, press , then one of A/B/C to set its priority. Then r to refresh.

Pretty Prioritisation Markers

Let's set four priority levels and their colours: The more intense colours are for more urgent tasks.

(setq org-lowest-priority ?C) ;; Now org-speed-eky ‘,’ gives 3 options
(setq org-priority-faces
'((?A :foreground "red"            :weight bold) ;; :background "LightCyan1")
  (?B :foreground "orange"         :weight bold)
  (?C :foreground "green"          :weight bold)))
;; See all colours with: M-x list-colors-display
  • C-c , anywhere to set the priority of the current heading.
    • We may press A-D or SPC to an remove existing priority.

Priority markers are of the form [#𝒳], the fancy priorities package visually renders them as words or icons.

(use-package org-fancy-priorities
  :diminish org-fancy-priorities-mode
  :hook   (org-mode . org-fancy-priorities-mode)
  :custom (org-fancy-priorities-list '("High" "MID" "LOW")) ;; "OPTIONAL"
  ;; Let's use the “Eisenhower map of priority”…
  ;; :custom (org-fancy-priorities-list '("Urgent and Important"     ;; Do now!
  ;;                                      "Not Urgent But Important" ;; Do schedule this.
  ;;                                      "Urgent But Not Important" ;; Delegate?
  ;;                                      "Not Urgent and Not Important")) ;; Don't do / Optional
  )

At work, my tasks list is massive and constantly growing; so to avoid stressing myself out, I try to add time effort estimates to my tasks with org-set-effort then I open my Org agenda and run d (for day view) then org-agenda-columns (C-c C-x C-c) to see a column-view of my time estimates along with a total time estimate for the day. Since there are unexpected pair-programming calls, or meetings go longer than expected, I only schedule for 7 hours each day (in a usual 8-hour work day); i.e., if my time estimates exceed 7h then I reschedule or cancel some things.

(require 'org-agenda)

;; How should the columns view look?
(setq org-columns-default-format   "%60ITEM(Task) %6Effort(Estim){:} %3PRIORITY %TAGS")

;; Press “c” in Org agenda to see the columns view; (default binding C-c C-x C-c is too long!)
(org-defkey org-agenda-mode-map "c" #'org-agenda-columns)
(org-defkey org-agenda-mode-map "C" #'org-agenda-goto-calendar)

;; Press “e” in columns view to alter “e”ffort “e”stimates.
(require 'org-colview)
(org-defkey org-columns-map "e"
            ;; Refresh after making an effort estimate.
            (lambda () (interactive) (org-agenda-set-effort) (org-agenda-columns)))

Near the top of my TODOS file, I have the following clickable link: It looks pretty and when I click it, takes me where I need to go.

[[elisp:(progn (org-agenda nil "a") (org-agenda-columns) (delete-other-windows))][Show agenda with time estimates]]

5.1.6. Step 5: Doing the work

Since A tasks are the important and urgent ones, if you do all of the A tasks and nothing else today, no one would suffer. It's a good day (─‿‿─).

There should be no scheduling nor prioritising at this stage. You should not be touching your tasks file until your next review session: Either at the end of the day or the start of the next.

  • Leverage priorities! E.g., When a full day has several C tasks, reschedule them for later in the week without a second thought.
    • You've already provided consideration when assigning priorities.

5.1.7. Step 6: Moving a task toward completion

My workflow states are described in the section 5.5 and contain states: TODO, STARTED, WAITING, ON_HOLD, CANCELLED, DONE.

  • Tasks marked WAITING are ones for which we are awaiting some event, like someone to reply to our query. As such, these tasks can be rescheduled until I give up or the awaited event happens —in which case I go to STARTED and document the reply to my query.
  • The task may be put off indefinitely with ON_HOLD, or I may choose never to do it with CANCELLED. Along with DONE, these three mark a task as completed and so it needn't appear in any agenda view.

I personally clock-in and clock-out of tasks —keep reading—, where upon clocking-out I'm prompted for a note about what I've accomplished so far. Entering a comment about what I've done, even if it's very little, feels like I'm getting something done. It's an explicit marker of progress.

In the past, I would make a “captain's log” at the end of the day, but that's like commenting code after it's written, I didn't always feel like doing it and it wasn't that important after the fact. The continuous approach of noting after every clock-out is much more practical, for me at least.

5.1.8. Step 7: Archiving Tasks

During the review state, when a task is completed, ‘archive’ it with org-archive-subtree or C-c C-x C-s: This marks it as done, adds a time stamp, and moves it to a local *.org_archive file. What was our ‘to do’ list becomes a ‘ta da’ list showcasing all we have done (•̀ᴗ•́)و

Archiving keeps task lists clutter free, but unlike deletion it allows us, possibly rarely, to look up details of a task or what tasks were completed in a certain time frame —which may be a motivational act, to see that you have actually completed more than you thought, provided you make and archive tasks regularly. We can use M-x org-search-view to search an org file and the archive file too, if we enable it so.

;; C-c a s ➩ Search feature also looks into archived files.
;; Helpful when need to dig stuff up from the past.
(setq org-agenda-text-search-extra-files '(agenda-archives))
;; enables the org-agenda variables.
(require 'org-agenda) ;; Need this to have “org-agenda-custom-commands” defined.

(unless noninteractive
    ;; ➩ Show my agenda upon Emacs startup.
    (org-agenda "a" "a"))

Let's install some helpful views for our agenda.

  • C-c a c: See completed tasks at the end of the day and archive them.

    ;; Pressing ‘c’ in the org-agenda view shows all completed tasks,
    ;; which should be archived.
    (add-to-list 'org-agenda-custom-commands
      '("c" todo "DONE|ON_HOLD|CANCELLED" nil))
    
  • C-c a u: See unscheduled, undeadlined, and undated tasks in my todo files. Which should then be scheduled or archived.

    (add-to-list 'org-agenda-custom-commands
      '("u" alltodo ""
         ((org-agenda-skip-function
            (lambda ()
                  (org-agenda-skip-entry-if 'scheduled 'deadline 'regexp  "\n]+>")))
                  (org-agenda-overriding-header "Unscheduled TODO entries: "))))
    

At the end of the day, let's schedule at least 3 things that must be done the next day; i.e., have priority A.

5.2. Tag! You're it!

Even when items are categorised under their own parent headings, they may be related in some way and that can made explicit by adding a :tag: to their headings; e.g., two entries both have the :jasim:@work: tags, then looking for the :@work: tag shows me all entries that are tagged as “at work”.

Tags provide a cross-section of one's entries.

Tags let us find related stuff quickly, even though they're differently categorised.

After calling org-agenda, we may select m to match for tags, or use org-tags-view to search for tags.

What to tag? Common tags are :@laptop:, :@work:, :@home: to identify the location where tasks take place —Use: When I'm at a particular place, I need only consider tasks that apply to that place ;-) Other tags I use are :𝑭𝑳: to identify remarks or email or request from person 𝑭irstname 𝑳astname; or something that might be interesting to that person. I also use :video:, :book:, :paper:; which let me quickly find all videos! Finally, I also use :project_name: to identify notes that may be of interest to a particular project, but are more appropriately categorised elsewhere —e.g., when learning about an Emacs feature, I may tag my notes with another project's name to consider whether that feature could be useful there.

How to tag?

You can just add a :tag₁:⋯:tagₙ: after a heading. If you press space, before the tags, then they are automatically indented flushright to column 77; postive numbers do not flushright but use exact column number.

 (setq org-tags-column -77) ;; the default

Use C-c C-q, or org-set-tags-command, on a heading or just the speed key : on the asterisks of a heading to set the tags of an item —as usual, with Helm we obtain a window of all existing tags to select from. Unfortunatley, this only supports having one tag; for more, you can add them in manually or …

;; Press ‘:’ on a heading to add a tag, press TAB to see all tags, press RETURN on a tag to add it, press TAB again to add more tags, when all done press RETURN twice.
(use-package helm-org) ;; Helm for org headlines and keywords completion.
(add-to-list 'helm-completing-read-handlers-alist
             '(org-set-tags-command . helm-org-completing-read-tags))

;; Also provides: helm-org-capture-templates

Now : or C-c C-q will show existing tags for the current heading, press TAB to obtain a list of all exisiting tags, press C-SPC to select the desired tags, then TAB or RET to confirm the resulting tag list, and RET to finish or TAB to select more tags.

Let's render tags by Unicode symbols.

(use-package org-pretty-tags
  :diminish org-pretty-tags-mode
  :demand t
  :config
   (setq org-pretty-tags-surrogate-strings
         '(("Neato"    . "💡")
           ("Blog"     . "✍")
           ("Audio"    . "♬")
           ("Video"    . "📺")
           ("Book"     . "📚")
           ("Running"  . "🏃")
           ("Question" . "❓")
           ("Wife"     . "💕")
           ("Text"     . "💬") ; 📨 📧
           ("Friends"  . "👪")
           ("Self"     . "🍂")
           ("Finances" . "💰")
           ("Car"      . "🚗") ; 🚙 🚗 🚘
           ("Urgent"   . "🔥"))) ;; 📥 📤 📬
   (org-pretty-tags-global-mode 1))

5.3. Automating Pomodoro —“Commit for only 25 minutes!”

Effort estimates are for an entire task. Yet, sometimes it's hard to even get started on some tasks.

  • The code below ensures a 25 minute timer is started whenever clocking in happens.
    • The timer is in the lower right of the modeline.
  • When the timer runs out, we get a notification.
  • We may have the momentum to continue on the difficult task, or clock-out and take a break after documenting what was accomplished.
;; Tasks get a 25 minute count down timer
(setq org-timer-default-timer 25)

;; Use the timer we set when clocking in happens.
(add-hook 'org-clock-in-hook
  (lambda () (org-timer-set-timer '(16))))

;; unless we clocked-out with less than a minute left,
;; show disappointment message.
(add-hook 'org-clock-out-hook
  (lambda ()
  (unless (s-prefix? "0:00" (org-timer-value-string))
     (message-box "The basic 25 minutes on this difficult task are not up; it's a shame to see you leave."))
     (org-timer-stop)))

Note that this does not conflict with the total effort estimate for the task.

(I'm told there's a package already made for this —maybe I need to stop writing code, and do more searches; then again, I've learned a lot by writing code.)

5.4. Journaling

Thus far I've made it easy to quickly capture ideas and tasks, not so much on the analysis phase:

  • What was accomplished today?
  • What are some notably bad habits? Good habits?
  • What are some future steps?

Rather than overloading the capture mechanism for such thoughts, let's employ org-journal —journal entries are stored in files such as journal/20190407, where the file name is simply the date, or only one file per year as I've set it up below. Each entry is the week day, along with the date, then each child tree is an actual entry with a personal title preceded by the time the entry was made. Unlike capture and its agenda support, journal ensures entries are maintained in chronological order with calendar support.

Since org files are plain text files, an entry can be written anywhere and later ported to the journal. Or, written directly in the journal file if we add the necessary Org-header: Asterisks and time.

The separation of concerns is to emphasise the capture stage as being quick and relatively mindless, whereas the journaling stage as being mindful. Even though we may utilise capture to provide quick support for including journal entries, I have set my journal to be on a yearly basis —one file per year— since I want to be able to look at previous entries when making the current entry; after all, it's hard to compare and contrast easily unless there's multiple entries opened already.

As such, ideally at the end of the day, I can review what has happened, and what has not, and why this is the case, and what I intend to do about it, and what problems were encountered and how they were solved —in case the problem is encountered again in the future. Consequently, if I encounter previously confronted situations, problems, all I have to do is reread my journal to get an idea of how to progress. Read more about the importance of reviewing your day on a daily basis.

Moreover, by journaling with Org on a daily basis, it can be relatively easy to produce a report on what has been happening recently, at work for example. I'd like to have multiple journals, for work and for personal life, as such I will utilise a prefix argument to obtain my work specific entries.

5.4.1. The Setup

Anyhow, the setup:

(use-package org-journal
  ;; C-u C-c j ⇒ Work journal ;; C-c j ⇒ Personal journal
  :bind (("C-c j" . my/org-journal-new-entry))
  :config
    (setq org-journal-dir         "~/Desktop/" ;; "~/Dropbox/journal/"
          org-journal-file-type   'yearly
          org-journal-file-format "Personal-%Y-%m-%d.org")

    (defun my/org-journal-new-entry (prefix)
      "Open today’s journal file and start a new entry.

  With a prefix, we use the work journal; otherwise the personal journal."
      (interactive "P")
      (let ((org-journal-dir (if prefix "~/Desktop/" org-journal-dir))
            (org-journal-file-format (if prefix "Work-%Y-%m-%d.org" org-journal-file-format)))
        (org-journal-new-entry nil)
        (org-mode)
        (org-show-all))))

5.4.2. Super Terse Tutorial

Bindings available in org-journal-mode, when journaling:

  • C-c C-j: Insert a new entry into the current journal file.
    • Note that keys for org-journal-new-entry shadow those for org-goto.
  • C-c C-s: Search the journal for a string.
    • Note that keys for org-journal-search shadow those for org-schedule.

All journal entries are registered in the Emacs Calendar. To see available journal entries do M-x calendar. Bindings available in the calendar-mode:

  • j: View an entry in a new buffer.
  • i j: ‘I’nsert a new ‘j’ournal entry into the day’s file.
  • f w/m/y/f/F: ‘F’ind, search, in all entries of the current week, month, year, all of time, of in all entries in the future.

All journal entries are registered in the Emacs Calendar. To see available journal entries do M-x calendar. Bindings available in the calendar-mode:

  • j d display an entry; use j r to jump to the new reading buffer; reading is in view-mode: q to quit reading and SPC to scroll.
  • j s w/m/y/f search the journal entries of the current week/month/year or for all time
  • [/] go the previous/next day with journal entries

5.4.3. Guided Journaling

Sometimes it can be tough to journal, but filling in a template can be a way to get started. Later on, we will setup 4.5 which will allow us to write journal_guided then TAB to obtain the template below. Each $𝓃 indicates a position that we may input text, after which we TAB to move to next location.

Just like the undo-tree setup at the start of this read, we use a noweb-ref to present this template in a natural position; then later when template expansion it setup, we request it to be tangled.

** journal_guided: Introspection & Growth
I'm writing from ${1:location}.

Gut answer, today I feel ${2:scale}/10.
⇒ ${3:Few words or paragraphs to explain what's on your mind.}

${4: All things which cause us to groan or recoil are part of the tax of
life. These things you should never hope or seek to escape.  Life is a battle,
and to live is to fight.

⟨ Press TAB once you've read this mantra. ⟩
$(when yas-moving-away-p "")
}
`(progn
  (eww "https://www.dailyinspirationalquotes.in/")
  (sit-for 2) (when nil let eww load)
  (read-only-mode -1)
  (goto-line 52)
  (kill-line)
  (kill-buffer)
  (yank))`
${7:
Self Beliefs:
+ I am working on a healthier lifestyle, including a low-carb diet.

  - I’m also investing in a healthy, long-lasting relationship.

  ➩ These are what I want and are important to me. ⇦

+ I will not use any substances to avoid real issues in my life. I must own them.

+ Everything I’m searching for is already inside of me.

+ Progress is more important than perfection.

⟨ Press TAB once you've read these beliefs. ⟩
$(when yas-moving-away-p "")
}

*Three things I'm grateful for:*
1. ${8:??? … e.g., old relationship, something great yesterday, an opportunity I
   have today, something simple near me within sight}
2. ${9:??? … e.g., old relationship, something great yesterday, an opportunity I
   have today, something simple near me within sight}
3. ${10:??? … e.g., old relationship, something great yesterday, an opportunity I
   have today, something simple near me within sight}

*Three things that would make today great:*
1. ${11:???}
2. ${12:???}
3. ${13:???}

*What one thing is top of mind today?*
${14:???}

*What’s one opportunity I want to go after?*
${15:???}

*What’s one thing I’m really proud of OR I’m amazed and in awe of?*
${16:???}

$0

Besides a bit of webscraping to obtain a daily inspirational quote image, and the necessary yasnippet code, this template was taken from a discussion on Hacker news: “I find journaling indispensable”. In time, I will likely alter it to meet my needs, but I like it as it is right now (•̀ᴗ•́)و

5.5. Workflow States

Here are some of my common workflow states, —the ‘X/Y’ indicates to do action ‘X’ when entering a state and ‘Y’ when leaving it, with ‘!’ denoting a timestamp should be generated and ‘@’ denoting a user note should be made.

(setq org-todo-keywords
      '((sequence "TODO(t)" "STARTED(s@/!)" "|" "DONE(d/!)")
        (sequence "WAITING(w@/!)" "ON_HOLD(h@/!)" "|" "CANCELLED(c@/!)")))

;; Since DONE is a terminal state, it has no exit-action.
;; Let's explicitly indicate time should be noted.
(setq org-log-done 'time)

The @ brings up a pop-up to make a local note about why the state changed. Super cool stuff!

In particular, we transition from TODO to STARTED once 15 minutes, or a reasonable amount, of work has transpired. Since all but one state are marked for logging, we could use the lognotestate logging facility of org-mode, which prompts for a note every time a task’s state is changed.

Entering a comment about what I've done, even if it's very little, feels like I'm getting something done. It's an explicit marker of progress and motivates me to want to change my task's states more often until I see it marked DONE.

Here's how they are coloured,

(setq org-todo-keyword-faces
      '(("TODO"      :foreground "red"          :weight bold)
        ("STARTED"   :foreground "blue"         :weight bold)
        ("DONE"      :foreground "forest green" :weight bold)
        ("WAITING"   :foreground "orange"       :weight bold)
        ("ON_HOLD"   :foreground "magenta"      :weight bold)
        ("CANCELLED" :foreground "forest green" :weight bold)))

Now we press C-c C-t then the letter shortcut to actually make the state of an org heading.

(setq org-use-fast-todo-selection t)

We can also change through states using Shift- left, or right.

Let's draw a state diagram to show what such a workflow looks like.

PlantUML supports drawing diagrams in a tremendously simple format —it even supports Graphviz/DOT directly and many other formats. Super simple setup instructions can be found here; below are a bit more involved instructions. Read the manual here.

;; Install the tool
; (async-shell-command "brew tap adoptopenjdk/openjdk; brew cask install adoptopenjdk13") ;; Dependency
; (async-shell-command "brew install plantuml")

;; Tell emacs where it is.
;; E.g., (async-shell-command "find / -name plantuml.jar")
(setq org-plantuml-jar-path
      "/usr/local/Cellar/plantuml/1.2020.19/libexec/plantuml.jar")

;; Enable C-c C-c to generate diagrams from plantuml src blocks.
(add-to-list 'org-babel-load-languages '(plantuml . t) )
(require 'ob-plantuml)

; Use fundamental mode when editing plantuml blocks with C-c '
(add-to-list 'org-src-lang-modes '("plantuml" . fundamental))

Let's use this!

skinparam defaultTextAlignment center  /' Text alignment '/

skinparam titleBorderRoundCorner 15
skinparam titleBorderThickness 2
skinparam titleBorderColor red
skinparam titleBackgroundColor Aqua-CadetBlue
title My Personal Task States

[*] -> Todo          /' This is my starting state '/
Done -right-> [*]    /' This is an end state      '/
Cancelled -up-> [*]  /' This is an end state      '/

/'A task is “Todo”, then it's “started”, then finally it's “done”. '/
Todo    -right-> Started
Started -down->  Waiting
Waiting -up->    Started
Started -right-> Done

/'Along the way, I may pause the task for some reason then
  return to it. This may be since I'm “Blocked” since I need
  something, or the task has been put on “hold” since it may not
  be important right now, and it may be “cancelled” eventually.
'/

Todo    -down-> Waiting
Waiting -up-> Todo
Waiting -up-> Done

Todo -down-> On_Hold
On_Hold -> Todo

On_Hold -down-> Cancelled
Waiting -down-> Cancelled
Todo    -down-> Cancelled

/' The Org-mode shortcuts for these states are as follows. '/
Todo      : t
On_Hold   : h
Started   : s
Waiting   : w
Cancelled : c
Done      : d

/' If a task is paused, we should document why this is the case. '/
note right  of Waiting:   Note what is\nblocking us.
note right  of Cancelled: Note reason\nfor cancellation.
note bottom of On_Hold:   Note reason\nfor reduced priority.

center footer  ♥‿♥ Org-mode is so cool (•̀ᴗ•́)و
/' Note that we could omit the “center, left, right” if we wished,
   or used a “header” instead.'/

workflow.png

Of note:

  • Multiline comments are with /' comment here '/, single quote starts a one-line comment.
  • Nodes don't need to be declared, and their names may contain spaces if they are enclosed in double-quotes.
  • One forms an arrow between two nodes by writing a line with x ->[label here] y or y <- x; or using --> and <-- for dashed lines. The label is optional.

    To enforce a particular layout, use -X-> where X ∈ {up, down, right, left}.

  • To declare that a node x has fields d, f we make two new lines having x : f and x : d.
  • One adds a note near a node x as follows: note right of x: words then newline\nthen more words.

    Likewise for notes on the left, top, bottom.

    • A note can be on several lines. It's terminated by end note.
  • Interesting sprites and many other things can be done with PlantUML. Read the docs.

This particular workflow is inspired by Bernt Hansen —while quickly searching through the PlantUML manual: The above is known as an “activity diagram” and it's covered in §4.

Org-mode may be used with PlantUML:

  • See §11,12 for using Org-mode notation to form ‘mindmaps’ and ‘work breakdown structures’.
  • Org-mode text formatters are also acknowledged but the delimiters must be doubled; see §16.1.

You can quickly write and see the resulting UMLs using https://liveuml.com/, for the most part.

5.6. Clocking Work Time

Let's keep track of the time we spend working on tasks that we may have captured for ourselves the previous day. Such statistics provides a good idea of how long it actually takes me to accomplish a certain task in the future and it lets me know where my time has gone.

Clock in
on a heading with I, or in the subtree with C-c C-x C-i.
Clock out
of a heading with O, or in the subtree with C-c C-x C-o.
Clock report
See clocked times with C-c C-x C-r.

After clocking out, the start and end times, as well as the elapsed time, are added to a drawer to the heading. We can punch in and out of tasks as many times as desired, say we took a break or switched to another task, and they will all be recorded into the drawer.

;; Record a note on what was accomplished when clocking out of an item.
(setq org-log-note-clock-out t)

To get started, we could estimate how long a task will take and clock-in; then clock-out and see how long it actually took.

Sometimes, at the beginning at least, I would accidentally invoke the transposed command C-x C-c, which saves all buffers and quits Emacs. So here's a helpful way to ensure I don't quit Emacs accidentally.

(setq confirm-kill-emacs 'yes-or-no-p)

A few more settings:

;; Resume clocking task when emacs is restarted
(org-clock-persistence-insinuate)

;; Show lot of clocking history
(setq org-clock-history-length 23)

;; Resume clocking task on clock-in if the clock is open
(setq org-clock-in-resume t)

;; Sometimes I change tasks I'm clocking quickly ---this removes clocked tasks with 0:00 duration
(setq org-clock-out-remove-zero-time-clocks t)

;; Clock out when moving task to a done state
(setq org-clock-out-when-done t)

;; Save the running clock and all clock history when exiting Emacs, load it on startup
(setq org-clock-persist t)

;; Do not prompt to resume an active clock
(setq org-clock-persist-query-resume nil)

;; Include current clocking task in clock reports
(setq org-clock-report-include-clocking-task t)

5.6.1. Finding tasks to clock in

Use one of the following options, with the top-most being the first to be tried.

  • From anywhere, C-u C-c C-x C-i yields a pop-up for recently clocked in tasks.
  • Pick something off today's agenda scheduled items.
  • Pick a Started task from the agenda view, work on this unfinished task.
  • Pick something from the TODO tasks list in the agenda view.

C-c C-x C-d also provides a quick summary of clocked time for the current org file.

5.6.2. Estimates versus actual time

Before clocking into a task, add to the properties drawer :Effort: 1:25 or C-c C-x C-e, for a task that you estimate will take an hour and twenty-five minutes, for example. Now the modeline will mention the time elapsed alongside the task name. Woah!

 (push '("Effort_ALL" . "0:15 0:30 0:45 1:00 2:00 3:00 4:00 5:00 6:00 0:00")
       org-global-properties)

Use speed keys e/E to insert an effort estimate, with the above being provided options, or to increment the current effort to the next one in the above list.

This is also useful when you simply want to put a time limit on a task that wont be completed anytime soon, say writing a thesis or a long article, but you still want to work on it for an hour a day and be warned when you exceed such a time constraint.

When you've gone above your estimate time, the modeline colours it red.

5.7. Habit Formation

The reason to use habits is that they come with a graph indicating consistency by colour, and the goal of the game is to have the longest possible chain —no red days!

A ‘habit’ is a usual (recurring) todo task marked as a habit: Use C-c C-x p to set the STYLE property to habit on a task to set it as a habit.

;; Show habits for every day in the agenda.
(setq org-habit-show-habits t)
(setq org-habit-show-habits-only-for-today nil)

;; This shows the ‘Seinfeld consistency’ graph closer to the habit heading.
(setq org-habit-graph-column 90)

;; In order to see the habit graphs, which I've placed rightwards, let's
;; always open org-agenda in ‘full screen’.
;; (setq org-agenda-window-setup 'only-window)
inch by inch anything's a cinch!

! means today and means a task has been done on that day; intuitively green means you're on track, yellow is warning sign of overdue, red is overdue, and blue is an acceptable break day.

Here's an example habit from the Org-mode manual, where .+𝒳d/𝒴d reads perform the habit once every 𝒳 days, but never let me go 𝒴 days without doing it.

** TODO Shave
   SCHEDULED: <2020-01-08 Wed .+2d/4d>
   :PROPERTIES:
   :STYLE:    habit
   :END:

Shave every 2 days, but we can take a 3-day break; however, on the 4th day, gotta shave! (To “ignore” a habit, just reschedule it for another day.)

Remember that in the agenda view if you alter a task, say with t to mark it done, then you need to use s to save the underlying todo/notes files; otherwise, any g will revert the change in the agenda buffer.

5.8. Actually Doing Things —or Sending notifications from Emacs

Let's setup a little audio-visual reminder to regularly check my agenda and ensure I'm not narrowing on a single task and ignoring others.

  • More generally, we can use this snippet of code to let Emacs notify us of other things.
  • For instance, the message function shows text in the minibuffer, which might be missed when there are multiple incoming messages or focus is on a non-Emacs application (gasp!). Then, my/notify could be used to produce MacOS system-wide notifications.

The text-to-speech tool we'll use is say; which on a Mac can be activated in a browser: Select some text, right-click, select Speech, then Start/Stop Speaking.

  • TODO: Make the command below randomly use an English speaking voice; “tldr say” to learn more.
  • TODO: Also finish reading the above mentioned links, with nice examples.
;; Obtain a notifications and text-to-speech utilities
(system-packages-ensure "say") ;; Built-into MacOS, but can be downloaded in Ubuntu
(system-packages-ensure "terminal-notifier") ;; MacOS specific
;; System Preferences → Notifications → Terminal Notifier → Allow “alerts”.
;; E.g.,: (shell-command "terminal-notifier -title \"Hiya\" -message \"hello\"")

By default, notifications are in banner style —they go away automatically— we can use alert style —in which they stay until dismissed— in MacOS as follows: System Preferences → Notifications → terminal-notifier → Alerts.

(cl-defun my/notify (message &key (titled "") at repeat-every-hour open)
  "Notify user with both an visual banner, with a beep sound, and a text-to-speech recitation.

When the user clicks on the resulting notification, unless a
given OPEN url is provided, the Emacs application is brough into
focus.

MESSAGE and TITLE are strings; AT is a time string as expected of
`run-at-time' such as \"11.23pm\" or \"5 sec\"; REPEAT-EVERY-HOUR
is a floating-point number of hours to continuously repeat the
alert.  OPEN is a URL that is opened when the user clicks the
notification. This can be a web or file URL, or any custom URL
scheme.

I initially used optional arguments, but realised that in due time
it would be more informative to use named arguments instead.

Example uses:

;; In 5 minutes from now, remind me to watch this neato video!
(my/notify \"🔔 Get things done! 📎 💻 \"
  :open \"https://www.youtube.com/watch?v=23tusPiiNZk&ab_channel=Motiversity\"
  :at \"5 minutes\")

;; Remind me to exercise every 1.5hours; starting at 8:00am.
(my/notify \"Take a 5min break and get your blood flowing!\"
           :titled \"Exercise\"
           :at \"8:00am\"
           :repeat-every-hour 1.5)

;; Actually getting things done!
(my/notify \"Is what you're doing actually in alignment with your goals?
           Maybe it's time to do another task?\"
           :titled \"Check your agenda!\"
           :at \"10:00am\"
           :repeat-every-hour 2)
"
  (run-at-time at ;; the time to make the alert
               (when repeat-every-hour (* 60 60 repeat-every-hour))
               #'async-shell-command
               (format "%s"
                       `(terminal-notifier
                         -title    ,(pp-to-string titled)
                         -message  ,(s-replace "\\n" "\n" (pp-to-string message))
                         ;; Play a random sound when the notification appears. See sound names with: ls /System/Library/Sounds
                         ;; Use the special NAME “default” for the default notification sound.
                         -sound ,(progn (require 'seq) (seq-random-elt (s-split "\n" (shell-command-to-string "ls /System/Library/Sounds"))))
                         ;; Don't create duplicates of the notification, just one instance;
                         ;; i.e., each notification belongs to a group and only one alert of the group may be present at any one time.
                         -group  ,(pp-to-string titled)
                         ;; Activate the application specified by ID when the user clicks the notification.
                         -activate org.gnu.Emacs
                         ,@(when open `(-open ,(pp-to-string open)))
                         ;; Run the shell command COMMAND when the user clicks the notification.
                         ;; -execute COMMAND
                         & ;; … and then speak! …
                         ;; NOTE:This was getting annyoning in the middle of work meetings.
                         ;; say ,(s-replace "\\n" " " (pp-to-string message))
                         ))))

The following two actual uses cases are also mentioned in my/notify docstring, since I want the documentation to be self-contained.

;; (Emojis look terrible in Lisp; but much better when the alert is actually made!)

;; Remind me to exercise every 1.5hours; starting at 8:00am.
(my/notify "Take a 5min break and get your blood flowing!\n\t\t🚣 🏃‍♂️ 🧗‍♂️ 🧘‍♂️ 🏊 🏋 🚴‍♂️"
           :titled "🤾‍♀️ Exercise  🚵‍♂️"
           :at "8:00am"
           :repeat-every-hour 1.5
           :open "https://www.youtube.com/watch?v=23tusPiiNZk&ab_channel=Motiversity")

;; Actually getting things done!
(my/notify "Is what you're doing actually in alignment with your goals? ✔️📉
           Maybe it's time to do another task? 📋"
           :titled "📆 Check your agenda! 🔔"
           :at "10:00am"
           :repeat-every-hour 2)
  • Here is an approach to triggering audio-visual alarms from Org-mode events —using org-agenda-to-appt.
  • Emacs's built in appointment notification facility can also be used as a alarm clock via M-x appt-add.

The marquee-header package let's show messages as “horizontal moving text along the top of the Emacs frame”, which is neat.

  • Slightly related is logms, which let's us make message calls but we can also see the context/source where those calls were made; as well as a clickable link back to the source.

6. Cosmetics

Upon startup, we want to be greeted with a useful, yet unobtrusive, message briefly detailing major system details. Moreover, the bottom-most area of the screen should display battery life, data, & time. Likewise, we may have a casual file explorer —primarily to show-off to newcomers, since great functionality is found with M-x dired ---dired.

;; Get org-headers to look pretty! E.g., * → ⊙, ** ↦ ◯, *** ↦ ★
;; https://github.com/emacsorphanage/org-bullets
(use-package org-bullets
  :hook (org-mode . org-bullets-mode))

6.1. Startup message: Emacs & Org versions

Let's always welcome ourselves when Emacs begins with a helpful message. For example, which user account is running and what are the version numbers of our primary tools.

;; Silence the usual message: Get more info using the about page via C-h C-a.
(setq inhibit-startup-message t)

(defun display-startup-echo-area-message ()
  "The message that is shown after ‘user-init-file’ is loaded."
  (message
      (concat "Welcome "      user-full-name
              "! Emacs "      emacs-version
              "; Org-mode "   org-version
              "; System "     (symbol-name system-type)
              "/"             (system-name)
              "; Time "       (emacs-init-time))))

Now my startup message is,

Welcome Musa Al-hassy! Emacs 27.1; Org-mode 9.4.4; System darwin/Musas-MacBook-Air.local; Time 13.331914 seconds

Let's change the Emacs frame to mention the name of the buffer in focus, as well as a nice ‘motto’:

;; Keep self motivated!
(setq frame-title-format '("" "%b - Living The Dream (•̀ᴗ•́)و"))

6.2. My to-do list: The initial buffer when Emacs opens up

I almost always have Emacs open; I don't need a dashboard, but would like to see my to-do list and my init file, side-by-side.

(unless noninteractive
  ;; Only run the following when we're in GUI mode;
  ;; i.e., don't run it in Github Actions when testing.
  (if my/personal-machine?
      (find-file "~/Dropbox/todo.org")
    ;; After startup, if Emacs is idle for 10 seconds, then open my work file;
    ;; which is a GPG file and so requires passphrase before other things can load.
    ;; (run-with-idle-timer 10 nil (lambda () (find-file "~/Desktop/work.org.gpg")))
      (find-file "~/Desktop/Work-2022-01-01.org")) ;; Org-journal for work
  (split-window-right)                          ;; C-x 3
  (other-window 1)                              ;; C-x 0
  (let ((enable-local-variables :all)           ;; Load *all* locals.
        (org-confirm-babel-evaluate nil))       ;; Eval *all* blocks.
    (ignore-errors (find-file "~/.emacs.d/init.org"))))

There is the neat-looking emacs-dashboard package that provides an extensbile yet minimalist splash screen showing recent files, projects, and bookmarks.

6.3. A sleek, informative, & fancy mode line

The ‘modeline’ is a part near the bottom of Emacs that gives information about the current buffer, such as its file-type/‘major-mode’ and enabled extensions/‘minor-modes’. Let's use the doom-modeline, which is a sleek & minimalistic, yet fancy setup with the following notable perks:

  • Gives each buffer a nice icon in the modeline (denoting its major mode; e.g., Lisp/JavaScript/Org/etc each get a cool icon).
  • Name of file becomes red when unsaved/modified.
  • Nice version control icon, with branch name.
  • Name of file is of the shape is shown as “project/file.ext”, when a project is detected using projectile.el.
  • Flycheck error reporting is ugly by default, and one would consider using flycheck-status-emojis to make things look better in a simple modeline, but Doom-modeline gives a nice status indicators for Flycheck.
  • Shows “+2” when the text scale is two above usual.

For fine-grained control on what/how things appear, there is doom-modeline-def-modeline and doom-modeline-set-modeline.

;; This package requires the fonts included with all-the-icons to be installed. Run M-x all-the-icons-install-fonts to do so.
;; The modeline looks really nice with doom-themes, e.g., doom-solarised-light.
(use-package doom-modeline
  :config (doom-modeline-mode))

  ;; Use minimal height so icons still fit; modeline gets slightly larger when
  ;; buffer is modified since the "save icon" shows up.  Let's disable the icon.
  ;; Let's also essentially disable the hud bar, a sort of progress-bar on where we are in the buffer.
  (setq doom-modeline-height 1)
  (setq doom-modeline-buffer-state-icon nil)
  (setq doom-modeline-hud t)
  (setq doom-modeline-bar-width 1)

  ;; Show 3 Flycheck numbers: “red-error / yellow-warning / green-info”, which
  ;; we can click to see a listing.
  ;; If not for doom-modeline, we'd need to use flycheck-status-emoji.el.
  (setq doom-modeline-checker-simple-format nil)

  ;; Don't display the buffer encoding, E.g., “UTF-8”.
  (setq doom-modeline-buffer-encoding nil)

  ;; Inactive buffers' modeline is greyed out.
  ;; (let ((it "Source Code Pro Light" ))
  ;;   (set-face-attribute 'mode-line nil :family it :height 100)
  ;;   (set-face-attribute 'mode-line-inactive nil :family it :height 100))

Enabled minor modes clutter up the modeline with their names, albeit some have useful status information shown. We can either selectively pick which names/status are shown using diminish.el, possibly forgetting which minor modes are enabled or we can use minions.el to “gather up” all enabled minor modes, and recently enabled ones, under a single menu which doom-modeline shows as a simple configurations gear icon. ⚙. :gear:

  (setq doom-modeline-minor-modes t)
  (use-package minions
    :init (minions-mode))

  ;; A quick hacky way to add stuff to doom-modeline is to add to the mode-line-process list.
  ;; E.g.:  (add-to-list 'mode-line-process '(:eval (format "%s" (count-words (point-min) (point-max)))))
  ;; We likely want to add this locally, to hooks on major modes.

6.3.2. Nice battery icon alongside with percentage, in doom-modeline

;; If not for doom-modeline, we'd need to use fancy-battery-mode.el.
(display-battery-mode +1)
[Posterity / Disabled] Fancy Battery Setup

Let's have it also show remaining battery life, coloured green if charging and coloured yellow otherwise. It is important to note that this package is no longer maintained. It works on my machine.

;; Let's use a fancy indicator …
(use-package fancy-battery
  :diminish
  :custom (fancy-battery-show-percentage  t)
          (battery-update-interval       15)
  :config (fancy-battery-mode))

6.3.3. Time & date

Let’s display the current time, with updates every second.

;; Show date and time as well.

;; [Simple Approach]
;; (setq display-time-day-and-date t)
;; (display-time)

;; [More Controlled Approach: Set date&time format]
;; a ≈ weekday; b ≈ month; d ≈ numeric day, R ≈ 24hr:minute.
(setq display-time-format "%a %b %d ╱ %r") ;; E.g.,:  Fri Mar 04 ╱ 03:42:08 pm
(setq display-time-interval 1) ;; Please update the time every second.
(display-time-mode)

But display-time-mode shows me a bit more info that I actually don't care for; so let's disable those.

;; I don't need the system load average in the modeline.
(setq display-time-default-load-average nil)
(setq display-time-load-average nil)

6.3.4. Column Numbers

Likewise, let's have the modeline display column numbers, but not line numbers. Instead, let's have line numbers on the side of the buffer; moreover let's have a uniform width for displaying line numbers, rather than having the width grow as necessary.

;; (column-number-mode                 t) ;; Enabled in doom-modeline by default
;; (line-number-mode                   t) ;; Not sure I want line numbers in modeline, since I have them in the left margin.

Line numbers are a conventionally expected part of a user interface, but I've realised that I seldom need to see them. I can still jump to a line number provided by a compilation error with M-g g; and toggle line numbers on when I'm pair programming with display-line-numbers-mode.

  • In Emacs, there are buffer which exist and contain textual data, but to actually see them one requires a window. In the same vein, there are line numbers but I don't need to always see them.
  • If I need an indication of ‘progress’, the modeline contains a percentage of how far I am in a buffer.
;; (setq display-line-numbers-width-start t)
;; (global-display-line-numbers-mode      t)
(global-linum-mode -1)

6.4. Exquisite Fonts and Themes

Emacs' default theme leaves much to be desired: It does not look sleek and shiny, which usually leaves first-timers with a poor, shallow, impression of the system. Below we install a few themes that make Emacs look exquisite. We cycle between the chosen themes with C-c t, my/toggle-theme.

“my/toggle-theme” Implementation
  • M-x load-theme RET TAB shows all themes, including built-in ones, that may be loaded.
  • Loading multiple themes results in their pallets mixed.
    • M-x disable-theme to remove a theme from the current pallet.

;; Treat all themes as safe; no query before use.
(setf custom-safe-themes t)

;; Nice looking themes ^_^
(use-package solarized-theme :defer t)
(use-package doom-themes :defer t)
(use-package spacemacs-common
  :defer t
  :ensure spacemacs-theme)
  • The Doom Themes also look rather appealing.
  • A showcase of many themes can be found here.

;; Infinite list of my commonly used themes.
(setq my/themes '(doom-laserwave doom-solarized-light doom-vibrant spacemacs-light solarized-gruvbox-dark solarized-gruvbox-light))
(setcdr (last my/themes) my/themes)

C-c t to toggle between the personal themes.

(cl-defun my/disable-all-themes (&optional (new-theme (pop my/themes)))
  "Disable all themes and load NEW-THEME, which defaults from ‘my/themes’.

When a universal prefix is given, “C-u C-c t”, we load a random
theme from all possible themes.  Nice way to learn about more
themes (•̀ᴗ•́)و"
  (interactive)
  (mapc #'disable-theme custom-enabled-themes)
  (-let [theme (if current-prefix-arg
                   (nth (random (length (custom-available-themes)))
                        (custom-available-themes))
                 new-theme)]
    (when theme
      (load-theme theme)
      (message "Theme %s" theme))))


(defalias 'my/toggle-theme #' my/disable-all-themes)
(global-set-key "\C-c\ t" 'my/toggle-theme)


;; (my/toggle-theme)
(use-package solarized-theme)
(my/toggle-theme 'doom-laserwave)

Apparently, there's already a package that accomplishes these goals and more: theme-looper. I may switch to it, but for now my simple function above is slightly informative, to me at least, about how themes work and it does what I want.

…Actually, the above learning adventure has made it easy to provide a similar setup for fonts 😁

Likewise, C-c F, my/toggle-font, to quickly change fonts (according to mood 😸). [I already use C-c f, my/org-mode-format, for the more likely operation of formatting text.]

“my/toggle-font” Implementation
;; Infinite list of my commonly used fonts
(setq my/fonts
      '("Roboto Mono Light 14" ;; Sleek
        "Input Mono 14"
        "Source Code Pro Light 14" ;; thin, similar to Inconsolata Light
        "Papyrus 14"
        "Bradley Hand Light 12"
        ;; "Chalkduster 14" ;; Laggy!
        "Courier Light 12"
        "Noteworthy 9"
        "Savoye LET 14"
        "Fantasque Sans Mono 16"
        ))
(setcdr (last my/fonts) my/fonts)

;; Let's ensure they're on our system
;; brew search "/font-/"   # List all fonts
(shell-command "brew tap homebrew/cask-fonts")
(system-packages-ensure "svn") ;; Required for the following font installs
(system-packages-ensure "font-roboto-mono")
(system-packages-ensure "font-input")
(system-packages-ensure "font-source-code-pro")
(system-packages-ensure "font-fira-mono")
(system-packages-ensure "font-mononoki")
(system-packages-ensure "font-monoid")
(system-packages-ensure "font-menlo-for-powerline")
(system-packages-ensure "font-fantasque-sans-mono")

;; Use “M-x set-face-font RET default RET”, or...
;; (set-face-font 'default "Source Code Pro Light14")

;; See ~2232 fonts
;; (append (fontset-list) (x-list-fonts "*" nil))

(cl-defun my/toggle-font (&optional (new-font (pop my/fonts)))
  "Load NEW-FONT, which defaults from ‘my/fonts’.

When a universal prefix is given, “C-u C-c F”, we load a random
font from all possible themes.  Nice way to learn about more
fonts (•̀ᴗ•́)و"
  (interactive)
  (let* ((all-fonts (append (fontset-list) (x-list-fonts "*" nil)))
         (font (if current-prefix-arg
                   (nth (random (length all-fonts)) all-fonts)
                 new-font)))
    (set-face-font 'default font)
    (message "Font: %s" font)))

(global-set-key "\C-c\ F" 'my/toggle-font)

;; Default font; the “ignore-⋯” is for users who may not have the font.
(ignore-errors (my/toggle-font "Fantasque Sans Mono 12"))
(ignore-errors (my/toggle-font "Source Code Pro Light 14"))

In any Org file, type elisp:menu-set-font; then you can click on this link to get a nice font selection menu —this can be useful for your own ‘personal startup buffer’.

Let's use the following theme and font, upon startup.

(unless noninteractive
  (my/toggle-font "Roboto Mono Light 14")
  (my/toggle-theme 'solarized-gruvbox-light))

6.5. Never lose the cursor

Let's have the entire line containing the cursour be slightly highlighted.

;; Make it very easy to see the line with the cursor.
(global-hl-line-mode t)

Moreover, we reduce the mental strain of locating the cursour when navigation happens: When we switch windows or scroll, for example, we get a wave of light near the cursor.

(use-package beacon
  :diminish
  :config (setq beacon-color "#666600")
  :hook   ((org-mode text-mode) . beacon-mode))

6.6. Dimming Unused Windows

Let's dim windows, and even the whole Emacs frame, when not in use.

(use-package dimmer
  :config (dimmer-mode))

A more ‘fine-grained’ tool dims all text except the ‘paragraph’ you're working on. It's nifty, but not for me.

6.7. Flashing when something goes wrong

Enable flashing mode-line on errors. E.g., C-g, or calling an unbound key sequence, or misspelling a word.

;; (setq visible-bell 1) ;; On MacOS, this shows a caution symbol ^_^

;; The doom themes package comes with a function to make the mode line flash on error.
(use-package doom-themes)
(require 'doom-themes-ext-visual-bell)
(doom-themes-visual-bell-config)

A blinking cursor rushes me to type; let's slow down. Recently I'm thinking that a blinking cursours prompts me to continue upwards and onwards.

(blink-cursor-mode 1)

6.8. Hiding Scrollbar, tool bar, and menu

As a laptop user, screen space is important, so let's remove rarely used visual items.

(unless noninteractive
  (tool-bar-mode   -1)  ;; No large icons please
  (scroll-bar-mode -1)  ;; No visual indicator please
  (menu-bar-mode   -1))  ;; The Mac OS top pane has menu options

6.9. Highlight & complete parenthesis pair when cursor is near ;-)

Highlight matching ‘parenthesis’ when near one of them.

(setq show-paren-delay  0)
(setq show-paren-style 'mixed)
(show-paren-mode)

Colour parens, and other delimiters, depending on their depth. Very useful for parens heavy languages like Lisp.

(use-package rainbow-delimiters
  :disabled
  :hook ((org-mode prog-mode text-mode) . rainbow-delimiters-mode))

For example:

(blue (purple (forest (green (yellow (blue))))))

There is a powerful package called ‘smartparens’ for working with pair-able characters, but I've found it to be too much for my uses. Instead I'll utilise the lightweight package electric, which Emacs provides out of the box.

(electric-pair-mode 1)

It supports, by default, ACSII pairs {}, [], () and Unicode ‘’, “”, ⟪⟫, ⟨⟩.

When writing Lisp, it is annoyong to have ‘<’ and ‘>’ be completed and considered as pairs. Let's disassociate them from both notions.

;; The ‘<’ and ‘>’ are not ‘parenthesis’, so give them no compleition.
(setq electric-pair-inhibit-predicate
      (lambda (c)
        (or (member c '(?< ?> ?~)) (electric-pair-default-inhibit c))))

;; Treat ‘<’ and ‘>’ as if they were words, instead of ‘parenthesis’.
(modify-syntax-entry ?< "w<")
(modify-syntax-entry ?> "w>")

Adding Org-emphasise markers for pair completion —Disabled.

Let's add the org-emphasises markers: If we select a word then press *, it becomes bold; likewise for / for emphasise.

(setq electric-pair-pairs
         '((?~ . ?~)
           (?* . ?*)
           (?/ . ?/)))

;; Let's also, for example, avoid obtaining double ‘~’ and ‘/’ when searching for a file.

;; Disable pairs when entering minibuffer
(add-hook 'minibuffer-setup-hook (lambda () (electric-pair-mode 0)))

;; Renable pairs when existing minibuffer
(add-hook 'minibuffer-exit-hook (lambda () (electric-pair-mode 1)))

I use ‘~’ and ‘/’ too much during file navigation, and ‘*’ when marking numerous Org headers, for which the ‘completed closing pair’ must tiresomely be deleted.

6.10. Proportional fonts for Headlines

Let's have headings stick out a bit.

  • The larger headings are cute and reminicint of word processors, but having headings coloured is enough —the larger size is too much.
(set-face-attribute 'org-document-title nil :height 2.0)
;; (set-face-attribute 'org-level-1 nil :height 1.0)
;; Remaining org-level-𝒾 have default height 1.0, for 𝒾 : 1..8.
;;
;; E.g., reset org-level-1 to default.
;; (custom-set-faces '(org-level-1 nil))

Remember you can always use Emacs' Custom utility to get Lisp incantations ;-) —See notes on Custom above.

6.11. Making Block Delimiters Less Intrusive

Let us render Org-mode's #+begin_src and #+end_src less obtrusively by, e.g., having the former render as a pencil marker and the latter as a tombstone —reminiscent of Halmos' QED end-of-proof marker.

Rasmus’ Incantation

This is from Rasmus Roulund.

  (defvar-local rasmus/org-at-src-begin -1
    "Variable that holds whether last position was a ")

  (defvar rasmus/ob-header-symbol ?☰
    "Symbol used for babel headers")

  (defun rasmus/org-prettify-src--update ()
    (let ((case-fold-search t)
          (re "^[ \t]*#\\+begin_src[ \t]+[^ \f\t\n\r\v]+[ \t]*")
          found)
      (save-excursion
        (goto-char (point-min))
        (while (re-search-forward re nil t)
          (goto-char (match-end 0))
          (let ((args (org-trim
                       (buffer-substring-no-properties (point)
                                                       (line-end-position)))))
            (when (org-string-nw-p args)
              (let ((new-cell (cons args rasmus/ob-header-symbol)))
                (cl-pushnew new-cell prettify-symbols-alist :test #'equal)
                (cl-pushnew new-cell found :test #'equal)))))
        (setq prettify-symbols-alist
              (cl-set-difference prettify-symbols-alist
                                 (cl-set-difference
                                  (cl-remove-if-not
                                   (lambda (elm)
                                     (eq (cdr elm) rasmus/ob-header-symbol))
                                   prettify-symbols-alist)
                                  found :test #'equal)))
        ;; Clean up old font-lock-keywords.
        (font-lock-remove-keywords nil prettify-symbols--keywords)
        (setq prettify-symbols--keywords (prettify-symbols--make-keywords))
        (font-lock-add-keywords nil prettify-symbols--keywords)
        (while (re-search-forward re nil t)
          (font-lock-flush (line-beginning-position) (line-end-position))))))

  (defun rasmus/org-prettify-src ()
    "Hide src options via `prettify-symbols-mode'.

  `prettify-symbols-mode' is used because it has uncollpasing. It's
  may not be efficient."
    (let* ((case-fold-search t)
           (at-src-block (save-excursion
                           (beginning-of-line)
                           (looking-at "^[ \t]*#\\+begin_src[ \t]+[^ \f\t\n\r\v]+[ \t]*"))))
      ;; Test if we moved out of a block.
      (when (or (and rasmus/org-at-src-begin
                     (not at-src-block))
                ;; File was just opened.
                (eq rasmus/org-at-src-begin -1))
        (rasmus/org-prettify-src--update))
      ;; Remove composition if at line; doesn't work properly.
      ;; (when at-src-block
      ;;   (with-silent-modifications
      ;;     (remove-text-properties (match-end 0)
      ;;                             (1+ (line-end-position))
      ;;                             '(composition))))
      (setq rasmus/org-at-src-begin at-src-block)))

  (defun rasmus/org-prettify-symbols ()
    (mapc (apply-partially 'add-to-list 'prettify-symbols-alist)
          (cl-reduce 'append
                     (mapcar (lambda (x) (list x (cons (upcase (car x)) (cdr x))))
                             `(("#+begin_src" . ?✎) ;; ➤ 🖝 ➟ ➤ ✎
                               ("#+end_src"   . ?□) ;; 
                               ("#+header:" . ,rasmus/ob-header-symbol)
                               ("#+begin_quote" . ?»)
                               ("#+end_quote" . ?«)))))
    (turn-on-prettify-symbols-mode)
    (add-hook 'post-command-hook 'rasmus/org-prettify-src t t))


;; Last up­dated: 2019-06-09
(add-hook 'org-mode-hook #'rasmus/org-prettify-symbols)
(org-mode-restart)

His development relies on built-in prettify-symbols-mode, which disguises strings in a buffer for the sake of readability or aesthetics. Following the example in the documentation, C-h f prettify-symbols-mode, we can quickly approximate his efforts for example blocks as follows, however a main issue is that source blocks have busybodied headers which his setup disguises as ‘≡’.

(global-prettify-symbols-mode)

(defvar my/prettify-alist nil
  "Musa's personal prettifications.")

(cl-loop for pair in '(;; Example of how pairs like this to beautify org block delimiters
                    ("#+begin_example" . (?ℰ (Br . Bl) ?⇒)) ;; ℰ⇒
                    ("#+end_example"   . ?⇐)                 ;; 
                    ;; Actuall beautifications
                    ("==" . ?≈) ("===" . ?≈) ("=" . ?≔) ;; Programming specific prettifications
                    ("<=" . ?≤) (">=" . ?≥)
                    ("->" . ?→) ("-->". ?⟶) ;; threading operators
                    ("[ ]" . ?□) ("[X]" . ?☑) ("[-]" . ?◐)) ;; Org checkbox symbols
      do (push pair my/prettify-alist))

;; Replace all Org [metadata]keywords with the “▷” symbol; e.g., “#+title: Hello” looks like “▷ Hello”.
(cl-loop for keyword in '(title author email date description options property startup export_file_name html_head)
         do (push (cons (format "#+%s:" keyword) ?▷) my/prettify-alist))

(cl-loop for hk in '(text-mode-hook prog-mode-hook org-mode-hook)
      do (add-hook hk (lambda ()
                        (setq prettify-symbols-alist
                              (append my/prettify-alist prettify-symbols-alist)))))

See “Mathematical Notation in Emacs” for how such prettifications can make verbose (Python) scripts much more readable by employing more economical disguises.

A nice sanity:

;; Un-disguise a symbol when cursour is inside it or at the right-edge of it.
(setq prettify-symbols-unprettify-at-point 'right-edge)

6.12. Hiding Emphasise Markers, Inlining Images, and LaTeX-as-PNG

Let's make some things prettier than they appear by default.

;; org-mode math is now highlighted ;-)
(setq org-highlight-latex-and-related '(latex))

;; Extra space between text and underline line
(setq x-underline-at-descent-line t)

;; Hide the *,=,/ markers
(setq org-hide-emphasis-markers t)

;; Let’s limit the width of images inlined in org buffers to 400px.
(setq org-image-actual-width 400)

;; Visually, I prefer to hide the markers of macros, so let’s do that:
;;  {{{go(here)}}} is shown in Emacs as go(here)
(setq org-hide-macro-markers t)

;; On HTML exports, Org-mode tries to include a validation link for the exported HTML. Let’s disable that since I never use it.
;; (setq org-html-validation-link nil)

;; Musa: This is super annoying, in practice.
(setq org-pretty-entities nil) ;; Also makes subscripts (x_{sub script}) and superscripts (x^{super script}) appear in org in a WYSIWYG fashion.
;; to have \alpha, \to and others display as utf8
;; http://orgmode.org/manual/Special-symbols.html
;;
;; Be default, any consectuive string after “_” or “^” will be shown in WYSIWYG fashion; the following requires “^{⋯}” instead.
;; (setq org-use-sub-superscripts (quote {}))

Org pretty entities seems rather impressive ---M-x org-entities-help to see all possibilities, or add your own. I'm already using the Agda input method, so I wont use Org's —Agda's gives me a tiny menu narrowing possibilities as I type. However, it does make subscripts (xsub script) and superscripts (xsuper script) appear in Org in a WYSIWYG fashion.


Automatically display emphasis markers and links when the cursor is on them. (c.f. fragtog below)

(use-package org-appear
  :hook (org-mode . org-appear-mode)
  :init (setq org-appear-autoemphasis  t
              org-appear-autolinks nil
              org-appear-autosubmarkers nil))

The following is now disabled (yet again, as of Dec/31/2020) —it makes my system slower than I'd like.

;; Show inline images when loading a new Org file.
(setq org-startup-with-inline-images t)

;; Whenever a src block is run, redisplay images so they're up-to-date.
;; Very useful when using ‘ob-latex-as-png’, below.
(add-hook 'org-babel-after-execute-hook #'org-redisplay-inline-images)

;; Automatically convert LaTeX fragments to inline images.
(setq org-startup-with-latex-preview t)

Org mode supports inline image previews of LaTeX fragments; e.g., \(e^{i \cdot \pi} - 1 = 0\) or \(\substack{𝔹 \\ ↓ \\ 𝒜}\). These can be toggled with C-c C-x C-l. Org-fragtog automates this, so fragment previews are disabled for editing when your cursor steps onto them, and re-enabled when the cursor leaves.

;; Automatically toggle LaTeX previews when cursour enters/leaves them
(use-package org-fragtog
  :hook (org-mode . org-fragtog-mode))

org-latex-preview, C-c C-x C-l, renders $e^{i \pi} + 1 = 0$ into a really nice inline image: \(e^{i \pi} + 1 = 0\). It also works for LaTeX environments —for personal environments, just (add-to-list 'org-latex-packages-alist "LaTeX definitions here").

;; Make previews a bit larger
(setq org-format-latex-options (plist-put org-format-latex-options :scale 1.5))

;; I use a lot of Unicode, so let's always include a unicode header.
(maybe-clone "https://armkeh.github.io/unicode-sty/")
(setq org-format-latex-header
      (concat org-format-latex-header
              "\n\\usepackage{\\string~\"/unicode-sty/unicode\"}"))
;;
;; Now this looks nice too!
;; $\substack{𝔹 \\ ↓ \\ 𝒜}$ and $\mathbb{B}$.

;; Always support unicode upon LaTeX export
;; No need to explicitly import armkeh's unicode-sty in each org file.
(add-to-list 'org-latex-packages-alist
  "\n\\usepackage{\\string~\"/unicode-sty/unicode\"}")

This approach does not work well for forming diagrams; I've tried to make tikzcd work this way and failed. Using ob-latex-as-png as a substitute.

;; Support “latex-as-png” src blocks, which show LaTeX as PNGs
(use-package ob-latex-as-png)

Use ref:my-stuff to refer to an Org entity with #+name: my-stuff; which must have a #+caption: ⋯ as well. Example entities include tables and source blocks; as well as figure blocks. For equation blocks, you must use a \label{⋯} directly.

;; Use the “#+name” the user provides, instead of generating label identifiers.
(setq org-latex-prefer-user-labels t)

6.13. Show off-screen heading at the top of the window

In case we forgot which heading we're under, let's keep the current heading stuck at the top of the window.

 (use-package org-sticky-header
  :hook (org-mode . org-sticky-header-mode)
  :config
  (setq-default
   org-sticky-header-full-path 'full
   ;; Child and parent headings are seperated by a /.
   org-sticky-header-outline-path-separator " / "))

6.14. Powerful Directory Editing with dired

C-x C-v to open a file or directory in dired, using the current buffer. ⟩

As mentioned earlier, dired is Emacs' built-in directory editor; it's opened with C-x d. Dired let's us treat directories as textual objects! In dired, press h to see the many actions available. Here's a few…

Super Terse ‘dired’ Tutorial
  • ( toggles hiding entry details, such as modification date and ownership
  • s sort entries; modeline will display “Dired by date” or “Dired by name”.
  • o to open entry in anOther window; or RET to open in place.
  • + to create a new directory; or M-x make-directory.
  • / to filter entries; with which-key, possible completions pop-up.
    • E.g., / f shows only files or / . png to obtain all entries with extension png.
    • / i g to hide git-ignored items ^_^
    • / / to remove all filters.
  • TAB to navigate between different groupings of entries.
    • RET on a drawer heading toggles folding it ^_^

The dired-hacks family of packages lets us, say, get a dired buffer out of a shell incantation that lists files, or use dired to open files with external tools. Below we use three of its packages.

Pressing i inserts a directory's children under it, indented, in the current buffer. Useful to see what's there.

(use-package dired-subtree
  :bind (:map dired-mode-map
              ("i" . dired-subtree-toggle)))

When directory 𝒳 has only one child 𝒴, then in dired, instead of 𝒳, show me 𝒳/𝒴 with 𝒳 greyed out.

(use-package dired-collapse
  :hook (dired-mode . dired-collapse-mode))

Begin dired with certain entries grouped together, according to some filtering requirement; and with “garbage” files not shown —i.e., those ending in .aux, .out, etc.

(use-package dired-filter
  :hook (dired-mode . (lambda () (dired-filter-group-mode)
                                 (dired-filter-by-garbage)))
  :custom
    (dired-garbage-files-regexp
      "\\(?:\\.\\(?:aux\\|bak\\|dvi\\|log\\|orig\\|rej\\|toc\\|out\\)\\)\\'")
    (dired-filter-group-saved-groups
      '(("default"
         ("Org"    (extension "org"))
         ("Executables" (exexutable))
         ("Directories" (directory))
         ("PDF"    (extension "pdf"))
         ("LaTeX"  (extension "tex" "bib"))
         ("Images" (extension "png"))
         ("Code"   (extension "hs" "agda" "lagda"))
         ("Archives"(extension "zip" "rar" "gz" "bz2" "tar"))))))
[Disabled] Neotree: Traditional Directory Tree Navigation

We open a nifty file manager upon startup.

;; Sidebar for project file navigation
(use-package neotree
  :defer t
  :disabled
  :config (global-set-key "\C-x\ d" 'neotree-toggle)
          (setq neo-theme 'icons)) ;; Uses all-the-icons from § Booting Up

;; Open it up upon startup.
;; (neotree-toggle)

By default C-x d invokes dired, but I prefer neotree for file management.

⟨ Edit: As a naive user, this is what I thought; yet a year later, I've almost never used neotree. ⟩

Useful navigational commands include

  • U to go up a directory.
  • C-c C-c to change directory focus; C-C c to type the directory out.
  • ? or h to get help and q to quit.

As always, to go to the neotree pane when it's the only other window, execute C-x o.

I rarely make use of this feature; company mode & Helm together quickly provide an automatic replacement for nearly all of my uses.

  • Reminiscent of GUI file managers is ranger; e.g., it has multi-column display of parent directories along with a file preview mechanism.

6.15. Persistent Scratch Buffer

The *scratch* buffer is a nice playground for temporary data or experiments.

However, by default its contents are not saved –which may be an issue if we have not relocated our playthings to their appropriate files. Whence let's save & restore the scratch buffer by default.

(use-package persistent-scratch
  :defer t
  ;; In this mode, the usual save key saves to the underlying persistent file.
  :bind (:map persistent-scratch-mode-map
              ("C-x C-s" . persistent-scratch-save)))

We might accidentally close this buffer, so we could utilise the following.

(defun scratch ()
   "Recreate the scratch buffer, loading any persistent state."
   (interactive)
   (switch-to-buffer-other-window (get-buffer-create "*scratch*"))
   (condition-case nil (persistent-scratch-restore) (insert initial-scratch-message))
   (org-mode)
   (persistent-scratch-mode)
   (persistent-scratch-autosave-mode 1))

;; This doubles as a quick way to avoid the common formula: C-x b RET *scratch*

;; Upon startup, close the default scratch buffer and open one as specfied above
(ignore-errors (kill-buffer "*scratch*") (scratch))

I use Org-mode often, so that's how I want things to appear.

(setq initial-scratch-message (concat
  "#+Title: Persistent Scratch Buffer"
  "\n#\n# Welcome! This’ a place for trying things out."
  "\n#\n# ⟨ ‘C-x C-s’ here saves to ~/.emacs.d/.persistent-scratch ⟩ \n\n"))

6.16. Tabs   Disabled

I really like my Helm-supported C-x b, but the visial appeal of a tab bar for Emacs is interesting. Let's try it out and see how long this lasts —it may be like Neotree: Something cute to show to others, but not as fast as the keyboard.

(use-package awesome-tab
  :disabled
  :quelpa (awesome-tab :fetcher git :url "https://github.com/manateelazycat/awesome-tab.git")
  :config (awesome-tab-mode t))

;; Show me /all/ the tabs at once, in one group.
(defun awesome-tab-buffer-groups ()
  (list (awesome-tab-get-group-name (current-buffer))))

It's been less than three days and I've found this utility to be unhelpful, to me anyhow.

An alternative is centaur-tabs.

6.17. Window resizing using the golden ratio   Disabled

Let's load the following package, which automatically resizes windows so that the window containing the cursor is the largest, according to the golden ratio. Consequently, the window we're working with is nice and large yet the other windows are still readable.

(use-package golden-ratio
  :disabled
  :diminish golden-ratio-mode
  :init (golden-ratio-mode 1))

After some time this got a bit annoying and I'm no longer using this.

6.18. Org-Emphasise for Parts of Words   Disabled

From stackoverflow, the following incantation allows us to have parts of works emphasied with org-mode; e.g., /half/ed, ~half~ed, and right in the m*idd*le! Super cool stuff!

(setcar org-emphasis-regexp-components " \t('\"{[:alpha:]")
(setcar (nthcdr 1 org-emphasis-regexp-components) "[:alpha:]- \t.,:!?;'\")}\\")
(org-set-emph-re 'org-emphasis-regexp-components org-emphasis-regexp-components)

I've disabled this feature since multiple occurrences of an emphasise marker are sometimes treated as one lengthy phrase being emphasised.

6.20. Replace phrases with nice SVG labels

let's us replase arbitrary regular expressions with beautiful SVG images that can be clicked to produce an action, and may have a tooltip to provide contextual information. Essentially an alternative to the built-in font-lock-mode, which performs arbitrary syntax highlighting.

  • For more power, use the svg-lib package.
  • The docs have nice examples. Here are more useful examples.

Below I setup a function, my/svg-tag-declare-badge to declaratively produce SVG badges.

(use-package svg-tag-mode
  :hook (org-mode prog-mode)
  ;; :config (global-svg-tag-mode) ;; Nope: Breaks xwidget-webkit-browse-url, issue#28.
  :config
  (cl-defun my/svg-tag-declare-badge (template face &optional tooltip-message-upon-hover)
    ;; Example faces: 'org-level-1 'org-todo 'font-lock-doc-face
    "Given a TEMPLATE of the shape \"𝑿❙𝒀\", make SVG badge whose tag is 𝑿 and label is 𝒀.

     When `svg-tags-mode' is enabled, every occurence of  \"\\(𝑿\\)\\(𝒀\\)\"
     is replaced by an SVG image essentially displaying “[𝑿∣𝒀]” using the given FACE.
     This badge can be clicked to show all instances in the buffer.
     You can see the badges documentation / intentions / help-message when you hover over it;
     to see TOOLTIP-MESSAGE-UPON-HOVER.

     Both 𝑿 and 𝒀 are regeular expressions; “❙” serves as the SVG tag-label delimiter
     ---i.e., it saves as from writing \"\\(𝑿\\)\\(𝒀\\)\". Moreover, the SVG is only active
     when regexp \"\\(𝑿\\)\\(𝒀\\)\" matches an instance."

    ;; Append tooltip message with a notice on what happens upon click.
    (--> "Click on me to all see occurrences of this badge, in the current buffer!"
         (if tooltip-message-upon-hover (concat tooltip-message-upon-hover "\n\n" it) it)
         (setq tooltip-message-upon-hover it))

    (-let [(tag label) (s-split "❙" template)]
      (-let [click-to-show-all-buffer-occurrences `(lambda () (interactive) (occur (concat ,tag ,label)))]
       ;; Make an SVG for the tag.
       (push
        (cons (format "\\(%s\\)%s" tag label) `((lambda (tag) (svg-tag-make (s-chop-suffix ":" (s-chop-prefixes '("[" "<" "/*")  tag))    :face (quote ,face) :inverse t :margin 0 :crop-right t :crop-left nil))
                                                ,click-to-show-all-buffer-occurrences
                                                ,tooltip-message-upon-hover))
        svg-tag-tags)
       ;; Make an SVG for the label.
       (push
        (cons (format "%s\\(%s\\)" tag label) `((lambda (label) (svg-tag-make (s-chop-suffixes '("]" ">" "*/") label) :face (quote ,face) :crop-left t))
                                                ,click-to-show-all-buffer-occurrences
                                                ,tooltip-message-upon-hover))
        svg-tag-tags))))

  ;; Let's start off empty; then declare badges below.
  (setq svg-tag-tags nil)

  ;; Using caps so that these stick-out somewhat even when svg-tags-mode is not present.
  (my/svg-tag-declare-badge "TODO:❙.*" 'org-todo "This is something I would like to do, in the future.")
  (my/svg-tag-declare-badge "SILLY:❙.*" 'error "I’m experimenting; don't forget to clean-up when you’re done!")
  (my/svg-tag-declare-badge "HACK:❙.*" 'error "This works, but it’s far from ideal. Plan to clean this in the future.")
  (my/svg-tag-declare-badge "FIXME:❙.*" 'org-todo "This is busted! Plan to fix this in the future.")
  (my/svg-tag-declare-badge "NOTE:❙.*" 'org-done "Something to be aware of; to keep in mind.")

  ;; [In]Active Time stamps --- M-x org-time-stamp
  (my/svg-tag-declare-badge "\\[2022-.* ❙.*]" 'org-done "This is an inactive time stamp. It does not trigger the parent entry to appear in the agenda.")
  (my/svg-tag-declare-badge "<2022-.* ❙.*>" 'org-todo "This is an active time stamp. It causes the parent Org entry to appear in the agenda.")

  ;; JavaScript Lint Rules: \* eslint (.*) */
  (my/svg-tag-declare-badge "/\\* eslint ❙.* \\*/" 'org-done "It looks like you’ deviating from common conventions: Tread cautiously!")

  ;; TODO: Make SVG tags for other interesting “2-part” pieces of textual information
  )

;; If everything is setup, the following examples should look like SVGs.
;; NOTE: Do something
;; TODO: fix me later
;; HACK: hiya
;; FIXME: this thing is busted 🎭
;; SILLY: start
;; SILLY: end
;; [2022-04-20 Sun 16:30]
;; <2022-04-20 Sun 16:30>
;; /* eslint eqeqeq: 0, curly: 2 */

;; NOTE: Toggle svg-tags-mode; useful when experimenting with new tags.
;; (progn (svg-tag-mode-off) (svg-tag-mode-on))

;; NOTE: (my/toggle-line-fontification) works fine with svg-tag-mode :-)

7. Prose

Emacs can be setup with a spellchecker and other expected features of a word processing tool —however these features apply Emacs-wide since nearly everything is essentially text (•̀ᴗ•́)و

  • Org-mode is a writer's best friend; it's large enough to deserve its own sections.

7.1. Whitespace

Let's start off by cleaning-up any accidental trailing whitespace and in other places upon save.

(add-hook 'before-save-hook 'whitespace-cleanup)

See here for making whitespace visible; including spaces, tabs, and newlines

7.2. Formatting Text

The following incantation, my/org-mode-format, makes it so that we can select some text then press C-c f (to get a list of possible character completions) then press the symbol we want our text to be surrounded with.

Details
(local-set-key (kbd "C-c f") #'my/org-mode-format)
(defun my/org-mode-format (&optional text)
"Surround selected region with the given Org emphasises marker.

E.g., if this command is bound to “C-c f” then the sequence
“C-c f b” would make the currenly selected text be bold.
Likewise, “C-c f *” would achieve the same goal.

When you press “C-c f”, a message is shown with a list of
useful single-character completions.

Note: “C-c f 𝓍”, for an unrecognised marker 𝓍, just inserts
the character 𝓍 before and after the selected text."
  (interactive "P") ;; Works on a region
  ; (message "b,* ⟨Bold⟩; i,/ ⟨Italics⟩; u,_ ⟨Underline⟩; c,~ ⟨Monotype⟩")
  (message "⟨Bold b,*⟩ ⟨Italics i,/⟩ ⟨Underline u,_⟩ ⟨Monotype c,~⟩")
  (let ((kind (read-char)))
    ;; Map letters to Org formatting symbols
    (setq kind (or (plist-get '(b ?\*   i ?\/   u ?\_   c ?\~)
                              (intern (string kind)))
                   kind))
    (insert-pair text kind kind)))

7.3. Fill-mode —Word Wrapping

In fill mode, when you type past the end of a line, Emacs automatically starts a new line, cleverly formatting paragraphs. This is a powerful form of “word wrap”.

(setq-default fill-column 80          ;; Let's avoid going over 80 columns
              truncate-lines nil      ;; I never want to scroll horizontally
              indent-tabs-mode nil)   ;; Use spaces instead of tabs

Certain variables are sensibly local to a buffer, and so setq only alters their value for one buffer. Using setq-default we change a variable's default value, in every buffer.

;; Wrap long lines when editing text
(add-hook 'text-mode-hook 'turn-on-auto-fill)
(add-hook 'org-mode-hook 'turn-on-auto-fill)

;; Do not show the “Fill” indicator in the mode line.
(diminish 'auto-fill-function)

We may press M-q to cleverly redistribute the line breaks within any paragraph, thereby making it look better. With a prefix argument, it justifies it as well —i.e., pads extra white space to make the paragraph appear rectangular.

Note that M-o M-s centres a line of text ;-) Fun stuff!

Fill-mode is also known as “hard word wrapping”, which has the counterpart “soft word wrapping” …

Visual line mode is built-in and provides support for editing by visual lines: Lines off the screen are visually word wrapped, but logically remain one line. Moreover C-a,e,k operate on visual lines rather than logical lines.

;; Bent arrows at the end and start of long lines.
(setq visual-line-fringe-indicators '(left-curly-arrow right-curly-arrow))
(diminish 'visual-line-mode)
(global-visual-line-mode 1)

Visual line mode is useful when I have way too many windows open or when using smaller frames.

7.4. Pretty Lists Markers

When writing, it's common to use +,-,* to enumerate unordered lists —especially so in Org-mode wherein they denote structured text. Let's render them visually as Unicode bullets.

;; (x y z) ≈ (existing-item replacement-item positivity-of-preceding-spaces)
(cl-loop for (x y z) in '(("+" "◦" *)
                       ("-" "•" *)
                       ("*" "⋆" +))
      do (font-lock-add-keywords 'org-mode
                                 `((,(format "^ %s\\([%s]\\) " z x)
                                    (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) ,y)))))))

7.5. Fix spelling as you type —thesaurus & dictionary too!

I would like to check spelling on the fly.

C-;
Cycle through corrections for word at point.
M-$
Check and correct spelling of the word at point
M-x ispell-change-dictionary RET TAB
To see what dictionaries are available.

Install spell-checking application as well as a reliable English dictionary, WordNet.

(system-packages-ensure "aspell")
(system-packages-ensure "wordnet")

flyspell-prog-mode enables spell checking for programming by only considering comments and strings.

(use-package flyspell
  :diminish
  :hook ((prog-mode . flyspell-prog-mode)
         ((org-mode text-mode) . flyspell-mode)))

Enabling fly-spell for text-mode enables it for org and latex modes since they derive from text-mode.

Flyspell needs a spell checking tool, which is not included in Emacs. We install aspell spell checker using, say, homebrew via brew install aspell. Note that Emacs' ispell is the interface to such a command line spelling utility.

(setq ispell-program-name "/usr/local/bin/aspell")
(setq ispell-dictionary "en_GB") ;; set the default dictionary

[Disabled] Allow spelling support for CamlCase words like “EmacsIsCool”.

(setq  ispell-extra-args '("--sug-mode=ultra"
                            "--run-together"
                            "--run-together-limit=5"
                            "--run-together-min=2"))

Let us select a correct spelling merely by clicking on a word —for the rare days I have a mouse.

(eval-after-load "flyspell"
  ' (progn
     (define-key flyspell-mouse-map [down-mouse-3] #'flyspell-correct-word)
     (define-key flyspell-mouse-map [mouse-3] #'undefined)))

Colour incorrect works; default is an underline.

(global-font-lock-mode t)
(custom-set-faces '(flyspell-incorrect ((t (:inverse-video t)))))

Finally, save to user dictionary without asking:

(setq ispell-silently-savep t)

Let's keep track of my personal word set by having it be in my version controlled .emacs directory. Note that the default location is ~/.[i|a]spell.DICT for a specified dictionary DICT.

(setq ispell-personal-dictionary "~/.emacs.d/.aspell.en.pws")

Nowadays, I very rarely write non-literate programs, but if I do I'd like to check spelling only in comments/strings. E.g.,

(add-hook          'c-mode-hook 'flyspell-prog-mode)
(add-hook 'emacs-lisp-mode-hook 'flyspell-prog-mode)

Use the thesaurus Emacs frontend Synosaurus to avoid unwarranted repetition.

(use-package synosaurus
  :diminish synosaurus-mode
  :init    (synosaurus-mode)
  :config  (setq synosaurus-choose-method 'popup) ;; 'ido is default.
           (global-set-key (kbd "M-#") 'synosaurus-choose-and-replace))

The thesaurus is powered by the Wordnet wn tool, which can be invoked without an internet connection!

;; (shell-command "brew cask install xquartz &") ;; Dependency
;; (shell-command "brew install wordnet &")

Let's use Wordnet as a dictionary via the wordnut package.

(use-package wordnut
 :bind ("M-!" . wordnut-lookup-current-word))

;; Use M-& for async shell commands.

Use M-↑,↓ to navigate dictionary results, and wordnut-search for a new search.

An alternative to wordnut is to use the lightweight define-word package; which I think is not ideal since it provides way less information.

7.6. Using a Grammar & Style Checker

[ A possibly better alternative is Vale. ]

Let's install a grammar and style checker. We get the offline tool from the bottom of the LanguageTool website, then relocate it as follows.

(use-package langtool
 :defer t
 :custom
  (langtool-language-tool-jar
   "~/Applications/LanguageTool-4.5/languagetool-commandline.jar"))

Now we can run langtool-check on the subsequent grammatically incorrect text —which is from the LanguageTool website— which colours errors in red, when we click on them we get the reason why; then we may invoke langtool-correct-buffer to quickly use the suggestions to fix each correction, and finally invoke langtool-check-done to stop any remaining red colouring.

LanguageTool offers spell and grammar checking. Just paste your text here
and click the 'Check Text' button. Click the colored phrases for details
on potential errors. or use this text too see an few of of the problems
that LanguageTool can detecd. What do you thinks of grammar checkers?
Please not that they are not perfect. Style issues get a blue marker:
It's 5 P.M. in the afternoon. The weather was nice on Thursday, 27 June 2017
--uh oh, that's the wrong date ;-)

By looking around the source code, I can do all three stages smoothly (•̀ᴗ•́)و

;; Quickly check, correct, then clean up /region/ with M-^
(eval-after-load 'langtool
(progn
(add-hook 'langtool-error-exists-hook
  (lambda ()
     (langtool-correct-buffer)
     (langtool-check-done)))

(global-set-key "\M-^"
                (lambda ()
                  (interactive)
                  (message "Grammar checking begun ...")
                  (langtool-check)))))

The checking command is silent, we added a bit of comforting acknowledgement to the user.

7.7. Lightweight Prose Proofchecking

Let's write good!

(use-package writegood-mode
  ;; Load this whenver I'm composing prose.
  :hook (text-mode org-mode)
  ;; Don't show me the “Wg” marker in the mode line
  :diminish
  ;; Some additional weasel words.
  :config
  (--map (push it writegood-weasel-words)
         '("some" "simple" "simply" "easy" "often" "easily" "probably"
           "clearly"               ;; Is the premise undeniably true?
           "experience shows"      ;; Whose? What kind? How does it do so?
           "may have"              ;; It may also have not!
           "it turns out that")))  ;; How does it turn out so?
           ;; ↯ What is the evidence of highighted phrase? ↯

Inspired by Matt Might's 3 shell scripts to improve your writing, or "My Ph.D. advisor rewrote himself in bash", this Emacs interface emphasises, via underline, the following weaknesses in writing —so that I can fix them or decide that they are appropriate for the scenario.

Sentences that cut out the following problems may become stronger —by being more terse or precise.

Weasel Words

Phrases that sound good without conveying information; such as vague precision or subjective phrases.

E.g., a number of, surprisingly, very close.

It's okay not to have exact details, but rather than “I don't know” explain why not and what the next steps will be.

Passive Voice

Phrases wherein interest is in the object experiencing an action, rather than the subject that performs the action.

  • Bad: The house was built by my father.
  • Good: My father built this house.

Likewise, including relevant or explanatory information as in “X guarantees Y” is an improvement over “Y is guaranteed”.

Sometimes the subject really is irrelevant, such as “We did X” whereas “X happened” suffices.

👍 If the relevant subject is unclear and, also, the text reads better in the active, then change a phrase.

Duplicated Words

Occurrences of, say, “the the”.

Harder to catch manually, but easier mechanically ;-)

7.8. Placeholder Text —For Learning & Experimenting

When learning about Emacs formatting commands, such as zap-to-char M-z or transpose M-t, it's best to have filler text —even better when it's automatically generated instead of typing it out ourselves. The following will give us a series of commands lorem-ipsum-insert-⋯ for inserting lists, sentences, paragraphs and using a prefix argument, with C-u, we can request to generate any number of them.

(use-package lorem-ipsum :defer t)

‘Lorem’ is not a word itself, but it comes from the Latin ‘Dolorem Ipsum’ which means “pain in and of itself”.

See this Emacs Cheat Sheet to try out the textual navigation and formatting bindings on lorem ipsum, gibberish text.

7.9. Some text to make us smile

The dad-joke queries https://icanhazdadjoke.com to bring us some funny.

(use-package dad-joke
  :defer t
  :config (defun dad-joke () (interactive) (insert (dad-joke-get))))

For example, M-x dad-joke now inserts:

What are the strongest days of the week? Saturday and Sunday…the rest are weekdays.

7.10. Unicode Input via Agda Input

Agda is one of my favourite languages, it's like Haskell on steroids. Let's set it up for the main sake of its Unicode input —you may do likewise using TeX input. ( The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!) )

Agda input mode makes it extremely easy to use unicode in documents, something I strongly prefer to do. When I can use symbols directly, instead of (for instance) LaTeX commands, it makes my plaintext far more readable.Armkeh .emacs config

(system-packages-ensure "agda")
To use the Agda standard library by default
   mkdir -p ~/.agda
   echo /usr/local/lib/agda/standard-library.agda-lib >>~/.agda/libraries
   echo standard-library >>~/.agda/defaults

Invoke brew info agda to get these instructions and the version of Agda just installed.

Get font support for subscripts (, if) need be
  • Download and unzip the symbola font
  • CMD + SPCfont book+ ⇒ select the symbola directory you just unzipped

(Note: In the before time, you could brew install this font.)

Executing agda-mode setup appends the following text to the .emacs file. Let's put it here ourselves.

(unless noninteractive
  (load-file (let ((coding-system-for-read 'utf-8))
               (shell-command-to-string "/usr/local/bin/agda-mode locate"))))

I almost always want the agda-mode input method —it's like the TeX method, but better.

;; MA: This results in "Package cl is deprecated" !?
(unless noninteractive
  (use-package agda-input
  :ensure nil ;; I have it locally.
  :demand t
  :hook ((text-mode prog-mode) . (lambda () (set-input-method "Agda")))
  :custom (default-input-method "Agda")))
  ;; Now C-\ or M-x toggle-input-method turn it on and offers


;; TODO add a hook that when the input method becomes Agda, just don't bother showing me in the modeline.
;; E.g., "Π" when using unicode input with Agda
;; Useful to have in the modeline, say when typing in Arabic.
;; (add-variable-watcher
;;  'current-input-method
;;  (lambda (_ newvalue 'set _)
;;    (setq current-input-method-title
;;          (if (equal newvalue "Agda") nil newvalue))))

Unicode doesn't intend to cover things that are achievable with markup, so only a limited subset of the alphabet is available as subscript; but all is available as superscript, except ‘q’.

ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓ ⁰ ¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ⁺ ⁻ ⁼ ⁽ ⁾ ₀ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ₊ ₋ ₌ ₍ ₎ ᵃ ᵇ ᶜ ᵈ ᵉ ᶠ ᵍ ʰ ⁱ ʲ ᵏ ˡ ᵐ ⁿ ᵒ ᵖ ʳ ˢ ᵗ ᵘ ᵛ ʷ ˣ ʸ ᶻ ᴬ ᴮ ᴰ ᴱ ᴳ ᴴ ᴵ ᴶ ᴷ ᴸ ᴹ ᴺ ᴼ ᴾ ᴿ ᵀ ᵁ ⱽ ᵂ ᵅ ᵝ ᵞ ᵟ ᵋ ᶿ ᶥ ᶲ ᵠ ᵡ ᵦ ᵧ ᵨ ᵩ ᵪ

brew cask install font-symbola ⇒ Includes fonts for subscripts; e.g., ₐₙₑₕᵢⱼₖₗₘₙₒₚₜₛ

Below are my personal Agda input symbol translations; e.g., \set → 𝒮ℯ𝓉. Note that we could give a symbol new Agda TeX binding interactively: M-x customize-variable agda-input-user-translations then INS then for key sequence type set then INS and for string paste 𝒮ℯ𝓉.

(unless noninteractive (add-to-list 'agda-input-user-translations '("set" "𝒮ℯ𝓉")))

Better yet, as a loop:

(unless noninteractive
(cl-loop for item
      in '(;; Arabic ornate parenthesis U+FD3E / U+FD3F
          ("(" "﴾")
          (")" "﴿")
          ("cmd" "⌘")
           ;; categorial ;;
           ("alg" "𝒜𝓁ℊ")
           ("split" "▵")
           ("join" "▿")
           ("adj" "⊣")
           (";;" "﹔")
           (";;" "⨾")
           (";;" "∘")
           ;; logic
           ("if" "⇐")
           ("onlyif" "⇒")
           ;; lattices ;;
           ("meet" "⊓")
           ("join" "⊔")
           ;; tortoise brackets, infix relations
           ("((" "〔")
           ("))" "〕")
           ;; residuals
           ("syq"  "╳")
           ("over" "╱")
           ("under" "╲")
           ;; Z-quantification range notation ;;
           ;; e.g., “∀ x ❙ R • P” ;;
           ("|"    "❙")
           ("with" "❙")
           ;; Z relational operators
           ("domainrestriction" "◁")
           ("domr" "◁")
           ("domainantirestriction" "⩤")
           ("doma" "⩤")
           ("rangerestriction" "▷")
           ("ranr" "▷")
           ("rangeantirestriction" "⩥")
           ("rana" "⩥")
           ;; adjunction isomorphism pair ;;
           ("floor"  "⌊⌋")
           ("lower"  "⌊⌋")
           ("lad"    "⌊⌋")
           ("ceil"   "⌈⌉")
           ("raise"  "⌈⌉")
           ("rad"    "⌈⌉")
           ;; Replies
           ("yes"  "✔")
           ("no"    "❌")
           ;; Arrows
           ("<=" "⇐")
        ;; more (key value) pairs here
        )
      do (add-to-list 'agda-input-user-translations item)))

Also some silly stuff:

(unless noninteractive
;; Add to the list of translations using “emot” and the given, more specfic, name.
;; Whence, \emot shows all possible emotions.
(cl-loop for emot
      in `(;; angry, cry, why-you-no
           ("whyme" "ლ(ಠ益ಠ)ლ" "ヽ༼ಢ_ಢ༽ノ☂" "щ(゜ロ゜щ)" "‿︵(ಥ﹏ಥ)‿︵" "ಠ_ಠ" "(╬ ಠ益ಠ)" "・゚(*❦ω❦)*・゚" "(╯°□°)╯︵ ┻━┻") ;; flip the table
           ;; confused, disapprove, dead, shrug, awkward
           ("what" "「(°ヘ°)" "(ಠ_ಠ)" "(✖╭╮✖)" "¯\\_(ツ)_/¯"  "(´°ω°`)" "・✧_✧・")
           ;; dance, csi
           ("cool" "┏(-_-)┓┏(-_-)┛┗(-_- )┓"
            ,(s-collapse-whitespace "•_•)
                                      ( •_•)>⌐■-■
                                      (⌐■_■)"))
           ;; love, pleased, success, yesss, smile, excited, yay
           ("smile" "♥‿♥" "(─‿‿─)" "(•̀ᴗ•́)و" "ᕦ( ᴼ ڡ ᴼ )ᕤ" "(งಠ_ಠ)ง" "(。◕‿◕。)" "(◕‿◕)" "( ˃ ヮ˂)" "[ ⇀ ‿ ↼ ]" "٩(⁎❛ᴗ❛⁎)۶" "ᴵ’ᵐ ᵇᵉᵃᵘᵗⁱᶠᵘˡ" "(✿◠‿◠)")
           ;; flower high-5
           ("hug" "♡(✿ˇ◡ˇ)人(ˇ◡ˇ✿)♡" "(づ。◕‿◕。)づ" "(づ。◕‿‿‿‿◕。)づ"))
      do
      (add-to-list 'agda-input-user-translations emot)
      (add-to-list 'agda-input-user-translations (cons "emot" (cdr emot)))))

Finally let's effect such translations.

;; activate translations
(unless noninteractive (agda-input-setup))

Note that the effect of Emacs unicode input could be approximated using abbrev-mode.

7.11. Increase/decrease text size

The ‘usual’ text zoom keys C-±

(global-set-key (kbd "C-+") 'text-scale-increase)
(global-set-key (kbd "C--") 'text-scale-decrease)
;; C-x C-0 restores the default font size

If thou knowst the ELisp, forgive this shadowing of the negative-argument … we've still got M-- though.

Curious, this is one of the very first things I did when began using Emacs; yet, perhaps I would not have done it if I was simply told the defaults:

  • C-x C-=,+ increases text size
  • C-x C-- decreases test size
  • C-x C-0 restores it to the default size

So, the above snippet seems to save us of the prefix C-x and we lose on using ‘=’ for text increase and worse we need the shift-key to get access to the ‘+’.

I suppose this is just a habit inherited from using other tools. Fortunately, I did not inherit the need for the common user access bindings C-x kill, C-c copy, C-v paste, nor C-z undo of other applications. If you're interested, M-x cua-mode to enable CUA Bindings.

7.12. Moving Text Around

This extends Org-mode's M-↑,↓ to other modes, such as when coding.

;; M-↑,↓ moves line, or marked region; prefix is how many lines.
(use-package move-text
  :config (move-text-default-bindings))

7.13. Enabling CamelCase Aware Editing Operations

Subword movement lets us treat “EmacsIsAwesome” as three words ─“Emacs”, “Is”, and “Awesome”─ which is desirable since such naming is common among coders. Now, for example, M-f moves along each subword.

(global-subword-mode 1)
(diminish 'subword-mode)

7.14. Delete Selection Mode

Delete Selection mode lets you treat an Emacs region much like a typical text selection outside of Emacs: You can replace the active region. We can delete selected text just by hitting the backspace key.

(delete-selection-mode 1)

7.15.   M-n,p: Word-at-Point Navigation ╱╲ Automatic highlighting current symbol/word

Let's mimic the C-n,p constructs from line to word, so that unoccupied M-n,p now serve to take us to the next or previous instance of the word under the cursor. This is less intrusive than searching C-s or listing all occurrences M-s o. Moreover, let's automaticly highlight current symbols, words under the cursour, and cycle quickly between highlkighted locations with ahs-forward / M-→ and ahs-backward / M-←.

;; Default: M-→/← moves to the next/previous instance of the currently highlighted word
;; These are already meaningful commands in Org-mode, so we avoid these key re-bindings in Org-mode; TODO.
(use-package auto-highlight-symbol)
;;   :hook ((text-mode . auto-highlight-symbol-mode)
;;          (prog-mode . auto-highlight-symbol-mode)))
;;
;; This breaks Org Exports; e.g.,
;; C-c C-e h o  ⇒  Match data clobbered by buffer modification hooks

Let's also quickly replace the word at point:

(defun my/symbol-replace (replacement)
  "Replace all standalone symbols in the buffer matching the one at point."
  (interactive  (list (read-from-minibuffer "Replacement for thing at point: " nil)))
  (save-excursion
    (let ((symbol (or (thing-at-point 'symbol) (error "No symbol at point!"))))
      (beginning-of-buffer)
      ;; (query-replace-regexp symbol replacement)
      (replace-regexp (format "\\b%s\\b" (regexp-quote symbol)) replacement))))

(Unfortunately, as it currently is, there is no universal argument support: C-u 2 M-p does not take you to the second previous instance of a word —the prefix is instead ignored. We can quickly form a work-around with defhydra —which gives us temoporary modal modes.)

Finally, let's use M-n ╱ M-p ╱ M-' to move to the next, previous, and replace the word at point but also to give us a meanu to repeat any of these actions.

(defmacro my/make-navigation-hydra (initial-action)
  `(defhydra word-navigation
    (:body-pre (,initial-action)) "Word-at-point Navigation"
    ("n" ahs-forward "Next instance")
    ("p" smartscan-symbol-go-backward "Previous instance")
    ("r" my/symbol-replace "Replace all occurances")
    ("s" ahs-display-stat "Stats")))

;; (bind-key* str func) ≈ (global-set-key (kbd str) func)
(bind-key* "M-n" (my/make-navigation-hydra ahs-forward))
(bind-key* "M-p" (my/make-navigation-hydra ahs-backward))
(bind-key* "M-'" (my/make-navigation-hydra my/symbol-replace))

An alternative is the smart-scan library —which does the same thing, but does not provide the highlighting occurances at point.

7.16. Letter-based Navigation

At a glance of possible positions, across windows, and a key to jump there is a feature provided to us by ace-jump —here is an emacs-rocks 2-minute video.

For example, C-c SPC m greys our all windows and places a red letter at the start of any word that begins with m, then I may press a letter to jump to the associated position in the associated window. Using C-u C-c SPC and C-u C-u C-c SPC let me jump to any character or to any visible line.

➩ Super simple use case: Fix your eyes on an occurence of a word, then C-c SPC to quickly jump to it so as to edit the sentence in which it occurs.

  • It's like C-s but more lightweight.
(use-package ace-jump-mode
  :defer t
  :config (bind-key* "C-c SPC" 'ace-jump-mode))

;; See ace-jump issues to configure for use of home row keys.

There is a newer and somewhat more powerful package, avy, which accompishes the same goal. It uses a tree style to jumipng: Locations are given two letter combinations, one presses one letter to jump to a group of text, then another letter to jump somewhere in that grouping. I prefer ace-jump since it greys everthing out, whereas avy surrounds jump locations with a box. Here is an emacs-doom 6-minute video for avy.

There is also ace-isearch for bridinging different navgiational methods —one begins incremental search, s-f, then according to a pause and length of input, one of the navgiational methods, such as isearch or avy or helm-swoop, will be begun. I'm okay with using C-s for helm-swoop and C-c SPC for ace-jump, and still have s-f for incremental search, which I hardly use.

What is bind-keys*?

Major modes provide specfic use and so their bindings always take precedence over global bindings —e.g., the major mode binding may do what the global does but with extra mode-specfic behaviour, such as indentation. Other times, a major mode's binding simply uses the same key presses with completely unrelated behaviour. If we want to avoid having our global keybindings shadowed by a major mode, we may use the bind-key* macro of use-package, or the bind-keys* macro when there are multiple keys; these are macros, not clauses. —These essentially creates a dedicated minor mode behind the scenes, which saves us the work of doing it ourselves.

  (bind-keys* (k₁ . f₁) … (kₙ . fₙ))
These keybindings override all minor modes that use keys kᵢ.

Of course we can also use it without the asterisk; e.g.:

;; C-x o ⇒ Switch to the other window
;; C-x O ⇒ Switch back to the previous window
(bind-key "C-x O" (lambda () (interactive) (other-window -1)))

7.17.   C-c e n,p: Taking a tour of one's edits

This package allows us to move around the edit points of a buffer without actually undoing anything. We even obtain a brief description of what happend at each edit point. This seems useful for when I get interrupted or lose my train of thought: Just press C-c e p to see what I did recently and where —the “e” is for “e”dit.

;; Give me a description of the change made at a particular stop.
(use-package goto-chg
  :defer t
  :custom (glc-default-span 0))

(my/defhydra "C-c e" "Look at them edits!" bus
  :\  ("p" goto-last-change "Goto nᵗʰ last change")
      ("n" goto-last-change-reverse "Goto more recent change"))

Compare this with C-x u, or undo-tree-visualise, wherein undos are actually performed.

Notice, as a hydra, I can use C-c e followed by any combination of p and n to navigate my recent edits without having to supply the prefix each time.

7.18. visual-regexp

;; While constructing the regexp in the minibuffer, get live visual feedback for the (group) matches.
;; E.g., try: M-% use-\(.+?\) \(.+\)\b ENTER woah \1 and \2
;;
;; C-u M-%  do to regexp replace, without querying.
(use-package visual-regexp
  :config (define-key global-map (kbd "M-%")
            (lambda (&optional prefix) (interactive "P") (call-interactively (if prefix  #'vr/replace #'vr/query-replace)))))

7.19. LaTeX ⇐ Org-Mode

In this section we consider the Org-mode export for PDFs (LaTeX). For example, we account for LaTeX citations.

7.19.1. Get LaTeX:

(system-packages-ensure "mactex")
  • This is a redistribution of TeX Live specifically for macOS.
  • We get the 4GB version since it has everything and so do not need to worry about missing style files.
  • This took about 12 minutes on my machine.

Restart Emacs, enter $e^{i \cdot \pi} + 1 = 0$ then press C-c C-x C-l to have it rendered inline.

  • Minted: Get tool for colourful code snippets for LaTeX —see “minted” in the main article.

    (system-packages-ensure "pygments")
    
  • Not anymore: Get a neato PDF presentation console: brew install pdfpc

    • With pdfpc myfile.pdf you get a nice timer and multiple views of the current slide and upcoming slides —with support for multiple monitors.
    • Install ScreenBrush, from the Apple Store, for easily drawing/annotating my screen —e.g., when I'm giving a virtual lecture to my students.
    • An alternative is brew install mupdf then mupdf-gl myfile.pdf and press f for fullscreen then a for adding/adorning drawings —it was too rough to use live.

    Finally, within Emacs: M-x pdf-tools-install

7.19.2. Working with Citations   Disabled Not_Used

An exquisite system for handling references.

The following entity will display useful data when the mouse hovers over it (•̀ᴗ•́)و If you click on it, then you're in for a lot of super neat stuff, such as searching for the pdf online!

cite:agdaoverview ( In HTML export, the citation doesn't link anywhere. )

(unless noninteractive

(use-package org-ref
  :custom ;; Files to look at when no “╲bibliography{⋯}” is not present in a file.
          ;; Most useful for non-LaTeX files.
        (reftex-default-bibliography '("~/thesis-proposal/papers/References.bib"))
        (bibtex-completion-bibliography (car reftex-default-bibliography))
        (org-ref-default-bibliography reftex-default-bibliography))

;; Quick BibTeX references, sometimes.
(use-package helm-bibtex)
(use-package biblio) )

Execute M-x helm-bibtex or C-c ] and, say, enter emacs and you will be presented with all the entries in the bib database that mention ‘emacs’. Super cool stuff. Moreover, if no such entries exist, then we can look some up using the interface!

Read the manual online or better yet as an org-file with M-x org-ref-help.

This is an Org-mode application since the citations have tooltips and export nicely to LaTeX & HTML via the Org-mode exporter.

7.19.3. Bibliography & Coloured LaTeX using Minted

Execute the following for bibliography references as well as minted Org-mode uses the Minted package for source code highlighting in PDF/LaTeX —which in turn requires the pygmentize system tool.

(setq org-latex-listings 'minted
      org-latex-packages-alist '(("" "minted"))
      org-latex-pdf-process
      '("pdflatex -shell-escape -output-directory %o %f"
        "biber %b"
        "pdflatex -shell-escape -output-directory %o %f"
        "pdflatex -shell-escape -output-directory %o %f"))

For faster pdf generation, possibly with errors, consider invoking:

(setq org-latex-pdf-process
      '("pdflatex -interaction nonstopmode -output-directory %o %f"))

By default, Org exports LaTeX using the nonstopmode option, which tries its best to produce a PDF —which ignores typesetting errors altogether, which is not necessary ideal when using LaTeX.

7.20. HTML ⇐ Org-mode

In this section we consider the Org-mode exporters for PDFs and HTMLs. For example, we account for LaTeX citations and reliable HTML anchors.

(use-package htmlize :defer t)
;; Main use: Org produced htmls are coloured.
;; Can be used to export a file into a coloured html.

7.20.1. Ensuring Useful HTML Anchors

Upon HTML export, each tree heading is assigned an ID to be used for hyperlinks. Default IDs are something like org1957a9d, which does not endure the test of time: Re-export will produce a different id. Here's a rough snippet to generate IDs from headings, by replacing spaces with hyphens, for headings without IDs.

(defun my/ensure-headline-ids (&rest _)
  "Org trees without a

All non-alphanumeric characters are cleverly replaced with ‘-’.

If multiple trees end-up with the same id property, issue a
message and undo any property insertion thus far.

E.g., ↯ We'll go on a ∀∃⇅ adventure
   ↦  We'll-go-on-a-adventure
"
  (interactive)
  (let ((ids))
    (org-map-entries
     (lambda ()
       (org-with-point-at (point)
         (let ((id (org-entry-get nil "CUSTOM_ID")))
           (unless id
             (thread-last (nth 4 (org-heading-components))
               (s-replace-regexp "[^[:alnum:]']" "-")
               (s-replace-regexp "-+" "-")
               (s-chop-prefix "-")
               (s-chop-suffix "-")
               (setq id))
             (if (not (member id ids))
                 (push id ids)
               (message-box "Oh no, a repeated id!\n\n\t%s" id)
               (undo)
               (setq quit-flag t))
             (org-entry-put nil "CUSTOM_ID" id))))))))

;; Whenever html & md export happens, ensure we have headline ids.
(advice-add 'org-html-export-to-html   :before 'my/ensure-headline-ids)
(advice-add 'org-md-export-to-markdown :before 'my/ensure-headline-ids)

One may then use [[#my-custom-id]] to link to the entry with CUSTOM_ID property my-custom-id.

Interestingly, org-set-property, C-c C-x p, lets us insert a property from a selection of available ones, then we'll be prompted for a value for it from a list of values you've used elsewhere. This is useful for remaining consistent for when trees share similar properties.

7.20.2. Clickable Headlines

By default, HTML export generates ID's to headlines so they may be referenced to, but there is no convenient way to get at them to refer to a particular heading. The following spell fixes this issue: Headlines are now clickable, resulting in a link to the headline itself.

;; Src: https://writepermission.com/org-blogging-clickable-headlines.html
(setq org-html-format-headline-function
      (lambda (todo todo-type priority text tags info)
        "Format a headline with a link to itself."
        (let* ((headline (get-text-property 0 :parent text))
               (id (or (org-element-property :CUSTOM_ID headline)
                       (ignore-errors (org-export-get-reference headline info))
                       (org-element-property :ID headline)))
               (link (if id
                         (format "<a href=\"#%s\">%s</a>" id text)
                       text)))
          (org-html-format-headline-default-function todo todo-type priority link tags info))))

Warning: The header cannot already be a link! Otherwise you get cyrptic and unhelpful error (wrong-type-argument plistp :section-number); which then pollutes the current Emacs session resulting in stange nil errors after C-x C-s, thereby forcing a full Emacs restart. Instead, you need at least one portion of each heading to be not a link.

  1. Need to have a custom id declared.

      :PROPERTIES:
      :CUSTOM_ID: my-header
      :END:
    
  2. Failing headers: * [[link]] nor * ~code~ nor * $math$.
    • Any non-link text before it will work: ok [[link]].
      • Using Unicode non-breaking space ‘ ’ is ok.
    • Text only after the link is insufficient.

7.20.3. HTML “Folded Drawers”

(defun my/org-drawer-format (name contents)
  "Export to HTML the drawers named with prefix ‘fold_’, ignoring case.

The resulting drawer is a ‘code-details’ and so appears folded;
the user clicks it to see the information therein.
Henceforth, these are called ‘fold drawers’.

Drawers without such a prefix may be nonetheless exported if their
body contains ‘:export: t’ ---this switch does not appear in the output.
Thus, we are biased to generally not exporting non-fold drawers.

One may suspend export of fold drawers by having ‘:export: nil’
in their body definition.

Fold drawers naturally come with a title.
Either it is specfied in the drawer body by ‘:title: ⋯’,
or otherwise the drawer's name is used with all underscores replaced
by spaces.
"
  (let* ((contents′ (replace-regexp-in-string ":export:.*\n?" "" contents))
         (fold? (s-prefix? "fold_" name 'ignore-case))
         (export? (string-match ":export:\s+t" contents))
         (not-export? (string-match ":export:\s+nil" contents))
         (title′ (and (string-match ":title:\\(.*\\)\n" contents)
                      (match-string 1 contents))))

    ;; Ensure we have a title.
    (unless title′ (setq title′ (s-join " " (cdr (s-split "_" name)))))

    ;; Output
    (cond
     ((and export? (not fold?)) contents′)
     (not-export? nil)
     (fold?
      (thread-last contents′
        (replace-regexp-in-string ":title:.*\n" "")
        (format "<details class=\"code-details\"> <summary> <strong>
            <font face=\"Courier\" size=\"3\" color=\"green\"> %s
            </font> </strong> </summary> %s </details>" title′))))))

(setq org-html-format-drawer-function 'my/org-drawer-format)

With the following invocations we only see the odd indexed ‘hello’s, where the latter two are folded up.

:this-drawer-is-exported:
:export: t
hello 1
:End:

:this-drawer-is-NOT-exported:
hello 2
:End:

:fold_This_drawer_has_a_title_in_the_body:
:title: I am the drawer title 0

hello 3
:End:

:fold_This_drawer_is_NOT_exported:
:title: Why are we here?
:export: nil

hello 4
:End:

:fold_I_am_the_drawer_title_1:

hello 5
:End:

I doubt I could show an example in the Github README, since no HTML export is happening using my setup. In case you're reading this on my blog, which has exported HTML. Here's the example:

Now that I've written this, I'm thinking it may have been preferably to use an org-block…?

7.20.4. Diagrams with Mermaid —Not Reccommended

Let's try out an alternative to PlantUML —covered below in §5.5.

First, let's get the tool.

npm install mermaid.cli
sudo git clone git@github.com:arnm/mermaid-layer.git ~/.emacs.d/private/mermaid

Then, let's get the associated mermaid package.

(use-package ob-mermaid
  :custom ob-mermaid-cli-path "~/node_modules/.bin/mmdc")

Then, C-c C-c on the following:

test.png

  • C-c C-x C-v ⇒ Show images inline
  • Mermaid supported headers:
    • file to name the svg/png/pdf output
    • width or height or the resulting image
    • theme used, such as default, forest, dark, neutral, for foreground entities
    • background-color such as transparent, red, #F0F0F0
      • The transparent option is nice ^_^
  • You can insert new lines using <br> and horizontal rules via <hr>. Similarly you can use other HTML tags such as <center>; if you have too many you can make CSS file then use the header argument :css-file.
  • Add “non-breaking space” with &nbsp;. This is a forced extra space and it prevents a line break at its location. You can insert it repeatedly, but for two spaces use &ensp; and for four spaces use &emsp;.

If link text cuts off prematurely, use extra space with a newline: A-- text &ensp;<br> -->B.

Warning: JavaScript has some issues when working with Unicode and so, being a JavaScript utility, mermaid hangs when Unicode is used. On the upside, being a JavaScript utility, mermaid entities can have arbitrary code attached to them to be executed upon clicks —for use in browsers.

  • However, the Greek letters are supported; e.g., γ and Σ.

See here for possible node shapes.

After forming an intricate diagram of related design patterns, I had to use a number of HTML notions, such as <i>, <strong>, <em>, <h1>, &ensp;, <br>, <pre>, <center> and it was a bit more than I would have liked. In particular, the only way to change font size was to use the deprecated HTML tag <big> or heading tags like <h1>; even worse, the resulting PDF image did not look nice —I had to stretch it out.

The command line tool is lacking functionality and so the docs are not helpful. E.g., I cannot produce pie charts using the command line tool.

7.20.5.   Reveal.JS – The HTML Presentation Framework

Org-mode documents can be transformed into beautiful slide decks with org-reveal with the following two simple lines.

(use-package ox-reveal
  :custom (org-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js"))

For example, execute, C-x C-e after the closing parenthesis of, the following block to see an example slide-deck (─‿‿─)

(progn (shell-command "curl https://raw.githubusercontent.com/yjwen/org-reveal/696613edef0fe17a9c53146f79933fe7c4101100/Readme.org >> Trying_out_reveal.org")
       (switch-to-buffer (find-file "Trying_out_reveal.org"))
       (org-reveal-export-to-html-and-browse))

Org-mode exporting, C-c C-e, now includes an option R for such reveal slide decks.

Two dimensional slides may be a bit new to some people, so I like to give viewers an option, in tiny font, to view the slide-deck continuously and remind them that ? provides useful shortcuts.

(setq org-reveal-title-slide "<h1>%t</h1> <h3>%a</h3>
<font size=\"1\">
<a href=\"?print-pdf&showNotes=true\">
⟪ Flattened View ; Press <code>?</code> for Help ⟫
</a>
</font>")

One should remove the &showNotes=true if they do not want to include speaker notes in the flattened view.

Within the flatenned view, one may wish to CTRL/CMD+P then save the resulting PDF locally.

7.20.6. Org-mode ⇐ HTML   Disabled

The following let's us copy htlm into org format using eww, Emacs' built-in web browser.

;; See: https://emacs.stackexchange.com/questions/7171/paste-html-into-org-mode
(use-package org-eww
 :quelpa (org-eww :fetcher git :url "https://github.com/Fuco1/org-mode.git"))

It does not work as I'd like, but may prove useful to have around.

  • Possibly useful: Open a webpage with M-x eww then toggle M-x read-only-mode to edit the text, say for notes or deletions, as you read! No need to copy-paste.

org-web-tools claims to view, capture, and archive Web pages in Org-mode; this may be a very useful tool.

(use-package org-web-tools
  :config
  ;; Insert an Org-mode link to the URL in the clipboard or kill-ring. Downloads
  ;; the page to get the HTML title.
  ;; (bind-key* "C-c C-l" #'org-web-tools-insert-link-for-url) ;; Instead, see my/org-insert-link-dwim below.
  )

Other useful functions, needing pandoc: org-web-tools-insert-web-page-as-entry and org-web-tools-convert-links-to-page-entries.

;; C-u C-c C-l ⇒ Paste URL with title, WITHOUT prompting me for anything.
;; C-c C-l ⇒ Prompt me for title.
(bind-key* "C-c C-l"
           (lambda () (interactive)
             (call-interactively
              (if current-prefix-arg
                  #'org-web-tools-insert-link-for-url
                #'my/org-insert-link-dwim))))
;; From:
(defun my/org-insert-link-dwim ()
  "Like `org-insert-link' but with personal dwim preferences.

- When text is selected, use that as the link description --and prompt for link type
- When a URL is in the clipboard, use that as the link type
- On an existing Org link, prompt to alter the link then to alter the description
- With a ‘C-u’ prefix, prompts for a file to link to.
  - It is relative to the current directory; use ‘C-u C-u’ to get an absolute path.

It fallsback to `org-insert-link' when possible.

Functin Source: https://xenodium.com/emacs-dwim-do-what-i-mean/"
  (interactive)
  (let* ((point-in-link (org-in-regexp org-link-any-re 1))
         (clipboard-url (when (string-match-p "^http" (current-kill 0))
                          (current-kill 0)))
         (region-content (when (region-active-p)
                           (buffer-substring-no-properties (region-beginning)
                                                           (region-end)))))
    (cond ((and region-content clipboard-url (not point-in-link))
           (delete-region (region-beginning) (region-end))
           (insert (org-make-link-string clipboard-url region-content)))
          ((and clipboard-url (not point-in-link))
           (insert (org-make-link-string
                    clipboard-url
                    (read-string "title: "
                                 (with-current-buffer (url-retrieve-synchronously clipboard-url)
                                   (dom-text (car
                                              (dom-by-tag (libxml-parse-html-region
                                                           (point-min)
                                                           (point-max))
                                                          'title))))))))
          (t
           (call-interactively 'org-insert-link)))))

8. Programming

Herein we configure utilites for version control, function and variable lookup, and template expansion for inescapably repetitive scenarios.

TODO: Fix these docs

8.1. Quickly Run Code Snippets

Sometimes we want to quickly run some code without making a dedicated file or with a file but without remembering the terminal incantation to do so, enter quickrun. Anywhere, we can select a snippet of code and run quickrun-region to execute that snippet after selecting the associated programming language, or quickrun-replace-region if we want the results in-line. If our language of choice does not exist, we can easily add support for it.

;; In any programming buffer, “M-x quickrun” to execute that program.
;; Super useful when wanting to quickly test things out, in a playground.
;;
;; E.g., Make a new file named “hello.py” containing “print "hi"”, then “M-x quickrun”.
;;
;; Enable “quickrun-autorun-mode” to run code after every save.
(use-package quickrun
  ;; ⇒ “C-c C-r” to see output, “q” to close output
  ;; ⇒ “C-u C-c C-r” prompts for a language (Useful when testing snippets different from current programming mode)
  ;; ⇒ In a non-programming buffer, “C-c C-r” runs selected region.
  :config (bind-key* "C-c C-r"
                     (lambda (&optional start end)
                       (interactive "r")
                       (if (use-region-p)
                           (quickrun-region start end)
                         (quickrun current-prefix-arg)))))

Example…

(system-packages-ensure "rust") ;; Rust Compiler
;; Select the following then press C-c C-r: fn main() { println!("Hello, World!"); }

Actually, let's get a full Rust development environment for Emacs (which also has great support for Org-babel.)

(use-package rustic)
;; Open any Rust file, and run “M-x lsp” which will then prompt you to install
;; rust-analyzer, the rust LSP.
;;
;; LSP for Rust ⇒ Goto definition (M-. / ⌘-l), code completion with types and
;; docstrings, colourful documentation on hover, “Run [Test] | Debug” overlays,
;; super nice stuff! Run “M-!”/[M-x company-show-doc-buffer] if you want the doc in a colourful buffer.
;;
;; Below, hover over “Vec” and see nice, scrollable, colourful docs on vectors.
;;    let v:Vec<_> = vec![1, 2, 3];

;; The offical Rust toolchain installer
(system-packages-ensure "rustup")
(shell-command "rustup update")

8.2. C-x C-e ∷ REPL-driven development for ELisp / NodeJS / Python / Etc !

Evaluate code and see the results inline —A feedback loop that is faster than ever!

Within Emacs, C-x C-e evaluates a Lisp expression anywhere; e.g., at the end of (message-box "hello world") press C-x C-e to see a greeting.

  • We ran some code without explicitly running an interpreter/repl/compiler!
  • This is known as “REPL driven development” (RDD): There is a running REPL server for your language, implicitly in the background, and your editor (say with C-x C-e) will send it a line (or a selected region) of code for evaluation; we then see the result as an overlay in our current buffer.
    • You choose which code gets (re)evaluated.
  • A quick introduction to RDD can be viewed at PurelyFunctional.tv.

8.2.1. ELisp

By default, Emacs Lisp's C-x C-e shows results only in the minibuffer; near the bottom of the screen. Let's also have evaluation results displayed as inline overlays —at the location that the user, us, is actually looking/working; rather than forcing their eyes to shift up&down when writing&evaluating.

  • C-u C-x C-e inserts the evaluation result at point; C-u 0 C-x C-e does so without truncating lengthy output.
  • Read this Sweet & short blog/GIFs on practical uses of C-x C-e when working with Lisp.
;; Evaluation Result OverlayS for Emacs Lisp
(use-package eros
  :init (eros-mode t))

8.2.2. JavaScript

Let's setup RDD for JavaScript —by having a NodeJS repl server running in the background.

(use-package skerrick
  :init
  ;; Needs to be run on the very first install of skerrick. Or when you want to upgrade.
  (unless (equal (shell-command-to-string "type skerrick") "skerrick not found\n")
    (skerrick-install-or-upgrade-server-binary)))

;; Should be run in a JS buffer; it is buffer specific.
;; (skerrick-start-server)

;; Now main function, entry point is:
;; M-x skerrick-eval-region

Let's provide a quick keyboard shortcut. E.g., C-x C-e evaluates ELisp, so let's mimic that for JS buffers:

(require 'js) ;; Defines js-mode-map

;; Evaluate a region, if any is selected; otherwise evaluate the current line.
(bind-key
 "C-x C-e"  (lambda ()
              (interactive)
              (if (use-region-p)
                  (skerrick-eval-region)
                (beginning-of-line)
                (set-mark-command nil)
                (end-of-line)
                (skerrick-eval-region)
                (pop-mark)))
 'js-mode-map)

For instance,

// Start the server... then
// On each line and press C-x C-e
let a = "hello"
let b = "world"
(a + ' ' + b).toUpperCase()
// The final line should show: HELLO WORLD
  1. Preserving the context

    When testing an application, you might notice a bug in a particular context —i.e., a particular configuration in the app.

    1. The classic approach is to kill the app; i.e., stop the server that is, well, serving the app.
    2. Solve the problem.
    3. Try to get back to the configuration, context, you were in beforehand and check that the problem has been resolved.

    A better approach is to ignore the bookkeeping steps, 1&3, and just do step 2. For that, there are numerous packages:

8.2.3. Python, etc

It seems to be super easy to add such a support for other languages, such as Python. See the final comment here for the tiny change required.

8.3. devdocs

;; 1. Get docs of a languages: M-x devdocs-install
;; 2. Lookup docs: [C-u] M-x devdocs-lookup
;; 𝟚. Lookup docs: [C-u] C-c d
(use-package devdocs
  :bind ("C-c d" . #'devdocs-lookup)
  :config
  (when nil ;; “C-x C-e” the following once.
    (cl-loop for lang in '(javascript ramda typescript html css sass
                       vue~3 vuex~4 vue_router~4 "angularjs~1.6"
                       nginx webpack~5 web_extensions
                       ;;
                       eslint  jest jq jsdoc prettier
                       mocha chai jasmine
                       ;;
                       bash docker~19 git homebrew elisp
                       ;;
                       postgresql~14 redis sqlite
                       ;;
                       rust ruby~3 minitest "rails~7.0")
          do (devdocs-install (list (cons 'slug (format "%s" lang)))))))

8.4. How do I do something?

When programming, sometimes you just gotta Google “how do I do ⋯”.

  • The usual process is (1) open a browser, (2) make a Google query, (3) look at StackOverflow's most upvoted answer for your query, (4) copy-paste the code solution/example to your editor; [(5) get distracted by interesting things you'd like to read].
  • Better would be to use the howdoi tool, which gives instant coding answers for common questions via the command line.
  • Below, my Emacs Lisp function howdoi let's me reduce the 4-step process to just 2 steps: Write your query anywhere then call M-x howdoi on it to replace the query with the answer. (Or C-u M-x howdoi to see the full answer and a link to it on StackOverflow.)

    ⚡ Never open your browser to look for help again ⚡
(system-packages-ensure "howdoi")

(cl-defun howdoi (&optional show-full-answer)
  "Instantly insert coding answers.

Replace a query with a code solution; replace it with an entire
answer if a prefix is provided.

Example usage:

   On a new line, write a question such as:

      search and replace buffer Emacs Lisp

   Then invoke ‘M-x howdoi’ anywhere on the line
   to get a code snippet; or ‘C-u M-x howdoi’ to get a full answer to your query.
"
  (interactive "P")
  (let ((query (s-collapse-whitespace (substring-no-properties (thing-at-point 'line))))
        (flag (if show-full-answer "-a" "")))
    (beginning-of-line)
    (kill-line)
    (insert (shell-command-to-string (format "howdoi %s %s" query flag)))))

8.5. Sleek Semantic Selection

Super sleek way to select regions: Anywhere press ⌘-r to select the current word, press it again to select sentence, then again for the current paragraph, then more to get the current section.

(use-package expand-region
  :bind (("s-r" . #'er/expand-region)))

You can watch an introductory ~3 minute video to expand-region at Emacs Rocks!.

That is, repeated ⌘+r expands the selection to the next logical segment of text: In writing this means “Word, sentence, paragraph”, and in programming this means “identifier, then incrementally larger scopes”.

8.6. Managing Processes/Servers from within Emacs —Work-specific functions

Let's make a few interactive Emacs Lisp functions to reduce the amount of time I need to be in a terminal. I'll use the prefix “w-” for work stuff. Example tasks:

  • Start/stop my servers
  • Interactively select an app to be opened in the browser
  • Do database migrations/rollbacks

⟨ Obfuscated with lorem ipsum text. ⟩ services-dashboard.png


Not using this, a bit too verbose to setup for each service but, more accurately, does not Just Workᵀᴹ for my needs.

;; “M-x prodigy”, then press “s” to start a service; “S” to stop it; “$” to see it; “r”estart
(use-package prodigy :disabled t)
  ;; C-h v prodigy-services ⇒ See possible properties.

8.6.1. my/defaliases

(defalias 'defaliases 'my/defaliases)
(defmacro my/defaliases (src &rest tgts)
  "Provide names TGTS as synonymous aliases for SRC, for discovarability.

Often a function SRC can be construed from different perspectives, names, purposes TGTS.
Another example is when I define things with the ‘my/’ prefix, but also want to use them without.

Example use: (my/defaliases view-hello-file greet-others learn-about-the-world)

In particular:  (my/defaliases OLD NEW) ≈ (defalias 'NEW 'OLD)."
  `(--map (eval (quote (defalias `,it (quote ,src)))) (quote ,tgts)))

8.6.2. Making unkillable buffers & shells

Making unkillable buffers & shells
(defun my/declare-unkillable-buffer (name)
  (add-hook 'kill-buffer-query-functions
            `(lambda () (or (not (equal (buffer-name) ,name))
                       (progn (message "Not allowed to kill %s, burying instead; otherwise use “M-x force-kill”" (buffer-name))
                              (bury-buffer))))))

(my/defaliases my/force-kill force-kill w-force-kill)
(cl-defun my/force-kill (&optional buffer-name)
  (interactive)
  (-let [kill-buffer-query-functions nil]
    (if buffer-name
        (kill-buffer buffer-name)
      (kill-current-buffer))
    (ignore-errors (delete-window))))

(cl-defun my/run-unkillable-shell (command &optional (buffer-name command))
  "Example use: (my/run-unkillable-shell \"cd ~/my-noejds-project; npm run dev\" \"my-nodejs-project\")"
  (-let [it (get-buffer buffer-name)]
    (if it
        (switch-to-buffer-other-window it)
      (async-shell-command command buffer-name)
      (my/declare-unkillable-buffer buffer-name))))

8.6.4. w-start/stop-services

(defvar my/services nil "List of all services defined; used with `w-start-services' and `w-stop-services'.")

(defun w-start-services ()
  (interactive)
  (cl-loop for 𝑺 in my/services
           do (funcall (intern (format "w-start-%s" 𝑺)))))

(defun w-stop-services ()
  (interactive)
  (cl-loop for 𝑺 in my/services
           do (funcall (intern (format "w-stop-%s" 𝑺)))))

8.6.5. w-status-of-services

;; It takes about ~3 seconds to build the Status of Services page, so let's jump to it if it's already built, and the user/me can request a refresh, if need be.
(global-set-key (kbd "M-S-SPC")
  (lambda () (interactive)
    (-let [buf (get-buffer "Status of Services")] (if buf (switch-to-buffer buf) (w-status-of-services)))))
;;
;; Since M-S-SPC brings up the transient menu, and most commands close the status buffer or are transient, we get the perception that the transient menu is "sticky"; i.e., stuck to the buffer, even though this is not true. I do not yet know how to make a transient menu stuck to a buffer.
;;
(defun w-status-of-services ()
  "Show me status of all servers, including their current git branch, and most recent emitted output."
  (interactive)
  (defvar w-status-of-services/branch-name-width 12
    "What is the length of the longest branch name? Let's use that to ensure there's enough whitespace.")
  (-->
      (-let [ shells (--filter (s-starts-with? "Shell" (process-name it)) (process-list)) ]
        (cl-loop for 𝑺 in (mapcar #'pp-to-string my/services)
              for associated-shell = (--find (s-contains? (format "%s" 𝑺) (cl-third (process-command it))) shells)
              for status = (or (ignore-errors (process-status associated-shell)) '💥)
              for branch = (-let [default-directory (format "~/%s" 𝑺)]
                             (magit-get-current-branch))
              for _ = (setq w-status-of-services/branch-name-width (max (length branch) w-status-of-services/branch-name-width))
              for 𝑺-buffer = (--find (s-starts-with? (format "%s" 𝑺) it) (mapcar 'buffer-name (buffer-list)))
              for saying = (let (most-recent-shell-output (here (current-buffer)))
                             (if (not 𝑺-buffer)
                                 " ─Server not started─ "
                               (switch-to-buffer 𝑺-buffer)
                               (end-of-buffer)
                               (beginning-of-line)
                               (setq most-recent-shell-output (or (thing-at-point 'line t) ""))
                               (switch-to-buffer here)
                               ;; FIXME:here
                               (--> (s-truncate 135 (s-trim most-recent-shell-output))
                                  (if (s-contains? "|" it)
                                      (cl-second (s-split "|" it))
                                    it)
                                  (s-trim it)
                                  (if (<= (length it) 3) (s-repeat 70 " ") it))))
              for _ = (if (or (s-contains? "Error" saying) (not 𝑺-buffer)) (setq status  '💥))
              for keymap = (copy-keymap org-mouse-map)
              do (cl-loop for (key action)
                          on `(;; Checkout branch/PR
                               c (w-pr-checkout (format "~/%s" ,𝑺))
                               ;; Restart service, remaining on current branch [not switching to “main”!]
                                 r (-let [current-prefix-arg t]
                                     (funcall (intern (format "w-stop-%s" ,𝑺)))
                                     (funcall (intern (format "w-start-%s" ,𝑺))))
                               ;; See the repo in the web
                                 w (--> (format "%s" ,𝑺)
                                      (if (s-contains? "/" it) (f-parent it) it)
                                      (format "https://github.com/%s/%s" work/gh-user it)
                                      (browse-url it))
                               ;; Visit service shell
                               <return>
                                (when ,𝑺-buffer
                                  (delete-other-windows)
                                  (split-window-below)
                                  (switch-to-buffer ,𝑺-buffer)
                                  (end-of-buffer)
                                  (other-window 1))
                               ;; See service magit buffer
                              <tab> (progn (magit-status (format "~/%s" ,𝑺)) (delete-other-windows)))
                          by #'cddr
                          do (define-key keymap (kbd (format "%s" key))
                                         `(lambda () (interactive) ,action)))
              collect
              ;; “%𝑾s” ⇒ Print a string with at least width 𝑾: If length(str) ≤ 𝑾, then pad with spaces on the left side.
              ;; Use “%-𝑾s” to instead pad with spaces to the right.
              (list keymap (format (format "%%s %%-20s %%-%ss %%s" (+ 5 w-status-of-services/branch-name-width)) status 𝑺 branch saying))))

      ;; Setup buffer
      (-let [buf "Status of Services"]
        (ignore-errors (kill-buffer buf))
        (switch-to-buffer buf)
        (delete-other-windows)
        it)
      ;; Insert out buttons
      (--each it
        ;; https://www.gnu.org/software/emacs/manual/html_node/elisp/Overlay-Properties.html
        (-let [help (s-join "\n"
                            '("Keybindings:"
                              "[C-u] c   ∷  Checkout PR [or branch]   \t\t\t b  ∷  Browse an app"
                              "tab       ∷  See service magit buffer  \t\t\t i  ∷  Inject users"
                              "return    ∷  Visit service shell       \t\t\t s  ∷  SQL buffer"
                              "r         ∷  Restart service           \t\t\t w  ∷  See the repo in the web"
                              "g         ∷  Refresh this view         \t\t\t q  ∷  Quit, and kill, this buffer"))]
          (insert-text-button (s-replace "\"" "″" (s-replace "run" "✅" (nth 1 it)))
                         'face nil
                         ;; 'mouse-face '(:box t) ;; I use the cursor more than the mouse, so don't want two distinct views.
                         'keymap (nth 0 it)
                         ;; NOTE: The functions are called only when the minor mode cursor-sensor-mode is turned on.
                         ;; When cursor enters the button, we temporarily make it a box and show shortcuts in message area.
                         'cursor-sensor-functions `((lambda (_ old-pos entered?)
                                                      (message ,help)
                                                      (setq entered? (equal entered? 'entered))
                                                      (-let [self (button-at (if entered? (point) old-pos))]
                                                        (read-only-mode 0) ;; Temporarily disable help-mode's read-only-mode setup.
                                                        (if entered?
                                                            (button-put self 'face '(:box "yellow" :weight bold))
                                                          (button-put self 'face nil)))))
                         'help-echo help)
                         (insert "\n")))
      ;; Do some highlighting, as a cautionary measure.
      (highlight-regexp ".*crashed.*" 'hi-red-b)
      ;; Forbid editing
      (help-mode) ;; This wont do the button face changes I like when cursor moves; so I disable read-only-mode temporarily when making the changes.
      (cursor-sensor-mode)
      (visual-line-mode -1)
      (toggle-truncate-lines)
      ;; Add some specific work related bindings
      (local-set-key "b" #'w-browse-app)
      (local-set-key "i" #'w-inject-users)
      (local-set-key "s" (lambda () (interactive) (w-sql) (delete-other-windows)))
      ;; Add general view keys
      (local-set-key "g" (lambda () "Refresh this view" (interactive) (ignore-errors (kill-buffer-and-window)) (w-status-of-services)))
      (local-set-key "q" (lambda ()  "Quit buffer" (interactive) (ignore-errors (kill-buffer-and-window))))
      ;; Go to the first entry, so my “homemade echo menu” appears.
      (beginning-of-buffer)))

8.6.6. my/defservice

;; Even though I'm doing frequent prunes, it helps to give docker some leeway.
;; NOTE: Docker Icon → Preferences → Resources →  4 CPUs; 8gb Memory; 2gb Swap; 120 DiskImageSize.
(cl-defmacro my/defservice
    (repo &key (main-setup "git checkout main; git pull; git status; hr; npm ci; hr; time docker system prune -af")
          (cmd "npm run docker:dev")
          (example ""))
  "Example use:

   (my/defservice 𝒟 :cmd 𝒞 :example ℰ)

    (w-start-𝒟)    ≈ Unkillable shell: cd 𝒟; 𝒞
    (w-is-up-𝒟?)   ≈ Open browser at ℰ
    (w-stop-𝒟)     ≈ Kill all emacs-buffers & docker-images containing 𝒟 in their name

  (w-[start|stop]-services)  ⇒ Starts/stops all defined services."
  (add-to-list 'my/services repo)
  `(list
     (cl-defun ,(intern (format "w-start-%s" repo)) ()
       "Start server off of ‘main’, with prefix just start server off of current branch."
       (interactive)
       (let ((command (format "cd ~/%s; pwd; hr; %s; hr; %s"
                              (quote ,repo)
                              (if current-prefix-arg "" ,main-setup)
                              ,cmd))
             (buf-name (format "%s/%s" (quote ,repo)
                               (if current-prefix-arg "main"
                                 (-let [default-directory ,(format "~/%s" repo)]
                                   (magit-get-current-branch))))))
         (my/run-unkillable-shell
          (format "echo %s; hr; %s" (pp-to-string command) command) ;; Show command being run in output buffer, then run that command
          buf-name)
         (with-current-buffer buf-name (read-only-mode))))

     (cl-defun ,(intern (format "w-stop-%s" repo)) ()
       "Force-kill all unkillable buffers that mention REPO in their name. Also stop any docker services mentioning REPO in their name."
       (interactive)
       (my/docker-stop ,(pp-to-string repo))
       (thread-last (buffer-list)
         (mapcar 'buffer-name)
         (--filter (s-contains-p ,(pp-to-string repo) it))
         (mapcar #'my/force-kill)))

     (if ,example
         (cl-defun ,(intern (format "w-is-up-%s?" repo)) ()
           (interactive)
           (browse-url ,example)
           (message "If the URL is busted, then the repo is not up correctly or the server has an error!")))))

In my private work.el I have declarations of the form (my/defservice ⟨directory⟩ :cmd "npm run dev" :example "Example URL to try out for this server").

(when my/work-machine?
  (load-file "~/Desktop/work.el"))

8.7. Project management & navigation

Version controlled repositories are considered “projects” —no setup needed—, but you can declare your own too.

Videos:

This is so sweet at work (and possibly at home!): From anywhere,

  • C-x p p ⟨select your project⟩ RET ⟨start typing to see any file anywhere in the project⟩
  • C-x p b ⇒ Switch to buffers only in the current “stream of thought” (project).
  • C-x p f ⇒ Find files only in the current “stream of thought” (project).
  • C-x p s g ⇒ Search the project using grep; TAB in the resulting buffer to open files.
  • C-x p S ⇒ Save all project buffers
  • C-x p k ⇒ Kill all buffers relating to the parent project
  • C-x p & ⇒ Runs an async-shell-command in the project's root directory
  • C-x p x s ⇒ Start or visit a shell for the project
  • C-x p r ⇒ Runs interactive query-replace on all files in the projects
  • C-x p e ⇒ Show a list of recently visited files, in the current project
  • C-x p V ⇒ Open a project that has been modified, but not pushed with version control.
;; More info & key bindings: https://docs.projectile.mx/projectile/usage.html
(use-package projectile
  :config
  (projectile-mode +1)
  (define-key projectile-mode-map (kbd "C-x p") 'projectile-command-map)

  ;; Replace usual find-file with a project-wide version :-)
  (global-set-key (kbd "C-x f") #'projectile-find-file)

  ;; Makes indexing large projects much faster, after first time.
  ;; Since its caching, some files may be out of sync; you can delete the cache
  ;; with: C-u C-x f
  (setq projectile-enable-caching t)

  (define-key projectile-mode-map (kbd "C-x p s")
    ;; I prefer helm-do-grep-ag since it shows me a live search
    (lambda () (interactive)
       (let ((default-directory (car (projectile-get-project-directories (projectile-acquire-root)))))
         ;; (shell-command-to-string "echo $PWD")
         (helm-do-grep-ag nil))))) ;; “p”roject “s”earch

Let's get the file path of the current file. We bind it and make it a top-level invokable function.

(define-key projectile-mode-map (kbd "C-x p c")
  (defun my/copy-current-file-path ()
    "Add current file path to kill ring."
    (interactive)
    (message (kill-new buffer-file-name))))

8.8. TODO Projectile

;; https://cestlaz.github.io/posts/using-emacs-33-projectile-jump/
;; https://github.com/bbatsov/projectile
(use-package projectile
:config (projectile-global-mode))
(define-key projectile-mode-map (kbd "s-p") 'projectile-command-map)

8.9. TODO Are there any errors in my code?

Flycheck gives us syntax checking and linting tools to automatically check the contents of buffers while you type, and reports warnings and errors directly in the buffer, or in an optional error list. Sadly, the default reporting of errors looks like FlyC 3 in the modeline, but with flycheck-status-emoji we can see the status using cute & compact emoji such as 😱3.

(use-package flycheck-status-emoji
  :config
  (load-library "flycheck-status-emoji")
  (diminish-undo 'flycheck-mode)
  (flycheck-status-emoji-mode))

Let's also have a nifty modal menu to quickly navigate between errors.

(use-package helm-flycheck)
 (bind-key*
 "C-c !"
 (defhydra my/flycheck-hydra (:color blue :hint nil)
   "Move around flycheck errors and get info about them"
   ("n" flycheck-next-error "next" :column "Navigation")
   ("p" flycheck-previous-error "previous")
   ("f" flycheck-first-error "first")
   ("l" flycheck-list-errors "list")
   ("h" helm-flycheck "helm") ;; Jump to an error / see-errors from a nice interactive menu

   ("e" flycheck-explain-error-at-point "explain"  :column "Current errror")
   ("c" flycheck-copy-errors-as-kill "copy")

   ("d" flycheck-describe-checker "Describe checker"  :column "More")
   ("s" flycheck-select-checker "Select checker")
   ("S" flycheck-verify-setup "Suggest setup")
   ("m" flycheck-manual "manual")))

8.9.1. On the fly syntax checking

Flycheck is a on-the-fly syntax checker that relies on external programs to check buffers; which must be installed separately.

  • E.g., ghc is required for Haskell; whereas Emacs Lisp is checked by Emacs' own byte compiler, emacs-lisp.
  • Sometimes more than one checking tool applies, use C-c ! s to select a different checker.
  • C-c ! n,p,l takes you to the ‘n’ext or ‘p’revious error, or ‘l’ist all errors in another buffer.

    C-c ! c to explicitly recheck the buffer.

(use-package flycheck
  :diminish
  :init (global-flycheck-mode)
  :config ;; There may be multiple tools; I have GHC not Stack, so let's avoid that.
  (setq-default flycheck-disabled-checkers '(haskell-stack-ghc emacs-lisp-checkdoc))
  :custom (flycheck-display-errors-delay .3))

In an org-src block, we press C-c ' to get into the language's mode where flycheck will provide warnings.

module Main where

main :: IO ()
main = putStrLn $ "nice" ++ f 0

f :: Int -> String
f x = x -- show x
-- type error

In-general, flycheck is intended for self-contained raw code —not for source blocks in Org-mode. Whence, the above example is a complete Haskell program, with a named module and main method.

I think the built-in flymake syntax checker is better for Emacs Lisp, so let's use that for ELisp.

(use-package flymake
  :hook ((emacs-lisp-mode . (lambda () (flycheck-mode -1)))
         (emacs-lisp-mode . flymake-mode))
  :bind (:map flymake-mode-map
              ("C-c ! n" . flymake-goto-next-error)
              ("C-c ! p" . flymake-goto-prev-error)))

Try it out:

(setq 1 2) ;; Error: ‘1’ is not a variable.

8.10. Jumping to definitions & references

Out-of-the-box Emacs has ‘xref’ utilities M-. and C-u M-. to Find Identifier References; however, tags to source definitions need to be generated using the etags program. Nonetheless, the xref utilites are impressive and some just work: For example, M-? cleverly finds all references for an identifier in ‘near by’ files; whereas C-u M-. RET my/.*, for example, uses the given regular expression to list all identifiers with prefix my/, thereby listing my personally defined names ^_^

C-M-. 𝓇𝓮ℊ𝓮𝓍 Find all identifiers whose name matches the given pattern

Let's get dumb-jump, where the ‘dumb’ is possibly due to the fact that it works by brute-force regular-expression lookup of pre-defined ‘definitional template’ rules. It “just works” ^_^

(use-package dumb-jump
  :bind (("M-g q"     . dumb-jump-quick-look) ;; Show me in a tooltip.
         ("M-g ."     . dumb-jump-go-other-window)
         ("M-g b"     . dumb-jump-back)
         ("M-g p"     . dumb-jump-go-prompt)
         ("M-g a"     . xref-find-apropos)) ;; aka C-M-.
  :config
  ;; If source file is visible, just shift focus to it.
  (setq dumb-jump-use-visible-window t))

In Lisp, for binding macros, it lists all possible mentions of the bound variable —the first is likely what is desired. Alternatively, one could just add the necessary rule to the variable dumb-jump-find-rules. Otherwise, it works fine even for locally bound definitions. It works depending on the extension of a file.

8.11. Documentation Pop-Ups

Let documentation pop-up when we pause on a completion. This is very useful when editing in a particular coding language, say via C-c ' for org-src blocks. Or when working in a language-specific buffer.

(use-package company-quickhelp
 :config
   (setq company-quickhelp-delay 0.1)
   (company-quickhelp-mode)
   ;; Especially when learning a new language, looking up its definition/docstring can be helpful.
   ;; Note: I use “M-!” everywhere else to mean “define word at point”.
   (bind-key "M-!" #'company-show-doc-buffer 'prog-mode-map))

This M-! is from plain company, but I wanted to define it here beside the quickhelp setup since they're related utilities.

8.12. Turbolog: What's the value of this expression?   JavaScript

With turbo-log, I can select an expression then press C-x l l to have that expression be part of a console log message, in the next line, that also mentions the line number and buffer's name. I can toggle all these inserted messages to be comments with C-x l c and C-x l u, and when I'm done debugging I can quickly get rid of all of them with C-x l d.

( Before TurboLog, I used a snippet bound to l l that inserted

console.log("%c ******* LOOK HERE *******", "color: green; font-weight: bold;");
console.log({ List the variables here whose values you want to log });

)

8.12.1. TODO ll-debug

ll-debug.el provides commands to support a low level debug style. It features quick insertion of various debug output statements and improved functions for commenting and uncommenting chunks of code.

I don't use debuggers very much. I know they can be a big help in some situations and I tried some of them, but I find it almost always more direct/convenient/enlightening to put a quick 'printf' into a critical area to see what is happening than to fire up a big clumsy extra program where it takes me ages just to step through to the interesting point. In order to avoid repeated typing of 'printf("I AM HERE\n");' and similar stuff, I created `ll-debug-insert'. It inserts a statement into your sourcecode that will display a debug message. It generates unique messages on each invocation (the message consists of a big fat DEBUG together with a counter and the current filename).

—ll-debug.el

;; C-u C-v C-d ⇒ Log a message, printing values of expressions.
;; E.g., in JS this prints, console.log("DEBUG-5-del.js","  1 + 3:",1 + 3);
;; Note “5” is the fifth debug message, and “del.js” is the name of the buffer.
;; Works with Rust, Java, Lisps, JS, TS, Clojure, C/C++, Ruby, Matlab/Octave, Shell, Perl.
(use-package ll-debug
  :config
  (bind-key "C-x l" (lambda () (interactive) (ll-debug-insert 1)) #'prog-mode-map))

;; See variable `ll-debug-statement-alist' if you want to know which
;; modes are currently supported by ll-debug. You can add new modes
;; with `ll-debug-register-mode'.
;;
;; If you want to get rid of the debug messages, use
;; `ll-debug-revert'. It finds and removes the lines with the debug
;; output statements, asking for confirmation before it removes
;; anything.

8.13. Which function are we writing?

In the modeline, show the name of the function we're currently writing.

(add-hook 'prog-mode-hook #'which-function-mode)
(add-hook 'org-mode-hook  #'which-function-mode)

In Org-mode, this places the current heading in the modeline.

In Lisp mode, ensure we always have matching parens.

(add-hook 'emacs-lisp-mode-hook #'check-parens)

8.14. Highlight defined Lisp symbols

Usually Emacs only highlights macro names, the following incantation makes it highlight all defined names —as long as we're in Lisp mode, whence in org-src blocks we use C-c '.

;; Emacs Lisp specific
(use-package highlight-defined
  :hook (emacs-lisp-mode . highlight-defined-mode))

Super helpful in making my Emacs configuration: If a name is not highlighted, then I've misspelled it or it doesn't exist! :smile:

8.15. Being Generous with Whitespace

The following minor mode automatically adds spacing around operators.

(use-package electric-operator
  :diminish
  :hook (c-mode . electric-operator-mode))

I dislike it when users write x=y+1 —whitespace is free and helpful. ⟨ Also, languages with arbitrary identifiers, like Lisp and Agda, would accept x=y+1 as an identifier, not an expression! ⟩

8.16. Coding with a Fruit Salad: Semantic Highlighting

What should be highlighted when we write code? Static keywords with fixed uses, or dynamic user-defined names?

  • Syntax highlighting ⇨ Specific words are highlighted in strong colours so that the structure can be easily gleaned.

    • Generally this only includes a language's keywords, such as if, loop, begin, end, cond.
    • User defined names generally share one colour; usually black.
    • Hence, an if block may be seen as one coloured keyword followed by a blob of black text.

    Obvious keywords are highlighted while the rest remains in black!

  • Semantic highlighting ⇨ Identifiers obtain unique colouring.
    • This makes it much easier to visually spot dependencies with a quick glance.
      • One can see how data flows through a function.
    • In dynamic languages, this is a visual form of typing: Different colours are for different names.
      • Especially helpful for (library) names that are almost the same.
      • This can be accomplished anywhere in Emacs by pressing M-s h . on a selected phrase.

For Emacs, Color Identifiers Mode gives unique highlighting to identifiers.

  • It comes with support for a bunch of languages, and one can add support for others.
  • It picks colours adaptively to fit the theme; one uses M-x color-identifiers:regenerate-colors after a theme change.
(use-package color-identifiers-mode
  :config (global-color-identifiers-mode))

;; Sometimes just invoke: M-x color-identifiers:refresh

When writing a new name, after about ~5 seconds it obtains a colour which is then propagated immediately to any new occurrences. This timeout before recolouring is to avoid any lag from multithreading and can be changed by altering the following line (#64) in the source file, changing the 5 to a smaller number.

(run-with-idle-timer 5 t 'color-identifiers:refresh)

Here are further reads:

8.17. Jump between windows using Cmd+Arrow & between recent buffers with Meta-Tab   relocate

We can use C-x o to switch to the ‘o’ther window, and C-u 𝓃 C-x o to switch to the 𝓃-th next clockwise window, but using s-↑,↓,←,→ may be faster.

(use-package windmove
  :config ;; use command key on Mac
          (windmove-default-keybindings 'super)
          ;; wrap around at edges
          (setq windmove-wrap-around t))

The docs, for the following, have usage examples.

(use-package buffer-flip
  :bind
   (:map buffer-flip-map
    ("M-<tab>"   . buffer-flip-forward)
    ("M-S-<tab>" . buffer-flip-backward)
    ("C-g"       . buffer-flip-abort))
  :config
    (setq buffer-flip-skip-patterns
        '("^\\*helm\\b")))
;; key to begin cycling buffers.
(global-set-key (kbd "M-<tab>") 'buffer-flip)

See buffer-move if you're interested in moving the buffers, and their windows, into new configurations.

8.18. Shell / Terminal   relocate

8.18.1. hr: A horizontal for your terminal

When working in the terminal, at least in my day job, it can be helpful to visually segregate large chunks of output. With the hr command, I can run hr 'output5`, for example, to have the same the string output5 repeated horizontal across one line of my terminal. We can also just run hr which is the same as hr '#'. Finally, you can also run hr '-#-' '-' '-#-' to have 3 horizontal lines and more generally hr p₁ p₂ … pₙ will produce n-many horizontal lines with the $ith$-line having pattern pᵢ.

(system-packages-ensure "hr") ;; ≈ brew install hr

8.19. Github Browser Extensions (for Chrome)

octotree
Lets you explore the files and folders of a repository with a tree and search bar. Can also quickly browse the files changed in the current PR/branch.
isometric-contributions
Renders your contribution graph on your profile in a nicer way: 3D instead of the default 2D.
OctoLinker
Turns language-specific statements like include, require or import into links. Gives you the ability to go to files by clicking at their paths, not only internal files it also works for imported packages!
github-hovercard
Show a neat hover card when you hover over an element! No need to open everything in a new page, just hover over it!
github-file-icons
Gives different filetypes different icons to GitHub 🚀
gifs-for-github
Makes it easy to search GIPHY and add a GIF into any GitHub comment box.
github-mention-highlighter
Any GitHub issue you've been mentioned in and your mentions should now be more easily visible.
:flame: refined-github

Browser extension that simplifies the GitHub interface and adds useful features

  • Makes whitespace characters visible
  • Adds one-click merge conflict fixers
  • Adds reaction avatars showing who reacted to a comment
  • Linkifies issue/PR references and URLs in code and conversation titles
  • Adds a button to revert all the changes to a file in a PR
  • Lets you hide every event except comments or unresolved comments in issues and PRs
  • 🔥 Adds a build/CI status icon next to the repo’s name.
  • Adds a button to download entire folders, via download-directory.github.io.
  • Adds a link to preview HTML files.
  • Adds a button to copy a file’s content.
  • Shows PRs that touch the current file.
  • Enables tab and shift tab for indentation in comment fields.
  • Adds a button to insert collapsible content (via <details>)

8.20. Browse remote files

Sometimes you want to see the current file in Github; e.g., you select a region and press M-x browse-at-remote-kill to get the URL for that region in Github then you can send that URL to your peers when referencing something.

;; Usage: [Optionally select a region then] M-x browse-at-remote[-kill]
(use-package browse-at-remote)

8.21. A nice Emacs interface for a portion of the “gh” CLI

;; A nice Emacs interface for the a portion of the “gh” CLI.
(my/defaliases my/gh-checkout gh-checkout w-pr-checkout w-branch-checkout)
(cl-defun my/gh-checkout (&optional repo)
  "With prefix, select a branch name; otherwise a Pull Request name.

If no REPO is provided, let the user select one from a menu.
Example use:      (w-pr-checkout \"~/my-repo\")
                  (w-pr-checkout)"
  (interactive)
  (let* ((repo (or repo (completing-read "Repo: " (projectile-relevant-known-projects))))
         (default-directory repo) ;; temporarily override this global variable, used with magit
         (current-branch (magit-get-current-branch))
         (all-branches (magit-list-local-branch-names))
         (status (format "cd %s; gh pr status" repo)))
    (if current-prefix-arg
        (-let [branch (completing-read (format "New branch (Currently “%s”): " current-branch) all-branches)]
          (shell-command-to-string (format "cd %s; git checkout %s" repo branch)))
      (let* ((PR-list (s-split "\n" (shell-command-to-string (format "cd %s; gh pr list" repo))))
             (pr♯ (car (s-split "\t" (completing-read "PR: " PR-list))))
             (_ (shell-command-to-string (format "cd %s; gh pr checkout %s" repo pr♯)))
             (new-branch (magit-get-current-branch)))
        ;; Show nice status
        (async-shell-command status)
        (magit-status repo)))))

8.22. copy-as-format: Emacs function to copy buffer locations as GitHub/Slack/JIRA etc… formatted code.

;; Usage: [C-u] M-x copy-as-format ⇒ Copies selected region, or current line.
;; Also use: copy-as-format-𝒮, to format to a particular 𝒮tyle.
;; Without suffix 𝒮, format defaults to `copy-as-format-default`.
;; With a prefix argument prompt for the format style 𝒮.
;; Easy to add more formats.
(use-package copy-as-format)

8.24. SQL —via LSP

When doing ‘serious’ database work, I love using DBeaver. But when I only want to quickly run a query, to check something, without the fricition of switching applications, then doing so in Emacs is a no-brainer: I use w-sql to produce a query buffer, if one doesn't exist, then C-c C-c to send queries. (This also serves as a nice reference mechanism for useful queries.)

Let's use LSP for SQL, to get

  • Neato tooltips on table definitions and column constraints,
    • Write select * from my-table t then delete the * and enter t. to see all valid columns; or just delete the * and press . to see them.
  • Completions for tables & column names,
  • C-c C-c to execute query at point [My personal setup below],
    • Run all queries with lsp-execute-code-action
  • When sharing with others, maybe execute lsp-format-buffer
  • Note: The tokenizer is known to a bit buggy.
    • E.g., select 1 + 2 as "Numerical, yeah!" will not be run due to the ‘!’.
;; Installation: go install github.com/lighttiger2505/sqls
(setq lsp-sqls-server "/Users/musa/go/bin/sqls")
(setq lsp-sqls-timeout 1)
(setq lsp-sqls-workspace-config-path nil)
;; https://emacs-lsp.github.io/lsp-mode/page/lsp-sqls/
(setq lsp-sqls-connections
      ;; (--map `((driver . "postgresql") (dataSourceName . ,it)) work/sqls-connections))
      `(((driver . "postgresql") (dataSourceName . ,(car work/sqls-connections)))))
;; TODO: Remove this 'car'!
(add-hook 'sql-mode-hook 'lsp)

(use-package org-modern)
(defun my/execute-query-at-point ()
  "Execute query at point and make resulting table an Org table and modernise it"
  (interactive)
  (lsp-sql-execute-paragraph)
  (other-window 1) (org-modern-global-mode) (org-mode) (read-only-mode -1)
  (while (re-search-forward "^\+" nil t) (replace-match "|"  nil t))
  (toggle-truncate-lines)
  (beginning-of-buffer) (execute-kbd-macro (read-kbd-macro "<tab>"))
  (read-only-mode) (other-window -1)
  (local-set-key "q" (lambda ()  "Quit buffer" (interactive) (ignore-errors (kill-buffer-and-window)))))


(bind-key "C-c C-c" #'my/execute-query-at-point 'sql-mode-map)
(bind-key "C-c C-<return>" #'my/execute-query-at-point 'sql-mode-map)

(defun w-sql ()
  "Quickly run a SQL query, then dispose of the buffer when done.

Uses the first connection available, to change connections
invoke M-x `lsp-sql-switch-connection'."
  (interactive)
  ;; LSP only works on files; not buffers; so I use this file.
  (find-file "~/.emacs.d/scratch.sql")
  (insert work/sql-queries) ;; docs and examples
  (sql-mode)
  (hs-minor-mode -1) ;; I don't want the above comments to be collapsed away.
  (beginning-of-buffer))
Previous setup ~ ejc-sql [Disabled]
  • ejc-sql crashes with updates.
  • ✔ I like that ejc-sql gives me M-. to jump to definitions of tables/functions/etc.
    • “Go to definition”: [M-.] M-x ejc-describe-entity ⇒ Show source code for a stored view, function, table, etc.
    • Get entity definition: show creation SQL of view, package, function, procedure or type.
    • C-c e t ⇒ See list of tables
  • ✔ ejc works with org-mode out of the box; https://github.com/kostafey/ejc-sql#use-with-org-mode

⇒ BEST OF BOTH WORLDS? ⇐ (setq ejc-sql-separator "– /") ;; Otherwise LSP sql server sees ‘/’ as a syntax error.

;; (system-packages-ensure "leiningen")
;; NOTE: You must commented out the (require 'ejc-direx) from ejc-sql.el
;; Reason: https://github.com/kostafey/ejc-sql/issues/163
(use-package ejc-sql
  :config
  (require 'ejc-company)
  (push 'ejc-company-backend company-backends)
  (setq ejc-completion-system 'standard) ;; Use my setup; i.e., Helm.
  ;; [C-u] C-c C-c ⇒ Evaluate current query [With JSON PP].
  (bind-key "C-c C-c"
             (lambda () (interactive)
               (setq ejc-sql-complete-query-hook
                     (if current-prefix-arg
                         '(w-ejc-result-pp-json)  ;; Defined below
                       '((lambda () ;; Give each line of text just one screen line.
                           (switch-to-buffer-other-window "*ejc-sql-output*")
                           (visual-line-mode -1)
                           (toggle-truncate-lines)
                           (other-window -1)))))
               (ejc-eval-user-sql-at-point))
             'sql-mode-map))

(defun w-sql ()
  "Quickly run a SQL query, then dispose of the buffer when done.

By default uses a connection named “xxxx”, to see a list of
other connections call with a prefix argument."
  (interactive)

  ;; Get DB credentials. One does “M-x ejc-connect-interactive” once, then
  ;;  “M-x ejc-insert-connection-data” and paste that into your init; then
  ;; “M-x ejc-connect” provides a completion of possible DBs to connect to.
  (load-file "~/Desktop/work.el")
  ;; For the following: Alternatively, we could make a new binding such as “C-c C-j”
  ;; which temporarily adds to this hook, then calls
  (add-to-list 'ejc-sql-complete-query-hook 'w-ejc-result-pp-json) ;; Defined below
  (require 'ejc-sql)
  (-let [connection-name (if current-prefix-arg (ejc-read-connection-name) "xxxx")]
    (switch-to-buffer-other-window (format "*SQL/%s*" connection-name))
    (thread-last
        `("\n\n/\n-- DOCS & EXAMPLES\n--"
          "-- SQL queries should be seperated by “/”"
          "-- [C-u] C-c C-c ⇒ Evaluate current query [With JSON PP]"
          "-- In result window, TAB/RET to navigate the columns/rows."
          "-- C-c e t ⇒ List all tables"
          "-- C-h t   ⇒ ‘H’elp for a ‘t’able"
          "\nselect 1 + 2 as \"Numerical, yeah!\""
          "\n/\n"
          "-- More examples of useful SQL queries I might need, but don't want to remember"
          ;; I've moved them out to my private work.el file.
          ,@work/sql-queries)
      (s-join "\n")
      insert)
    (sql-mode)
    (hs-minor-mode -1) ;; I don't want the above comments to be collapsed away.
    (ejc-connect connection-name)
    (beginning-of-buffer)
    (message "Connecting to DB... please wait a moment")))

(defun w-ejc-result-pp-json ()
  "Pretty print JSON ejc-result buffer."
  (interactive)
  (ignore-errors
    (switch-to-buffer-other-window "*ejc-sql-output*")
    (beginning-of-buffer)
    (re-search-forward "{")
    (backward-char 1)
    (delete-region (point-min) (point))
    (end-of-buffer)
    (re-search-backward "|")
    (kill-line)
    (json-mode)
    (json-pretty-print-buffer)
    (other-window -1)
    (message-box "hiya")))

;; Add minimal table autocomplete to SQL (require 'ejc-company) (push 'ejc-company-backend company-backends) (add-hook 'ejc-sql-minor-mode-hook (lambda () (company-mode t)))

8.25. Docker

;; Usage: M-x docker [RET ?]
(use-package docker
  :config
  (my/defaliases docker-containers w-show-docker-containers))

(defun w-stop&remove-docker-containers ()
  (interactive)
  (shell-command "docker stop $(docker ps -a -q)")
  (shell-command "docker rm $(docker ps -a -q)"))

(defun w-postgres-status ()
  (interactive)
  (display-message-or-buffer (s-replace "healthy" "🆙 healthy 🍏" (shell-command-to-string "docker ps -a | grep postgres"))))

(cl-defun w-kill-process-running-on-port (&optional (port (completing-read "Port: " '("3310" "80" "9000" "8000" "8080" "etc, whatever you want"))))
  "We use ‘lsof’ to list open files; as in:  lsof -i :3310 +c0
  The +c0 prints the full name of the command rather than truncating it.

  We then find the PID and kill the process."
  (interactive)
  (-let [process (shell-command-to-string (format "lsof -i :%s +c0" 3310))]
    (-let [pid (ignore-errors (cl-second (s-split " " (cl-second (s-split "\n" process)))))]
      (shell-command (format "kill %s" pid))
      (message process))))


8.25.1. my/docker-stop

(defun my/docker-stop (ctr)
  "Stop all containers that mention CTR in their name, image, command, or container id"
  (thread-last (shell-command-to-string "docker ps -a")
    (s-split "\n")
    (--filter (s-contains-p ctr it))
    (--map (car (s-split " " it))) ;; Get docker container ids
    (--map (shell-command (concat "docker stop " it)))))

8.26. my/open-in-terminal '⌘

(defalias 'my/open-in-terminal ')
(cl-defun  (&rest cmds)
  "Run terminal commands CMDS in a new MacOS Terminal instance, and bring it to focus.

Example: (⌘ \"echo hello\" \"echo world\")

Useful for those cases where I have to interact with non-trivial ‘interactive terminal menus’."
  (shell-command (format "osascript -e 'tell app \"Terminal\" to activate do script %s'"
                         (pp-to-string (s-join ";" cmds)))))

;; (⌘ "echo hello" "echo world")

8.27. Check if application APP is currently running, in use.

(cl-defun my/application-running? (app)
  "Check if application APP is currently running, in use."
  (not (equal "0" (s-trim (shell-command-to-string (format "ps aux | grep -v grep | grep -ci %s" app))))))

8.29. JSON

Since web apps tend to be RESTful, payloads are JSON. To use the JSON, we need to tediously find paths to particular fields; let's do so with automatically, without error..

When we open a JSON file, we are promoted to install an LSP server, which

  • Checks that the file is valid JSON.
  • Shows the full path to the cursor's location, at the top of the window.
(use-package json-mode)

Useful bindings are in the docstring of json-mode.

C-c C-f
Pretty print buffer
C-c C-p
Copy path to field at point

Note that there is also json-pretty-print-buffer; which can be used to uglify a JSON buffer if a prefix is provided.

Let's make a hydra for JSON,

(my/defhydra nil "JSON Browser" gamepad
  :Buffer
  ("p" #'json-mode-show-path "Copy path to field at point")
  ;; ("f" #'json-mode-beautify "Format Buffer")
  ;; ("m"  (lambda () (interactive) (json-pretty-print-buffer t)) "Minify/ugligy buffer")
  ("t"  (lambda  () (interactive)
          (if my/json-hydra/pretty-printed?
              (json-pretty-print-buffer t)
            (json-mode-beautify (point-min) (point-max)))
          (setq my/json-hydra/pretty-printed? (not my/json-hydra/pretty-printed?)))
   "Toggle format/uglify of buffer"
   :toggle (progn (defvar my/json-hydra/pretty-printed? nil)
                  my/json-hydra/pretty-printed?)))

;; TODO: (bind-key "C-c SPC" 'my/hydra/JSON\ Browser/body 'json-mode-map)
;; NOTE: “C-x SPC” is for rectangle editing.

Interesting, but not for me: json-par: Emacs minor mode for structural editing of JSON.

Also: JSON to X is a website to convert JSON to other formats, such as in Rust or as JSDoc type annotations.

8.30. w-screencapture

(bind-key "C-c s"
  (cl-defun w-screencapture ()
    "Interactively capture screen and save to clipboard; then paste in Slack, etc, with ⌘-c.

  After we run this command, we can swipe up on mousepad to select different desktops, then
  click & drag to select portition of screen to capture.

  Captured screen is NOT saved to disk, only copied to clipboard.

In MacOs,
+ Command + Shift + 5  ⇒  Select screen record
+ Command + Shift + 4  ⇒  Selection Screenshot
+ Command + Shift + 3  ⇒  Screenshot

See: https://osxdaily.com/2011/08/11/take-screen-shots-terminal-mac-os-x"
    (interactive)
    (async-shell-command "screencapture -i -c")))

(cl-defun w-delete-all-screenshots ()
    "Delete all “Screen Shot ⋯” files in ~/Desktop."
    (interactive)
    (thread-last (shell-command-to-string "cd ~/Desktop; ls")
      (s-split "\n")
      (--filter (s-starts-with-p "Screen Shot" it))
      (--map (f-delete (format "~/Desktop/%s" it)))))

8.31. Screencapturing the Current Emacs Frame

Sometimes an image can be tremendously convincing, or at least sufficiently inviting. The following incantation is written for MacOS and uses it's native screencapture utility, as well as magick.

(defun my/capture-emacs-frame (&optional prefix output)
"Insert a link to a screenshot of the current Emacs frame.

Unless the name of the OUTPUT file is provided, read it from the
user. If PREFIX is provided, let the user select a portion of the screen."
(interactive "p")
(defvar my/emacs-window-id
   (s-collapse-whitespace (shell-command-to-string "osascript -e 'tell app \"Emacs\" to id of window 1'"))
   "The window ID of the current Emacs frame.

    Takes a second to compute, whence a defvar.")

(let* ((screen  (if prefix "-i" (concat "-l" my/emacs-window-id)))
       (temp    (format "emacs_temp_%s.png" (random)))
       (default (format-time-string "emacs-%m-%d-%Y-%H:%M:%S.png")))
;; Get output file name
  (unless output
    (setq output (read-string (format "Emacs screenshot filename (%s): " default)))
    (when (s-blank-p output) (setq output default)))
;; Clear minibuffer before capturing screen or prompt user
(message (if prefix "Please select region for capture …" "♥‿♥"))
;; Capture current screen and resize
(thread-first
    (format "screencapture -T 2 %s %s" screen temp)
    (concat "; magick convert -resize 60% " temp " " output)
    (shell-command))
(f-delete temp)
;; Insert a link to the image and reload inline images.
(insert (concat "[[file:" output "]]")))
(org-display-inline-images nil t))

(bind-key* "C-c M-s" #'my/capture-emacs-frame)

Why this way? On MacOS, ImageMagick's import doesn't seem to work —not at all for me! Also, I dislike how large the resulting image is. As such, I'm using MacOS's screencapture utility, which in-turn requires me to somehow obtain frame IDs. Hence, the amount of work needed to make this happen on my system was most simple if I just wrote it out myself rather than tweaking an existing system.

  • C-c C-x C-v ⇒ Toggle inline images!

8.32. Comment-boxes up to the fill-column

GIF: comment-box.gif

(defun my/comment-box (b e)
  "Draw a box comment around the region but arrange for the region
to extend to at least the fill column. Place the point after the
comment box.

Source: http://irreal.org/blog/?p=374

To do fancy stuff like removing boxes, centering them, etc
see https://github.com/lewang/rebox2/blob/master/rebox2.el"
  (interactive "r")
  (let ((e (copy-marker e t)))
    (goto-char b)
    (end-of-line)
    (insert-char ?  (- fill-column (current-column)))
    (comment-box b e 1)
    (goto-char e)
    (set-marker e nil)))

8.33. Auto-format on Save

Auto-format source code in many languages with one command

Lets you auto-format source code in many languages using the same command for all languages, instead of learning a different Emacs package and formatting command for each language.

Just do M-x format-all-buffer and it will try its best to do the right thing. To auto-format code on save, use the minor mode format-all-mode.

  • You will need to install external programs to do the formatting. If format-all-buffer can't find the right program, it will try to tell you how to install it.
(use-package format-all
  ;; To enable format on save for most programming language buffers:
  :hook (prog-mode . format-all-mode)
  :config
  ;; Please use the default formatters; I don't care too much.
  (add-hook 'format-all-mode-hook 'format-all-ensure-formatter))

;; For JavaScript prettification: It automatically inserts semicolons, forces newlines, inserts parens, etc.
;; Lots of redundant stuff, but stuff to make it easy to work with others.
(shell-command "npm install --global prettier")
;; Specific package to do only JS prettification: https://github.com/prettier/prettier-emacs

8.34. Searching Hydra

(my/defhydra "s-f" "\t\tLocate Everything" search
   :Buffer
   ;; find all the occurrences of a string, pull out the lines containing the string to another buffer where [F2] I can edit and save,
   ("e" helm-swoop  "Editable")
    ;; Implicit Regex, colourful
   ("c" swiper "Classic")

   :Project
   ;; “:toggle ℰ”: ℰ is a Boolean expression that is evaluated to tell us whether the state is on-or-off
   ("t"  (lambda  () (interactive)) "Ignore specs/jsons"
    :toggle (let* ((with-hole "ag %s --line-numbers -S --color --nogroup %%s %%s %%s") ;; ≈ original value of ‘helm-grep-ag-command
                   (ignores "--ignore=\"*spec.js\" --ignore=\"*.json\" --ignore=\"*.json5\"")
                   (on (equal helm-grep-ag-command (format with-hole ignores))))
              (if on (progn (setq helm-grep-ag-command (format with-hole "")) nil) ;; ≈ turn off the toggle
                (setq  helm-grep-ag-command (format with-hole ignores)))))
   ("f" (lambda () (interactive) (helm-do-grep-ag t)) "File type")
   ("d" (lambda () (interactive) (-let [default-directory (read-directory-name "Where do you want to search? ")] (helm-do-grep-ag nil)))  "Directory")
   ("D" (lambda () (interactive) (-let [default-directory (read-directory-name "Where do you want to search? ")] (helm-do-grep-ag t)))  "Directory & type"))

8.35. Peer Review / Pull Request Template for Work

I run w-pr-template to get a new Org buffer with a review template. I make notes and check-off boxes with C-c C-c as usual, and when I'm done I press C-c C-s to copy the buffer to clipboard, in markdown format. Then I can paste it into Github.

(cl-defun w-pr-template ()
  "Hi"
  (interactive)
  (-let [buf "PR Template ~ Press “C-c C-s” when done"]
    (ignore-errors (kill-buffer buf))
    (switch-to-buffer buf)
    (insert "w-pr-template")
    (yankpad-expand)
    (org-mode)
    (beginning-of-buffer)
    (use-local-map (copy-keymap org-mode-map))
    (local-set-key (kbd "C-c C-s")
                   `(lambda ()
                     (interactive)
                     (beginning-of-buffer)
                     (replace-string "[X]" "✅")
                     (beginning-of-buffer)
                     (replace-string "[ ]" "❌")
                     (beginning-of-buffer)
                     (replace-string "[-]" "🚧")
                     (-let [org-export-with-toc nil]
                       (org-md-export-as-markdown)
                       (kill-ring-save (point-min) (point-max)))
                     (kill-buffer-and-window) ;; Kills the new org-md-export buffer
                     (kill-buffer ,buf) ;; Kills this temporary PR template buffer
                     (message "PR notes saved to clipboard in Github markdown")))))
** w-pr-template: Peer Review / Pull Request Template for Work
I followed the testing instructions and everything look's good 😁

Below is a detailed checklist of what I went through.

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

1. [ ] Pull Request: The PR template was fully filled out.
   - [ ] Clear description of the problem and how it was solved.
   - [ ] I've cross-checked the description with the associated Jira ticket; and
     everything is implemented.
   - [ ] I've ticked-off the PR's check-boxes.
   - [ ] Good use of bullet points (-) and code font (‵, ‷) to make the prose
     easier to read.
   - [ ] The commit messages are well-written.
   - [ ] Travis CI succeeds.
   - [ ] PR author annotated source code, with Github comments, before the review.
     - Annotations guide the reviewer through the changes, showing which files to look at first and defending the reason behind each code modification.

2. [ ] Functionality: The code behaves as the author intended.
   - [ ] I was able to reproduce the bug on ~main~.
   - [ ] Ran the code and used it as an end-user would.
       Namely, I made a new form, submitted an instance, checked ~In-Progress~,
     & ~Form Reports~.
   - [ ]  I've tried all kinds of quotes and unicode, "𝒰"\‘ℕ’/𝒊′ℭ″𝑂؛𝒟⨾'∃',
        input for text inputs.
   - [ ] Followed the happy path in the provided testing instructions.
     - Nope; none provided.
   - [ ] Also tried the following edge case: ⋯ ⁉ ⋯

3. [ ] Tests: There are new unit tests (but sadly no E2Es/integration).
   - [ ] Meaningful: Tests actually test that the code is performing the
     intended functionality.
   - [ ] Avoid global test fixtures and seeds, add data per-test.
       TL;DR: To prevent test coupling and easily reason about the test flow, each test
       should add and act on its own set of DB rows.

4. [ ] Naming: Clear and informative names were chosen for top-level-items/variables/methods.

5. [ ] Comments: New top-level-items/variables/methods have clear and useful
   documentation.
   - [ ] Sometimes the code is clear ---e.g., 5 lines perform a toggle---
     but we can improve readability by providing a “comment as function”
     ---e.g., making a ~toggle~ function, then calling it where it is used;
     this new function is likely to be smaller than the original inlined use.

     Some comments-as-functions have been suggested.

6. [ ] Being Neighbourly: There was nearby code that could have been
   improved/update, and suggestions have been left as to how to do so.
   - We're likely to touch these files again in the future, so why not leave
     things better than we found them 🚀

7. [ ] Code Smells: Are there any [[https://blog.codinghorror.com/code-smells/][code smells?]]

8. [ ] Syntax: I've read every line.
   - Nope, there was some stuff I'm not familair with. If the PR authour can
     jump on a call and walk me through them, that'd be awesome!
   - [ ] I've left various suggestions and feedback, against specific lines
     of code. Happy to discuss these further!
   - [ ] I took my time while reviewing your code, and I'm not depending on
     others to catch errors.

9. [ ] Complexity: Another developer can easily understand and use this code when
   they come across it in the future :-)

10. [ ] Modulairty: Is there any redundant or duplicate code? Is the code as modular as possible?

11. [ ] Backwards Compatiable: I made a form in ~main~, involving the work in
    this branch, and it worked fine in this branch.
    - I was able to edit the form, submit it, and checked that it looked find in ~Form Reports~.

12. [ ] Best Practices: The following rules-of-thumb are adhered to, more or less.
      #+html: <details>
    - Remove some redundancy using a bit of laws of algorithmics, namely
      ~[𝒊𝒇-𝒅𝒊𝒔𝒕𝒓𝒊𝒃𝒖𝒕𝒊𝒗𝒊𝒕𝒚] (a ? f(b) : f(c)) ≡ f(a ? b : c)~, which increases
      readability a tad.
      # Especially when “f” is a lengthy expression; it may also be ideal to give
      # “a ? b : c” a local name.
    - Fail fast, validate arguments [we have some in-house validation util libraries]
    - Be aware that ~0, "", []~ are all falsey values in JS: If a variable ~x~
      can be one of those things, then ~if (x)~ is not always approriate; better
      may be ~if(typeof x === 'integer')~ since this communicates two things (1)
      the variable is defined, and (2) what it's type expected is.
      + Likewise, better use ~typeof~ instead of ~x !== null~.
    - You have variables declared a bit from their use sites; the distance
      creates an unnecessary disconnect ---especially since you don't use these
      variables elsewhere. Please relocate them to be closer to their use sites.
    - Strings are sanitised
    - Errors are caught; with ~try/catch~
    - Global variables are avoided, when possible.
    - ~const~ is preferred to ~let~; ~var~ should seldom be used.
      - Use ~var~ and ~function~ when you want definitions hoisted to the top of
        their enclosing scope.
    - ~===~ is preferred to ~==~.
    - Use default arguments instead of short-circuiting or conditionals
      - ~f (x) { x = x || defaultValue; ⋯ }~ ≡ ~f (x = defaultValue) { ⋯ }~.
      - Named parameters can also be optional, with default values:
        ~f(obj) { let prop = obj.prop || defaultProp; ⋯}~ ≣ ~f ({prop =
        defaultProp}) { ⋯ }~
    - Unless you really need an array,  handled an indefinite number of
      arguments using rest parameters: ~function f(...args) {⋯ // use ‘args’ as
      an array}~ can be invoked ~f(x₁, x₂, …)~ _without_ array brackets; or as
      ~f(...arr)~ if you have an array in-hand.
    - Function arguments: 3 or fewer ideally
    - If you need to declare an argument but are not using it, prefix it's name
      with an underscore.
    - Encapsulate conditionals in a separate _well-named_ function, if possible
    - Avoid negative conditionals; e.g., by making use of well-choosen names.
    - Use Demorgan's rules: ~!x && !y ≣ !(x || y)~ and ~!x || !y ≣ !(x && y)~.
    - Use ~try/catch~ with ~async/await~; or promises with both ~then~ and ~catch~.
    - Don't ignore rejected promises, log it to external logging service
    - Related chunks of code are clearly demarcated.
    - If an anonymous function is too long, more than 2 lines, give it a name:
      E.g., in JS, ~arr.map(x => ...) ≣ arr.map( function
      doingSomeComplexStuff(x) { return ...} )~.  The name aids in communicating
      the intent, and is useful for debugging.
    - [ES6] Braces are used for block scope, and not simulated using IIFEs.
    - Avoid explicit newlines with ~+ "\n" +~ in-favour of Template Literals, which preserve line
      breaks.
    - Use Destructuring instead of explicit projections; aids in readability.
      - Note∶ ~let y = x.y~ ≡ ~let {y} = x~ only holds when ~x~ is not ~null~
        (and so when ~x~ is not a expression involving ~?.~).

    #+html: </details>

9. Web-Development

First, let's get some useful Cheat Sheets…

;; Get the repos locally, and use: M-x my/cheatsheet to view the pretty HTML sheets.
(mapcar #'my/cheatsheet '("JavaScript" "Vue" "AngularJS"))

9.1. Quickly produce HTML from CSS-like selectors

Emmet-mode (outside of Emacs, this is known as Emmet and Zen Coding), is a powerful abbreviation engine that expands CSS selectors into HTML code. It's a neat way to write markup quickly in Emacs.

Watch this demo video. Or here's an example with filler text: #page>.logo+ul#navigation>li*5>a>lorem3 expands into

<div id="page">
    <div class="logo"></div>
    <ul id="navigation">
        <li><a href="">Justo eget magna!</a></li>
        <li><a href="">Mattis pellentesque id!</a></li>
        <li><a href="">Turpis massa tincidunt.</a></li>
        <li><a href="">Sit amet, porttitor.</a></li>
        <li><a href="">Feugiat nisl pretium.</a></li>
    </ul>
</div>

Likewise, we can make 5 links next to 5 input boxes with: (a[href=www.google.ca]{Click me!}+label{Name:}>input[value="first name"])*5.

Anyhow, here's my setup:

;; USAGE: Place point in an emmet snippet and press C-j to expand it to appropriate tag structure;
;; e.g., #q.x>p C-j. Alternatively, press C-j then start typing an emmet snippet to see it preview live.
;; [C-j is just M-x emmet-expand-line]
;;
(use-package emmet-mode ;; C-j ! RET  === Makes an entire HTML template for you.
  :hook (web-mode . emmet-mode))
;;
;; Please show me an HTML expansion preview as I type
(setq emmet-preview-default t) ;; Press C-j then start typing; e.g., C-j #q.x.y>p>b RET
;;
;; After expanding, positioned the cursor between first empty quotes.
;; The preview can help with tricky CSS precedence rules; e.g., C-j gives the same thing for: a>b+c>d   ==  a>(b+(c>d))
(setq emmet-move-cursor-between-quotes t) ;; E.g., C-j #q[name] RET
;;
(add-hook 'sgml-mode-hook 'emmet-mode) ;; Auto-start on any markup modes
;; (add-hook 'css-mode-hook  'emmet-mode) ;; enable Emmet's css abbreviation.
Notable HTML key bindings (including [[https://www.gnu.org/software/emacs/manual/html_node/emacs/HTML-Mode.html] [built-ins]])
Wrap with Abbreviation / C-c C-c w
Select a region, then enter an abbreviation.
  • For example, select the phrase Hello, World! then C-c C-c w q>b to place that phrase in a quote, and make it bold.
Go to Edit Point / C-M-{Leftarrow, Rightarrow}
to move between editable-tags/empty-attributes. (Demo)
C-c TAB
Cheaply preview the HTML file by toggling visibility of tags.
C-c /, C-c C-e
Insert a close tag for the innermost unterminated tag (sgml-close-tag). If called within a tag or a comment, close it instead of inserting a close tag.
C-c ? tag RET
Display a description of the meaning of tag tag (sgml-tag-help). If the argument tag is empty, describe the tag at point. :fire:
C-c C-d
Delete the tag at or after point, and delete the matching tag too (sgml-delete-tag). If the tag at or after point is an opening tag, delete the closing tag too; if it is a closing tag, delete the opening tag too.
C-c C-f/b
More 'f'orward or 'b'ackward one tag element. —Nice way to quicly check that your tags are balanced as you think they are.
C-c C-a
Interactively insert attribute values for the current tag. :fire:
  • Super helpful when you don't remember the possible attributes of a tag, or their possible values.
  • Interactively specify a tag and its attributes!
Notable Emmet Features & Examples
  • Example that includes Ids, classes, children, sibling selectors, {content}, 10 random lorem text, parenthesis for grouping; as well as attributes with and without values.

    ParentTag#Id.Class1.Class2[Attribute1 Attribute2]>ChildTag{hiya there}>lorem10+(GrandChild1>GreatGrandChild11)+GrandChild2#Id2[attribute=value]
    
  • Nesting (>) can be 'undone' with "climb-up"(^): a>b>c>d^^e == a>(b>c>d)+e
  • Multiplication is repeated addition: X*3>Y == (X>Y)+(X>Y)+(X>Y)
  • In general, loremN produces \(N\) many random filler text. E.g., make a dummy list: ul>li*3>lorem3

Other notable snippets include a#neato.x.y.z, input[hola=value], img, p, table+, ul+, ol+.

  • There are also CSS abbreviations; but that's a story for another time.
  • This cheat sheet has all the supported abbreviations, with expansions.

  • ID and CLASS attributes: div#page.section.main
    • div tag name can be omitted when writing element starting from ID or CLASS: #content>.section is the same as div#content>div.section.
  • Custom attributes: div[title], a[title="Hello world" rel], td[colspan=2]
  • Element multiplication: li*5 will output <li> tag five times.
  • Item numbering with the $ character: li.item$*3 will output <li> tag three times, replacing $ character with item number.
  • Multiple $ characters in a row are used as zero padding, i.e.: li.item$$$li.item001
  • Abbreviation groups with unlimited nesting: div#page>(div#header>ul#nav>li*4>a)+(div#page>(h1>span)+p*2)+div#footer
    • You can literally write a full document markup with just a single line.
  • Text support: p>{Click }+a{here}+{ to continue}.

Good tutorials:

For more on writing HTML within Emacs, see this ~2min yet very informative video by Emacs Rocks!

Let's add a new snippet so that: angular C-j C-c C-v shows us an interactive web application.

(cl-defun my/add-emmet-snippet (abbreviation expansion)
  "Add ABBREVIATION as a snippet in `emmet-mode' to be EXPANSION.

Both arguments are strings."
  (add-hook 'emmet-mode-hook
   ;; [Should this be added to “emmet-snippets” variable instead?]
            `(lambda () (puthash ,abbreviation ,expansion emmet-tag-snippets-table))))


(setq emmet-mode-hook nil)

(my/add-emmet-snippet "vue"
"<!doctype html>
<html lang=\"en\">
    <head>
        <title>Salamun Alaykum, world!</title>
        <script src=\"https://unpkg.com/vue@3\"></script>
        <!-- <link rel=\"stylesheet\" type=\"text/css\" href=\"styles.css\" /> -->
        <style type=\"text/css\">
         input, #reply { color: darkcyan; font-size: 14pt }
        </style>
    </head>
    <body>
        <div id=\"Hola\">
            <h1>Number Guessing Game</h1>
            <input type=\"number\" v-model=\"guess\" style=\"width: 25%;\" v-bind:placeholder=`${prompt}`>
            <button v-on:click=\"go(guess)\">Learn Something!</button>
            <div id=\"reply\"> {{reply(guess)}} </div>
        </div>
        <!-- <script src=\"myscripts.js\"></script> -->
        <script type=\"text/javascript\">
         let myApp = Vue.createApp({
             data() {
                 return { guess: null
                        , prompt: \"Enter a guess between 0 and 100\"
                        , secret: Math.floor(Math.random() * 100)
                        }
             },
             methods: { reply(gs) { return gs == this.secret ? \"You win!\" : (gs < this.secret ? \"Too low\" : \"Too high\"); }
                      , go(number) { window.location.href = \"https://www.wolframalpha.com/input?i=\" + number }
                      }
         }).mount('#Hola')
        </script>
    </body>
</html>")

(my/add-emmet-snippet "angular"
"<!doctype html>
<html lang=\"en\" ng-app=\"Hola\">
  <head>
    <title>Salamun Alaykum, world!</title>
    <script src=\"https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js\"></script>
    <!-- <script src=\"myscripts.js\"></script> -->
    <script type=\"text/javascript\">
      angular.module(\"Hola\", [])
        .controller(\"prompt\",
           ($scope, $window) => {
              $scope.prompt = \"Enter a guess between 0 and 100\"
              $scope.secret = Math.floor(Math.random() * 100)
              $scope.reply  = gs => gs == $scope.secret ? \"You win!\" : (gs < $scope.secret ? \"Too low\" : \"Too high\")
              $scope.go = number => { $window.location.href = \"https://www.wolframalpha.com/input?i=\" + number }
          })
    </script>
    <!-- <link rel=\"stylesheet\" type=\"text/css\" href=\"styles.css\" /> -->
    <style type=\"text/css\">
       input, #reply { color: darkcyan; font-size: 14pt }
    </style>
  </head>
  <body>
    <div ng-controller=\"prompt\">
      <h1>Number Guessing Game</h1>
      <input type=\"number\" ng-model=\"guess\" style=\"width: 25%;\" placeholder=\"{{prompt}}\">
      <button ng-click=\"go(guess)\">Learn Something!</button>
      <div id=\"reply\"> {{reply(guess)}} </div>
    </div>
  </body>
</html>")

A non-blocking alternative to the built-in alert():

;; A way to show results of trying things out ---when not using a reactive framework.
(my/add-emmet-snippet "message"
"     // Append “text” node to the end of tag with “id”.
     // Example: <button onclick=\"message(\"myID\", \"Hello!\")\"> Speak! </button>
     function message(id, text = \"Hello, world\") {
         const tag = document.createElement(\"p\") // <p></p>
         const textNode = document.createTextNode(text)
         tag.appendChild(textNode); // <p>Hello, world</p>
         const element = document.getElementById(id);
         element.appendChild(tag);
     }")

A snippet for a form with (1) automatic alignment, and (2) submission handled by local JavaScript.

(my/add-emmet-snippet "form"
        "<h1> <a href=\"https://www.quackit.com/css/grid/tutorial/form_layout_with_auto_placement.cfm\">
            Automatically aligned form items</a> </h1>

        <form name=\"hola\"  onsubmit=\"go(hola.elements);\">
            <label>Name</label>
            <input name=\"name\" type=\"text\" required/>

            <label>Comments</label>
            <textarea name=\"comments\" maxlength=\"500\"></textarea>

            <input type=\"submit\"/>
        </form>

        <!-- <script src=\"myscripts.js\"></script> -->
        <script>
         let go = form => { alert(`${form.name.value}: “${form.comments.value}”`) }
        </script>

        <!-- <link rel=\"stylesheet\" type=\"text/css\" href=\"styles.css\" /> -->
        <style>
         form {
             /* We want the inputs&labels to be thought of as rows in a grid*/
             display: grid;
             grid-auto-flow: row;
             /* Each row has 2 columns. */
             grid-template-columns: [mylabels] auto [myinputs] 1fr;
             grid-gap: .8em;     /* Distance between form elements */
             background: beige;
             padding: 1.2em;
         }
         /* Let's attach column names to elements */
         form > label  {
             grid-column: mylabels;
             grid-row: auto;
         }
         form > input,
         form > textarea {
             grid-column: myinputs;
             grid-row: auto;
         }
         input, textarea { color: darkcyan; font-size: 14pt }
        </style>")

9.2. LSP for HTML + CSS

Notes: I've tried LSP for HTML but I didn't find it provided much, and even worse, it seemed to ignore the useful information I get from Flycheck!

  • When I accidentally duplicate Ids, or miss important tag attributes, Flycheck (C-c !) reminds of these things! :-)
  • This is also LSP for Emmet, which let's you expand any text you write as if it were an emmet snippet such that you can TAB to the various editable regions in the expansion to fill them out. E.g., ul>li*5 RET produces a yas-snippet which you TAB through to fill out.
    • If you don't want some text to be treated as a snippet, press C-g.
    • As above, this ruins flycheck support and highlighting —requiring web-mode to get some highlighting.

However, LSP for CSS is indispensable!

  • It provides auto-completion for properties, and if you pause then a tool tip with useful explanations of properties. :fire:
  • Moreover, if you press ENTER on a completion candidate, then you get to select a possible value from another list!
  • When I mouse hover over a property, I get a 1-line tooltip describing the property.
  • No need to remember which properties are possible, what they do, and their possible values! Just start typing and see what pops-up!
M-x lsp-install-server RET css-ls
;; When I accidentally duplicate a property in a rule, please report that as an error.
(setq lsp-css-lint-duplicate-properties "error")

;; If I accidentally enter an unknown property (e.g., writing Canadian “colour” instead of American “color”),
;; then I'll be notified with an error notice.
(setq lsp-css-lint-unknown-properties "error")


(use-package lsp-mode
  :hook  ;; Every programming mode should enter & start LSP, with which-key support
         (css-mode . lsp-mode) ;; Enter LSP mode
         (css-mode . lsp))      ;; Start LSP server

9.2.1. CSS Property Argument Information in the Echo Area

LSP for CSS shows me a tooltip with a description and the argument syntax of a property; but that's only when entering a new property. What if I'm updating a property; or just browsing a property and want some information on the arguments list? css-eldoc to the rescue.

  • Eldoc-mode is a minor-mode which shows you, in the echo area, the argument list of the function call you are currently writing.
;; [USAGE] In a CSS file, place cursor anywhere after the colon (but before ‘;’)
;; in “columns: 0ch;” or in “columns: ” and look at the echo area for how
;; arguments to this property should look like.
(use-package css-eldoc
  :init (progn (require 'css-eldoc) (turn-on-css-eldoc)))
;; [Possibly useful snippet to keep around.]
;; Change priority of a server; useful when multiple servers are running for a buffer.
;; (setf (lsp--client-priority (gethash 'emmet-ls lsp-clients)) 4)

9.3. Show me HTML+CSS Changes Live as I Type!

(use-package impatient-mode)

(use-package web-mode
  :init (add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode)))


;;     C-c C-v: Browse buffer within external browser.
;; C-u C-c C-v: Ensure impatient-mode is enabled for current buffer and browse it WITHIN Emacs.
;; [xwidget-webkit has some bugs; e.g., sometimes buttons that should redirect don't do anything.]
;; [The “C-u” option is useful when I want to “see” the resulting HTML change as I type; e.g., new content or styling.]
;; [Note the “angular” snippet above works beautifully /within/ Emacs; use “b/f” to move backward/forward in the browser.]
(bind-key "C-c C-v"
          (lambda (open-within-emacs) (interactive "P")
            (if (not open-within-emacs)
                (browse-url-of-buffer (current-buffer))
              (unless (process-status "httpd") (httpd-start))
              (unless impatient-mode (impatient-mode))
              (let ((browser (car (--filter (s-starts-with? "*xwidget" (buffer-name it)) (buffer-list))))
                    (file (buffer-name)))
                (when browser (switch-to-buffer browser) (let (kill-buffer-query-functions) (kill-buffer)))
                (split-window-below)
                (other-window -1)
                (xwidget-webkit-browse-url (concat "http://localhost:8080/imp/live/" file))
                (preview-it-mode -1) ;; Looks poor; and I don't need it when writing HTML.
                (other-window -1))))
          'web-mode-map)
(bind-key "M-q" #'sgml-pretty-print 'web-mode-map)

9.4. Eldoc for Lisp and Haskell —documentation in the mini-buffer

In emacs-lisp-mode we can enable eldoc-mode —“Elisp Live Documentation”— to display information about a function or a variable in the echo area. Likewise for Haskell.

(use-package eldoc
  :diminish eldoc-mode
  :hook (emacs-lisp-mode . turn-on-eldoc-mode)
        (lisp-interaction-mode . turn-on-eldoc-mode)
        (haskell-mode . turn-on-haskell-doc-mode)
        (haskell-mode . turn-on-haskell-indent))

;; Slightly shorten eldoc display delay.
(setq eldoc-idle-delay 0.4) ;; Default 0.5

The less casual Haskeller would likely want to use intero to obtain more support; e.g., obtain suggestions from GHC about redundant imports or type signatures.

9.5. Modern Browsing within Emacs   Disabled

# https://github.com/d12frosted/homebrew-emacs-plus
$ brew tap d12frosted/emacs-plus
$ brew install emacs-plus@29 --with-xwidgets
$ /usr/local/Cellar/emacs-plus@29/29.0.50/bin/emacs-29.0.50 &

# In ~/.bashrc, put the following at the end:
alias emacs="/usr/local/Cellar/emacs-plus@29/29.0.50/bin/emacs-29.0.50"

I like using Chrome —I like the integration of all things Google.

(cl-defun internet (&optional (url (concat "https://www." (read-string "https://www."))))
  "Browse to URL using `xwidget-webkit-browse-url'; see also `browse-url'."
  (interactive)
  (delete-other-windows)
  (split-window-right)
  (xwidget-webkit-browse-url url))

(my/defhydra "C-c p" "Emacs Browser" gamepad
  :Internet
  ("m" (internet "https://mail.google.com/mail/u/0/#inbox") "gMail"  :exit t)
  ("c" (internet "https://calendar.google.com/calendar/u/0/r") "gCalendar"  :exit t)
  ("e" (internet "https://www.reddit.com/r/emacs/") "Emacs Forum"  :exit t)
  ("b" (internet) "Browse"  :exit t))

9.6. VueJS

(use-package vue-mode)

10. Lisp Helpers / Kill all buffers that are not associated with a file

(cl-defun my/clean-buffers ()
  "Kill all buffers that are not associated with a file.
  By convention, such files are named in *earmuffs* style."
  (interactive)
  (mapcar #'kill-buffer (--filter (s-matches? "\\*.*\\*" it) (mapcar #'buffer-name (buffer-list)))))

11. Toggles Hydra   Not_yet_tangled

(my/defhydra "C-c t" "Toggles" toggle-on
   :Theme
   ("t t" my/toggle-theme "Theme")
   ("t s" (thread-last (all-completions "doom" (custom-available-themes))
            (completing-read "Load custom theme: ")
            intern
            load-theme)
    "Select Theme")

   :UI
   ("i" display-fill-column-indicator-mode :toggle t)
   ("f" my/toggle-font "font")
   ("F" writeroom-mode "Focused Work!" :toggle t)
   ("d" treemacs "directory finder" :toggle t)
   ("n" display-line-numbers-mode "line number" :toggle t)
   ("u f" (setq frame-title-format (completing-read "New frame title: " nil)) "New frame title") ;; Useful for random screenshots

   :Possibly_in_the_way
   ("e" electric-pair-mode "electric pair" :toggle t)
   ("c" flyspell-mode "spell check" :toggle t)
   ("s" prettify-symbols-mode "pretty symbol" :toggle t)
   ("w" whitespace-cleanup "Clean up whitespace on save" :toggle t)
   ;; ("a" global-aggressive-indent-mode "aggressive indent" :toggle t)
   ;; ("d" global-hungry-delete-mode "hungry delete" :toggle t)

   :Modeline
   ("m d" doom-modeline-mode "modern mode-line" :toggle t)
   ("m b" display-battery-mode "battery" :toggle t)
   ("m t" display-time-mode "time" :toggle t)
   ("m w" which-function-mode "which function" :toggle t)

   :Highlight
   ("h l" global-hl-line-mode "line" :toggle t)
   ("h p" show-paren-mode "paren" :toggle t)
   ;; ("h s" symbol-overlay-mode "symbol" :toggle t)
   ;; ("h r" rainbow-mode "rainbow" :toggle t)
   ;; ("h w" (setq-default show-trailing-whitespace (not show-trailing-whitespace))
   ;; "whitespace" :toggle show-trailing-whitespace)
   ;; ("h d" rainbow-delimiters-mode "delimiter" :toggle t)
   ("h i" highlight-indent-guides-mode "indent" :toggle t)
   ("h t" global-hl-todo-mode "todo" :toggle t)
   ;; ("x" highlight-sexp-mode "sexp" :toggle t)
   ;; ("t" hl-todo-mode "todo" :toggle t)

   :Program
   ("f" flycheck-mode "flycheck" :toggle t)
   ;; ("F" flymake-mode "flymake" :toggle t)
   ("o" origami-mode "folding" :toggle t)
   ;; ("O" hs-minor-mode "hideshow" :toggle t)
   ("W" subword-mode "subword" :toggle t)
   ("E" toggle-debug-on-error "debug on error" :toggle (default-value 'debug-on-error))
   ("Q" toggle-debug-on-quit "debug on quit" :toggle (default-value 'debug-on-quit))
   ;; ("v" global-diff-hl-mode "gutter" :toggle t)
   ;; ("V" diff-hl-flydiff-mode "live gutter" :toggle t)
   ;; ("M" diff-hl-margin-mode "margin gutter" :toggle t)
   ;; ("D" diff-hl-dired-mode "dired gutter" :toggle t)

   ("P" (if (profiler-running-p)
            (progn (profiler-report) (profiler-stop))
          (profiler-start 'cpu+mem))
    "Profiler start / report" :exit (profiler-running-p)))

;; Places single frame in centre of screen; hides many UI things (e.g., mode line).
;; Nice for focused / immersive work.
;;
;; When in writeroom, let's also dim the font color of text in surrounding
;; paragraphs / code blocks.
(use-package writeroom-mode
  :config (use-package focus)
  :hook (writeroom-mode . (lambda () (focus-mode 'toggle))))

12. Lost Souls   Outdated_Documentation Not_yet_tangled

12.1. Note: M-S-SPC is for my personal servers dashboard.

;; Note: M-S-SPC is for my personal servers dashboard.
(global-set-key (kbd "M-SPC")   (lambda () (interactive) (setq org-agenda-files (list org-default-notes-file)) (org-agenda nil "a") (delete-other-windows) (beginning-of-buffer)))

(use-package ace-jump-mode ;; Already installed above, somewhere.
  :config (bind-key* "C-c SPC" 'ace-jump-mode))

12.2. Zoom

;; An automatic window-resizing mechanism.
;; A “calmer” alternative to golden-ratio.
;; https://github.com/cyrus-and/zoom
(use-package zoom
  :diminish
  :config (zoom-mode t))

12.3. Ibuffer

;; Let's use an improved buffer list.
(use-package ibuffer ;; This is built-into Emacs.
  :bind ("C-x C-b" . ibuffer))
;; It uses similar commands as does dired; e.g.,
;; / . org
;; This filters (“/”) the list with extensions (“.”) being “org”.

(use-package ibuffer-vc
  :hook (ibuffer . (lambda ()
                     (ibuffer-vc-set-filter-groups-by-vc-root)
                     (unless (eq ibuffer-sorting-mode 'alphabetic)
                       (ibuffer-do-sort-by-alphabetic))))
  :custom
  (ibuffer-formats '((mark modified read-only " "
                           (name 18 18 :left :elide) " "
                           (size 9 -1 :right) " "
                           (mode 16 16 :left :elide) " "
                           (vc-status 16 16 :left) " "
                           (vc-relative-file)))))

12.4. find function at point

Friendly reminder that M-. takes you to the definition, and M-, takes you back to where you originally where.

12.5. “C-x 2” and “C-x 3” now create a new window horizontally/vertically and send cursor there

;; When we split open a new window, we usually want to jump to the new window.
(advice-add #'split-window-below :after (lambda (&rest _) (other-window 1)))
(advice-add #'split-window-right :after (lambda (&rest _) (other-window 1)))

12.6. Semantic Change

Using ⌘-i and ⌘-o we can quickly, for example, delete a string or its contents; or delete a {}-block or just its contents; or delete a ()-argument list or just its contents, etc.

change-inner gives you vim's ci command:

  • change-inner { ⇒ Delete all text starting from the first ‘{’ delimiter to the next one; but keep the delimiters.
  • change-outer { ⇒ As above, but also delete the delimiters.
(use-package change-inner
  :diminish
  :bind (("s-i" . #'change-inner)
         ("s-o" . #'change-outer)))

12.7. Drag Stuff   Disabled

;; Move current word ←/→, or current line ↑/↓.
;; Todo: Compare with org-metaup and org-metadown...
(use-package drag-stuff
  :diminish
  :config (cl-loop for (key . action) in '(("<M-down>" . drag-stuff-down)
                                      ("<M-up>" . drag-stuff-up)
                                      ("<M-right>" . drag-stuff-right)
                                      ("<M-left>" . drag-stuff-left))
                do (bind-key key action org-mode-map))
      (drag-stuff-global-mode 1))

Ruins Org-mode's M-↑/↓ for moving entire sections around.

12.8. Indentation Guide

The following is also “OK” in Org-mode ;-)

;; Add a visual indent guide
(use-package highlight-indent-guides
  :hook (prog-mode . highlight-indent-guides-mode)
  :custom
  (highlight-indent-guides-method 'character)
  (highlight-indent-guides-character ?|)
  (highlight-indent-guides-responsive 'stack))

12.9. Commenting

Let's get some nifty commenting features —the link has nice usage gifs.

  • repeatedly does (1) comments current line, (2) inserts a comment at the end of the current line, and (3) deletes an existing end-of-line comment.

    indents the current enf-of-line comment with any above it.

    For use with Org-mode, it's best to use org-edit-src-code —which I've bound to .

    (use-package comment-dwim-2
      :bind ("M-;" . comment-dwim-2))
    
     ;; Not ideal: M-; comments a parent Org heading and not the current line.
     ;; (define-key org-mode-map (kbd "M-;") 'org-comment-dwim-2)
    

12.10. Having a workspace manager in Emacs

I've loved using XMonad as a window tiling manager. I've enjoyed the ability to segregate my tasks according to what ‘project’ I'm working on; such as research, marking, Emacs play, etc. With perspective, I can do the same thing :-)

That is, I can have a million buffers, but only those that belong to a workspace will be visible when I'm switching between buffers, for example. ( The awesome-tab and centaur-tab, mentioned elsewhere here, can be used to achieve the same thing by ‘grouping buffers together’. )

(use-package perspective
  :defer t
  :config ;; Activate it.
          (persp-mode)
          ;; In the modeline, tell me which workspace I'm in.
          (persp-turn-on-modestring))

All commands are prefixed by C-x x; main commands:

s, n/→, p/←
‘S’elect a workspace to go to or create it, or go to ‘n’ext one, or go to ‘p’revious one.
c
Query a perspective to kill.
r
Rename a perspective.
A
Add buffer to current perspective & remove it from all others.

As always, since we've installed which-key, it suffices to press C-x x then look at the resulting menu 😃

12.11. Editor Documentation with Contextual Information

Emacs is an extensible self-documenting editor!

Let's use a helpful Emacs documentation system that cleanly shows a lot of contextual information —then let's extend that to work as we want it to: C-h o to describe the symbol at point.

(use-package helpful :defer t)

(defun my/describe-symbol (symbol)
  "A “C-h o” replacement using “helpful”:
   If there's a thing at point, offer that as default search item.

   If a prefix is provided, i.e., “C-u C-h o” then the built-in
   “describe-symbol” command is used.

   ⇨ Pretty docstrings, with links and highlighting.
   ⇨ Source code of symbol.
   ⇨ Callers of function symbol.
   ⇨ Key bindings for function symbol.
   ⇨ Aliases.
   ⇨ Options to enable tracing, dissable, and forget/unbind the symbol!
  "
  (interactive "p")
  (let* ((thing (symbol-at-point))
         (val (completing-read
               (format "Describe symbol (default %s): " thing)
               (vconcat (list thing) obarray)
               (lambda (vv)
                 (cl-some (lambda (x) (funcall (nth 1 x) vv))
                          describe-symbol-backends))
               t nil nil))
         (it (intern val)))
    (cond
     (current-prefix-arg (funcall #'describe-symbol it))
     ((or (functionp it) (macrop it) (commandp it)) (helpful-callable it))
     (t (helpful-symbol it)))))

;; Keybindings.
(global-set-key (kbd "C-h o") #'my/describe-symbol)
(global-set-key (kbd "C-h k") #'helpful-key)

I like helpful and wanted it to have the same behaviour as C-h o, which helpful-at-point does not achieve. The incantation above makes C-h o use helpful in that if the cursor is on a symbol, then it is offered to the user as a default search item for help, otherwise a plain search box for help appears. Using a universal argument lets us drop to the built-in help command.

12.12.   README —From init.org to init.el

Rather than manually extracting the Lisp code from this literate document each time we alter it, let's instead add a ‘hook’ —a method that is invoked on a particular event, in this case when we save the file. More precisely, in this case, C-x C-s is a normal save whereas C-u C-x C-s is a save after forming init.elc and README.md.

  1. The my/make-init-el-and-README function

    We ‘hook on’ the following function to the usual save method that is associated with this file only.

      (defun my/make-init-el-and-README ()
        "Tangle an el and a github README from my init.org."
        (interactive "P") ;; Places value of universal argument into: current-prefix-arg
        (when current-prefix-arg
          (let* ((time      (current-time))
                 (_date     (format-time-string "_%Y-%m-%d"))
                 (.emacs    "~/.emacs")
                 (.emacs.el "~/.emacs.el"))
            ;; Make README.org
            (save-excursion
              (org-babel-goto-named-src-block "make-readme") ;; See next subsubsection.
              (org-babel-execute-src-block))
    
            ;; remove any other initialisation file candidates
            (ignore-errors
              (f-move .emacs    (concat .emacs _date))
              (f-move .emacs.el (concat .emacs.el _date)))
    
            ;; Make init.el
            (org-babel-tangle)
            ;; (byte-compile-file "~/.emacs.d/init.el")
            (load-file "~/.emacs.d/init.el")
    
            ;; Acknowledgement
            (message "Tangled, compiled, and loaded init.el; and made README.md … %.06f seconds"
                     (float-time (time-since time))))))
    
    (add-hook 'after-save-hook 'my/make-init-el-and-README nil 'local-to-this-file-please)
    
  2. The Org-block named make-readme

    Where the following block has #+NAME: make-readme before it. This source block generates the README for the associated Github repository.

    (save-buffer)
    (with-temp-buffer
        (insert
        "#+EXPORT_FILE_NAME: README.org
    
         # Logos and birthday present painting
         #+HTML:" (s-collapse-whitespace (concat
        " <p align=\"center\">
           <img src=\"images/emacs-logo.png\" width=150 height=150/>
         </p>
    
         <p align=\"center\">
            <a href=\"https://www.gnu.org/software/emacs/\">
                 <img src=\"https://img.shields.io/badge/GNU%20Emacs-" emacs-version "-b48ead.svg?style=plastic\"/></a>
            <a href=\"https://orgmode.org/\"><img src=\"https://img.shields.io/badge/org--mode-" org-version "-489a9f.svg?style=plastic\"/></a>
         </p>
    
         <p align=\"center\">
           <img src=\"images/emacs-birthday-present.png\" width=250 height=250/>
         </p>
        "))
    
       ;; My Literate Setup; need the empty new lines for the export
       "
    
         I enjoy reading others' /literate/ configuration files and
         incorporating what I learn into my own. The result is a
         sufficiently well-documented and accessible read that yields
         a stylish and functional system (•̀ᴗ•́)و
    
         This ~README.org~ has been automatically generated from my
         configuration and its contents below are accessible
         in (outdated) blog format, with /colour/, or as colourful
         PDF, [[https://alhassy.github.io/init/][here]]. Enjoy
         :smile:
    
         #+INCLUDE: init.org
        ")
    
        ;; No code execution on export
        ;; ⟪ For a particular block, we use “:eval never-export”. ⟫
        (let ((org-export-use-babel nil))
          (org-mode)
          (org-org-export-to-org)))
    

    Alternatively, evaluate the above source block with C-c C-c to produce a README file.

    For the ‘badges’, see https://shields.io/. The syntax above is structured:

    https://img.shields.io/badge/<LABEL>-<MESSAGE>-<COLOR>.svg
    
  3. ‘Table of Contents’ for Org vs. Github

    The above mentioned package toc-org, which creates an up-to-date table of contents in an org file, at any heading tagged :TOC:. It's useful primarily for README files on Github. There is also org-make-toc, which is more flexible: The former provides only a top-level TOC; whereas this package allows TOCs at the sibling level, say, to produce a TOC of only the subsections of a particular heading, and other TOC features. Unlike toc-org, org-make-toc uses property drawers to designate TOC matter.

    (use-package toc-org
      ;; Automatically update toc when saving an Org file.
      :hook (org-mode . toc-org-mode)
      ;; Use both “:ignore_N:” and ":export_N:” to exlude headings from the TOC.
      :custom (toc-org-noexport-regexp
               "\\(^*+\\)\s+.*:\\(ignore\\|noexport\\)\\([@_][0-9]\\)?:\\($\\|[^ ]*?:$\\)"))
    

    However, toc-org produces broken links for numbered sections. That is, if we use #+OPTIONS: num:t then a section, say ** =~/.emacs= vs. =init.org= as the first subheading of the third heading, then it renders with the text preceeded by 3.1. On the left-most part of the heading, Github provides a a link option; clicking provides a link to this exact location in the README, changing the current URL to something like https://github.com/alhassy/emacs.d#31-emacs-vs-initorg. Now, toc-org produces Github-style anchors from Org headings, but does not account for numbers, and so gives us https://github.com/alhassy/emacs.d#emacs-vs-initorg, which is so close but missing the translated number, 31.

    I've experimented with using toc-org links using org-style, instead of the default Github style, but it seems that the org-style completely breaks rendering the resulting readme. Likewise, it seems that headings that are links break the TOC link; whence my section on the Reveal slide-deck system has a broken link to it. Perhaps org-make-toc solves these issues —something to look into.

    I'm not sure how I feel about actually having the Github-serving TOC in my source file. It's nice to have around, from an essay-perspecive, but it breaks HTML export since its links are not well-behaved; e.g., :ignore:-ed headlines appear in the toc, but do not link to any visible heading in the HTML; likewise, headings with URLS in their names break. As such, below I've developed a way to erase it altogether —alternatively, one could mark the toc as :noexport:, but this would then, in my current approach, not result in a toc in the resulting README.

    (cl-defun my/org-replace-tree-contents (heading &key (with "") (offset 0))
      "Replace the contents of org tree HEADING with WITH, starting at OFFSET.
    
    Clear a subtree leaving first 3 lines untouched  ⇐  :offset 3
    Deleting a tree & its contents                   ⇐  :offset -1, or any negative number.
    Do nothing to a tree of 123456789 lines          ⇐  :offset 123456789
    
    Precondition: offset < most-positive-fixnum; else we wrap to a negative number."
      (interactive)
      (save-excursion
        (beginning-of-buffer)
        (re-search-forward (format "^\\*+ %s" (regexp-quote heading)))
        ;; To avoid ‘forward-line’ from spilling onto other trees.
        (org-narrow-to-subtree)
        (org-mark-subtree)
        ;; The 1+ is to avoid the heading.
        (dotimes (_ (1+ offset)) (forward-line))
        (delete-region (region-beginning) (region-end))
        (insert with)
        (widen)))
    
    ;; Erase :TOC: body ---provided we're using toc-org.
    ;; (my/org-replace-tree-contents "Table of Contents")
    
  4. Alternate approaches to generating a README

    Github supports several markup languages, one of which is Org-mode.

    • It seems that Github uses org-ruby to convert org-mode to html.
    • Here is a repo demonstrating how Github interprets Org-mode files.
    • org-ruby supports inline #+HTML but not html blocks.

    It seems coloured HTML does not render well:

    (org-html-export-to-html)
    (shell-command "mv README.html README.md")
    

    JavaScript supported display of web pages with:

    #+INFOJS_OPT: view:info toc:t buttons:t
    

    This looks nice for standalone pages, but doesn't incorporate nicely with github README.org.

    Usually, Github readme files are in markdown, which we may obtain from an Org file with M-x org-md-export-to-markdown.

    • [ ] By default, this approach results in grey-coloured source blocks —eek!
    • [X]

      It allows strategic placement of a table of contents.

      Declare #+options: toc:nil at the top of the Org file, then have #+TOC: headlines 2 in a strategic position for a table of contents, say after a brief explanation of what the readme is for.

    • [X] It allows us to preview the readme locally before comitting, using grip.
       ;; grip looks for README.md
       (system-packages-ensure "grip")
       ;; Next: (async-shell-command "cd ~/.emacs.d/; grip")
    

    We can approximate this behaviour for the other approaches:

    1. Export to markdown.
    2. COMMENT-out any :TOC:-tagged sections —their links are not valid markdown links, since they don't refer to any markdown labels.
    3. Rename the exported file to README.md.
    4. Run grip.

12.13. Org-mode's <𝒳 Block Expansions

In org-mode we type <X TAB to obtain environment templates, such as <s for source blocks or <q for quote blocks. It seems recent changes to the org-mode structure template expansion necessitate explicitly loading org-tempo.

(require 'org-tempo)

To insert source blocks with the assistance of a pop-up: C-c C-v d ;-) Perhaps more usefully, invoking within a source block splits it up into two separate blocks! Moreover, if invoked on a selected region, it puts the region into a new code block! Wow!

  • C-c C-, refers to org-insert-structure-template, which provides non-source blocks, such as quote <q, comment <C, center <c, notes <n, examples <e, and <l and <h and <a for LaTeX and HTML and ASCII export blocks.
    • <X allows you to obtain the org-block assigned to shortcut X.
    • The contents of comment blocks are ignored upon export.
  • C-c C-v C-d and C-c C-v d refer to the org-babel-demarcate-block, which provides source blocks.
We shall improve upon this system below using snippets.

E.g., s_em TAB to obtain an org-src block marked with emacs-lisp as the language. This saves us a few key strokes.

12.14. What's changed & who's to blame?

Let's have, in a fringe, an indicator for altered regions in a version controlled file. The symbols “+, =” appear in a fringe by default for alterations —we may change these if we like.

;; Hunk navigation and commiting.
(use-package git-gutter
  :diminish
  :config (global-git-gutter-mode))
;; Diff updates happen in real time according when user is idle.

Let's set a hydra so we can press C-x v n n p n to move the next two altered hunks, move back one, then move to the next. This saves me having to supply the prefix C-x v each time I navigate among my alterations. At any point we may also press u 𝕩 to denote C-u ⟪prefix⟫ 𝕩.

(defhydra hydra-version-control (global-map "C-x v")
  "Version control"
  ;; Syntax: (extension method description)
  ("n" git-gutter:next-hunk      "Next hunk")
  ("p" git-gutter:previous-hunk  "Previous hunk")
  ("d" git-gutter:popup-hunk     "Show hunk diff")
  ("r" git-gutter:revert-hunk    "Revert hunk\n")
  ("c" git-gutter:stage-hunk     "Stage hunk")
  ("s" git-gutter:statistic      "How many added & deleted lines"))

Commiting with C-x v c let's us use C-c C-k to cancel and C-c C-c to submit the given message; C-c C-a to amend the previous commit.

Alternatively, we may use diff-hl:

;; Colour fringe to indicate alterations.
;; (use-package diff-hl)
;; (global-diff-hl-mode)

A few more helpful version control features:

;; Popup for who's to blame for alterations.
(use-package git-messenger
  :custom ;; Always show who authored the commit and when.
          (git-messenger:show-detail t)
          ;; Message menu let's us use magit diff to see the commit change.
          (git-messenger:use-magit-popup t))

;; View current file in browser on github.
;; More generic is “browse-at-remote”.
(use-package github-browse-file :defer t)

;; Add these to the version control hydra.
;;
(defhydra hydra-version-control (global-map "C-x v")
  ("b" git-messenger:popup-message "Who's to blame?")
  ;; C-u C-x b ╱ u b ∷ Also show who authored the change and when.
  ("g" github-browse-file-blame "Show file in browser in github")
  ("s" magit-status "Git status of current buffer"))

Perhaps C-x v b will motivate smaller, frequent, commits.

Obtaining URL links to the current location of a file —URLs are added to the kill ring. Usefully, if git-timemachine-mode is active, the generated link points to the version of the file being visited.

(use-package git-link :defer t)

(defhydra hydra-version-control (global-map "C-x v")
  ("l" git-link "Git URL for current location"))

Read here for more about version control in general.

12.15. Emacs keybindings for my browser   Disabled

⟨ I was a bit too Emacs-happy at one-point; this' cool, but I rarely use it; except C-x b: A buffer approach is far superior to a tab-based one. ⟩

I've downloaded the Vimium extension for Google Chrome, and have copy-pasted these Emacs key bindings into it. Now C-h in my browser shows which Emacs-like bindings can be used to navigate my browser ^_^

12.16. Using Emacs in any text area on my OS   Disabled

⟨ I was a bit too Emacs-happy at one-point; this' cool, but I rarely use it. ⟩

Using the Emacs-Anywhere tool, I can press Cmd Shift e to have an Emacs frame appear, produce text with Emacs editing capabilities, then C-x 5 0 to have the resulting text dumped into the text area I was working in.

This way I can use Emacs literally anywhere for textual input!

For my Mac OSX:

(shell-command "curl -fsSL https://raw.github.com/zachcurry/emacs-anywhere/master/install | bash")

(server-start)

The tools that use emacs-anywhere —such as my web browser— and emacs-anywhere itself need to be given sufficient OS permissions:

System Preferences → Security & Privacy → Accessibility

Then check the emacs-anywhere box from the following gui and provide a keyboard shortcut:

System Preferences → Keyboard → Shortcuts → Services

(•̀ᴗ•́)و

I always want to be in Org-mode and input unicode:

(add-hook 'ea-popup-hook
  (lambda (app-name window-title x y w h)
    (org-mode)
    (set-input-method "Agda")))

12.17. Reload buffer with f5

I do this so often it's not even funny.

(global-set-key [f5] '(lambda () (interactive) (revert-buffer nil t nil)))

In Mac OS, one uses Cmd-r to reload a page and Emacs binds buffer reversion to Cmd-u –in Emacs, Mac's Cmd is referred to as the ‘super key’ and denoted s.

Moreover, since I use Org-mode to generate code blocks and occasionally inspect them, it would be nice if they automatically reverted when they were regenerated –Emacs should also prompt me if I make any changes!

;; Auto update buffers that change on disk.
;; Will be prompted if there are changes that could be lost.
(global-auto-revert-mode 1)
;; Auto refreshes every 2 seconds. Don’t forget to refresh the version control status as well.
(setq auto-revert-interval 2
      auto-revert-check-vc-info t
      global-auto-revert-non-file-buffers t
      auto-revert-verbose nil)

;; Don't show me the “ARev” marker in the mode line
(diminish 'auto-revert-mode)

12.18. Kill to start of line

Dual to C-k,

;; M-k kills to the left
(global-set-key "\M-k" '(lambda () (interactive) (kill-line 0)) )

12.19. Killing buffers & windows: C-x k has a family

Let's extend the standard C-x k with prefix support, so that we can invoke variations: Kill this buffer, kill other buffer, or kill all other buffers.

By default C-x k prompts to select which buffer should be selected. I almost always want to kill the current buffer, so let's not waste time making such a tedious decision. Moreover, if I've killed a buffer, I usually also don't want the residual window, so let's get rid of it.

(global-set-key (kbd "C-x k")
  (lambda (&optional prefix)
"C-x k     ⇒ Kill current buffer & window
C-u C-x k ⇒ Kill OTHER window and its buffer
C-u C-u C-x C-k ⇒ Kill all other buffers and windows

Prompt only if there are unsaved changes."
     (interactive "P")
     (pcase (or (car prefix) 0)
       ;; C-x k     ⇒ Kill current buffer & window
       (0  (kill-this-buffer)
           (unless (one-window-p) (delete-window)))
       ;; C-u C-x k ⇒ Kill OTHER window and its buffer
       (4  (other-window 1)
           (kill-this-buffer)
           (unless (one-window-p) (delete-window)))
       ;; C-u C-u C-x C-k ⇒ Kill all other buffers and windows
       (16   (mapc 'kill-buffer (delq (current-buffer) (buffer-list)))
             (delete-other-windows)))))

The incantation C-u C-x k will reduce the noise of all the documentation buffers I tend to consult.

12.20. Switching from 2 horizontal windows to 2 vertical windows

I often find myself switching from a horizontal view of two windows in Emacs to a vertical view. This requires a variation of C-x 1 RET C-x 3 RET C-x o C-x b RET. Instead I now only need to type C-| to make this switch.

(defun my/ensure-two-vertical-windows ()
  "I used this method often when programming in Coq.

When there are two vertical windows, this method ensures the left-most
window contains the buffer with the cursour in it."
  (interactive)
  (let ((otherBuffer (buffer-name)))
    (other-window 1)                ;; C-x 0
    (delete-window)                 ;; C-x 0
    (split-window-right)                        ;; C-x 3
    (other-window 1)                ;; C-x 0
    (switch-to-buffer otherBuffer)      ;; C-x b RET
    (other-window 1)))

(global-set-key (kbd "C-|") 'my/ensure-two-vertical-windows)

12.21. Obtaining Values of #+KEYWORD Annotations

Org-mode settings are, for the most part, in the form #+KEYWORD: VALUE. Of notable interest are the TITLE and NAME keywords. We use the following org-keywords function to obtain the values of arbitrary #+THIS : THAT pairs, which may not necessarily be supported by native Org-mode –we do so for the case, for example, of the CATEGORIES and IMAGE tags associated with an article.

;; Src: http://kitchingroup.cheme.cmu.edu/blog/2013/05/05/Getting-keyword-options-in-org-files/
(defun org-keywords ()
  "Parse the buffer and return a cons list of (property . value) from lines like: #+PROPERTY: value"
  (org-element-map (org-element-parse-buffer 'element) 'keyword
                   (lambda (keyword) (cons (org-element-property :key keyword)
                                           (org-element-property :value keyword)))))

(defun org-keyword (KEYWORD)
  "Get the value of a KEYWORD in the form of #+KEYWORD: value"
  (cdr (assoc KEYWORD (org-keywords))))

Note that capitalisation in a ”#+KeyWord” is irrelevant.

See here on how to see the abstract syntax tree of an org file and how to manipulate it.

12.22. Publishing articles to my personal blog

I try to blog occasionally, so here's a helpful function to quickly publish the current article to my blog.

(define-key global-map "\C-cb" 'my/publish-to-blog)

(cl-defun my/publish-to-blog (&optional (draft nil) (local nil))
  "
  Using ‘AlBasmala’ setup to publish current article to my blog.
  Details of AlBasmala can be found here:
  https://alhassy.github.io/AlBasmala/

  Locally: ~/alhassy.github.io/content/AlBasmala.org

  A ‘draft’ will be produced in about ~7 seconds, but does not re-produce
  a PDF and the article has a draft marker near the top. Otherwise,
  it will generally take ~30 seconds due to PDF production, which is normal.
  The default is not a draft and it takes ~20 seconds for the live
  github.io page to update.

  The ‘local’ optiona indicates whether the resulting article should be
  viewed using the local server or the live webpage. Live page is default.

  When ‘draft’ and ‘local’ are both set, the resulting page may momentarily
  show a page-not-found error, simply refresh.
  "

  (load-file "~/alhassy.github.io/content/AlBasmala.el")

  ;; --MOVE ME TO ALBASMALA--
  ;; Sometimes the file I'm working with is not a .org file, so:
  (setq file.org (buffer-name))

  (preview-article :draft draft)
  (unless draft (publish))
  (let ((server (if local "http://localhost:4000/" "https://alhassy.github.io/")))
    (async-shell-command (concat "open " server NAME "/") "*blog-post-in-browser*"))
)

12.23. Jumping without hassle

(defun my/org-goto-line (line)
  "Go to the indicated line, unfolding the parent Org header.

   Implementation: Go to the line, then look at the 1st previous
   org header, now we can unfold it whence we do so, then we go
   back to the line we want to be at.
  "
  (interactive "nEnter line: ")
  (goto-line line)
  (org-previous-visible-heading 1)
  (org-cycle)
  (goto-line line))

12.24. The evils of this world

;; “PDF” stands for Portable Document Format, since you should be able to open
;; it anywhere. Disgustingly, fillable PDF's made with Adobe can only be
;; smoothly opened & printed in Adobe ---Chrome can open them, but not print
;; them. Unfortunately, various government & insurance forms are only provided in
;; this format.
(system-packages-ensure "adobe-acrobat-reader")

12.25. TODO Hydra Timer

(setq org-clock-sound t) ;; Standard Emacs beep
(my/defhydra "C-c x" "Time Tracking" clock-o
  ;; Org-Clock ---must be on an Org header;; but the timer works from anywhere
  :Tasks
  ("n" (my/org-journal-new-entry :work) "New")
  ("v" (progn (my/org-journal-new-entry :work) (revert-buffer t t) (org-journal-mode)) "View all")
  :Timer
  ("s" org-timer-start "Start")
  ("S" org-timer-stop "Stop")
  ("x" org-timer-set-timer "Set")
  ("p" org-timer "Print")
  :Org-Clock
  ("i" org-clock-in "in")
  ("o" org-clock-out "out")
  ("c" org-clock-cancel "cancel" :color pink :column "Do")
  ("d" org-clock-display "display")
  ("e" org-clock-modify-effort-estimate "effort")

  ("j" org-clock-goto "Jump to task") ;; Jump to  the headline of the currently clocked in task. With a C-u prefix argument, select the target task from a list of recently clocked tasks.
  ("r" org-clock-report "Insert clocktable"))

12.26. Makes Org/Markdown previewabvle as we type!!! ♥

  ;; Shows up as a magnifying glass in doom-modeline.
  (use-package grip-mode)
  ;;  :hook ((markdown-mode org-mode) . grip-mode)
  ;; Pretty annyoning actually; instead we should call it as needed.
  )

12.27. Draw pretty unicode tables in org-mode   to_include

This turns the “—” and other ASCII for tables into ‘smooth’ lines ^_^

a b
1 2
   
(quelpa '(org-pretty-table
         :repo "Fuco1/org-pretty-table"
         :fetcher github))

(add-hook 'org-mode-hook 'org-pretty-table-mode)

Being an ‘on the fly replacement mechanism’, we get that “C-u 80 -” also results in one smooth horizontal rule and vertical sequences of ‘|’ results in a smooth vertical line.

12.28. Cucumber

;; Emacs mode for editing Cucumber plain text stories
;; “.feature” files now open up with nice colouring.
(use-package feature-mode)
;;
;; C-c ,g       Go to step-definition under point (requires ruby_parser gem >= 3.14.2)
;;
;; TODO: Ruby specific; but the source could be edited to work for JS.
;; (use-package cucumber-goto-step)

Other packages to consider looking into include:

12.29. Syntax highlighting —numbers and escape characters

Lightweight syntax highlighting improvement for numbers and escape sequences (e.g. \n, \t) within quotes.

(use-package highlight-numbers
  :hook ((prog-mode) . highlight-numbers-mode))
;; Not text-mode , look Bad

(use-package highlight-escape-sequences
  :hook (prog-mode . hes-mode))

12.30. shell-command-and-run

(defalias 'my/shell-command-and-run 'shell-command-and-run)
(defun shell-command-and-run (cmd name &rest more-commands)
  "Run shell command CMD (possibly opening a new repl/terminal) and then MORE-COMMANDS.

When to use this function? Whenever you're finding yourself in the situation:
(1) Open a terminal, (2) start an interactive repl, (3) rename the buffer name to be informative,
(4) run some default/initial commands. See also the `term' function.

CMD and NAME are strings; MORE-COMMANDS is an arbitrary number of strings.

The name of this function does not contain my personal prefix ‘my’,
since I'd like it to show up as a possible completion when I type
shell-command’.

For example,

   (shell-command-and-run
    \"ghci\" \"Playing with Haskell\"
    \"let x = 4\"
    \":t x\")

This results in an interactive shell buffer named “*Playing with Haskell*” with contents:

   GHCi, version 8.10.7: https://www.haskell.org/ghc/  :? for help
   Prelude> let x = 4
   Prelude> :t x
   x :: Num p => p
   Prelude> ❙"
  (interactive)
  (let* ((default-directory "~/")
         (proc (get-buffer-process
                (ansi-term cmd name))))
    (term-send-string
     proc (concat (s-join "\n" more-commands) "\n"))))

12.31. Keeping my system up to date

Let's ensure our system is always up to date.

(defun my/stay-up-to-date ()
  "Ensure that OS and Emacs package listings are up to date.

   Takes ~5 seconds when everything is up to date."
  (async-shell-command "brew update && brew upgrade")
  (other-window 1)
  (rename-buffer "Keeping-system-up-to-date")

  (package-refresh-contents 'please-do-so-in-the-background)
  (message "Updated Emacs package manager.")
  (other-window 1))

(add-hook 'after-init-hook 'my/stay-up-to-date)

;; For now, doing this since I'm also calling my/stay-up-to-date with
;; after-init-hook which hides the startup message.
(add-hook 'after-init-hook 'display-startup-echo-area-message)

12.32. Compile   posterity

# Local Variables:
# eval: (message "Load file specific stuffs here")
# compile-command: (async-shell-command (concat "open " (org-latex-export-to-pdf)))
# End:

Since nearly every file I work with is ─or can be coerced into being─ in org mode, I usually have a section * footer that contains something like the above.

Let's remove repeated matter.

;; Silently save before compiling.
(setq compilation-ask-about-save nil)

;; Silently kill previous compilation process before starting a new one.
(setq compilation-always-kill t)

;; Scroll as compilation output is procuded in *Compilation* buffer; e.g., pdflatex
;; Use 'first-error to stop scrolling on the first error encountered; otherwise ‘t’.
(setq compilation-scroll-output 'first-error)

;; Don't stop on informaiton messages or warnings; only on errors.
(setq compilation-skip-threshold 2)
;; My global compile command
(setq compile-command
  '(async-shell-command (concat "open " (org-latex-export-to-pdf))))

;; Bind ‘recompile’ to ‘C-c C-m’ ─“m” for “m”ake
(global-set-key (kbd "C-c C-m") 'recompile)

;; Also a helpful quick f-key.
(global-set-key (kbd "<f7>") 'recompile)

12.33. Let's jump to a current Chrome browser tab, or one from our Chrome history, from within Emacs.

;; M-x helm-chrome-history
;; [Your Chrome History SQLite database file: helm-chrome-history-file]
(use-package helm-chrome-history)
;; M-x helm-chrome-control
(use-package helm-chrome-control)

There is helm-taskswitch for switching between X-windows: Whereas ⌘-tab gives a list of most recently used program, helm-taskswitch gives a completing read of all OS windows. I'm using a MacOS right now, so X-windows are not the default —but can be setup.

12.34. Get Shell history within Emacs via Completing Read with Helm

;; Usage: M-x helm-shell-history
(use-package helm-shell-history
  :config
  (setq helm-shell-history-file "~/.zsh_history")
  (bind-key "M-r" #'helm-shell-history shell-mode-map))

12.35. Launch macOS apps with Helm

;; MacOS's default ⌘-SPC does not let us do either of the following scenarios:
;; Usage: M-x helm-osx-app RET preferences bat RET ⇒ See battery preferences settings
;; Another Usage: M-x helm-osx-app RET ⇒ See all apps, maybe we forgot about one of them from an install a long time ago, and open it
;; See https://www.alfredapp.com/ as an alternative (for non-Emacs users), which can do more.
(use-package helm-osx-app)
;; For non-MacOS, we can use counsel-osx-app, whose name is misleading.

12.37. Let's make working with Emacs Lisp even better!

(use-package elisp-demos
  :config
  ;; Show demos when I do a `C-h o'.
  (advice-add 'helpful-update :after #'elisp-demos-advice-helpful-update)
  ;; Show demos in tooltips when I pause to select a completion, in Emacs Lisp mode.
  (advice-add 'describe-function-1 :after #'elisp-demos-advice-describe-function-1))

12.38. A butler for your buffers. Group buffers into workspaces with programmable rules, and easily switch to and manipulate them.

(use-package bufler
  :config (bind-key "C-x C-b" #'bufler-list))
;; I still prefer “C-x b” to be “helm-mini”, since when looking for a buffer it also shows me recently visited files.

12.39. Let's try out this dope theme and this one too!

(use-package stimmung-themes
  :quelpa (stimmung-themes :fetcher github :repo "motform/stimmung-themes")
  :config (load-theme 'stimmung-themes-light))

(use-package shanty-themes)
(load-theme 'shanty-themes-light)

(setq-default cursor-type 'bar)

13. Conclusion —Why Configuration Files Should be Literate

A configuration file sets up various features for a tool —and serves as an essential learning point. In order to remember them, what they do, and possibly where you learned about them —which may include additional resources— it is pertinent to document such facts. Benefits of documentating features include:

  • A list of the features with human readable names! —In case you forget what you invested time on!
  • Personal documentation! —Reduce wasting time Googling things that you knew in the past!
  • Convincing Need
    • Making notes with decriptive text, as suggested below, will make it clear whether you actually need the feature or “just threw it becuase it looks cool” —which leads to ‘init bankruptcy’.

      Moreover, actually documenting a feature may make it more to recall that you have the feature and have notes for it.

Programs are meant to be read by humans and only incidentally for computers to execute. —Donald Knuth

Alongside a feature's installation, I've tried to provide the following:

  • Why would I want this? Motivation!
    • Example scenerios and use-cases.
  • How do I actually use it? Super terse usage details to “get going”!
  • Where is the offical documentation page, or repository? Discovarability!
  • Comparisions: Are there other similar features, builtin or otherwise? How do they compare? Why have I decided for this one instead of another one?
  • Additional comments and reminders related to the feature.
    • E.g., why the feature is now disabled, ‘commented out’, when before it was useful.

Programs without documentation have little value; it's like a claim without evidence! —Me

Here are some benefits of having a tool's configurations written literately as an Org-mode file, then tangeling as appropriate.

  • Modularity! —or “In Praise of the Monolith”

    It may not be feasible, or practical, to split a tool's configuration file into multiple file hierarchy. Yet, with Org-mode we may reify the hierarchical structure as ‘sections’ and have the resulting configuration read more like a novel, easily folding and navigating, between sections.

    • Section headers provide organisation and they're collapsable.

    Even if you can make multiple files, using one monolithic file allows:

    • Really easy to quickly re-organise code!
      • Use w to move content almost instanteously!
      • In contrast, it's harder to review an entire project, when it's in pieces.
    • Many files requires coming up with descriptive file names; instead prefer descriptive org headings ^_^
    • Easily navigatable hierarchy with a nested directory/org-heading structure.
      • Have headings with an introducttory paragraph that explains the kind of features being considered —or, lazily, look at the outlined view of subheadings to see what's there.
    • Easy search & review of features since they're in one file.
      • Multiple files makes it harder to remember which features live where.
    • One file is easy to distribute & share!

    Many small files are great for collobaration —there'll likely be less merge conflicts. However, configuration files are usually a one-person project.

  • Toggle feature selection without altering any code!

    With a single # key press, we can comment out a section, thereby disabling the features it provides. The features are neither deleted nor forgotten, but we can experiment with having them there or not without altering any code! Alternatively, one mays use the :noexport: tag on a section header.

    In contrast, an illiterate setup would have us commenting out large chunks of code, which is not as easy to manage.

  • Really easy to delete content!

    After a while, I come back and realise I've implemented something silly or that is available via some external package, I can quickly delete it.

  • Can quickly export to different mediums!

    If you want to share your configuration with others, then an HTML rendition with a table of contents and text sprinkled everywhere is more likely to attract onlookers since they can easily jump to the sections they're interested in.

  • Easily digestible chunks of code!

    With a literate approach, one is empowered to have short source blocks; e.g., not exceeding 30 lines —read more here. This is more likely to ensure (possibly by extracting code into its own functions): The listing fits on one screen, avoiding deeply nested control structures, non-repeating common logical patterns, increased confidence that the implementation meets the stated purpose.

The only reason I would use multiple files or raw code for setting up a tool would be if I did not have a literate programming environment; i.e., Org-mode.



Emacs is fun (•̀ᴗ•́)و

Bye!

Being replaced at the office

Thanks to friends for “replacing me” while I was away ♥‿♥ ᴵ’ᵐ ᵇᵉᵃᵘᵗⁱᶠᵘˡ